serf-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhuij...@apache.org
Subject svn commit: r1709439 - in /serf/trunk: buckets/hpack_buckets.c buckets/request_buckets.c serf_private.h test/test_buckets.c
Date Mon, 19 Oct 2015 16:13:11 GMT
Author: rhuijben
Date: Mon Oct 19 16:13:10 2015
New Revision: 1709439

URL: http://svn.apache.org/viewvc?rev=1709439&view=rev
Log:
Add another piece of the HTTP/2 puzzle: a very simple HPACK encoder,
and a bit of plumbing to allow converting a HTTP/1.1 style request to
a HPACK set.

* serf-dev/dev/buckets/hpack_buckets.c
  (serf_hpack_item_t,
   serf_hpack_context_t): New struct.
  (hpack_copy_from_headers,
   serf__bucket_hpack_create_from_request,
   serf_bucket_hpack_create,
   serf_bucket_hpack_setc,
   serf_bucket_hpack_setx,
   hpack_int,
   serialize,
   serf_hpack_read,
   serf_hpack_read_iovec,
   serf_hpack_peek,
   serf_hpack_destroy_and_data): New functions.
  (serf_bucket_type_hpack): New bucket definition.

* serf-dev/dev/buckets/request_buckets.c
  (serf__bucket_request_read): New function.

* serf-dev/dev/serf_private.h
  (serf__bucket_request_read): New function.
  (serf__bucket_hpack_create_from_request): New function.

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

Modified:
    serf/trunk/buckets/hpack_buckets.c
    serf/trunk/buckets/request_buckets.c
    serf/trunk/serf_private.h
    serf/trunk/test/test_buckets.c

Modified: serf/trunk/buckets/hpack_buckets.c
URL: http://svn.apache.org/viewvc/serf/trunk/buckets/hpack_buckets.c?rev=1709439&r1=1709438&r2=1709439&view=diff
==============================================================================
--- serf/trunk/buckets/hpack_buckets.c (original)
+++ serf/trunk/buckets/hpack_buckets.c Mon Oct 19 16:13:10 2015
@@ -239,3 +239,317 @@ serf__hpack_huffman_encode(const char *t
 
   return APR_SUCCESS;
 }
