Author: rhuijben Date: Tue Oct 20 20:23:04 2015 New Revision: 1709676 URL: http://svn.apache.org/viewvc?rev=1709676&view=rev Log: Checkpoint the current prototype of a http2 protocol implementation.
When used with a recent OpenSSL and a serf_get with '--http2' enabled this version can successfully send HTTP/2 requests to http2 enabled servers such as https://www.google.com and https://www.akamai.com But that is about it... it just dumps the frame information when using the debug mode. * serf-dev/dev/protocols/http2_protocol.c (header): Fix indentation. (includes): Add protocols/http2_protocol.h. (serf__http2_protocol_init): Schedule http2 preface and initial setting frame. (setup_for_http2): New function. (http2_read): Properly handle the success result. Print out the debug information from go-away frames if there is interesting data. (Google sends parser errors as text there). (http2_protocol_read): Handle the ssl implementation's stop_writing. (http2_protocol_write): Write out data when possible. * serf-dev/dev/protocols/http2_protocol.h New private header, containing many HTTP2 specific constants. Added: serf/trunk/protocols/http2_protocol.h (with props) 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=1709676&r1=1709675&r2=1709676&view=diff ============================================================================== --- serf/trunk/protocols/http2_protocol.c (original) +++ serf/trunk/protocols/http2_protocol.c Tue Oct 20 20:23:04 2015 @@ -1,22 +1,22 @@ /* ==================================================================== -* 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. -* ==================================================================== -*/ + * 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 <apr_pools.h> #include <apr_poll.h> @@ -28,6 +28,7 @@ #include "serf_bucket_util.h" #include "serf_private.h" +#include "protocols/http2_protocol.h" static apr_status_t http2_protocol_read(serf_connection_t *conn); @@ -71,6 +72,7 @@ void serf__http2_protocol_init(serf_conn { serf_http2_procotol_state_t *ctx; apr_pool_t *protocol_pool; + serf_bucket_t *tmp; apr_pool_create(&protocol_pool, conn->pool); @@ -85,6 +87,64 @@ void serf__http2_protocol_init(serf_conn conn->perform_hangup = http2_protocol_hangup; conn->perform_teardown = http2_protocol_teardown; conn->protocol_baton = ctx; + + /* Disable HTTP/1.1 guessing that affects writability */ + conn->probable_keepalive_limit = 0; + conn->max_outstanding_requests = 0; + + /* Send the HTTP/2 Connection Preface */ + tmp = SERF_BUCKET_SIMPLE_STRING("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", + conn->allocator); + serf_bucket_aggregate_append(conn->ostream_tail, tmp); + + /* And now a settings frame */ + { + serf_bucket_t *no_settings; + 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*/ + 16384 /* max_framesize */, + NULL, NULL, conn->allocator); + + serf_bucket_aggregate_append(conn->ostream_tail, tmp); + } +} + +/* Creates a HTTP/2 request from a serf request */ +static apr_status_t +setup_for_http2(serf_request_t *request) +{ + apr_status_t status; + serf_bucket_t *rq; + serf_bucket_t *hpack; + serf_bucket_t *body; + static apr_int32_t NEXT_frame = 1; + + apr_int32_t streamid = NEXT_frame; + NEXT_frame += 2; + + rq = request->req_bkt; + + status = serf__bucket_hpack_create_from_request(&hpack, NULL, + rq, + request->conn->host_info.scheme, + request->allocator); + if (status) + return status; + + 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(request->conn->ostream_tail, + hpack); + + return APR_SUCCESS; } static apr_status_t @@ -93,10 +153,46 @@ http2_read(serf_connection_t *conn) serf_http2_procotol_state_t *ctx = conn->protocol_baton; apr_status_t status = APR_SUCCESS; + while (TRUE) { status = APR_SUCCESS; + { + serf_request_t *request = conn->unwritten_reqs; + + if (request) + { + apr_status_t status; + serf_bucket_t *req_bkt; + + /* 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; + + if (!request->req_bkt + || !SERF_BUCKET_IS_REQUEST(request->req_bkt)) + { + status = serf__setup_request(request); + if (status) + return status; + } + + status = setup_for_http2(request); + if (status) + return status; + } + } + if (ctx->cur_frame) { const char *data; @@ -137,6 +233,8 @@ http2_read(serf_connection_t *conn) memcpy(&ctx->buffer[ctx->buffer_used], data, len); ctx->buffer_used += len; } + else + break; if (APR_STATUS_IS_EOF(status)) { @@ -144,20 +242,28 @@ http2_read(serf_connection_t *conn) unsigned char frametype; unsigned char flags; - status = 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); + if (frametype == HTTP2_FRAME_TYPE_GOAWAY && conn) + serf__log(LOGLVL_WARNING, LOGCOMP_CONN, __FILE__, conn->config, + "Go away reason %d: %s\n", ctx->buffer[7], + apr_pstrmemdup(conn->pool, + &ctx->buffer[8], + (ctx->buffer_used >= 8) + ? ctx->buffer_used-8 : 0)); + serf_bucket_destroy(ctx->cur_payload); ctx->cur_frame = ctx->cur_payload = NULL; ctx->in_payload = FALSE; ctx->buffer_used = 0; } - else if (status) - break; + else + continue; } if (APR_STATUS_IS_EOF(status)) @@ -186,7 +292,18 @@ http2_read(serf_connection_t *conn) static apr_status_t http2_protocol_read(serf_connection_t *conn) { - apr_status_t status = http2_read(conn); + apr_status_t status; + + /* If the stop_writing flag was set on the connection, reset it now because + there is some data to read. */ + if (conn->stop_writing) + { + conn->stop_writing = 0; + conn->dirty_conn = 1; + conn->ctx->dirty_pollset = 1; + } + + status = http2_read(conn); if (!status) return APR_SUCCESS; @@ -208,7 +325,19 @@ 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_procotol_state_t *ctx = conn->protocol_baton; + apr_status_t status; + + status = serf__connection_flush(conn, TRUE); + + if (APR_STATUS_IS_EAGAIN(status)) + return APR_SUCCESS; + else if (status) + return status; + + /* Probably nothing to write. Connection will check new requests */ + conn->dirty_conn = 1; + conn->ctx->dirty_pollset = 1; return APR_SUCCESS; } Added: serf/trunk/protocols/http2_protocol.h URL: http://svn.apache.org/viewvc/serf/trunk/protocols/http2_protocol.h?rev=1709676&view=auto ============================================================================== --- serf/trunk/protocols/http2_protocol.h (added) +++ serf/trunk/protocols/http2_protocol.h Tue Oct 20 20:23:04 2015 @@ -0,0 +1,85 @@ +/* ==================================================================== + * 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. + * ==================================================================== + */ + +#ifndef SERF_PROTOCOL_HTTP2_PRIVATE_H +#define SERF_PROTOCOL_HTTP2_PRIVATE_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/* ********** HTTP2 Frame types ********** */ + +/* The standard maximum framesize. Always supported */ +#define HTTP2_DEFAULT_MAX_FRAMESIZE 16384 + +/* Frame type is an 8 bit unsigned integer */ + +/* http://tools.ietf.org/html/rfc7540#section-11.2 */ +#define HTTP2_FRAME_TYPE_DATA 0x00 +#define HTTP2_FRAME_TYPE_HEADERS 0x01 +#define HTTP2_FRAME_TYPE_PRIORITY 0x02 +#define HTTP2_FRAME_TYPE_RST_STREAM 0x03 +#define HTTP2_FRAME_TYPE_SETTINGS 0x04 +#define HTTP2_FRAME_TYPE_PUSH_PROMISE 0x05 +#define HTTP2_FRAME_TYPE_PING 0x06 +#define HTTP2_FRAME_TYPE_GOAWAY 0x07 +#define HTTP2_FRAME_TYPE_WINDOW_UPDATE 0x08 +#define HTTP2_FRAME_TYPE_CONTINUATION 0x09 +/* https://httpwg.github.io/http-extensions/alt-svc.html + documents that frame 0x0A will most likely be assigned + to ALT-SVC */ + +/* ********** HTTP2 Flags ********** */ + +/* Flags are currently unique over all frame types, but perhaps this + may change in future specs */ + + +/* Defined on DATA and HEADERS */ +#define HTTP2_FLAG_END_STREAM 0x01 +/* Defined on HEADERS and CONTINUATION */ +#define HTTP2_FLAG_END_HEADERS 0x04 +/* Defined on DATA and HEADERS */ +#define HTTP2_FLAG_PADDED 0x08 +/* Defined on HEADERS */ +#define HTTP2_FLAG_PRIORITY 0x20 + + +/* ********** HTTP2 Settings ********** */ + +/* Settings are 16 bit unsigned integers*/ +#define HTTP2_SETTING_HEADER_TABLE_SIZE 0x0001 /* default: 4096 */ +#define HTTP2_SETTING_ENABLE_PUSH 0x0002 /* default: 1 */ +#define HTTP2_SETTING_MAX_CONCURRENT_STREAMS 0x0003 /* default: (infinite) */ +#define HTTP2_SETTING_INITIAL_WINDOW_SIZE 0x0004 /* default: 65535 */ +#define HTTP2_SETTING_MAX_FRAME_SIZE 0x0005 /* default: 16384 */ +#define HTTP2_SETTING_MAX_HEADER_LIST_SIZE 0x0006 /* default: (infinite) */ + +/* https://tools.ietf.org/html/rfc7540#section-3.5 */ +#define HTTP2_CONNECTION_PREFIX "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" + + +#ifdef __cplusplus +} +#endif + +#endif Propchange: serf/trunk/protocols/http2_protocol.h ------------------------------------------------------------------------------ svn:eol-style = native