Author: rhuijben
Date: Sun Nov 15 12:52:43 2015
New Revision: 1714449
URL: http://svn.apache.org/viewvc?rev=1714449&view=rev
Log:
Start replacing a whole lot of return APR_EONOTIMPL in the serf listening
and server side code with actual connection setup code, to prepare some
actual http2 testing in our testsuite.
With http/2 the server and client side can re-use a lot of code and I don't
feel like writing a dedicated test server that will need its own testing.
I will try to prepare the server for allowing http/1 too, but that is not
a priority to me right now.
* context.c
(check_dirty_pollsets): Update incoming connections like we do outgoing.
(serf_context_create_ex): Init new list.
* incoming.c
(client_detect_eof): New function.
(client_connected): New function.
(serf__process_client): Call client_connected at initial connect.
(serf__process_listener): Properly handle EINPROGRESS.
(incoming_cleanup): New function.
(serf_incoming_create): Rename to...
(serf_incoming_create2): ... this and add assential arguments to allow
supporting things like https, etc.
(serf_incoming_create): New function over serf_incoming_create2.
(serf_listener_create): Handle error scenarios. Remove setting
APR_SO_REUSEADDR (see r1711233 for reasons).
(serf__incoming_update_pollset): New function.
* serf.h
(serf_incoming_closed_t): New typedef.
(serf_incoming_create): Add ### comment.
(serf_incoming_create2): New function.
* serf_private.h
(serf_context_t): Use bool for dirty pollset. Add incomings array.
(serf_incoming_t): Add many variables, similar to their outgoing counterparts.
(serf_connection_t): Use bool for dirty_conn.
(serf__incoming_update_pollset): New function.
* test/test_all.c
(testlist): Declare new testfile.
* test/test_serf.h
(test_server): New function.
* test/test_server.c
New file.
* test/test_util.c
(setup_test_client_context): Don't overwrite config if already set.
Added:
serf/trunk/test/test_server.c (with props)
Modified:
serf/trunk/context.c
serf/trunk/incoming.c
serf/trunk/serf.h
serf/trunk/serf_private.h
serf/trunk/test/test_all.c
serf/trunk/test/test_serf.h
serf/trunk/test/test_util.c
Modified: serf/trunk/context.c
URL:
http://svn.apache.org/viewvc/serf/trunk/context.c?rev=1714449&r1=1714448&r2=1714449&view=diff
==============================================================================
--- serf/trunk/context.c (original)
+++ serf/trunk/context.c Sun Nov 15 12:52:43 2015
@@ -75,6 +75,20 @@ static apr_status_t check_dirty_pollsets
return status;
}
+ for (i = ctx->incomings->nelts; i--; ) {
+ serf_incoming_t *incoming = GET_INCOMING(ctx, i);
+ apr_status_t status;
+
+ if (!incoming->dirty_conn) {
+ continue;
+ }
+
+ incoming->dirty_conn = false;
+
+ if ((status = serf__incoming_update_pollset(incoming)) != APR_SUCCESS)
+ return status;
+ }
+
/* reset our context flag now */
ctx->dirty_pollset = 0;
@@ -168,6 +182,9 @@ serf_context_t *serf_context_create_ex(
/* default to a single connection since that is the typical case */
ctx->conns = apr_array_make(pool, 1, sizeof(serf_connection_t *));
+ /* and we typically have no servers */
+ ctx->incomings = apr_array_make(pool, 0, sizeof(serf_incoming_t *));
+
/* Initialize progress status */
ctx->progress_read = 0;
ctx->progress_written = 0;
Modified: serf/trunk/incoming.c
URL:
http://svn.apache.org/viewvc/serf/trunk/incoming.c?rev=1714449&r1=1714448&r2=1714449&view=diff
==============================================================================
--- serf/trunk/incoming.c (original)
+++ serf/trunk/incoming.c Sun Nov 15 12:52:43 2015
@@ -27,6 +27,68 @@
#include "serf_private.h"
+static apr_status_t client_detect_eof(void *baton,
+ serf_bucket_t *aggregator)
+{
+ serf_incoming_t *client = baton;
+ client->hit_eof = true;
+ return APR_EAGAIN;
+}
+
+static apr_status_t client_connected(serf_incoming_t *client)
+{
+ /* serf_context_t *ctx = client->ctx; */
+ apr_status_t status;
+ serf_bucket_t *ostream;
+
+ /* ### TODO: Store ip address in config for logging */
+
+ serf__log(LOGLVL_DEBUG, LOGCOMP_CONN, __FILE__, client->config,
+ "socket for client 0x%x connected\n", client);
+
+ /* ### Connection does auth setup here */
+
+ if (client->ostream_head == NULL) {
+ client->ostream_head = serf_bucket_aggregate_create(client->allocator);
+ }
+
+ if (client->ostream_tail == NULL) {
+ client->ostream_tail = serf_bucket_aggregate_create(client->allocator);
+
+ serf_bucket_aggregate_hold_open(client->ostream_tail,
+ client_detect_eof, client);
+ }
+
+ ostream = client->ostream_tail;
+
+ status = client->setup(client->skt,
+ &client->stream,
+ &ostream,
+ client->setup_baton, client->pool);
+
+ if (status) {
+ /* extra destroy here since it wasn't added to the head bucket yet. */
+ serf_bucket_destroy(client->ostream_tail);
+ /* ### Cleanup! (serf__connection_pre_cleanup) */
+ return status;
+ }
+
+ /* Share the configuration with all the buckets in the newly created output
+ chain (see PLAIN or ENCRYPTED scenario's), including the request buckets
+ created by the application (ostream_tail will handle this for us). */
+ serf_bucket_set_config(client->ostream_head, client->config);
+
+ /* Share the configuration with the ssl_decrypt and socket buckets. The
+ response buckets wrapping the ssl_decrypt/socket buckets won't get the
+ config automatically because they are upstream. */
+ serf_bucket_set_config(client->stream, client->config);
+
+ serf_bucket_aggregate_append(client->ostream_head,
+ ostream);
+
+ return status;
+}
+
static apr_status_t read_from_client(serf_incoming_t *client)
{
return APR_ENOTIMPL;
@@ -40,6 +102,15 @@ static apr_status_t write_to_client(serf
apr_status_t serf__process_client(serf_incoming_t *client, apr_int16_t events)
{
apr_status_t rv;
+
+ if (client->wait_for_connect && (events & (APR_POLLIN | APR_POLLOUT))) {
+ rv = client_connected(client);
+ client->wait_for_connect = FALSE;
+ if (rv) {
+ return rv;
+ }
+ }
+
if ((events & APR_POLLIN) != 0) {
rv = read_from_client(client);
if (rv) {
@@ -67,58 +138,163 @@ apr_status_t serf__process_client(serf_i
apr_status_t serf__process_listener(serf_listener_t *l)
{
- apr_status_t rv;
+ apr_status_t status;
apr_socket_t *in;
apr_pool_t *p;
/* THIS IS NOT OPTIMAL */
apr_pool_create(&p, l->pool);
- rv = apr_socket_accept(&in, l->skt, p);
+ status = apr_socket_accept(&in, l->skt, p);
+
+ if (status != APR_SUCCESS
+ && !APR_STATUS_IS_EINPROGRESS(status)) {
- if (rv) {
apr_pool_destroy(p);
- return rv;
+ return status;
}
- rv = l->accept_func(l->ctx, l, l->accept_baton, in, p);
+ status = l->accept_func(l->ctx, l, l->accept_baton, in, p);
- if (rv) {
+ if (status) {
apr_pool_destroy(p);
- return rv;
}
- return rv;
+ return status;
}
+static apr_status_t incoming_cleanup(void *baton)
+{
+ serf_incoming_t *incoming = baton;
-apr_status_t serf_incoming_create(
+ apr_socket_close(incoming->skt);
+
+
+ return APR_SUCCESS;
+}
+
+apr_status_t serf_incoming_create2(
serf_incoming_t **client,
serf_context_t *ctx,
apr_socket_t *insock,
- void *request_baton,
+ serf_connection_setup_t setup,
+ void *setup_baton,
+ serf_incoming_closed_t closed,
+ void *closed_baton,
serf_incoming_request_cb_t request,
+ void *request_baton,
apr_pool_t *pool)
{
apr_status_t rv;
- serf_incoming_t *ic = apr_palloc(pool, sizeof(*ic));
+ apr_pool_t *ic_pool;
+
+ apr_pool_create(&ic_pool, pool);
+
+ serf_incoming_t *ic = apr_palloc(ic_pool, sizeof(*ic));
ic->ctx = ctx;
+ ic->pool = ic_pool;
+ 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->skt = insock;
+
+ ic->dirty_conn = false;
+ ic->wait_for_connect = true;
+
+ ic->setup = setup;
+ ic->setup_baton = setup_baton;
+ ic->closed = closed;
+ ic->closed_baton = closed_baton;
+
+ /* A bucket wrapped around our socket (for reading responses). */
+ ic->stream = NULL;
+ ic->ostream_head = NULL;
+ ic->ostream_tail = NULL;
+ ic->ssltunnel_ostream = NULL;
+
ic->desc.desc_type = APR_POLL_SOCKET;
ic->desc.desc.s = ic->skt;
- ic->desc.reqevents = APR_POLLIN;
+ ic->desc.reqevents = APR_POLLIN | APR_POLLERR | APR_POLLHUP;
+
+ /* Store the connection specific info in the configuration store */
+ /* ### Doesn't work... Doesn't support listeners yet*/
+ /*rv = serf__config_store_get_config(ctx, ic, &config, pool);
+ if (rv) {
+ apr_pool_destroy(l->pool);
+ return rv;
+ }
+ ic->config = config;*/
+ ic->config = NULL; /* FIX!! */
rv = ctx->pollset_add(ctx->pollset_baton,
&ic->desc, &ic->baton);
- *client = ic;
+
+ if (!rv) {
+ apr_pool_cleanup_register(ic->pool, ic, incoming_cleanup,
+ apr_pool_cleanup_null);
+ *client = ic;
+ }
+ else {
+ apr_pool_destroy(ic_pool);
+ /* 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);
+
+ 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;
+}
apr_status_t serf_listener_create(
serf_listener_t **listener,
@@ -142,8 +318,10 @@ apr_status_t serf_listener_create(
apr_pool_create(&l->pool, pool);
rv = apr_sockaddr_info_get(&sa, host, APR_UNSPEC, port, 0, l->pool);
- if (rv)
+ if (rv) {
+ apr_pool_destroy(l->pool);
return rv;
+ }
rv = apr_socket_create(&l->skt, sa->family,
SOCK_STREAM,
@@ -154,17 +332,17 @@ apr_status_t serf_listener_create(
if (rv)
return rv;
- rv = apr_socket_opt_set(l->skt, APR_SO_REUSEADDR, 1);
- if (rv)
- return rv;
-
rv = apr_socket_bind(l->skt, sa);
- if (rv)
- return rv;
+ if (rv) {
+ apr_pool_destroy(l->pool);
+ return rv;
+ }
rv = apr_socket_listen(l->skt, 5);
- if (rv)
+ if (rv) {
+ apr_pool_destroy(l->pool);
return rv;
+ }
l->desc.desc_type = APR_POLL_SOCKET;
l->desc.desc.s = l->skt;
@@ -172,10 +350,100 @@ apr_status_t serf_listener_create(
rv = ctx->pollset_add(ctx->pollset_baton,
&l->desc, &l->baton);
- if (rv)
+ if (rv) {
+ apr_pool_destroy(l->pool);
return rv;
+ }
*listener = l;
return APR_SUCCESS;
}
+
+apr_status_t serf__incoming_update_pollset(serf_incoming_t *incoming)
+{
+ serf_context_t *ctx = incoming->ctx;
+ apr_status_t status;
+ apr_pollfd_t desc = { 0 };
+ bool data_waiting;
+
+ if (!incoming->skt) {
+ return APR_SUCCESS;
+ }
+
+ /* Remove the socket from the poll set. */
+ desc.desc_type = APR_POLL_SOCKET;
+ desc.desc.s = incoming->skt;
+ desc.reqevents = incoming->reqevents;
+
+ status = ctx->pollset_rm(ctx->pollset_baton,
+ &desc, &incoming->baton);
+ if (status && !APR_STATUS_IS_NOTFOUND(status))
+ return status;
+
+ /* Now put it back in with the correct read/write values. */
+ desc.reqevents = APR_POLLIN | APR_POLLHUP | APR_POLLERR;
+
+ /* If we are not connected yet, we just want to know when we are */
+ if (incoming->wait_for_connect) {
+ data_waiting = true;
+ desc.reqevents |= APR_POLLOUT;
+ }
+ else {
+ /* Directly look at the connection data. While this may look
+ more expensive than the cheap checks later this peek is
+ just checking a bit of ram.
+
+ But it also has the nice side effect of removing references
+ from the aggregate to requests that are done.
+ */
+ if (incoming->vec_len) {
+ /* We still have vecs in the connection, which lifetime is
+ managed by buckets inside conn->ostream_head.
+
+ Don't touch ostream as that might destroy the vecs */
+
+ data_waiting = true;
+ }
+ else {
+ serf_bucket_t *ostream;
+
+ ostream = incoming->ostream_head;
+
+ if (!ostream)
+ ostream = incoming->ssltunnel_ostream;
+
+ if (ostream) {
+ const char *dummy_data;
+ apr_size_t len;
+
+ status = serf_bucket_peek(ostream, &dummy_data, &len);
+
+ if (SERF_BUCKET_READ_ERROR(status) || len > 0) {
+ /* DATA or error waiting */
+ data_waiting = TRUE; /* Error waiting */
+ }
+ else if (! status || APR_STATUS_IS_EOF(status)) {
+ data_waiting = FALSE;
+ }
+ else
+ data_waiting = FALSE; /* EAGAIN / EOF / WAIT_CONN */
+ }
+ else
+ data_waiting = FALSE;
+ }
+
+ if (data_waiting) {
+ desc.reqevents |= APR_POLLOUT;
+ }
+ }
+
+ /* save our reqevents, so we can pass it in to remove later. */
+ incoming->reqevents = desc.reqevents;
+
+ /* Note: even if we don't want to read/write this socket, we still
+ * want to poll it for hangups and errors.
+ */
+ return ctx->pollset_add(ctx->pollset_baton,
+ &desc, &incoming->baton);
+}
Modified: serf/trunk/serf.h
URL:
http://svn.apache.org/viewvc/serf/trunk/serf.h?rev=1714449&r1=1714448&r2=1714449&view=diff
==============================================================================
--- serf/trunk/serf.h (original)
+++ serf/trunk/serf.h Sun Nov 15 12:52:43 2015
@@ -377,6 +377,15 @@ typedef void (*serf_connection_closed_t)
apr_pool_t *pool);
/**
+ * Like serf_connection_closed_t, but applies to incoming connections.
+ */
+typedef void(*serf_incoming_closed_t)(
+ serf_incoming_t *incoming,
+ void *closed_baton,
+ apr_status_t why,
+ apr_pool_t *pool);
+
+/**
* Response data has arrived and should be processed.
*
* Whenever response data for @a request arrives (initially, or continued data
@@ -512,6 +521,7 @@ typedef apr_status_t (*serf_incoming_req
void *request_baton,
apr_pool_t *pool);
+/* ### Arguments in bad order. Doesn't support SSL */
apr_status_t serf_incoming_create(
serf_incoming_t **client,
serf_context_t *ctx,
@@ -520,6 +530,17 @@ apr_status_t serf_incoming_create(
serf_incoming_request_cb_t request,
apr_pool_t *pool);
+apr_status_t serf_incoming_create2(
+ serf_incoming_t **client,
+ serf_context_t *ctx,
+ apr_socket_t *insock,
+ serf_connection_setup_t setup,
+ void *setup_baton,
+ serf_incoming_closed_t closed,
+ void *closed_baton,
+ serf_incoming_request_cb_t request,
+ void *request_baton,
+ apr_pool_t *pool);
Modified: serf/trunk/serf_private.h
URL:
http://svn.apache.org/viewvc/serf/trunk/serf_private.h?rev=1714449&r1=1714448&r2=1714449&view=diff
==============================================================================
--- serf/trunk/serf_private.h (original)
+++ serf/trunk/serf_private.h Sun Nov 15 12:52:43 2015
@@ -279,12 +279,15 @@ struct serf_context_t {
serf_socket_remove_t pollset_rm;
/* one of our connections has a dirty pollset state. */
- int dirty_pollset;
+ bool dirty_pollset;
/* the list of active connections */
apr_array_header_t *conns;
#define GET_CONN(ctx, i) (((serf_connection_t **)(ctx)->conns->elts)[i])
+ apr_array_header_t *incomings;
+#define GET_INCOMING(ctx, i) (((serf_incoming_t **)(ctx)->incomings->elts)[i])
+
/* Proxy server address */
apr_sockaddr_t *proxy_address;
@@ -328,8 +331,40 @@ struct serf_incoming_t {
serf_io_baton_t baton;
void *request_baton;
serf_incoming_request_cb_t request;
- apr_socket_t *skt;
+
+ apr_socket_t *skt; /* Lives in parent of POOL */
+ apr_pool_t *pool;
+ serf_bucket_alloc_t *allocator;
+
apr_pollfd_t desc;
+
+ /* the last reqevents we gave to pollset_add */
+ apr_int16_t reqevents;
+
+ struct iovec vec[IOV_MAX];
+ int vec_len;
+
+ serf_connection_setup_t setup;
+ void *setup_baton;
+ serf_incoming_closed_t closed;
+ void *closed_baton;
+
+ bool dirty_conn;
+ bool wait_for_connect;
+ bool hit_eof;
+
+ /* A bucket wrapped around our socket (for reading responses). */
+ serf_bucket_t *stream;
+ /* A reference to the aggregate bucket that provides the boundary between
+ * request level buckets and connection level buckets.
+ */
+ serf_bucket_t *ostream_head;
+ serf_bucket_t *ostream_tail;
+
+ /* Aggregate bucket used to send the CONNECT request. */
+ serf_bucket_t *ssltunnel_ostream;
+
+ serf_config_t *config;
};
/* States for the different stages in the lifecyle of a connection. */
@@ -362,7 +397,7 @@ struct serf_connection_t {
apr_int16_t seen_in_pollset;
/* are we a dirty connection that needs its poll status updated? */
- int dirty_conn;
+ bool dirty_conn;
/* number of completed requests we've sent */
unsigned int completed_requests;
@@ -565,6 +600,7 @@ void serf__context_progress_delta(void *
/* from incoming.c */
apr_status_t serf__process_client(serf_incoming_t *l, apr_int16_t events);
apr_status_t serf__process_listener(serf_listener_t *l);
+apr_status_t serf__incoming_update_pollset(serf_incoming_t *incoming);
/* from outgoing.c */
apr_status_t serf__open_connections(serf_context_t *ctx);
Modified: serf/trunk/test/test_all.c
URL:
http://svn.apache.org/viewvc/serf/trunk/test/test_all.c?rev=1714449&r1=1714448&r2=1714449&view=diff
==============================================================================
--- serf/trunk/test/test_all.c (original)
+++ serf/trunk/test/test_all.c Sun Nov 15 12:52:43 2015
@@ -29,11 +29,12 @@ static const struct testlist {
const char *testname;
CuSuite *(*func)(void);
} tests[] = {
- {"context", test_context},
- {"buckets", test_buckets},
- {"ssl", test_ssl},
- {"auth", test_auth},
- {"internal", test_internal},
+ {"context", test_context},
+ {"buckets", test_buckets},
+ {"ssl", test_ssl},
+ {"auth", test_auth},
+ {"internal", test_internal},
+ {"server", test_server},
#if 0
/* internal test for the mock bucket. */
{"mock", test_mock_bucket},
Modified: serf/trunk/test/test_serf.h
URL:
http://svn.apache.org/viewvc/serf/trunk/test/test_serf.h?rev=1714449&r1=1714448&r2=1714449&view=diff
==============================================================================
--- serf/trunk/test/test_serf.h (original)
+++ serf/trunk/test/test_serf.h Sun Nov 15 12:52:43 2015
@@ -60,6 +60,7 @@ CuSuite *test_buckets(void);
CuSuite *test_ssl(void);
CuSuite *test_auth(void);
CuSuite *test_internal(void);
+CuSuite *test_server(void);
CuSuite *test_mock_bucket(void);
/* Test setup declarations */
Added: serf/trunk/test/test_server.c
URL:
http://svn.apache.org/viewvc/serf/trunk/test/test_server.c?rev=1714449&view=auto
==============================================================================
--- serf/trunk/test/test_server.c (added)
+++ serf/trunk/test/test_server.c Sun Nov 15 12:52:43 2015
@@ -0,0 +1,171 @@
+/* ====================================================================
+ * 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 <stdlib.h>
+
+#include <apr.h>
+#include <apr_pools.h>
+#include <apr_strings.h>
+#include <apr_version.h>
+
+#include "serf.h"
+
+#include "test_serf.h"
+
+static apr_status_t client_setup(apr_socket_t *skt,
+ serf_bucket_t **read_bkt,
+ serf_bucket_t **write_bkt,
+ void *setup_baton,
+ apr_pool_t *pool)
+{
+ test_baton_t *tb = setup_baton;
+
+ *read_bkt = serf_bucket_socket_create(skt, tb->bkt_alloc);
+ return APR_SUCCESS;
+}
+
+static apr_status_t client_closed(serf_incoming_t *client,
+ void *closed_baton,
+ apr_status_t why,
+ apr_pool_t *pool)
+{
+ return APR_ENOTIMPL;
+}
+
+static apr_status_t client_request_acceptor(serf_context_t *ctx,
+ serf_incoming_request_t *req,
+ void *request_baton,
+ apr_pool_t *pool)
+{
+ return APR_ENOTIMPL;
+}
+
+static apr_status_t client_acceptor(serf_context_t *ctx,
+ serf_listener_t *l,
+ void *accept_baton,
+ apr_socket_t *insock,
+ apr_pool_t *pool)
+{
+ serf_incoming_t *incoming;
+ test_baton_t *tb = accept_baton;
+
+ return serf_incoming_create2(&incoming, ctx, insock,
+ client_setup, tb,
+ client_closed, tb,
+ client_request_acceptor, tb,
+ pool);
+}
+
+void setup_test_server(test_baton_t *tb)
+{
+ serf_listener_t *listener;
+ apr_status_t status;
+ apr_port_t listen_port = 47080;
+
+ if (!tb->mh) /* TODO: move this to test_setup */
+ tb->mh = mhInit();
+
+ tb->context = serf_context_create(tb->pool);
+
+ while ((status = serf_listener_create(&listener, tb->context,
+ "localhost", listen_port,
+ tb, client_acceptor,
+ tb->pool)) != APR_SUCCESS)
+ {
+ listen_port++;
+ }
+
+ tb->serv_port = listen_port;
+ tb->serv_host = apr_psprintf(tb->pool, "%s:%d", "localhost", tb->serv_port);
+ tb->serv_url = apr_psprintf(tb->pool, "http://%s", tb->serv_host);
+}
+
+static apr_status_t
+run_client_server_loop(test_baton_t *tb,
+ int num_requests,
+ handler_baton_t handler_ctx[],
+ apr_pool_t *pool)
+{
+ apr_pool_t *iter_pool;
+ int i, done = 0;
+ apr_status_t status;
+ apr_time_t finish_time = apr_time_now() + apr_time_from_sec(15);
+
+ apr_pool_create(&iter_pool, pool);
+
+ while (!done)
+ {
+ 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) &&
+ SERF_BUCKET_READ_ERROR(status))
+ return status;
+
+ done = 1;
+ for (i = 0; i < num_requests; i++)
+ done &= handler_ctx[i].done;
+
+ if (!done && (apr_time_now() > finish_time))
+ return APR_ETIMEDOUT;
+ }
+ apr_pool_destroy(iter_pool);
+
+ return APR_SUCCESS;
+}
+
+void test_listen_http(CuTest *tc)
+{
+ test_baton_t *tb = tc->testBaton;
+ apr_status_t status;
+ handler_baton_t handler_ctx[2];
+ const int num_requests = sizeof(handler_ctx) / sizeof(handler_ctx[0]);
+
+ setup_test_server(tb);
+
+ status = setup_test_client_context(tb, NULL, tb->pool);
+ CuAssertIntEquals(tc, APR_SUCCESS, status);
+
+ create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
+ create_new_request(tb, &handler_ctx[1], "GET", "/", 2);
+
+ status = run_client_server_loop(tb, num_requests,
+ handler_ctx, tb->pool);
+ CuAssertIntEquals(tc, APR_ENOTIMPL, status);
+}
+
+
+/*****************************************************************************/
+CuSuite *test_server(void)
+{
+ CuSuite *suite = CuSuiteNew();
+
+ CuSuiteSetSetupTeardownCallbacks(suite, test_setup, test_teardown);
+
+ SUITE_ADD_TEST(suite, test_listen_http);
+
+ return suite;
+}
Propchange: serf/trunk/test/test_server.c
------------------------------------------------------------------------------
svn:eol-style = native
Modified: serf/trunk/test/test_util.c
URL:
http://svn.apache.org/viewvc/serf/trunk/test/test_util.c?rev=1714449&r1=1714448&r2=1714449&view=diff
==============================================================================
--- serf/trunk/test/test_util.c (original)
+++ serf/trunk/test/test_util.c Sun Nov 15 12:52:43 2015
@@ -420,7 +420,9 @@ setup_test_client_context(test_baton_t *
{
apr_status_t status;
- tb->context = serf_context_create(pool);
+ if (!tb->context)
+ tb->context = serf_context_create(pool);
+
tb->conn_setup = conn_setup ? conn_setup :
default_http_conn_setup;
status = use_new_connection(tb, pool);