+
+/* ==================================================================== */
+
+typedef struct serf_hpack_item_t
+{
+  const char *key;
+  apr_size_t key_len;
+  const char *value;
+  apr_size_t value_len;
+
+  struct serf_hpack_item_t *next;
+  struct serf_hpack_item_t *prev;
+} serf_hpack_item_t;
+
+typedef struct serf_hpack_context_t
+{
+  serf_bucket_alloc_t *alloc;
+
+  serf_hpack_item_t *first;
+  serf_hpack_item_t *last;
+} serf_hpack_context_t;
+
+static apr_status_t
+hpack_copy_from_headers(void *baton,
+                        const char *key,
+                        const char *value)
+{
+  serf_bucket_t *hpack = baton;
+
+  if (!strcasecmp(key, "Host")
+      || !strcasecmp(key, "Connection")
+      || !strncasecmp(key, "Connection-", 11))
+    {
+      return APR_SUCCESS;
+    }
+
+  serf_bucket_hpack_setc(hpack, key, value);
+
+  return APR_SUCCESS;
+}
+
+
+apr_status_t
+serf__bucket_hpack_create_from_request(serf_bucket_t **new_hpack_bucket,
+                                       serf_hpack_table_t *hpack_table,
+                                       serf_bucket_t *request,
+                                       const char *scheme,
+                                       serf_bucket_alloc_t *allocator)
+{
+  const char *uri, *method, *host;
+
+  serf_bucket_t *hpack = serf_bucket_hpack_create(hpack_table, allocator);
+
+  serf_bucket_t *headers = serf_bucket_request_get_headers(request);
+
+  host = serf_bucket_headers_get(headers, "Host");
+
+  serf__bucket_request_read(request, NULL, &uri, &method);
+
+  serf_bucket_hpack_setc(hpack, ":method", method);
+  serf_bucket_hpack_setc(hpack, ":scheme", scheme);
+  serf_bucket_hpack_setc(hpack, ":authority", host);
+  serf_bucket_hpack_setc(hpack, ":path", uri);
+
+  serf_bucket_headers_do(headers, hpack_copy_from_headers, hpack);
+
+  *new_hpack_bucket = hpack;
+
+  return APR_SUCCESS;
+}
+
+
+serf_bucket_t *
+serf_bucket_hpack_create(serf_hpack_table_t *hpack_table,
+                         serf_bucket_alloc_t *allocator)
+
+{
+  serf_hpack_context_t *ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
+
+  ctx->alloc = allocator;
+  ctx->first = ctx->last = NULL;
+
+  return serf_bucket_create(&serf_bucket_type_hpack, allocator, ctx);
+}
+
+void serf_bucket_hpack_setc(serf_bucket_t *hpack_bucket,
+                            const char *key,
+                            const char *value)
+{
+  serf_bucket_hpack_setx(hpack_bucket,
+                         key, strlen(key), TRUE,
+                         value, strlen(value), TRUE);
+}
+
+void serf_bucket_hpack_setx(serf_bucket_t *hpack_bucket,
+                            const char *key,
+                            apr_size_t key_size,
+                            int header_copy,
+                            const char *value,
+                            apr_size_t value_size,
+                            int value_copy)
+{
+  serf_hpack_context_t *ctx = hpack_bucket->data;
+  serf_hpack_item_t *hi;
+
+  for (hi = ctx->first; hi; hi = hi->next)
+    {
+      if (key_size == hi->key_len
+          && !strncasecmp(key, hi->key, key_size))
+        {
+          break;
+        }
+    }
+
+  /* TODO: Handle *_copy by keeping some flags */
+
+  if (hi && value[0] == ':')
+    {
+      serf_bucket_mem_free(ctx->alloc, (void*)hi->value);
+      hi->value = serf_bstrmemdup(ctx->alloc, value, value_size);
+      hi->value_len = value_size;
+
+      return;
+    }
+  else if (hi)
+    {
+      /* We probably want to allow duplicate *and* join behavior? */
+    }
+
+  hi = serf_bucket_mem_alloc(ctx->alloc, sizeof(*hi));
+
+  /* Convert keys to lower case as in RFC? Or keep case for
+     1.1 like compatibility */
+
+  hi->key = serf_bstrmemdup(ctx->alloc, key, key_size);
+  hi->key_len = key_size;
+  hi->value = serf_bstrmemdup(ctx->alloc, value, value_size);
+  hi->value_len = value_size;
+
+  hi->prev = ctx->last;
+  hi->next = NULL;
+  if (ctx->last)
+    {
+      ctx->last->next = hi;
+      ctx->last = hi;
+    }
+  else
+    ctx->first = ctx->last = hi;
+}
+
+static void hpack_int(unsigned char flags, int bits, apr_uint64_t value, char to[10], apr_size_t
*used)
+{
+  unsigned char max_direct;
+  flags = flags & ~((1 << bits) - 1);
+  apr_size_t u;
+
+  max_direct = (unsigned char)(((apr_uint16_t)1 << bits) - 1);
+
+  if (value < max_direct)
+    {
+      to[0] = flags | (unsigned char)value;
+      *used = 1;
+      return;
+    }
+
+  to[0] = flags | max_direct;
+  value -= max_direct;
+  u = 1;
+
+  while (value >= 0x80)
+    {
+      to[u++] = (value & 0x7F) | 0x80;
+      value >>= 7;
+    }
+
+  to[u++] = (unsigned char)value;
+  *used = u;
+}
+
+static apr_status_t
+serialize(serf_bucket_t *bucket)
+{
+  /* Quick and dirty write out headers for V2
+
+     Needs A LOT of improvement.
+
+     Currently implements a complete in memory copy to the least
+     efficient HTTP2 / HPACK header format 
+   */
+  serf_hpack_context_t *ctx = bucket->data;
+  serf_bucket_alloc_t *alloc = ctx->alloc;
+
+  /* Put on our aggregate bucket cloak */
+  serf_bucket_aggregate_become(bucket);
+
+  serf_hpack_item_t *hi;
+  serf_hpack_item_t *next;
+
+  for (hi = ctx->first; hi; hi = next)
+    {
+      char intbuf[10];
+      apr_size_t intbuf_len;
+
+      next = hi->next;
+
+      /* Literal header, no indexing (=has a name) */
+      hpack_int(0x40, 6, 0, intbuf, &intbuf_len);
+
+      serf_bucket_aggregate_append(bucket,
+              serf_bucket_simple_copy_create(intbuf, intbuf_len, alloc));
+
+      /* Name is literal, no huffman encoding */
+      hpack_int(0, 7, hi->key_len, intbuf, &intbuf_len);
+      serf_bucket_aggregate_append(bucket,
+              serf_bucket_simple_copy_create(intbuf, intbuf_len, alloc));
+
+      serf_bucket_aggregate_append(bucket,
+              serf_bucket_simple_own_create(hi->key, hi->key_len, alloc));
+
+      /* Value is literal, no huffman encoding */
+      hpack_int(0, 7, hi->value_len, intbuf, &intbuf_len);
+      serf_bucket_aggregate_append(bucket,
+              serf_bucket_simple_copy_create(intbuf, intbuf_len, alloc));
+
+      serf_bucket_aggregate_append(bucket,
+              serf_bucket_simple_own_create(hi->value, hi->value_len, alloc));
+
+      /* We handed ownership of key and value, so we only have to free item */
+      serf_bucket_mem_free(alloc, hi);
+    }
+  ctx->first = ctx->last = NULL;
+
+  serf_bucket_mem_free(alloc, ctx);
+
+  return APR_SUCCESS;
+}
+
+apr_status_t
+serf_hpack_read(serf_bucket_t *bucket,
+                apr_size_t requested,
+                const char **data,
+                apr_size_t *len)
+{
+  apr_status_t status = serialize(bucket);
+
+  if (status)
+    return status;
+
+  return serf_bucket_read(bucket, requested, data, len);
+}
+
+apr_status_t
+serf_hpack_read_iovec(serf_bucket_t *bucket,
+                      apr_size_t requested,
+                      int vecs_size,
+                      struct iovec *vecs,
+                      int *vecs_used)
+{
+  apr_status_t status = serialize(bucket);
+
+  if (status)
+    return status;
+
+  return serf_bucket_read_iovec(bucket, requested, vecs_size, vecs, vecs_used);
+}
+
+static apr_status_t
+serf_hpack_peek(serf_bucket_t *bucket,
+                const char **data,
+                apr_size_t *len)
+{
+  apr_status_t status = serialize(bucket);
+
+  if (status)
+    return status;
+
+  return serf_bucket_peek(bucket, data, len);
+}
+
+static void
+serf_hpack_destroy_and_data(serf_bucket_t *bucket)
+{
+  serf_hpack_context_t *ctx = bucket->data;
+  serf_hpack_item_t *hi;
+  serf_hpack_item_t *next;
+
+  for (hi = ctx->first; hi; hi = next)
+    {
+      next = hi->next;
+
+      /* TODO: Implement conditional free */
+
+      serf_bucket_mem_free(ctx->alloc, (char*)hi->key);
+      serf_bucket_mem_free(ctx->alloc, (char*)hi->value);
+      serf_bucket_mem_free(ctx->alloc, hi);
+    }
+
+  serf_default_destroy_and_data(bucket);
+}
+
+
+/* ### need to implement */
+#define serf_hpack_readline NULL
+
+const serf_bucket_type_t serf_bucket_type_hpack = {
+  "HPACK",
+  serf_hpack_read,
+  serf_hpack_readline,
+  serf_hpack_read_iovec,
+  serf_default_read_for_sendfile,
+  serf_default_read_bucket,
+  serf_hpack_peek,
+  serf_hpack_destroy_and_data,
+};

