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;
}