1
0
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:
Stefan Eissing
2015-11-20 13:58:32 +00:00
parent 7b0b425f7a
commit a41cbc4dcd
7 changed files with 187 additions and 41 deletions

View File

@@ -69,14 +69,7 @@ static apr_status_t add_h1_header(h2_request *req, apr_pool_t *pool,
{
char *hname, *hvalue;
if (H2_HD_MATCH_LIT("expect", 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. */
if (h2_req_ignore_header(name, nlen)) {
return APR_SUCCESS;
}
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)
{
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;
}
@@ -222,23 +218,11 @@ apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
return APR_EINVAL;
}
/* be safe, some header we do not accept on h2(c) */
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) {
return APR_BADARG;
}
apr_table_set(req->headers, "Host", req->authority);
/* Always set the "Host" header from :authority, see rfc7540, ch. 8.1.2.3 */
if (!req->authority) {
return APR_BADARG;
}
apr_table_setn(req->headers, "Host", req->authority);
s = apr_table_get(req->headers, "Content-Length");
if (s) {
@@ -290,15 +274,7 @@ static apr_status_t add_h1_trailer(h2_request *req, apr_pool_t *pool,
{
char *hname, *hvalue;
if (H2_HD_MATCH_LIT("expect", 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. */
if (h2_req_ignore_trailer(name, nlen)) {
return APR_SUCCESS;
}

View File

@@ -24,6 +24,7 @@ typedef struct h2_response {
int http_status;
apr_off_t content_length;
apr_table_t *header;
apr_table_t *trailer;
} h2_response;
h2_response *h2_response_create(int stream_id,

View File

@@ -1076,6 +1076,18 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
}
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;
}

View File

@@ -304,17 +304,22 @@ apr_status_t h2_stream_schedule(h2_stream *stream, int eos,
status = h2_mplx_process(stream->session->mplx, stream->id,
stream->request, eos, cmp, ctx);
stream->scheduled = 1;
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c,
"h2_stream(%ld-%d): scheduled %s %s://%s%s",
stream->session->id, stream->id,
stream->request->method, stream->request->scheme,
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);
}
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, stream->session->c,
"h2_stream(%ld-%d): scheduled %s %s://%s%s",
stream->session->id, stream->id,
stream->request->method, stream->request->scheme,
stream->request->authority, stream->request->path);
return status;
}
@@ -365,6 +370,22 @@ static apr_status_t input_add_data(h2_stream *stream,
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 status = APR_SUCCESS;
@@ -381,7 +402,15 @@ apr_status_t h2_stream_close_input(h2_stream *stream)
H2_STREAM_IN(APLOG_TRACE2, stream, "close_pre");
if (close_input(stream) && stream->bbin) {
if (stream->request->chunked) {
status = input_add_data(stream, "0\r\n\r\n", 5, 0);
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);
}
}
if (status == APR_SUCCESS) {
@@ -610,3 +639,9 @@ apr_status_t h2_stream_submit_pushes(h2_stream *stream)
}
return status;
}
apr_table_t *h2_stream_get_trailers(h2_stream *stream)
{
/* TODO */
return NULL;
}

View File

@@ -290,4 +290,14 @@ int h2_stream_needs_submit(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__) */

View File

@@ -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,
int http_status,
apr_table_t *header)
@@ -879,3 +894,94 @@ h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p,
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);
}
}

View File

@@ -30,6 +30,11 @@ char *h2_strlwr(char *s);
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
* HTTP header syntax, rfc7230.
@@ -75,6 +80,7 @@ typedef struct h2_ngheader {
apr_size_t nvlen;
} 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,
int http_status,
apr_table_t *header);