📄 http_filters.c
字号:
* we don't want to send out 0 Content-Lengths if it is a head request. * This happens when modules try to outsmart the server, and return * if they see a HEAD request. Apache 1.3 handlers were supposed to * just return in that situation, and the core handled the HEAD. In * 2.0, if a handler returns, then the core sends an EOS bucket down * the filter stack, and the content-length filter computes a C-L of * zero and that gets put in the headers, and we end up sending a * zero C-L to the client. We can't just remove the C-L filter, * because well behaved 2.0 handlers will send their data down the stack, * and we will compute a real C-L for the head request. RBB */ if (r->header_only && (clheader = apr_table_get(r->headers_out, "Content-Length")) && !strcmp(clheader, "0")) { apr_table_unset(r->headers_out, "Content-Length"); } b2 = apr_brigade_create(r->pool, c->bucket_alloc); basic_http_header(r, b2, protocol); h.pool = r->pool; h.bb = b2; if (r->status == HTTP_NOT_MODIFIED) { apr_table_do((int (*)(void *, const char *, const char *)) form_header_field, (void *) &h, r->headers_out, "Connection", "Keep-Alive", "ETag", "Content-Location", "Expires", "Cache-Control", "Vary", "Warning", "WWW-Authenticate", "Proxy-Authenticate", "Set-Cookie", "Set-Cookie2", NULL); } else { send_all_header_fields(&h, r); } terminate_header(b2); ap_pass_brigade(f->next, b2); if (r->header_only) { apr_brigade_destroy(b); ctx->headers_sent = 1; return OK; } r->sent_bodyct = 1; /* Whatever follows is real body stuff... */ if (r->chunked) { /* We can't add this filter until we have already sent the headers. * If we add it before this point, then the headers will be chunked * as well, and that is just wrong. */ ap_add_output_filter("CHUNK", NULL, r, r->connection); } /* Don't remove this filter until after we have added the CHUNK filter. * Otherwise, f->next won't be the CHUNK filter and thus the first * brigade won't be chunked properly. */ ap_remove_output_filter(f); return ap_pass_brigade(f->next, b);}/* In HTTP/1.1, any method can have a body. However, most GET handlers * wouldn't know what to do with a request body if they received one. * This helper routine tests for and reads any message body in the request, * simply discarding whatever it receives. We need to do this because * failing to read the request body would cause it to be interpreted * as the next request on a persistent connection. * * Since we return an error status if the request is malformed, this * routine should be called at the beginning of a no-body handler, e.g., * * if ((retval = ap_discard_request_body(r)) != OK) { * return retval; * } */AP_DECLARE(int) ap_discard_request_body(request_rec *r){ apr_bucket_brigade *bb; int rv, seen_eos; /* Sometimes we'll get in a state where the input handling has * detected an error where we want to drop the connection, so if * that's the case, don't read the data as that is what we're trying * to avoid. * * This function is also a no-op on a subrequest. */ if (r->main || r->connection->keepalive == AP_CONN_CLOSE || ap_status_drops_connection(r->status)) { return OK; } bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); seen_eos = 0; do { apr_bucket *bucket; rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN); if (rv != APR_SUCCESS) { /* FIXME: If we ever have a mapping from filters (apr_status_t) * to HTTP error codes, this would be a good place for them. * * If we received the special case AP_FILTER_ERROR, it means * that the filters have already handled this error. * Otherwise, we should assume we have a bad request. */ if (rv == AP_FILTER_ERROR) { apr_brigade_destroy(bb); return rv; } else { apr_brigade_destroy(bb); return HTTP_BAD_REQUEST; } } for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket = APR_BUCKET_NEXT(bucket)) { const char *data; apr_size_t len; if (APR_BUCKET_IS_EOS(bucket)) { seen_eos = 1; break; } /* These are metadata buckets. */ if (bucket->length == 0) { continue; } /* We MUST read because in case we have an unknown-length * bucket or one that morphs, we want to exhaust it. */ rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { apr_brigade_destroy(bb); return HTTP_BAD_REQUEST; } } apr_brigade_cleanup(bb); } while (!seen_eos); return OK;}/* Here we deal with getting the request message body from the client. * Whether or not the request contains a body is signaled by the presence * of a non-zero Content-Length or by a Transfer-Encoding: chunked. * * Note that this is more complicated than it was in Apache 1.1 and prior * versions, because chunked support means that the module does less. * * The proper procedure is this: * * 1. Call ap_setup_client_block() near the beginning of the request * handler. This will set up all the necessary properties, and will * return either OK, or an error code. If the latter, the module should * return that error code. The second parameter selects the policy to * apply if the request message indicates a body, and how a chunked * transfer-coding should be interpreted. Choose one of * * REQUEST_NO_BODY Send 413 error if message has any body * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me. * REQUEST_CHUNKED_PASS If chunked, pass the chunk headers with body. * * In order to use the last two options, the caller MUST provide a buffer * large enough to hold a chunk-size line, including any extensions. * * 2. When you are ready to read a body (if any), call ap_should_client_block(). * This will tell the module whether or not to read input. If it is 0, * the module should assume that there is no message body to read. * * 3. Finally, call ap_get_client_block in a loop. Pass it a buffer and its size. * It will put data into the buffer (not necessarily a full buffer), and * return the length of the input block. When it is done reading, it will * return 0 if EOF, or -1 if there was an error. * If an error occurs on input, we force an end to keepalive. * * This step also sends a 100 Continue response to HTTP/1.1 clients if appropriate. */AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy){ const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding"); const char *lenp = apr_table_get(r->headers_in, "Content-Length"); r->read_body = read_policy; r->read_chunked = 0; r->remaining = 0; if (tenc) { if (strcasecmp(tenc, "chunked")) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Unknown Transfer-Encoding %s", tenc); return HTTP_NOT_IMPLEMENTED; } if (r->read_body == REQUEST_CHUNKED_ERROR) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "chunked Transfer-Encoding forbidden: %s", r->uri); return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED; } r->read_chunked = 1; } else if (lenp) { char *endstr; if (apr_strtoff(&r->remaining, lenp, &endstr, 10) || *endstr || r->remaining < 0) { r->remaining = 0; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Invalid Content-Length"); return HTTP_BAD_REQUEST; } } if ((r->read_body == REQUEST_NO_BODY) && (r->read_chunked || (r->remaining > 0))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s with body is not allowed for %s", r->method, r->uri); return HTTP_REQUEST_ENTITY_TOO_LARGE; }#ifdef AP_DEBUG { /* Make sure ap_getline() didn't leave any droppings. */ core_request_config *req_cfg = (core_request_config *)ap_get_module_config(r->request_config, &core_module); AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(req_cfg->bb)); }#endif return OK;}AP_DECLARE(int) ap_should_client_block(request_rec *r){ /* First check if we have already read the request body */ if (r->read_length || (!r->read_chunked && (r->remaining <= 0))) { return 0; } return 1;}/* get_client_block is called in a loop to get the request message body. * This is quite simple if the client includes a content-length * (the normal case), but gets messy if the body is chunked. Note that * r->remaining is used to maintain state across calls and that * r->read_length is the total number of bytes given to the caller * across all invocations. It is messy because we have to be careful not * to read past the data provided by the client, since these reads block. * Returns 0 on End-of-body, -1 on error or premature chunk end. * */AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer, apr_size_t bufsiz){ apr_status_t rv; apr_bucket_brigade *bb; if (r->remaining < 0 || (!r->read_chunked && r->remaining == 0)) { return 0; } bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); if (bb == NULL) { r->connection->keepalive = AP_CONN_CLOSE; return -1; } rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, bufsiz); /* We lose the failure code here. This is why ap_get_client_block should * not be used. */ if (rv != APR_SUCCESS) { /* if we actually fail here, we want to just return and * stop trying to read data from the client. */ r->connection->keepalive = AP_CONN_CLOSE; apr_brigade_destroy(bb); return -1; } /* If this fails, it means that a filter is written incorrectly and that * it needs to learn how to properly handle APR_BLOCK_READ requests by * returning data when requested. */ AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(bb)); /* Check to see if EOS in the brigade. * * If so, we have to leave a nugget for the *next* ap_get_client_block * call to return 0. */ if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { if (r->read_chunked) { r->remaining = -1; } else { r->remaining = 0; } } rv = apr_brigade_flatten(bb, buffer, &bufsiz); if (rv != APR_SUCCESS) { apr_brigade_destroy(bb); return -1; } /* XXX yank me? */ r->read_length += bufsiz; apr_brigade_destroy(bb); return bufsiz;}/* Context struct for ap_http_outerror_filter */typedef struct { int seen_eoc;} outerror_filter_ctx_t;/* Filter to handle any error buckets on output */apr_status_t ap_http_outerror_filter(ap_filter_t *f, apr_bucket_brigade *b){ request_rec *r = f->r; outerror_filter_ctx_t *ctx = (outerror_filter_ctx_t *)(f->ctx); apr_bucket *e; /* Create context if none is present */ if (!ctx) { ctx = apr_pcalloc(r->pool, sizeof(outerror_filter_ctx_t)); f->ctx = ctx; } for (e = APR_BRIGADE_FIRST(b); e != APR_BRIGADE_SENTINEL(b); e = APR_BUCKET_NEXT(e)) { if (AP_BUCKET_IS_ERROR(e)) { /* * Start of error handling state tree. Just one condition * right now :) */ if (((ap_bucket_error *)(e->data))->status == HTTP_BAD_GATEWAY) { /* stream aborted and we have not ended it yet */ r->connection->keepalive = AP_CONN_CLOSE; } continue; } /* Detect EOC buckets and memorize this in the context. */ if (AP_BUCKET_IS_EOC(e)) { ctx->seen_eoc = 1; } } /* * Remove all data buckets that are in a brigade after an EOC bucket * was seen, as an EOC bucket tells us that no (further) resource * and protocol data should go out to the client. OTOH meta buckets * are still welcome as they might trigger needed actions down in * the chain (e.g. in network filters like SSL). * Remark 1: It is needed to dump ALL data buckets in the brigade * since an filter in between might have inserted data * buckets BEFORE the EOC bucket sent by the original * sender and we do NOT want this data to be sent. * Remark 2: Dumping all data buckets here does not necessarily mean * that no further data is send to the client as: * 1. Network filters like SSL can still be triggered via * meta buckets to talk with the client e.g. for a * clean shutdown. * 2. There could be still data that was buffered before * down in the chain that gets flushed by a FLUSH or an * EOS bucket. */ if (ctx->seen_eoc) { for (e = APR_BRIGADE_FIRST(b); e != APR_BRIGADE_SENTINEL(b); e = APR_BUCKET_NEXT(e)) { if (!APR_BUCKET_IS_METADATA(e)) { APR_BUCKET_REMOVE(e); } } } return ap_pass_brigade(f->next, b);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -