static apr_status_t apreq_output_filter_test(ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; apreq_handle_t *handle; apr_bucket_brigade *eos; struct ctx_t ctx = {r, bb}; const apr_table_t *t; if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) return ap_pass_brigade(f->next,bb); eos = apr_brigade_split(bb, APR_BRIGADE_LAST(bb)); handle = apreq_handle_apache2(r); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, "appending parsed data"); apr_brigade_puts(bb, NULL, NULL, "\n--APREQ OUTPUT FILTER--\nARGS:\n"); apreq_args(handle, &t); if (t != NULL) apr_table_do(dump_table, &ctx, t, NULL); apreq_body(handle, &t); if (t != NULL) { apr_brigade_puts(bb,NULL,NULL,"BODY:\n"); apr_table_do(dump_table, &ctx, t, NULL); } APR_BRIGADE_CONCAT(bb,eos); return ap_pass_brigade(f->next,bb); }
AP_DECLARE(apr_status_t) ap_save_brigade(ap_filter_t *f, apr_bucket_brigade **saveto, apr_bucket_brigade **b, apr_pool_t *p) { apr_bucket *e; apr_status_t rv, srv = APR_SUCCESS; /* If have never stored any data in the filter, then we had better * create an empty bucket brigade so that we can concat. */ if (!(*saveto)) { *saveto = apr_brigade_create(p, f->c->bucket_alloc); } for (e = APR_BRIGADE_FIRST(*b); e != APR_BRIGADE_SENTINEL(*b); e = APR_BUCKET_NEXT(e)) { rv = apr_bucket_setaside(e, p); /* If the bucket type does not implement setaside, then * (hopefully) morph it into a bucket type which does, and set * *that* aside... */ if (rv == APR_ENOTIMPL) { const char *s; apr_size_t n; rv = apr_bucket_read(e, &s, &n, APR_BLOCK_READ); if (rv == APR_SUCCESS) { rv = apr_bucket_setaside(e, p); } } if (rv != APR_SUCCESS) { srv = rv; /* Return an error but still save the brigade if * ->setaside() is really not implemented. */ if (rv != APR_ENOTIMPL) { return rv; } } } APR_BRIGADE_CONCAT(*saveto, *b); return srv; }
static apr_status_t substitute_filter(ap_filter_t *f, apr_bucket_brigade *bb) { apr_size_t bytes; apr_size_t len; apr_size_t fbytes; const char *buff; const char *nl = NULL; char *bflat; apr_bucket *b; apr_bucket *tmp_b; apr_bucket_brigade *tmp_bb = NULL; apr_status_t rv; subst_dir_conf *cfg = (subst_dir_conf *) ap_get_module_config(f->r->per_dir_config, &substitute_module); substitute_module_ctx *ctx = f->ctx; /* * First time around? Create the saved bb that we used for each pass * through. Note that we can also get here when we explicitly clear ctx, * for error handling */ if (!ctx) { f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); /* * Create all the temporary brigades we need and reuse them to avoid * creating them over and over again from r->pool which would cost a * lot of memory in some cases. */ ctx->linebb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); ctx->linesbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); ctx->pattbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); /* * Everything to be passed to the next filter goes in * here, our pass brigade. */ ctx->passbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); /* Create our temporary pool only once */ apr_pool_create(&(ctx->tpool), f->r->pool); apr_table_unset(f->r->headers_out, "Content-Length"); } /* * Shortcircuit processing */ if (APR_BRIGADE_EMPTY(bb)) return APR_SUCCESS; /* * Here's the concept: * Read in the data and look for newlines. Once we * find a full "line", add it to our working brigade. * If we've finished reading the brigade and we have * any left over data (not a "full" line), store that * for the next pass. * * Note: anything stored in ctx->linebb for sure does not have * a newline char, so we don't concat that bb with the * new bb, since we would spending time searching for the newline * in data we know it doesn't exist. So instead, we simply scan * our current bb and, if we see a newline, prepend ctx->linebb * to the front of it. This makes the code much less straight- * forward (otherwise we could APR_BRIGADE_CONCAT(ctx->linebb, bb) * and just scan for newlines and not bother with needing to know * when ctx->linebb needs to be reset) but also faster. We'll take * the speed. * * Note: apr_brigade_split_line would be nice here, but we * really can't use it since we need more control and we want * to re-use already read bucket data. * * See mod_include if still confused :) */ while ((b = APR_BRIGADE_FIRST(bb)) && (b != APR_BRIGADE_SENTINEL(bb))) { if (APR_BUCKET_IS_EOS(b)) { /* * if we see the EOS, then we need to pass along everything we * have. But if the ctx->linebb isn't empty, then we need to add * that to the end of what we'll be passing. */ if (!APR_BRIGADE_EMPTY(ctx->linebb)) { rv = apr_brigade_pflatten(ctx->linebb, &bflat, &fbytes, ctx->tpool); if (rv != APR_SUCCESS) goto err; if (fbytes > cfg->max_line_length) { rv = APR_ENOMEM; goto err; } tmp_b = apr_bucket_transient_create(bflat, fbytes, f->r->connection->bucket_alloc); rv = do_pattmatch(f, tmp_b, ctx->pattbb, ctx->tpool); if (rv != APR_SUCCESS) goto err; APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb); apr_brigade_cleanup(ctx->linebb); } APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->passbb, b); } /* * No need to handle FLUSH buckets separately as we call * ap_pass_brigade anyway at the end of the loop. */ else if (APR_BUCKET_IS_METADATA(b)) { APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->passbb, b); } else { /* * We have actual "data" so read in as much as we can and start * scanning and splitting from our read buffer */ rv = apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ); if (rv != APR_SUCCESS || bytes == 0) { apr_bucket_delete(b); } else { int num = 0; while (bytes > 0) { nl = memchr(buff, APR_ASCII_LF, bytes); if (nl) { len = (apr_size_t) (nl - buff) + 1; /* split *after* the newline */ apr_bucket_split(b, len); /* * We've likely read more data, so bypass rereading * bucket data and continue scanning through this * buffer */ bytes -= len; buff += len; /* * we need b to be updated for future potential * splitting */ tmp_b = APR_BUCKET_NEXT(b); APR_BUCKET_REMOVE(b); /* * Hey, we found a newline! Don't forget the old * stuff that needs to be added to the front. So we * add the split bucket to the end, flatten the whole * bb, morph the whole shebang into a bucket which is * then added to the tail of the newline bb. */ if (!APR_BRIGADE_EMPTY(ctx->linebb)) { APR_BRIGADE_INSERT_TAIL(ctx->linebb, b); rv = apr_brigade_pflatten(ctx->linebb, &bflat, &fbytes, ctx->tpool); if (rv != APR_SUCCESS) goto err; if (fbytes > cfg->max_line_length) { /* Avoid pflattening further lines, we will * abort later on anyway. */ rv = APR_ENOMEM; goto err; } b = apr_bucket_transient_create(bflat, fbytes, f->r->connection->bucket_alloc); apr_brigade_cleanup(ctx->linebb); } rv = do_pattmatch(f, b, ctx->pattbb, ctx->tpool); if (rv != APR_SUCCESS) goto err; /* * Count how many buckets we have in ctx->passbb * so far. Yes, this is correct we count ctx->passbb * and not ctx->pattbb as we do not reset num on every * iteration. */ for (b = APR_BRIGADE_FIRST(ctx->pattbb); b != APR_BRIGADE_SENTINEL(ctx->pattbb); b = APR_BUCKET_NEXT(b)) { num++; } APR_BRIGADE_CONCAT(ctx->passbb, ctx->pattbb); /* * If the number of buckets in ctx->passbb reaches an * "insane" level, we consume much memory for all the * buckets as such. So lets flush them down the chain * in this case and thus clear ctx->passbb. This frees * the buckets memory for further processing. * Usually this condition should not become true, but * it is a safety measure for edge cases. */ if (num > AP_MAX_BUCKETS) { b = apr_bucket_flush_create( f->r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(ctx->passbb, b); rv = ap_pass_brigade(f->next, ctx->passbb); apr_brigade_cleanup(ctx->passbb); num = 0; apr_pool_clear(ctx->tpool); if (rv != APR_SUCCESS) goto err; } b = tmp_b; } else { /* * no newline in whatever is left of this buffer so * tuck data away and get next bucket */ APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->linebb, b); bytes = 0; } } } } if (!APR_BRIGADE_EMPTY(ctx->passbb)) { rv = ap_pass_brigade(f->next, ctx->passbb); apr_brigade_cleanup(ctx->passbb); if (rv != APR_SUCCESS) goto err; } apr_pool_clear(ctx->tpool); } /* Anything left we want to save/setaside for the next go-around */ if (!APR_BRIGADE_EMPTY(ctx->linebb)) { /* * Provide ap_save_brigade with an existing empty brigade * (ctx->linesbb) to avoid creating a new one. */ ap_save_brigade(f, &(ctx->linesbb), &(ctx->linebb), f->r->pool); tmp_bb = ctx->linebb; ctx->linebb = ctx->linesbb; ctx->linesbb = tmp_bb; } return APR_SUCCESS; err: if (rv == APR_ENOMEM) ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01328) "Line too long, URI %s", f->r->uri); apr_pool_clear(ctx->tpool); return rv; }
static apr_status_t rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *input_bb) { apr_status_t rv = APR_SUCCESS; rl_ctx_t *ctx = f->ctx; apr_bucket *fb; int do_sleep = 0; apr_bucket_alloc_t *ba = f->r->connection->bucket_alloc; apr_bucket_brigade *bb = input_bb; if (f->c->aborted) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01454) "rl: conn aborted"); apr_brigade_cleanup(bb); return APR_ECONNABORTED; } if (ctx == NULL) { const char *rl = NULL; int ratelimit; /* no subrequests. */ if (f->r->main != NULL) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } rl = apr_table_get(f->r->subprocess_env, "rate-limit"); if (rl == NULL) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } /* rl is in kilo bytes / second */ ratelimit = atoi(rl) * 1024; if (ratelimit <= 0) { /* remove ourselves */ ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } /* first run, init stuff */ ctx = apr_palloc(f->r->pool, sizeof(rl_ctx_t)); f->ctx = ctx; ctx->state = RATE_LIMIT; ctx->speed = ratelimit; /* calculate how many bytes / interval we want to send */ /* speed is bytes / second, so, how many (speed / 1000 % interval) */ ctx->chunk_size = (ctx->speed / (1000 / RATE_INTERVAL_MS)); ctx->tmpbb = apr_brigade_create(f->r->pool, ba); ctx->holdingbb = apr_brigade_create(f->r->pool, ba); } while (ctx->state != RATE_ERROR && (!APR_BRIGADE_EMPTY(bb) || !APR_BRIGADE_EMPTY(ctx->holdingbb))) { apr_bucket *e; if (!APR_BRIGADE_EMPTY(ctx->holdingbb)) { APR_BRIGADE_CONCAT(bb, ctx->holdingbb); } while (ctx->state == RATE_FULLSPEED && !APR_BRIGADE_EMPTY(bb)) { /* Find where we 'stop' going full speed. */ for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) { if (AP_RL_BUCKET_IS_END(e)) { apr_bucket *f; f = APR_RING_LAST(&bb->list); APR_RING_UNSPLICE(e, f, link); APR_RING_SPLICE_TAIL(&ctx->holdingbb->list, e, f, apr_bucket, link); ctx->state = RATE_LIMIT; break; } } if (f->c->aborted) { apr_brigade_cleanup(bb); ctx->state = RATE_ERROR; break; } fb = apr_bucket_flush_create(ba); APR_BRIGADE_INSERT_TAIL(bb, fb); rv = ap_pass_brigade(f->next, bb); if (rv != APR_SUCCESS) { ctx->state = RATE_ERROR; ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, f->r, APLOGNO(01455) "rl: full speed brigade pass failed."); } } while (ctx->state == RATE_LIMIT && !APR_BRIGADE_EMPTY(bb)) { for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) { if (AP_RL_BUCKET_IS_START(e)) { apr_bucket *f; f = APR_RING_LAST(&bb->list); APR_RING_UNSPLICE(e, f, link); APR_RING_SPLICE_TAIL(&ctx->holdingbb->list, e, f, apr_bucket, link); ctx->state = RATE_FULLSPEED; break; } } while (!APR_BRIGADE_EMPTY(bb)) { apr_bucket *stop_point; apr_off_t len = 0; if (f->c->aborted) { apr_brigade_cleanup(bb); ctx->state = RATE_ERROR; break; } if (do_sleep) { apr_sleep(RATE_INTERVAL_MS * 1000); } else { do_sleep = 1; } apr_brigade_length(bb, 1, &len); rv = apr_brigade_partition(bb, ctx->chunk_size, &stop_point); if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { ctx->state = RATE_ERROR; ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01456) "rl: partition failed."); break; } if (stop_point != APR_BRIGADE_SENTINEL(bb)) { apr_bucket *f; apr_bucket *e = APR_BUCKET_PREV(stop_point); f = APR_RING_FIRST(&bb->list); APR_RING_UNSPLICE(f, e, link); APR_RING_SPLICE_HEAD(&ctx->tmpbb->list, f, e, apr_bucket, link); } else { APR_BRIGADE_CONCAT(ctx->tmpbb, bb); } fb = apr_bucket_flush_create(ba); APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, fb); #if 0 brigade_dump(f->r, ctx->tmpbb); brigade_dump(f->r, bb); #endif rv = ap_pass_brigade(f->next, ctx->tmpbb); apr_brigade_cleanup(ctx->tmpbb); if (rv != APR_SUCCESS) { ctx->state = RATE_ERROR; ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, f->r, APLOGNO(01457) "rl: brigade pass failed."); break; } } } } return rv; }
AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; conn_rec *c = r->connection; apr_bucket *e; apr_bucket_brigade *bsend; apr_bucket_brigade *tmpbb; apr_off_t range_start; apr_off_t range_end; apr_off_t clength = 0; apr_status_t rv; int found = 0; int num_ranges; char *boundary = NULL; char *bound_head = NULL; apr_array_header_t *indexes; indexes_t *idx; int i; int original_status; int max_ranges = get_max_ranges(r); /* * Iterate through the brigade until reaching EOS or a bucket with * unknown length. */ for (e = APR_BRIGADE_FIRST(bb); (e != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(e) && e->length != (apr_size_t)-1); e = APR_BUCKET_NEXT(e)) { clength += e->length; } /* * Don't attempt to do byte range work if this brigade doesn't * contain an EOS, or if any of the buckets has an unknown length; * this avoids the cases where it is expensive to perform * byteranging (i.e. may require arbitrary amounts of memory). */ if (!APR_BUCKET_IS_EOS(e) || clength <= 0) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } original_status = r->status; num_ranges = ap_set_byterange(r, clength, &indexes); /* We have nothing to do, get out of the way. */ if (num_ranges == 0 || (max_ranges >= 0 && num_ranges > max_ranges)) { r->status = original_status; ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } /* this brigade holds what we will be sending */ bsend = apr_brigade_create(r->pool, c->bucket_alloc); if (num_ranges < 0) return send_416(f, bsend); if (num_ranges > 1) { /* Is ap_make_content_type required here? */ const char *orig_ct = ap_make_content_type(r, r->content_type); boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx", (apr_uint64_t)r->request_time, c->id); ap_set_content_type(r, apr_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/", "byteranges; boundary=", boundary, NULL)); if (strcasecmp(orig_ct, NO_CONTENT_TYPE)) { bound_head = apr_pstrcat(r->pool, CRLF "--", boundary, CRLF "Content-type: ", orig_ct, CRLF "Content-range: bytes ", NULL); } else { /* if we have no type for the content, do our best */ bound_head = apr_pstrcat(r->pool, CRLF "--", boundary, CRLF "Content-range: bytes ", NULL); } ap_xlate_proto_to_ascii(bound_head, strlen(bound_head)); } tmpbb = apr_brigade_create(r->pool, c->bucket_alloc); idx = (indexes_t *)indexes->elts; for (i = 0; i < indexes->nelts; i++, idx++) { range_start = idx->start; range_end = idx->end; rv = copy_brigade_range(bb, tmpbb, range_start, range_end); if (rv != APR_SUCCESS ) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "copy_brigade_range() failed [%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]", range_start, range_end, clength); continue; } found = 1; /* * For single range requests, we must produce Content-Range header. * Otherwise, we need to produce the multipart boundaries. */ if (num_ranges == 1) { apr_table_setn(r->headers_out, "Content-Range", apr_psprintf(r->pool, "bytes " BYTERANGE_FMT, range_start, range_end, clength)); } else { char *ts; e = apr_bucket_pool_create(bound_head, strlen(bound_head), r->pool, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bsend, e); ts = apr_psprintf(r->pool, BYTERANGE_FMT CRLF CRLF, range_start, range_end, clength); ap_xlate_proto_to_ascii(ts, strlen(ts)); e = apr_bucket_pool_create(ts, strlen(ts), r->pool, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bsend, e); } APR_BRIGADE_CONCAT(bsend, tmpbb); if (i && !(i & 0x1F)) { /* * Every now and then, pass what we have down the filter chain. * In this case, the content-length filter cannot calculate and * set the content length and we must remove any Content-Length * header already present. */ apr_table_unset(r->headers_out, "Content-Length"); if ((rv = ap_pass_brigade(f->next, bsend)) != APR_SUCCESS) return rv; apr_brigade_cleanup(bsend); } } if (found == 0) { /* bsend is assumed to be empty if we get here. */ return send_416(f, bsend); } if (num_ranges > 1) { char *end; /* add the final boundary */ end = apr_pstrcat(r->pool, CRLF "--", boundary, "--" CRLF, NULL); ap_xlate_proto_to_ascii(end, strlen(end)); e = apr_bucket_pool_create(end, strlen(end), r->pool, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bsend, e); } e = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bsend, e); /* we're done with the original content - all of our data is in bsend. */ apr_brigade_cleanup(bb); apr_brigade_destroy(tmpbb); /* send our multipart output */ return ap_pass_brigade(f->next, bsend); }
static apr_status_t rate_limit_filter(ap_filter_t *f, apr_bucket_brigade *bb) { apr_status_t rv = APR_SUCCESS; rl_ctx_t *ctx = f->ctx; apr_bucket_alloc_t *ba = f->r->connection->bucket_alloc; /* Set up our rl_ctx_t on first use */ if (ctx == NULL) { const char *rl = NULL; int ratelimit; int burst = 0; /* no subrequests. */ if (f->r->main != NULL) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } /* Configuration: rate limit */ rl = apr_table_get(f->r->subprocess_env, "rate-limit"); if (rl == NULL) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } /* rl is in kilo bytes / second */ ratelimit = atoi(rl) * 1024; if (ratelimit <= 0) { /* remove ourselves */ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(03488) "rl: disabling: rate-limit = %s (too high?)", rl); ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } /* Configuration: optional initial burst */ rl = apr_table_get(f->r->subprocess_env, "rate-initial-burst"); if (rl != NULL) { burst = atoi(rl) * 1024; if (burst <= 0) { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(03489) "rl: disabling burst: rate-initial-burst = %s (too high?)", rl); burst = 0; } } /* Set up our context */ ctx = apr_palloc(f->r->pool, sizeof(rl_ctx_t)); f->ctx = ctx; ctx->state = RATE_LIMIT; ctx->speed = ratelimit; ctx->burst = burst; ctx->do_sleep = 0; /* calculate how many bytes / interval we want to send */ /* speed is bytes / second, so, how many (speed / 1000 % interval) */ ctx->chunk_size = (ctx->speed / (1000 / RATE_INTERVAL_MS)); ctx->tmpbb = apr_brigade_create(f->r->pool, ba); ctx->holdingbb = apr_brigade_create(f->r->pool, ba); } else { APR_BRIGADE_PREPEND(bb, ctx->holdingbb); } while (!APR_BRIGADE_EMPTY(bb)) { apr_bucket *e; if (ctx->state == RATE_FULLSPEED) { /* Find where we 'stop' going full speed. */ for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) { if (AP_RL_BUCKET_IS_END(e)) { apr_brigade_split_ex(bb, e, ctx->holdingbb); ctx->state = RATE_LIMIT; break; } } e = apr_bucket_flush_create(ba); APR_BRIGADE_INSERT_TAIL(bb, e); rv = ap_pass_brigade(f->next, bb); apr_brigade_cleanup(bb); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, f->r, APLOGNO(01455) "rl: full speed brigade pass failed."); return rv; } } else { for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); e = APR_BUCKET_NEXT(e)) { if (AP_RL_BUCKET_IS_START(e)) { apr_brigade_split_ex(bb, e, ctx->holdingbb); ctx->state = RATE_FULLSPEED; break; } } while (!APR_BRIGADE_EMPTY(bb)) { apr_off_t len = ctx->chunk_size + ctx->burst; APR_BRIGADE_CONCAT(ctx->tmpbb, bb); /* * Pull next chunk of data; the initial amount is our * burst allotment (if any) plus a chunk. All subsequent * iterations are just chunks with whatever remaining * burst amounts we have left (in case not done in the * first bucket). */ rv = apr_brigade_partition(ctx->tmpbb, len, &e); if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, APLOGNO(01456) "rl: partition failed."); return rv; } /* Send next metadata now if any */ while (e != APR_BRIGADE_SENTINEL(ctx->tmpbb) && APR_BUCKET_IS_METADATA(e)) { e = APR_BUCKET_NEXT(e); } if (e != APR_BRIGADE_SENTINEL(ctx->tmpbb)) { apr_brigade_split_ex(ctx->tmpbb, e, bb); } else { apr_brigade_length(ctx->tmpbb, 1, &len); } /* * Adjust the burst amount depending on how much * we've done up to now. */ if (ctx->burst) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(03485) "rl: burst %d; len %"APR_OFF_T_FMT, ctx->burst, len); if (len < ctx->burst) { ctx->burst -= len; } else { ctx->burst = 0; } } e = APR_BRIGADE_LAST(ctx->tmpbb); if (APR_BUCKET_IS_EOS(e)) { ap_remove_output_filter(f); } else if (!APR_BUCKET_IS_FLUSH(e)) { if (APR_BRIGADE_EMPTY(bb)) { /* Wait for more (or next call) */ break; } e = apr_bucket_flush_create(ba); APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, e); } #if defined(RLFDEBUG) brigade_dump(f->r, ctx->tmpbb); brigade_dump(f->r, bb); #endif /* RLFDEBUG */ if (ctx->do_sleep) { apr_sleep(RATE_INTERVAL_MS * 1000); } else { ctx->do_sleep = 1; } rv = ap_pass_brigade(f->next, ctx->tmpbb); apr_brigade_cleanup(ctx->tmpbb); if (rv != APR_SUCCESS) { /* Most often, user disconnects from stream */ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, f->r, APLOGNO(01457) "rl: brigade pass failed."); return rv; } } } if (!APR_BRIGADE_EMPTY(ctx->holdingbb)) { /* Any rate-limited data in tmpbb is sent unlimited along * with the rest. */ APR_BRIGADE_CONCAT(bb, ctx->tmpbb); APR_BRIGADE_CONCAT(bb, ctx->holdingbb); } } #if defined(RLFDEBUG) brigade_dump(f->r, ctx->tmpbb); #endif /* RLFDEBUG */ /* Save remaining tmpbb with the correct lifetime for the next call */ return ap_save_brigade(f, &ctx->holdingbb, &ctx->tmpbb, f->r->pool); }
apr_status_t ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { apr_status_t rv; core_net_rec *net = f->ctx; core_ctx_t *ctx = net->in_ctx; const char *str; apr_size_t len; if (mode == AP_MODE_INIT) { /* * this mode is for filters that might need to 'initialize' * a connection before reading request data from a client. * NNTP over SSL for example needs to handshake before the * server sends the welcome message. * such filters would have changed the mode before this point * is reached. however, protocol modules such as NNTP should * not need to know anything about SSL. given the example, if * SSL is not in the filter chain, AP_MODE_INIT is a noop. */ return APR_SUCCESS; } if (!ctx) { net->in_ctx = ctx = apr_palloc(f->c->pool, sizeof(*ctx)); ctx->b = apr_brigade_create(f->c->pool, f->c->bucket_alloc); ctx->tmpbb = apr_brigade_create(f->c->pool, f->c->bucket_alloc); /* seed the brigade with the client socket. */ rv = ap_run_insert_network_bucket(f->c, ctx->b, net->client_socket); if (rv != APR_SUCCESS) return rv; } else if (APR_BRIGADE_EMPTY(ctx->b)) { return APR_EOF; } /* ### This is bad. */ BRIGADE_NORMALIZE(ctx->b); /* check for empty brigade again *AFTER* BRIGADE_NORMALIZE() * If we have lost our socket bucket (see above), we are EOF. * * Ideally, this should be returning SUCCESS with EOS bucket, but * some higher-up APIs (spec. read_request_line via ap_rgetline) * want an error code. */ if (APR_BRIGADE_EMPTY(ctx->b)) { return APR_EOF; } if (mode == AP_MODE_GETLINE) { /* we are reading a single LF line, e.g. the HTTP headers */ rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN); /* We should treat EAGAIN here the same as we do for EOF (brigade is * empty). We do this by returning whatever we have read. This may * or may not be bogus, but is consistent (for now) with EOF logic. */ if (APR_STATUS_IS_EAGAIN(rv) && block == APR_NONBLOCK_READ) { rv = APR_SUCCESS; } return rv; } /* ### AP_MODE_PEEK is a horrific name for this mode because we also * eat any CRLFs that we see. That's not the obvious intention of * this mode. Determine whether anyone actually uses this or not. */ if (mode == AP_MODE_EATCRLF) { apr_bucket *e; const char *c; /* The purpose of this loop is to ignore any CRLF (or LF) at the end * of a request. Many browsers send extra lines at the end of POST * requests. We use the PEEK method to determine if there is more * data on the socket, so that we know if we should delay sending the * end of one request until we have served the second request in a * pipelined situation. We don't want to actually delay sending a * response if the server finds a CRLF (or LF), becuause that doesn't * mean that there is another request, just a blank line. */ while (1) { if (APR_BRIGADE_EMPTY(ctx->b)) return APR_EOF; e = APR_BRIGADE_FIRST(ctx->b); rv = apr_bucket_read(e, &str, &len, APR_NONBLOCK_READ); if (rv != APR_SUCCESS) return rv; c = str; while (c < str + len) { if (*c == APR_ASCII_LF) c++; else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF) c += 2; else return APR_SUCCESS; } /* If we reach here, we were a bucket just full of CRLFs, so * just toss the bucket. */ /* FIXME: Is this the right thing to do in the core? */ apr_bucket_delete(e); } return APR_SUCCESS; } /* If mode is EXHAUSTIVE, we want to just read everything until the end * of the brigade, which in this case means the end of the socket. * To do this, we attach the brigade that has currently been setaside to * the brigade that was passed down, and send that brigade back. * * NOTE: This is VERY dangerous to use, and should only be done with * extreme caution. FWLIW, this would be needed by an MPM like Perchild; * such an MPM can easily request the socket and all data that has been * read, which means that it can pass it to the correct child process. */ if (mode == AP_MODE_EXHAUSTIVE) { apr_bucket *e; /* Tack on any buckets that were set aside. */ APR_BRIGADE_CONCAT(b, ctx->b); /* Since we've just added all potential buckets (which will most * likely simply be the socket bucket) we know this is the end, * so tack on an EOS too. */ /* We have read until the brigade was empty, so we know that we * must be EOS. */ e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); return APR_SUCCESS; } /* read up to the amount they specified. */ if (mode == AP_MODE_READBYTES || mode == AP_MODE_SPECULATIVE) { apr_bucket *e; AP_DEBUG_ASSERT(readbytes > 0); e = APR_BRIGADE_FIRST(ctx->b); rv = apr_bucket_read(e, &str, &len, block); if (APR_STATUS_IS_EAGAIN(rv) && block == APR_NONBLOCK_READ) { /* getting EAGAIN for a blocking read is an error; for a * non-blocking read, return an empty brigade. */ return APR_SUCCESS; } else if (rv != APR_SUCCESS) { return rv; } else if (block == APR_BLOCK_READ && len == 0) { /* We wanted to read some bytes in blocking mode. We read * 0 bytes. Hence, we now assume we are EOS. * * When we are in normal mode, return an EOS bucket to the * caller. * When we are in speculative mode, leave ctx->b empty, so * that the next call returns an EOS bucket. */ apr_bucket_delete(e); if (mode == AP_MODE_READBYTES) { e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); } return APR_SUCCESS; } /* Have we read as much data as we wanted (be greedy)? */ if (len < readbytes) { apr_size_t bucket_len; rv = APR_SUCCESS; /* We already registered the data in e in len */ e = APR_BUCKET_NEXT(e); while ((len < readbytes) && (rv == APR_SUCCESS) && (e != APR_BRIGADE_SENTINEL(ctx->b))) { /* Check for the availability of buckets with known length */ if (e->length != -1) { len += e->length; e = APR_BUCKET_NEXT(e); } else { /* * Read from bucket, but non blocking. If there isn't any * more data, well than this is fine as well, we will * not wait for more since we already got some and we are * only checking if there isn't more. */ rv = apr_bucket_read(e, &str, &bucket_len, APR_NONBLOCK_READ); if (rv == APR_SUCCESS) { len += bucket_len; e = APR_BUCKET_NEXT(e); } } } } /* We can only return at most what we read. */ if (len < readbytes) { readbytes = len; } rv = apr_brigade_partition(ctx->b, readbytes, &e); if (rv != APR_SUCCESS) { return rv; } /* Must do move before CONCAT */ ctx->tmpbb = apr_brigade_split_ex(ctx->b, e, ctx->tmpbb); if (mode == AP_MODE_READBYTES) { APR_BRIGADE_CONCAT(b, ctx->b); } else if (mode == AP_MODE_SPECULATIVE) { apr_bucket *copy_bucket; for (e = APR_BRIGADE_FIRST(ctx->b); e != APR_BRIGADE_SENTINEL(ctx->b); e = APR_BUCKET_NEXT(e)) { rv = apr_bucket_copy(e, ©_bucket); if (rv != APR_SUCCESS) { return rv; } APR_BRIGADE_INSERT_TAIL(b, copy_bucket); } } /* Take what was originally there and place it back on ctx->b */ APR_BRIGADE_CONCAT(ctx->b, ctx->tmpbb); } return APR_SUCCESS; }
apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb) { conn_rec *c = f->c; core_net_rec *net = f->ctx; core_output_filter_ctx_t *ctx = net->out_ctx; apr_bucket_brigade *bb = NULL; apr_bucket *bucket, *next, *flush_upto = NULL; apr_size_t bytes_in_brigade, non_file_bytes_in_brigade; int eor_buckets_in_brigade, morphing_bucket_in_brigade; apr_status_t rv; int loglevel = ap_get_conn_module_loglevel(c, APLOG_MODULE_INDEX); /* Fail quickly if the connection has already been aborted. */ if (c->aborted) { if (new_bb != NULL) { apr_brigade_cleanup(new_bb); } return APR_ECONNABORTED; } if (ctx == NULL) { ctx = apr_pcalloc(c->pool, sizeof(*ctx)); net->out_ctx = (core_output_filter_ctx_t *)ctx; /* * Need to create tmp brigade with correct lifetime. Passing * NULL to apr_brigade_split_ex would result in a brigade * allocated from bb->pool which might be wrong. */ ctx->tmp_flush_bb = apr_brigade_create(c->pool, c->bucket_alloc); /* same for buffered_bb and ap_save_brigade */ ctx->buffered_bb = apr_brigade_create(c->pool, c->bucket_alloc); } if (new_bb != NULL) bb = new_bb; if ((ctx->buffered_bb != NULL) && !APR_BRIGADE_EMPTY(ctx->buffered_bb)) { if (new_bb != NULL) { APR_BRIGADE_PREPEND(bb, ctx->buffered_bb); } else { bb = ctx->buffered_bb; } c->data_in_output_filters = 0; } else if (new_bb == NULL) { return APR_SUCCESS; } /* Scan through the brigade and decide whether to attempt a write, * and how much to write, based on the following rules: * * 1) The new_bb is null: Do a nonblocking write of as much as * possible: do a nonblocking write of as much data as possible, * then save the rest in ctx->buffered_bb. (If new_bb == NULL, * it probably means that the MPM is doing asynchronous write * completion and has just determined that this connection * is writable.) * * 2) Determine if and up to which bucket we need to do a blocking * write: * * a) The brigade contains a flush bucket: Do a blocking write * of everything up that point. * * b) The request is in CONN_STATE_HANDLER state, and the brigade * contains at least THRESHOLD_MAX_BUFFER bytes in non-file * buckets: Do blocking writes until the amount of data in the * buffer is less than THRESHOLD_MAX_BUFFER. (The point of this * rule is to provide flow control, in case a handler is * streaming out lots of data faster than the data can be * sent to the client.) * * c) The request is in CONN_STATE_HANDLER state, and the brigade * contains at least MAX_REQUESTS_IN_PIPELINE EOR buckets: * Do blocking writes until less than MAX_REQUESTS_IN_PIPELINE EOR * buckets are left. (The point of this rule is to prevent too many * FDs being kept open by pipelined requests, possibly allowing a * DoS). * * d) The brigade contains a morphing bucket: If there was no other * reason to do a blocking write yet, try reading the bucket. If its * contents fit into memory before THRESHOLD_MAX_BUFFER is reached, * everything is fine. Otherwise we need to do a blocking write the * up to and including the morphing bucket, because ap_save_brigade() * would read the whole bucket into memory later on. * * 3) Actually do the blocking write up to the last bucket determined * by rules 2a-d. The point of doing only one flush is to make as * few calls to writev() as possible. * * 4) If the brigade contains at least THRESHOLD_MIN_WRITE * bytes: Do a nonblocking write of as much data as possible, * then save the rest in ctx->buffered_bb. */ if (new_bb == NULL) { rv = send_brigade_nonblocking(net->client_socket, bb, &(ctx->bytes_written), c); if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) { /* The client has aborted the connection */ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c, "core_output_filter: writing data to the network"); apr_brigade_cleanup(bb); c->aborted = 1; return rv; } setaside_remaining_output(f, ctx, bb, c); return APR_SUCCESS; } bytes_in_brigade = 0; non_file_bytes_in_brigade = 0; eor_buckets_in_brigade = 0; morphing_bucket_in_brigade = 0; for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket = next) { next = APR_BUCKET_NEXT(bucket); if (!APR_BUCKET_IS_METADATA(bucket)) { if (bucket->length == (apr_size_t)-1) { /* * A setaside of morphing buckets would read everything into * memory. Instead, we will flush everything up to and * including this bucket. */ morphing_bucket_in_brigade = 1; } else { bytes_in_brigade += bucket->length; if (!APR_BUCKET_IS_FILE(bucket)) non_file_bytes_in_brigade += bucket->length; } } else if (AP_BUCKET_IS_EOR(bucket)) { eor_buckets_in_brigade++; } if (APR_BUCKET_IS_FLUSH(bucket) || non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER || morphing_bucket_in_brigade || eor_buckets_in_brigade > MAX_REQUESTS_IN_PIPELINE) { /* this segment of the brigade MUST be sent before returning. */ if (loglevel >= APLOG_TRACE6) { char *reason = APR_BUCKET_IS_FLUSH(bucket) ? "FLUSH bucket" : (non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER) ? "THRESHOLD_MAX_BUFFER" : morphing_bucket_in_brigade ? "morphing bucket" : "MAX_REQUESTS_IN_PIPELINE"; ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c, "will flush because of %s", reason); ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c, "seen in brigade%s: bytes: %" APR_SIZE_T_FMT ", non-file bytes: %" APR_SIZE_T_FMT ", eor " "buckets: %d, morphing buckets: %d", flush_upto == NULL ? " so far" : " since last flush point", bytes_in_brigade, non_file_bytes_in_brigade, eor_buckets_in_brigade, morphing_bucket_in_brigade); } /* * Defer the actual blocking write to avoid doing many writes. */ flush_upto = next; bytes_in_brigade = 0; non_file_bytes_in_brigade = 0; eor_buckets_in_brigade = 0; morphing_bucket_in_brigade = 0; } } if (flush_upto != NULL) { ctx->tmp_flush_bb = apr_brigade_split_ex(bb, flush_upto, ctx->tmp_flush_bb); if (loglevel >= APLOG_TRACE8) { ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c, "flushing now"); } rv = send_brigade_blocking(net->client_socket, bb, &(ctx->bytes_written), c); if (rv != APR_SUCCESS) { /* The client has aborted the connection */ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c, "core_output_filter: writing data to the network"); apr_brigade_cleanup(bb); c->aborted = 1; return rv; } if (loglevel >= APLOG_TRACE8) { ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c, "total bytes written: %" APR_SIZE_T_FMT, ctx->bytes_written); } APR_BRIGADE_CONCAT(bb, ctx->tmp_flush_bb); } if (loglevel >= APLOG_TRACE8) { ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c, "brigade contains: bytes: %" APR_SIZE_T_FMT ", non-file bytes: %" APR_SIZE_T_FMT ", eor buckets: %d, morphing buckets: %d", bytes_in_brigade, non_file_bytes_in_brigade, eor_buckets_in_brigade, morphing_bucket_in_brigade); } if (bytes_in_brigade >= THRESHOLD_MIN_WRITE) { rv = send_brigade_nonblocking(net->client_socket, bb, &(ctx->bytes_written), c); if ((rv != APR_SUCCESS) && (!APR_STATUS_IS_EAGAIN(rv))) { /* The client has aborted the connection */ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c, "core_output_filter: writing data to the network"); apr_brigade_cleanup(bb); c->aborted = 1; return rv; } if (loglevel >= APLOG_TRACE8) { ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c, "tried nonblocking write, total bytes " "written: %" APR_SIZE_T_FMT, ctx->bytes_written); } } setaside_remaining_output(f, ctx, bb, c); return APR_SUCCESS; }
static apr_status_t merge_xml_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { apr_status_t rv; request_rec *r = f->r; merge_ctx_t *ctx = f->ctx; apr_bucket *bucket; int seen_eos = 0; /* We shouldn't be added if we're not a MERGE/DELETE, but double check. */ if ((r->method_number != M_MERGE) && (r->method_number != M_DELETE)) { ap_remove_input_filter(f); return ap_get_brigade(f->next, bb, mode, block, readbytes); } if (!ctx) { f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx)); ctx->parser = apr_xml_parser_create(r->pool); ctx->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); apr_pool_create(&ctx->pool, r->pool); } rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes); if (rv != APR_SUCCESS) return rv; for (bucket = APR_BRIGADE_FIRST(ctx->bb); bucket != APR_BRIGADE_SENTINEL(ctx->bb); bucket = APR_BUCKET_NEXT(bucket)) { const char *data; apr_size_t len; if (APR_BUCKET_IS_EOS(bucket)) { seen_eos = 1; break; } if (APR_BUCKET_IS_METADATA(bucket)) continue; rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) return rv; rv = apr_xml_parser_feed(ctx->parser, data, len); if (rv != APR_SUCCESS) { /* Clean up the parser. */ (void) apr_xml_parser_done(ctx->parser, NULL); break; } } /* This will clear-out the ctx->bb as well. */ APR_BRIGADE_CONCAT(bb, ctx->bb); if (seen_eos) { apr_xml_doc *pdoc; /* Remove ourselves now. */ ap_remove_input_filter(f); /* tell the parser that we're done */ rv = apr_xml_parser_done(ctx->parser, &pdoc); if (rv == APR_SUCCESS) { #if APR_CHARSET_EBCDIC apr_xml_parser_convert_doc(r->pool, pdoc, ap_hdrs_from_ascii); #endif /* stash the doc away for mod_dav_svn's later use. */ rv = apr_pool_userdata_set(pdoc, "svn-request-body", NULL, r->pool); if (rv != APR_SUCCESS) return rv; } } return APR_SUCCESS; }
apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *b) { apr_status_t rv; apr_bucket_brigade *more; conn_rec *c = f->c; core_net_rec *net = f->ctx; core_output_filter_ctx_t *ctx = net->out_ctx; apr_read_type_e eblock = APR_NONBLOCK_READ; apr_pool_t *input_pool = b->p; /* Fail quickly if the connection has already been aborted. */ if (c->aborted) { apr_brigade_cleanup(b); return APR_ECONNABORTED; } if (ctx == NULL) { ctx = apr_pcalloc(c->pool, sizeof(*ctx)); net->out_ctx = ctx; } /* If we have a saved brigade, concatenate the new brigade to it */ if (ctx->b) { APR_BRIGADE_CONCAT(ctx->b, b); b = ctx->b; ctx->b = NULL; } /* Perform multiple passes over the brigade, sending batches of output to the connection. */ while (b && !APR_BRIGADE_EMPTY(b)) { apr_size_t nbytes = 0; apr_bucket *last_e = NULL; /* initialized for debugging */ apr_bucket *e; /* one group of iovecs per pass over the brigade */ apr_size_t nvec = 0; apr_size_t nvec_trailers = 0; struct iovec vec[MAX_IOVEC_TO_WRITE]; struct iovec vec_trailers[MAX_IOVEC_TO_WRITE]; /* one file per pass over the brigade */ apr_file_t *fd = NULL; apr_size_t flen = 0; apr_off_t foffset = 0; /* keep track of buckets that we've concatenated * to avoid small writes */ apr_bucket *last_merged_bucket = NULL; /* tail of brigade if we need another pass */ more = NULL; /* Iterate over the brigade: collect iovecs and/or a file */ for (e = APR_BRIGADE_FIRST(b); e != APR_BRIGADE_SENTINEL(b); e = APR_BUCKET_NEXT(e)) { /* keep track of the last bucket processed */ last_e = e; if (APR_BUCKET_IS_EOS(e) || AP_BUCKET_IS_EOC(e)) { break; } else if (APR_BUCKET_IS_FLUSH(e)) { if (e != APR_BRIGADE_LAST(b)) { more = apr_brigade_split(b, APR_BUCKET_NEXT(e)); } break; } /* It doesn't make any sense to use sendfile for a file bucket * that represents 10 bytes. */ else if (APR_BUCKET_IS_FILE(e) && (e->length >= AP_MIN_SENDFILE_BYTES)) { apr_bucket_file *a = e->data; /* We can't handle more than one file bucket at a time * so we split here and send the file we have already * found. */ if (fd) { more = apr_brigade_split(b, e); break; } fd = a->fd; flen = e->length; foffset = e->start; } else { const char *str; apr_size_t n; rv = apr_bucket_read(e, &str, &n, eblock); if (APR_STATUS_IS_EAGAIN(rv)) { /* send what we have so far since we shouldn't expect more * output for a while... next time we read, block */ more = apr_brigade_split(b, e); eblock = APR_BLOCK_READ; break; } eblock = APR_NONBLOCK_READ; if (n) { if (!fd) { if (nvec == MAX_IOVEC_TO_WRITE) { /* woah! too many. buffer them up, for use later. */ apr_bucket *temp, *next; apr_bucket_brigade *temp_brig; if (nbytes >= AP_MIN_BYTES_TO_WRITE) { /* We have enough data in the iovec * to justify doing a writev */ more = apr_brigade_split(b, e); break; } /* Create a temporary brigade as a means * of concatenating a bunch of buckets together */ temp_brig = apr_brigade_create(f->c->pool, f->c->bucket_alloc); if (last_merged_bucket) { /* If we've concatenated together small * buckets already in a previous pass, * the initial buckets in this brigade * are heap buckets that may have extra * space left in them (because they * were created by apr_brigade_write()). * We can take advantage of this by * building the new temp brigade out of * these buckets, so that the content * in them doesn't have to be copied again. */ APR_BRIGADE_PREPEND(b, temp_brig); brigade_move(temp_brig, b, APR_BUCKET_NEXT(last_merged_bucket)); } temp = APR_BRIGADE_FIRST(b); while (temp != e) { apr_bucket *d; rv = apr_bucket_read(temp, &str, &n, APR_BLOCK_READ); apr_brigade_write(temp_brig, NULL, NULL, str, n); d = temp; temp = APR_BUCKET_NEXT(temp); apr_bucket_delete(d); } nvec = 0; nbytes = 0; temp = APR_BRIGADE_FIRST(temp_brig); APR_BUCKET_REMOVE(temp); APR_BRIGADE_INSERT_HEAD(b, temp); apr_bucket_read(temp, &str, &n, APR_BLOCK_READ); vec[nvec].iov_base = (char*) str; vec[nvec].iov_len = n; nvec++; /* Just in case the temporary brigade has * multiple buckets, recover the rest of * them and put them in the brigade that * we're sending. */ for (next = APR_BRIGADE_FIRST(temp_brig); next != APR_BRIGADE_SENTINEL(temp_brig); next = APR_BRIGADE_FIRST(temp_brig)) { APR_BUCKET_REMOVE(next); APR_BUCKET_INSERT_AFTER(temp, next); temp = next; apr_bucket_read(next, &str, &n, APR_BLOCK_READ); vec[nvec].iov_base = (char*) str; vec[nvec].iov_len = n; nvec++; } apr_brigade_destroy(temp_brig); last_merged_bucket = temp; e = temp; last_e = e; } else { vec[nvec].iov_base = (char*) str; vec[nvec].iov_len = n; nvec++; } } else { /* The bucket is a trailer to a file bucket */ if (nvec_trailers == MAX_IOVEC_TO_WRITE) { /* woah! too many. stop now. */ more = apr_brigade_split(b, e); break; } vec_trailers[nvec_trailers].iov_base = (char*) str; vec_trailers[nvec_trailers].iov_len = n; nvec_trailers++; } nbytes += n; } } } /* Completed iterating over the brigade, now determine if we want * to buffer the brigade or send the brigade out on the network. * * Save if we haven't accumulated enough bytes to send, the connection * is not about to be closed, and: * * 1) we didn't see a file, we don't have more passes over the * brigade to perform, AND we didn't stop at a FLUSH bucket. * (IOW, we will save plain old bytes such as HTTP headers) * or * 2) we hit the EOS and have a keep-alive connection * (IOW, this response is a bit more complex, but we save it * with the hope of concatenating with another response) */ if (nbytes + flen < AP_MIN_BYTES_TO_WRITE && !AP_BUCKET_IS_EOC(last_e) && ((!fd && !more && !APR_BUCKET_IS_FLUSH(last_e)) || (APR_BUCKET_IS_EOS(last_e) && c->keepalive == AP_CONN_KEEPALIVE))) { /* NEVER save an EOS in here. If we are saving a brigade with * an EOS bucket, then we are doing keepalive connections, and * we want to process to second request fully. */ if (APR_BUCKET_IS_EOS(last_e)) { apr_bucket *bucket; int file_bucket_saved = 0; apr_bucket_delete(last_e); for (bucket = APR_BRIGADE_FIRST(b); bucket != APR_BRIGADE_SENTINEL(b); bucket = APR_BUCKET_NEXT(bucket)) { /* Do a read on each bucket to pull in the * data from pipe and socket buckets, so * that we don't leave their file descriptors * open indefinitely. Do the same for file * buckets, with one exception: allow the * first file bucket in the brigade to remain * a file bucket, so that we don't end up * doing an mmap+memcpy every time a client * requests a <8KB file over a keepalive * connection. */ if (APR_BUCKET_IS_FILE(bucket) && !file_bucket_saved) { file_bucket_saved = 1; } else { const char *buf; apr_size_t len = 0; rv = apr_bucket_read(bucket, &buf, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, "core_output_filter:" " Error reading from bucket."); return HTTP_INTERNAL_SERVER_ERROR; } } } } if (!ctx->deferred_write_pool) { apr_pool_create(&ctx->deferred_write_pool, c->pool); apr_pool_tag(ctx->deferred_write_pool, "deferred_write"); } ap_save_brigade(f, &ctx->b, &b, ctx->deferred_write_pool); return APR_SUCCESS; } if (fd) { apr_hdtr_t hdtr; apr_size_t bytes_sent; #if APR_HAS_SENDFILE apr_int32_t flags = 0; #endif memset(&hdtr, '\0', sizeof(hdtr)); if (nvec) { hdtr.numheaders = nvec; hdtr.headers = vec; } if (nvec_trailers) { hdtr.numtrailers = nvec_trailers; hdtr.trailers = vec_trailers; } #if APR_HAS_SENDFILE if (apr_file_flags_get(fd) & APR_SENDFILE_ENABLED) { if (c->keepalive == AP_CONN_CLOSE && APR_BUCKET_IS_EOS(last_e)) { /* Prepare the socket to be reused */ flags |= APR_SENDFILE_DISCONNECT_SOCKET; } rv = sendfile_it_all(net, /* the network information */ fd, /* the file to send */ &hdtr, /* header and trailer iovecs */ foffset, /* offset in the file to begin sending from */ flen, /* length of file */ nbytes + flen, /* total length including headers */ &bytes_sent, /* how many bytes were sent */ flags); /* apr_sendfile flags */ } else #endif { rv = emulate_sendfile(net, fd, &hdtr, foffset, flen, &bytes_sent); } if (logio_add_bytes_out && bytes_sent > 0) logio_add_bytes_out(c, bytes_sent); fd = NULL; } else { apr_size_t bytes_sent; rv = writev_it_all(net->client_socket, vec, nvec, nbytes, &bytes_sent); if (logio_add_bytes_out && bytes_sent > 0) logio_add_bytes_out(c, bytes_sent); } apr_brigade_cleanup(b); /* drive cleanups for resources which were set aside * this may occur before or after termination of the request which * created the resource */ if (ctx->deferred_write_pool) { if (more && more->p == ctx->deferred_write_pool) { /* "more" belongs to the deferred_write_pool, * which is about to be cleared. */ if (APR_BRIGADE_EMPTY(more)) { more = NULL; } else { /* uh oh... change more's lifetime * to the input brigade's lifetime */ apr_bucket_brigade *tmp_more = more; more = NULL; ap_save_brigade(f, &more, &tmp_more, input_pool); } } apr_pool_clear(ctx->deferred_write_pool); } if (rv != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_INFO, rv, c, "core_output_filter: writing data to the network"); if (more) apr_brigade_cleanup(more); /* No need to check for SUCCESS, we did that above. */ if (!APR_STATUS_IS_EAGAIN(rv)) { c->aborted = 1; return APR_ECONNABORTED; } return APR_SUCCESS; } b = more; more = NULL; } /* end while () */ return APR_SUCCESS; }
static int ef_unified_filter(ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; conn_rec *c = r->connection; ef_ctx_t *ctx = f->ctx; apr_bucket *b; ef_dir_t *dc; apr_size_t len; const char *data; apr_status_t rv; char buf[4096]; apr_bucket *eos = NULL; apr_bucket_brigade *bb_tmp; dc = ctx->dc; bb_tmp = apr_brigade_create(r->pool, c->bucket_alloc); for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) { if (APR_BUCKET_IS_EOS(b)) { eos = b; break; } rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_bucket_read()"); return rv; } /* Good cast, we just tested len isn't negative */ if (len > 0 && (rv = pass_data_to_filter(f, data, (apr_size_t)len, bb_tmp)) != APR_SUCCESS) { return rv; } } apr_brigade_cleanup(bb); APR_BRIGADE_CONCAT(bb, bb_tmp); apr_brigade_destroy(bb_tmp); if (eos) { /* close the child's stdin to signal that no more data is coming; * that will cause the child to finish generating output */ if ((rv = apr_file_close(ctx->proc->in)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_file_close(child input)"); return rv; } /* since we've seen eos and closed the child's stdin, set the proper pipe * timeout; we don't care if we don't return from apr_file_read() for a while... */ rv = apr_file_pipe_timeout_set(ctx->proc->out, r->server->timeout); if (rv) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "apr_file_pipe_timeout_set(child output)"); return rv; } } do { len = sizeof(buf); rv = apr_file_read(ctx->proc->out, buf, &len); if ((rv && !APR_STATUS_IS_EOF(rv) && !APR_STATUS_IS_EAGAIN(rv)) || dc->debug >= DBGLVL_GORY) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, "apr_file_read(child output), len %" APR_SIZE_T_FMT, !rv ? len : -1); } if (APR_STATUS_IS_EAGAIN(rv)) { if (eos) { /* should not occur, because we have an APR timeout in place */ AP_DEBUG_ASSERT(1 != 1); } return APR_SUCCESS; } if (rv == APR_SUCCESS) { b = apr_bucket_heap_create(buf, len, NULL, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); } } while (rv == APR_SUCCESS); if (!APR_STATUS_IS_EOF(rv)) { return rv; } if (eos) { b = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); ctx->hit_eos = 1; } return APR_SUCCESS; }
static apr_status_t xlate_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { apr_status_t rv; charset_req_t *reqinfo = ap_get_module_config(f->r->request_config, &charset_lite_module); charset_dir_t *dc = ap_get_module_config(f->r->per_dir_config, &charset_lite_module); charset_filter_ctx_t *ctx = f->ctx; apr_size_t buffer_size; int hit_eos; if (!ctx) { /* this is SetInputFilter path; grab the preallocated context, * if any; note that if we decided not to do anything in an earlier * handler, we won't even have a reqinfo */ if (reqinfo) { ctx = f->ctx = reqinfo->input_ctx; reqinfo->input_ctx = NULL; /* prevent SNAFU if user coded us twice * in the filter chain; we can't have two * instances using the same context */ } if (!ctx) { /* no idea how to translate; don't do anything */ ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(charset_filter_ctx_t)); ctx->dc = dc; ctx->noop = 1; } } ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, f->r, "xlate_in_filter() - " "charset_source: %s charset_default: %s", dc && dc->charset_source ? dc->charset_source : "(none)", dc && dc->charset_default ? dc->charset_default : "(none)"); if (!ctx->ran) { /* filter never ran before */ chk_filter_chain(f); ctx->ran = 1; if (!ctx->noop && !ctx->is_sb && apr_table_get(f->r->headers_in, "Content-Length")) { /* A Content-Length header is present, but it won't be valid after * conversion because we're not converting between two single-byte * charsets. This will affect most CGI scripts and may affect * some modules. * Content-Length can't be unset here because that would break * being able to read the request body. * Processing of chunked request bodies is not impacted by this * filter since the the length was not declared anyway. */ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, f->r, "Request body length may change, resulting in " "misprocessing by some modules or scripts"); } } if (ctx->noop) { return ap_get_brigade(f->next, bb, mode, block, readbytes); } if (APR_BRIGADE_EMPTY(ctx->bb)) { if ((rv = ap_get_brigade(f->next, bb, mode, block, readbytes)) != APR_SUCCESS) { return rv; } } else { APR_BRIGADE_PREPEND(bb, ctx->bb); /* first use the leftovers */ } buffer_size = INPUT_XLATE_BUF_SIZE; rv = xlate_brigade(ctx, bb, ctx->tmp, &buffer_size, &hit_eos); if (rv == APR_SUCCESS) { if (!hit_eos) { /* move anything leftover into our context for next time; * we don't currently "set aside" since the data came from * down below, but I suspect that for long-term we need to * do that */ APR_BRIGADE_CONCAT(ctx->bb, bb); } if (buffer_size < INPUT_XLATE_BUF_SIZE) { /* do we have output? */ apr_bucket *e; e = apr_bucket_heap_create(ctx->tmp, INPUT_XLATE_BUF_SIZE - buffer_size, NULL, f->r->connection->bucket_alloc); /* make sure we insert at the head, because there may be * an eos bucket already there, and the eos bucket should * come after the data */ APR_BRIGADE_INSERT_HEAD(bb, e); } else { /* XXX need to get some more data... what if the last brigade * we got had only the first byte of a multibyte char? we need * to grab more data from the network instead of returning an * empty brigade */ } /* If we have any metadata at the head of ctx->bb, go ahead and move it * onto the end of bb to be returned to our caller. */ if (!APR_BRIGADE_EMPTY(ctx->bb)) { apr_bucket *b = APR_BRIGADE_FIRST(ctx->bb); while (b != APR_BRIGADE_SENTINEL(ctx->bb) && APR_BUCKET_IS_METADATA(b)) { APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(bb, b); b = APR_BRIGADE_FIRST(ctx->bb); } } } else { log_xlate_error(f, rv); } return rv; }
static apr_status_t h2_filter_slave_in(ap_filter_t* f, apr_bucket_brigade* bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { h2_task *task; apr_status_t status = APR_SUCCESS; apr_bucket *b, *next; apr_off_t bblen; const int trace1 = APLOGctrace1(f->c); apr_size_t rmax = ((readbytes <= APR_SIZE_MAX)? (apr_size_t)readbytes : APR_SIZE_MAX); task = h2_ctx_cget_task(f->c); ap_assert(task); if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "h2_slave_in(%s): read, mode=%d, block=%d, readbytes=%ld", task->id, mode, block, (long)readbytes); } if (mode == AP_MODE_INIT) { return ap_get_brigade(f->c->input_filters, bb, mode, block, readbytes); } if (f->c->aborted) { return APR_ECONNABORTED; } if (!task->input.bb) { return APR_EOF; } /* Cleanup brigades from those nasty 0 length non-meta buckets * that apr_brigade_split_line() sometimes produces. */ for (b = APR_BRIGADE_FIRST(task->input.bb); b != APR_BRIGADE_SENTINEL(task->input.bb); b = next) { next = APR_BUCKET_NEXT(b); if (b->length == 0 && !APR_BUCKET_IS_METADATA(b)) { apr_bucket_delete(b); } } while (APR_BRIGADE_EMPTY(task->input.bb)) { /* Get more input data for our request. */ if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_slave_in(%s): get more data from mplx, block=%d, " "readbytes=%ld", task->id, block, (long)readbytes); } if (task->input.beam) { status = h2_beam_receive(task->input.beam, task->input.bb, block, 128*1024); } else { status = APR_EOF; } if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, f->c, "h2_slave_in(%s): read returned", task->id); } if (APR_STATUS_IS_EAGAIN(status) && (mode == AP_MODE_GETLINE || block == APR_BLOCK_READ)) { /* chunked input handling does not seem to like it if we * return with APR_EAGAIN from a GETLINE read... * upload 100k test on test-ser.example.org hangs */ status = APR_SUCCESS; } else if (APR_STATUS_IS_EOF(status)) { break; } else if (status != APR_SUCCESS) { return status; } if (trace1) { h2_util_bb_log(f->c, task->stream_id, APLOG_TRACE2, "input.beam recv raw", task->input.bb); } if (h2_task_logio_add_bytes_in) { apr_brigade_length(bb, 0, &bblen); h2_task_logio_add_bytes_in(f->c, bblen); } } /* Nothing there, no more data to get. Return APR_EAGAIN on * speculative reads, this is ap_check_pipeline()'s trick to * see if the connection needs closing. */ if (status == APR_EOF && APR_BRIGADE_EMPTY(task->input.bb)) { return (mode == AP_MODE_SPECULATIVE)? APR_EAGAIN : APR_EOF; } if (trace1) { h2_util_bb_log(f->c, task->stream_id, APLOG_TRACE2, "task_input.bb", task->input.bb); } if (APR_BRIGADE_EMPTY(task->input.bb)) { if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c, "h2_slave_in(%s): no data", task->id); } return (block == APR_NONBLOCK_READ)? APR_EAGAIN : APR_EOF; } if (mode == AP_MODE_EXHAUSTIVE) { /* return all we have */ APR_BRIGADE_CONCAT(bb, task->input.bb); } else if (mode == AP_MODE_READBYTES) { status = h2_brigade_concat_length(bb, task->input.bb, rmax); } else if (mode == AP_MODE_SPECULATIVE) { status = h2_brigade_copy_length(bb, task->input.bb, rmax); } else if (mode == AP_MODE_GETLINE) { /* we are reading a single LF line, e.g. the HTTP headers. * this has the nasty side effect to split the bucket, even * though it ends with CRLF and creates a 0 length bucket */ status = apr_brigade_split_line(bb, task->input.bb, block, HUGE_STRING_LEN); if (APLOGctrace1(f->c)) { char buffer[1024]; apr_size_t len = sizeof(buffer)-1; apr_brigade_flatten(bb, buffer, &len); buffer[len] = 0; if (trace1) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_slave_in(%s): getline: %s", task->id, buffer); } } } else { /* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not * to support it. Seems to work. */ ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c, APLOGNO(03472) "h2_slave_in(%s), unsupported READ mode %d", task->id, mode); status = APR_ENOTIMPL; } if (trace1) { apr_brigade_length(bb, 0, &bblen); ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c, "h2_slave_in(%s): %ld data bytes", task->id, (long)bblen); } return status; }
static int xlate_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { apr_status_t rv; charset_req_t *reqinfo = ap_get_module_config(f->r->request_config, &charset_lite_module); charset_dir_t *dc = ap_get_module_config(f->r->per_dir_config, &charset_lite_module); charset_filter_ctx_t *ctx = f->ctx; apr_size_t buffer_size; int hit_eos; if (!ctx) { /* this is SetInputFilter path; grab the preallocated context, * if any; note that if we decided not to do anything in an earlier * handler, we won't even have a reqinfo */ if (reqinfo) { ctx = f->ctx = reqinfo->input_ctx; reqinfo->input_ctx = NULL; /* prevent SNAFU if user coded us twice * in the filter chain; we can't have two * instances using the same context */ } if (!ctx) { /* no idea how to translate; don't do anything */ ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(charset_filter_ctx_t)); ctx->dc = dc; ctx->noop = 1; } } if (dc->debug >= DBGLVL_GORY) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, "xlate_in_filter() - " "charset_source: %s charset_default: %s", dc && dc->charset_source ? dc->charset_source : "(none)", dc && dc->charset_default ? dc->charset_default : "(none)"); } if (!ctx->ran) { /* filter never ran before */ chk_filter_chain(f); ctx->ran = 1; } if (ctx->noop) { return ap_get_brigade(f->next, bb, mode, block, readbytes); } if (APR_BRIGADE_EMPTY(ctx->bb)) { if ((rv = ap_get_brigade(f->next, bb, mode, block, readbytes)) != APR_SUCCESS) { return rv; } } else { APR_BRIGADE_PREPEND(bb, ctx->bb); /* first use the leftovers */ } buffer_size = INPUT_XLATE_BUF_SIZE; rv = xlate_brigade(ctx, bb, ctx->tmp, &buffer_size, &hit_eos); if (rv == APR_SUCCESS) { if (!hit_eos) { /* move anything leftover into our context for next time; * we don't currently "set aside" since the data came from * down below, but I suspect that for long-term we need to * do that */ APR_BRIGADE_CONCAT(ctx->bb, bb); } if (buffer_size < INPUT_XLATE_BUF_SIZE) { /* do we have output? */ apr_bucket *e; e = apr_bucket_heap_create(ctx->tmp, INPUT_XLATE_BUF_SIZE - buffer_size, NULL, f->r->connection->bucket_alloc); /* make sure we insert at the head, because there may be * an eos bucket already there, and the eos bucket should * come after the data */ APR_BRIGADE_INSERT_HEAD(bb, e); } else { /* XXX need to get some more data... what if the last brigade * we got had only the first byte of a multibyte char? we need * to grab more data from the network instead of returning an * empty brigade */ } } else { log_xlate_error(f, rv); } return rv; }