1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

new directive H2Push on/off to en-/disable HTTP/2 server pushes. Server pushes are recognized by Link: headers in responses that carry the rel=preload parameter

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1714219 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stefan Eissing
2015-11-13 14:54:15 +00:00
parent 17bb21130c
commit 364b3f79c4
19 changed files with 676 additions and 344 deletions

View File

@@ -89,6 +89,74 @@
</usage>
</directivesynopsis>
<directivesynopsis>
<name>H2Push</name>
<description>H2 Server Push Switch</description>
<syntax>H2Push on|off</syntax>
<default>H2Push on</default>
<contextlist>
<context>server config</context>
<context>virtual host</context>
</contextlist>
<usage>
<p>
This directive toggles the usage of the HTTP/2 server push
protocol feature. This should be used inside a
<directive module="core" type="section">VirtualHost</directive>
section to enable direct HTTP/2 communication for that virtual host.
</p>
<p>
The HTTP/2 protocol allows the server to push other resources to
a client when it asked for a particular one. This is helpful
if those resources are connected in some way and the client can
be expected to ask for it anyway. The pushing then saves the
time it takes the client to ask for the resources itself. On the
other hand, pushing resources the client never needs or already
has is a waste of bandwidth.
</p>
<p>
Server pushes are detected by inspecting the <code>Link</code> headers of
responses (see https://tools.ietf.org/html/rfc5988 for the
specification). When a link thus specified has the <code>rel=preload</code>
attribute, it is treated as a resource to be pushed.
</p>
<p>
Link headers in responses are either set by the application or
can be configured via <module>mod_headers</module> as:
</p>
<example><title>mod_headers example</title>
<highlight language="config">
<Location /index.html>
Header add Link "</css/site.css>;rel=preload"
Header add Link "</images/logo.jpg>;rel=preload"
</Location>
</highlight>
</example>
<p>
As the example shows, there can be several link headers added
to a response, resulting in several pushes being triggered. There
are no checks in the module to avoid pushing the same resource
twice or more to one client. Use with care.
</p>
<p>
HTTP/2 server pushes are enabled by default. This directive
allows it to be switch off on all resources of this server/virtual
host.
</p>
<example><title>Example</title>
<highlight language="config">
H2Push off
</highlight>
</example>
<p>
Last but not least, pushes happen only when the client signals
its willingness to accept those. Most browsers do, some, like Safari 9,
do not.
</p>
</usage>
</directivesynopsis>
<directivesynopsis>
<name>H2Upgrade</name>
<description>H2 Upgrade Protocol Switch</description>
@@ -285,7 +353,7 @@
<p>
This directive sets maximum number of <em>extra</em> file handles
a HTTP/2 session is allowed to use. A file handle is counted as
<em>extra</em> when it is transfered from a h2 worker thread to
<em>extra</em> when it is transferred from a h2 worker thread to
the main HTTP/2 connection handling. This commonly happens when
serving static files.
</p><p>
@@ -362,8 +430,8 @@
<p>
The name stems from the
<a href="https://wiki.mozilla.org/Security/Server_Side_TLS">Security/Server Side TLS</a>
definitions at mozilla where "modern compatiblity" is defined. Mozilla Firefox and
other browsers require modern compatiblity for HTTP/2 connections. As everything
definitions at mozilla where "modern compatibility" is defined. Mozilla Firefox and
other browsers require modern compatibility for HTTP/2 connections. As everything
in OpSec, this is a moving target and can be expected to evolve in the future.
</p>
<p>
@@ -423,7 +491,7 @@
<p>
In deployments where servers are reached locally or over reliable
connections only, the value might be decreased with 0 disabling
any warmup phase alltogether.
any warmup phase altogether.
</p>
<p>
The following example sets the size to zero, effectively disabling
@@ -458,7 +526,7 @@
<p>
See <directive type="section">H2TLSWarmUpSize</directive> for a
description of TLS warmup. H2TLSCoolDownSecs reflects the fact
that connections may detoriate over time (and TCP flow adjusts)
that connections may deteriorate over time (and TCP flow adjusts)
for idle connections as well. It is beneficial to overall performance
to fall back to the pre-warmup phase after a number of seconds that
no data has been sent.
@@ -469,7 +537,7 @@
</p>
<p>
The following example sets the seconds to zero, effectively disabling
any cooldown. Warmed up TLS connections stay on maximum record
any cool down. Warmed up TLS connections stay on maximum record
size.
</p>
<example><title>Example</title>

