Author: rhuijben
Date: Wed Oct 28 17:12:57 2015
New Revision: 1711068
URL: http://svn.apache.org/viewvc?rev=1711068&view=rev
Log:
Start abstracting http2 streams as a small step towards full http2 frame
routing in the http2 protocol engine.
This patch fixes two bugs that broke running requests against nghttpd2
and httpd 2.4.17 with mod_http2.
* protocols/http2_protocol.c
(serf_http2_stream_t): Move to http2_protocol.h
(serf_http2_protocol_t): Store some settings.
(http2_protocol_cleanup): Release streams.
(serf__http2_protocol_init): Handle new settings.
Don't write yet, to allow h2direct handshakes.
(setup_for_http2): Create stream and extract the rest
of the code to serf_http2__stream_setup_request.
(serf_http2__allocate_stream_id,
move_to_head,
serf_http2__stream_get): new function.
* protocols/http2_protocol.h
(http2_read): Fix size logging. Don't ack SETTING acks. Log
reset reasons.
* protocols/http2_stream.c
New file.
(serf_http2__stream_create,
serf_http2__stream_cleanup,
serf_http2__stream_setup_request): New function.
Added:
serf/trunk/protocols/http2_stream.c (with props)
Modified:
serf/trunk/protocols/http2_protocol.c
serf/trunk/protocols/http2_protocol.h
Modified: serf/trunk/protocols/http2_protocol.c
URL:
http://svn.apache.org/viewvc/serf/trunk/protocols/http2_protocol.c?rev=1711068&r1=1711067&r2=1711068&view=diff
==============================================================================
--- serf/trunk/protocols/http2_protocol.c (original)
+++ serf/trunk/protocols/http2_protocol.c Wed Oct 28 17:12:57 2015
@@ -127,37 +127,6 @@ serf_bucket_create_numberv(serf_bucket_a
return serf_bucket_simple_own_create(buffer, sz, allocator);
}
-
-struct serf_http2_stream_t
-{
- struct serf_http2_protocol_t *ctx;
-
- /* Linked list of currently existing streams */
- struct serf_http2_stream_t *next;
- struct serf_http2_stream_t *prev;
-
- serf_request_t *request; /* May be NULL as streams may outlive requests */
-
- apr_int64_t lr_window; /* local->remote */
- apr_int64_t rl_window; /* remote->local */
-
- /* -1 until allocated. Odd is client side initiated, even server side */
- apr_int32_t streamid;
-
- enum
- {
- H2S_IDLE = 0,
- H2S_RESERVED_REMOTE,
- H2S_RESERVED_LOCAL,
- H2S_OPEN,
- H2S_HALFCLOSED_REMOTE,
- H2S_HALFCLOSED_LOCAL,
- H2S_CLOSED
- } status;
-
- /* TODO: Priority, etc. */
-};
-
struct serf_http2_protocol_t
{
apr_pool_t *pool;
@@ -166,6 +135,9 @@ struct serf_http2_protocol_t
serf_hpack_table_t *hpack_tbl;
+ apr_uint32_t default_lr_window;
+ apr_uint32_t default_rl_window;
+
apr_int64_t lr_window; /* local->remote */
apr_int64_t rl_window; /* remote->local */
apr_int32_t next_local_streamid;
@@ -186,7 +158,17 @@ static apr_status_t
http2_protocol_cleanup(void *state)
{
serf_connection_t *conn = state;
- /* serf_http2_protocol_t *ctx = conn->protocol_baton; */
+ serf_http2_protocol_t *h2 = conn->protocol_baton;
+ serf_http2_stream_t *stream, *next;
+
+ /* First clean out all streams */
+ for (stream = h2->first; stream; stream = next)
+ {
+ next = stream->next;
+ serf_http2__stream_cleanup(stream);
+ }
+
+ h2->first = h2->last = NULL;
conn->protocol_baton = NULL;
return APR_SUCCESS;
@@ -204,8 +186,13 @@ void serf__http2_protocol_init(serf_conn
ctx->pool = protocol_pool;
ctx->conn = conn;
ctx->ostream = conn->ostream_tail;
- ctx->lr_window = HTTP2_DEFAULT_WINDOW_SIZE;
- ctx->rl_window = HTTP2_DEFAULT_WINDOW_SIZE;
+
+ /* Defaults until negotiated */
+ ctx->default_lr_window = HTTP2_DEFAULT_WINDOW_SIZE;
+ ctx->default_rl_window = HTTP2_DEFAULT_WINDOW_SIZE;
+
+ ctx->lr_window = ctx->default_lr_window;
+ ctx->rl_window = ctx->default_rl_window;
ctx->next_local_streamid = 1; /* 2 if we would be the server */
ctx->next_remote_streamid = 2; /* 1 if we would be the client */
@@ -254,55 +241,33 @@ void serf__http2_protocol_init(serf_conn
NULL, NULL, NULL,
HTTP2_DEFAULT_MAX_FRAMESIZE,
NULL, NULL, conn->allocator);
- serf_http2__enqueue_frame(ctx, tmp, TRUE);
+ serf_http2__enqueue_frame(ctx, tmp, FALSE);
}
}
/* Creates a HTTP/2 request from a serf request */
static apr_status_t
-setup_for_http2(serf_http2_protocol_t *ctx,
+setup_for_http2(serf_http2_protocol_t *h2,
serf_request_t *request)
{
- apr_status_t status;
- serf_bucket_t *hpack;
- serf_bucket_t *body;
- static apr_int32_t NEXT_frame = 1;
+ serf_http2_stream_t *stream;
- apr_int32_t streamid = NEXT_frame;
- NEXT_frame += 2;
+ stream = serf_http2__stream_create(h2, -1,
+ h2->default_lr_window,
+ h2->default_rl_window,
+ h2->conn->allocator);
- if (!request->req_bkt)
+ if (h2->first)
{
- status = serf__setup_request(request);
- if (status)
- return status;
+ stream->next = h2->first;
+ h2->first->prev = stream;
+ h2->first = stream;
}
+ else
+ h2->last = h2->first = stream;
- serf__bucket_request_read(request->req_bkt, &body, NULL, NULL);
- status = serf__bucket_hpack_create_from_request(&hpack, NULL,
request->req_bkt,
-
request->conn->host_info.scheme,
- request->allocator);
- if (status)
- return status;
-
- if (!body)
- {
- /* This destroys the body... Perhaps we should make an extract
- and clear api */
- serf_bucket_destroy(request->req_bkt);
- request->req_bkt = NULL;
- }
-
- hpack = serf__bucket_http2_frame_create(hpack, HTTP2_FRAME_TYPE_HEADERS,
- HTTP2_FLAG_END_STREAM
- | HTTP2_FLAG_END_HEADERS,
- &streamid, NULL, NULL,
- HTTP2_DEFAULT_MAX_FRAMESIZE,
- NULL, NULL, request->allocator);
-
- serf_http2__enqueue_frame(ctx, hpack, TRUE);
-
- return APR_SUCCESS;
+ return serf_http2__stream_setup_request(stream, h2->hpack_tbl,
+ request);
}
apr_status_t
@@ -378,6 +343,7 @@ http2_read(serf_connection_t *conn)
unsigned char flags;
unsigned char frametype;
apr_int32_t streamid;
+ apr_uint64_t size;
status = serf__bucket_http2_unframe_read_info(ctx->cur_frame,
&streamid,
&frametype,
@@ -386,9 +352,11 @@ http2_read(serf_connection_t *conn)
if (status && !APR_STATUS_IS_EOF(status))
break;
+ size = serf_bucket_get_remaining(ctx->cur_frame);
+
serf__log(LOGLVL_INFO, SERF_LOGHTTP2, conn->config,
"Start 0x%02x http2 frame on stream 0x%x, flags=0x%x,
size=0x%x\n",
- (int)frametype, (int)streamid, (int)flags,
(int)ctx->buffer_used);
+ (int)frametype, (int)streamid, (int)flags, (int)size);
ctx->in_payload = TRUE;
@@ -454,7 +422,16 @@ http2_read(serf_connection_t *conn)
(ctx->buffer_used >= 8)
?
ctx->buffer_used-8 : 0));
- if (frametype == HTTP2_FRAME_TYPE_SETTINGS)
+ if (frametype == HTTP2_FRAME_TYPE_RST_STREAM && conn)
+ serf__log(LOGLVL_WARNING, SERF_LOGHTTP2, conn->config,
+ "Reset reason %d: %s\n", ctx->buffer[7],
+ apr_pstrmemdup(conn->pool,
+ &ctx->buffer[8],
+ (ctx->buffer_used >= 8)
+ ? ctx->buffer_used - 8 : 0));
+
+ if (frametype == HTTP2_FRAME_TYPE_SETTINGS
+ && !(flags & HTTP2_FLAG_ACK))
{
/* Always ack settings */
serf_http2__enqueue_frame(
@@ -609,3 +586,98 @@ http2_protocol_teardown(serf_connection_
apr_pool_destroy(ctx->pool);
conn->protocol_baton = NULL;
}
+
+apr_int32_t
+serf_http2__allocate_stream_id(void *baton,
+ apr_int32_t *streamid)
+{
+ serf_http2_stream_t *stream = baton;
+
+ /* Do we need to assign a new id?
+
+ We do this when converting the frame to on-wire data, to avoid
+ creating frames out of order... which would make the other side
+ deny our frame.
+ */
+ if (stream->streamid < 0)
+ {
+ stream->streamid = stream->h2->next_local_streamid;
+ stream->h2->next_local_streamid += 2;
+
+ if (stream->status == H2S_INIT)
+ stream->status = H2S_IDLE;
+ }
+
+ return stream->streamid;
+}
+
+static void
+move_to_head(serf_http2_stream_t *stream)
+{
+ /* Not implemented yet */
+}
+
+serf_http2_stream_t *
+serf_http2__stream_get(serf_http2_protocol_t *h2,
+ apr_int32_t streamid,
+ int create_for_remote,
+ int move_first)
+{
+ serf_http2_stream_t *stream;
+
+ if (streamid < 0)
+ return NULL;
+
+ for (stream = h2->first; stream; stream->next)
+ {
+ if (stream->streamid == streamid)
+ {
+ if (move_first && stream != h2->first)
+ move_to_head(stream);
+
+ return stream;
+ }
+ }
+
+ if (create_for_remote
+ && (streamid & 0x01) == (h2->next_remote_streamid & 0x01))
+ {
+ serf_http2_stream_t *rs;
+ stream = serf_http2__stream_create(h2, streamid,
+ h2->default_lr_window,
+ h2->default_rl_window,
+ h2->conn->allocator);
+
+ if (h2->first)
+ {
+ stream->next = h2->first;
+ h2->first->prev = stream;
+ h2->first = stream;
+ }
+ else
+ h2->last = h2->first = stream;
+
+ if (streamid < h2->next_remote_streamid)
+ stream->status = H2S_CLOSED;
+ else
+ h2->next_remote_streamid = (streamid + 2);
+
+ for (rs = h2->first; rs; rs = rs->next)
+ {
+ if (rs->status <= H2S_IDLE
+ && rs->streamid < streamid
+ && (streamid & 0x01) == (rs->streamid & 0x01))
+ {
+ /* https://tools.ietf.org/html/rfc7540#section-5.1.1
+ The first use of a new stream identifier implicitly closes
+ all streams in the "idle" state that might have been
+ initiated by that peer with a lower-valued stream identifier.
+ */
+ rs->status = H2S_CLOSED;
+ }
+ }
+
+ return stream;
+ }
+ return NULL;
+}
Modified: serf/trunk/protocols/http2_protocol.h
URL:
http://svn.apache.org/viewvc/serf/trunk/protocols/http2_protocol.h?rev=1711068&r1=1711067&r2=1711068&view=diff
==============================================================================
--- serf/trunk/protocols/http2_protocol.h (original)
+++ serf/trunk/protocols/http2_protocol.h Wed Oct 28 17:12:57 2015
@@ -87,13 +87,79 @@ extern "C" {
/* ------------------------------------- */
typedef struct serf_http2_protocol_t serf_http2_protocol_t;
-typedef struct serf_http2_stream_t serf_http2_stream_t;
+typedef struct serf_http2_stream_t
+{
+ struct serf_http2_protocol_t *h2;
+ serf_bucket_alloc_t *alloc;
+ /* Linked list of currently existing streams */
+ struct serf_http2_stream_t *next;
+ struct serf_http2_stream_t *prev;
+
+ serf_request_t *request; /* May be NULL as streams may outlive requests */
+
+ apr_int64_t lr_window; /* local->remote */
+ apr_int64_t rl_window; /* remote->local */
+
+ /* -1 until allocated. Odd is client side initiated, even server side */
+ apr_int32_t streamid;
+
+ enum
+ {
+ H2S_INIT = 0,
+ H2S_IDLE,
+ H2S_RESERVED_REMOTE,
+ H2S_RESERVED_LOCAL,
+ H2S_OPEN,
+ H2S_HALFCLOSED_REMOTE,
+ H2S_HALFCLOSED_LOCAL,
+ H2S_CLOSED
+ } status;
+
+ /* TODO: Priority, etc. */
+} serf_http2_stream_t;
+
+/* Enques an http2 frame for output */
apr_status_t
serf_http2__enqueue_frame(serf_http2_protocol_t *h2,
serf_bucket_t *frame,
int pump);
+/* Creates a new stream */
+serf_http2_stream_t *
+serf_http2__stream_create(serf_http2_protocol_t *h2,
+ apr_int32_t streamid,
+ apr_uint32_t lr_window,
+ apr_uint32_t rl_window,
+ serf_bucket_alloc_t *alloc);
+
+/* Allocates a new stream id for a stream.
+ BATON is a serf_http2_stream_t * instance.
+
+ Passed to serf__bucket_http2_frame_create when writing for
+ a stream.
+*/
+apr_int32_t
+serf_http2__allocate_stream_id(void *baton,
+ apr_int32_t *streamid);
+
+void
+serf_http2__stream_cleanup(serf_http2_stream_t *stream);
+
+serf_http2_stream_t *
+serf_http2__stream_get_by_id(serf_http2_protocol_t *h2,
+ apr_int32_t streamid,
+ int create_for_remote,
+ int move_first);
+
+/* Sets up STREAM to handle REQUEST */
+apr_status_t
+serf_http2__stream_setup_request(serf_http2_stream_t *stream,
+ serf_hpack_table_t *hpack_tbl,
+ serf_request_t *request);
+
+
+
#ifdef __cplusplus
}
#endif
Added: serf/trunk/protocols/http2_stream.c
URL:
http://svn.apache.org/viewvc/serf/trunk/protocols/http2_stream.c?rev=1711068&view=auto
==============================================================================
--- serf/trunk/protocols/http2_stream.c (added)
+++ serf/trunk/protocols/http2_stream.c Wed Oct 28 17:12:57 2015
@@ -0,0 +1,114 @@
+/* ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include <stdlib.h>
+
+#include <apr_pools.h>
+
+#include "serf.h"
+#include "serf_bucket_util.h"
+#include "serf_private.h"
+
+#include "protocols/http2_buckets.h"
+#include "protocols/http2_protocol.h"
+
+serf_http2_stream_t *
+serf_http2__stream_create(serf_http2_protocol_t *h2,
+ apr_int32_t streamid,
+ apr_uint32_t lr_window,
+ apr_uint32_t rl_window,
+ serf_bucket_alloc_t *alloc)
+{
+ serf_http2_stream_t *stream = serf_bucket_mem_alloc(alloc, sizeof(*stream));
+
+ stream->h2 = h2;
+ stream->alloc = alloc;
+
+ stream->next = stream->prev = NULL;
+ stream->request = NULL;
+
+ stream->lr_window = lr_window;
+ stream->rl_window = rl_window;
+
+ if (streamid >= 0)
+ stream->streamid = streamid;
+ else
+ stream->streamid = -1; /* Undetermined yet */
+
+ stream->status = (streamid >= 0) ? H2S_IDLE : H2S_INIT;
+
+ return stream;
+}
+
+void
+serf_http2__stream_cleanup(serf_http2_stream_t *stream)
+{
+ serf_bucket_mem_free(stream->alloc, stream);
+}
+
+apr_status_t
+serf_http2__stream_setup_request(serf_http2_stream_t *stream,
+ serf_hpack_table_t *hpack_tbl,
+ serf_request_t *request)
+{
+ apr_status_t status;
+ serf_bucket_t *hpack;
+ serf_bucket_t *body;
+
+ stream->request = request;
+
+ if (!request->req_bkt)
+ {
+ status = serf__setup_request(request);
+ if (status)
+ return status;
+ }
+
+ serf__bucket_request_read(request->req_bkt, &body, NULL, NULL);
+ status = serf__bucket_hpack_create_from_request(&hpack, hpack_tbl,
+ request->req_bkt,
+
request->conn->host_info.scheme,
+ request->allocator);
+ if (status)
+ return status;
+
+ if (!body)
+ {
+ /* This destroys the body... Perhaps we should make an extract
+ and clear api */
+ serf_bucket_destroy(request->req_bkt);
+ request->req_bkt = NULL;
+ }
+
+ hpack = serf__bucket_http2_frame_create(hpack, HTTP2_FRAME_TYPE_HEADERS,
+ HTTP2_FLAG_END_STREAM
+ | HTTP2_FLAG_END_HEADERS,
+ &stream->streamid,
+ serf_http2__allocate_stream_id,
+ stream,
+ HTTP2_DEFAULT_MAX_FRAMESIZE,
+ NULL, NULL, request->allocator);
+
+ serf_http2__enqueue_frame(stream->h2, hpack, TRUE);
+
+ stream->status = H2S_OPEN; /* Headers sent */
+
+ return APR_SUCCESS;
+}
Propchange: serf/trunk/protocols/http2_stream.c
------------------------------------------------------------------------------
svn:eol-style = native