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