Author: rhuijben
Date: Mon Nov 16 10:47:39 2015
New Revision: 1714546
URL: http://svn.apache.org/viewvc?rev=1714546&view=rev
Log:
Following up on r1714449, extend the incoming client support to basically
support incoming http/1 requests. This makes the test added in the previous
revision work.
* deprecated.c
New file, providing backwards compatibility for the severyly limited
serf_incoming_create() function.
* incoming.c
(client_connected): Setup protocol detection.
(destroy_request): New function.
(response_finished): New function.
(serf_incoming_response_create): New public function.
(perform_peek_protocol): New function.
(read_from_client): Add simple http/1 style implementation.
(socket_writev,
no_more_writes,
serf__client_flush): New functions. Based on their outgoing variants.
(write_to_client): Implement writing via the previous 3 new functions.
(serf__process_listener): Set socket to not blocking.
(serf_incoming_create2): Tweak arguments. Initialize some values. Store
client in context to allow pollset updating.
(ic_setup_baton_t,
dummy_closed,
serf_incoming_create): Move to deprecated.c.
(serf__incoming_update_pollset): Tweak comment.
* serf.h
(serf_incoming_request_handler_t,
serf_incoming_response_setup_t,
serf_incoming_request_setup_t): New typedefs.
(serf_incoming_request_cb_t): Deprecate.
(serf_incoming_create): Tweak comment. Deprecate.
(serf_incoming_create2): Update argument type.
(serf_incoming_response_create): New function.
* serf_private.h
(serf_incoming_request_t): Define struct that had no definition before.
(serf_incoming_t): Extend.
* test/test_server.c
(client_request_handler,
client_generate_response): New functions.
(client_request_acceptor): Tweak arguments. Setup new handlers.
(run_client_server_loop): Remove obsolete comment.
(test_listen_http): Expect requests to actually work.
Added:
serf/trunk/deprecated.c (with props)
Modified:
serf/trunk/incoming.c
serf/trunk/serf.h
serf/trunk/serf_private.h
serf/trunk/test/test_server.c
Added: serf/trunk/deprecated.c
URL: http://svn.apache.org/viewvc/serf/trunk/deprecated.c?rev=1714546&view=auto
==============================================================================
--- serf/trunk/deprecated.c (added)
+++ serf/trunk/deprecated.c Mon Nov 16 10:47:39 2015
@@ -0,0 +1,146 @@
+/* ====================================================================
+ * 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>
+
+#include "serf.h"
+#include "serf_bucket_util.h"
+#include "serf_private.h"
+
+
+/* From incoming.c */
+typedef struct ic_setup_baton_t
+{
+ serf_incoming_t *incoming;
+ serf_incoming_request_cb_t request;
+ serf_context_t *ctx;
+ void *request_baton;
+} ic_setup_baton_t;
+
+static apr_status_t dummy_setup(apr_socket_t *skt,
+ serf_bucket_t **read_bkt,
+ serf_bucket_t **write_bkt,
+ void *setup_baton,
+ apr_pool_t *pool)
+{
+ ic_setup_baton_t *isb = setup_baton;
+
+ *read_bkt = serf_bucket_socket_create(skt, isb->incoming->allocator);
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t dummy_closed(serf_incoming_t *incoming,
+ void *closed_baton,
+ apr_status_t why,
+ apr_pool_t *pool)
+{
+ return APR_SUCCESS;
+}
+
+static apr_status_t drain_handler(serf_incoming_request_t *req,
+ serf_bucket_t *request,
+ void *baton,
+ apr_pool_t *pool)
+{
+ apr_status_t status;
+
+ do {
+ const char *data;
+ apr_size_t len;
+
+ status = serf_bucket_read(request, SERF_READ_ALL_AVAIL, &data, &len);
+ } while (status == APR_SUCCESS);
+
+ return status;
+}
+
+static apr_status_t response_setup(serf_bucket_t **resp_bkt,
+ serf_incoming_request_t *req,
+ void *setup_baton,
+ serf_bucket_alloc_t *allocator,
+ apr_pool_t *pool)
+{
+#define CRLF "\r\n"
+ *resp_bkt = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 Discarded" CRLF
+ "Content-Length: 25" CRLF
+ "Content-Type: text/plain" CRLF
+ CRLF
+ "Successfully Discarded." CRLF,
+ allocator);
+ return APR_SUCCESS;
+}
+
+static apr_status_t wrap_request(serf_bucket_t **req_bkt,
+ serf_bucket_t *stream,
+ serf_incoming_request_t *req,
+ void *request_baton,
+ serf_incoming_request_handler_t *handler,
+ void **handler_baton,
+ serf_incoming_response_setup_t *setup,
+ void **setup_baton,
+ apr_pool_t *pool)
+{
+ ic_setup_baton_t *isb = request_baton;
+ apr_status_t status;
+
+ status = isb->request(isb->ctx, req, isb->request_baton, pool);
+
+ if (!status) {
+ *req_bkt = serf_bucket_incoming_request_create(stream,
+ stream->allocator);
+
+ *handler = drain_handler;
+ *handler_baton = isb;
+
+ *setup = response_setup;
+ *setup_baton = isb;
+ }
+
+ return status;
+}
+
+apr_status_t serf_incoming_create(
+ serf_incoming_t **client,
+ serf_context_t *ctx,
+ apr_socket_t *insock,
+ void *request_baton,
+ serf_incoming_request_cb_t request,
+ apr_pool_t *pool)
+{
+ ic_setup_baton_t *isb;
+ apr_status_t status;
+
+ isb = apr_pcalloc(pool, sizeof(*isb));
+
+ isb->ctx = ctx;
+ isb->request = request;
+ isb->request_baton = request_baton;
+
+ status = serf_incoming_create2(client, ctx, insock,
+ dummy_setup, isb,
+ dummy_closed, isb,
+ wrap_request, isb, pool);
+
+ if (!status)
+ isb->incoming = *client;
+
+ return status;
+}
Propchange: serf/trunk/deprecated.c
------------------------------------------------------------------------------
svn:eol-style = native
Modified: serf/trunk/incoming.c
URL:
http://svn.apache.org/viewvc/serf/trunk/incoming.c?rev=1714546&r1=1714545&r2=1714546&view=diff
==============================================================================
--- serf/trunk/incoming.c (original)
+++ serf/trunk/incoming.c Mon Nov 16 10:47:39 2015
@@ -86,17 +86,396 @@ static apr_status_t client_connected(ser
serf_bucket_aggregate_append(client->ostream_head,
ostream);
+ client->proto_peek_bkt = serf_bucket_aggregate_create(client->allocator);
+
+ serf_bucket_aggregate_append(
+ client->proto_peek_bkt,
+ serf_bucket_barrier_create(client->stream,
+ client->allocator));
+
+ return status;
+}
+
+/* Destroy an incoming request and its resources */
+static apr_status_t destroy_request(serf_incoming_request_t *request)
+{
+ serf_incoming_t *incoming = request->incoming;
+ apr_pool_destroy(request->pool);
+
+ serf_bucket_mem_free(incoming->allocator, request);
+
+ return APR_SUCCESS;
+}
+
+/* Called when the response is completely written and the write bucket
+ is destroyed. Most likely the request is now 100% done */
+static apr_status_t response_finished(void *baton,
+ apr_uint64_t bytes_written)
+{
+ serf_incoming_request_t *request = baton;
+ apr_status_t status = APR_SUCCESS;
+
+ request->response_finished = true;
+
+ if (request->request_read && request->response_finished) {
+ status = destroy_request(request);
+ }
+ return status;
+}
+
+apr_status_t serf_incoming_response_create(serf_incoming_request_t *request)
+{
+ apr_status_t status;
+ serf_bucket_alloc_t *alloc;
+ serf_bucket_t *bucket;
+
+ if (request->response_written)
+ return APR_SUCCESS;
+
+ alloc = request->incoming->allocator;
+
+ status = request->response_setup(&bucket, request,
+ request->response_setup_baton,
+ alloc, request->pool);
+
+ if (status)
+ return status;
+
+ request->response_written = true;
+
+ /* ### Needs work for other protocols */
+ serf_bucket_aggregate_append(request->incoming->ostream_tail,
+ serf__bucket_event_create(bucket,
+ request,
+ NULL,
+ NULL,
+ response_finished,
+ alloc));
+
+ /* Want write event */
+ request->incoming->dirty_conn = true;
+ request->incoming->ctx->dirty_pollset = true;
+
+ return APR_SUCCESS;
+}
+
+apr_status_t perform_peek_protocol(serf_incoming_t *client)
+{
+ const char h2prefix[] = "PRI * HTTP/2.0\r\n";
+ const apr_size_t h2prefixlen = 16;
+ const char *data;
+ apr_size_t len;
+
+ struct peek_data_t
+ {
+ char buffer[sizeof(h2prefix)];
+ int read;
+ } *peek_data = client->protocol_baton;
+
+ apr_status_t status;
+
+ if (!peek_data) {
+
+ status = serf_bucket_peek(client->stream, &data, &len);
+
+ if (len > h2prefixlen)
+ len = h2prefixlen;
+
+ if (len && memcmp(data, h2prefix, len) != 0) {
+ /* This is not HTTP/2 */
+
+ /* Easy out */
+ serf_bucket_destroy(client->proto_peek_bkt);
+ client->proto_peek_bkt = NULL;
+
+ return APR_SUCCESS;
+ }
+ else if (len == h2prefixlen) {
+ /* We have HTTP/2 */
+ client->framing = SERF_CONNECTION_FRAMING_TYPE_HTTP2;
+
+ serf_bucket_destroy(client->proto_peek_bkt);
+ client->proto_peek_bkt = NULL;
+
+ return APR_SUCCESS;
+ }
+
+ peek_data = serf_bucket_mem_calloc(client->allocator,
+ sizeof(*peek_data));
+ client->protocol_baton = peek_data;
+ }
+
+ do {
+ status = serf_bucket_read(client->stream,
+ h2prefixlen - peek_data->read,
+ &data, &len);
+
+ if (SERF_BUCKET_READ_ERROR(status))
+ return status;
+
+ memcpy(peek_data->buffer + peek_data->read, data, len);
+ peek_data->read += len;
+
+ if (len && memcmp(data, h2prefix, len)) {
+ /* This is not HTTP/2 */
+
+ /* Put data ahead of other data and do the usual thing */
+ serf_bucket_aggregate_prepend(client->proto_peek_bkt,
+ serf_bucket_simple_own_create(
+ peek_data->buffer,
+ peek_data->read,
+ client->allocator));
+
+ return APR_SUCCESS;
+ }
+ else if (len == h2prefixlen) {
+ /* We have HTTP/2 */
+ client->framing = SERF_CONNECTION_FRAMING_TYPE_HTTP2;
+
+ /* Put data ahead of other data and do the usual thing */
+ serf_bucket_aggregate_prepend(client->proto_peek_bkt,
+ serf_bucket_simple_own_create(
+ peek_data->buffer,
+ peek_data->read,
+ client->allocator));
+
+ return APR_SUCCESS;
+ }
+ } while (status == APR_SUCCESS);
+
return status;
}
static apr_status_t read_from_client(serf_incoming_t *client)
{
- return APR_ENOTIMPL;
+ apr_status_t status;
+ serf_incoming_request_t *rq;
+
+ if (client->proto_peek_bkt)
+ {
+ status = perform_peek_protocol(client);
+ if (status)
+ return status;
+ }
+
+ do {
+ rq = client->current_request;
+ if (!rq) {
+
+ serf_bucket_t *read_bkt;
+ rq = serf_bucket_mem_calloc(client->allocator, sizeof(*rq));
+
+ apr_pool_create(&rq->pool, client->pool);
+ rq->incoming = client;
+
+ if (client->proto_peek_bkt) {
+ read_bkt = client->proto_peek_bkt;
+ client->proto_peek_bkt = NULL;
+ }
+ else
+ read_bkt = serf_bucket_barrier_create(client->stream,
+ client->allocator);
+
+ status = client->req_setup(&rq->req_bkt, read_bkt, rq,
+ client->req_setup_baton,
+ &rq->handler, &rq->handler_baton,
+ &rq->response_setup,
+ &rq->response_setup_baton,
+ rq->pool);
+
+ if (status) {
+ apr_pool_destroy(rq->pool);
+ serf_bucket_mem_free(client->allocator, rq);
+ return status;
+ }
+ }
+
+ /* Run handler once or multiple times until status? */
+ status = rq->handler(rq, rq->req_bkt, rq->handler_baton, rq->pool);
+ if (SERF_BUCKET_READ_ERROR(status))
+ return status;
+
+ if (APR_STATUS_IS_EOF(status)) {
+ /* Write response if this hasn't been done yet */
+ status = serf_incoming_response_create(rq);
+
+ if (SERF_BUCKET_READ_ERROR(status))
+ return status;
+
+ rq->request_read = true;
+ client->current_request = NULL;
+
+ if (rq->request_read && rq->response_finished) {
+ status = destroy_request(rq);
+ }
+
+ /* ### TODO: Check if connection is also at EOF? */
+ status = APR_SUCCESS;
+ }
+ }
+ while (status == APR_SUCCESS);
+
+ return status;
+}
+
+static apr_status_t socket_writev(serf_incoming_t *client)
+{
+ apr_size_t written;
+ apr_status_t status;
+
+ status = apr_socket_sendv(client->skt, client->vec,
+ client->vec_len, &written);
+ if (status && !APR_STATUS_IS_EAGAIN(status))
+ serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, client->config,
+ "socket_sendv error %d\n", status);
+
+ /* did we write everything? */
+ if (written) {
+ apr_size_t len = 0;
+ int i;
+
+ serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, client->config,
+ "--- socket_sendv: %d bytes. --\n", written);
+
+ for (i = 0; i < client->vec_len; i++) {
+ len += client->vec[i].iov_len;
+ if (written < len) {
+ serf__log_nopref(LOGLVL_DEBUG, LOGCOMP_RAWMSG, client->config,
+ "%.*s", client->vec[i].iov_len - (len -
written),
+ client->vec[i].iov_base);
+ if (i) {
+ memmove(client->vec, &client->vec[i],
+ sizeof(struct iovec) * (client->vec_len - i));
+ client->vec_len -= i;
+ }
+ client->vec[0].iov_base = (char *)client->vec[0].iov_base +
(client->vec[0].iov_len - (len - written));
+ client->vec[0].iov_len = len - written;
+ break;
+ } else {
+ serf__log_nopref(LOGLVL_DEBUG, LOGCOMP_RAWMSG, client->config,
+ "%.*s",
+ client->vec[i].iov_len,
client->vec[i].iov_base);
+ }
+ }
+ if (len == written) {
+ client->vec_len = 0;
+ }
+ serf__log_nopref(LOGLVL_DEBUG, LOGCOMP_RAWMSG, client->config, "\n");
+
+ /* Log progress information */
+ serf__context_progress_delta(client->ctx, 0, written);
+ }
+
+ return status;
+}
+
+static apr_status_t no_more_writes(serf_incoming_t *client)
+{
+ /* Note that we should hold new requests until we open our new socket. */
+ serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, client->config,
+ "stop writing on client 0x%x\n", client);
+
+ /* Clear our iovec. */
+ client->vec_len = 0;
+
+ /* Update the pollset to know we don't want to write on this socket any
+ * more.
+ */
+ client->dirty_conn = true;
+ client->ctx->dirty_pollset = true;
+ return APR_SUCCESS;
+}
+
+static apr_status_t serf__client_flush(serf_incoming_t *client,
+ bool pump)
+{
+ apr_status_t status = APR_SUCCESS;
+ apr_status_t read_status = APR_SUCCESS;
+ serf_bucket_t *ostreamh = client->ostream_head;
+
+ client->hit_eof = FALSE;
+
+ while (status == APR_SUCCESS) {
+
+ /* First try to write out what is already stored in the
+ connection vecs. */
+ while (client->vec_len && !status) {
+ status = socket_writev(client);
+
+ /* If the write would have blocked, then we're done.
+ * Don't try to write anything else to the socket.
+ */
+ if (APR_STATUS_IS_EPIPE(status)
+ || APR_STATUS_IS_ECONNRESET(status)
+ || APR_STATUS_IS_ECONNABORTED(status))
+ return no_more_writes(client);
+ }
+
+ if (status || !pump)
+ return status;
+ else if (read_status || client->vec_len || client->hit_eof)
+ return read_status;
+
+ /* ### optimize at some point by using read_for_sendfile */
+ /* TODO: now that read_iovec will effectively try to return as much
+ data as available, we probably don't want to read ALL_AVAIL, but
+ a lower number, like the size of one or a few TCP packets, the
+ available TCP buffer size ... */
+ client->hit_eof = 0;
+ read_status = serf_bucket_read_iovec(ostreamh,
+ SERF_READ_ALL_AVAIL,
+ IOV_MAX,
+ client->vec,
+ &client->vec_len);
+
+ if (read_status == SERF_ERROR_WAIT_CONN) {
+ /* The bucket told us that it can't provide more data until
+ more data is read from the socket. This normally happens
+ during a SSL handshake.
+
+ We should avoid looking for writability for a while so
+ that (hopefully) something will appear in the bucket so
+ we can actually write something. otherwise, we could
+ end up in a CPU spin: socket wants something, but we
+ don't have anything (and keep returning EAGAIN) */
+ client->stop_writing = true;
+ client->dirty_conn = true;
+ client->ctx->dirty_pollset = true;
+
+ read_status = APR_EAGAIN;
+ }
+ else if (APR_STATUS_IS_EAGAIN(read_status)) {
+
+ /* We read some stuff, but did we read everything ? */
+ if (client->hit_eof)
+ read_status = APR_SUCCESS;
+ }
+ else if (SERF_BUCKET_READ_ERROR(read_status)) {
+
+ /* Something bad happened. Propagate any errors. */
+ return read_status;
+ }
+ }
+
+ return status;
}
static apr_status_t write_to_client(serf_incoming_t *client)
{
- return APR_ENOTIMPL;
+ apr_status_t status;
+
+ status = serf__client_flush(client, true);
+
+ if (APR_STATUS_IS_EAGAIN(status))
+ return APR_SUCCESS;
+ else if (status)
+ return status;
+
+ /* Probably nothing to write. Connection will check new requests */
+ client->dirty_conn = 1;
+ client->ctx->dirty_pollset = 1;
+
+ return APR_SUCCESS;
}
apr_status_t serf__process_client(serf_incoming_t *client, apr_int16_t events)
@@ -153,6 +532,15 @@ apr_status_t serf__process_listener(serf
return status;
}
+ /* Set the socket to be non-blocking */
+ if ((status = apr_socket_timeout_set(client->skt, 0)) != APR_SUCCESS)
+ return status;
+
+ /* Disable Nagle's algorithm */
+ if ((status = apr_socket_opt_set(client->skt,
+ APR_TCP_NODELAY, 1)) != APR_SUCCESS)
+ return status;
+
status = l->accept_func(l->ctx, l, l->accept_baton, in, p);
if (status) {
@@ -180,8 +568,8 @@ apr_status_t serf_incoming_create2(
void *setup_baton,
serf_incoming_closed_t closed,
void *closed_baton,
- serf_incoming_request_cb_t request,
- void *request_baton,
+ serf_incoming_request_setup_t req_setup,
+ void *req_setup_baton,
apr_pool_t *pool)
{
apr_status_t rv;
@@ -197,12 +585,13 @@ apr_status_t serf_incoming_create2(
ic->allocator = serf_bucket_allocator_create(ic_pool, NULL, NULL);
ic->baton.type = SERF_IO_CLIENT;
ic->baton.u.client = ic;
- ic->request_baton = request_baton;
- ic->request = request;
+ ic->req_setup = req_setup;
+ ic->req_setup_baton = req_setup_baton;
ic->skt = insock;
ic->dirty_conn = false;
ic->wait_for_connect = true;
+ ic->vec_len = 0;
ic->setup = setup;
ic->setup_baton = setup_baton;
@@ -215,6 +604,9 @@ apr_status_t serf_incoming_create2(
ic->ostream_tail = NULL;
ic->ssltunnel_ostream = NULL;
+ ic->protocol_baton = NULL;
+ ic->current_request = NULL;
+
ic->desc.desc_type = APR_POLL_SOCKET;
ic->desc.desc.s = ic->skt;
ic->desc.reqevents = APR_POLLIN | APR_POLLERR | APR_POLLHUP;
@@ -242,59 +634,9 @@ apr_status_t serf_incoming_create2(
/* Let caller handle the socket */
}
- return rv;
-}
-
-typedef struct ic_setup_baton_t
-{
- serf_incoming_t *incoming;
-} ic_setup_baton_t;
-
-static apr_status_t dummy_setup(apr_socket_t *skt,
- serf_bucket_t **read_bkt,
- serf_bucket_t **write_bkt,
- void *setup_baton,
- apr_pool_t *pool)
-{
- ic_setup_baton_t *isb = setup_baton;
-
- *read_bkt = serf_bucket_socket_create(skt, isb->incoming->allocator);
+ *(serf_incoming_t **)apr_array_push(ctx->incomings) = *client;
- return APR_SUCCESS;
-}
-
-static apr_status_t dummy_closed(serf_incoming_t *incoming,
- void *closed_baton,
- apr_status_t why,
- apr_pool_t *pool)
-{
- return APR_SUCCESS;
-}
-
-apr_status_t serf_incoming_create(
- serf_incoming_t **client,
- serf_context_t *ctx,
- apr_socket_t *insock,
- void *request_baton,
- serf_incoming_request_cb_t request,
- apr_pool_t *pool)
-{
- ic_setup_baton_t *isb;
- apr_status_t status;
-
- /* Allocate baton to hand over created listener
- (to get access to its allocator) */
- isb = apr_pcalloc(pool, sizeof(*isb));
-
- status = serf_incoming_create2(client, ctx, insock,
- dummy_setup, isb,
- dummy_closed, isb,
- request, request_baton, pool);
-
- if (!status)
- isb->incoming = *client;
-
- return status;
+ return rv;
}
apr_status_t serf_listener_create(
@@ -400,7 +742,7 @@ apr_status_t serf__incoming_update_polls
*/
if (incoming->vec_len) {
/* We still have vecs in the connection, which lifetime is
- managed by buckets inside conn->ostream_head.
+ managed by buckets inside client->ostream_head.
Don't touch ostream as that might destroy the vecs */
Modified: serf/trunk/serf.h
URL:
http://svn.apache.org/viewvc/serf/trunk/serf.h?rev=1714546&r1=1714545&r2=1714546&view=diff
==============================================================================
--- serf/trunk/serf.h (original)
+++ serf/trunk/serf.h Mon Nov 16 10:47:39 2015
@@ -515,13 +515,39 @@ apr_status_t serf_listener_create(
serf_accept_client_t accept_func,
apr_pool_t *pool);
-typedef apr_status_t (*serf_incoming_request_cb_t)(
- serf_context_t *ctx,
+typedef apr_status_t (*serf_incoming_request_handler_t)(
+ serf_incoming_request_t *req,
+ serf_bucket_t *request,
+ void *handler_baton,
+ apr_pool_t *pool);
+
+typedef apr_status_t (*serf_incoming_response_setup_t)(
+ serf_bucket_t **resp_bkt,
+ serf_incoming_request_t *req,
+ void *setup_baton,
+ serf_bucket_alloc_t *allocator,
+ apr_pool_t *pool);
+
+typedef apr_status_t (*serf_incoming_request_setup_t)(
+ serf_bucket_t **req_bkt,
+ serf_bucket_t *stream,
serf_incoming_request_t *req,
void *request_baton,
+ serf_incoming_request_handler_t *handler,
+ void **handler_baton,
+ serf_incoming_response_setup_t *response_setup,
+ void *response_setup_baton,
apr_pool_t *pool);
-/* ### Arguments in bad order. Doesn't support SSL */
+/* ### Deprecated: can't do anything with request */
+typedef apr_status_t(*serf_incoming_request_cb_t)(
+ serf_context_t *ctx,
+ serf_incoming_request_t *req,
+ void *request_baton,
+ apr_pool_t *pool);
+
+/* ### Deprecated: Misses ssl support and actual
+ request handling. */
apr_status_t serf_incoming_create(
serf_incoming_t **client,
serf_context_t *ctx,
@@ -538,11 +564,15 @@ apr_status_t serf_incoming_create2(
void *setup_baton,
serf_incoming_closed_t closed,
void *closed_baton,
- serf_incoming_request_cb_t request,
- void *request_baton,
+ serf_incoming_request_setup_t req_setup,
+ void *req_setup_baton,
apr_pool_t *pool);
-
+/* Allows creating a response before the request is completely
+ read. Will call the response create function if it hasn't
+ been called yet. */
+apr_status_t serf_incoming_response_create(
+ serf_incoming_request_t *request);
/**
* Reset the connection, but re-open the socket again.
Modified: serf/trunk/serf_private.h
URL:
http://svn.apache.org/viewvc/serf/trunk/serf_private.h?rev=1714546&r1=1714545&r2=1714546&view=diff
==============================================================================
--- serf/trunk/serf_private.h (original)
+++ serf/trunk/serf_private.h Mon Nov 16 10:47:39 2015
@@ -184,6 +184,25 @@ struct serf_request_t {
struct serf_request_t *next;
};
+struct serf_incoming_request_t
+{
+ serf_incoming_t *incoming;
+ apr_pool_t *pool;
+
+ serf_bucket_t *req_bkt;
+
+ serf_incoming_request_handler_t handler;
+ void *handler_baton;
+
+ serf_incoming_response_setup_t response_setup;
+ void *response_setup_baton;
+
+ bool request_read;
+ bool response_written;
+ bool response_finished;
+ serf_bucket_t *response_bkt;
+};
+
typedef struct serf_pollset_t {
/* the set of connections to poll */
apr_pollset_t *pollset;
@@ -329,8 +348,8 @@ struct serf_listener_t {
struct serf_incoming_t {
serf_context_t *ctx;
serf_io_baton_t baton;
- void *request_baton;
- serf_incoming_request_cb_t request;
+ serf_incoming_request_setup_t req_setup;
+ void *req_setup_baton;
apr_socket_t *skt; /* Lives in parent of POOL */
apr_pool_t *pool;
@@ -349,9 +368,14 @@ struct serf_incoming_t {
serf_incoming_closed_t closed;
void *closed_baton;
+ serf_connection_framing_type_t framing;
+
bool dirty_conn;
bool wait_for_connect;
bool hit_eof;
+ bool stop_writing;
+
+ void *protocol_baton;
/* A bucket wrapped around our socket (for reading responses). */
serf_bucket_t *stream;
@@ -365,6 +389,10 @@ struct serf_incoming_t {
serf_bucket_t *ssltunnel_ostream;
serf_config_t *config;
+
+ serf_bucket_t *proto_peek_bkt;
+
+ serf_incoming_request_t *current_request; /* For HTTP/1 */
};
/* States for the different stages in the lifecyle of a connection. */
Modified: serf/trunk/test/test_server.c
URL:
http://svn.apache.org/viewvc/serf/trunk/test/test_server.c?rev=1714546&r1=1714545&r2=1714546&view=diff
==============================================================================
--- serf/trunk/test/test_server.c (original)
+++ serf/trunk/test/test_server.c Mon Nov 16 10:47:39 2015
@@ -49,12 +49,63 @@ static apr_status_t client_closed(serf_i
return APR_ENOTIMPL;
}
-static apr_status_t client_request_acceptor(serf_context_t *ctx,
+static apr_status_t client_request_handler(serf_incoming_request_t *req,
+ serf_bucket_t *request,
+ void *handler_baton,
+ apr_pool_t *pool)
+{
+ const char *data;
+ apr_size_t len;
+ apr_status_t status;
+
+ do
+ {
+ status = serf_bucket_read(request, SERF_READ_ALL_AVAIL, &data, &len);
+ }
+ while (status == APR_SUCCESS);
+
+ return status;
+}
+
+static apr_status_t client_generate_response(serf_bucket_t **resp_bkt,
+ serf_incoming_request_t *req,
+ void *setup_baton,
+ serf_bucket_alloc_t *allocator,
+ apr_pool_t *pool)
+{
+ serf_bucket_t *tmp;
+#define CRLF "\r\n"
+
+ tmp = SERF_BUCKET_SIMPLE_STRING("HTTP/1.1 200 OK" CRLF
+ "Content-Length: 4" CRLF
+ CRLF
+ "OK" CRLF,
+ allocator);
+
+ *resp_bkt = tmp;
+ return APR_SUCCESS;
+}
+
+static apr_status_t client_request_acceptor(serf_bucket_t **req_bkt,
+ serf_bucket_t *stream,
serf_incoming_request_t *req,
void *request_baton,
+ serf_incoming_request_handler_t
*handler,
+ void **handler_baton,
+ serf_incoming_response_setup_t
*response,
+ void **response_baton,
apr_pool_t *pool)
{
- return APR_ENOTIMPL;
+ test_baton_t *tb = request_baton;
+ *req_bkt = serf_bucket_incoming_request_create(stream, stream->allocator);
+
+ *handler = client_request_handler;
+ *handler_baton = tb;
+
+ *response = client_generate_response;
+ *response_baton = tb;
+
+ return APR_SUCCESS;
}
static apr_status_t client_acceptor(serf_context_t *ctx,
@@ -114,11 +165,6 @@ run_client_server_loop(test_baton_t *tb,
{
apr_pool_clear(iter_pool);
-
- /* Even if the mock server returned an error, it may have written
- something to the client. So process that data first, handle the error
- later. */
-
/* run client event loop */
status = serf_context_run(tb->context, 0, iter_pool);
if (!APR_STATUS_IS_TIMEUP(status) &&
@@ -154,7 +200,7 @@ void test_listen_http(CuTest *tc)
status = run_client_server_loop(tb, num_requests,
handler_ctx, tb->pool);
- CuAssertIntEquals(tc, APR_ENOTIMPL, status);
+ CuAssertIntEquals(tc, APR_SUCCESS, status);
}