You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2015/11/20 16:13:11 UTC
svn commit: r1715371 [3/6] - in /httpd/httpd/branches/2.4.x: ./
docs/manual/mod/ modules/http2/
Added: httpd/httpd/branches/2.4.x/modules/http2/h2_push.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_push.c?rev=1715371&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_push.c (added)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_push.c Fri Nov 20 15:13:11 2015
@@ -0,0 +1,400 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <apr_strings.h>
+#include <apr_lib.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_h2.h"
+#include "h2_util.h"
+#include "h2_push.h"
+#include "h2_request.h"
+#include "h2_response.h"
+
+
+typedef struct {
+ const h2_request *req;
+ apr_pool_t *pool;
+ apr_array_header_t *pushes;
+ const char *s;
+ size_t slen;
+ size_t i;
+
+ const char *link;
+ apr_table_t *params;
+ char b[4096];
+} link_ctx;
+
+static int attr_char(char c)
+{
+ switch (c) {
+ case '!':
+ case '#':
+ case '$':
+ case '&':
+ case '+':
+ case '-':
+ case '.':
+ case '^':
+ case '_':
+ case '`':
+ case '|':
+ case '~':
+ return 1;
+ default:
+ return apr_isalnum(c);
+ }
+}
+
+static int ptoken_char(char c)
+{
+ switch (c) {
+ case '!':
+ case '#':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case '-':
+ case '.':
+ case '/':
+ case ':':
+ case '<':
+ case '=':
+ case '>':
+ case '?':
+ case '@':
+ case '[':
+ case ']':
+ case '^':
+ case '_':
+ case '`':
+ case '{':
+ case '|':
+ case '}':
+ case '~':
+ return 1;
+ default:
+ return apr_isalnum(c);
+ }
+}
+
+static int skip_ws(link_ctx *ctx)
+{
+ char c;
+ while (ctx->i < ctx->slen
+ && (((c = ctx->s[ctx->i]) == ' ') || (c == '\t'))) {
+ ++ctx->i;
+ }
+ return (ctx->i < ctx->slen);
+}
+
+static int find_chr(link_ctx *ctx, char c, size_t *pidx)
+{
+ size_t j;
+ for (j = ctx->i; j < ctx->slen; ++j) {
+ if (ctx->s[j] == c) {
+ *pidx = j;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int read_chr(link_ctx *ctx, char c)
+{
+ if (ctx->i < ctx->slen && ctx->s[ctx->i] == c) {
+ ++ctx->i;
+ return 1;
+ }
+ return 0;
+}
+
+static char *mk_str(link_ctx *ctx, size_t end)
+{
+ if (ctx->i < end) {
+ return apr_pstrndup(ctx->pool, ctx->s + ctx->i, end - ctx->i);
+ }
+ return "";
+}
+
+static int read_qstring(link_ctx *ctx, char **ps)
+{
+ if (skip_ws(ctx) && read_chr(ctx, '\"')) {
+ size_t end;
+ if (find_chr(ctx, '\"', &end)) {
+ *ps = mk_str(ctx, end);
+ ctx->i = end + 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int read_ptoken(link_ctx *ctx, char **ps)
+{
+ if (skip_ws(ctx)) {
+ size_t i;
+ for (i = ctx->i; i < ctx->slen && ptoken_char(ctx->s[i]); ++i) {
+ /* nop */
+ }
+ if (i > ctx->i) {
+ *ps = mk_str(ctx, i);
+ ctx->i = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static int read_link(link_ctx *ctx)
+{
+ if (skip_ws(ctx) && read_chr(ctx, '<')) {
+ size_t end;
+ if (find_chr(ctx, '>', &end)) {
+ ctx->link = mk_str(ctx, end);
+ ctx->i = end + 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int read_pname(link_ctx *ctx, char **pname)
+{
+ if (skip_ws(ctx)) {
+ size_t i;
+ for (i = ctx->i; i < ctx->slen && attr_char(ctx->s[i]); ++i) {
+ /* nop */
+ }
+ if (i > ctx->i) {
+ *pname = mk_str(ctx, i);
+ ctx->i = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int read_pvalue(link_ctx *ctx, char **pvalue)
+{
+ if (skip_ws(ctx) && read_chr(ctx, '=')) {
+ if (read_qstring(ctx, pvalue) || read_ptoken(ctx, pvalue)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int read_param(link_ctx *ctx)
+{
+ if (skip_ws(ctx) && read_chr(ctx, ';')) {
+ char *name, *value = "";
+ if (read_pname(ctx, &name)) {
+ read_pvalue(ctx, &value); /* value is optional */
+ apr_table_setn(ctx->params, name, value);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int read_sep(link_ctx *ctx)
+{
+ if (skip_ws(ctx) && read_chr(ctx, ',')) {
+ return 1;
+ }
+ return 0;
+}
+
+static void init_params(link_ctx *ctx)
+{
+ if (!ctx->params) {
+ ctx->params = apr_table_make(ctx->pool, 5);
+ }
+ else {
+ apr_table_clear(ctx->params);
+ }
+}
+
+static int same_authority(const h2_request *req, const apr_uri_t *uri)
+{
+ if (uri->scheme != NULL && strcmp(uri->scheme, req->scheme)) {
+ return 0;
+ }
+ if (uri->hostinfo != NULL && strcmp(uri->hostinfo, req->authority)) {
+ return 0;
+ }
+ return 1;
+}
+
+static int set_header(void *ctx, const char *key, const char *value)
+{
+ apr_table_setn(ctx, key, value);
+ return 1;
+}
+
+
+static int add_push(link_ctx *ctx)
+{
+ /* so, we have read a Link header and need to decide
+ * if we transform it into a push.
+ */
+ const char *rel = apr_table_get(ctx->params, "rel");
+ if (rel && !strcmp("preload", rel)) {
+ apr_uri_t uri;
+ if (apr_uri_parse(ctx->pool, ctx->link, &uri) == APR_SUCCESS) {
+ if (uri.path && same_authority(ctx->req, &uri)) {
+ char *path;
+ apr_table_t *headers;
+ h2_request *req;
+ h2_push *push;
+
+ /* We only want to generate pushes for resources in the
+ * same authority than the original request.
+ * icing: i think that is wise, otherwise we really need to
+ * check that the vhost/server is available and uses the same
+ * TLS (if any) parameters.
+ */
+ path = apr_uri_unparse(ctx->pool, &uri, APR_URI_UNP_OMITSITEPART);
+
+ push = apr_pcalloc(ctx->pool, sizeof(*push));
+ push->initiating_id = ctx->req->id;
+
+ headers = apr_table_make(ctx->pool, 5);
+ apr_table_do(set_header, headers, ctx->req->headers,
+ "User-Agent",
+ "Cache-Control",
+ "Accept-Language",
+ NULL);
+ /* TODO: which headers do we add here?
+ */
+
+ req = h2_request_createn(0, ctx->pool,
+ ctx->req->method,
+ ctx->req->scheme,
+ ctx->req->authority,
+ path, headers);
+ h2_request_end_headers(req, ctx->pool, 1);
+ push->req = req;
+
+ if (!ctx->pushes) {
+ ctx->pushes = apr_array_make(ctx->pool, 5, sizeof(h2_push*));
+ }
+ APR_ARRAY_PUSH(ctx->pushes, h2_push*) = push;
+ }
+ }
+ }
+ return 0;
+}
+
+static void inspect_link(link_ctx *ctx, const char *s, size_t slen)
+{
+ /* RFC 5988 <https://tools.ietf.org/html/rfc5988#section-6.2.1>
+ Link = "Link" ":" #link-value
+ link-value = "<" URI-Reference ">" *( ";" link-param )
+ link-param = ( ( "rel" "=" relation-types )
+ | ( "anchor" "=" <"> URI-Reference <"> )
+ | ( "rev" "=" relation-types )
+ | ( "hreflang" "=" Language-Tag )
+ | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) )
+ | ( "title" "=" quoted-string )
+ | ( "title*" "=" ext-value )
+ | ( "type" "=" ( media-type | quoted-mt ) )
+ | ( link-extension ) )
+ link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] )
+ | ( ext-name-star "=" ext-value )
+ ext-name-star = parmname "*" ; reserved for RFC2231-profiled
+ ; extensions. Whitespace NOT
+ ; allowed in between.
+ ptoken = 1*ptokenchar
+ ptokenchar = "!" | "#" | "$" | "%" | "&" | "'" | "("
+ | ")" | "*" | "+" | "-" | "." | "/" | DIGIT
+ | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA
+ | "[" | "]" | "^" | "_" | "`" | "{" | "|"
+ | "}" | "~"
+ media-type = type-name "/" subtype-name
+ quoted-mt = <"> media-type <">
+ relation-types = relation-type
+ | <"> relation-type *( 1*SP relation-type ) <">
+ relation-type = reg-rel-type | ext-rel-type
+ reg-rel-type = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
+ ext-rel-type = URI
+
+ and from <https://tools.ietf.org/html/rfc5987>
+ parmname = 1*attr-char
+ attr-char = ALPHA / DIGIT
+ / "!" / "#" / "$" / "&" / "+" / "-" / "."
+ / "^" / "_" / "`" / "|" / "~"
+ */
+
+ ctx->s = s;
+ ctx->slen = slen;
+ ctx->i = 0;
+
+ while (read_link(ctx)) {
+ init_params(ctx);
+ while (read_param(ctx)) {
+ /* nop */
+ }
+ add_push(ctx);
+ if (!read_sep(ctx)) {
+ break;
+ }
+ }
+}
+
+static int head_iter(void *ctx, const char *key, const char *value)
+{
+ if (!apr_strnatcasecmp("link", key)) {
+ inspect_link(ctx, value, strlen(value));
+ }
+ return 1;
+}
+
+apr_array_header_t *h2_push_collect(apr_pool_t *p, const h2_request *req,
+ const h2_response *res)
+{
+ /* Collect push candidates from the request/response pair.
+ *
+ * One source for pushes are "rel=preload" link headers
+ * in the response.
+ *
+ * TODO: This may be extended in the future by hooks or callbacks
+ * where other modules can provide push information directly.
+ */
+ if (res->header) {
+ link_ctx ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.req = req;
+ ctx.pool = p;
+
+ apr_table_do(head_iter, &ctx, res->header, NULL);
+ return ctx.pushes;
+ }
+ return NULL;
+}
Added: httpd/httpd/branches/2.4.x/modules/http2/h2_push.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_push.h?rev=1715371&view=auto
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_push.h (added)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_push.h Fri Nov 20 15:13:11 2015
@@ -0,0 +1,33 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __mod_h2__h2_push__
+#define __mod_h2__h2_push__
+
+struct h2_request;
+struct h2_response;
+struct h2_ngheader;
+
+typedef struct h2_push {
+ int initiating_id;
+ const struct h2_request *req;
+ const char *as;
+} h2_push;
+
+
+apr_array_header_t *h2_push_collect(apr_pool_t *p,
+ const struct h2_request *req,
+ const struct h2_response *res);
+
+#endif /* defined(__mod_h2__h2_push__) */
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_request.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_request.c?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_request.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_request.c Fri Nov 20 15:13:11 2015
@@ -19,68 +19,146 @@
#include <httpd.h>
#include <http_core.h>
+#include <http_protocol.h>
#include <http_config.h>
#include <http_log.h>
#include "h2_private.h"
#include "h2_mplx.h"
-#include "h2_to_h1.h"
#include "h2_request.h"
#include "h2_task.h"
#include "h2_util.h"
-h2_request *h2_request_create(int id, apr_pool_t *pool,
- apr_bucket_alloc_t *bucket_alloc)
+h2_request *h2_request_create(int id, apr_pool_t *pool)
+{
+ return h2_request_createn(id, pool, NULL, NULL, NULL, NULL, NULL);
+}
+
+h2_request *h2_request_createn(int id, apr_pool_t *pool,
+ const char *method, const char *scheme,
+ const char *authority, const char *path,
+ apr_table_t *header)
{
h2_request *req = apr_pcalloc(pool, sizeof(h2_request));
- if (req) {
- req->id = id;
- req->pool = pool;
- req->bucket_alloc = bucket_alloc;
- }
+
+ req->id = id;
+ req->method = method;
+ req->scheme = scheme;
+ req->authority = authority;
+ req->path = path;
+ req->headers = header? header : apr_table_make(pool, 10);
+
return req;
}
void h2_request_destroy(h2_request *req)
{
- if (req->to_h1) {
- h2_to_h1_destroy(req->to_h1);
- req->to_h1 = NULL;
+}
+
+static apr_status_t inspect_clen(h2_request *req, const char *s)
+{
+ char *end;
+ req->content_length = apr_strtoi64(s, &end, 10);
+ return (s == end)? APR_EINVAL : APR_SUCCESS;
+}
+
+static apr_status_t add_h1_header(h2_request *req, apr_pool_t *pool,
+ const char *name, size_t nlen,
+ const char *value, size_t vlen)
+{
+ char *hname, *hvalue;
+
+ if (h2_req_ignore_header(name, nlen)) {
+ return APR_SUCCESS;
+ }
+ else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
+ const char *existing = apr_table_get(req->headers, "cookie");
+ if (existing) {
+ char *nval;
+
+ /* Cookie header come separately in HTTP/2, but need
+ * to be merged by "; " (instead of default ", ")
+ */
+ hvalue = apr_pstrndup(pool, value, vlen);
+ nval = apr_psprintf(pool, "%s; %s", existing, hvalue);
+ apr_table_setn(req->headers, "Cookie", nval);
+ return APR_SUCCESS;
+ }
+ }
+ else if (H2_HD_MATCH_LIT("host", name, nlen)) {
+ if (apr_table_get(req->headers, "Host")) {
+ return APR_SUCCESS; /* ignore duplicate */
+ }
+ }
+
+ hname = apr_pstrndup(pool, name, nlen);
+ hvalue = apr_pstrndup(pool, value, vlen);
+ h2_util_camel_case_header(hname, nlen);
+ apr_table_mergen(req->headers, hname, hvalue);
+
+ return APR_SUCCESS;
+}
+
+typedef struct {
+ h2_request *req;
+ apr_pool_t *pool;
+} h1_ctx;
+
+static int set_h1_header(void *ctx, const char *key, const char *value)
+{
+ h1_ctx *x = ctx;
+ 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;
+}
+
+static apr_status_t add_all_h1_header(h2_request *req, apr_pool_t *pool,
+ apr_table_t *header)
+{
+ h1_ctx x;
+ x.req = req;
+ x.pool = pool;
+ apr_table_do(set_h1_header, &x, header, NULL);
+ return APR_SUCCESS;
}
-static apr_status_t insert_request_line(h2_request *req, h2_mplx *m);
-apr_status_t h2_request_rwrite(h2_request *req, request_rec *r, h2_mplx *m)
+apr_status_t h2_request_rwrite(h2_request *req, request_rec *r)
{
apr_status_t status;
- req->method = r->method;
+
+ req->method = r->method;
+ req->scheme = (r->parsed_uri.scheme? r->parsed_uri.scheme
+ : ap_http_scheme(r));
req->authority = r->hostname;
- req->path = r->uri;
- if (!ap_strchr_c(req->authority, ':') && r->parsed_uri.port_str) {
- req->authority = apr_psprintf(req->pool, "%s:%s", req->authority,
- r->parsed_uri.port_str);
+ req->path = apr_uri_unparse(r->pool, &r->parsed_uri,
+ APR_URI_UNP_OMITSITEPART);
+
+ if (!ap_strchr_c(req->authority, ':') && r->server) {
+ req->authority = apr_psprintf(r->pool, "%s:%d", req->authority,
+ (int)r->server->port);
}
- req->scheme = NULL;
-
- status = insert_request_line(req, m);
- if (status == APR_SUCCESS) {
- status = h2_to_h1_add_headers(req->to_h1, r->headers_in);
- }
+ AP_DEBUG_ASSERT(req->scheme);
+ AP_DEBUG_ASSERT(req->authority);
+ AP_DEBUG_ASSERT(req->path);
+ AP_DEBUG_ASSERT(req->method);
+
+ status = add_all_h1_header(req, r->pool, r->headers_in);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
- "h2_request(%d): written request %s %s, host=%s",
- req->id, req->method, req->path, req->authority);
-
+ "h2_request(%d): rwrite %s host=%s://%s%s",
+ req->id, req->method, req->scheme, req->authority, req->path);
+
return status;
}
-apr_status_t h2_request_write_header(h2_request *req,
- const char *name, size_t nlen,
- const char *value, size_t vlen,
- h2_mplx *m)
+apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
+ const char *name, size_t nlen,
+ const char *value, size_t vlen)
{
apr_status_t status = APR_SUCCESS;
@@ -90,8 +168,8 @@ apr_status_t h2_request_write_header(h2_
if (name[0] == ':') {
/* pseudo header, see ch. 8.1.2.3, always should come first */
- if (req->to_h1) {
- ap_log_perror(APLOG_MARK, APLOG_ERR, 0, req->pool,
+ if (!apr_is_empty_table(req->headers)) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool,
APLOGNO(02917)
"h2_request(%d): pseudo header after request start",
req->id);
@@ -100,25 +178,25 @@ apr_status_t h2_request_write_header(h2_
if (H2_HEADER_METHOD_LEN == nlen
&& !strncmp(H2_HEADER_METHOD, name, nlen)) {
- req->method = apr_pstrndup(req->pool, value, vlen);
+ req->method = apr_pstrndup(pool, value, vlen);
}
else if (H2_HEADER_SCHEME_LEN == nlen
&& !strncmp(H2_HEADER_SCHEME, name, nlen)) {
- req->scheme = apr_pstrndup(req->pool, value, vlen);
+ req->scheme = apr_pstrndup(pool, value, vlen);
}
else if (H2_HEADER_PATH_LEN == nlen
&& !strncmp(H2_HEADER_PATH, name, nlen)) {
- req->path = apr_pstrndup(req->pool, value, vlen);
+ req->path = apr_pstrndup(pool, value, vlen);
}
else if (H2_HEADER_AUTH_LEN == nlen
&& !strncmp(H2_HEADER_AUTH, name, nlen)) {
- req->authority = apr_pstrndup(req->pool, value, vlen);
+ req->authority = apr_pstrndup(pool, value, vlen);
}
else {
char buffer[32];
memset(buffer, 0, 32);
strncpy(buffer, name, (nlen > 31)? 31 : nlen);
- ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, req->pool,
+ ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, pool,
APLOGNO(02954)
"h2_request(%d): ignoring unknown pseudo header %s",
req->id, buffer);
@@ -126,57 +204,121 @@ apr_status_t h2_request_write_header(h2_
}
else {
/* non-pseudo header, append to work bucket of stream */
- if (!req->to_h1) {
- status = insert_request_line(req, m);
- if (status != APR_SUCCESS) {
- return status;
- }
- }
-
- if (status == APR_SUCCESS) {
- status = h2_to_h1_add_header(req->to_h1,
- name, nlen, value, vlen);
- }
+ status = add_h1_header(req, pool, name, nlen, value, vlen);
}
return status;
}
-apr_status_t h2_request_write_data(h2_request *req,
- const char *data, size_t len)
+apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
{
- return h2_to_h1_add_data(req->to_h1, data, len);
-}
+ const char *s;
+
+ if (req->eoh) {
+ return APR_EINVAL;
+ }
-apr_status_t h2_request_end_headers(h2_request *req, struct h2_mplx *m,
- h2_task *task, int eos)
-{
- if (!req->to_h1) {
- apr_status_t status = insert_request_line(req, m);
- if (status != APR_SUCCESS) {
- return status;
+ /* 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) {
+ if (inspect_clen(req, s) != APR_SUCCESS) {
+ ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
+ APLOGNO(02959)
+ "h2_request(%d): content-length value not parsed: %s",
+ req->id, s);
+ return APR_EINVAL;
+ }
+ }
+ else {
+ /* no content-length given */
+ req->content_length = -1;
+ 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 we have a content-type, but already see eos, no more
+ * data will come. Signal a zero content length explicitly.
+ */
+ apr_table_setn(req->headers, "Content-Length", "0");
+ }
+ }
+
+ req->eoh = 1;
+
+ /* In the presence of trailers, force behaviour of chunked encoding */
+ s = apr_table_get(req->headers, "Trailer");
+ if (s && s[0]) {
+ req->trailers = apr_table_make(pool, 5);
+ if (!req->chunked) {
+ req->chunked = 1;
+ apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
}
}
- return h2_to_h1_end_headers(req->to_h1, task, eos);
+
+ return APR_SUCCESS;
}
-apr_status_t h2_request_close(h2_request *req)
+static apr_status_t add_h1_trailer(h2_request *req, apr_pool_t *pool,
+ const char *name, size_t nlen,
+ const char *value, size_t vlen)
{
- return h2_to_h1_close(req->to_h1);
+ char *hname, *hvalue;
+
+ if (h2_req_ignore_trailer(name, nlen)) {
+ return APR_SUCCESS;
+ }
+
+ hname = apr_pstrndup(pool, name, nlen);
+ hvalue = apr_pstrndup(pool, value, vlen);
+ h2_util_camel_case_header(hname, nlen);
+
+ apr_table_mergen(req->trailers, hname, hvalue);
+
+ return APR_SUCCESS;
}
-static apr_status_t insert_request_line(h2_request *req, h2_mplx *m)
+
+apr_status_t h2_request_add_trailer(h2_request *req, apr_pool_t *pool,
+ const char *name, size_t nlen,
+ const char *value, size_t vlen)
{
- req->to_h1 = h2_to_h1_create(req->id, req->pool, req->bucket_alloc,
- req->method,
- req->scheme,
- req->authority,
- req->path, m);
- return req->to_h1? APR_SUCCESS : APR_ENOMEM;
+ if (!req->trailers) {
+ ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool,
+ "h2_request(%d): unanounced trailers",
+ req->id);
+ return APR_EINVAL;
+ }
+ if (nlen == 0 || name[0] == ':') {
+ ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool,
+ "h2_request(%d): pseudo header in trailer",
+ req->id);
+ return APR_EINVAL;
+ }
+ return add_h1_trailer(req, pool, name, nlen, value, vlen);
}
-apr_status_t h2_request_flush(h2_request *req)
+#define OPT_COPY(p, s) ((s)? apr_pstrdup(p, s) : NULL)
+
+void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src)
{
- return h2_to_h1_flush(req->to_h1);
+ /* keep the dst id */
+ dst->method = OPT_COPY(p, src->method);
+ dst->scheme = OPT_COPY(p, src->scheme);
+ dst->authority = OPT_COPY(p, src->authority);
+ dst->path = OPT_COPY(p, src->path);
+ dst->headers = apr_table_clone(p, src->headers);
+ dst->content_length = src->content_length;
+ dst->chunked = src->chunked;
+ dst->eoh = src->eoh;
}
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_request.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_request.h?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_request.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_request.h Fri Nov 20 15:13:11 2015
@@ -19,9 +19,6 @@
/* h2_request is the transformer of HTTP2 streams into HTTP/1.1 internal
* format that will be fed to various httpd input filters to finally
* become a request_rec to be handled by soemone.
- *
- * Ideally, we would make a request_rec without serializing the headers
- * we have only to make someone else parse them back.
*/
struct h2_to_h1;
struct h2_mplx;
@@ -30,38 +27,45 @@ struct h2_task;
typedef struct h2_request h2_request;
struct h2_request {
- int id; /* http2 stream id */
- apr_pool_t *pool;
- apr_bucket_alloc_t *bucket_alloc;
- struct h2_to_h1 *to_h1; /* Converter to HTTP/1.1 format*/
-
+ int id; /* stream id */
+
/* pseudo header values, see ch. 8.1.2.3 */
const char *method;
const char *scheme;
const char *authority;
const char *path;
+
+ apr_table_t *headers;
+ apr_table_t *trailers;
+
+ apr_off_t content_length;
+ int chunked;
+ int eoh;
};
-h2_request *h2_request_create(int id, apr_pool_t *pool,
- apr_bucket_alloc_t *bucket_alloc);
+h2_request *h2_request_create(int id, apr_pool_t *pool);
+
+h2_request *h2_request_createn(int id, apr_pool_t *pool,
+ const char *method, const char *scheme,
+ const char *authority, const char *path,
+ apr_table_t *headers);
+
void h2_request_destroy(h2_request *req);
-apr_status_t h2_request_flush(h2_request *req);
+apr_status_t h2_request_rwrite(h2_request *req, request_rec *r);
+
+apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
+ const char *name, size_t nlen,
+ const char *value, size_t vlen);
-apr_status_t h2_request_write_header(h2_request *req,
- const char *name, size_t nlen,
- const char *value, size_t vlen,
- struct h2_mplx *m);
+apr_status_t h2_request_add_trailer(h2_request *req, apr_pool_t *pool,
+ const char *name, size_t nlen,
+ const char *value, size_t vlen);
-apr_status_t h2_request_write_data(h2_request *request,
- const char *data, size_t len);
+apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos);
-apr_status_t h2_request_end_headers(h2_request *req, struct h2_mplx *m,
- struct h2_task *task, int eos);
+void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src);
-apr_status_t h2_request_close(h2_request *req);
-apr_status_t h2_request_rwrite(h2_request *req, request_rec *r,
- struct h2_mplx *m);
#endif /* defined(__mod_h2__h2_request__) */
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_response.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_response.c?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_response.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_response.c Fri Nov 20 15:13:11 2015
@@ -25,23 +25,14 @@
#include <nghttp2/nghttp2.h>
#include "h2_private.h"
+#include "h2_h2.h"
#include "h2_util.h"
#include "h2_response.h"
-static h2_ngheader *make_ngheader(apr_pool_t *pool, const char *status,
- apr_table_t *header);
-
-static int ignore_header(const char *name)
-{
- return (H2_HD_MATCH_LIT_CS("connection", name)
- || H2_HD_MATCH_LIT_CS("proxy-connection", name)
- || H2_HD_MATCH_LIT_CS("upgrade", name)
- || H2_HD_MATCH_LIT_CS("keep-alive", name)
- || H2_HD_MATCH_LIT_CS("transfer-encoding", name));
-}
h2_response *h2_response_create(int stream_id,
- const char *http_status,
+ int rst_error,
+ int http_status,
apr_array_header_t *hlines,
apr_pool_t *pool)
{
@@ -53,7 +44,8 @@ h2_response *h2_response_create(int stre
}
response->stream_id = stream_id;
- response->status = http_status;
+ response->rst_error = rst_error;
+ response->http_status = http_status? http_status : 500;
response->content_length = -1;
if (hlines) {
@@ -72,10 +64,8 @@ h2_response *h2_response_create(int stre
while (*sep == ' ' || *sep == '\t') {
++sep;
}
- if (ignore_header(hline)) {
- /* never forward, ch. 8.1.2.2 */
- }
- else {
+
+ if (!h2_util_ignore_header(hline)) {
apr_table_merge(header, hline, sep);
if (*sep && H2_HD_MATCH_LIT_CS("content-length", hline)) {
char *end;
@@ -96,7 +86,7 @@ h2_response *h2_response_create(int stre
header = apr_table_make(pool, 0);
}
- response->rheader = header;
+ response->header = header;
return response;
}
@@ -109,9 +99,22 @@ h2_response *h2_response_rcreate(int str
}
response->stream_id = stream_id;
- response->status = apr_psprintf(pool, "%d", r->status);
+ response->http_status = r->status;
response->content_length = -1;
- response->rheader = header;
+ response->header = header;
+
+ if (response->http_status == HTTP_FORBIDDEN) {
+ const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden");
+ if (cause) {
+ /* This request triggered a TLS renegotiation that is now allowed
+ * in HTTP/2. Tell the client that it should use HTTP/1.1 for this.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, response->http_status, r,
+ "h2_response(%ld-%d): renegotiate forbidden, cause: %s",
+ (long)r->connection->id, stream_id, cause);
+ response->rst_error = H2_ERR_HTTP_1_1_REQUIRED;
+ }
+ }
return response;
}
@@ -125,108 +128,12 @@ h2_response *h2_response_copy(apr_pool_t
{
h2_response *to = apr_pcalloc(pool, sizeof(h2_response));
to->stream_id = from->stream_id;
- to->status = apr_pstrdup(pool, from->status);
+ to->http_status = from->http_status;
to->content_length = from->content_length;
- if (from->rheader) {
- to->ngheader = make_ngheader(pool, to->status, from->rheader);
+ if (from->header) {
+ to->header = apr_table_clone(pool, from->header);
}
return to;
}
-typedef struct {
- nghttp2_nv *nv;
- size_t nvlen;
- size_t nvstrlen;
- size_t offset;
- char *strbuf;
- apr_pool_t *pool;
-} nvctx_t;
-
-static int count_header(void *ctx, const char *key, const char *value)
-{
- if (!ignore_header(key)) {
- nvctx_t *nvctx = (nvctx_t*)ctx;
- nvctx->nvlen++;
- nvctx->nvstrlen += strlen(key) + strlen(value) + 2;
- }
- return 1;
-}
-
-#define NV_ADD_LIT_CS(nv, k, v) addnv_lit_cs(nv, k, sizeof(k) - 1, v, strlen(v))
-#define NV_ADD_CS_CS(nv, k, v) addnv_cs_cs(nv, k, strlen(k), v, strlen(v))
-#define NV_BUF_ADD(nv, s, len) memcpy(nv->strbuf, s, len); \
-s = nv->strbuf; \
-nv->strbuf += len + 1
-
-static void addnv_cs_cs(nvctx_t *ctx, const char *key, size_t key_len,
- const char *value, size_t val_len)
-{
- nghttp2_nv *nv = &ctx->nv[ctx->offset];
-
- NV_BUF_ADD(ctx, key, key_len);
- NV_BUF_ADD(ctx, value, val_len);
-
- nv->name = (uint8_t*)key;
- nv->namelen = key_len;
- nv->value = (uint8_t*)value;
- nv->valuelen = val_len;
-
- ctx->offset++;
-}
-
-static void addnv_lit_cs(nvctx_t *ctx, const char *key, size_t key_len,
- const char *value, size_t val_len)
-{
- nghttp2_nv *nv = &ctx->nv[ctx->offset];
-
- NV_BUF_ADD(ctx, value, val_len);
-
- nv->name = (uint8_t*)key;
- nv->namelen = key_len;
- nv->value = (uint8_t*)value;
- nv->valuelen = val_len;
-
- ctx->offset++;
-}
-
-static int add_header(void *ctx, const char *key, const char *value)
-{
- if (!ignore_header(key)) {
- nvctx_t *nvctx = (nvctx_t*)ctx;
- NV_ADD_CS_CS(nvctx, key, value);
- }
- return 1;
-}
-
-static h2_ngheader *make_ngheader(apr_pool_t *pool, const char *status,
- apr_table_t *header)
-{
- size_t n;
- h2_ngheader *h;
- nvctx_t ctx;
-
- ctx.nv = NULL;
- ctx.nvlen = 1;
- ctx.nvstrlen = strlen(status) + 1;
- ctx.offset = 0;
- ctx.strbuf = NULL;
- ctx.pool = pool;
-
- apr_table_do(count_header, &ctx, header, NULL);
-
- n = (sizeof(h2_ngheader)
- + (ctx.nvlen * sizeof(nghttp2_nv)) + ctx.nvstrlen);
- h = apr_pcalloc(pool, n);
- if (h) {
- ctx.nv = (nghttp2_nv*)(h + 1);
- ctx.strbuf = (char*)&ctx.nv[ctx.nvlen];
-
- NV_ADD_LIT_CS(&ctx, ":status", status);
- apr_table_do(add_header, &ctx, header, NULL);
-
- h->nv = ctx.nv;
- h->nvlen = ctx.nvlen;
- }
- return h;
-}
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_response.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_response.h?rev=1715371&r1=1715370&r2=1715371&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_response.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_response.h Fri Nov 20 15:13:11 2015
@@ -16,26 +16,22 @@
#ifndef __mod_h2__h2_response__
#define __mod_h2__h2_response__
-/* h2_response is just the data belonging the the head of a HTTP response,
- * suitable prepared to be fed to nghttp2 for response submit.
- */
-typedef struct h2_ngheader {
- nghttp2_nv *nv;
- apr_size_t nvlen;
-} h2_ngheader;
+struct h2_push;
typedef struct h2_response {
int stream_id;
- const char *status;
+ int rst_error;
+ int http_status;
apr_off_t content_length;
- apr_table_t *rheader;
- h2_ngheader *ngheader;
+ apr_table_t *header;
+ apr_table_t *trailer;
} h2_response;
h2_response *h2_response_create(int stream_id,
- const char *http_status,
- apr_array_header_t *hlines,
- apr_pool_t *pool);
+ int rst_error,
+ int http_status,
+ apr_array_header_t *hlines,
+ apr_pool_t *pool);
h2_response *h2_response_rcreate(int stream_id, request_rec *r,
apr_table_t *header, apr_pool_t *pool);