/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * http_filter.c --- HTTP routines which either filters or deal with filters. */ #include "apr.h" #include "apr_strings.h" #include "apr_buckets.h" #include "apr_lib.h" #include "apr_signal.h" #define APR_WANT_STDIO /* for sscanf */ #define APR_WANT_STRFUNC #define APR_WANT_MEMFUNC #include "apr_want.h" #include "util_filter.h" #include "ap_config.h" #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_protocol.h" #include "http_main.h" #include "http_request.h" #include "http_vhost.h" #include "http_connection.h" #include "http_log.h" /* For errors detected in basic auth common * support code... */ #include "apr_date.h" /* For apr_date_parse_http and APR_DATE_BAD */ #include "util_charset.h" #include "util_ebcdic.h" #include "util_time.h" #include "mod_core.h" #if APR_HAVE_STDARG_H #include #endif #if APR_HAVE_UNISTD_H #include #endif APLOG_USE_MODULE(http); typedef struct http_filter_ctx { apr_off_t remaining; apr_off_t limit; apr_off_t limit_used; apr_int32_t chunk_used; apr_int32_t chunk_bws; apr_int32_t chunkbits; enum { BODY_NONE, /* streamed data */ BODY_LENGTH, /* data constrained by content length */ BODY_CHUNK, /* chunk expected */ BODY_CHUNK_PART, /* chunk digits */ BODY_CHUNK_EXT, /* chunk extension */ BODY_CHUNK_CR, /* got space(s) after digits, expect [CR]LF or ext */ BODY_CHUNK_LF, /* got CR after digits or ext, expect LF */ BODY_CHUNK_DATA, /* data constrained by chunked encoding */ BODY_CHUNK_END, /* chunked data terminating CRLF */ BODY_CHUNK_END_LF, /* got CR after data, expect LF */ BODY_CHUNK_TRAILER /* trailers */ } state; unsigned int eos_sent :1; apr_bucket_brigade *bb; } http_ctx_t; /* bail out if some error in the HTTP input filter happens */ static apr_status_t bail_out_on_error(http_ctx_t *ctx, ap_filter_t *f, int http_error) { apr_bucket *e; apr_bucket_brigade *bb = ctx->bb; apr_brigade_cleanup(bb); if (f->r->proxyreq == PROXYREQ_RESPONSE) { switch (http_error) { case HTTP_REQUEST_ENTITY_TOO_LARGE: return APR_ENOSPC; case HTTP_REQUEST_TIME_OUT: return APR_INCOMPLETE; case HTTP_NOT_IMPLEMENTED: return APR_ENOTIMPL; default: return APR_EGENERAL; } } e = ap_bucket_error_create(http_error, NULL, f->r->pool, f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); ctx->eos_sent = 1; /* If chunked encoding / content-length are corrupt, we may treat parts * of this request's body as the next one's headers. * To be safe, disable keep-alive. */ f->r->connection->keepalive = AP_CONN_CLOSE; return ap_pass_brigade(f->r->output_filters, bb); } /** * Parse a chunk line with optional extension, detect overflow. * There are several error cases: * 1) If the chunk link is misformatted, APR_EINVAL is returned. * 2) If the conversion would require too many bits, APR_EGENERAL is returned. * 3) If the conversion used the correct number of bits, but an overflow * caused only the sign bit to flip, then APR_ENOSPC is returned. * A negative chunk length always indicates an overflow error. */ static apr_status_t parse_chunk_size(http_ctx_t *ctx, const char *buffer, apr_size_t len, int linelimit, int strict) { apr_size_t i = 0; while (i < len) { char c = buffer[i]; ap_xlate_proto_from_ascii(&c, 1); /* handle CRLF after the chunk */ if (ctx->state == BODY_CHUNK_END || ctx->state == BODY_CHUNK_END_LF) { if (c == LF) { if (strict && (ctx->state != BODY_CHUNK_END_LF)) { /* * CR missing before LF. */ return APR_EINVAL; } ctx->state = BODY_CHUNK; } else if (c == CR && ctx->state == BODY_CHUNK_END) { ctx->state = BODY_CHUNK_END_LF; } else { /* * CRLF expected. */ return APR_EINVAL; } i++; continue; } /* handle start of the chunk */ if (ctx->state == BODY_CHUNK) { if (!apr_isxdigit(c)) { /* * Detect invalid character at beginning. This also works for * empty chunk size lines. */ return APR_EINVAL; } else { ctx->state = BODY_CHUNK_PART; } ctx->remaining = 0; ctx->chunkbits = sizeof(apr_off_t) * 8; ctx->chunk_used = 0; ctx->chunk_bws = 0; } if (c == LF) { if (strict && (ctx->state != BODY_CHUNK_LF)) { /* * CR missing before LF. */ return APR_EINVAL; } if (ctx->remaining) { ctx->state = BODY_CHUNK_DATA; } else { ctx->state = BODY_CHUNK_TRAILER; } } else if (ctx->state == BODY_CHUNK_LF) { /* * LF expected. */ return APR_EINVAL; } else if (c == CR) { ctx->state = BODY_CHUNK_LF; } else if (c == ';') { ctx->state = BODY_CHUNK_EXT; } else if (ctx->state == BODY_CHUNK_EXT) { /* * Control chars (excluding tabs) are invalid. * TODO: more precisely limit input */ if (c != '\t' && apr_iscntrl(c)) { return APR_EINVAL; } } else if (c == ' ' || c == '\t') { /* Be lenient up to 10 implied *LWS, a legacy of RFC 2616, * and noted as errata to RFC7230; * https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667 */ ctx->state = BODY_CHUNK_CR; if (++ctx->chunk_bws > 10) { return APR_EINVAL; } } else if (ctx->state == BODY_CHUNK_CR) { /* * ';', CR or LF expected. */ return APR_EINVAL; } else if (ctx->state == BODY_CHUNK_PART) { int xvalue; /* ignore leading zeros */ if (!ctx->remaining && c == '0') { i++; continue; } ctx->chunkbits -= 4; if (ctx->chunkbits < 0) { /* overflow */ return APR_ENOSPC; } if (c >= '0' && c <= '9') { xvalue = c - '0'; } else if (c >= 'A' && c <= 'F') { xvalue = c - 'A' + 0xa; } else if (c >= 'a' && c <= 'f') { xvalue = c - 'a' + 0xa; } else { /* bogus character */ return APR_EINVAL; } ctx->remaining = (ctx->remaining << 4) | xvalue; if (ctx->remaining < 0) { /* overflow */ return APR_ENOSPC; } } else { /* Should not happen */ return APR_EGENERAL; } i++; } /* sanity check */ ctx->chunk_used += len; if (ctx->chunk_used < 0 || ctx->chunk_used > linelimit) { return APR_ENOSPC; } return APR_SUCCESS; } static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f, apr_bucket_brigade *b, int merge) { int rv; apr_bucket *e; request_rec *r = f->r; apr_table_t *saved_headers_in = r->headers_in; int saved_status = r->status; r->status = HTTP_OK; r->headers_in = r->trailers_in; apr_table_clear(r->headers_in); ap_get_mime_headers(r); if(r->status == HTTP_OK) { r->status = saved_status; e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); ctx->eos_sent = 1; rv = APR_SUCCESS; } else { const char *error_notes = apr_table_get(r->notes, "error-notes"); ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(02656) "Error while reading HTTP trailer: %i%s%s", r->status, error_notes ? ": " : "", error_notes ? error_notes : ""); rv = APR_EINVAL; } if(!merge) { r->headers_in = saved_headers_in; } else { r->headers_in = apr_table_overlay(r->pool, saved_headers_in, r->trailers_in); } return rv; } /* This is the HTTP_INPUT filter for HTTP requests and responses from * proxied servers (mod_proxy). It handles chunked and content-length * bodies. This can only be inserted/used after the headers * are successfully parsed. */ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) { core_server_config *conf = (core_server_config *) ap_get_module_config(f->r->server->module_config, &core_module); int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE); apr_bucket *e; http_ctx_t *ctx = f->ctx; apr_status_t rv; int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE; apr_bucket_brigade *bb; int again; /* just get out of the way of things we don't want. */ if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) { return ap_get_brigade(f->next, b, mode, block, readbytes); } if (!ctx) { const char *tenc, *lenp; f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); ctx->state = BODY_NONE; ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); bb = ctx->bb; /* LimitRequestBody does not apply to proxied responses. * Consider implementing this check in its own filter. * Would adding a directive to limit the size of proxied * responses be useful? */ if (!f->r->proxyreq) { ctx->limit = ap_get_limit_req_body(f->r); } else { ctx->limit = 0; } tenc = apr_table_get(f->r->headers_in, "Transfer-Encoding"); lenp = apr_table_get(f->r->headers_in, "Content-Length"); if (tenc) { if (strcasecmp(tenc, "chunked") == 0 /* fast path */ || ap_find_last_token(f->r->pool, tenc, "chunked")) { ctx->state = BODY_CHUNK; } else if (f->r->proxyreq == PROXYREQ_RESPONSE) { /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23 * Section 3.3.3.3: "If a Transfer-Encoding header field is * present in a response and the chunked transfer coding is not * the final encoding, the message body length is determined by * reading the connection until it is closed by the server." */ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(02555) "Unknown Transfer-Encoding: %s; " "using read-until-close", tenc); tenc = NULL; } else { /* Something that isn't a HTTP request, unless some future * edition defines new transfer encodings, is unsupported. */ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01585) "Unknown Transfer-Encoding: %s", tenc); return bail_out_on_error(ctx, f, HTTP_BAD_REQUEST); } lenp = NULL; } if (lenp) { char *endstr; ctx->state = BODY_LENGTH; /* Protects against over/underflow, non-digit chars in the * string (excluding leading space) (the endstr checks) * and a negative number. */ if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10) || endstr == lenp || *endstr || ctx->remaining < 0) { ctx->remaining = 0; ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01587) "Invalid Content-Length"); return bail_out_on_error(ctx, f, HTTP_BAD_REQUEST); } /* If we have a limit in effect and we know the C-L ahead of * time, stop it here if it is invalid. */ if (ctx->limit && ctx->limit < ctx->remaining) { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01588) "Requested content-length of %" APR_OFF_T_FMT " is larger than the configured limit" " of %" APR_OFF_T_FMT, ctx->remaining, ctx->limit); return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE); } } /* If we don't have a request entity indicated by the headers, EOS. * (BODY_NONE is a valid intermediate state due to trailers, * but it isn't a valid starting state.) * * RFC 2616 Section 4.4 note 5 states that connection-close * is invalid for a request entity - request bodies must be * denoted by C-L or T-E: chunked. * * Note that since the proxy uses this filter to handle the * proxied *response*, proxy responses MUST be exempt. */ if (ctx->state == BODY_NONE && f->r->proxyreq != PROXYREQ_RESPONSE) { e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); ctx->eos_sent = 1; return APR_SUCCESS; } /* Since we're about to read data, send 100-Continue if needed. * Only valid on chunked and C-L bodies where the C-L is > 0. */ if ((ctx->state == BODY_CHUNK || (ctx->state == BODY_LENGTH && ctx->remaining > 0)) && f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1) && !(f->r->eos_sent || f->r->bytes_sent)) { if (!ap_is_HTTP_SUCCESS(f->r->status)) { ctx->state = BODY_NONE; ctx->eos_sent = 1; } else { char *tmp; int len; /* if we send an interim response, we're no longer * in a state of expecting one. */ f->r->expecting_100 = 0; tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL " ", ap_get_status_line(HTTP_CONTINUE), CRLF CRLF, NULL); len = strlen(tmp); ap_xlate_proto_to_ascii(tmp, len); apr_brigade_cleanup(bb); e = apr_bucket_pool_create(tmp, len, f->r->pool, f->c->bucket_alloc); APR_BRIGADE_INSERT_HEAD(bb, e); e = apr_bucket_flush_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); rv = ap_pass_brigade(f->c->output_filters, bb); if (rv != APR_SUCCESS) { return AP_FILTER_ERROR; } } } } /* sanity check in case we're read twice */ if (ctx->eos_sent) { e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); return APR_SUCCESS; } do { apr_brigade_cleanup(b); again = 0; /* until further notice */ /* read and handle the brigade */ switch (ctx->state) { case BODY_CHUNK: case BODY_CHUNK_PART: case BODY_CHUNK_EXT: case BODY_CHUNK_CR: case BODY_CHUNK_LF: case BODY_CHUNK_END: case BODY_CHUNK_END_LF: { rv = ap_get_brigade(f->next, b, AP_MODE_GETLINE, block, 0); /* for timeout */ if (block == APR_NONBLOCK_READ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b)) || (APR_STATUS_IS_EAGAIN(rv)))) { return APR_EAGAIN; } if (rv == APR_EOF) { return APR_INCOMPLETE; } if (rv != APR_SUCCESS) { return rv; } e = APR_BRIGADE_FIRST(b); while (e != APR_BRIGADE_SENTINEL(b)) { const char *buffer; apr_size_t len; if (!APR_BUCKET_IS_METADATA(e)) { int parsing = 0; rv = apr_bucket_read(e, &buffer, &len, APR_BLOCK_READ); if (rv == APR_SUCCESS) { parsing = 1; rv = parse_chunk_size(ctx, buffer, len, f->r->server->limit_req_fieldsize, strict); } if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r, APLOGNO(01590) "Error reading/parsing chunk %s ", (APR_ENOSPC == rv) ? "(overflow)" : ""); if (parsing) { if (rv != APR_ENOSPC) { http_error = HTTP_BAD_REQUEST; } return bail_out_on_error(ctx, f, http_error); } return rv; } } apr_bucket_delete(e); e = APR_BRIGADE_FIRST(b); } again = 1; /* come around again */ if (ctx->state == BODY_CHUNK_TRAILER) { /* Treat UNSET as DISABLE - trailers aren't merged by default */ return read_chunked_trailers(ctx, f, b, conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE); } break; } case BODY_NONE: case BODY_LENGTH: case BODY_CHUNK_DATA: { /* Ensure that the caller can not go over our boundary point. */ if (ctx->state != BODY_NONE && ctx->remaining < readbytes) { readbytes = ctx->remaining; } if (readbytes > 0) { apr_off_t totalread; rv = ap_get_brigade(f->next, b, mode, block, readbytes); /* for timeout */ if (block == APR_NONBLOCK_READ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b)) || (APR_STATUS_IS_EAGAIN(rv)))) { return APR_EAGAIN; } if (rv == APR_EOF && ctx->state != BODY_NONE && ctx->remaining > 0) { return APR_INCOMPLETE; } if (rv != APR_SUCCESS) { return rv; } /* How many bytes did we just read? */ apr_brigade_length(b, 0, &totalread); /* If this happens, we have a bucket of unknown length. Die because * it means our assumptions have changed. */ AP_DEBUG_ASSERT(totalread >= 0); if (ctx->state != BODY_NONE) { ctx->remaining -= totalread; if (ctx->remaining > 0) { e = APR_BRIGADE_LAST(b); if (APR_BUCKET_IS_EOS(e)) { apr_bucket_delete(e); return APR_INCOMPLETE; } } else if (ctx->state == BODY_CHUNK_DATA) { /* next chunk please */ ctx->state = BODY_CHUNK_END; ctx->chunk_used = 0; } } /* We have a limit in effect. */ if (ctx->limit) { /* FIXME: Note that we might get slightly confused on * chunked inputs as we'd need to compensate for the chunk * lengths which may not really count. This seems to be up * for interpretation. */ ctx->limit_used += totalread; if (ctx->limit < ctx->limit_used) { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01591) "Read content length of " "%" APR_OFF_T_FMT " is larger than the " "configured limit of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit); return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE); } } } /* If we have no more bytes remaining on a C-L request, * save the caller a round trip to discover EOS. */ if (ctx->state == BODY_LENGTH && ctx->remaining == 0) { e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); ctx->eos_sent = 1; } break; } case BODY_CHUNK_TRAILER: { rv = ap_get_brigade(f->next, b, mode, block, readbytes); /* for timeout */ if (block == APR_NONBLOCK_READ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b)) || (APR_STATUS_IS_EAGAIN(rv)))) { return APR_EAGAIN; } if (rv != APR_SUCCESS) { return rv; } break; } default: { /* Should not happen */ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(02901) "Unexpected body state (%i)", (int)ctx->state); return APR_EGENERAL; } } } while (again); return APR_SUCCESS; } struct check_header_ctx { request_rec *r; int strict; }; /* check a single header, to be used with apr_table_do() */ static int check_header(struct check_header_ctx *ctx, const char *name, const char **val) { const char *pos, *end; char *dst = NULL; if (name[0] == '\0') { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02428) "Empty response header name, aborting request"); return 0; } if (ctx->strict) { end = ap_scan_http_token(name); } else { end = ap_scan_vchar_obstext(name); } if (*end) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02429) "Response header name '%s' contains invalid " "characters, aborting request", name); return 0; } for (pos = *val; *pos; pos = end) { end = ap_scan_http_field_content(pos); if (*end) { if (end[0] != CR || end[1] != LF || (end[2] != ' ' && end[2] != '\t')) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, APLOGNO(02430) "Response header '%s' value of '%s' contains " "invalid characters, aborting request", name, pos); return 0; } if (!dst) { *val = dst = apr_palloc(ctx->r->pool, strlen(*val) + 1); } } if (dst) { memcpy(dst, pos, end - pos); dst += end - pos; if (*end) { /* skip folding and replace with a single space */ end += 3 + strspn(end + 3, "\t "); *dst++ = ' '; } } } if (dst) { *dst = '\0'; } return 1; } static int check_headers_table(apr_table_t *t, struct check_header_ctx *ctx) { const apr_array_header_t *headers = apr_table_elts(t); apr_table_entry_t *header; int i; for (i = 0; i < headers->nelts; ++i) { header = &APR_ARRAY_IDX(headers, i, apr_table_entry_t); if (!header->key) { continue; } if (!check_header(ctx, header->key, (const char **)&header->val)) { return 0; } } return 1; } /** * Check headers for HTTP conformance * @return 1 if ok, 0 if bad */ static APR_INLINE int check_headers(request_rec *r) { struct check_header_ctx ctx; core_server_config *conf = ap_get_core_module_config(r->server->module_config); ctx.r = r; ctx.strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE); return check_headers_table(r->headers_out, &ctx) && check_headers_table(r->err_headers_out, &ctx); } static int check_headers_recursion(request_rec *r) { void *check = NULL; apr_pool_userdata_get(&check, "check_headers_recursion", r->pool); if (check) { return 1; } apr_pool_userdata_setn("true", "check_headers_recursion", NULL, r->pool); return 0; } typedef struct header_struct { apr_pool_t *pool; apr_bucket_brigade *bb; } header_struct; /* Send a single HTTP header field to the client. Note that this function * is used in calls to apr_table_do(), so don't change its interface. * It returns true unless there was a write error of some kind. */ static int form_header_field(header_struct *h, const char *fieldname, const char *fieldval) { #if APR_CHARSET_EBCDIC char *headfield; apr_size_t len; headfield = apr_pstrcat(h->pool, fieldname, ": ", fieldval, CRLF, NULL); len = strlen(headfield); ap_xlate_proto_to_ascii(headfield, len); apr_brigade_write(h->bb, NULL, NULL, headfield, len); #else struct iovec vec[4]; struct iovec *v = vec; v->iov_base = (void *)fieldname; v->iov_len = strlen(fieldname); v++; v->iov_base = ": "; v->iov_len = sizeof(": ") - 1; v++; v->iov_base = (void *)fieldval; v->iov_len = strlen(fieldval); v++; v->iov_base = CRLF; v->iov_len = sizeof(CRLF) - 1; apr_brigade_writev(h->bb, NULL, NULL, vec, 4); #endif /* !APR_CHARSET_EBCDIC */ return 1; } /* This routine is called by apr_table_do and merges all instances of * the passed field values into a single array that will be further * processed by some later routine. Originally intended to help split * and recombine multiple Vary fields, though it is generic to any field * consisting of comma/space-separated tokens. */ static int uniq_field_values(void *d, const char *key, const char *val) { apr_array_header_t *values; char *start; char *e; char **strpp; int i; values = (apr_array_header_t *)d; e = apr_pstrdup(values->pool, val); do { /* Find a non-empty fieldname */ while (*e == ',' || apr_isspace(*e)) { ++e; } if (*e == '\0') { break; } start = e; while (*e != '\0' && *e != ',' && !apr_isspace(*e)) { ++e; } if (*e != '\0') { *e++ = '\0'; } /* Now add it to values if it isn't already represented. * Could be replaced by a ap_array_strcasecmp() if we had one. */ for (i = 0, strpp = (char **) values->elts; i < values->nelts; ++i, ++strpp) { if (*strpp && strcasecmp(*strpp, start) == 0) { break; } } if (i == values->nelts) { /* if not found */ *(char **)apr_array_push(values) = start; } } while (*e != '\0'); return 1; } /* * Since some clients choke violently on multiple Vary fields, or * Vary fields with duplicate tokens, combine any multiples and remove * any duplicates. */ static void fixup_vary(request_rec *r) { apr_array_header_t *varies; varies = apr_array_make(r->pool, 5, sizeof(char *)); /* Extract all Vary fields from the headers_out, separate each into * its comma-separated fieldname values, and then add them to varies * if not already present in the array. */ apr_table_do(uniq_field_values, varies, r->headers_out, "Vary", NULL); /* If we found any, replace old Vary fields with unique-ified value */ if (varies->nelts > 0) { apr_table_setn(r->headers_out, "Vary", apr_array_pstrcat(r->pool, varies, ',')); } } /* Send a request's HTTP response headers to the client. */ static apr_status_t send_all_header_fields(header_struct *h, const request_rec *r) { const apr_array_header_t *elts; const apr_table_entry_t *t_elt; const apr_table_entry_t *t_end; struct iovec *vec; struct iovec *vec_next; elts = apr_table_elts(r->headers_out); if (elts->nelts == 0) { return APR_SUCCESS; } t_elt = (const apr_table_entry_t *)(elts->elts); t_end = t_elt + elts->nelts; vec = (struct iovec *)apr_palloc(h->pool, 4 * elts->nelts * sizeof(struct iovec)); vec_next = vec; /* For each field, generate * name ": " value CRLF */ do { vec_next->iov_base = (void*)(t_elt->key); vec_next->iov_len = strlen(t_elt->key); vec_next++; vec_next->iov_base = ": "; vec_next->iov_len = sizeof(": ") - 1; vec_next++; vec_next->iov_base = (void*)(t_elt->val); vec_next->iov_len = strlen(t_elt->val); vec_next++; vec_next->iov_base = CRLF; vec_next->iov_len = sizeof(CRLF) - 1; vec_next++; t_elt++; } while (t_elt < t_end); if (APLOGrtrace4(r)) { t_elt = (const apr_table_entry_t *)(elts->elts); do { ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, " %s: %s", ap_escape_logitem(r->pool, t_elt->key), ap_escape_logitem(r->pool, t_elt->val)); t_elt++; } while (t_elt < t_end); } #if APR_CHARSET_EBCDIC { apr_size_t len; char *tmp = apr_pstrcatv(r->pool, vec, vec_next - vec, &len); ap_xlate_proto_to_ascii(tmp, len); return apr_brigade_write(h->bb, NULL, NULL, tmp, len); } #else return apr_brigade_writev(h->bb, NULL, NULL, vec, vec_next - vec); #endif } /* Confirm that the status line is well-formed and matches r->status. * If they don't match, a filter may have negated the status line set by a * handler. * Zap r->status_line if bad. */ static apr_status_t validate_status_line(request_rec *r) { char *end; if (r->status_line) { int len = strlen(r->status_line); if (len < 3 || apr_strtoi64(r->status_line, &end, 10) != r->status || (end - 3) != r->status_line || (len >= 4 && ! apr_isspace(r->status_line[3]))) { r->status_line = NULL; return APR_EGENERAL; } /* Since we passed the above check, we know that length three * is equivalent to only a 3 digit numeric http status. * RFC2616 mandates a trailing space, let's add it. */ if (len == 3) { r->status_line = apr_pstrcat(r->pool, r->status_line, " ", NULL); return APR_EGENERAL; } return APR_SUCCESS; } return APR_EGENERAL; } /* * Determine the protocol to use for the response. Potentially downgrade * to HTTP/1.0 in some situations and/or turn off keepalives. * * also prepare r->status_line. */ static void basic_http_header_check(request_rec *r, const char **protocol) { apr_status_t rv; if (r->assbackwards) { /* no such thing as a response protocol */ return; } rv = validate_status_line(r); if (!r->status_line) { r->status_line = ap_get_status_line(r->status); } else if (rv != APR_SUCCESS) { /* Status line is OK but our own reason phrase * would be preferred if defined */ const char *tmp = ap_get_status_line(r->status); if (!strncmp(tmp, r->status_line, 3)) { r->status_line = tmp; } } /* Note that we must downgrade before checking for force responses. */ if (r->proto_num > HTTP_VERSION(1,0) && apr_table_get(r->subprocess_env, "downgrade-1.0")) { r->proto_num = HTTP_VERSION(1,0); } /* kludge around broken browsers when indicated by force-response-1.0 */ if (r->proto_num == HTTP_VERSION(1,0) && apr_table_get(r->subprocess_env, "force-response-1.0")) { *protocol = "HTTP/1.0"; r->connection->keepalive = AP_CONN_CLOSE; } else { *protocol = AP_SERVER_PROTOCOL; } } /* fill "bb" with a barebones/initial HTTP response header */ static void basic_http_header(request_rec *r, apr_bucket_brigade *bb, const char *protocol) { char *date = NULL; const char *proxy_date = NULL; const char *server = NULL; const char *us = ap_get_server_banner(); header_struct h; struct iovec vec[4]; if (r->assbackwards) { /* there are no headers to send */ return; } /* Output the HTTP/1.x Status-Line and the Date and Server fields */ vec[0].iov_base = (void *)protocol; vec[0].iov_len = strlen(protocol); vec[1].iov_base = (void *)" "; vec[1].iov_len = sizeof(" ") - 1; vec[2].iov_base = (void *)(r->status_line); vec[2].iov_len = strlen(r->status_line); vec[3].iov_base = (void *)CRLF; vec[3].iov_len = sizeof(CRLF) - 1; #if APR_CHARSET_EBCDIC { char *tmp; apr_size_t len; tmp = apr_pstrcatv(r->pool, vec, 4, &len); ap_xlate_proto_to_ascii(tmp, len); apr_brigade_write(bb, NULL, NULL, tmp, len); } #else apr_brigade_writev(bb, NULL, NULL, vec, 4); #endif h.pool = r->pool; h.bb = bb; /* * keep the set-by-proxy server and date headers, otherwise * generate a new server header / date header */ if (r->proxyreq != PROXYREQ_NONE) { proxy_date = apr_table_get(r->headers_out, "Date"); if (!proxy_date) { /* * proxy_date needs to be const. So use date for the creation of * our own Date header and pass it over to proxy_date later to * avoid a compiler warning. */ date = apr_palloc(r->pool, APR_RFC822_DATE_LEN); ap_recent_rfc822_date(date, r->request_time); } server = apr_table_get(r->headers_out, "Server"); } else { date = apr_palloc(r->pool, APR_RFC822_DATE_LEN); ap_recent_rfc822_date(date, r->request_time); } form_header_field(&h, "Date", proxy_date ? proxy_date : date ); if (!server && *us) server = us; if (server) form_header_field(&h, "Server", server); if (APLOGrtrace3(r)) { ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "Response sent with status %d%s", r->status, APLOGrtrace4(r) ? ", headers:" : ""); /* * Date and Server are less interesting, use TRACE5 for them while * using TRACE4 for the other headers. */ ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, " Date: %s", proxy_date ? proxy_date : date ); if (server) ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, " Server: %s", server); } /* unset so we don't send them again */ apr_table_unset(r->headers_out, "Date"); /* Avoid bogosity */ if (server) { apr_table_unset(r->headers_out, "Server"); } } AP_DECLARE(void) ap_basic_http_header(request_rec *r, apr_bucket_brigade *bb) { const char *protocol = NULL; basic_http_header_check(r, &protocol); basic_http_header(r, bb, protocol); } static void terminate_header(apr_bucket_brigade *bb) { char crlf[] = CRLF; apr_size_t buflen; buflen = strlen(crlf); ap_xlate_proto_to_ascii(crlf, buflen); apr_brigade_write(bb, NULL, NULL, crlf, buflen); } AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r) { core_server_config *conf; int rv; apr_bucket_brigade *bb; header_struct h; apr_bucket *b; int body; char *bodyread = NULL, *bodyoff; apr_size_t bodylen = 0; apr_size_t bodybuf; long res = -1; /* init to avoid gcc -Wall warning */ if (r->method_number != M_TRACE) { return DECLINED; } /* Get the original request */ while (r->prev) { r = r->prev; } conf = ap_get_core_module_config(r->server->module_config); if (conf->trace_enable == AP_TRACE_DISABLE) { apr_table_setn(r->notes, "error-notes", "TRACE denied by server configuration"); return HTTP_METHOD_NOT_ALLOWED; } if (conf->trace_enable == AP_TRACE_EXTENDED) /* XXX: should be = REQUEST_CHUNKED_PASS */ body = REQUEST_CHUNKED_DECHUNK; else body = REQUEST_NO_BODY; if ((rv = ap_setup_client_block(r, body))) { if (rv == HTTP_REQUEST_ENTITY_TOO_LARGE) apr_table_setn(r->notes, "error-notes", "TRACE with a request body is not allowed"); return rv; } if (ap_should_client_block(r)) { if (r->remaining > 0) { if (r->remaining > 65536) { apr_table_setn(r->notes, "error-notes", "Extended TRACE request bodies cannot exceed 64k\n"); return HTTP_REQUEST_ENTITY_TOO_LARGE; } /* always 32 extra bytes to catch chunk header exceptions */ bodybuf = (apr_size_t)r->remaining + 32; } else { /* Add an extra 8192 for chunk headers */ bodybuf = 73730; } bodyoff = bodyread = apr_palloc(r->pool, bodybuf); /* only while we have enough for a chunked header */ while ((!bodylen || bodybuf >= 32) && (res = ap_get_client_block(r, bodyoff, bodybuf)) > 0) { bodylen += res; bodybuf -= res; bodyoff += res; } if (res > 0 && bodybuf < 32) { /* discard_rest_of_request_body into our buffer */ while (ap_get_client_block(r, bodyread, bodylen) > 0) ; apr_table_setn(r->notes, "error-notes", "Extended TRACE request bodies cannot exceed 64k\n"); return HTTP_REQUEST_ENTITY_TOO_LARGE; } if (res < 0) { return HTTP_BAD_REQUEST; } } ap_set_content_type(r, "message/http"); /* Now we recreate the request, and echo it back */ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); #if APR_CHARSET_EBCDIC { char *tmp; apr_size_t len; len = strlen(r->the_request); tmp = apr_pmemdup(r->pool, r->the_request, len); ap_xlate_proto_to_ascii(tmp, len); apr_brigade_putstrs(bb, NULL, NULL, tmp, CRLF_ASCII, NULL); } #else apr_brigade_putstrs(bb, NULL, NULL, r->the_request, CRLF, NULL); #endif h.pool = r->pool; h.bb = bb; apr_table_do((int (*) (void *, const char *, const char *)) form_header_field, (void *) &h, r->headers_in, NULL); apr_brigade_puts(bb, NULL, NULL, CRLF_ASCII); /* If configured to accept a body, echo the body */ if (bodylen) { b = apr_bucket_pool_create(bodyread, bodylen, r->pool, bb->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); } ap_pass_brigade(r->output_filters, bb); return DONE; } typedef struct header_filter_ctx { int headers_sent; } header_filter_ctx; AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_bucket_brigade *b) { request_rec *r = f->r; conn_rec *c = r->connection; const char *clheader; const char *protocol = NULL; apr_bucket *e; apr_bucket_brigade *b2; header_struct h; header_filter_ctx *ctx = f->ctx; const char *ctype; ap_bucket_error *eb = NULL; apr_status_t rv = APR_SUCCESS; int recursive_error = 0; AP_DEBUG_ASSERT(!r->main); if (!ctx) { ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx)); } else if (ctx->headers_sent) { /* Eat body if response must not have one. */ if (r->header_only || r->status == HTTP_NO_CONTENT) { apr_brigade_cleanup(b); return APR_SUCCESS; } } for (e = APR_BRIGADE_FIRST(b); e != APR_BRIGADE_SENTINEL(b); e = APR_BUCKET_NEXT(e)) { if (AP_BUCKET_IS_ERROR(e) && !eb) { eb = e->data; continue; } /* * If we see an EOC bucket it is a signal that we should get out * of the way doing nothing. */ if (AP_BUCKET_IS_EOC(e)) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, b); } } if (!ctx->headers_sent && !check_headers(r)) { /* We may come back here from ap_die() below, * so clear anything from this response. */ apr_table_clear(r->headers_out); apr_table_clear(r->err_headers_out); apr_brigade_cleanup(b); /* Don't recall ap_die() if we come back here (from its own internal * redirect or error response), otherwise we can end up in infinite * recursion; better fall through with 500, minimal headers and an * empty body (EOS only). */ if (!check_headers_recursion(r)) { ap_die(HTTP_INTERNAL_SERVER_ERROR, r); return AP_FILTER_ERROR; } r->status = HTTP_INTERNAL_SERVER_ERROR; e = ap_bucket_eoc_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); e = apr_bucket_eos_create(c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); r->content_type = r->content_encoding = NULL; r->content_languages = NULL; ap_set_content_length(r, 0); recursive_error = 1; } else if (eb) { int status; status = eb->status; apr_brigade_cleanup(b); ap_die(status, r); return AP_FILTER_ERROR; } if (r->assbackwards) { r->sent_bodyct = 1; ap_remove_output_filter(f); rv = ap_pass_brigade(f->next, b); goto out; } /* * Now that we are ready to send a response, we need to combine the two * header field tables into a single table. If we don't do this, our * later attempts to set or unset a given fieldname might be bypassed. */ if (!apr_is_empty_table(r->err_headers_out)) { r->headers_out = apr_table_overlay(r->pool, r->err_headers_out, r->headers_out); } /* * Remove the 'Vary' header field if the client can't handle it. * Since this will have nasty effects on HTTP/1.1 caches, force * the response into HTTP/1.0 mode. * * Note: the force-response-1.0 should come before the call to * basic_http_header_check() */ if (apr_table_get(r->subprocess_env, "force-no-vary") != NULL) { apr_table_unset(r->headers_out, "Vary"); r->proto_num = HTTP_VERSION(1,0); apr_table_setn(r->subprocess_env, "force-response-1.0", "1"); } else { fixup_vary(r); } /* * Now remove any ETag response header field if earlier processing * says so (such as a 'FileETag None' directive). */ if (apr_table_get(r->notes, "no-etag") != NULL) { apr_table_unset(r->headers_out, "ETag"); } /* determine the protocol and whether we should use keepalives. */ basic_http_header_check(r, &protocol); ap_set_keepalive(r); if (r->chunked) { apr_table_mergen(r->headers_out, "Transfer-Encoding", "chunked"); apr_table_unset(r->headers_out, "Content-Length"); } if (r->status == HTTP_NO_CONTENT) { apr_table_unset(r->headers_out, "Content-Length"); } ctype = ap_make_content_type(r, r->content_type); if (ctype) { apr_table_setn(r->headers_out, "Content-Type", ctype); } if (r->content_encoding) { apr_table_setn(r->headers_out, "Content-Encoding", r->content_encoding); } if (!apr_is_empty_array(r->content_languages)) { int i; char *token; char **languages = (char **)(r->content_languages->elts); const char *field = apr_table_get(r->headers_out, "Content-Language"); while (field && (token = ap_get_list_item(r->pool, &field)) != NULL) { for (i = 0; i < r->content_languages->nelts; ++i) { if (!strcasecmp(token, languages[i])) break; } if (i == r->content_languages->nelts) { *((char **) apr_array_push(r->content_languages)) = token; } } field = apr_array_pstrcat(r->pool, r->content_languages, ','); apr_table_setn(r->headers_out, "Content-Language", field); } /* * Control cachability for non-cacheable responses if not already set by * some other part of the server configuration. */ if (r->no_cache && !apr_table_get(r->headers_out, "Expires")) { char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN); ap_recent_rfc822_date(date, r->request_time); apr_table_addn(r->headers_out, "Expires", date); } /* This is a hack, but I can't find anyway around it. The idea is that * 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); rv = ap_pass_brigade(f->next, b2); if (rv != APR_SUCCESS) { goto out; } ctx->headers_sent = 1; if (r->header_only || r->status == HTTP_NO_CONTENT) { apr_brigade_cleanup(b); goto out; } 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); rv = ap_pass_brigade(f->next, b); out: if (recursive_error) { return AP_FILTER_ERROR; } return rv; } /* * Map specific APR codes returned by the filter stack to HTTP error * codes, or the default status code provided. Use it as follows: * * return ap_map_http_request_error(rv, HTTP_BAD_REQUEST); * * If the filter has already handled the error, AP_FILTER_ERROR will * be returned, which is cleanly passed through. * * These mappings imply that the filter stack is reading from the * downstream client, the proxy will map these codes differently. */ AP_DECLARE(int) ap_map_http_request_error(apr_status_t rv, int status) { switch (rv) { case AP_FILTER_ERROR: return AP_FILTER_ERROR; case APR_ENOSPC: return HTTP_REQUEST_ENTITY_TOO_LARGE; case APR_ENOTIMPL: return HTTP_NOT_IMPLEMENTED; case APR_TIMEUP: case APR_ETIMEDOUT: return HTTP_REQUEST_TIME_OUT; default: return status; } } /* 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 seen_eos; apr_status_t rv; /* 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) { apr_brigade_destroy(bb); return ap_map_http_request_error(rv, 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_INFO, 0, r, APLOGNO(01592) "Unknown Transfer-Encoding %s", tenc); return HTTP_NOT_IMPLEMENTED; } if (r->read_body == REQUEST_CHUNKED_ERROR) { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01593) "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_INFO, 0, r, APLOGNO(01594) "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_INFO, 0, r, APLOGNO(01595) "%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_core_module_config(r->request_config); 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 == AP_FILTER_ERROR) { /* AP_FILTER_ERROR means a filter has responded already, * we are DONE. */ apr_brigade_destroy(bb); return -1; } 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); }