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. */