Author: rhuijben
Date: Mon Nov 30 09:38:34 2015
New Revision: 1717218

URL: http://svn.apache.org/viewvc?rev=1717218&view=rev
Log:
Add an outgoing response bucket for usage with our server/listening mode.

Use this to hide some HTTP/1.1 requirements from server implementations.

* buckets/response_buckets.c
  (outgoing_response_t): New define.
  (serf_bucket_outgoing_response_create,
   serf_bucket_outgoing_response_get_headers,
   serf_bucket_outgoing_response_prepare): New public functions.
  (serialize_outgoing_response,
   serf_outgoing_resp_read,
   serf_outgoing_resp_read_iovec,
   serf_outgoing_resp_readline,
   serf_outgoing_resp_peek,
   serf_outgoing_resp_set_config,
   serf_outgoing_resp_destroy): New function.
  (serf_bucket_type_outgoing_response): New bucket type.

* incoming.c
  (http1_enqueue_reponse): If the response is an outgoing response bucket,
    prepare it for transfer over HTTP/1.1

* serf_bucket_types.h
  (serf_bucket_type_outgoing_response): New bucket.
  (SERF_BUCKET_IS_OUTGOING_RESPONSE): New define.
  (serf_bucket_outgoing_response_create,
   serf_bucket_outgoing_response_get_headers,
   serf_bucket_outgoing_response_prepare): New function.

* test/test_server.c
  (client_generate_response): Create proper response.

Modified:
    serf/trunk/buckets/response_buckets.c
    serf/trunk/incoming.c
    serf/trunk/serf_bucket_types.h
    serf/trunk/test/test_server.c

Modified: serf/trunk/buckets/response_buckets.c
URL: 
http://svn.apache.org/viewvc/serf/trunk/buckets/response_buckets.c?rev=1717218&r1=1717217&r2=1717218&view=diff
==============================================================================
--- serf/trunk/buckets/response_buckets.c (original)
+++ serf/trunk/buckets/response_buckets.c Mon Nov 30 09:38:34 2015
@@ -779,3 +779,232 @@ const serf_bucket_type_t serf_bucket_typ
     serf_default_get_remaining,
     serf_response_set_config,
 };
