mirror of
https://github.com/apache/httpd.git
synced 2025-04-18 22:24:07 +03:00
*) core/mod_http/mod_http2:
- adds new meta bucket types REQUEST, RESPONSE and HEADERS to the API. - adds a new method for setting standard response headers Date and Server - adds helper methods for formatting parts of HTTP/1.x, like headers and end chunks for use in non-core parts of the server, e.g. mod_proxy - splits the HTTP_IN filter into a "generic HTTP" and "specific HTTP/1.x" filter. The latter one named HTTP1_BODY_IN. - Uses HTTP1_BODY_IN only for requests with HTTP version <= 1.1 - Removes the chunked input simulation from mod_http2 - adds body_indeterminate flag to request_rec that indicates that a request body may be present and needs to be read/discarded. This replaces logic that thinks without Content-Length and Transfer-Encoding, no request body can exist. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1899547 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a6ed77c35e
commit
4442201e61
@ -699,6 +699,7 @@ SET(LIBHTTPD_SOURCES
|
||||
server/eoc_bucket.c
|
||||
server/eor_bucket.c
|
||||
server/error_bucket.c
|
||||
server/headers_bucket.c
|
||||
server/listen.c
|
||||
server/log.c
|
||||
server/mpm/winnt/child.c
|
||||
|
@ -705,7 +705,10 @@
|
||||
* 20211221.5 (2.5.1-dev) Add hook create_secondary_connection and method
|
||||
* ap_create_secondary_connection() to have connection
|
||||
* setup of http2-like connections in core.
|
||||
*
|
||||
* 20211221.6 (2.5.1-dev) Add new meta buckets request/response/headers
|
||||
* Add field `body_indeterminate` in request_rec
|
||||
* Add new http/1.x formatting helpers
|
||||
* Add ap_assign_request()
|
||||
*/
|
||||
|
||||
#define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
|
||||
@ -713,7 +716,7 @@
|
||||
#ifndef MODULE_MAGIC_NUMBER_MAJOR
|
||||
#define MODULE_MAGIC_NUMBER_MAJOR 20211221
|
||||
#endif
|
||||
#define MODULE_MAGIC_NUMBER_MINOR 5 /* 0...n */
|
||||
#define MODULE_MAGIC_NUMBER_MINOR 6 /* 0...n */
|
||||
|
||||
/**
|
||||
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
|
||||
|
@ -67,6 +67,18 @@ AP_DECLARE(request_rec *) ap_create_request(conn_rec *c);
|
||||
*/
|
||||
request_rec *ap_read_request(conn_rec *c);
|
||||
|
||||
/**
|
||||
* Assign the method, uri and protocol to the request.
|
||||
* @param r The current request
|
||||
* @param method the HTTP method
|
||||
* @param uri the request uri
|
||||
* @param protocol the request protocol
|
||||
* @return 1 on success, 0 on failure
|
||||
*/
|
||||
AP_DECLARE(int) ap_assign_request(request_rec *r,
|
||||
const char *method, const char *uri,
|
||||
const char *protocol);
|
||||
|
||||
/**
|
||||
* Parse and validate the request line.
|
||||
* @param r The current request
|
||||
@ -1027,6 +1039,252 @@ AP_DECLARE(apr_bucket *) ap_bucket_error_create(int error, const char *buf,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list);
|
||||
|
||||
/** @see ap_bucket_type_request */
|
||||
typedef struct ap_bucket_request ap_bucket_request;
|
||||
|
||||
/**
|
||||
* @struct ap_bucket_request
|
||||
* @brief A bucket referring to a HTTP request
|
||||
*
|
||||
*/
|
||||
struct ap_bucket_request {
|
||||
/** Number of buckets using this memory */
|
||||
apr_bucket_refcount refcount;
|
||||
apr_pool_t *pool; /* pool that holds the contents, not for modification */
|
||||
const char *method; /* request method */
|
||||
const char *uri; /* request uri */
|
||||
const char *protocol; /* request protocol */
|
||||
apr_table_t *headers; /* request headers */
|
||||
};
|
||||
|
||||
/** @see ap_bucket_type_request */
|
||||
AP_DECLARE_DATA extern const apr_bucket_type_t ap_bucket_type_request;
|
||||
|
||||
/**
|
||||
* Determine if a bucket is a request bucket
|
||||
* @param e The bucket to inspect
|
||||
* @return true or false
|
||||
*/
|
||||
#define AP_BUCKET_IS_REQUEST(e) (e->type == &ap_bucket_type_request)
|
||||
|
||||
/**
|
||||
* Make the bucket passed in a request bucket
|
||||
* Copies all parameters to the given pool.
|
||||
* @param b The bucket to make into a request bucket
|
||||
* @param method the HTTP method
|
||||
* @param uri the uri requested
|
||||
* @param protocol the protocol requested
|
||||
* @param headers the table of response headers.
|
||||
* @param p A pool to allocate out of.
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_request_make(
|
||||
apr_bucket *b,
|
||||
const char *method,
|
||||
const char *uri,
|
||||
const char *protocol,
|
||||
apr_table_t *headers,
|
||||
apr_pool_t *p);
|
||||
|
||||
/**
|
||||
* Make the bucket passed in a request bucket
|
||||
* Uses all paramters without copying.
|
||||
* @param b The bucket to make into a request bucket
|
||||
* @param method the HTTP method
|
||||
* @param uri the uri requested
|
||||
* @param protocol the protocol requested
|
||||
* @param headers the table of response headers.
|
||||
* @param p A pool to allocate out of.
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_request_maken(
|
||||
apr_bucket *b,
|
||||
const char *method,
|
||||
const char *uri,
|
||||
const char *protocol,
|
||||
apr_table_t *headers,
|
||||
apr_pool_t *p);
|
||||
|
||||
/**
|
||||
* Create a bucket referring to a HTTP request.
|
||||
* Copies all parameters to the given pool.
|
||||
* @param method the HTTP method
|
||||
* @param uri the uri requested
|
||||
* @param protocol the protocol requested
|
||||
* @param headers the table of response headers.
|
||||
* @param p A pool to allocate the error string out of.
|
||||
* @param list The bucket allocator from which to allocate the bucket
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_request_create(
|
||||
const char *method,
|
||||
const char *uri,
|
||||
const char *protocol,
|
||||
apr_table_t *headers,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list);
|
||||
|
||||
/**
|
||||
* Create a bucket referring to a HTTP request.
|
||||
* Uses all paramters without copying.
|
||||
* @param method the HTTP method
|
||||
* @param uri the uri requested
|
||||
* @param protocol the protocol requested
|
||||
* @param headers the HTTP response headers.
|
||||
* @param p A pool to allocate the error string out of.
|
||||
* @param list The bucket allocator from which to allocate the bucket
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_request_createn(
|
||||
const char *method,
|
||||
const char *uri,
|
||||
const char *protocol,
|
||||
apr_table_t *headers,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list);
|
||||
|
||||
/**
|
||||
* Clone a request bucket into another pool/bucket_alloc that may
|
||||
* have a separate lifetime than the source bucket/pool.
|
||||
* @param source the request bucket to clone
|
||||
* @param p A pool to allocate the data out of.
|
||||
* @param list The bucket allocator from which to allocate the bucket
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_request_clone(apr_bucket *source,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list);
|
||||
|
||||
/** @see ap_bucket_type_response */
|
||||
typedef struct ap_bucket_response ap_bucket_response;
|
||||
|
||||
/**
|
||||
* @struct ap_bucket_response
|
||||
* @brief A bucket referring to a HTTP response
|
||||
*
|
||||
*/
|
||||
struct ap_bucket_response {
|
||||
/** Number of buckets using this memory */
|
||||
apr_bucket_refcount refcount;
|
||||
apr_pool_t *pool; /* pool that holds the contents, not for modification */
|
||||
int status; /* The status code */
|
||||
const char *reason; /* The optional HTTP reason for the status. */
|
||||
apr_table_t *headers; /* The response headers */
|
||||
apr_table_t *notes; /* internal notes about the response */
|
||||
};
|
||||
|
||||
/** @see ap_bucket_type_headers */
|
||||
AP_DECLARE_DATA extern const apr_bucket_type_t ap_bucket_type_response;
|
||||
|
||||
/**
|
||||
* Determine if a bucket is a response bucket
|
||||
* @param e The bucket to inspect
|
||||
* @return true or false
|
||||
*/
|
||||
#define AP_BUCKET_IS_RESPONSE(e) (e->type == &ap_bucket_type_response)
|
||||
|
||||
/**
|
||||
* Make the bucket passed in a response bucket
|
||||
* @param b The bucket to make into a response bucket
|
||||
* @param status The HTTP status code of the response.
|
||||
* @param reason textual description of status, can be NULL.
|
||||
* @param headers the table of response headers.
|
||||
* @param notes internal notes on the response
|
||||
* @param p A pool to allocate out of.
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_response_make(apr_bucket *b, int status,
|
||||
const char *reason, apr_table_t *headers,
|
||||
apr_table_t *notes, apr_pool_t *p);
|
||||
|
||||
/**
|
||||
* Create a bucket referring to a HTTP response.
|
||||
* @param status The HTTP status code.
|
||||
* @param reason textual description of status, can be NULL.
|
||||
* @param headers the HTTP response headers.
|
||||
* @param notes internal notes on the response
|
||||
* @param p A pool to allocate the error string out of.
|
||||
* @param list The bucket allocator from which to allocate the bucket
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_response_create(
|
||||
int status, const char *reason,
|
||||
apr_table_t *headers,
|
||||
apr_table_t *notes,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list);
|
||||
|
||||
/**
|
||||
* Clone a RESPONSE bucket into another pool/bucket_alloc that may
|
||||
* have a separate lifetime than the source bucket/pool.
|
||||
* @param source the response bucket to clone
|
||||
* @param p A pool to allocate the data out of.
|
||||
* @param list The bucket allocator from which to allocate the bucket
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_response_clone(apr_bucket *source,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list);
|
||||
|
||||
/** @see ap_bucket_type_headers */
|
||||
typedef struct ap_bucket_headers ap_bucket_headers;
|
||||
|
||||
/**
|
||||
* @struct ap_bucket_headers
|
||||
* @brief A bucket referring to an HTTP header set
|
||||
*
|
||||
*/
|
||||
struct ap_bucket_headers {
|
||||
/** Number of buckets using this memory */
|
||||
apr_bucket_refcount refcount;
|
||||
apr_pool_t *pool; /* pool that holds the contents, not for modification */
|
||||
apr_table_t *headers; /* The headers */
|
||||
|
||||
};
|
||||
|
||||
/** @see ap_bucket_type_headers */
|
||||
AP_DECLARE_DATA extern const apr_bucket_type_t ap_bucket_type_headers;
|
||||
|
||||
/**
|
||||
* Determine if a bucket is an headers bucket
|
||||
* @param e The bucket to inspect
|
||||
* @return true or false
|
||||
*/
|
||||
#define AP_BUCKET_IS_HEADERS(e) (e->type == &ap_bucket_type_headers)
|
||||
|
||||
/**
|
||||
* Make the bucket passed in a headers bucket
|
||||
* @param b The bucket to make into a headers bucket
|
||||
* @param headers the table of headers.
|
||||
* @param p A pool to allocate out of.
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_headers_make(apr_bucket *b,
|
||||
apr_table_t *headers, apr_pool_t *p);
|
||||
|
||||
/**
|
||||
* Create a bucket referring to a table of HTTP headers.
|
||||
* @param headers the HTTP headers in the bucket.
|
||||
* @param p A pool to allocate the error string out of.
|
||||
* @param list The bucket allocator from which to allocate the bucket
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_headers_create(apr_table_t *headers,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list);
|
||||
|
||||
/**
|
||||
* Clone a HEADER bucket into another pool/bucket_alloc that may
|
||||
* have a separate lifetime than the source bucket/pool.
|
||||
* @param source the header bucket to clone
|
||||
* @param p A pool to allocate the data out of.
|
||||
* @param list The bucket allocator from which to allocate the bucket
|
||||
* @return The new bucket, or NULL if allocation failed
|
||||
*/
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_headers_clone(apr_bucket *source,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list);
|
||||
|
||||
AP_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, apr_bucket_brigade *b);
|
||||
AP_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_bucket_brigade *b);
|
||||
AP_DECLARE_NONSTD(apr_status_t) ap_content_length_filter(ap_filter_t *,
|
||||
@ -1047,6 +1305,13 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r
|
||||
*/
|
||||
AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub_r);
|
||||
|
||||
/**
|
||||
* Set standard response headers, such as `Date` and `Server`
|
||||
* in r->headers_out. Takes care of precedence of existing
|
||||
* values from proxied requests.
|
||||
*/
|
||||
AP_DECLARE(void) ap_set_std_response_headers(request_rec *r);
|
||||
|
||||
/**
|
||||
* Send an interim (HTTP 1xx) response immediately.
|
||||
* @param r The request
|
||||
@ -1054,6 +1319,33 @@ AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub_r);
|
||||
*/
|
||||
AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers);
|
||||
|
||||
/**
|
||||
* Append the headers in HTTP/1.1 format to the brigade.
|
||||
* @param b the brigade to append to
|
||||
* @param r the reqeust this is done for (pool and logging)
|
||||
* @param headers the headers to append
|
||||
*/
|
||||
AP_DECLARE(apr_status_t) ap_h1_append_headers(apr_bucket_brigade *b,
|
||||
request_rec *r,
|
||||
apr_table_t *headers);
|
||||
|
||||
/**
|
||||
* Append the HTTP/1.1 header termination (empty CRLF) to the brigade.
|
||||
* @param b the brigade to append to
|
||||
*/
|
||||
AP_DECLARE(apr_status_t) ap_h1_terminate_header(apr_bucket_brigade *b);
|
||||
|
||||
/**
|
||||
* Insert/Append the last chunk in a HTTP/1.1 Transfer-Encoding chunked.
|
||||
* @param b the brigade to add the chunk to
|
||||
* @param eos the bucket before to add or NULL for insert at tail
|
||||
* @param r the request handled
|
||||
* @param trailers table of trailers or NULL
|
||||
*/
|
||||
AP_DECLARE(void) ap_h1_add_end_chunk(apr_bucket_brigade *b,
|
||||
apr_bucket *eos,
|
||||
request_rec *r,
|
||||
apr_table_t *trailers);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -1144,6 +1144,14 @@ struct request_rec {
|
||||
* the elements of this field.
|
||||
*/
|
||||
ap_request_bnotes_t bnotes;
|
||||
/** Indicates that the request has a body of unknown length and
|
||||
* protocol handlers need to read it, even if only to discard the
|
||||
* data. In HTTP/1.1 this is set on chunked transfer encodings, but
|
||||
* newer HTTP versions can transfer such bodies by other means. The
|
||||
* absence of a "Transfer-Encoding" header is no longer sufficient
|
||||
* to conclude that no body is there.
|
||||
*/
|
||||
int body_indeterminate;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -40,6 +40,7 @@ extern "C" {
|
||||
|
||||
/* Handles for core filters */
|
||||
AP_DECLARE_DATA extern ap_filter_rec_t *ap_http_input_filter_handle;
|
||||
AP_DECLARE_DATA extern ap_filter_rec_t *ap_h1_body_in_filter_handle;
|
||||
AP_DECLARE_DATA extern ap_filter_rec_t *ap_http_header_filter_handle;
|
||||
AP_DECLARE_DATA extern ap_filter_rec_t *ap_chunk_filter_handle;
|
||||
AP_DECLARE_DATA extern ap_filter_rec_t *ap_http_outerror_filter_handle;
|
||||
@ -52,6 +53,10 @@ 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_status_t ap_h1_body_in_filter(ap_filter_t *f, apr_bucket_brigade *b,
|
||||
ap_input_mode_t mode, apr_read_type_e block,
|
||||
apr_off_t readbytes);
|
||||
|
||||
/* HTTP/1.1 chunked transfer encoding filter. */
|
||||
apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b);
|
||||
|
||||
|
@ -525,6 +525,10 @@ SOURCE=.\server\error_bucket.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\server\headers_bucket.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\server\util.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include "apr_strings.h"
|
||||
#include "apr_lib.h"
|
||||
#include "apr_thread_proc.h" /* for RLIMIT stuff */
|
||||
|
||||
#define APR_WANT_STRFUNC
|
||||
@ -28,6 +29,7 @@
|
||||
#include "http_config.h"
|
||||
#include "http_connection.h"
|
||||
#include "http_core.h"
|
||||
#include "http_log.h"
|
||||
#include "http_protocol.h" /* For index_of_response(). Grump. */
|
||||
#include "http_request.h"
|
||||
|
||||
@ -38,19 +40,28 @@
|
||||
|
||||
#include "mod_core.h"
|
||||
|
||||
/*
|
||||
* A pointer to this is used to memorize in the filter context that a bad
|
||||
* gateway error bucket had been seen. It is used as an invented unique pointer.
|
||||
*/
|
||||
static char bad_gateway_seen;
|
||||
|
||||
APLOG_USE_MODULE(http);
|
||||
|
||||
|
||||
typedef struct chunk_out_ctx {
|
||||
int bad_gateway_seen;
|
||||
apr_table_t *trailers;
|
||||
} chunk_out_ctx;
|
||||
|
||||
|
||||
apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
|
||||
{
|
||||
conn_rec *c = f->r->connection;
|
||||
chunk_out_ctx *ctx = f->ctx;
|
||||
apr_bucket_brigade *more, *tmp;
|
||||
apr_bucket *e;
|
||||
apr_status_t rv;
|
||||
|
||||
if (!ctx) {
|
||||
ctx = f->ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
|
||||
}
|
||||
|
||||
for (more = tmp = NULL; b; b = more, more = NULL) {
|
||||
apr_off_t bytes = 0;
|
||||
apr_bucket *eos = NULL;
|
||||
@ -65,28 +76,43 @@ apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
|
||||
e != APR_BRIGADE_SENTINEL(b);
|
||||
e = APR_BUCKET_NEXT(e))
|
||||
{
|
||||
if (APR_BUCKET_IS_EOS(e)) {
|
||||
/* there shouldn't be anything after the eos */
|
||||
ap_remove_output_filter(f);
|
||||
eos = e;
|
||||
break;
|
||||
}
|
||||
if (AP_BUCKET_IS_ERROR(e) &&
|
||||
(((ap_bucket_error *)(e->data))->status == HTTP_BAD_GATEWAY ||
|
||||
((ap_bucket_error *)(e->data))->status == HTTP_GATEWAY_TIME_OUT)) {
|
||||
/*
|
||||
* We had a broken backend. Memorize this in the filter
|
||||
* context.
|
||||
*/
|
||||
f->ctx = &bad_gateway_seen;
|
||||
continue;
|
||||
}
|
||||
if (APR_BUCKET_IS_FLUSH(e)) {
|
||||
flush = e;
|
||||
if (e != APR_BRIGADE_LAST(b)) {
|
||||
more = apr_brigade_split_ex(b, APR_BUCKET_NEXT(e), tmp);
|
||||
if (APR_BUCKET_IS_METADATA(e)) {
|
||||
if (APR_BUCKET_IS_EOS(e)) {
|
||||
/* there shouldn't be anything after the eos */
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, f->r,
|
||||
"ap_http_chunk_filter eos seen, removing filter");
|
||||
ap_remove_output_filter(f);
|
||||
eos = e;
|
||||
break;
|
||||
}
|
||||
if (AP_BUCKET_IS_ERROR(e) &&
|
||||
(((ap_bucket_error *)(e->data))->status == HTTP_BAD_GATEWAY ||
|
||||
((ap_bucket_error *)(e->data))->status == HTTP_GATEWAY_TIME_OUT)) {
|
||||
/*
|
||||
* We had a broken backend. Memorize this in the filter
|
||||
* context.
|
||||
*/
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, f->r,
|
||||
"ap_http_chunk_filter bad gateway error, suppressing end chunk");
|
||||
ctx->bad_gateway_seen = 1;
|
||||
continue;
|
||||
}
|
||||
if (APR_BUCKET_IS_FLUSH(e)) {
|
||||
flush = e;
|
||||
if (e != APR_BRIGADE_LAST(b)) {
|
||||
more = apr_brigade_split_ex(b, APR_BUCKET_NEXT(e), tmp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (AP_BUCKET_IS_HEADERS(e)) {
|
||||
ap_bucket_headers *hdrs = e->data;
|
||||
if (!apr_is_empty_table(hdrs->headers)) {
|
||||
if (!ctx->trailers) {
|
||||
ctx->trailers = apr_table_make(f->r->pool, 5);
|
||||
}
|
||||
apr_table_overlap(ctx->trailers, hdrs->headers, APR_OVERLAP_TABLES_MERGE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (e->length == (apr_size_t)-1) {
|
||||
/* unknown amount of data (e.g. a pipe) */
|
||||
@ -132,6 +158,9 @@ apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
|
||||
* Insert the chunk header, specifying the number of bytes in
|
||||
* the chunk.
|
||||
*/
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, f->r,
|
||||
"ap_http_chunk_filter sending chunk of %"
|
||||
APR_UINT64_T_HEX_FMT " bytes", (apr_uint64_t)bytes);
|
||||
hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
|
||||
"%" APR_UINT64_T_HEX_FMT CRLF, (apr_uint64_t)bytes);
|
||||
ap_xlate_proto_to_ascii(chunk_hdr, hdr_len);
|
||||
@ -175,12 +204,8 @@ apr_status_t ap_http_chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
|
||||
* marker above, but this is a bit more straight-forward for
|
||||
* now.
|
||||
*/
|
||||
if (eos && !f->ctx) {
|
||||
/* XXX: (2) trailers ... does not yet exist */
|
||||
e = apr_bucket_immortal_create(ZERO_ASCII CRLF_ASCII
|
||||
/* <trailers> */
|
||||
CRLF_ASCII, 5, c->bucket_alloc);
|
||||
APR_BUCKET_INSERT_BEFORE(eos, e);
|
||||
if (eos && !ctx->bad_gateway_seen) {
|
||||
ap_h1_add_end_chunk(b, eos, f->r, ctx->trailers);
|
||||
}
|
||||
|
||||
/* pass the brigade to the next filter. */
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
/* Handles for core filters */
|
||||
AP_DECLARE_DATA ap_filter_rec_t *ap_http_input_filter_handle;
|
||||
AP_DECLARE_DATA ap_filter_rec_t *ap_h1_body_in_filter_handle;
|
||||
AP_DECLARE_DATA ap_filter_rec_t *ap_http_header_filter_handle;
|
||||
AP_DECLARE_DATA ap_filter_rec_t *ap_chunk_filter_handle;
|
||||
AP_DECLARE_DATA ap_filter_rec_t *ap_http_outerror_filter_handle;
|
||||
@ -301,6 +302,9 @@ static void register_hooks(apr_pool_t *p)
|
||||
ap_http_input_filter_handle =
|
||||
ap_register_input_filter("HTTP_IN", ap_http_filter,
|
||||
NULL, AP_FTYPE_PROTOCOL);
|
||||
ap_h1_body_in_filter_handle =
|
||||
ap_register_input_filter("HTTP1_BODY_IN", ap_h1_body_in_filter,
|
||||
NULL, AP_FTYPE_TRANSCODE);
|
||||
ap_http_header_filter_handle =
|
||||
ap_register_output_filter("HTTP_HEADER", ap_http_header_filter,
|
||||
NULL, AP_FTYPE_PROTOCOL);
|
||||
|
@ -254,21 +254,28 @@ static apr_status_t parse_chunk_size(http_ctx_t *ctx, const char *buffer,
|
||||
}
|
||||
|
||||
static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f,
|
||||
apr_bucket_brigade *b, int merge)
|
||||
apr_bucket_brigade *b)
|
||||
{
|
||||
int rv;
|
||||
apr_bucket *e;
|
||||
request_rec *r = f->r;
|
||||
apr_table_t *trailers;
|
||||
apr_table_t *saved_headers_in = r->headers_in;
|
||||
int saved_status = r->status;
|
||||
|
||||
trailers = apr_table_make(r->pool, 5);
|
||||
r->status = HTTP_OK;
|
||||
r->headers_in = r->trailers_in;
|
||||
apr_table_clear(r->headers_in);
|
||||
r->headers_in = trailers;
|
||||
ap_get_mime_headers(r);
|
||||
r->headers_in = saved_headers_in;
|
||||
|
||||
if(r->status == HTTP_OK) {
|
||||
if (r->status == HTTP_OK) {
|
||||
r->status = saved_status;
|
||||
|
||||
if (!apr_is_empty_table(trailers)) {
|
||||
e = ap_bucket_headers_create(trailers, r->pool, b->bucket_alloc);
|
||||
APR_BRIGADE_INSERT_TAIL(b, e);
|
||||
}
|
||||
e = apr_bucket_eos_create(f->c->bucket_alloc);
|
||||
APR_BRIGADE_INSERT_TAIL(b, e);
|
||||
ctx->at_eos = 1;
|
||||
@ -284,17 +291,16 @@ static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f,
|
||||
rv = APR_EINVAL;
|
||||
}
|
||||
|
||||
if(!merge) {
|
||||
r->headers_in = saved_headers_in;
|
||||
}
|
||||
else {
|
||||
r->headers_in = apr_table_overlay(r->pool, saved_headers_in,
|
||||
r->trailers_in);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
typedef struct h1_in_ctx_t
|
||||
{
|
||||
unsigned int at_trailers:1;
|
||||
unsigned int at_eos:1;
|
||||
unsigned int seen_data:1;
|
||||
} h1_in_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
|
||||
@ -303,6 +309,122 @@ static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f,
|
||||
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, *next;
|
||||
h1_in_ctx_t *ctx = f->ctx;
|
||||
request_rec *r = f->r;
|
||||
apr_status_t rv;
|
||||
|
||||
if (!ctx) {
|
||||
f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
|
||||
}
|
||||
|
||||
/* 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 the read is to be nonblocking though, the caller may not want to
|
||||
* handle this just now (e.g. mod_proxy_http), and is prepared to read
|
||||
* nothing if the client really waits for 100 continue, so we don't
|
||||
* send it now and wait for later blocking read.
|
||||
*
|
||||
* In any case, even if r->expecting remains set at the end of the
|
||||
* request handling, ap_set_keepalive() will finally do the right
|
||||
* thing (i.e. "Connection: close" the connection).
|
||||
*/
|
||||
if (block == APR_BLOCK_READ
|
||||
&& r->expecting_100 && r->proto_num >= HTTP_VERSION(1,1)
|
||||
&& !(ctx->at_eos || r->eos_sent || r->bytes_sent)) {
|
||||
if (!ap_is_HTTP_SUCCESS(r->status)) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
|
||||
"ap_http_in_filter: status != OK, not sending 100-continue");
|
||||
ctx->at_eos = 1; /* send EOS below */
|
||||
}
|
||||
else if (!ctx->seen_data) {
|
||||
int saved_status = r->status;
|
||||
const char *saved_status_line = r->status_line;
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
|
||||
"ap_http_in_filter: sending 100-continue");
|
||||
r->status = HTTP_CONTINUE;
|
||||
r->status_line = NULL;
|
||||
ap_send_interim_response(r, 0);
|
||||
AP_DEBUG_ASSERT(!r->expecting_100);
|
||||
r->status_line = saved_status_line;
|
||||
r->status = saved_status;
|
||||
}
|
||||
else {
|
||||
/* https://tools.ietf.org/html/rfc7231#section-5.1.1
|
||||
* A server MAY omit sending a 100 (Continue) response if it
|
||||
* has already received some or all of the message body for
|
||||
* the corresponding request [...]
|
||||
*/
|
||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(10260)
|
||||
"request body already/partly received while "
|
||||
"100-continue is expected, omit sending interim "
|
||||
"response");
|
||||
r->expecting_100 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* sanity check in case we're read twice */
|
||||
if (ctx->at_eos) {
|
||||
e = apr_bucket_eos_create(f->c->bucket_alloc);
|
||||
APR_BRIGADE_INSERT_TAIL(b, e);
|
||||
rv = APR_SUCCESS;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rv = ap_get_brigade(f->next, b, mode, block, readbytes);
|
||||
if (APR_SUCCESS == rv) {
|
||||
for (e = APR_BRIGADE_FIRST(b);
|
||||
e != APR_BRIGADE_SENTINEL(b);
|
||||
e = next)
|
||||
{
|
||||
next = APR_BUCKET_NEXT(e);
|
||||
if (!APR_BUCKET_IS_METADATA(e)) {
|
||||
if (e->length != 0) {
|
||||
ctx->seen_data = 1;
|
||||
}
|
||||
if (ctx->at_trailers) {
|
||||
/* DATA after trailers? Someone smuggling something? */
|
||||
rv = AP_FILTER_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (AP_BUCKET_IS_HEADERS(e)) {
|
||||
/* trailers */
|
||||
ap_bucket_headers * hdrs = e->data;
|
||||
|
||||
/* Allow multiple HEADERS buckets carrying trailers here,
|
||||
* will not happen from HTTP/1.x and current H2 implementation,
|
||||
* but is an option. */
|
||||
ctx->at_trailers = 1;
|
||||
if (!apr_is_empty_table(hdrs->headers)) {
|
||||
r->trailers_in = apr_table_overlay(r->pool, r->trailers_in, hdrs->headers);
|
||||
}
|
||||
apr_bucket_delete(e);
|
||||
}
|
||||
if (APR_BUCKET_IS_EOS(e)) {
|
||||
ctx->at_eos = 1;
|
||||
if (!apr_is_empty_table(r->trailers_in)) {
|
||||
core_server_config *conf = ap_get_module_config(
|
||||
r->server->module_config, &core_module);
|
||||
if (conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE) {
|
||||
r->headers_in = apr_table_overlay(r->pool, r->headers_in, r->trailers_in);
|
||||
}
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
return rv;
|
||||
}
|
||||
|
||||
apr_status_t ap_h1_body_in_filter(ap_filter_t *f, apr_bucket_brigade *b,
|
||||
ap_input_mode_t mode, apr_read_type_e block,
|
||||
apr_off_t readbytes)
|
||||
{
|
||||
core_server_config *conf =
|
||||
(core_server_config *) ap_get_module_config(f->r->server->module_config,
|
||||
@ -360,6 +482,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
|
||||
*/
|
||||
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r, APLOGNO(01585)
|
||||
"Unknown Transfer-Encoding: %s", tenc);
|
||||
ap_die(HTTP_NOT_IMPLEMENTED, f->r);
|
||||
return APR_EGENERAL;
|
||||
}
|
||||
lenp = NULL;
|
||||
@ -407,51 +530,6 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 the read is to be nonblocking though, the caller may not want to
|
||||
* handle this just now (e.g. mod_proxy_http), and is prepared to read
|
||||
* nothing if the client really waits for 100 continue, so we don't
|
||||
* send it now and wait for later blocking read.
|
||||
*
|
||||
* In any case, even if r->expecting remains set at the end of the
|
||||
* request handling, ap_set_keepalive() will finally do the right
|
||||
* thing (i.e. "Connection: close" the connection).
|
||||
*/
|
||||
if (block == APR_BLOCK_READ
|
||||
&& (ctx->state == BODY_CHUNK
|
||||
|| (ctx->state == BODY_LENGTH && ctx->remaining > 0))
|
||||
&& f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1)
|
||||
&& !(ctx->at_eos || f->r->eos_sent || f->r->bytes_sent)) {
|
||||
if (!ap_is_HTTP_SUCCESS(f->r->status)) {
|
||||
ctx->state = BODY_NONE;
|
||||
ctx->at_eos = 1; /* send EOS below */
|
||||
}
|
||||
else if (!ctx->seen_data) {
|
||||
int saved_status = f->r->status;
|
||||
const char *saved_status_line = f->r->status_line;
|
||||
f->r->status = HTTP_CONTINUE;
|
||||
f->r->status_line = NULL;
|
||||
ap_send_interim_response(f->r, 0);
|
||||
AP_DEBUG_ASSERT(!f->r->expecting_100);
|
||||
f->r->status_line = saved_status_line;
|
||||
f->r->status = saved_status;
|
||||
}
|
||||
else {
|
||||
/* https://tools.ietf.org/html/rfc7231#section-5.1.1
|
||||
* A server MAY omit sending a 100 (Continue) response if it
|
||||
* has already received some or all of the message body for
|
||||
* the corresponding request [...]
|
||||
*/
|
||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, APLOGNO(10260)
|
||||
"request body already/partly received while "
|
||||
"100-continue is expected, omit sending interim "
|
||||
"response");
|
||||
f->r->expecting_100 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* sanity check in case we're read twice */
|
||||
if (ctx->at_eos) {
|
||||
e = apr_bucket_eos_create(f->c->bucket_alloc);
|
||||
@ -519,8 +597,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
|
||||
|
||||
if (ctx->state == BODY_CHUNK_TRAILER) {
|
||||
/* Treat UNSET as DISABLE - trailers aren't merged by default */
|
||||
return read_chunked_trailers(ctx, f, b,
|
||||
conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE);
|
||||
return read_chunked_trailers(ctx, f, b);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -1636,25 +1713,22 @@ cleanup:
|
||||
|
||||
AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
|
||||
{
|
||||
const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
|
||||
const char *lenp = apr_table_get(r->headers_in, "Content-Length");
|
||||
|
||||
r->read_body = read_policy;
|
||||
r->read_chunked = 0;
|
||||
r->remaining = 0;
|
||||
|
||||
if (tenc) {
|
||||
if (ap_cstr_casecmp(tenc, "chunked")) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01592)
|
||||
"Unknown Transfer-Encoding %s", tenc);
|
||||
return HTTP_NOT_IMPLEMENTED;
|
||||
}
|
||||
if (r->body_indeterminate) {
|
||||
/* Protocols like HTTP/2 can carry bodies without length and
|
||||
* HTTP/1.1 has chunked encoding signalled via this note.
|
||||
*/
|
||||
if (r->read_body == REQUEST_CHUNKED_ERROR) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01593)
|
||||
"chunked Transfer-Encoding forbidden: %s", r->uri);
|
||||
"indeterminate request body length forbidden: %s", r->uri);
|
||||
r->read_chunked = 0;
|
||||
return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED;
|
||||
}
|
||||
|
||||
r->read_chunked = 1;
|
||||
}
|
||||
else if (lenp) {
|
||||
|
@ -1485,3 +1485,108 @@ AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
|
||||
l->method_list->nelts = 0;
|
||||
}
|
||||
|
||||
/* Send a request's HTTP response headers to the client.
|
||||
*/
|
||||
AP_DECLARE(apr_status_t) ap_h1_append_headers(apr_bucket_brigade *bb,
|
||||
request_rec *r,
|
||||
apr_table_t *headers)
|
||||
{
|
||||
const apr_array_header_t *elts;
|
||||
const apr_table_entry_t *t_elt;
|
||||
const apr_table_entry_t *t_end;
|
||||
struct iovec *vec;
|
||||
struct iovec *vec_next;
|
||||
|
||||
elts = apr_table_elts(headers);
|
||||
if (elts->nelts == 0) {
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
t_elt = (const apr_table_entry_t *)(elts->elts);
|
||||
t_end = t_elt + elts->nelts;
|
||||
vec = (struct iovec *)apr_palloc(r->pool, 4 * elts->nelts *
|
||||
sizeof(struct iovec));
|
||||
vec_next = vec;
|
||||
|
||||
/* For each field, generate
|
||||
* name ": " value CRLF
|
||||
*/
|
||||
do {
|
||||
if (t_elt->key && t_elt->val) {
|
||||
vec_next->iov_base = (void*)(t_elt->key);
|
||||
vec_next->iov_len = strlen(t_elt->key);
|
||||
vec_next++;
|
||||
vec_next->iov_base = ": ";
|
||||
vec_next->iov_len = sizeof(": ") - 1;
|
||||
vec_next++;
|
||||
vec_next->iov_base = (void*)(t_elt->val);
|
||||
vec_next->iov_len = strlen(t_elt->val);
|
||||
vec_next++;
|
||||
vec_next->iov_base = CRLF;
|
||||
vec_next->iov_len = sizeof(CRLF) - 1;
|
||||
vec_next++;
|
||||
}
|
||||
t_elt++;
|
||||
} while (t_elt < t_end);
|
||||
|
||||
if (APLOGrtrace4(r)) {
|
||||
t_elt = (const apr_table_entry_t *)(elts->elts);
|
||||
do {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, " %s: %s",
|
||||
t_elt->key, t_elt->val);
|
||||
t_elt++;
|
||||
} while (t_elt < t_end);
|
||||
}
|
||||
|
||||
#if APR_CHARSET_EBCDIC
|
||||
{
|
||||
apr_size_t len;
|
||||
char *tmp = apr_pstrcatv(r->pool, vec, vec_next - vec, &len);
|
||||
ap_xlate_proto_to_ascii(tmp, len);
|
||||
return apr_brigade_write(bb, NULL, NULL, tmp, len);
|
||||
}
|
||||
#else
|
||||
return apr_brigade_writev(bb, NULL, NULL, vec, vec_next - vec);
|
||||
#endif
|
||||
}
|
||||
|
||||
AP_DECLARE(apr_status_t) ap_h1_terminate_header(apr_bucket_brigade *bb)
|
||||
{
|
||||
char crlf[] = CRLF;
|
||||
apr_size_t buflen;
|
||||
|
||||
buflen = strlen(crlf);
|
||||
ap_xlate_proto_to_ascii(crlf, buflen);
|
||||
return apr_brigade_write(bb, NULL, NULL, crlf, buflen);
|
||||
}
|
||||
|
||||
AP_DECLARE(void) ap_h1_add_end_chunk(apr_bucket_brigade *b,
|
||||
apr_bucket *eos,
|
||||
request_rec *r,
|
||||
apr_table_t *trailers)
|
||||
{
|
||||
if (!trailers || apr_is_empty_table(trailers)) {
|
||||
apr_bucket *e;
|
||||
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
||||
"append empty end chunk");
|
||||
e = apr_bucket_immortal_create(ZERO_ASCII CRLF_ASCII
|
||||
CRLF_ASCII, 5, b->bucket_alloc);
|
||||
if (eos) {
|
||||
APR_BUCKET_INSERT_BEFORE(eos, e);
|
||||
}
|
||||
else {
|
||||
APR_BRIGADE_INSERT_TAIL(b, e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
apr_bucket_brigade *tmp;
|
||||
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
||||
"append end chunk with trailers");
|
||||
tmp = eos? apr_brigade_split_ex(b, eos, NULL) : NULL;
|
||||
apr_brigade_write(b, NULL, NULL, ZERO_ASCII CRLF_ASCII, 3);
|
||||
ap_h1_append_headers(b, r, trailers);
|
||||
ap_h1_terminate_header(b);
|
||||
if (tmp) APR_BRIGADE_CONCAT(b, tmp);
|
||||
}
|
||||
}
|
||||
|
@ -478,6 +478,16 @@ static apr_status_t c2_process(h2_conn_ctx_t *conn_ctx, conn_rec *c)
|
||||
|
||||
/* the request_rec->server carries the timeout value that applies */
|
||||
h2_conn_ctx_set_timeout(conn_ctx, r->server->timeout);
|
||||
/* We only handle this one request on the connection and tell everyone
|
||||
* that there is no need to keep it "clean" if something fails. Also,
|
||||
* this prevents mod_reqtimeout from doing funny business with monitoring
|
||||
* keepalive timeouts.
|
||||
*/
|
||||
r->connection->keepalive = AP_CONN_CLOSE;
|
||||
|
||||
if (conn_ctx->beam_in && !apr_table_get(r->headers_in, "Content-Length")) {
|
||||
r->body_indeterminate = 1;
|
||||
}
|
||||
|
||||
if (h2_config_sgeti(conn_ctx->server, H2_CONF_COPY_FILES)) {
|
||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
|
||||
|
@ -668,139 +668,6 @@ apr_status_t h2_c2_filter_response_out(ap_filter_t *f, apr_bucket_brigade *bb)
|
||||
}
|
||||
|
||||
|
||||
struct h2_chunk_filter_t {
|
||||
const char *id;
|
||||
int eos_chunk_added;
|
||||
apr_bucket_brigade *bbchunk;
|
||||
apr_off_t chunked_total;
|
||||
};
|
||||
typedef struct h2_chunk_filter_t h2_chunk_filter_t;
|
||||
|
||||
|
||||
static void make_chunk(conn_rec *c, h2_chunk_filter_t *fctx, apr_bucket_brigade *bb,
|
||||
apr_bucket *first, apr_off_t chunk_len,
|
||||
apr_bucket *tail)
|
||||
{
|
||||
/* Surround the buckets [first, tail[ with new buckets carrying the
|
||||
* HTTP/1.1 chunked encoding format. If tail is NULL, the chunk extends
|
||||
* to the end of the brigade. */
|
||||
char buffer[128];
|
||||
apr_bucket *b;
|
||||
apr_size_t len;
|
||||
|
||||
len = (apr_size_t)apr_snprintf(buffer, H2_ALEN(buffer),
|
||||
"%"APR_UINT64_T_HEX_FMT"\r\n", (apr_uint64_t)chunk_len);
|
||||
b = apr_bucket_heap_create(buffer, len, NULL, bb->bucket_alloc);
|
||||
APR_BUCKET_INSERT_BEFORE(first, b);
|
||||
b = apr_bucket_immortal_create("\r\n", 2, bb->bucket_alloc);
|
||||
if (tail) {
|
||||
APR_BUCKET_INSERT_BEFORE(tail, b);
|
||||
}
|
||||
else {
|
||||
APR_BRIGADE_INSERT_TAIL(bb, b);
|
||||
}
|
||||
fctx->chunked_total += chunk_len;
|
||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
|
||||
"h2_c2(%s): added chunk %ld, total %ld",
|
||||
fctx->id, (long)chunk_len, (long)fctx->chunked_total);
|
||||
}
|
||||
|
||||
static int ser_header(void *ctx, const char *name, const char *value)
|
||||
{
|
||||
apr_bucket_brigade *bb = ctx;
|
||||
apr_brigade_printf(bb, NULL, NULL, "%s: %s\r\n", name, value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static apr_status_t read_and_chunk(ap_filter_t *f, h2_conn_ctx_t *conn_ctx,
|
||||
apr_read_type_e block) {
|
||||
h2_chunk_filter_t *fctx = f->ctx;
|
||||
request_rec *r = f->r;
|
||||
apr_status_t status = APR_SUCCESS;
|
||||
|
||||
if (!fctx->bbchunk) {
|
||||
fctx->bbchunk = apr_brigade_create(r->pool, f->c->bucket_alloc);
|
||||
}
|
||||
|
||||
if (APR_BRIGADE_EMPTY(fctx->bbchunk)) {
|
||||
apr_bucket *b, *next, *first_data = NULL;
|
||||
apr_bucket_brigade *tmp;
|
||||
apr_off_t bblen = 0;
|
||||
|
||||
/* get more data from the lower layer filters. Always do this
|
||||
* in larger pieces, since we handle the read modes ourself. */
|
||||
status = ap_get_brigade(f->next, fctx->bbchunk,
|
||||
AP_MODE_READBYTES, block, conn_ctx->mplx->stream_max_mem);
|
||||
if (status != APR_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
for (b = APR_BRIGADE_FIRST(fctx->bbchunk);
|
||||
b != APR_BRIGADE_SENTINEL(fctx->bbchunk);
|
||||
b = next) {
|
||||
next = APR_BUCKET_NEXT(b);
|
||||
if (APR_BUCKET_IS_METADATA(b)) {
|
||||
if (first_data) {
|
||||
make_chunk(f->c, fctx, fctx->bbchunk, first_data, bblen, b);
|
||||
first_data = NULL;
|
||||
}
|
||||
|
||||
if (H2_BUCKET_IS_HEADERS(b)) {
|
||||
h2_headers *headers = h2_bucket_headers_get(b);
|
||||
|
||||
ap_assert(headers);
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
||||
"h2_c2(%s-%d): receiving trailers",
|
||||
conn_ctx->id, conn_ctx->stream_id);
|
||||
tmp = apr_brigade_split_ex(fctx->bbchunk, b, NULL);
|
||||
if (!apr_is_empty_table(headers->headers)) {
|
||||
status = apr_brigade_puts(fctx->bbchunk, NULL, NULL, "0\r\n");
|
||||
apr_table_do(ser_header, fctx->bbchunk, headers->headers, NULL);
|
||||
status = apr_brigade_puts(fctx->bbchunk, NULL, NULL, "\r\n");
|
||||
}
|
||||
else {
|
||||
status = apr_brigade_puts(fctx->bbchunk, NULL, NULL, "0\r\n\r\n");
|
||||
}
|
||||
r->trailers_in = apr_table_clone(r->pool, headers->headers);
|
||||
APR_BUCKET_REMOVE(b);
|
||||
apr_bucket_destroy(b);
|
||||
APR_BRIGADE_CONCAT(fctx->bbchunk, tmp);
|
||||
apr_brigade_destroy(tmp);
|
||||
fctx->eos_chunk_added = 1;
|
||||
}
|
||||
else if (APR_BUCKET_IS_EOS(b)) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
||||
"h2_c2(%s-%d): receiving eos",
|
||||
conn_ctx->id, conn_ctx->stream_id);
|
||||
if (!fctx->eos_chunk_added) {
|
||||
tmp = apr_brigade_split_ex(fctx->bbchunk, b, NULL);
|
||||
status = apr_brigade_puts(fctx->bbchunk, NULL, NULL, "0\r\n\r\n");
|
||||
APR_BRIGADE_CONCAT(fctx->bbchunk, tmp);
|
||||
apr_brigade_destroy(tmp);
|
||||
}
|
||||
fctx->eos_chunk_added = 0;
|
||||
}
|
||||
}
|
||||
else if (b->length == 0) {
|
||||
APR_BUCKET_REMOVE(b);
|
||||
apr_bucket_destroy(b);
|
||||
}
|
||||
else {
|
||||
if (!first_data) {
|
||||
first_data = b;
|
||||
bblen = 0;
|
||||
}
|
||||
bblen += b->length;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_data) {
|
||||
make_chunk(f->c, fctx, fctx->bbchunk, first_data, bblen, NULL);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
apr_status_t h2_c2_filter_request_in(ap_filter_t* f,
|
||||
apr_bucket_brigade* bb,
|
||||
ap_input_mode_t mode,
|
||||
@ -808,7 +675,6 @@ apr_status_t h2_c2_filter_request_in(ap_filter_t* f,
|
||||
apr_off_t readbytes)
|
||||
{
|
||||
h2_conn_ctx_t *conn_ctx = h2_conn_ctx_get(f->c);
|
||||
h2_chunk_filter_t *fctx = f->ctx;
|
||||
request_rec *r = f->r;
|
||||
apr_status_t status = APR_SUCCESS;
|
||||
apr_bucket *b, *next;
|
||||
@ -817,89 +683,36 @@ apr_status_t h2_c2_filter_request_in(ap_filter_t* f,
|
||||
&core_module);
|
||||
ap_assert(conn_ctx);
|
||||
|
||||
if (!fctx) {
|
||||
fctx = apr_pcalloc(r->pool, sizeof(*fctx));
|
||||
fctx->id = apr_psprintf(r->pool, "%s-%d", conn_ctx->id, conn_ctx->stream_id);
|
||||
f->ctx = fctx;
|
||||
}
|
||||
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, f->r,
|
||||
"h2_c2(%s-%d): request input, exp=%d",
|
||||
conn_ctx->id, conn_ctx->stream_id, r->expecting_100);
|
||||
if (!conn_ctx->request->chunked) {
|
||||
status = ap_get_brigade(f->next, bb, mode, block, readbytes);
|
||||
/* pipe data through, just take care of trailers */
|
||||
for (b = APR_BRIGADE_FIRST(bb);
|
||||
b != APR_BRIGADE_SENTINEL(bb); b = next) {
|
||||
next = APR_BUCKET_NEXT(b);
|
||||
if (H2_BUCKET_IS_HEADERS(b)) {
|
||||
h2_headers *headers = h2_bucket_headers_get(b);
|
||||
ap_assert(headers);
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
||||
"h2_c2(%s-%d): receiving trailers",
|
||||
conn_ctx->id, conn_ctx->stream_id);
|
||||
r->trailers_in = headers->headers;
|
||||
if (conf && conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE) {
|
||||
r->headers_in = apr_table_overlay(r->pool, r->headers_in,
|
||||
r->trailers_in);
|
||||
}
|
||||
APR_BUCKET_REMOVE(b);
|
||||
apr_bucket_destroy(b);
|
||||
ap_remove_input_filter(f);
|
||||
|
||||
if (headers->raw_bytes && h2_c_logio_add_bytes_in) {
|
||||
h2_c_logio_add_bytes_in(f->c, headers->raw_bytes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Things are more complicated. The standard HTTP input filter, which
|
||||
* does a lot what we do not want to duplicate, also cares about chunked
|
||||
* transfer encoding and trailers.
|
||||
* We need to simulate chunked encoding for it to be happy.
|
||||
*/
|
||||
if ((status = read_and_chunk(f, conn_ctx, block)) != APR_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (mode == AP_MODE_EXHAUSTIVE) {
|
||||
/* return all we have */
|
||||
APR_BRIGADE_CONCAT(bb, fctx->bbchunk);
|
||||
}
|
||||
else if (mode == AP_MODE_READBYTES) {
|
||||
status = h2_brigade_concat_length(bb, fctx->bbchunk, readbytes);
|
||||
}
|
||||
else if (mode == AP_MODE_SPECULATIVE) {
|
||||
status = h2_brigade_copy_length(bb, fctx->bbchunk, readbytes);
|
||||
}
|
||||
else if (mode == AP_MODE_GETLINE) {
|
||||
/* we are reading a single LF line, e.g. the HTTP headers.
|
||||
* this has the nasty side effect to split the bucket, even
|
||||
* though it ends with CRLF and creates a 0 length bucket */
|
||||
status = apr_brigade_split_line(bb, fctx->bbchunk, block, HUGE_STRING_LEN);
|
||||
if (APLOGctrace1(f->c)) {
|
||||
char buffer[1024];
|
||||
apr_size_t len = sizeof(buffer)-1;
|
||||
apr_brigade_flatten(bb, buffer, &len);
|
||||
buffer[len] = 0;
|
||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
|
||||
"h2_c2(%s-%d): getline: %s",
|
||||
conn_ctx->id, conn_ctx->stream_id, buffer);
|
||||
status = ap_get_brigade(f->next, bb, mode, block, readbytes);
|
||||
/* pipe data through, just take care of trailers */
|
||||
for (b = APR_BRIGADE_FIRST(bb);
|
||||
b != APR_BRIGADE_SENTINEL(bb); b = next) {
|
||||
next = APR_BUCKET_NEXT(b);
|
||||
if (H2_BUCKET_IS_HEADERS(b)) {
|
||||
h2_headers *headers = h2_bucket_headers_get(b);
|
||||
ap_assert(headers);
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
||||
"h2_c2(%s-%d): receiving trailers",
|
||||
conn_ctx->id, conn_ctx->stream_id);
|
||||
r->trailers_in = headers->headers;
|
||||
if (conf && conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE) {
|
||||
r->headers_in = apr_table_overlay(r->pool, r->headers_in,
|
||||
r->trailers_in);
|
||||
}
|
||||
APR_BUCKET_REMOVE(b);
|
||||
apr_bucket_destroy(b);
|
||||
ap_remove_input_filter(f);
|
||||
|
||||
if (headers->raw_bytes && h2_c_logio_add_bytes_in) {
|
||||
h2_c_logio_add_bytes_in(f->c, headers->raw_bytes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not
|
||||
* to support it. Seems to work. */
|
||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c,
|
||||
APLOGNO(02942)
|
||||
"h2_c2, unsupported READ mode %d", mode);
|
||||
status = APR_ENOTIMPL;
|
||||
}
|
||||
|
||||
h2_util_bb_log(f->c, conn_ctx->stream_id, APLOG_TRACE2, "returning input", bb);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -632,8 +632,8 @@ static apr_status_t c1_process_stream(h2_mplx *m,
|
||||
if (APLOGctrace1(m->c1)) {
|
||||
const h2_request *r = stream->request;
|
||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c1,
|
||||
H2_STRM_MSG(stream, "process %s %s://%s%s chunked=%d"),
|
||||
r->method, r->scheme, r->authority, r->path, r->chunked);
|
||||
H2_STRM_MSG(stream, "process %s %s://%s%s"),
|
||||
r->method, r->scheme, r->authority, r->path);
|
||||
}
|
||||
|
||||
rv = h2_stream_setup_input(stream);
|
||||
|
@ -211,15 +211,7 @@ apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos,
|
||||
* internal request processing is used to HTTP/1.1, so we
|
||||
* need to either add a Content-Length or a Transfer-Encoding
|
||||
* if any content can be expected. */
|
||||
if (!eos) {
|
||||
/* We have not seen a content-length and have no eos,
|
||||
* simulate a chunked encoding for our HTTP/1.1 infrastructure,
|
||||
* in case we have "H2SerializeHeaders on" here
|
||||
*/
|
||||
req->chunked = 1;
|
||||
apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
|
||||
}
|
||||
else if (apr_table_get(req->headers, "Content-Type")) {
|
||||
if (eos && apr_table_get(req->headers, "Content-Type")) {
|
||||
/* If we have a content-type, but already seen eos, no more
|
||||
* data will come. Signal a zero content length explicitly.
|
||||
*/
|
||||
|
@ -815,7 +815,8 @@ PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *c
|
||||
PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
|
||||
{
|
||||
ap_add_input_filter("HTTP_IN", NULL, r, c);
|
||||
return OK;
|
||||
ap_add_input_filter("HTTP1_BODY_IN", NULL, r, c);
|
||||
return OK;
|
||||
}
|
||||
|
||||
PROXY_DECLARE(const char *) ap_proxy_location_reverse_map(request_rec *r,
|
||||
|
@ -14,7 +14,7 @@ LTLIBRARY_SOURCES = \
|
||||
util_charset.c util_cookies.c util_debug.c util_xml.c \
|
||||
util_filter.c util_pcre.c util_regex.c $(EXPORTS_DOT_C) \
|
||||
scoreboard.c error_bucket.c protocol.c core.c request.c ssl.c provider.c \
|
||||
eoc_bucket.c eor_bucket.c core_filters.c \
|
||||
eoc_bucket.c eor_bucket.c headers_bucket.c core_filters.c \
|
||||
util_expr_parse.c util_expr_scan.c util_expr_eval.c \
|
||||
apreq_cookie.c apreq_error.c apreq_module.c \
|
||||
apreq_module_cgi.c apreq_module_custom.c apreq_param.c \
|
||||
|
269
server/headers_bucket.c
Normal file
269
server/headers_bucket.c
Normal file
@ -0,0 +1,269 @@
|
||||
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "http_protocol.h"
|
||||
#include "apr_buckets.h"
|
||||
#include "apr_strings.h"
|
||||
#if APR_HAVE_STRINGS_H
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
||||
static apr_status_t dummy_read(apr_bucket *b, const char **str,
|
||||
apr_size_t *len, apr_read_type_e block)
|
||||
{
|
||||
*str = NULL;
|
||||
*len = 0;
|
||||
return APR_SUCCESS;
|
||||
}
|
||||
|
||||
static void request_bucket_destroy(void *data)
|
||||
{
|
||||
ap_bucket_request *h = data;
|
||||
|
||||
if (apr_bucket_shared_destroy(h)) {
|
||||
apr_bucket_free(h);
|
||||
}
|
||||
}
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_request_make(
|
||||
apr_bucket *b,
|
||||
const char *method,
|
||||
const char *uri,
|
||||
const char *protocol,
|
||||
apr_table_t *headers,
|
||||
apr_pool_t *p)
|
||||
{
|
||||
return ap_bucket_request_maken(b, apr_pstrdup(p, method),
|
||||
apr_pstrdup(p, uri), protocol,
|
||||
headers? apr_table_clone(p, headers) : NULL,
|
||||
p);
|
||||
}
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_request_maken(
|
||||
apr_bucket *b,
|
||||
const char *method,
|
||||
const char *uri,
|
||||
const char *protocol,
|
||||
apr_table_t *headers,
|
||||
apr_pool_t *p)
|
||||
{
|
||||
ap_bucket_request *h;
|
||||
|
||||
h = apr_bucket_alloc(sizeof(*h), b->list);
|
||||
h->pool = p;
|
||||
h->method = method;
|
||||
h->uri = uri;
|
||||
h->protocol = protocol;
|
||||
h->headers = headers;
|
||||
|
||||
b = apr_bucket_shared_make(b, h, 0, 0);
|
||||
b->type = &ap_bucket_type_request;
|
||||
return b;
|
||||
}
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_request_create(
|
||||
const char *method,
|
||||
const char *uri,
|
||||
const char *protocol,
|
||||
apr_table_t *headers,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list)
|
||||
{
|
||||
apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
|
||||
|
||||
APR_BUCKET_INIT(b);
|
||||
b->free = apr_bucket_free;
|
||||
b->list = list;
|
||||
return ap_bucket_request_make(b, method, uri, protocol, headers, p);
|
||||
}
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_request_createn(
|
||||
const char *method,
|
||||
const char *uri,
|
||||
const char *protocol,
|
||||
apr_table_t *headers,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list)
|
||||
{
|
||||
apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
|
||||
|
||||
APR_BUCKET_INIT(b);
|
||||
b->free = apr_bucket_free;
|
||||
b->list = list;
|
||||
return ap_bucket_request_maken(b, method, uri, protocol, headers, p);
|
||||
}
|
||||
|
||||
AP_DECLARE_DATA const apr_bucket_type_t ap_bucket_type_request = {
|
||||
"REQUEST", 5, APR_BUCKET_METADATA,
|
||||
request_bucket_destroy,
|
||||
dummy_read,
|
||||
apr_bucket_setaside_notimpl,
|
||||
apr_bucket_split_notimpl,
|
||||
apr_bucket_shared_copy
|
||||
};
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_request_clone(
|
||||
apr_bucket *source,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list)
|
||||
{
|
||||
ap_bucket_request *sreq = source->data;
|
||||
|
||||
AP_DEBUG_ASSERT(AP_BUCKET_IS_REQUEST(source));
|
||||
return ap_bucket_request_create(sreq->method, sreq->uri,
|
||||
sreq->protocol, sreq->headers, p, list);
|
||||
}
|
||||
|
||||
static void response_bucket_destroy(void *data)
|
||||
{
|
||||
ap_bucket_response *h = data;
|
||||
|
||||
if (apr_bucket_shared_destroy(h)) {
|
||||
apr_bucket_free(h);
|
||||
}
|
||||
}
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_response_make(apr_bucket *b, int status,
|
||||
const char *reason,
|
||||
apr_table_t *headers,
|
||||
apr_table_t *notes,
|
||||
apr_pool_t *p)
|
||||
{
|
||||
ap_bucket_response *h;
|
||||
|
||||
h = apr_bucket_alloc(sizeof(*h), b->list);
|
||||
h->pool = p;
|
||||
h->status = status;
|
||||
h->reason = reason? apr_pstrdup(p, reason) : NULL;
|
||||
h->headers = headers? apr_table_copy(p, headers) : apr_table_make(p, 5);
|
||||
h->notes = notes? apr_table_copy(p, notes) : apr_table_make(p, 5);
|
||||
|
||||
b = apr_bucket_shared_make(b, h, 0, 0);
|
||||
b->type = &ap_bucket_type_response;
|
||||
return b;
|
||||
}
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_response_create(int status, const char *reason,
|
||||
apr_table_t *headers,
|
||||
apr_table_t *notes,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list)
|
||||
{
|
||||
apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
|
||||
|
||||
APR_BUCKET_INIT(b);
|
||||
b->free = apr_bucket_free;
|
||||
b->list = list;
|
||||
return ap_bucket_response_make(b, status, reason, headers, notes, p);
|
||||
}
|
||||
|
||||
AP_DECLARE_DATA const apr_bucket_type_t ap_bucket_type_response = {
|
||||
"RESPONSE", 5, APR_BUCKET_METADATA,
|
||||
response_bucket_destroy,
|
||||
dummy_read,
|
||||
apr_bucket_setaside_notimpl,
|
||||
apr_bucket_split_notimpl,
|
||||
apr_bucket_shared_copy
|
||||
};
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_response_clone(apr_bucket *source,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list)
|
||||
{
|
||||
ap_bucket_response *sresp = source->data;
|
||||
apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
|
||||
ap_bucket_response *h;
|
||||
|
||||
AP_DEBUG_ASSERT(AP_BUCKET_IS_RESPONSE(source));
|
||||
APR_BUCKET_INIT(b);
|
||||
b->free = apr_bucket_free;
|
||||
b->list = list;
|
||||
h = apr_bucket_alloc(sizeof(*h), b->list);
|
||||
h->status = sresp->status;
|
||||
h->reason = sresp->reason? apr_pstrdup(p, sresp->reason) : NULL;
|
||||
h->headers = apr_table_clone(p, sresp->headers);
|
||||
h->notes = apr_table_clone(p, sresp->notes);
|
||||
|
||||
b = apr_bucket_shared_make(b, h, 0, 0);
|
||||
b->type = &ap_bucket_type_response;
|
||||
return b;
|
||||
}
|
||||
|
||||
static void headers_bucket_destroy(void *data)
|
||||
{
|
||||
ap_bucket_headers *h = data;
|
||||
|
||||
if (apr_bucket_shared_destroy(h)) {
|
||||
apr_bucket_free(h);
|
||||
}
|
||||
}
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_headers_make(apr_bucket *b,
|
||||
apr_table_t *headers,
|
||||
apr_pool_t *p)
|
||||
{
|
||||
ap_bucket_headers *h;
|
||||
|
||||
h = apr_bucket_alloc(sizeof(*h), b->list);
|
||||
h->pool = p;
|
||||
h->headers = headers? apr_table_copy(p, headers) : apr_table_make(p, 5);
|
||||
|
||||
b = apr_bucket_shared_make(b, h, 0, 0);
|
||||
b->type = &ap_bucket_type_headers;
|
||||
return b;
|
||||
}
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_headers_create(apr_table_t *headers,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list)
|
||||
{
|
||||
apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
|
||||
|
||||
APR_BUCKET_INIT(b);
|
||||
b->free = apr_bucket_free;
|
||||
b->list = list;
|
||||
return ap_bucket_headers_make(b, headers, p);
|
||||
}
|
||||
|
||||
AP_DECLARE_DATA const apr_bucket_type_t ap_bucket_type_headers = {
|
||||
"HEADERS", 5, APR_BUCKET_METADATA,
|
||||
headers_bucket_destroy,
|
||||
dummy_read,
|
||||
apr_bucket_setaside_notimpl,
|
||||
apr_bucket_split_notimpl,
|
||||
apr_bucket_shared_copy
|
||||
};
|
||||
|
||||
AP_DECLARE(apr_bucket *) ap_bucket_headers_clone(apr_bucket *source,
|
||||
apr_pool_t *p,
|
||||
apr_bucket_alloc_t *list)
|
||||
{
|
||||
ap_bucket_headers *shdrs = source->data;
|
||||
apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
|
||||
ap_bucket_headers *h;
|
||||
|
||||
AP_DEBUG_ASSERT(AP_BUCKET_IS_HEADERS(source));
|
||||
APR_BUCKET_INIT(b);
|
||||
b->free = apr_bucket_free;
|
||||
b->list = list;
|
||||
h = apr_bucket_alloc(sizeof(*h), b->list);
|
||||
h->headers = apr_table_clone(p, shdrs->headers);
|
||||
|
||||
b = apr_bucket_shared_make(b, h, 0, 0);
|
||||
b->type = &ap_bucket_type_headers;
|
||||
return b;
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "mod_core.h"
|
||||
#include "util_charset.h"
|
||||
#include "util_ebcdic.h"
|
||||
#include "util_time.h"
|
||||
#include "scoreboard.h"
|
||||
|
||||
#if APR_HAVE_STDARG_H
|
||||
@ -1465,6 +1466,18 @@ static void apply_server_config(request_rec *r)
|
||||
r->per_dir_config = r->server->lookup_defaults;
|
||||
}
|
||||
|
||||
AP_DECLARE(int) ap_assign_request(request_rec *r,
|
||||
const char *method, const char *uri,
|
||||
const char *protocol)
|
||||
{
|
||||
/* dummy, for now */
|
||||
(void)r;
|
||||
(void)method;
|
||||
(void)uri;
|
||||
(void)protocol;
|
||||
return 0;
|
||||
}
|
||||
|
||||
request_rec *ap_read_request(conn_rec *conn)
|
||||
{
|
||||
int access_status;
|
||||
@ -1583,6 +1596,14 @@ request_rec *ap_read_request(conn_rec *conn)
|
||||
*/
|
||||
ap_add_input_filter_handle(ap_http_input_filter_handle,
|
||||
NULL, r, r->connection);
|
||||
if (r->proto_num <= HTTP_VERSION(1,1)) {
|
||||
ap_add_input_filter_handle(ap_h1_body_in_filter_handle,
|
||||
NULL, r, r->connection);
|
||||
if (r->proto_num >= HTTP_VERSION(1,0)
|
||||
&& apr_table_get(r->headers_in, "Transfer-Encoding")) {
|
||||
r->body_indeterminate = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate Host/Expect headers and select vhost. */
|
||||
if (!ap_check_request_header(r)) {
|
||||
@ -2385,6 +2406,38 @@ static int send_header(void *data, const char *key, const char *val)
|
||||
}
|
||||
#endif
|
||||
|
||||
AP_DECLARE(void) ap_set_std_response_headers(request_rec *r)
|
||||
{
|
||||
const char *server = NULL, *date;
|
||||
char *s;
|
||||
|
||||
/* Before generating a response, we make sure that `Date` and `Server`
|
||||
* headers are present. When proxying requests, we preserver existing
|
||||
* values and replace them otherwise.
|
||||
*/
|
||||
if (r->proxyreq != PROXYREQ_NONE) {
|
||||
date = apr_table_get(r->headers_out, "Date");
|
||||
if (!date) {
|
||||
s = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
|
||||
ap_recent_rfc822_date(s, r->request_time);
|
||||
date = s;
|
||||
}
|
||||
server = apr_table_get(r->headers_out, "Server");
|
||||
}
|
||||
else {
|
||||
s = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
|
||||
ap_recent_rfc822_date(s, r->request_time);
|
||||
date = s;
|
||||
}
|
||||
|
||||
apr_table_setn(r->headers_out, "Date", date);
|
||||
|
||||
if (!server)
|
||||
server = ap_get_server_banner();
|
||||
if (server && *server)
|
||||
apr_table_setn(r->headers_out, "Server", server);
|
||||
}
|
||||
|
||||
AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers)
|
||||
{
|
||||
hdr_ptr x;
|
||||
|
Loading…
x
Reference in New Issue
Block a user