View File

@@ -53,6 +53,7 @@ static h2_config defconf = {
-1, /* HTTP/1 Upgrade support */
1024*1024, /* TLS warmup size */
1, /* TLS cooldown secs */
1, /* HTTP/2 server push enabled */
};
static int files_per_session = 0;
@@ -108,6 +109,7 @@ static void *h2_config_create(apr_pool_t *pool,
conf->h2_upgrade = DEF_VAL;
conf->tls_warmup_size = DEF_VAL;
conf->tls_cooldown_secs = DEF_VAL;
conf->h2_push = DEF_VAL;
return conf;
}
@@ -151,6 +153,7 @@ void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
n->h2_upgrade = H2_CONFIG_GET(add, base, h2_upgrade);
n->tls_warmup_size = H2_CONFIG_GET(add, base, tls_warmup_size);
n->tls_cooldown_secs = H2_CONFIG_GET(add, base, tls_cooldown_secs);
n->h2_push = H2_CONFIG_GET(add, base, h2_push);
return n;
}
@@ -196,6 +199,8 @@ apr_int64_t h2_config_geti64(h2_config *conf, h2_config_var_t var)
return H2_CONFIG_GET(conf, &defconf, tls_warmup_size);
case H2_CONF_TLS_COOLDOWN_SECS:
return H2_CONFIG_GET(conf, &defconf, tls_cooldown_secs);
case H2_CONF_PUSH:
return H2_CONFIG_GET(conf, &defconf, h2_push);
default:
return DEF_VAL;
}
@@ -358,6 +363,23 @@ static const char *h2_conf_set_direct(cmd_parms *parms,
return "value must be On or Off";
}
static const char *h2_conf_set_push(cmd_parms *parms,
void *arg, const char *value)
{
h2_config *cfg = h2_config_sget(parms->server);
if (!strcasecmp(value, "On")) {
cfg->h2_push = 1;
return NULL;
}
else if (!strcasecmp(value, "Off")) {
cfg->h2_push = 0;
return NULL;
}
(void)arg;
return "value must be On or Off";
}
static const char *h2_conf_set_modern_tls_only(cmd_parms *parms,
void *arg, const char *value)
{
@@ -444,6 +466,8 @@ const command_rec h2_cmds[] = {
RSRC_CONF, "number of bytes on TLS connection before doing max writes"),
AP_INIT_TAKE1("H2TLSCoolDownSecs", h2_conf_set_tls_cooldown_secs, NULL,
RSRC_CONF, "seconds of idle time on TLS before shrinking writes"),
AP_INIT_TAKE1("H2Push", h2_conf_set_push, NULL,
RSRC_CONF, "off to disable HTTP/2 server push"),
AP_END_CMD
};

View File

@@ -38,6 +38,7 @@ typedef enum {
H2_CONF_UPGRADE,
H2_CONF_TLS_WARMUP_SIZE,
H2_CONF_TLS_COOLDOWN_SECS,
H2_CONF_PUSH,
} h2_config_var_t;
/* Apache httpd module configuration for h2. */
@@ -59,6 +60,7 @@ typedef struct h2_config {
int h2_upgrade; /* Allow HTTP/1 upgrade to h2/h2c */
apr_int64_t tls_warmup_size; /* Amount of TLS data to send before going full write size */
int tls_cooldown_secs; /* Seconds of idle time before going back to small TLS records */
int h2_push; /* if HTTP/2 server push is enabled */
} h2_config;

View File

