|
Packit |
3adb1e |
/* ====================================================================
|
|
Packit |
3adb1e |
* Licensed to the Apache Software Foundation (ASF) under one
|
|
Packit |
3adb1e |
* or more contributor license agreements. See the NOTICE file
|
|
Packit |
3adb1e |
* distributed with this work for additional information
|
|
Packit |
3adb1e |
* regarding copyright ownership. The ASF licenses this file
|
|
Packit |
3adb1e |
* to you under the Apache License, Version 2.0 (the
|
|
Packit |
3adb1e |
* "License"); you may not use this file except in compliance
|
|
Packit |
3adb1e |
* with the License. You may obtain a copy of the License at
|
|
Packit |
3adb1e |
*
|
|
Packit |
3adb1e |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
3adb1e |
*
|
|
Packit |
3adb1e |
* Unless required by applicable law or agreed to in writing,
|
|
Packit |
3adb1e |
* software distributed under the License is distributed on an
|
|
Packit |
3adb1e |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
Packit |
3adb1e |
* KIND, either express or implied. See the License for the
|
|
Packit |
3adb1e |
* specific language governing permissions and limitations
|
|
Packit |
3adb1e |
* under the License.
|
|
Packit |
3adb1e |
* ====================================================================
|
|
Packit |
3adb1e |
*/
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
#include <apr_pools.h>
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
#include "serf.h"
|
|
Packit |
3adb1e |
#include "serf_bucket_util.h"
|
|
Packit |
3adb1e |
#include "test_serf.h"
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
/* This bucket uses a list of count - data/len - status actions (provided by the
|
|
Packit |
3adb1e |
test case), to control the read / read_iovec operations. */
|
|
Packit |
3adb1e |
typedef struct {
|
|
Packit |
3adb1e |
mockbkt_action *actions;
|
|
Packit |
3adb1e |
int len;
|
|
Packit |
3adb1e |
const char *current_data;
|
|
Packit |
3adb1e |
int remaining_data;
|
|
Packit |
3adb1e |
int current_action;
|
|
Packit |
3adb1e |
int remaining_times;
|
|
Packit |
3adb1e |
} mockbkt_context_t;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
serf_bucket_t *serf_bucket_mock_create(mockbkt_action *actions,
|
|
Packit |
3adb1e |
int len,
|
|
Packit |
3adb1e |
serf_bucket_alloc_t *allocator)
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
mockbkt_context_t *ctx;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
|
|
Packit |
3adb1e |
ctx->actions = actions;
|
|
Packit |
3adb1e |
ctx->len = len;
|
|
Packit |
3adb1e |
ctx->current_data = 0l;
|
|
Packit |
3adb1e |
ctx->remaining_data = -1;
|
|
Packit |
3adb1e |
ctx->current_action = 0;
|
|
Packit |
3adb1e |
ctx->remaining_times = -1;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
return serf_bucket_create(&serf_bucket_type_mock, allocator, ctx);
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
static apr_status_t next_action(mockbkt_context_t *ctx)
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
mockbkt_action *action;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
while (1)
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
if (ctx->current_action >= ctx->len)
|
|
Packit |
3adb1e |
return APR_EOF;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
action = &ctx->actions[ctx->current_action];
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
if (ctx->remaining_times == 0) {
|
|
Packit |
3adb1e |
ctx->current_action++;
|
|
Packit |
3adb1e |
ctx->remaining_times = -1;
|
|
Packit |
3adb1e |
ctx->remaining_data = -1;
|
|
Packit |
3adb1e |
continue;
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
if (ctx->remaining_data <= 0) {
|
|
Packit |
3adb1e |
ctx->current_data = action->data;
|
|
Packit |
3adb1e |
ctx->remaining_times = action->times;
|
|
Packit |
3adb1e |
ctx->remaining_data = strlen(action->data);
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
return APR_SUCCESS;
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
static apr_status_t serf_mock_readline(serf_bucket_t *bucket,
|
|
Packit |
3adb1e |
int acceptable, int *found,
|
|
Packit |
3adb1e |
const char **data, apr_size_t *len)
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
mockbkt_context_t *ctx = bucket->data;
|
|
Packit |
3adb1e |
mockbkt_action *action;
|
|
Packit |
3adb1e |
apr_status_t status;
|
|
Packit |
3adb1e |
const char *start_line;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
status = next_action(ctx);
|
|
Packit |
3adb1e |
if (status) {
|
|
Packit |
3adb1e |
*len = 0;
|
|
Packit |
3adb1e |
return status;
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
action = &ctx->actions[ctx->current_action];
|
|
Packit |
3adb1e |
start_line = *data = ctx->current_data;
|
|
Packit |
3adb1e |
*len = ctx->remaining_data;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
serf_util_readline(&start_line, len, acceptable, found);
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
/* See how much ctx->current moved forward. */
|
|
Packit |
3adb1e |
*len = start_line - ctx->current_data;
|
|
Packit |
3adb1e |
ctx->remaining_data -= *len;
|
|
Packit |
3adb1e |
ctx->current_data += *len;
|
|
Packit |
3adb1e |
if (ctx->remaining_data == 0)
|
|
Packit |
3adb1e |
ctx->remaining_times--;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
return ctx->remaining_data ? APR_SUCCESS : action->status;
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
static apr_status_t serf_mock_read(serf_bucket_t *bucket,
|
|
Packit |
3adb1e |
apr_size_t requested,
|
|
Packit |
3adb1e |
const char **data, apr_size_t *len)
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
mockbkt_context_t *ctx = bucket->data;
|
|
Packit |
3adb1e |
mockbkt_action *action;
|
|
Packit |
3adb1e |
apr_status_t status;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
status = next_action(ctx);
|
|
Packit |
3adb1e |
if (status) {
|
|
Packit |
3adb1e |
*len = 0;
|
|
Packit |
3adb1e |
return status;
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
action = &ctx->actions[ctx->current_action];
|
|
Packit |
3adb1e |
*len = requested < ctx->remaining_data ? requested : ctx->remaining_data;
|
|
Packit |
3adb1e |
*data = ctx->current_data;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
ctx->remaining_data -= *len;
|
|
Packit |
3adb1e |
ctx->current_data += *len;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
if (ctx->remaining_data == 0)
|
|
Packit |
3adb1e |
ctx->remaining_times--;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
return ctx->remaining_data ? APR_SUCCESS : action->status;
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
static apr_status_t serf_mock_peek(serf_bucket_t *bucket,
|
|
Packit |
3adb1e |
const char **data,
|
|
Packit |
3adb1e |
apr_size_t *len)
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
mockbkt_context_t *ctx = bucket->data;
|
|
Packit |
3adb1e |
mockbkt_action *action;
|
|
Packit |
3adb1e |
apr_status_t status;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
status = next_action(ctx);
|
|
Packit |
3adb1e |
if (status)
|
|
Packit |
3adb1e |
return status;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
action = &ctx->actions[ctx->current_action];
|
|
Packit |
3adb1e |
*len = ctx->remaining_data;
|
|
Packit |
3adb1e |
*data = ctx->current_data;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
/* peek only returns an error, APR_EOF or APR_SUCCESS.
|
|
Packit |
3adb1e |
APR_EAGAIN is returned as APR_SUCCESS. */
|
|
Packit |
3adb1e |
if (SERF_BUCKET_READ_ERROR(action->status))
|
|
Packit |
3adb1e |
return status;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
return action->status == APR_EOF ? APR_EOF : APR_SUCCESS;
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
/* An action { "", 0, APR_EAGAIN } means that serf should exit serf_context_run
|
|
Packit |
3adb1e |
and pass the buck back to the application. As long as no new data arrives,
|
|
Packit |
3adb1e |
this action remains active.
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
This function allows the 'application' to trigger the arrival of more data.
|
|
Packit |
3adb1e |
If the current action is { "", 0, APR_EAGAIN }, reduce the number of times
|
|
Packit |
3adb1e |
the action should run by one, and proceed with the next action if needed.
|
|
Packit |
3adb1e |
*/
|
|
Packit |
3adb1e |
apr_status_t serf_bucket_mock_more_data_arrived(serf_bucket_t *bucket)
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
mockbkt_context_t *ctx = bucket->data;
|
|
Packit |
3adb1e |
mockbkt_action *action;
|
|
Packit |
3adb1e |
apr_status_t status;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
status = next_action(ctx);
|
|
Packit |
3adb1e |
if (status)
|
|
Packit |
3adb1e |
return status;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
action = &ctx->actions[ctx->current_action];
|
|
Packit |
3adb1e |
if (ctx->remaining_data == 0 && action->status == APR_EAGAIN) {
|
|
Packit |
3adb1e |
ctx->remaining_times--;
|
|
Packit |
3adb1e |
action->times--;
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
return APR_SUCCESS;
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
const serf_bucket_type_t serf_bucket_type_mock = {
|
|
Packit |
3adb1e |
"MOCK",
|
|
Packit |
3adb1e |
serf_mock_read,
|
|
Packit |
3adb1e |
serf_mock_readline,
|
|
Packit |
3adb1e |
serf_default_read_iovec,
|
|
Packit |
3adb1e |
serf_default_read_for_sendfile,
|
|
Packit |
3adb1e |
serf_default_read_bucket,
|
|
Packit |
3adb1e |
serf_mock_peek,
|
|
Packit |
3adb1e |
serf_default_destroy_and_data,
|
|
Packit |
3adb1e |
};
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
/* internal test for the mock buckets */
|
|
Packit |
3adb1e |
static void test_basic_mock_bucket(CuTest *tc)
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
serf_bucket_t *mock_bkt;
|
|
Packit |
3adb1e |
apr_pool_t *test_pool = tc->testBaton;
|
|
Packit |
3adb1e |
serf_bucket_alloc_t *alloc = serf_bucket_allocator_create(test_pool, NULL,
|
|
Packit |
3adb1e |
NULL);
|
|
Packit |
3adb1e |
/* read one line */
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
mockbkt_action actions[]= {
|
|
Packit |
3adb1e |
{ 1, "HTTP/1.1 200 OK" CRLF, APR_EOF },
|
|
Packit |
3adb1e |
};
|
|
Packit |
3adb1e |
mock_bkt = serf_bucket_mock_create(actions, 1, alloc);
|
|
Packit |
3adb1e |
read_and_check_bucket(tc, mock_bkt,
|
|
Packit |
3adb1e |
"HTTP/1.1 200 OK" CRLF);
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
mock_bkt = serf_bucket_mock_create(actions, 1, alloc);
|
|
Packit |
3adb1e |
readlines_and_check_bucket(tc, mock_bkt, SERF_NEWLINE_CRLF,
|
|
Packit |
3adb1e |
"HTTP/1.1 200 OK" CRLF, 1);
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
/* read one line, character per character */
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
apr_status_t status;
|
|
Packit |
3adb1e |
const char *expected = "HTTP/1.1 200 OK" CRLF;
|
|
Packit |
3adb1e |
mockbkt_action actions[]= {
|
|
Packit |
3adb1e |
{ 1, "HTTP/1.1 200 OK" CRLF, APR_EOF },
|
|
Packit |
3adb1e |
};
|
|
Packit |
3adb1e |
mock_bkt = serf_bucket_mock_create(actions, 1, alloc);
|
|
Packit |
3adb1e |
do
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
const char *data;
|
|
Packit |
3adb1e |
apr_size_t len;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
status = serf_bucket_read(mock_bkt, 1, &data, &len;;
|
|
Packit |
3adb1e |
CuAssert(tc, "Got error during bucket reading.",
|
|
Packit |
3adb1e |
!SERF_BUCKET_READ_ERROR(status));
|
|
Packit |
3adb1e |
CuAssert(tc, "Read more data than expected.",
|
|
Packit |
3adb1e |
strlen(expected) >= len);
|
|
Packit |
3adb1e |
CuAssert(tc, "Read data is not equal to expected.",
|
|
Packit |
3adb1e |
strncmp(expected, data, len) == 0);
|
|
Packit |
3adb1e |
CuAssert(tc, "Read more data than requested.",
|
|
Packit |
3adb1e |
len <= 1);
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
expected += len;
|
|
Packit |
3adb1e |
} while(!APR_STATUS_IS_EOF(status));
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
CuAssert(tc, "Read less data than expected.", strlen(expected) == 0);
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
/* read multiple lines */
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
mockbkt_action actions[]= {
|
|
Packit |
3adb1e |
{ 1, "HTTP/1.1 200 OK" CRLF, APR_SUCCESS },
|
|
Packit |
3adb1e |
{ 1, "Content-Type: text/plain" CRLF, APR_EOF },
|
|
Packit |
3adb1e |
};
|
|
Packit |
3adb1e |
mock_bkt = serf_bucket_mock_create(actions, 2, alloc);
|
|
Packit |
3adb1e |
readlines_and_check_bucket(tc, mock_bkt, SERF_NEWLINE_CRLF,
|
|
Packit |
3adb1e |
"HTTP/1.1 200 OK" CRLF
|
|
Packit |
3adb1e |
"Content-Type: text/plain" CRLF, 2);
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
/* read empty line */
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
mockbkt_action actions[]= {
|
|
Packit |
3adb1e |
{ 1, "HTTP/1.1 200 OK" CRLF, APR_SUCCESS },
|
|
Packit |
3adb1e |
{ 1, "", APR_EAGAIN },
|
|
Packit |
3adb1e |
{ 1, "Content-Type: text/plain" CRLF, APR_EOF },
|
|
Packit |
3adb1e |
};
|
|
Packit |
3adb1e |
mock_bkt = serf_bucket_mock_create(actions, 3, alloc);
|
|
Packit |
3adb1e |
read_and_check_bucket(tc, mock_bkt,
|
|
Packit |
3adb1e |
"HTTP/1.1 200 OK" CRLF
|
|
Packit |
3adb1e |
"Content-Type: text/plain" CRLF);
|
|
Packit |
3adb1e |
mock_bkt = serf_bucket_mock_create(actions, 3, alloc);
|
|
Packit |
3adb1e |
readlines_and_check_bucket(tc, mock_bkt, SERF_NEWLINE_CRLF,
|
|
Packit |
3adb1e |
"HTTP/1.1 200 OK" CRLF
|
|
Packit |
3adb1e |
"Content-Type: text/plain" CRLF, 2);
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
/* read empty line */
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
mockbkt_action actions[]= {
|
|
Packit |
3adb1e |
{ 1, "HTTP/1.1 200 OK" CR, APR_SUCCESS },
|
|
Packit |
3adb1e |
{ 1, "", APR_EAGAIN },
|
|
Packit |
3adb1e |
{ 1, LF, APR_EOF },
|
|
Packit |
3adb1e |
};
|
|
Packit |
3adb1e |
mock_bkt = serf_bucket_mock_create(actions,
|
|
Packit |
3adb1e |
sizeof(actions)/sizeof(actions[0]),
|
|
Packit |
3adb1e |
alloc);
|
|
Packit |
3adb1e |
read_and_check_bucket(tc, mock_bkt,
|
|
Packit |
3adb1e |
"HTTP/1.1 200 OK" CRLF);
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
mock_bkt = serf_bucket_mock_create(actions,
|
|
Packit |
3adb1e |
sizeof(actions)/sizeof(actions[0]),
|
|
Packit |
3adb1e |
alloc);
|
|
Packit |
3adb1e |
readlines_and_check_bucket(tc, mock_bkt, SERF_NEWLINE_CRLF,
|
|
Packit |
3adb1e |
"HTTP/1.1 200 OK" CRLF, 1);
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
/* test more_data_arrived */
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
apr_status_t status;
|
|
Packit |
3adb1e |
const char *data;
|
|
Packit |
3adb1e |
apr_size_t len;
|
|
Packit |
3adb1e |
int i;
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
mockbkt_action actions[]= {
|
|
Packit |
3adb1e |
{ 1, "", APR_EAGAIN },
|
|
Packit |
3adb1e |
{ 1, "blabla", APR_EOF },
|
|
Packit |
3adb1e |
};
|
|
Packit |
3adb1e |
mock_bkt = serf_bucket_mock_create(actions,
|
|
Packit |
3adb1e |
sizeof(actions)/sizeof(actions[0]),
|
|
Packit |
3adb1e |
alloc);
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
for (i = 0; i < 5; i++) {
|
|
Packit |
3adb1e |
status = serf_bucket_peek(mock_bkt, &data, &len;;
|
|
Packit |
3adb1e |
CuAssertIntEquals(tc, APR_SUCCESS, status);
|
|
Packit |
3adb1e |
CuAssertIntEquals(tc, 0, len);
|
|
Packit |
3adb1e |
CuAssertIntEquals(tc, '\0', *data);
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
serf_bucket_mock_more_data_arrived(mock_bkt);
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
status = serf_bucket_peek(mock_bkt, &data, &len;;
|
|
Packit |
3adb1e |
CuAssertIntEquals(tc, APR_EOF, status);
|
|
Packit |
3adb1e |
CuAssertIntEquals(tc, 6, len);
|
|
Packit |
3adb1e |
CuAssert(tc, "Read data is not equal to expected.",
|
|
Packit |
3adb1e |
strncmp("blabla", data, len) == 0);
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
}
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
CuSuite *test_mock_bucket(void)
|
|
Packit |
3adb1e |
{
|
|
Packit |
3adb1e |
CuSuite *suite = CuSuiteNew();
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
CuSuiteSetSetupTeardownCallbacks(suite, test_setup, test_teardown);
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
SUITE_ADD_TEST(suite, test_basic_mock_bucket);
|
|
Packit |
3adb1e |
|
|
Packit |
3adb1e |
return suite;
|
|
Packit |
3adb1e |
}
|