+
+/* ==================================================================== */
+
+typedef struct outgoing_response_t {
+    int status;
+    int http_version;
+    char *reason;
+
+    serf_bucket_t *body;
+    serf_bucket_t *headers;
+    serf_config_t *config;
+} outgoing_response_t;
+
+serf_bucket_t *serf_bucket_outgoing_response_create(
+    serf_bucket_t *body,
+    int status,
+    const char *reason,
+    int http_version,
+    serf_bucket_alloc_t *allocator)
+{
+    outgoing_response_t *ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
+
+    ctx->headers = serf_bucket_headers_create(allocator);
+    ctx->body = body;
+    ctx->config = NULL;
+
+    ctx->status = status;
+    ctx->reason = serf_bstrdup(allocator, reason ? reason : "-");
+    ctx->http_version = http_version;
+
+    return serf_bucket_create(&serf_bucket_type_outgoing_response,
+                              allocator, ctx);
+}
+
+serf_bucket_t *serf_bucket_outgoing_response_get_headers(
+    serf_bucket_t *outgoing_response)
+{
+    outgoing_response_t *ctx = outgoing_response->data;
+
+    return ctx->headers;
+}
+
+void serf_bucket_outgoing_response_prepare(
+    serf_bucket_t *outgoing_response,
+    int http_version,
+    int allow_chunking)
+{
+    outgoing_response_t *ctx = outgoing_response->data;
+    apr_uint64_t content_length;
+
+    if (serf_bucket_headers_get(ctx->headers, "Content-Length"))
+        return; /* Already safe */
+
+    if (ctx->status <= 199 || ctx->status == 304 || ctx->status == 204)
+        return; /* No body description necessary */
+
+    if (http_version == SERF_HTTP_11
+        && serf_bucket_headers_get(ctx->headers, "Transfer-Encoding")) {
+
+        return; /* Transfer method already described */
+    }
+
+    if (!ctx->body)
+        content_length = 0;
+    else
+        content_length = serf_bucket_get_remaining(ctx->body);
+
+    /* If we can produce a Content-Length, produce it. Even for HTTP/2
+       (assuming this method is called for HTTP2) */
+    if (content_length != SERF_LENGTH_UNKNOWN) {
+        char buf[30];
+        sprintf(buf, "%" APR_INT64_T_FMT, content_length);
+
+        serf_bucket_headers_setc(ctx->headers, "Content-Length", buf);
+    }
+    else if (http_version == SERF_HTTP_11) {
+        ctx->body = serf_bucket_chunk_create(ctx->body,
+                                                outgoing_response->allocator);
+        serf_bucket_headers_set(ctx->headers, "Transfer-Encoding",
+                                "chunked");
+    }
+
+    /* ELSE:
+            HTTP/2 works with streams, so OK
+            FCGI works with streams, so OK
+    */
+}
+
+
+
+static void serialize_outgoing_response(serf_bucket_t *bucket)
+{
+    outgoing_response_t *ctx = bucket->data;
+    const char *status_line;
+    apr_size_t status_line_len;
+
+    serf_bucket_aggregate_become(bucket);
+    serf_bucket_set_config(bucket, ctx->config);
+
+    {
+        struct iovec status_vecs[9];
+        char http_high[10];
+        char http_low[10];
+        char status[10];
+        itoa(SERF_HTTP_VERSION_MAJOR(ctx->http_version), http_high, 10);
+        itoa(SERF_HTTP_VERSION_MINOR(ctx->http_version), http_low, 10);
+        itoa(ctx->status, status, 10);
+
+        status_vecs[0].iov_base = "HTTP/";
+        status_vecs[0].iov_len = 5;
+        status_vecs[1].iov_base = http_high;
+        status_vecs[1].iov_len = strlen(http_high);
+        status_vecs[2].iov_base = ".";
+        status_vecs[2].iov_len = 1;
+        status_vecs[3].iov_base = http_low;
+        status_vecs[3].iov_len = strlen(http_low);
+        status_vecs[4].iov_base = " ";
+        status_vecs[4].iov_len = 1;
+        status_vecs[5].iov_base = status;
+        status_vecs[5].iov_len = strlen(status);
+        status_vecs[6].iov_base = " ";
+        status_vecs[6].iov_len = 1;
+        status_vecs[7].iov_base = ctx->reason;
+        status_vecs[7].iov_len = strlen(ctx->reason);
+        status_vecs[8].iov_base = "\r\n";
+        status_vecs[8].iov_len = 2;
+
+        status_line = serf_bstrcatv(bucket->allocator,
+                                    status_vecs, COUNT_OF(status_vecs),
+                                    &status_line_len);
+    }
+
+    serf_bucket_aggregate_append(
+        bucket,
+        serf_bucket_simple_own_create(status_line, status_line_len,
+                                      bucket->allocator));
+    serf_bucket_aggregate_append(bucket, ctx->headers);
+
+    if (ctx->body)
+        serf_bucket_aggregate_append(bucket, ctx->body);
+
+    if (ctx->reason)
+        serf_bucket_mem_free(bucket->allocator, ctx->reason);
+
+    serf_bucket_mem_free(bucket->allocator, ctx);
+}
+
+static apr_status_t serf_outgoing_resp_read(serf_bucket_t *bucket,
+                                            apr_size_t requested,
+                                            const char **data,
+                                            apr_size_t *len)
+{
+    serialize_outgoing_response(bucket);
+
+    return bucket->type->read(bucket, requested, data, len);
+}
+
+static apr_status_t serf_outgoing_resp_read_iovec(serf_bucket_t *bucket,
+                                                  apr_size_t requested,
+                                                  int vecs_size,
+                                                  struct iovec *vecs,
+                                                  int *vecs_used)
+{
+    serialize_outgoing_response(bucket);
+
+    return bucket->type->read_iovec(bucket, requested, vecs_size,
+                                    vecs, vecs_used);
+}
+
+static apr_status_t serf_outgoing_resp_readline(serf_bucket_t *bucket,
+                                                int acceptable,
+                                                int *found,
+                                                const char **data,
+                                                apr_size_t *len)
+{
+    serialize_outgoing_response(bucket);
+
+    return bucket->type->readline(bucket, acceptable, found, data, len);
+}
+
+static apr_status_t serf_outgoing_resp_peek(serf_bucket_t *bucket,
+                                            const char **data,
+                                            apr_size_t *len)
+{
+    serialize_outgoing_response(bucket);
+
+    return bucket->type->peek(bucket, data, len);
+}
+
+static apr_status_t serf_outgoing_resp_set_config(serf_bucket_t *bucket,
+                                                  serf_config_t *config)
+{
+    outgoing_response_t *ctx = bucket->data;
+    ctx->config = config;
+
+    if (ctx->body)
+        serf_bucket_set_config(ctx->body, config);
+
+    return serf_bucket_set_config(ctx->headers, config);
+}
+
+static void serf_outgoing_resp_destroy(serf_bucket_t *bucket)
+{
+    outgoing_response_t *ctx = bucket->data;
+
+    serf_bucket_destroy(ctx->headers);
+    if (ctx->body)
+        serf_bucket_destroy(ctx->body);
+
+    if (ctx->reason)
+        serf_bucket_mem_free(bucket->allocator, ctx->reason);
+
+    serf_default_destroy_and_data(bucket);
+}
+
+
+const serf_bucket_type_t serf_bucket_type_outgoing_response = {
+    "OUTGOING-RESPONSE",
+    serf_outgoing_resp_read,
+    serf_outgoing_resp_readline,
+    serf_outgoing_resp_read_iovec,
+    serf_default_read_for_sendfile,
+    serf_buckets_are_v2,
+    serf_outgoing_resp_peek,
+    serf_outgoing_resp_destroy,
+    serf_default_read_bucket,
+    serf_default_get_remaining,
+    serf_outgoing_resp_set_config,
+};