@@ -664,7 +664,7 @@ static int h2_h2_post_read_req(request_rec *r)
struct h2_task *task = h2_ctx_get_task(ctx);
if (task) {
/* h2_task connection for a stream, not for h2c */
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
"adding h1_to_h2_resp output filter");
if (task->serialize_headers) {
ap_remove_output_filter_byhandle(r->output_filters, "H1_TO_H2_RESP");

View File

@@ -583,7 +583,7 @@ static apr_status_t out_open(h2_mplx *m, int stream_id, h2_response *response,
h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
if (io) {
if (f) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
"h2_mplx(%ld-%d): open response: %d, rst=%d",
m->id, stream_id, response->http_status,
response->rst_error);

View File

@@ -17,6 +17,7 @@
#include <stdio.h>
#include <apr_strings.h>
#include <apr_lib.h>
#include <httpd.h>
#include <http_core.h>
@@ -31,18 +32,283 @@
typedef struct {
apr_array_header_t *pushes;
apr_pool_t *pool;
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 size_t skip_ws(const char *s, size_t i, size_t max)
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 (i < max && (((c = s[i]) == ' ') || (c == '\t'))) {
++i;
while (ctx->i < ctx->slen
&& (((c = ctx->s[ctx->i]) == ' ') || (c == '\t'))) {
++ctx->i;
}
return 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)
@@ -77,20 +343,41 @@ static void inspect_link(link_ctx *ctx, const char *s, size_t slen)
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
/ "!" / "#" / "$" / "&" / "+" / "-" / "."
/ "^" / "_" / "`" / "|" / "~"
*/
/* TODO */
(void)skip_ws;
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)
{
link_ctx ctx;
ctx.pushes = NULL;
ctx.pool = p;
ctx.req = req;
/* Collect push candidates from the request/response pair.
*
* One source for pushes are "rel=preload" link headers
@@ -99,16 +386,15 @@ apr_array_header_t *h2_push_collect(apr_pool_t *p, const h2_request *req,
* TODO: This may be extended in the future by hooks or callbacks
* where other modules can provide push information directly.
*/
if (res->ngheader) {
int i;
for (i = 0; i < res->ngheader->nvlen; ++i) {
nghttp2_nv *nv = &res->ngheader->nv[i];
if (nv->namelen == 4
&& apr_strnatcasecmp("link", (const char *)nv->name)) {
inspect_link(&ctx, (const char *)nv->value, nv->valuelen);
}
}
}
if (res->header) {
link_ctx ctx;
memset(&ctx, 0, sizeof(ctx));
ctx.req = req;
ctx.pool = p;
return ctx.pushes;
apr_table_do(head_iter, &ctx, res->header, NULL);
return ctx.pushes;
}
return NULL;
}

View File

@@ -22,7 +22,6 @@ struct h2_ngheader;
typedef struct h2_push {
int initiating_id;
const struct h2_request *req;
const struct h2_ngheader *promise;
const char *as;
} h2_push;

View File

@@ -30,12 +30,23 @@
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));
req->id = id;
req->headers = apr_table_make(pool, 10);
req->content_length = -1;
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;
}
@@ -44,45 +55,26 @@ void h2_request_destroy(h2_request *req)
{
}
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_HD_MATCH_LIT("transfer-encoding", name, nlen)) {
if (!apr_strnatcasecmp("chunked", value)) {
/* This should never arrive here in a HTTP/2 request */
ap_log_perror(APLOG_MARK, APLOG_ERR, APR_BADARG, pool,
APLOGNO(02945)
"h2_request: 'transfer-encoding: chunked' received");
return APR_BADARG;
}
}
else if (H2_HD_MATCH_LIT("content-length", name, nlen)) {
char *end;
req->content_length = apr_strtoi64(value, &end, 10);
if (value == end) {
ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
APLOGNO(02959)
"h2_request(%d): content-length value not parsed: %s",
req->id, value);
return APR_EINVAL;
}
req->chunked = 0;
}
else if (H2_HD_MATCH_LIT("content-type", name, nlen)) {
/* If we see a content-type and have no length (yet),
* we need to chunk. */
req->chunked = (req->content_length == -1);
}
else if ((req->seen_host && H2_HD_MATCH_LIT("host", name, nlen))
|| 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("keep-alive", name, nlen)
|| H2_HD_MATCH_LIT("http2-settings", name, nlen)) {
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. */
return APR_SUCCESS;
}
@@ -91,7 +83,7 @@ static apr_status_t add_h1_header(h2_request *req, apr_pool_t *pool,
if (existing) {
char *nval;
/* Cookie headers come separately in HTTP/2, but need
/* Cookie header come separately in HTTP/2, but need
* to be merged by "; " (instead of default ", ")
*/
hvalue = apr_pstrndup(pool, value, vlen);
@@ -101,7 +93,9 @@ static apr_status_t add_h1_header(h2_request *req, apr_pool_t *pool,
}
}
else if (H2_HD_MATCH_LIT("host", name, nlen)) {
req->seen_host = 1;
if (apr_table_get(req->headers, "Host")) {
return APR_SUCCESS; /* ignore duplicate */
}
}
hname = apr_pstrndup(pool, name, nlen);
@@ -124,13 +118,13 @@ static int set_h1_header(void *ctx, const char *key, const char *value)
return 1;
}
static apr_status_t add_h1_headers(h2_request *req, apr_pool_t *pool,
apr_table_t *headers)
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, headers, NULL);
apr_table_do(set_h1_header, &x, header, NULL);
return APR_SUCCESS;
}
@@ -150,7 +144,7 @@ apr_status_t h2_request_rwrite(h2_request *req, request_rec *r)
r->parsed_uri.port_str);
}
status = add_h1_headers(req, r->pool, r->headers_in);
status = add_all_h1_header(req, r->pool, r->headers_in);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
"h2_request(%d): rwrite %s host=%s://%s%s",
@@ -215,11 +209,22 @@ apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
{
const char *s;
if (req->eoh) {
return APR_EINVAL;
}
if (!req->seen_host) {
/* 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) {
@@ -228,20 +233,34 @@ apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
apr_table_set(req->headers, "Host", req->authority);
}
if (eos && req->chunked) {
/* We had chunking figured out, but the EOS is already there.
* unmark chunking and set a definitive content-length.
*/
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;
}
req->chunked = 0;
apr_table_setn(req->headers, "Content-Length", "0");
}
else if (req->chunked) {
/* We have not seen a content-length. We therefore must
* pass any request content in chunked form.
*/
apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
else {
/* no content-length given */
req->content_length = -1;
s = apr_table_get(req->headers, "Content-Type");
if (eos && s) {
req->chunked = 0;
apr_table_setn(req->headers, "Content-Length", "0");
}
else if (s) {
/* We have not seen a content-length, but a content-type.
* must pass any request content in chunked form.
*/
req->chunked = 1;
apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
}
}
req->eoh = 1;
return APR_SUCCESS;
@@ -253,13 +272,12 @@ void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src)
{
/* keep the dst id */
dst->method = OPT_COPY(p, src->method);
dst->scheme = OPT_COPY(p, src->method);
dst->authority = OPT_COPY(p, src->method);
dst->path = 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;
dst->seen_host = src->seen_host;
}

