📄 http_filters.c
字号:
/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as * applicable. * * Licensed 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_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>#endifstatic 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 } state; int eos_sent;} http_ctx_t;/* 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; /* 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_palloc(f->r->pool, sizeof(*ctx)); ctx->state = BODY_NONE; ctx->remaining = 0; ctx->limit_used = 0; ctx->eos_sent = 0; /* 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; } } else if (lenp) { 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) { apr_bucket_brigade *bb; ctx->remaining = 0; ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "Invalid Content-Length"); bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, 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); } /* 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) { apr_bucket_brigade *bb; 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); bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, 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); } } /* 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)) { char *tmp; apr_bucket_brigade *bb; tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL, " ", ap_get_status_line(100), CRLF CRLF, NULL); bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); 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) { char line[30]; apr_bucket_brigade *bb; apr_size_t len = 30; apr_off_t brigade_length; bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, APR_BLOCK_READ, 0); if (rv == APR_SUCCESS) { /* We have to check the length of the brigade we got back. * We will not accept partial or blank lines. */ rv = apr_brigade_length(bb, 1, &brigade_length); if (rv == APR_SUCCESS && (!brigade_length || brigade_length > f->r->server->limit_req_line)) { rv = APR_ENOSPC; } if (rv == APR_SUCCESS) { rv = apr_brigade_flatten(bb, line, &len); if (rv == APR_SUCCESS) { ctx->remaining = get_chunk_size(line); } } } 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 */ e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, 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); } 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; } } } 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); ctx->eos_sent = 1; return APR_SUCCESS; case BODY_CHUNK: { char line[30]; apr_bucket_brigade *bb; apr_size_t len = 30; apr_status_t http_error = HTTP_REQUEST_ENTITY_TOO_LARGE; bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); /* We need to read the CRLF after the chunk. */ rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, APR_BLOCK_READ, 0); apr_brigade_cleanup(bb); if (rv == APR_SUCCESS) { /* Read the real chunk line. */ rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, APR_BLOCK_READ, 0); if (rv == APR_SUCCESS) { rv = apr_brigade_flatten(bb, line, &len); if (rv == APR_SUCCESS) { /* Wait a sec, that's a blank line! Oh no. */ if (!len) { rv = APR_EGENERAL; http_error = HTTP_SERVICE_UNAVAILABLE; } else { ctx->remaining = get_chunk_size(line); } } } apr_brigade_cleanup(bb); } /* Detect chunksize error (such as overflow) */ if (rv != APR_SUCCESS || ctx->remaining < 0) { apr_status_t out_error; ctx->remaining = 0; /* Reset it in case we have to * come back here later */ 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; out_error = ap_pass_brigade(f->r->output_filters, bb); return rv; } 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; } } break; } } /* Ensure that the caller can not go over our boundary point. */ if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK) { if (ctx->remaining < readbytes) { readbytes = ctx->remaining; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -