Author: rhuijben
Date: Tue Oct 20 20:12:18 2015
New Revision: 1709674

URL: http://svn.apache.org/viewvc?rev=1709674&view=rev
Log:
Add an initial implementation of an http/2 framing bucket.

This adds a lot of plumbing for
- On demand streamid allocation
- Dynamic windowing

But currently it can just create valid single frames.

* buckets/http2_frame_buckets.c
  (header): Fix indentation.
  (serf_http2_frame_context_t): New typedef.
  (serf_bucket_http2_frame_create,
   serf_bucket_http2_frame_within_frame,
   http2_prepare_frame,
   serf_http2_frame_read,
   serf_http2_frame_read_iovec,
   serf_http2_frame_peek,
   serf_http2_frame_destroy): New functions.
  (serf_http2_frame_readline): New define.
  (serf_bucket_type_http2_frame): New bucket definition.

* serf-dev/dev/serf_bucket_types.h
  (serf_bucket_type_http2_frame): New bucket.
  (SERF_BUCKET_IS_HTTP2_FRAME): New define.
  (serf_bucket_http2_frame_create): New function.
  (serf_bucket_http2_frame_within_frame): New function.

* serf-dev/dev/test/test_buckets.c
  (test_http2_frame_bucket_basic): New function.
  (test_buckets): Add test_http2_frame_bucket_basic.

Modified:
    serf/trunk/buckets/http2_frame_buckets.c
    serf/trunk/serf_bucket_types.h
    serf/trunk/test/test_buckets.c

Modified: serf/trunk/buckets/http2_frame_buckets.c
URL: 
http://svn.apache.org/viewvc/serf/trunk/buckets/http2_frame_buckets.c?rev=1709674&r1=1709673&r2=1709674&view=diff
==============================================================================
--- serf/trunk/buckets/http2_frame_buckets.c (original)
+++ serf/trunk/buckets/http2_frame_buckets.c Tue Oct 20 20:12:18 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>
 
@@ -514,3 +514,237 @@ const serf_bucket_type_t serf_bucket_typ
   serf_default_ignore_config
 };
 