View File

@@ -40,12 +40,15 @@ struct h2_request {
apr_off_t content_length;
int chunked;
int eoh;
int seen_host;
};
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_rwrite(h2_request *req, request_rec *r);

View File

@@ -27,20 +27,8 @@
#include "h2_private.h"
#include "h2_h2.h"
#include "h2_util.h"
#include "h2_push.h"
#include "h2_response.h"
static void make_ngheader(apr_pool_t *pool, h2_response *to,
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,
int rst_error,
@@ -76,10 +64,8 @@ h2_response *h2_response_create(int stream_id,
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;
@@ -100,7 +86,7 @@ h2_response *h2_response_create(int stream_id,
header = apr_table_make(pool, 0);
}
response->rheader = header;
response->header = header;
return response;
}
@@ -115,7 +101,7 @@ h2_response *h2_response_rcreate(int stream_id, request_rec *r,
response->stream_id = stream_id;
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");
@@ -144,149 +130,10 @@ h2_response *h2_response_copy(apr_pool_t *pool, h2_response *from)
to->stream_id = from->stream_id;
to->http_status = from->http_status;
to->content_length = from->content_length;
if (from->rheader) {
make_ngheader(pool, to, 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;
apr_array_header_t *pushes;
} 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 h2_push *h2_parse_preload(apr_pool_t *pool, const char *str)
{
/* TODO: implement this */
return NULL;
}
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);
if (apr_strnatcasecmp("link", key)
&& ap_strstr_c("preload", value)) {
/* Detect link headers with rel=preload to use as possible
* server push candidates. */
h2_push *push;
if ((push = h2_parse_preload(nvctx->pool, value))) {
if (!nvctx->pushes) {
nvctx->pushes = apr_array_make(nvctx->pool, 5,
sizeof(h2_push*));
}
APR_ARRAY_PUSH(nvctx->pushes, h2_push*) = push;
}
}
}
return 1;
}
static void make_ngheader(apr_pool_t *pool, h2_response *to,
apr_table_t *header)
{
size_t n;
h2_ngheader *h;
nvctx_t ctx;
char *status;
to->ngheader = NULL;
to->pushes = NULL;
status = apr_psprintf(pool, "%d", to->http_status);
ctx.nv = NULL;
ctx.nvlen = 1;
ctx.nvstrlen = strlen(status) + 1;
ctx.offset = 0;
ctx.strbuf = NULL;
ctx.pool = pool;
ctx.pushes = NULL;
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;
to->ngheader = h;
to->pushes = ctx.pushes;
}
}
int h2_response_push_count(h2_response *response)
{
return response->pushes? response->pushes->nelts : 0;
}
h2_push *h2_response_get_push(h2_response *response, size_t i)
{
if (response->pushes && i < response->pushes->nelts) {
return APR_ARRAY_IDX(response->pushes, i, h2_push*);
}
return NULL;
}

