📄 http_filters.c
字号:
/* 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"#define CORE_PRIVATE#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 <stdarg.h>#endif#if APR_HAVE_UNISTD_H#include <unistd.h>#endif#define INVALID_CHAR -2static long get_chunk_size(char *);typedef struct http_filter_ctx { apr_off_t remaining; apr_off_t limit; apr_off_t limit_used; enum { BODY_NONE, BODY_LENGTH, BODY_CHUNK, BODY_CHUNK_PART } state; int eos_sent; char chunk_ln[32]; char *pos; apr_off_t linesize; apr_bucket_brigade *bb;} http_ctx_t;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); 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; return ap_pass_brigade(f->r->output_filters, bb);}static apr_status_t get_remaining_chunk_line(http_ctx_t *ctx, apr_bucket_brigade *b, int linelimit){ apr_status_t rv; apr_off_t brigade_length; apr_bucket *e; const char *lineend; apr_size_t len; /* * As the brigade b should have been requested in mode AP_MODE_GETLINE * all buckets in this brigade are already some type of memory * buckets (due to the needed scanning for LF in mode AP_MODE_GETLINE) * or META buckets. */ rv = apr_brigade_length(b, 0, &brigade_length); if (rv != APR_SUCCESS) { return rv; } /* Sanity check. Should never happen. See above. */ if (brigade_length == -1) { return APR_EGENERAL; } if (!brigade_length) { return APR_EAGAIN; } ctx->linesize += brigade_length; if (ctx->linesize > linelimit) { return APR_ENOSPC; } /* * As all buckets are already some type of memory buckets or META buckets * (see above), we only need to check the last byte in the last data bucket. */ for (e = APR_BRIGADE_LAST(b); e != APR_BRIGADE_SENTINEL(b); e = APR_BUCKET_PREV(e)) { if (APR_BUCKET_IS_METADATA(e)) { continue; } rv = apr_bucket_read(e, &lineend, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { return rv; } if (len > 0) { break; /* we got the data we want */ } /* If we got a zero-length data bucket, we try the next one */ } /* We had no data in this brigade */ if (!len || e == APR_BRIGADE_SENTINEL(b)) { return APR_EAGAIN; } if (lineend[len - 1] != APR_ASCII_LF) { return APR_EAGAIN; } /* Line is complete. So reset ctx->linesize for next round. */ ctx->linesize = 0; return APR_SUCCESS;}static apr_status_t get_chunk_line(http_ctx_t *ctx, apr_bucket_brigade *b, int linelimit){ apr_size_t len; int tmp_len; apr_status_t rv; tmp_len = sizeof(ctx->chunk_ln) - (ctx->pos - ctx->chunk_ln) - 1; /* Saveguard ourselves against underflows */ if (tmp_len < 0) { len = 0; } else { len = (apr_size_t) tmp_len; } /* * Check if there is space left in ctx->chunk_ln. If not, then either * the chunk size is insane or we have chunk-extensions. Ignore both * by discarding the remaining part of the line via * get_remaining_chunk_line. Only bail out if the line is too long. */ if (len > 0) { rv = apr_brigade_flatten(b, ctx->pos, &len); if (rv != APR_SUCCESS) { return rv; } ctx->pos += len; ctx->linesize += len; *(ctx->pos) = '\0'; /* * Check if we really got a full line. If yes the * last char in the just read buffer must be LF. * If not advance the buffer and return APR_EAGAIN. * We do not start processing until we have the * full line. */ if (ctx->pos[-1] != APR_ASCII_LF) { /* Check if the remaining data in the brigade has the LF */ return get_remaining_chunk_line(ctx, b, linelimit); } /* Line is complete. So reset ctx->pos for next round. */ ctx->pos = ctx->chunk_ln; return APR_SUCCESS; } return get_remaining_chunk_line(ctx, b, linelimit);}/* 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){ apr_bucket *e; http_ctx_t *ctx = f->ctx; apr_status_t rv; apr_off_t totalread; int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE; apr_bucket_brigade *bb; /* 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->pos = ctx->chunk_ln; 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")) { ctx->state = BODY_CHUNK; } /* test lenp, because it gives another case we can handle */ else if (!lenp) { /* Something that isn't in HTTP, unless some future * edition defines new transfer ecodings, is unsupported. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "Unknown Transfer-Encoding: %s", tenc); return bail_out_on_error(ctx, f, HTTP_NOT_IMPLEMENTED); } else { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, "Unknown Transfer-Encoding: %s; using Content-Length", tenc); tenc = NULL; } } if (lenp && !tenc) { char *endstr; ctx->state = BODY_LENGTH; errno = 0; /* 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_ERR, 0, f->r, "Invalid Content-Length"); return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE); } /* 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_ERR, 0, f->r, "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; tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL, " ", ap_get_status_line(100), CRLF CRLF, NULL); apr_brigade_cleanup(bb); e = apr_bucket_pool_create(tmp, strlen(tmp), 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); ap_pass_brigade(f->c->output_filters, bb); } } /* We can't read the chunk until after sending 100 if required. */ if (ctx->state == BODY_CHUNK) { apr_brigade_cleanup(bb); rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, block, 0); /* for timeout */ if (block == APR_NONBLOCK_READ && ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) || (APR_STATUS_IS_EAGAIN(rv)) )) { ctx->state = BODY_CHUNK_PART; return APR_EAGAIN; } if (rv == APR_SUCCESS) { rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line); if (APR_STATUS_IS_EAGAIN(rv)) { apr_brigade_cleanup(bb); ctx->state = BODY_CHUNK_PART; return rv; } if (rv == APR_SUCCESS) { ctx->remaining = get_chunk_size(ctx->chunk_ln); if (ctx->remaining == INVALID_CHAR) { rv = APR_EGENERAL; http_error = HTTP_SERVICE_UNAVAILABLE; } } } apr_brigade_cleanup(bb); /* Detect chunksize error (such as overflow) */ if (rv != APR_SUCCESS || ctx->remaining < 0) { ctx->remaining = 0; /* Reset it in case we have to * come back here later */ return bail_out_on_error(ctx, f, http_error); } if (!ctx->remaining) { /* Handle trailers by calling ap_get_mime_headers again! */ ctx->state = BODY_NONE; ap_get_mime_headers(f->r); e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); ctx->eos_sent = 1; return APR_SUCCESS; } } } else { bb = ctx->bb; } if (ctx->eos_sent) { e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); return APR_SUCCESS; } if (!ctx->remaining) { switch (ctx->state) { case BODY_NONE: break; case BODY_LENGTH: e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -