diff options
Diffstat (limited to 'test/test_context.c')
-rw-r--r-- | test/test_context.c | 1011 |
1 files changed, 1011 insertions, 0 deletions
diff --git a/test/test_context.c b/test/test_context.c new file mode 100644 index 0000000..d2a24d0 --- /dev/null +++ b/test/test_context.c @@ -0,0 +1,1011 @@ +/* Copyright 2002-2007 Justin Erenkrantz and Greg Stein + * + * Licensed 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" +#include "server/test_server.h" + +typedef struct { + serf_response_acceptor_t acceptor; + void *acceptor_baton; + + serf_response_handler_t handler; + + apr_array_header_t *sent_requests; + apr_array_header_t *accepted_requests; + apr_array_header_t *handled_requests; + int req_id; + + const char *method; + const char *path; + int done; + + test_baton_t *tb; +} handler_baton_t; + +static serf_bucket_t* accept_response(serf_request_t *request, + serf_bucket_t *stream, + void *acceptor_baton, + apr_pool_t *pool) +{ + serf_bucket_t *c; + serf_bucket_alloc_t *bkt_alloc; + handler_baton_t *ctx = acceptor_baton; + + /* get the per-request bucket allocator */ + bkt_alloc = serf_request_get_alloc(request); + + /* Create a barrier so the response doesn't eat us! */ + c = serf_bucket_barrier_create(stream, bkt_alloc); + + APR_ARRAY_PUSH(ctx->accepted_requests, int) = ctx->req_id; + + return serf_bucket_response_create(c, bkt_alloc); +} + +static apr_status_t setup_request(serf_request_t *request, + void *setup_baton, + serf_bucket_t **req_bkt, + serf_response_acceptor_t *acceptor, + void **acceptor_baton, + serf_response_handler_t *handler, + void **handler_baton, + apr_pool_t *pool) +{ + handler_baton_t *ctx = setup_baton; + serf_bucket_t *body_bkt; + + /* create a simple body text */ + const char *str = apr_psprintf(pool, "%d", ctx->req_id); + body_bkt = serf_bucket_simple_create(str, strlen(str), NULL, NULL, + serf_request_get_alloc(request)); + *req_bkt = + serf_request_bucket_request_create(request, + ctx->method, ctx->path, + body_bkt, + serf_request_get_alloc(request)); + + APR_ARRAY_PUSH(ctx->sent_requests, int) = ctx->req_id; + + *acceptor = ctx->acceptor; + *acceptor_baton = ctx; + *handler = ctx->handler; + *handler_baton = ctx; + + return APR_SUCCESS; +} + +static apr_status_t handle_response(serf_request_t *request, + serf_bucket_t *response, + void *handler_baton, + apr_pool_t *pool) +{ + handler_baton_t *ctx = handler_baton; + + if (! response) { + serf_connection_request_create(ctx->tb->connection, + setup_request, + ctx); + return APR_SUCCESS; + } + + while (1) { + apr_status_t status; + const char *data; + apr_size_t len; + + status = serf_bucket_read(response, 2048, &data, &len); + if (SERF_BUCKET_READ_ERROR(status)) + return status; + + if (APR_STATUS_IS_EOF(status)) { + APR_ARRAY_PUSH(ctx->handled_requests, int) = ctx->req_id; + ctx->done = TRUE; + return APR_EOF; + } + + if (APR_STATUS_IS_EAGAIN(status)) { + return status; + } + + } + + return APR_SUCCESS; +} + +/* Validate that requests are sent and completed in the order of creation. */ +static void test_serf_connection_request_create(CuTest *tc) +{ + test_baton_t *tb; + serf_request_t *request1, *request2; + handler_baton_t handler_ctx, handler2_ctx; + apr_status_t status; + apr_pool_t *iter_pool; + apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; + int i; + test_server_message_t message_list[] = { + {CHUNKED_REQUEST(1, "1")}, + {CHUNKED_REQUEST(1, "2")}, + }; + + test_server_action_t action_list[] = { + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + }; + apr_pool_t *test_pool = test_setup(); + + accepted_requests = apr_array_make(test_pool, 2, sizeof(int)); + sent_requests = apr_array_make(test_pool, 2, sizeof(int)); + handled_requests = apr_array_make(test_pool, 2, sizeof(int)); + + /* Set up a test context with a server */ + status = test_server_setup(&tb, + message_list, 2, + action_list, 2, 0, NULL, + test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + handler_ctx.method = "GET"; + handler_ctx.path = "/"; + handler_ctx.done = FALSE; + + handler_ctx.acceptor = accept_response; + handler_ctx.acceptor_baton = NULL; + handler_ctx.handler = handle_response; + handler_ctx.req_id = 1; + handler_ctx.accepted_requests = accepted_requests; + handler_ctx.sent_requests = sent_requests; + handler_ctx.handled_requests = handled_requests; + + request1 = serf_connection_request_create(tb->connection, + setup_request, + &handler_ctx); + + handler2_ctx = handler_ctx; + handler2_ctx.req_id = 2; + + request2 = serf_connection_request_create(tb->connection, + setup_request, + &handler2_ctx); + + apr_pool_create(&iter_pool, test_pool); + + while (!handler_ctx.done || !handler2_ctx.done) + { + apr_pool_clear(iter_pool); + + status = test_server_run(tb->serv_ctx, 0, iter_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + status = serf_context_run(tb->context, 0, iter_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + } + apr_pool_destroy(iter_pool); + + /* Check that all requests were received */ + CuAssertIntEquals(tc, 2, sent_requests->nelts); + CuAssertIntEquals(tc, 2, accepted_requests->nelts); + CuAssertIntEquals(tc, 2, handled_requests->nelts); + + /* Check that the requests were sent in the order we created them */ + for (i = 0; i < sent_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(sent_requests, i, int); + CuAssertIntEquals(tc, i + 1, req_nr); + } + + /* Check that the requests were received in the order we created them */ + for (i = 0; i < handled_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(handled_requests, i, int); + CuAssertIntEquals(tc, i + 1, req_nr); + } + + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} + +/* Validate that priority requests are sent and completed before normal + requests. */ +static void test_serf_connection_priority_request_create(CuTest *tc) +{ + test_baton_t *tb; + serf_request_t *request1, *request2, *request3; + handler_baton_t handler_ctx, handler2_ctx, handler3_ctx; + apr_status_t status; + apr_pool_t *iter_pool; + apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; + int i; + + test_server_message_t message_list[] = { + {CHUNKED_REQUEST(1, "1")}, + {CHUNKED_REQUEST(1, "2")}, + {CHUNKED_REQUEST(1, "3")}, + }; + + test_server_action_t action_list[] = { + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + }; + + apr_pool_t *test_pool = test_setup(); + + accepted_requests = apr_array_make(test_pool, 3, sizeof(int)); + sent_requests = apr_array_make(test_pool, 3, sizeof(int)); + handled_requests = apr_array_make(test_pool, 3, sizeof(int)); + + /* Set up a test context with a server */ + status = test_server_setup(&tb, + message_list, 3, + action_list, 3, 0, NULL, + test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + handler_ctx.method = "GET"; + handler_ctx.path = "/"; + handler_ctx.done = FALSE; + + handler_ctx.acceptor = accept_response; + handler_ctx.acceptor_baton = NULL; + handler_ctx.handler = handle_response; + handler_ctx.req_id = 2; + handler_ctx.accepted_requests = accepted_requests; + handler_ctx.sent_requests = sent_requests; + handler_ctx.handled_requests = handled_requests; + + request1 = serf_connection_request_create(tb->connection, + setup_request, + &handler_ctx); + + handler2_ctx = handler_ctx; + handler2_ctx.req_id = 3; + + request2 = serf_connection_request_create(tb->connection, + setup_request, + &handler2_ctx); + handler3_ctx = handler_ctx; + handler3_ctx.req_id = 1; + + request3 = serf_connection_priority_request_create(tb->connection, + setup_request, + &handler3_ctx); + + apr_pool_create(&iter_pool, test_pool); + + while (!handler_ctx.done || !handler2_ctx.done || !handler3_ctx.done) + { + apr_pool_clear(iter_pool); + + status = test_server_run(tb->serv_ctx, 0, iter_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + status = serf_context_run(tb->context, 0, iter_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + /* Debugging purposes only! */ + serf_debug__closed_conn(tb->bkt_alloc); + } + apr_pool_destroy(iter_pool); + + /* Check that all requests were received */ + CuAssertIntEquals(tc, 3, sent_requests->nelts); + CuAssertIntEquals(tc, 3, accepted_requests->nelts); + CuAssertIntEquals(tc, 3, handled_requests->nelts); + + /* Check that the requests were sent in the order we created them */ + for (i = 0; i < sent_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(sent_requests, i, int); + CuAssertIntEquals(tc, i + 1, req_nr); + } + + /* Check that the requests were received in the order we created them */ + for (i = 0; i < handled_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(handled_requests, i, int); + CuAssertIntEquals(tc, i + 1, req_nr); + } + + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} + +/* Test that serf correctly handles the 'Connection:close' header when the + server is planning to close the connection. */ +#define NUM_REQUESTS 10 +static void test_serf_closed_connection(CuTest *tc) +{ + test_baton_t *tb; + apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; + apr_status_t status; + handler_baton_t handler_ctx[NUM_REQUESTS]; + int done = FALSE, i; + + test_server_message_t message_list[] = { + {CHUNKED_REQUEST(1, "1")}, + {CHUNKED_REQUEST(1, "2")}, + {CHUNKED_REQUEST(1, "3")}, + {CHUNKED_REQUEST(1, "4")}, + {CHUNKED_REQUEST(1, "5")}, + {CHUNKED_REQUEST(1, "6")}, + {CHUNKED_REQUEST(1, "7")}, + {CHUNKED_REQUEST(1, "8")}, + {CHUNKED_REQUEST(1, "9")}, + {CHUNKED_REQUEST(2, "10")} + }; + + test_server_action_t action_list[] = { + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, + "HTTP/1.1 200 OK" CRLF + "Transfer-Encoding: chunked" CRLF + "Connection: close" CRLF + CRLF + "0" CRLF + CRLF + }, + {SERVER_IGNORE_AND_KILL_CONNECTION}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, + "HTTP/1.1 200 OK" CRLF + "Transfer-Encoding: chunked" CRLF + "Connection: close" CRLF + CRLF + "0" CRLF + CRLF + }, + {SERVER_IGNORE_AND_KILL_CONNECTION}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + }; + + apr_pool_t *test_pool = test_setup(); + + accepted_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); + sent_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); + handled_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); + + /* Set up a test context with a server. */ + status = test_server_setup(&tb, + message_list, 10, + action_list, 12, + 0, + NULL, + test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + for (i = 0 ; i < NUM_REQUESTS ; i++) { + /* Send some requests on the connections */ + handler_ctx[i].method = "GET"; + handler_ctx[i].path = "/"; + handler_ctx[i].done = FALSE; + + handler_ctx[i].acceptor = accept_response; + handler_ctx[i].acceptor_baton = NULL; + handler_ctx[i].handler = handle_response; + handler_ctx[i].req_id = i+1; + handler_ctx[i].accepted_requests = accepted_requests; + handler_ctx[i].sent_requests = sent_requests; + handler_ctx[i].handled_requests = handled_requests; + handler_ctx[i].tb = tb; + + serf_connection_request_create(tb->connection, + setup_request, + &handler_ctx[i]); + } + + while (1) { + status = test_server_run(tb->serv_ctx, 0, test_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + status = serf_context_run(tb->context, 0, test_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + /* Debugging purposes only! */ + serf_debug__closed_conn(tb->bkt_alloc); + + done = TRUE; + for (i = 0 ; i < NUM_REQUESTS ; i++) + if (handler_ctx[i].done == FALSE) { + done = FALSE; + break; + } + if (done) + break; + } + + /* Check that all requests were received */ + CuAssertTrue(tc, sent_requests->nelts >= NUM_REQUESTS); + CuAssertIntEquals(tc, NUM_REQUESTS, accepted_requests->nelts); + CuAssertIntEquals(tc, NUM_REQUESTS, handled_requests->nelts); + + /* Cleanup */ + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} +#undef NUM_REQUESTS + +/* Test if serf is sending the request to the proxy, not to the server + directly. */ +static void test_serf_setup_proxy(CuTest *tc) +{ + test_baton_t *tb; + serf_request_t *request; + handler_baton_t handler_ctx; + apr_status_t status; + apr_pool_t *iter_pool; + apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; + int i; + int numrequests = 1; + + test_server_message_t message_list[] = { + {"GET http://localhost:" SERV_PORT_STR " HTTP/1.1" CRLF\ + "Host: localhost:" SERV_PORT_STR CRLF\ + "Transfer-Encoding: chunked" CRLF\ + CRLF\ + "1" CRLF\ + "1" CRLF\ + "0" CRLF\ + CRLF} + }; + + test_server_action_t action_list_proxy[] = { + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + }; + + apr_pool_t *test_pool = test_setup(); + + accepted_requests = apr_array_make(test_pool, numrequests, sizeof(int)); + sent_requests = apr_array_make(test_pool, numrequests, sizeof(int)); + handled_requests = apr_array_make(test_pool, numrequests, sizeof(int)); + + /* Set up a test context with a server, no messages expected. */ + status = test_server_proxy_setup(&tb, + /* server messages and actions */ + NULL, 0, + NULL, 0, + /* server messages and actions */ + message_list, 1, + action_list_proxy, 1, + 0, + NULL, test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + handler_ctx.method = "GET"; + handler_ctx.path = "/"; + handler_ctx.done = FALSE; + + handler_ctx.acceptor = accept_response; + handler_ctx.acceptor_baton = NULL; + handler_ctx.handler = handle_response; + handler_ctx.req_id = 1; + handler_ctx.accepted_requests = accepted_requests; + handler_ctx.sent_requests = sent_requests; + handler_ctx.handled_requests = handled_requests; + + request = serf_connection_request_create(tb->connection, + setup_request, + &handler_ctx); + + apr_pool_create(&iter_pool, test_pool); + + while (!handler_ctx.done) + { + apr_pool_clear(iter_pool); + + status = test_server_run(tb->serv_ctx, 0, iter_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + status = test_server_run(tb->proxy_ctx, 0, iter_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + status = serf_context_run(tb->context, 0, iter_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + /* Debugging purposes only! */ + serf_debug__closed_conn(tb->bkt_alloc); + } + apr_pool_destroy(iter_pool); + + /* Check that all requests were received */ + CuAssertIntEquals(tc, numrequests, sent_requests->nelts); + CuAssertIntEquals(tc, numrequests, accepted_requests->nelts); + CuAssertIntEquals(tc, numrequests, handled_requests->nelts); + + /* Check that the requests were sent in the order we created them */ + for (i = 0; i < sent_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(sent_requests, i, int); + CuAssertIntEquals(tc, i + 1, req_nr); + } + + /* Check that the requests were received in the order we created them */ + for (i = 0; i < handled_requests->nelts; i++) { + int req_nr = APR_ARRAY_IDX(handled_requests, i, int); + CuAssertIntEquals(tc, i + 1, req_nr); + } + + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} + +/***************************************************************************** + * Test if we can make serf send requests one by one. + *****************************************************************************/ + +/* Resend the first request 4 times by reducing the pipeline bandwidth to + one request at a time, and by adding the first request again at the start of + the outgoing queue. */ +static apr_status_t +handle_response_keepalive_limit(serf_request_t *request, + serf_bucket_t *response, + void *handler_baton, + apr_pool_t *pool) +{ + handler_baton_t *ctx = handler_baton; + + if (! response) { + return APR_SUCCESS; + } + + while (1) { + apr_status_t status; + const char *data; + apr_size_t len; + + status = serf_bucket_read(response, 2048, &data, &len); + if (SERF_BUCKET_READ_ERROR(status)) { + return status; + } + + if (APR_STATUS_IS_EOF(status)) { + APR_ARRAY_PUSH(ctx->handled_requests, int) = ctx->req_id; + ctx->done = TRUE; + if (ctx->req_id == 1 && ctx->handled_requests->nelts < 3) { + serf_connection_priority_request_create(ctx->tb->connection, + setup_request, + ctx); + ctx->done = FALSE; + } + return APR_EOF; + } + } + + return APR_SUCCESS; +} + +#define SEND_REQUESTS 5 +#define RCVD_REQUESTS 7 +static void test_keepalive_limit_one_by_one(CuTest *tc) +{ + test_baton_t *tb; + apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; + apr_status_t status; + handler_baton_t handler_ctx[SEND_REQUESTS]; + int done = FALSE, i; + + test_server_message_t message_list[] = { + {CHUNKED_REQUEST(1, "1")}, + {CHUNKED_REQUEST(1, "1")}, + {CHUNKED_REQUEST(1, "1")}, + {CHUNKED_REQUEST(1, "2")}, + {CHUNKED_REQUEST(1, "3")}, + {CHUNKED_REQUEST(1, "4")}, + {CHUNKED_REQUEST(1, "5")}, + }; + + test_server_action_t action_list[] = { + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + }; + + apr_pool_t *test_pool = test_setup(); + + accepted_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); + sent_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); + handled_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); + + /* Set up a test context with a server. */ + status = test_server_setup(&tb, + message_list, 7, + action_list, 7, 0, NULL, + test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + for (i = 0 ; i < SEND_REQUESTS ; i++) { + /* Send some requests on the connections */ + handler_ctx[i].method = "GET"; + handler_ctx[i].path = "/"; + handler_ctx[i].done = FALSE; + + handler_ctx[i].acceptor = accept_response; + handler_ctx[i].acceptor_baton = NULL; + handler_ctx[i].handler = handle_response_keepalive_limit; + handler_ctx[i].req_id = i+1; + handler_ctx[i].accepted_requests = accepted_requests; + handler_ctx[i].sent_requests = sent_requests; + handler_ctx[i].handled_requests = handled_requests; + handler_ctx[i].tb = tb; + + serf_connection_request_create(tb->connection, + setup_request, + &handler_ctx[i]); + serf_connection_set_max_outstanding_requests(tb->connection, 1); + } + + while (1) { + status = test_server_run(tb->serv_ctx, 0, test_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + status = serf_context_run(tb->context, 0, test_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + /* Debugging purposes only! */ + serf_debug__closed_conn(tb->bkt_alloc); + + done = TRUE; + for (i = 0 ; i < SEND_REQUESTS ; i++) + if (handler_ctx[i].done == FALSE) { + done = FALSE; + break; + } + if (done) + break; + } + + /* Check that all requests were received */ + CuAssertIntEquals(tc, RCVD_REQUESTS, sent_requests->nelts); + CuAssertIntEquals(tc, RCVD_REQUESTS, accepted_requests->nelts); + CuAssertIntEquals(tc, RCVD_REQUESTS, handled_requests->nelts); + + /* Cleanup */ + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} +#undef SEND_REQUESTS +#undef RCVD_REQUESTS + +/***************************************************************************** + * Test if we can make serf first send requests one by one, and then change + * back to burst mode. + *****************************************************************************/ +#define SEND_REQUESTS 5 +#define RCVD_REQUESTS 7 +/* Resend the first request 2 times by reducing the pipeline bandwidth to + one request at a time, and by adding the first request again at the start of + the outgoing queue. */ +static apr_status_t +handle_response_keepalive_limit_burst(serf_request_t *request, + serf_bucket_t *response, + void *handler_baton, + apr_pool_t *pool) +{ + handler_baton_t *ctx = handler_baton; + + if (! response) { + return APR_SUCCESS; + } + + while (1) { + apr_status_t status; + const char *data; + apr_size_t len; + + status = serf_bucket_read(response, 2048, &data, &len); + if (SERF_BUCKET_READ_ERROR(status)) { + return status; + } + + if (APR_STATUS_IS_EOF(status)) { + APR_ARRAY_PUSH(ctx->handled_requests, int) = ctx->req_id; + ctx->done = TRUE; + if (ctx->req_id == 1 && ctx->handled_requests->nelts < 3) { + serf_connection_priority_request_create(ctx->tb->connection, + setup_request, + ctx); + ctx->done = FALSE; + } + else { + /* No more one-by-one. */ + serf_connection_set_max_outstanding_requests(ctx->tb->connection, + 0); + } + return APR_EOF; + } + + if (APR_STATUS_IS_EAGAIN(status)) { + return status; + } + } + + return APR_SUCCESS; +} + +static void test_keepalive_limit_one_by_one_and_burst(CuTest *tc) +{ + test_baton_t *tb; + apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; + apr_status_t status; + handler_baton_t handler_ctx[SEND_REQUESTS]; + int done = FALSE, i; + + test_server_message_t message_list[] = { + {CHUNKED_REQUEST(1, "1")}, + {CHUNKED_REQUEST(1, "1")}, + {CHUNKED_REQUEST(1, "1")}, + {CHUNKED_REQUEST(1, "2")}, + {CHUNKED_REQUEST(1, "3")}, + {CHUNKED_REQUEST(1, "4")}, + {CHUNKED_REQUEST(1, "5")}, + }; + + test_server_action_t action_list[] = { + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + }; + + apr_pool_t *test_pool = test_setup(); + + accepted_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); + sent_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); + handled_requests = apr_array_make(test_pool, RCVD_REQUESTS, sizeof(int)); + + /* Set up a test context with a server. */ + status = test_server_setup(&tb, + message_list, 7, + action_list, 7, 0, NULL, + test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + for (i = 0 ; i < SEND_REQUESTS ; i++) { + /* Send some requests on the connections */ + handler_ctx[i].method = "GET"; + handler_ctx[i].path = "/"; + handler_ctx[i].done = FALSE; + + handler_ctx[i].acceptor = accept_response; + handler_ctx[i].acceptor_baton = NULL; + handler_ctx[i].handler = handle_response_keepalive_limit_burst; + handler_ctx[i].req_id = i+1; + handler_ctx[i].accepted_requests = accepted_requests; + handler_ctx[i].sent_requests = sent_requests; + handler_ctx[i].handled_requests = handled_requests; + handler_ctx[i].tb = tb; + + serf_connection_request_create(tb->connection, + setup_request, + &handler_ctx[i]); + serf_connection_set_max_outstanding_requests(tb->connection, 1); + } + + while (1) { + status = test_server_run(tb->serv_ctx, 0, test_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + status = serf_context_run(tb->context, 0, test_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + /* Debugging purposes only! */ + serf_debug__closed_conn(tb->bkt_alloc); + + done = TRUE; + for (i = 0 ; i < SEND_REQUESTS ; i++) + if (handler_ctx[i].done == FALSE) { + done = FALSE; + break; + } + if (done) + break; + } + + /* Check that all requests were received */ + CuAssertIntEquals(tc, RCVD_REQUESTS, sent_requests->nelts); + CuAssertIntEquals(tc, RCVD_REQUESTS, accepted_requests->nelts); + CuAssertIntEquals(tc, RCVD_REQUESTS, handled_requests->nelts); + + /* Cleanup */ + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} +#undef SEND_REQUESTS +#undef RCVD_REQUESTS + +#define NUM_REQUESTS 5 +typedef struct { + apr_off_t read; + apr_off_t written; +} progress_baton_t; + +static void +progress_cb(void *progress_baton, apr_off_t read, apr_off_t written) +{ + test_baton_t *tb = progress_baton; + progress_baton_t *pb = tb->user_baton; + + pb->read = read; + pb->written = written; +} + +static apr_status_t progress_conn_setup(apr_socket_t *skt, + serf_bucket_t **input_bkt, + serf_bucket_t **output_bkt, + void *setup_baton, + apr_pool_t *pool) +{ + test_baton_t *tb = setup_baton; + *input_bkt = serf_context_bucket_socket_create(tb->context, skt, tb->bkt_alloc); + return APR_SUCCESS; +} + +static void test_serf_progress_callback(CuTest *tc) +{ + test_baton_t *tb; + apr_array_header_t *accepted_requests, *handled_requests, *sent_requests; + apr_status_t status; + handler_baton_t handler_ctx[NUM_REQUESTS]; + int done = FALSE, i; + progress_baton_t *pb; + + test_server_message_t message_list[] = { + {CHUNKED_REQUEST(1, "1")}, + {CHUNKED_REQUEST(1, "2")}, + {CHUNKED_REQUEST(1, "3")}, + {CHUNKED_REQUEST(1, "4")}, + {CHUNKED_REQUEST(1, "5")}, + }; + + test_server_action_t action_list[] = { + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_RESPONSE(1, "2")}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + {SERVER_RESPOND, CHUNKED_EMPTY_RESPONSE}, + }; + + apr_pool_t *test_pool = test_setup(); + + accepted_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); + sent_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); + handled_requests = apr_array_make(test_pool, NUM_REQUESTS, sizeof(int)); + + /* Set up a test context with a server. */ + status = test_server_setup(&tb, + message_list, 5, + action_list, 5, 0, + progress_conn_setup, test_pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + /* Set up the progress callback. */ + pb = apr_pcalloc(test_pool, sizeof(*pb)); + tb->user_baton = pb; + serf_context_set_progress_cb(tb->context, progress_cb, tb); + + for (i = 0 ; i < NUM_REQUESTS ; i++) { + /* Send some requests on the connections */ + handler_ctx[i].method = "GET"; + handler_ctx[i].path = "/"; + handler_ctx[i].done = FALSE; + + handler_ctx[i].acceptor = accept_response; + handler_ctx[i].acceptor_baton = NULL; + handler_ctx[i].handler = handle_response; + handler_ctx[i].req_id = i+1; + handler_ctx[i].accepted_requests = accepted_requests; + handler_ctx[i].sent_requests = sent_requests; + handler_ctx[i].handled_requests = handled_requests; + handler_ctx[i].tb = tb; + + serf_connection_request_create(tb->connection, + setup_request, + &handler_ctx[i]); + } + + while (1) { + status = test_server_run(tb->serv_ctx, 0, test_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + status = serf_context_run(tb->context, 0, test_pool); + if (APR_STATUS_IS_TIMEUP(status)) + status = APR_SUCCESS; + CuAssertIntEquals(tc, APR_SUCCESS, status); + + /* Debugging purposes only! */ + serf_debug__closed_conn(tb->bkt_alloc); + + done = TRUE; + for (i = 0 ; i < NUM_REQUESTS ; i++) + if (handler_ctx[i].done == FALSE) { + done = FALSE; + break; + } + if (done) + break; + } + + /* Check that all requests were received */ + CuAssertTrue(tc, sent_requests->nelts >= NUM_REQUESTS); + CuAssertIntEquals(tc, NUM_REQUESTS, accepted_requests->nelts); + CuAssertIntEquals(tc, NUM_REQUESTS, handled_requests->nelts); + + /* Check that progress was reported. */ + CuAssertTrue(tc, pb->written > 0); + CuAssertTrue(tc, pb->read > 0); + + /* Cleanup */ + test_server_teardown(tb, test_pool); + test_teardown(test_pool); +} +#undef NUM_REQUESTS + +CuSuite *test_context(void) +{ + CuSuite *suite = CuSuiteNew(); + + SUITE_ADD_TEST(suite, test_serf_connection_request_create); + SUITE_ADD_TEST(suite, test_serf_connection_priority_request_create); + SUITE_ADD_TEST(suite, test_serf_closed_connection); + SUITE_ADD_TEST(suite, test_serf_setup_proxy); + SUITE_ADD_TEST(suite, test_keepalive_limit_one_by_one); + SUITE_ADD_TEST(suite, test_keepalive_limit_one_by_one_and_burst); + SUITE_ADD_TEST(suite, test_serf_progress_callback); + + return suite; +} |