Author: rhuijben Date: Mon Oct 26 20:19:07 2015 New Revision: 1710687 URL: http://svn.apache.org/viewvc?rev=1710687&view=rev Log: Checkpoint the current http2 protocol implementation. This now dumps the completely decoded hpack headers when logging is set.
This adds some helper functions to quickly create frames with a few integer values. * protocols/http2_protocol.c (includes): Add http2_buckets.h. (serf_bucket_create_numberv): New static function. (serf__http2_protocol_init): Construct hpack table. (http2_read): Read just the inner payload. Decode headers. Modified: serf/trunk/protocols/http2_protocol.c Modified: serf/trunk/protocols/http2_protocol.c URL: http://svn.apache.org/viewvc/serf/trunk/protocols/http2_protocol.c?rev=1710687&r1=1710686&r2=1710687&view=diff ============================================================================== --- serf/trunk/protocols/http2_protocol.c (original) +++ serf/trunk/protocols/http2_protocol.c Mon Oct 26 20:19:07 2015 @@ -28,6 +28,7 @@ #include "serf_bucket_util.h" #include "serf_private.h" +#include "protocols/http2_buckets.h" #include "protocols/http2_protocol.h" static apr_status_t @@ -42,6 +43,88 @@ http2_protocol_hangup(serf_connection_t static void http2_protocol_teardown(serf_connection_t *conn); +static serf_bucket_t * +serf_bucket_create_numberv(serf_bucket_alloc_t *allocator, const char *format, ...) +{ + va_list va; + const char *c; + char *buffer; + apr_size_t sz = 0; + unsigned char *r; + + va_start(va, format); + + for (c = format; *c; c++) + { + switch (*c) + { + case '1': /* char */ + sz += 1; + break; + case '2': /* apr_int16_t / apr_uint16_t */ + sz += 2; + break; + case '3': /* apr_int32_t / apr_uint32_t */ + sz += 3; + break; + case '4': /* apr_int32_t / apr_uint32_t */ + sz += 4; + break; + case '8': /* apr_int64_t / apr_uint64_t */ + sz += 8; + break; + default: + abort(); /* Invalid format */ + } + } + + buffer = serf_bucket_mem_alloc(allocator, sz); + r = (void*)buffer; + for (c = format; *c; c++) + { + apr_uint32_t tmp; + apr_uint64_t tmp_64; + + switch (*c) + { + case '1': + *r++ = va_arg(va, char); + break; + case '2': + tmp = va_arg(va, apr_uint16_t); + *r++ = (tmp >> 8) & 0xFF; + *r++ = tmp & 0xFF; + break; + case '3': + tmp = va_arg(va, apr_uint32_t); + *r++ = (tmp >> 16) & 0xFF; + *r++ = (tmp >> 8) & 0xFF; + *r++ = tmp & 0xFF; + break; + case '4': + tmp = va_arg(va, apr_uint32_t); + *r++ = (tmp >> 24) & 0xFF; + *r++ = (tmp >> 16) & 0xFF; + *r++ = (tmp >> 8) & 0xFF; + *r++ = tmp & 0xFF; + break; + case '8': + tmp_64 = va_arg(va, apr_uint64_t); + *r++ = (tmp_64 >> 56) & 0xFF; + *r++ = (tmp_64 >> 48) & 0xFF; + *r++ = (tmp_64 >> 40) & 0xFF; + *r++ = (tmp_64 >> 32) & 0xFF; + *r++ = (tmp_64 >> 24) & 0xFF; + *r++ = (tmp_64 >> 16) & 0xFF; + *r++ = (tmp_64 >> 8) & 0xFF; + *r++ = tmp_64 & 0xFF; + break; + } + } + return serf_bucket_simple_own_create(buffer, sz, allocator); +} + + typedef struct serf_http2_stream_t { struct serf_http2_procotol_state_t *ctx; @@ -77,6 +160,8 @@ typedef struct serf_http2_procotol_state apr_pool_t *pool; serf_bucket_t *ostream; + serf_hpack_table_t *hpack_tbl; + apr_int64_t lr_window; /* local->remote */ apr_int64_t rl_window; /* remote->local */ apr_int32_t next_local_streamid; @@ -121,6 +206,8 @@ void serf__http2_protocol_init(serf_conn ctx->first = ctx->last = NULL; + ctx->hpack_tbl = serf__hpack_table_create(TRUE, 16384, protocol_pool); + apr_pool_cleanup_register(protocol_pool, conn, http2_protocol_cleanup, apr_pool_cleanup_null); @@ -141,27 +228,27 @@ void serf__http2_protocol_init(serf_conn /* And now a settings frame and a huge window */ { - serf_bucket_t *no_settings; + serf_bucket_t *settings; serf_bucket_t *window_size; - apr_int32_t frame_id = 0; - no_settings = serf_bucket_simple_create("", 0, NULL, NULL, conn->allocator); - tmp = serf_bucket_http2_frame_create(no_settings, HTTP2_FRAME_TYPE_SETTINGS, 0, - &frame_id, NULL, NULL, /* Static id: 0*/ - HTTP2_DEFAULT_MAX_FRAMESIZE, - NULL, NULL, conn->allocator); + settings = serf_bucket_create_numberv(conn->allocator, "24", + (apr_int16_t)HTTP2_SETTING_HEADER_TABLE_SIZE, + (apr_int32_t)0); + tmp = serf__bucket_http2_frame_create(settings, HTTP2_FRAME_TYPE_SETTINGS, 0, + NULL, NULL, NULL, /* Static id: 0*/ + HTTP2_DEFAULT_MAX_FRAMESIZE, + NULL, NULL, conn->allocator); serf_bucket_aggregate_append(ctx->ostream, tmp); /* Add 2GB - 65535 to the current window. (Adding 2GB -1 appears to overflow at at least one server) */ - window_size = serf_bucket_simple_create("\x7F\xFF\x00\x00", 4, NULL, NULL, - conn->allocator); - tmp = serf_bucket_http2_frame_create(window_size, - HTTP2_FRAME_TYPE_WINDOW_UPDATE, 0, - &frame_id, NULL, NULL, - HTTP2_DEFAULT_MAX_FRAMESIZE, - NULL, NULL, conn->allocator); + window_size = serf_bucket_create_numberv(conn->allocator, "4", 0x7FFF0000); + tmp = serf__bucket_http2_frame_create(window_size, + HTTP2_FRAME_TYPE_WINDOW_UPDATE, 0, + NULL, NULL, NULL, + HTTP2_DEFAULT_MAX_FRAMESIZE, + NULL, NULL, conn->allocator); serf_bucket_aggregate_append(ctx->ostream, tmp); } } @@ -201,12 +288,12 @@ setup_for_http2(serf_http2_procotol_stat 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); + 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_bucket_aggregate_append(ctx->ostream, hpack); @@ -252,40 +339,49 @@ http2_read(serf_connection_t *conn) if (! ctx->in_payload) { unsigned char flags; + unsigned char frametype; - status = serf_bucket_http2_unframe_read_info(ctx->cur_frame, - NULL, NULL, &flags); + status = serf__bucket_http2_unframe_read_info(ctx->cur_frame, + NULL, &frametype, + &flags); - if (!SERF_BUCKET_READ_ERROR(status)) - { - ctx->in_payload = TRUE; + if (status) + break; + + ctx->in_payload = TRUE; - if (flags & HTTP2_FLAG_PADDED) - { - ctx->cur_payload = - serf_bucket_http2_unpad_create( + if (flags & HTTP2_FLAG_PADDED) + { + ctx->cur_payload = + serf__bucket_http2_unpad_create( ctx->cur_frame, TRUE, ctx->cur_frame->allocator); - } - else - ctx->cur_payload = ctx->cur_frame; } + else + ctx->cur_payload = ctx->cur_frame; - if (status) - break; + if (frametype == HTTP2_FRAME_TYPE_HEADERS) + { + ctx->cur_payload = serf__bucket_hpack_decode_create( + ctx->cur_payload, + NULL, NULL, + 16384, ctx->hpack_tbl, + ctx->cur_frame->allocator); + } } - status = serf_bucket_read(ctx->cur_frame, + status = serf_bucket_read(ctx->cur_payload, sizeof(ctx->buffer) - ctx->buffer_used, &data, &len); - if (!SERF_BUCKET_READ_ERROR(status)) + if (SERF_BUCKET_READ_ERROR(status)) + break; + + if (len) { memcpy(&ctx->buffer[ctx->buffer_used], data, len); ctx->buffer_used += len; } - else - break; if (APR_STATUS_IS_EOF(status)) { @@ -293,12 +389,20 @@ http2_read(serf_connection_t *conn) unsigned char frametype; unsigned char flags; - serf_bucket_http2_unframe_read_info(ctx->cur_frame, - &streamid, &frametype, - &flags); + 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\n", - (int)frametype, (int)streamid, (int)flags); + "Read 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, + "%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, @@ -308,6 +412,37 @@ http2_read(serf_connection_t *conn) (ctx->buffer_used >= 8) ? ctx->buffer_used-8 : 0)); + if (frametype == HTTP2_FRAME_TYPE_SETTINGS) + { + /* Always ack settings */ + serf_bucket_aggregate_append( + ctx->ostream, + serf__bucket_http2_frame_create( + NULL, + HTTP2_FRAME_TYPE_SETTINGS, + HTTP2_FLAG_ACK, + NULL, NULL, NULL, + HTTP2_DEFAULT_MAX_FRAMESIZE, + NULL, NULL, conn->allocator)); + } + 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__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)); + } + else if (frametype == HTTP2_FRAME_TYPE_PING) + { + /* TODO: PONG (=Ping Ack) */ + } + serf_bucket_destroy(ctx->cur_payload); ctx->cur_frame = ctx->cur_payload = NULL; ctx->in_payload = FALSE; @@ -332,9 +467,9 @@ http2_read(serf_connection_t *conn) } ctx->cur_frame = ctx->cur_payload = - serf_bucket_http2_unframe_create(conn->stream, FALSE, - HTTP2_DEFAULT_MAX_FRAMESIZE, - conn->stream->allocator); + serf__bucket_http2_unframe_create(conn->stream, FALSE, + HTTP2_DEFAULT_MAX_FRAMESIZE, + conn->stream->allocator); } return status;