Modified: serf/trunk/buckets/request_buckets.c
URL: http://svn.apache.org/viewvc/serf/trunk/buckets/request_buckets.c?rev=1709439&r1=1709438&r2=1709439&view=diff
==============================================================================
--- serf/trunk/buckets/request_buckets.c (original)
+++ serf/trunk/buckets/request_buckets.c Mon Oct 19 16:13:10 2015
@@ -71,6 +71,22 @@ serf_bucket_t *serf_bucket_request_get_h
     return ((request_context_t *)bucket->data)->headers;
 }
 
+void serf__bucket_request_read(serf_bucket_t *request_bucket,
+                               serf_bucket_t **body_bkt,
+                               const char **uri,
+                               const char **method)
+{
+    request_context_t *ctx = request_bucket->data;
+
+    if (body_bkt)
+        *body_bkt = ctx->body;
+    if (uri)
+        *uri = ctx->uri;
+    if (method)
+        *method = ctx->method;
+}
+
+
 void serf_bucket_request_set_root(
     serf_bucket_t *bucket,
     const char *root_url)

Modified: serf/trunk/serf_private.h
URL: http://svn.apache.org/viewvc/serf/trunk/serf_private.h?rev=1709439&r1=1709438&r2=1709439&view=diff
==============================================================================
--- serf/trunk/serf_private.h (original)
+++ serf/trunk/serf_private.h Mon Oct 19 16:13:10 2015
@@ -447,6 +447,21 @@ void serf__bucket_response_set_error_on_
 void serf__bucket_headers_remove(serf_bucket_t *headers_bucket,
                                  const char *header);
 