View File

@@ -18,24 +18,12 @@
struct h2_push;
/* 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;
int rst_error;
int http_status;
apr_off_t content_length;
apr_table_t *rheader;
h2_ngheader *ngheader;
apr_array_header_t *pushes;
apr_table_t *header;
} h2_response;
h2_response *h2_response_create(int stream_id,
@@ -51,20 +39,4 @@ void h2_response_destroy(h2_response *response);
h2_response *h2_response_copy(apr_pool_t *pool, h2_response *from);
/**
* Get the number of push proposals included with the response.
* @return number of push proposals in this response
*/
int h2_response_push_count(h2_response *response);
/**
* Get the ith h2_push contained in this response.
*
* @param response the response
* @param i the index of the push to get
* @return the ith h2_push or NULL if out of bounds
*/
struct h2_push *h2_response_get_push(h2_response *response, size_t i);
#endif /* defined(__mod_h2__h2_response__) */

View File

@@ -30,6 +30,7 @@
#include "h2_h2.h"
#include "h2_mplx.h"
#include "h2_push.h"
#include "h2_request.h"
#include "h2_response.h"
#include "h2_stream.h"
#include "h2_stream_set.h"
@@ -80,10 +81,6 @@ h2_stream *h2_session_open_stream(h2_session *session, int stream_id)
session->max_stream_received = stream->id;
}
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
"h2_session: stream(%ld-%d): opened",
session->id, stream_id);
return stream;
}
@@ -305,7 +302,7 @@ static int on_frame_not_send_cb(nghttp2_session *ngh2,
return 0;
}
static apr_status_t stream_destroy(h2_session *session,
static apr_status_t stream_release(h2_session *session,
h2_stream *stream,
uint32_t error_code)
{
@@ -342,12 +339,12 @@ static int on_stream_close_cb(nghttp2_session *ngh2, int32_t stream_id,
}
stream = h2_session_get_stream(session, stream_id);
if (stream) {
stream_destroy(session, stream, error_code);
stream_release(session, stream, error_code);
}
if (error_code) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
"h2_stream(%ld-%d): close error %d",
"h2_stream(%ld-%d): closed, error=%d",
session->id, (int)stream_id, error_code);
}
@@ -1121,24 +1118,40 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream)
if (stream->submitted) {
rv = NGHTTP2_PROTOCOL_ERROR;
}
else if (stream->response && stream->response->ngheader) {
else if (stream->response && stream->response->header) {
nghttp2_data_provider provider;
h2_response *response = stream->response;
h2_ngheader *ngh;
memset(&provider, 0, sizeof(provider));
provider.source.fd = stream->id;
provider.read_callback = stream_data_cb;
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
"h2_stream(%ld-%d): submitting response %d",
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
"h2_stream(%ld-%d): submit response %d",
session->id, stream->id, response->http_status);
ngh = h2_util_ngheader_make_res(stream->pool, response->http_status,
response->header);
rv = nghttp2_submit_response(session->ngh2, response->stream_id,
response->ngheader->nv,
response->ngheader->nvlen, &provider);
ngh->nv, ngh->nvlen, &provider);
/* If the submit worked,
* and this stream is not a pushed one itself,
* and HTTP/2 server push is enabled here,
* and the response is in the range 200-299 *),
* and the remote side has pushing enabled,
* -> find and perform any pushes on this stream
*
* *) the response code is relevant, as we do not want to
* make pushes on 401 or 403 codes, neiterh on 301/302
* and friends. And if we see a 304, we do not push either
* as the client, having this resource in its cache, might
* also have the pushed ones as well.
*/
if (!rv
&& !stream->promised_on
&& !stream->initiated_on
&& h2_config_geti(h2_config_get(session->c), H2_CONF_PUSH)
&& H2_HTTP_2XX(response->http_status)
&& h2_session_push_enabled(session)) {
@@ -1148,7 +1161,7 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream)
else {
int err = H2_STREAM_RST(stream, H2_ERR_PROTOCOL_ERROR);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
"h2_stream(%ld-%d): RST_STREAM, err=%d",
session->id, stream->id, err);
@@ -1169,15 +1182,18 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream)
return status;
}
struct h2_stream *h2_session_push(h2_session *session, h2_push *push)
struct h2_stream *h2_session_push(h2_session *session, h2_stream *is,
h2_push *push)
{
apr_status_t status;
h2_stream *stream;
h2_ngheader *ngh;
int nid;
ngh = h2_util_ngheader_make_req(is->pool, push->req);
nid = nghttp2_submit_push_promise(session->ngh2, 0, push->initiating_id,
push->promise->nv, push->promise->nvlen,
NULL);
ngh->nv, ngh->nvlen, NULL);
if (nid <= 0) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
"h2_stream(%ld-%d): submitting push promise fail: %s",
@@ -1186,16 +1202,17 @@ struct h2_stream *h2_session_push(h2_session *session, h2_push *push)
return NULL;
}
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, session->c,
"h2_stream(%ld-%d): promised new stream %d",
session->id, push->initiating_id, nid);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
"h2_stream(%ld-%d): promised new stream %d for %s %s",
session->id, push->initiating_id, nid,
push->req->method, push->req->path);
stream = h2_session_open_stream(session, nid);
if (stream) {
h2_stream_set_h2_request(stream, push->req);
h2_stream_set_h2_request(stream, is->id, push->req);
status = stream_end_headers(session, stream, 1);
if (status != APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_WARNING, status, session->c,
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
"h2_stream(%ld-%d): scheduling push stream",
session->id, stream->id);
h2_stream_cleanup(stream);
@@ -1203,7 +1220,7 @@ struct h2_stream *h2_session_push(h2_session *session, h2_push *push)
}
}
else {
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, session->c,
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
"h2_stream(%ld-%d): failed to create stream obj %d",
session->id, push->initiating_id, nid);
}

View File

@@ -189,10 +189,11 @@ apr_status_t h2_session_stream_destroy(h2_session *session,
* processing..
*
* @param session the session to work in
* @param stream the stream on which the push depends
* @param is the stream initiating the push
* @param push the push to promise
* @return the new promised stream or NULL
*/
struct h2_stream *h2_session_push(h2_session *session, struct h2_push *push);
struct h2_stream *h2_session_push(h2_session *session,
struct h2_stream *is, struct h2_push *push);
#endif /* defined(__mod_h2__h2_session__) */

View File

@@ -159,9 +159,6 @@ h2_stream *h2_stream_open(int id, apr_pool_t *pool, h2_session *session)
apr_status_t h2_stream_destroy(h2_stream *stream)
{
AP_DEBUG_ASSERT(stream);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, stream->session->c,
"h2_stream(%ld-%d): destroy", stream->session->id, stream->id);
if (stream->request) {
h2_request_destroy(stream->request);
stream->request = NULL;
@@ -237,9 +234,12 @@ apr_status_t h2_stream_set_request(h2_stream *stream, request_rec *r)
return status;
}
void h2_stream_set_h2_request(h2_stream *stream, const h2_request *req)
void h2_stream_set_h2_request(h2_stream *stream, int initiated_on,
const h2_request *req)
{
h2_request_copy(stream->pool, stream->request, req);
stream->initiated_on = initiated_on;
stream->request->eoh = 0;
}
apr_status_t h2_stream_add_header(h2_stream *stream,
@@ -286,10 +286,10 @@ apr_status_t h2_stream_schedule(h2_stream *stream, int eos,
}
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, stream->session->c,
"h2_mplx(%ld-%d): start stream, task %s %s (%s)",
"h2_stream(%ld-%d): scheduled %s %s://%s%s",
stream->session->id, stream->id,
stream->request->method, stream->request->path,
stream->request->authority);
stream->request->method, stream->request->scheme,
stream->request->authority, stream->request->path);
return status;
}
@@ -545,9 +545,12 @@ apr_status_t h2_stream_submit_pushes(h2_stream *stream)
pushes = h2_push_collect(stream->pool, stream->request, stream->response);
if (pushes && !apr_is_empty_array(pushes)) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
"h2_stream(%ld-%d): found %d push candidates",
stream->session->id, stream->id, pushes->nelts);
for (i = 0; i < pushes->nelts; ++i) {
h2_push *push = APR_ARRAY_IDX(pushes, i, h2_push*);
h2_stream *s = h2_session_push(stream->session, push);
h2_stream *s = h2_session_push(stream->session, stream, push);
if (!s) {
status = APR_ECONNRESET;
break;

View File

@@ -50,7 +50,7 @@ typedef struct h2_stream h2_stream;
struct h2_stream {
int id; /* http2 stream id */
int promised_on; /* http2 stream this was a promise on, or 0 */
int initiated_on; /* http2 stream id this was initiated on or 0 */
h2_stream_state_t state; /* http/2 state of this stream */
struct h2_session *session; /* the session this stream belongs to */
@@ -131,7 +131,8 @@ apr_status_t h2_stream_set_request(h2_stream *stream, request_rec *r);
* @param stream the stream to init the request for
* @param req the request for initializing, will be copied
*/
void h2_stream_set_h2_request(h2_stream *stream, const struct h2_request *req);
void h2_stream_set_h2_request(h2_stream *stream, int initiated_on,
const struct h2_request *req);
/*
* Add a HTTP/2 header (including pseudo headers) to the given stream.

View File

@@ -71,21 +71,6 @@ h2_task_input *h2_task_input_create(h2_task *task, apr_pool_t *pool,
/* We do not serialize and have eos already, no need to
* create a bucket brigade. */
}
if (APLOGcdebug(task->c)) {
char buffer[1024];
apr_size_t len = sizeof(buffer)-1;
if (input->bb) {
apr_brigade_flatten(input->bb, buffer, &len);
}
else {
len = 0;
}
buffer[len] = 0;
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c,
"h2_task_input(%s): request is: %s",
task->id, buffer);
}
}
return input;
}
@@ -105,21 +90,20 @@ apr_status_t h2_task_input_read(h2_task_input *input,
apr_status_t status = APR_SUCCESS;
apr_off_t bblen = 0;
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
"h2_task_input(%s): read, block=%d, mode=%d, readbytes=%ld",
input->task->id, block, mode, (long)readbytes);
if (is_aborted(f)) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
"h2_task_input(%s): is aborted",
input->task->id);
return APR_ECONNABORTED;
}
if (mode == AP_MODE_INIT) {
return ap_get_brigade(f->c->input_filters, bb, mode, block, readbytes);
}
if (is_aborted(f)) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
"h2_task_input(%s): is aborted", input->task->id);
return APR_ECONNABORTED;
}
if (input->bb) {
status = apr_brigade_length(input->bb, 1, &bblen);
if (status != APR_SUCCESS) {

View File

@@ -25,6 +25,7 @@
#include <nghttp2/nghttp2.h>
#include "h2_private.h"
#include "h2_request.h"
#include "h2_util.h"
size_t h2_util_hex_dump(char *buffer, size_t maxlen,
@@ -205,6 +206,10 @@ const char *h2_util_first_token_match(apr_pool_t *pool, const char *s,
return NULL;
}
/*******************************************************************************
* h2_util for bucket brigades
******************************************************************************/
/* DEEP_COPY==0 crashes under load. I think the setaside is fine,
* however buckets moved to another thread will still be
* free'd against the old bucket_alloc. *And* if the old
@@ -789,3 +794,91 @@ apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
return APR_SUCCESS;
}
/*******************************************************************************
* h2_ngheader
******************************************************************************/
int h2_util_ignore_header(const char *name)
{
/* never forward, ch. 8.1.2.2 */
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));
}
static int count_header(void *ctx, const char *key, const char *value)
{
if (!h2_util_ignore_header(key)) {
(*((size_t*)ctx))++;
}
return 1;
}
#define NV_ADD_LIT_CS(nv, k, v) add_header(nv, k, sizeof(k) - 1, v, strlen(v))
#define NV_ADD_CS_CS(nv, k, v) add_header(nv, k, strlen(k), v, strlen(v))
static int add_header(h2_ngheader *ngh,
const char *key, size_t key_len,
const char *value, size_t val_len)
{
nghttp2_nv *nv = &ngh->nv[ngh->nvlen++];
nv->name = (uint8_t*)key;
nv->namelen = key_len;
nv->value = (uint8_t*)value;
nv->valuelen = val_len;
return 1;
}
static int add_table_header(void *ctx, const char *key, const char *value)
{
if (!h2_util_ignore_header(key)) {
add_header(ctx, key, strlen(key), value, strlen(value));
}
return 1;
}
h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p,
int http_status,
apr_table_t *header)
{
h2_ngheader *ngh;
size_t n;
n = 1;
apr_table_do(count_header, &n, header, NULL);
ngh = apr_pcalloc(p, sizeof(h2_ngheader));
ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv));
NV_ADD_LIT_CS(ngh, ":status", apr_psprintf(p, "%d", http_status));
apr_table_do(add_table_header, ngh, header, NULL);
return ngh;
}
h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p,
const struct h2_request *req)
{
h2_ngheader *ngh;
size_t n;
AP_DEBUG_ASSERT(req);
n = 4;
apr_table_do(count_header, &n, req->headers, NULL);
ngh = apr_pcalloc(p, sizeof(h2_ngheader));
ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv));
NV_ADD_LIT_CS(ngh, ":scheme", req->scheme);
NV_ADD_LIT_CS(ngh, ":authority", req->authority);
NV_ADD_LIT_CS(ngh, ":path", req->path);
NV_ADD_LIT_CS(ngh, ":method", req->method);
apr_table_do(add_table_header, ngh, req->headers, NULL);
return ngh;
}