Modified: serf/trunk/incoming.c
URL: 
http://svn.apache.org/viewvc/serf/trunk/incoming.c?rev=1717218&r1=1717217&r2=1717218&view=diff
==============================================================================
--- serf/trunk/incoming.c (original)
+++ serf/trunk/incoming.c Mon Nov 30 09:38:34 2015
@@ -100,6 +100,9 @@ static apr_status_t http1_enqueue_repons
                                           void *enqueue_baton,
                                           serf_bucket_t *bucket)
 {
+    if (SERF_BUCKET_IS_OUTGOING_RESPONSE(bucket))
+        serf_bucket_outgoing_response_prepare(bucket, SERF_HTTP_11, TRUE);
+
     serf_bucket_aggregate_append(request->incoming->pump.ostream_tail,
                                  serf__bucket_event_create(bucket,
                                                            request,

Modified: serf/trunk/serf_bucket_types.h
URL: 
http://svn.apache.org/viewvc/serf/trunk/serf_bucket_types.h?rev=1717218&r1=1717217&r2=1717218&view=diff
==============================================================================
--- serf/trunk/serf_bucket_types.h (original)
+++ serf/trunk/serf_bucket_types.h Mon Nov 30 09:38:34 2015
@@ -118,6 +118,8 @@ serf_bucket_t *serf_bucket_response_crea
 #define SERF_HTTP_VERSION(major, minor)  ((major) * 1000 + (minor))
 #define SERF_HTTP_11 SERF_HTTP_VERSION(1, 1)
 #define SERF_HTTP_10 SERF_HTTP_VERSION(1, 0)
+/** @since New in 1.4. */
+#define SERF_HTTP_20 SERF_HTTP_VERSION(2, 0)
 #define SERF_HTTP_VERSION_MAJOR(shv) ((int)shv / 1000)
 #define SERF_HTTP_VERSION_MINOR(shv) ((int)shv % 1000)
 
@@ -199,6 +201,31 @@ serf_bucket_t *serf_bucket_response_body
     serf_bucket_alloc_t *allocator);
 
 /* ==================================================================== */
+/** @since New in 1.4 */
+extern const serf_bucket_type_t serf_bucket_type_outgoing_response;
+/** @since New in 1.4 */
+#define SERF_BUCKET_IS_OUTGOING_RESPONSE(b)         \
+                SERF_BUCKET_CHECK((b), outgoing_response)
+
+/** @since New in 1.4 */
+serf_bucket_t *serf_bucket_outgoing_response_create(
+    serf_bucket_t *body,
+    int status,
+    const char *reason,
+    int http_version,
+    serf_bucket_alloc_t *allocator);
+
+/** @since New in 1.4 */
+serf_bucket_t *serf_bucket_outgoing_response_get_headers(
+    serf_bucket_t *outgoing_response);
+
+/** @since New in 1.4 */
+void serf_bucket_outgoing_response_prepare(
+    serf_bucket_t *outgoing_response,
+    int http_version,
+    int allow_chunking);
+
+/* ==================================================================== */
 
 extern const serf_bucket_type_t serf_bucket_type_bwtp_frame;
 #define SERF_BUCKET_IS_BWTP_FRAME(b) SERF_BUCKET_CHECK((b), bwtp_frame)

Modified: serf/trunk/test/test_server.c
URL: 
http://svn.apache.org/viewvc/serf/trunk/test/test_server.c?rev=1717218&r1=1717217&r2=1717218&view=diff
==============================================================================
--- serf/trunk/test/test_server.c (original)
+++ serf/trunk/test/test_server.c Mon Nov 30 09:38:34 2015
@@ -73,27 +73,34 @@ static apr_status_t client_generate_resp
                                              apr_pool_t *pool)
 {
     test_baton_t *tb = setup_baton;
-    serf_bucket_t *tmp;
+    serf_bucket_t *resp;
+    serf_bucket_t *body;
+    serf_bucket_t *headers;
 #define CRLF "\r\n"
 
     if (tb->user_baton_l == 401) {
         tb->user_baton_l = 0;
 
-        tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 401 Unauth" CRLF
-                    "WWW-Authenticate: Basic realm=\"Test Suite\"" CRLF
-                                        "Content-Length: 4" CRLF
-                                        CRLF
-                                        "OK" CRLF,
-                                        allocator);
+        body = SERF_BUCKET_SIMPLE_STRING("NOT HERE" CRLF, allocator);
+
+        resp = serf_bucket_outgoing_response_create(body, 401, "Unauth",
+                                                    SERF_HTTP_11, allocator);
+
+        headers = serf_bucket_outgoing_response_get_headers(resp);
+
+        serf_bucket_headers_set(headers, "WWW-Authenticate",
+                                "Basic realm=\"Test Suite\"");
+    }
+    else {
+        body = SERF_BUCKET_SIMPLE_STRING("OK" CRLF, allocator);
+
+        resp = serf_bucket_outgoing_response_create(body, 200, "OK",
+                                                    SERF_HTTP_11, allocator);
+
+        headers = serf_bucket_outgoing_response_get_headers(resp);
     }
-    else
-        tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF
-                                        "Content-Length: 4" CRLF
-                                        CRLF
-                                        "OK" CRLF,
-                                        allocator);
 
-    *resp_bkt = tmp;
+    *resp_bkt = resp;
     return APR_SUCCESS;
 }
 


Reply via email to