+/**
+ * Read raw information stored in request REQUEST_BUCKET. All output values
+ * are directly copied from the internal state.
+ *
+ * Output values can be passed as NULL when not interested.
+ */
+void serf__bucket_request_read(serf_bucket_t *request_bucket,
+                               serf_bucket_t **body_bkt,
+                               const char **uri,
+                               const char **method);
+
+serf_bucket_t *serf_bucket_request_get_body(
+  serf_bucket_t *bucket);
+
+
 /*** Authentication handler declarations ***/
 
 typedef enum { PROXY, HOST } peer_t;
@@ -536,6 +551,13 @@ apr_status_t serf__hpack_huffman_encode(
                                         unsigned char *encoded,
                                         apr_size_t *encoded_len);
 
+apr_status_t serf__bucket_hpack_create_from_request(
+                                        serf_bucket_t **new_hpack_bucket,
+                                        serf_hpack_table_t *hpack_table,
+                                        serf_bucket_t *request,
+                                        const char *scheme,
+                                        serf_bucket_alloc_t *allocator);
+
 /* From connection_request.c */
 void serf__link_requests(serf_request_t **list, serf_request_t **tail,
                          serf_request_t *request);

Modified: serf/trunk/test/test_buckets.c
URL: http://svn.apache.org/viewvc/serf/trunk/test/test_buckets.c?rev=1709439&r1=1709438&r2=1709439&view=diff
==============================================================================
--- serf/trunk/test/test_buckets.c (original)
+++ serf/trunk/test/test_buckets.c Mon Oct 19 16:13:10 2015
@@ -2125,6 +2125,33 @@ static void test_hpack_huffman_encode(Cu
 }
 #undef VERIFY_REVERSE
 
+static void test_hpack_header_encode(CuTest *tc)
+{
+  test_baton_t *tb = tc->testBaton;
+  serf_bucket_alloc_t *alloc;
+  serf_bucket_t *hpack;
+  char resultbuffer[1024];
+  apr_size_t sz;
+
+  alloc = serf_bucket_allocator_create(tb->pool, NULL, NULL);
+
+  hpack = serf_bucket_hpack_create(NULL, alloc);
+
+  CuAssertTrue(tc, SERF_BUCKET_IS_HPACK(hpack));
+
+  serf_bucket_hpack_setc(hpack, ":method", "PUT");
+  serf_bucket_hpack_setc(hpack, ":scheme", "https");
+  serf_bucket_hpack_setc(hpack, ":path", "/");
+  serf_bucket_hpack_setc(hpack, ":authority", "localhost");
+
+  CuAssertIntEquals(tc, APR_EOF,
+                    read_all(hpack, resultbuffer, sizeof(resultbuffer), &sz));
+
+  /* CuAssertTrue(tc, ! SERF_BUCKET_IS_HPACK(hpack)); */
+  CuAssertTrue(tc, sz > 4);
+  CuAssertTrue(tc, sz <= 59); /* The all literal approach takes 59 bytes */
+}
+
 CuSuite *test_buckets(void)
 {
     CuSuite *suite = CuSuiteNew();
@@ -2158,6 +2185,7 @@ CuSuite *test_buckets(void)
     SUITE_ADD_TEST(suite, test_http2_unpad_buckets);
     SUITE_ADD_TEST(suite, test_hpack_huffman_decode);
     SUITE_ADD_TEST(suite, test_hpack_huffman_encode);
+    SUITE_ADD_TEST(suite, test_hpack_header_encode);
 #if 0
     /* This test for issue #152 takes a lot of time generating 4GB+ of random
        data so it's disabled by default. */



Mime
View raw message