#include "httpclient.h"
#include "../util/socket.h"
#include "../daemon/event.h"
#include "../proxy/httpclient.h"
#include <cx/buffer.h>
#include <cx/string.h>
#include <pthread.h>
static EVHandler *test_eventhandler;
static pthread_mutex_t test_mutex;
static pthread_cond_t test_cond;
static volatile int test_finished;
void http_client_tests_init(
void) {
pthread_mutex_init(&test_mutex,
NULL);
pthread_cond_init(&test_cond,
NULL);
EventHandlerConfig config;
config.isdefault =
0;
config.name = cx_str(
NULL);
config.nthreads =
1;
test_eventhandler = evhandler_create(&config);
}
void http_client_tests_cleanup(
void) {
ev_instance_shutdown(test_eventhandler->instances[
0]);
}
static void test_response_finished(HttpClient *client,
void *userdata) {
pthread_mutex_lock(&test_mutex);
pthread_cond_signal(&test_cond);
pthread_mutex_unlock(&test_mutex);
http_client_free(client);
test_finished =
1;
}
static ssize_t test_response_body_write(HttpClient *client,
void *buf,
size_t nbytes,
void *userdata) {
char *str = buf;
return cxBufferWrite(str,
1, nbytes, userdata);
}
static cxstring request_body_str;
static int request_body_pos =
0;
static size_t request_body_max_read =
16;
ssize_t test_request_body_read(HttpClient *client,
void *buf,
size_t nbytes,
void *userdata) {
if(nbytes > request_body_max_read) {
nbytes = request_body_max_read;
}
size_t available = request_body_str.length - request_body_pos;
if(nbytes > available) {
nbytes = available;
}
if(nbytes >
0) {
memcpy(buf, request_body_str.ptr + request_body_pos, nbytes);
request_body_pos += nbytes;
}
return nbytes;
}
CX_TEST_SUBROUTINE(test_httpclient, cxstring request_body,
int chunked_transfer, cxstring *response_blocks,
size_t num_blocks, CxBuffer *out) {
HttpClient *client = http_client_new(test_eventhandler->instances[
0]);
client->response_body_write = test_response_body_write;
client->response_body_write_userdata = out;
client->response_finished = test_response_finished;
int fds[
2];
util_socketpair(fds);
util_socket_setnonblock(fds[
0],
1);
http_client_set_socket(client, fds[
0]);
http_client_set_method(client,
"GET");
http_client_set_uri(client,
"/testx01");
request_body_str = request_body;
request_body_pos =
0;
if(request_body.length >
0) {
client->request_body_read = test_request_body_read;
if(chunked_transfer) {
http_client_enable_chunked_transfer_encoding(client);
}
else {
http_client_set_content_length(client, request_body.length);
}
}
test_finished =
0;
int ret = http_client_start(client);
CX_TEST_ASSERT(ret ==
0);
while(client->stage ==
0) {
char buf[
4096];
if(read(fds[
1], buf,
4096) <=
0) {
break;
}
}
for(
int i=
0;i<num_blocks;i++) {
size_t response_pos =
0;
cxstring response = response_blocks[i];
while(response_pos < response.length) {
size_t nbytes = response.length - response_pos;
const char *str = response.ptr + response_pos;
ssize_t w = write(fds[
1], str, nbytes);
CX_TEST_ASSERT(w >=
0);
response_pos += w;
}
}
pthread_mutex_lock(&test_mutex);
if(test_finished ==
0) {
pthread_cond_wait(&test_cond, &test_mutex);
}
pthread_mutex_unlock(&test_mutex);
close(fds[
1]);
}
CX_TEST(test_http_client_simple_get1) {
CX_TEST_DO {
cxstring response = cx_str(
"HTTP/1.1 200 OK\r\n"
"Content-length: 13\r\n"
"\r\n"
"Hello World!\n");
CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, &response,
1, out);
CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!\n"));
cxBufferFree(out);
}
}
CX_TEST(test_http_client_simple_get_line_io) {
CX_TEST_DO {
cxstring response[
8];
response[
0] = cx_str(
"HTTP/1.1 200 OK\r\n");
response[
1] = cx_str(
"Content-length: 13\r\n");
response[
2] = cx_str(
"\r\n");
response[
3] = cx_str(
"Hello World!\n");
CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, response,
4, out);
CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!\n"));
cxBufferFree(out);
}
}
CX_TEST(test_http_client_simple_get_small_blocksize) {
CX_TEST_DO {
cxstring response[
16];
response[
0] = cx_str(
"HTTP/1.1");
response[
1] = cx_str(
" 2");
response[
2] = cx_str(
"00 ");
response[
3] = cx_str(
" O");
response[
4] = cx_str(
"K\r\nContent-");
response[
5] = cx_str(
"length:");
response[
6] = cx_str(
" 26");
response[
7] = cx_str(
"\r");
response[
8] = cx_str(
"\nHost");
response[
9] = cx_str(
": localhost\r\n\r\nHello");
response[
10] = cx_str(
" World!\n");
response[
11] = cx_str(
"He");
response[
12] = cx_str(
"llo Wor");
response[
13] = cx_str(
"l");
response[
14] = cx_str(
"d!");
response[
15] = cx_str(
"\n");
CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, response,
16, out);
CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!\nHello World!\n"));
cxBufferFree(out);
}
}
CX_TEST(test_http_client_simple_get_1b_blocksize) {
CX_TEST_DO {
cxstring response_str = cx_str(
"HTTP/1.1 200 OK\r\n"
"Content-length: 13\r\n"
"\r\n"
"Hello World!\n");
cxstring *response = calloc(response_str.length,
sizeof(cxstring));
for(
int i=
0;i<response_str.length;i++) {
response[i] = cx_strn(response_str.ptr+i,
1);
}
CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, response, response_str.length, out);
CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!\n"));
cxBufferFree(out);
free(response);
}
}
CX_TEST(test_http_client_post_ctlen) {
CX_TEST_DO {
cxstring response_str = cx_str(
"HTTP/1.1 200 OK\r\n"
"Content-length: 21\r\n"
"\r\n"
"Hello World!---post1\n");
cxstring *response = calloc(response_str.length,
sizeof(cxstring));
for(
int i=
0;i<response_str.length;i++) {
response[i] = cx_strn(response_str.ptr+i,
1);
}
cxstring request_body = cx_str(
"test request body, needs more than one read (len > 16)\n");
CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
CX_TEST_CALL_SUBROUTINE(test_httpclient, request_body,
FALSE, response, response_str.length, out);
CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!---post1\n"));
cxBufferFree(out);
free(response);
}
}
CX_TEST(test_http_client_post_chunked) {
CX_TEST_DO {
cxstring response_str = cx_str(
"HTTP/1.1 200 OK\r\n"
"Content-length: 21\r\n"
"\r\n"
"Hello World!---post2\n");
cxstring *response = calloc(response_str.length,
sizeof(cxstring));
for(
int i=
0;i<response_str.length;i++) {
response[i] = cx_strn(response_str.ptr+i,
1);
}
cxstring request_body = cx_str(
"test request body, needs more than one read (len > 16)\n");
CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
CX_TEST_CALL_SUBROUTINE(test_httpclient, request_body,
TRUE, response, response_str.length, out);
CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!---post2\n"));
cxBufferFree(out);
free(response);
}
}
CX_TEST(test_http_client_post_ctlen_large) {
CX_TEST_DO {
cxstring response = cx_str(
"HTTP/1.1 200 OK\r\n"
"Content-length: 21\r\n"
"\r\n"
"Hello World!---post3\n");
size_t ctlen =
1024*
1024*
64;
char *req_body = malloc(ctlen);
cxstring request_body = cx_strn(req_body, ctlen);
CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
request_body_max_read =
4096;
CX_TEST_CALL_SUBROUTINE(test_httpclient, request_body,
FALSE, &response,
1, out);
CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!---post3\n"));
cxBufferFree(out);
free(req_body);
}
}
CX_TEST(test_http_client_get_incorrect_ctlen) {
CX_TEST_DO {
cxstring response = cx_str(
"HTTP/1.1 200 OK\r\n"
"Content-length: 5\r\n"
"\r\n"
"Hello World!\n");
CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, &response,
1, out);
CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello"));
cxBufferFree(out);
}
}
CX_TEST(test_http_client_broken_response) {
CX_TEST_DO {
cxstring response = cx_str(
"broken response\r\n"
"Content-length: 5\r\n"
"\r\n"
"Hello World!\n");
CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, &response,
1, out);
CX_TEST_ASSERT(out->size ==
0);
cxBufferFree(out);
}
}