+/* ==================================================================== */
+
+typedef struct serf_http2_frame_context_t {
+  serf_bucket_t *stream;
+  serf_bucket_alloc_t *alloc;
+  serf_bucket_t *chunk;
+  apr_status_t stream_status;
+  apr_size_t max_payload_size;
+  apr_int32_t stream_id;
+
+  unsigned char frametype;
+  unsigned char flags;
+  char end_of_stream;
+  char end_of_headers;
+  char created_frame;
+
+  apr_int32_t *p_stream_id;
+  void *stream_id_baton;
+  void (*stream_id_alloc)(void *baton, apr_int32_t *stream_id);
+
+  apr_size_t current_window;
+  void *alloc_window_baton;
+  apr_int32_t (*alloc_window)(void *baton,
+                              unsigned char frametype,
+                              apr_int32_t stream_id,
+                              apr_size_t requested,
+                              int peek);
+
+} serf_http2_frame_context_t;
+
+serf_bucket_t *
+serf_bucket_http2_frame_create(serf_bucket_t *stream,
+                               unsigned char frame_type,
+                               unsigned char flags,
+                               apr_int32_t *stream_id,
+                               void (*stream_id_alloc)(
+                                                  void *baton,
+                                                  apr_int32_t *stream_id),
+                               void *stream_id_baton,
+                               apr_size_t max_payload_size,
+                               apr_int32_t (*alloc_window)(
+                                                  void *baton,
+                                                  unsigned char frametype,
+                                                  apr_int32_t stream_id,
+                                                  apr_size_t requested,
+                                                  int peek),
+                               void *alloc_window_baton,
+                               serf_bucket_alloc_t *alloc)
+{
+  serf_http2_frame_context_t *ctx = serf_bucket_mem_alloc(alloc, sizeof(*ctx));
+
+  ctx->alloc = alloc;
+  ctx->stream = stream;
+  ctx->chunk = serf_bucket_aggregate_create(alloc);
+  ctx->stream_status = APR_SUCCESS;
+  ctx->max_payload_size = max_payload_size;
+  ctx->frametype = frame_type;
+  ctx->flags = flags;
+
+  if (max_payload_size > 0xFFFFFF)
+    max_payload_size = 0xFFFFFF;
+
+  ctx->stream_id = (stream_id && *stream_id >= 0) ? *stream_id : -1;
+  ctx->p_stream_id = stream_id ? stream_id : &ctx->stream_id;
+  ctx->stream_id_alloc = stream_id_alloc;
+  ctx->stream_id_baton = stream_id_baton;
+  ctx->current_window = 0;
+  ctx->alloc_window = alloc_window;
+  ctx->alloc_window_baton = alloc_window_baton;
+
+  ctx->end_of_stream = ctx->end_of_headers = ctx->created_frame = FALSE;
+
+  return serf_bucket_create(&serf_bucket_type_http2_frame, alloc, ctx);
+}
+
+
+int
+serf_bucket_http2_frame_within_frame(serf_bucket_t *bucket)
+{
+  const char *data;
+  apr_size_t len;
+  apr_status_t status = serf_bucket_peek(bucket, &data, &len);
+
+  return APR_STATUS_IS_EOF(status);
+}
+
+static apr_status_t
+http2_prepare_frame(serf_bucket_t *bucket)
+{
+  serf_http2_frame_context_t *ctx = bucket->data;
+  struct iovec vecs[512];
+  int vecs_used;
+  apr_size_t len;
+  unsigned char frame[FRAME_PREFIX_SIZE];
+  int i;
+
+  if (ctx->created_frame)
+    return APR_SUCCESS;
+
+  ctx->created_frame = TRUE;
+
+  ctx->stream_status = serf_bucket_read_iovec(ctx->stream,
+                                              ctx->max_payload_size,
+                                              512, vecs, &vecs_used);
+
+  if (SERF_BUCKET_READ_ERROR(ctx->stream_status))
+    return ctx->stream_status;
+
+  /* For this first version assume that everything fits in a single frame */
+  if (! APR_STATUS_IS_EOF(ctx->stream_status))
+    abort(); /* Not implemented yet */
+
+  if (ctx->stream_id < 0 && ctx->stream_id_alloc)
+    {
+      ctx->stream_id_alloc(ctx->stream_id_baton, ctx->p_stream_id);
+      ctx->stream_id = *ctx->p_stream_id;
+    }
+
+  len = 0;
+  for (i = 0; i < vecs_used; i++)
+    len += vecs[i].iov_len;
+
+  frame[0] = (len >> 16) & 0xFF;
+  frame[1] = (len >> 8) & 0xFF;
+  frame[2] = len & 0xFF;
+  frame[3] = ctx->frametype;
+  frame[4] = ctx->flags;
+  frame[5] = ((apr_uint32_t)ctx->stream_id >> 24) & 0x7F;
+  frame[6] = ((apr_uint32_t)ctx->stream_id >> 16) & 0xFF;
+  frame[7] = ((apr_uint32_t)ctx->stream_id >> 8) & 0xFF;
+  frame[8] = ctx->stream_id & 0xFF;
+
+  serf_bucket_aggregate_append(ctx->chunk,
+              serf_bucket_simple_copy_create((const char *)&frame,
+                                             FRAME_PREFIX_SIZE,
+                                             ctx->alloc));
+  if (vecs_used > 0)
+    serf_bucket_aggregate_append_iovec(ctx->chunk, vecs, vecs_used);
+
+  return APR_SUCCESS;
+}
+
+static apr_status_t
+serf_http2_frame_read(serf_bucket_t *bucket,
+                      apr_size_t requested,
+                      const char **data,
+                      apr_size_t *len)
+{
+  serf_http2_frame_context_t *ctx = bucket->data;
+  apr_status_t status;
+
+  status = http2_prepare_frame(bucket);
+  if (status)
+    return status;
+
+  status = serf_bucket_read(ctx->chunk, requested, data, len);
+
+  if (APR_STATUS_IS_EOF(status))
+    return ctx->stream_status;
+
+  return status;
+}
+
+static apr_status_t
+serf_http2_frame_read_iovec(serf_bucket_t *bucket,
+                            apr_size_t requested,
+                            int vecs_size,
+                            struct iovec *vecs,
+                            int *vecs_used)
+{
+  serf_http2_frame_context_t *ctx = bucket->data;
+  apr_status_t status;
+
+  status = http2_prepare_frame(bucket);
+  if (status)
+    return status;
+
+  status = serf_bucket_read_iovec(ctx->chunk, requested, vecs_size, vecs,
+                                  vecs_used);
+
+  if (APR_STATUS_IS_EOF(status))
+    return ctx->stream_status;
+
+  return status;
+}
+
+static apr_status_t
+serf_http2_frame_peek(serf_bucket_t *bucket,
+                      const char **data,
+                      apr_size_t *len)
+{
+  serf_http2_frame_context_t *ctx = bucket->data;
+  apr_status_t status;
+
+  status = http2_prepare_frame(bucket);
+  if (status)
+    return status;
+
+  status = serf_bucket_peek(ctx->chunk, data, len);
+
+  if (APR_STATUS_IS_EOF(status))
+    return ctx->stream_status;
+
+  return status;
+}
+
+static void
+serf_http2_frame_destroy(serf_bucket_t *bucket)
+{
+  serf_http2_frame_context_t *ctx = bucket->data;
+
+  serf_bucket_destroy(ctx->stream);
+  serf_bucket_destroy(ctx->chunk);
+
+  serf_default_destroy_and_data(bucket);
+}
+
+/* ### need to implement */
+#define serf_http2_frame_readline NULL
+
+const serf_bucket_type_t serf_bucket_type_http2_frame = {
+  "H2-FRAME",
+  serf_http2_frame_read,
+  serf_http2_frame_readline,
+  serf_http2_frame_read_iovec,
+  serf_default_read_for_sendfile,
+  serf_buckets_are_v2,
+  serf_http2_frame_peek,
+  serf_http2_frame_destroy,
+  serf_default_read_bucket,
+  serf_default_get_remaining,
+  serf_default_ignore_config
+};
+

