Author: rhuijben
Date: Wed Oct 28 12:11:37 2015
New Revision: 1710998
URL: http://svn.apache.org/viewvc?rev=1710998&view=rev
Log:
Improve the http2 protocol handler's event handling by adding a bit
more abstraction on writing frames.
* protocols/http2_protocol.c
(serf_http2_stream_t): Move the typedef to the .h file.
(serf_http2_procotol_state_t): Rename to...
(serf_http2_protocol_t): ... this. Move typedef to .h.
Add connection.
(serf__http2_protocol_init): Update init. Use new frame
write function.
(setup_for_http2): Use new frame write function.
(serf_http2__enqueue_frame): New function.
(http2_read): Move request create code to write callback. Tweak
frame writing and logging.
(http2_protocol_write): Move request create code here.
* protocols/http2_protocol.h
(includes): Declare some dependencies.
(SERF_LOGHTTP2): New define.
(serf_http2_protocol_t,
serf_http2_stream_t): Declare here to allow using in multiple c files.
(serf_http2__enqueue_frame): New function.
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=1710998&r1=1710997&r2=1710998&view=diff
==============================================================================
--- serf/trunk/protocols/http2_protocol.c (original)
+++ serf/trunk/protocols/http2_protocol.c Wed Oct 28 12:11:37 2015
@@ -128,9 +128,9 @@ serf_bucket_create_numberv(serf_bucket_a
}
-typedef struct serf_http2_stream_t
+struct serf_http2_stream_t
{
- struct serf_http2_procotol_state_t *ctx;
+ struct serf_http2_protocol_t *ctx;
/* Linked list of currently existing streams */
struct serf_http2_stream_t *next;
@@ -156,11 +156,12 @@ typedef struct serf_http2_stream_t
} status;
/* TODO: Priority, etc. */
-} serf_http2_stream_t;
+};
-typedef struct serf_http2_procotol_state_t
+struct serf_http2_protocol_t
{
apr_pool_t *pool;
+ serf_connection_t *conn;
serf_bucket_t *ostream;
serf_hpack_table_t *hpack_tbl;
@@ -179,13 +180,13 @@ typedef struct serf_http2_procotol_state
serf_bucket_t *cur_payload;
int in_payload;
-} serf_http2_procotol_state_t;
+};
static apr_status_t
http2_protocol_cleanup(void *state)
{
serf_connection_t *conn = state;
- /* serf_http2_procotol_state_t *ctx = conn->protocol_baton; */
+ /* serf_http2_protocol_t *ctx = conn->protocol_baton; */
conn->protocol_baton = NULL;
return APR_SUCCESS;
@@ -193,7 +194,7 @@ http2_protocol_cleanup(void *state)
void serf__http2_protocol_init(serf_connection_t *conn)
{
- serf_http2_procotol_state_t *ctx;
+ serf_http2_protocol_t *ctx;
apr_pool_t *protocol_pool;
serf_bucket_t *tmp;
@@ -201,6 +202,7 @@ void serf__http2_protocol_init(serf_conn
ctx = apr_pcalloc(protocol_pool, sizeof(*ctx));
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;
@@ -242,7 +244,7 @@ void serf__http2_protocol_init(serf_conn
HTTP2_DEFAULT_MAX_FRAMESIZE,
NULL, NULL, conn->allocator);
- serf_bucket_aggregate_append(ctx->ostream, tmp);
+ serf_http2__enqueue_frame(ctx, tmp, FALSE);
/* Add 2GB - 65535 to the current window.
(Adding 2GB -1 appears to overflow at at least one server) */
@@ -252,13 +254,13 @@ void serf__http2_protocol_init(serf_conn
NULL, NULL, NULL,
HTTP2_DEFAULT_MAX_FRAMESIZE,
NULL, NULL, conn->allocator);
- serf_bucket_aggregate_append(ctx->ostream, tmp);
+ serf_http2__enqueue_frame(ctx, tmp, TRUE);
}
}
/* Creates a HTTP/2 request from a serf request */
static apr_status_t
-setup_for_http2(serf_http2_procotol_state_t *ctx,
+setup_for_http2(serf_http2_protocol_t *ctx,
serf_request_t *request)
{
apr_status_t status;
@@ -298,41 +300,73 @@ setup_for_http2(serf_http2_procotol_stat
HTTP2_DEFAULT_MAX_FRAMESIZE,
NULL, NULL, request->allocator);
- serf_bucket_aggregate_append(ctx->ostream, hpack);
+ serf_http2__enqueue_frame(ctx, hpack, TRUE);
return APR_SUCCESS;
}
-static apr_status_t
-http2_read(serf_connection_t *conn)
+apr_status_t
+serf_http2__enqueue_frame(serf_http2_protocol_t *h2,
+ serf_bucket_t *frame,
+ int pump)
{
- serf_http2_procotol_state_t *ctx = conn->protocol_baton;
- apr_status_t status = APR_SUCCESS;
+ apr_status_t status;
- while (TRUE)
+ if (!pump && !h2->conn->dirty_conn)
{
- serf_request_t *request = conn->unwritten_reqs;
- status = APR_SUCCESS;
+ const char *data;
+ apr_size_t len;
- if (request)
+ /* Cheap check to see if we should request a write
+ event next time around */
+ status = serf_bucket_peek(h2->ostream, &data, &len);
+
+ if (SERF_BUCKET_READ_ERROR(status))
+ return status;
+
+ if (len == 0)
{
- /* Yuck.. there must be easier ways to do this, but I don't
- want to change outgoing.c all the time just yet. */
- conn->unwritten_reqs = request->next;
- if (conn->unwritten_reqs_tail == request)
- conn->unwritten_reqs = conn->unwritten_reqs_tail = NULL;
+ h2->conn->dirty_conn = TRUE;
+ h2->conn->ctx->dirty_pollset = TRUE;
+ }
+ }
- request->next = NULL;
+ serf_bucket_aggregate_append(h2->ostream, frame);
- if (conn->written_reqs_tail)
- conn->written_reqs_tail->next = request;
- else
- conn->written_reqs = conn->written_reqs_tail = request;
+ if (!pump)
+ return APR_SUCCESS;
- status = setup_for_http2(ctx, request);
- if (status)
- return status;
- }
+ /* Flush final output buffer (after ssl, etc.) */
+ status = serf__connection_flush(h2->conn, FALSE);
+ if (SERF_BUCKET_READ_ERROR(status))
+ return status;
+
+ /* Write new data to output buffer if necessary and
+ flush again */
+ if (!status)
+ status = serf__connection_flush(h2->conn, TRUE);
+
+ if (APR_STATUS_IS_EAGAIN(status))
+ {
+ h2->conn->dirty_conn = TRUE;
+ h2->conn->ctx->dirty_pollset = TRUE;
+ }
+ else if (SERF_BUCKET_READ_ERROR(status))
+ return status;
+
+ return APR_SUCCESS;
+}
+
+
+static apr_status_t
+http2_read(serf_connection_t *conn)
+{
+ serf_http2_protocol_t *ctx = conn->protocol_baton;
+ apr_status_t status = APR_SUCCESS;
+
+ while (TRUE)
+ {
+ status = APR_SUCCESS;
if (ctx->cur_frame)
{
@@ -343,14 +377,19 @@ http2_read(serf_connection_t *conn)
{
unsigned char flags;
unsigned char frametype;
+ apr_int32_t streamid;
status = serf__bucket_http2_unframe_read_info(ctx->cur_frame,
- NULL, &frametype,
+ &streamid,
&frametype,
&flags);
- if (status)
+ if (status && !APR_STATUS_IS_EOF(status))
break;
+ 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);
+
ctx->in_payload = TRUE;
if (flags & HTTP2_FLAG_PADDED)
@@ -395,20 +434,20 @@ http2_read(serf_connection_t *conn)
serf__bucket_http2_unframe_read_info(ctx->cur_frame,
&streamid, &frametype,
&flags);
- serf__log(LOGLVL_INFO, LOGCOMP_CONN, __FILE__, conn->config,
- "Read 0x%02x http2 frame on stream 0x%x, flags=0x%x,
size=0x%x\n",
+ serf__log(LOGLVL_INFO, SERF_LOGHTTP2, conn->config,
+ "Done 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);
if (frametype == HTTP2_FRAME_TYPE_DATA
|| frametype == HTTP2_FRAME_TYPE_HEADERS)
{
/* Ugly hack to dump body. Memory LEAK! */
- serf__log(LOGLVL_INFO, LOGCOMP_CONN, __FILE__, conn->config,
+ serf__log(LOGLVL_INFO, SERF_LOGHTTP2, conn->config,
"%s\n", apr_pstrmemdup(conn->pool, ctx->buffer,
ctx->buffer_used));
}
if (frametype == HTTP2_FRAME_TYPE_GOAWAY && conn)
- serf__log(LOGLVL_WARNING, LOGCOMP_CONN, __FILE__, conn->config,
+ serf__log(LOGLVL_WARNING, SERF_LOGHTTP2, conn->config,
"Go away reason %d: %s\n", ctx->buffer[7],
apr_pstrmemdup(conn->pool,
&ctx->buffer[8],
@@ -418,28 +457,30 @@ http2_read(serf_connection_t *conn)
if (frametype == HTTP2_FRAME_TYPE_SETTINGS)
{
/* Always ack settings */
- serf_bucket_aggregate_append(
- ctx->ostream,
+ serf_http2__enqueue_frame(
+ ctx,
serf__bucket_http2_frame_create(
NULL,
HTTP2_FRAME_TYPE_SETTINGS,
HTTP2_FLAG_ACK,
NULL, NULL, NULL,
HTTP2_DEFAULT_MAX_FRAMESIZE,
- NULL, NULL, conn->allocator));
+ NULL, NULL, conn->allocator),
+ TRUE);
}
else if (frametype == HTTP2_FRAME_TYPE_DATA)
{
/* Provide a bit of window space to the server after
receiving data */
- serf_bucket_aggregate_append(
- ctx->ostream,
+ serf_http2__enqueue_frame(
+ ctx,
serf__bucket_http2_frame_create(
serf_bucket_create_numberv(conn->allocator, "4",
(apr_int32_t)16384),
HTTP2_FRAME_TYPE_WINDOW_UPDATE, 0,
&streamid, NULL, NULL,
HTTP2_DEFAULT_MAX_FRAMESIZE,
- NULL, NULL, conn->allocator));
+ NULL, NULL, conn->allocator),
+ TRUE);
}
else if (frametype == HTTP2_FRAME_TYPE_PING)
{
@@ -514,9 +555,30 @@ http2_protocol_read(serf_connection_t *c
static apr_status_t
http2_protocol_write(serf_connection_t *conn)
{
- serf_http2_procotol_state_t *ctx = conn->protocol_baton;
+ serf_http2_protocol_t *ctx = conn->protocol_baton;
+ serf_request_t *request = conn->unwritten_reqs;
apr_status_t status;
+ if (request)
+ {
+ /* Yuck.. there must be easier ways to do this, but I don't
+ want to change outgoing.c all the time just yet. */
+ conn->unwritten_reqs = request->next;
+ if (conn->unwritten_reqs_tail == request)
+ conn->unwritten_reqs = conn->unwritten_reqs_tail = NULL;
+
+ request->next = NULL;
+
+ if (conn->written_reqs_tail)
+ conn->written_reqs_tail->next = request;
+ else
+ conn->written_reqs = conn->written_reqs_tail = request;
+
+ status = setup_for_http2(ctx, request);
+ if (status)
+ return status;
+ }
+
status = serf__connection_flush(conn, TRUE);
if (APR_STATUS_IS_EAGAIN(status))
@@ -534,7 +596,7 @@ http2_protocol_write(serf_connection_t *
static apr_status_t
http2_protocol_hangup(serf_connection_t *conn)
{
- /* serf_http2_procotol_state_t *ctx = conn->protocol_baton; */
+ /* serf_http2_protocol_t *ctx = conn->protocol_baton; */
return APR_EGENERAL;
}
@@ -542,7 +604,7 @@ http2_protocol_hangup(serf_connection_t
static void
http2_protocol_teardown(serf_connection_t *conn)
{
- serf_http2_procotol_state_t *ctx = conn->protocol_baton;
+ serf_http2_protocol_t *ctx = conn->protocol_baton;
apr_pool_destroy(ctx->pool);
conn->protocol_baton = NULL;
Modified: serf/trunk/protocols/http2_protocol.h
URL:
http://svn.apache.org/viewvc/serf/trunk/protocols/http2_protocol.h?rev=1710998&r1=1710997&r2=1710998&view=diff
==============================================================================
--- serf/trunk/protocols/http2_protocol.h (original)
+++ serf/trunk/protocols/http2_protocol.h Wed Oct 28 12:11:37 2015
@@ -21,6 +21,11 @@
#ifndef SERF_PROTOCOL_HTTP2_PRIVATE_H
#define SERF_PROTOCOL_HTTP2_PRIVATE_H
+#include "serf.h"
+#include "serf_private.h"
+
+#define SERF_LOGHTTP2 \
+ SERF_LOGCOMP_PROTOCOL, (__FILE__ ":" APR_STRINGIFY(__LINE__))
#ifdef __cplusplus
extern "C" {
@@ -79,6 +84,16 @@ extern "C" {
#define HTTP2_CONNECTION_PREFIX "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
+
+/* ------------------------------------- */
+typedef struct serf_http2_protocol_t serf_http2_protocol_t;
+typedef struct serf_http2_stream_t serf_http2_stream_t;
+
+apr_status_t
+serf_http2__enqueue_frame(serf_http2_protocol_t *h2,
+ serf_bucket_t *frame,
+ int pump);
+
#ifdef __cplusplus
}
#endif