⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 http_filters.c

📁 Apache官方在今天放出产品系列2.2的最新版本2.2.11的源码包 最流行的HTTP服务器软件之一
💻 C
📖 第 1 页 / 共 4 页
字号:
     * 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 + -