Modified: serf/trunk/serf_bucket_types.h
URL: 
http://svn.apache.org/viewvc/serf/trunk/serf_bucket_types.h?rev=1709674&r1=1709673&r2=1709674&view=diff
==============================================================================
--- serf/trunk/serf_bucket_types.h (original)
+++ serf/trunk/serf_bucket_types.h Tue Oct 20 20:12:18 2015
@@ -861,6 +861,33 @@ void serf_bucket_hpack_do(serf_bucket_t
                           void *baton);
 
 /* ==================================================================== */
+extern const serf_bucket_type_t serf_bucket_type_http2_frame;
+
+#define SERF_BUCKET_IS_HTTP2_FRAME(b) SERF_BUCKET_CHECK((b), http2_frame)
+
+serf_bucket_t *
+serf_bucket_http2_frame_create(serf_bucket_t *stream,
+                               unsigned char frame_type,
+                               unsigned char flags,
+                               apr_int32_t *stream_id,
+                               void(*stream_id_alloc)(
+                                 void *baton,
+                                 apr_int32_t *stream_id),
+                               void *stream_id_baton,
+                               apr_size_t max_payload_size,
+                               apr_int32_t(*alloc_window)(
+                                 void *baton,
+                                 unsigned char frametype,
+                                 apr_int32_t stream_id,
+                                 apr_size_t requested,
+                                 int peek),
+                               void *alloc_window_baton,
+                               serf_bucket_alloc_t *alloc);
+
+int
+serf_bucket_http2_frame_within_frame(serf_bucket_t *bucket);
+
+/* ==================================================================== */
 
 /* ### do we need a PIPE bucket type? they are simple apr_file_t objects */
 

Modified: serf/trunk/test/test_buckets.c
URL: 
http://svn.apache.org/viewvc/serf/trunk/test/test_buckets.c?rev=1709674&r1=1709673&r2=1709674&view=diff
==============================================================================
--- serf/trunk/test/test_buckets.c (original)
+++ serf/trunk/test/test_buckets.c Tue Oct 20 20:12:18 2015
@@ -2152,6 +2152,48 @@ static void test_hpack_header_encode(CuT
   CuAssertTrue(tc, sz <= 59); /* The all literal approach takes 59 bytes */
 }
 
+static void test_http2_frame_bucket_basic(CuTest *tc)
+{
+  test_baton_t *tb = tc->testBaton;
+  serf_bucket_alloc_t *alloc;
+  serf_bucket_t *hpack;
+  apr_size_t sz;
+  serf_bucket_t *body_in;
+  serf_bucket_t *frame_in;
+  serf_bucket_t *frame_out;
+  apr_int32_t exp_streamid = 0x01020304;
+
+  alloc = serf_bucket_allocator_create(tb->pool, NULL, NULL);
+
+  body_in = SERF_BUCKET_SIMPLE_STRING("This is no config!", alloc);
+  frame_in = serf_bucket_http2_frame_create(body_in, 99, 7, &exp_streamid,
+                                            NULL, NULL,
+                                            16384, NULL, NULL, alloc);
+  frame_out = serf_bucket_http2_unframe_create(frame_in, FALSE, 16384, alloc);
+
+  read_and_check_bucket(tc, frame_out, "This is no config!");
+
+  {
+    apr_int32_t streamid;
+    unsigned char frametype;
+    unsigned char flags;
+    const char *buffer;
+    apr_size_t sz;
+
+    CuAssertIntEquals(tc, 0,
+                      serf_bucket_http2_unframe_read_info(frame_out, &streamid,
+                                                          &frametype, &flags));
+    CuAssertIntEquals(tc, 0x01020304, streamid);
+    CuAssertIntEquals(tc, 99, frametype);
+    CuAssertIntEquals(tc, 7, flags);
+
+    CuAssertIntEquals(tc, APR_EOF,
+                      serf_bucket_read(frame_in, SERF_READ_ALL_AVAIL,
+                                       &buffer, &sz));
+    CuAssertIntEquals(tc, 0, sz);
+  }
+}
+
 CuSuite *test_buckets(void)
 {
     CuSuite *suite = CuSuiteNew();
@@ -2186,6 +2228,7 @@ CuSuite *test_buckets(void)
     SUITE_ADD_TEST(suite, test_hpack_huffman_decode);
     SUITE_ADD_TEST(suite, test_hpack_huffman_encode);
     SUITE_ADD_TEST(suite, test_hpack_header_encode);
+    SUITE_ADD_TEST(suite, test_http2_frame_bucket_basic);
 #if 0
     /* This test for issue #152 takes a lot of time generating 4GB+ of random
        data so it's disabled by default. */


Reply via email to