View File

@@ -16,6 +16,7 @@
#ifndef __mod_h2__h2_util__
#define __mod_h2__h2_util__
struct h2_request;
struct nghttp2_frame;
size_t h2_util_hex_dump(char *buffer, size_t maxlen,
@@ -67,6 +68,19 @@ apr_size_t h2_util_base64url_decode(const char **decoded,
nv->value = (uint8_t *)VALUE; \
nv->valuelen = strlen(VALUE)
int h2_util_ignore_header(const char *name);
typedef struct h2_ngheader {
nghttp2_nv *nv;
apr_size_t nvlen;
} h2_ngheader;
h2_ngheader *h2_util_ngheader_make_res(apr_pool_t *p,
int http_status,
apr_table_t *header);
h2_ngheader *h2_util_ngheader_make_req(apr_pool_t *p,
const struct h2_request *req);
/**
* Moves data from one brigade into another. If maxlen > 0, it only
* moves up to maxlen bytes into the target brigade, making bucket splits

View File

@@ -174,7 +174,7 @@ static apr_status_t get_mplx_next(h2_worker *worker, h2_mplx **pm,
* needed to give up with more than enough workers.
*/
if (task) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, workers->s,
"h2_worker(%d): start task(%s)",
h2_worker_get_id(worker), task->id);
/* Since we hand out a reference to the worker, we increase
@@ -326,7 +326,7 @@ apr_status_t h2_workers_register(h2_workers *workers, struct h2_mplx *m)
{
apr_status_t status = apr_thread_mutex_lock(workers->lock);
if (status == APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, status, workers->s,
ap_log_error(APLOG_MARK, APLOG_TRACE2, status, workers->s,
"h2_workers: register mplx(%ld)", m->id);
if (in_list(workers, m)) {
status = APR_EAGAIN;