mirror of
https://github.com/apache/httpd.git
synced 2025-08-08 15:02:10 +03:00
incoming trailers passed into chunked request bodies, outgoing trailers not supported yet
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1715363 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -69,14 +69,7 @@ static apr_status_t add_h1_header(h2_request *req, apr_pool_t *pool,
|
|||||||
{
|
{
|
||||||
char *hname, *hvalue;
|
char *hname, *hvalue;
|
||||||
|
|
||||||
if (H2_HD_MATCH_LIT("expect", name, nlen)
|
if (h2_req_ignore_header(name, nlen)) {
|
||||||
|| H2_HD_MATCH_LIT("upgrade", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("connection", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("proxy-connection", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("transfer-encoding", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("keep-alive", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("http2-settings", name, nlen)) {
|
|
||||||
/* ignore these. */
|
|
||||||
return APR_SUCCESS;
|
return APR_SUCCESS;
|
||||||
}
|
}
|
||||||
else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
|
else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
|
||||||
@@ -115,7 +108,10 @@ typedef struct {
|
|||||||
static int set_h1_header(void *ctx, const char *key, const char *value)
|
static int set_h1_header(void *ctx, const char *key, const char *value)
|
||||||
{
|
{
|
||||||
h1_ctx *x = ctx;
|
h1_ctx *x = ctx;
|
||||||
add_h1_header(x->req, x->pool, key, strlen(key), value, strlen(value));
|
size_t klen = strlen(key);
|
||||||
|
if (!h2_req_ignore_header(key, klen)) {
|
||||||
|
add_h1_header(x->req, x->pool, key, klen, value, strlen(value));
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,23 +218,11 @@ apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
|
|||||||
return APR_EINVAL;
|
return APR_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* be safe, some header we do not accept on h2(c) */
|
/* Always set the "Host" header from :authority, see rfc7540, ch. 8.1.2.3 */
|
||||||
apr_table_unset(req->headers, "expect");
|
|
||||||
apr_table_unset(req->headers, "upgrade");
|
|
||||||
apr_table_unset(req->headers, "connection");
|
|
||||||
apr_table_unset(req->headers, "proxy-connection");
|
|
||||||
apr_table_unset(req->headers, "transfer-encoding");
|
|
||||||
apr_table_unset(req->headers, "keep-alive");
|
|
||||||
apr_table_unset(req->headers, "http2-settings");
|
|
||||||
|
|
||||||
if (!apr_table_get(req->headers, "Host")) {
|
|
||||||
/* Need to add a "Host" header if not already there to
|
|
||||||
* make virtual hosts work correctly. */
|
|
||||||
if (!req->authority) {
|
if (!req->authority) {
|
||||||
return APR_BADARG;
|
return APR_BADARG;
|
||||||
}
|
}
|
||||||
apr_table_set(req->headers, "Host", req->authority);
|
apr_table_setn(req->headers, "Host", req->authority);
|
||||||
}
|
|
||||||
|
|
||||||
s = apr_table_get(req->headers, "Content-Length");
|
s = apr_table_get(req->headers, "Content-Length");
|
||||||
if (s) {
|
if (s) {
|
||||||
@@ -290,15 +274,7 @@ static apr_status_t add_h1_trailer(h2_request *req, apr_pool_t *pool,
|
|||||||
{
|
{
|
||||||
char *hname, *hvalue;
|
char *hname, *hvalue;
|
||||||
|
|
||||||
if (H2_HD_MATCH_LIT("expect", name, nlen)
|
if (h2_req_ignore_trailer(name, nlen)) {
|
||||||
|| H2_HD_MATCH_LIT("upgrade", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("connection", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("host", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("proxy-connection", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("transfer-encoding", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("keep-alive", name, nlen)
|
|
||||||
|| H2_HD_MATCH_LIT("http2-settings", name, nlen)) {
|
|
||||||
/* ignore these. */
|
|
||||||
return APR_SUCCESS;
|
return APR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ typedef struct h2_response {
|
|||||||
int http_status;
|
int http_status;
|
||||||
apr_off_t content_length;
|
apr_off_t content_length;
|
||||||
apr_table_t *header;
|
apr_table_t *header;
|
||||||
|
apr_table_t *trailer;
|
||||||
} h2_response;
|
} h2_response;
|
||||||
|
|
||||||
h2_response *h2_response_create(int stream_id,
|
h2_response *h2_response_create(int stream_id,
|
||||||
|
@@ -1076,6 +1076,18 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (eos) {
|
if (eos) {
|
||||||
|
apr_table_t *trailers = h2_stream_get_trailers(stream);
|
||||||
|
if (trailers && !apr_is_empty_table(trailers)) {
|
||||||
|
h2_ngheader *nh;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
nh = h2_util_ngheader_make(stream->pool, trailers);
|
||||||
|
rv = nghttp2_submit_trailer(ng2s, stream->id, nh->nv, nh->nvlen);
|
||||||
|
if (rv < 0) {
|
||||||
|
nread = rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -304,16 +304,21 @@ apr_status_t h2_stream_schedule(h2_stream *stream, int eos,
|
|||||||
status = h2_mplx_process(stream->session->mplx, stream->id,
|
status = h2_mplx_process(stream->session->mplx, stream->id,
|
||||||
stream->request, eos, cmp, ctx);
|
stream->request, eos, cmp, ctx);
|
||||||
stream->scheduled = 1;
|
stream->scheduled = 1;
|
||||||
}
|
|
||||||
else {
|
|
||||||
h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, stream->session->c,
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c,
|
||||||
"h2_stream(%ld-%d): scheduled %s %s://%s%s",
|
"h2_stream(%ld-%d): scheduled %s %s://%s%s",
|
||||||
stream->session->id, stream->id,
|
stream->session->id, stream->id,
|
||||||
stream->request->method, stream->request->scheme,
|
stream->request->method, stream->request->scheme,
|
||||||
stream->request->authority, stream->request->path);
|
stream->request->authority, stream->request->path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR);
|
||||||
|
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c,
|
||||||
|
"h2_stream(%ld-%d): RST=2 (internal err) %s %s://%s%s",
|
||||||
|
stream->session->id, stream->id,
|
||||||
|
stream->request->method, stream->request->scheme,
|
||||||
|
stream->request->authority, stream->request->path);
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -365,6 +370,22 @@ static apr_status_t input_add_data(h2_stream *stream,
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int input_add_header(void *str, const char *key, const char *value)
|
||||||
|
{
|
||||||
|
h2_stream *stream = str;
|
||||||
|
apr_status_t status = input_add_data(stream, key, strlen(key), 0);
|
||||||
|
if (status == APR_SUCCESS) {
|
||||||
|
status = input_add_data(stream, ": ", 2, 0);
|
||||||
|
if (status == APR_SUCCESS) {
|
||||||
|
status = input_add_data(stream, value, strlen(value), 0);
|
||||||
|
if (status == APR_SUCCESS) {
|
||||||
|
status = input_add_data(stream, "\r\n", 2, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (status == APR_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
apr_status_t h2_stream_close_input(h2_stream *stream)
|
apr_status_t h2_stream_close_input(h2_stream *stream)
|
||||||
{
|
{
|
||||||
apr_status_t status = APR_SUCCESS;
|
apr_status_t status = APR_SUCCESS;
|
||||||
@@ -381,8 +402,16 @@ apr_status_t h2_stream_close_input(h2_stream *stream)
|
|||||||
H2_STREAM_IN(APLOG_TRACE2, stream, "close_pre");
|
H2_STREAM_IN(APLOG_TRACE2, stream, "close_pre");
|
||||||
if (close_input(stream) && stream->bbin) {
|
if (close_input(stream) && stream->bbin) {
|
||||||
if (stream->request->chunked) {
|
if (stream->request->chunked) {
|
||||||
|
apr_table_t *trailers = stream->request->trailers;
|
||||||
|
if (trailers && !apr_is_empty_table(trailers)) {
|
||||||
|
status = input_add_data(stream, "0\r\n", 3, 0);
|
||||||
|
apr_table_do(input_add_header, stream, trailers, NULL);
|
||||||
|
status = input_add_data(stream, "\r\n", 2, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
status = input_add_data(stream, "0\r\n\r\n", 5, 0);
|
status = input_add_data(stream, "0\r\n\r\n", 5, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (status == APR_SUCCESS) {
|
if (status == APR_SUCCESS) {
|
||||||
status = h2_stream_input_flush(stream);
|
status = h2_stream_input_flush(stream);
|
||||||
@@ -610,3 +639,9 @@ apr_status_t h2_stream_submit_pushes(h2_stream *stream)
|
|||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apr_table_t *h2_stream_get_trailers(h2_stream *stream)
|
||||||
|
{
|
||||||
|
/* TODO */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
@@ -290,4 +290,14 @@ int h2_stream_needs_submit(h2_stream *stream);
|
|||||||
*/
|
*/
|
||||||
apr_status_t h2_stream_submit_pushes(h2_stream *stream);
|
apr_status_t h2_stream_submit_pushes(h2_stream *stream);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get optional trailers for this stream, may be NULL. Meaningful
|
||||||
|
* results can only be expected when the end of the response body has
|
||||||
|
* been reached.
|
||||||
|
*
|
||||||
|
* @param stream to ask for trailers
|
||||||
|
* @return trailers for NULL
|
||||||
|
*/
|
||||||
|
apr_table_t *h2_stream_get_trailers(h2_stream *stream);
|
||||||
|
|
||||||
#endif /* defined(__mod_h2__h2_stream__) */
|
#endif /* defined(__mod_h2__h2_stream__) */
|
||||||
|
@@ -834,6 +834,21 @@ static int add_table_header(void *ctx, const char *key, const char *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
h2_ngheader *h2_util_ngheader_make(apr_pool_t *p, apr_table_t *header)
|
||||||
|
{
|
||||||
|
h2_ngheader *ngh;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
apr_table_do(count_header, &n, header, NULL);
|
||||||
|
|
||||||
|
ngh = apr_pcalloc(p, sizeof(h2_ngheader));
|
||||||
|
ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv));
|
||||||
|
apr_table_do(add_table_header, ngh, header, NULL);
|
||||||
|
|
||||||
|
return ngh;
|
||||||
|
}
|
||||||
|
|
||||||
h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p,
|
h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p,
|
||||||
int http_status,
|
int http_status,
|
||||||
apr_table_t *header)
|
apr_table_t *header)
|
||||||
@@ -879,3 +894,94 @@ h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p,
|
|||||||
return ngh;
|
return ngh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* header HTTP/1 <-> HTTP/2 conversions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
size_t len;
|
||||||
|
} literal;
|
||||||
|
|
||||||
|
#define H2_DEF_LITERAL(n) { (n), (sizeof(n)-1) }
|
||||||
|
#define H2_ALEN(a) (sizeof(a)/sizeof((a)[0]))
|
||||||
|
#define H2_LIT_ARGS(a) (a),H2_ALEN(a)
|
||||||
|
|
||||||
|
static literal IgnoredRequestHeaders[] = {
|
||||||
|
H2_DEF_LITERAL("host"),
|
||||||
|
H2_DEF_LITERAL("expect"),
|
||||||
|
H2_DEF_LITERAL("upgrade"),
|
||||||
|
H2_DEF_LITERAL("connection"),
|
||||||
|
H2_DEF_LITERAL("keep-alive"),
|
||||||
|
H2_DEF_LITERAL("http2-settings"),
|
||||||
|
H2_DEF_LITERAL("proxy-connection"),
|
||||||
|
H2_DEF_LITERAL("transfer-encoding"),
|
||||||
|
};
|
||||||
|
static literal IgnoredRequestTrailers[] = { /* Ignore, see rfc7230, ch. 4.1.2 */
|
||||||
|
H2_DEF_LITERAL("te"),
|
||||||
|
H2_DEF_LITERAL("host"),
|
||||||
|
H2_DEF_LITERAL("range"),
|
||||||
|
H2_DEF_LITERAL("cookie"),
|
||||||
|
H2_DEF_LITERAL("expect"),
|
||||||
|
H2_DEF_LITERAL("pragma"),
|
||||||
|
H2_DEF_LITERAL("max-forwards"),
|
||||||
|
H2_DEF_LITERAL("cache-control"),
|
||||||
|
H2_DEF_LITERAL("authorization"),
|
||||||
|
H2_DEF_LITERAL("content-length"),
|
||||||
|
H2_DEF_LITERAL("proxy-authorization"),
|
||||||
|
};
|
||||||
|
static literal IgnoredResponseTrailers[] = {
|
||||||
|
H2_DEF_LITERAL("age"),
|
||||||
|
H2_DEF_LITERAL("date"),
|
||||||
|
H2_DEF_LITERAL("vary"),
|
||||||
|
H2_DEF_LITERAL("cookie"),
|
||||||
|
H2_DEF_LITERAL("expires"),
|
||||||
|
H2_DEF_LITERAL("warning"),
|
||||||
|
H2_DEF_LITERAL("location"),
|
||||||
|
H2_DEF_LITERAL("retry-after"),
|
||||||
|
H2_DEF_LITERAL("cache-control"),
|
||||||
|
H2_DEF_LITERAL("www-authenticate"),
|
||||||
|
H2_DEF_LITERAL("proxy-authenticate"),
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ignore_header(const literal *lits, size_t llen,
|
||||||
|
const char *name, size_t nlen)
|
||||||
|
{
|
||||||
|
const literal *lit;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < llen; ++i) {
|
||||||
|
lit = &lits[i];
|
||||||
|
if (lit->len == nlen && !apr_strnatcasecmp(lit->name, name)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int h2_req_ignore_header(const char *name, size_t len)
|
||||||
|
{
|
||||||
|
return ignore_header(H2_LIT_ARGS(IgnoredRequestHeaders), name, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int h2_req_ignore_trailer(const char *name, size_t len)
|
||||||
|
{
|
||||||
|
return (h2_req_ignore_header(name, len)
|
||||||
|
|| ignore_header(H2_LIT_ARGS(IgnoredRequestTrailers), name, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
int h2_res_ignore_trailer(const char *name, size_t len)
|
||||||
|
{
|
||||||
|
return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void h2_req_strip_ignored_header(apr_table_t *headers)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < H2_ALEN(IgnoredRequestHeaders); ++i) {
|
||||||
|
apr_table_unset(headers, IgnoredRequestHeaders[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -30,6 +30,11 @@ char *h2_strlwr(char *s);
|
|||||||
|
|
||||||
void h2_util_camel_case_header(char *s, size_t len);
|
void h2_util_camel_case_header(char *s, size_t len);
|
||||||
|
|
||||||
|
int h2_req_ignore_header(const char *name, size_t len);
|
||||||
|
int h2_req_ignore_trailer(const char *name, size_t len);
|
||||||
|
void h2_req_strip_ignored_header(apr_table_t *headers);
|
||||||
|
int h2_res_ignore_trailer(const char *name, size_t len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return != 0 iff the string s contains the token, as specified in
|
* Return != 0 iff the string s contains the token, as specified in
|
||||||
* HTTP header syntax, rfc7230.
|
* HTTP header syntax, rfc7230.
|
||||||
@@ -75,6 +80,7 @@ typedef struct h2_ngheader {
|
|||||||
apr_size_t nvlen;
|
apr_size_t nvlen;
|
||||||
} h2_ngheader;
|
} h2_ngheader;
|
||||||
|
|
||||||
|
h2_ngheader *h2_util_ngheader_make(apr_pool_t *p, apr_table_t *header);
|
||||||
h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p,
|
h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p,
|
||||||
int http_status,
|
int http_status,
|
||||||
apr_table_t *header);
|
apr_table_t *header);
|
||||||
|
Reference in New Issue
Block a user