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


Reply via email to