1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 #include "httpclient.h"
31
32 #include "../util/socket.h"
33 #include "../daemon/event.h"
34 #include "../proxy/httpclient.h"
35
36 #include <cx/buffer.h>
37 #include <cx/string.h>
38
39 #include <pthread.h>
40
41 static EVHandler *test_eventhandler;
42 static pthread_mutex_t test_mutex;
43 static pthread_cond_t test_cond;
44 static volatile int test_finished;
45
46 void http_client_tests_init(
void) {
47 pthread_mutex_init(&test_mutex,
NULL);
48 pthread_cond_init(&test_cond,
NULL);
49
50 EventHandlerConfig config;
51 config.isdefault =
0;
52 config.name = cx_str(
NULL);
53 config.nthreads =
1;
54 test_eventhandler = evhandler_create(&config);
55 }
56
57 void http_client_tests_cleanup(
void) {
58 ev_instance_shutdown(test_eventhandler->instances[
0]);
59 }
60
61 static void test_response_finished(HttpClient *client,
void *userdata) {
62 pthread_mutex_lock(&test_mutex);
63 pthread_cond_signal(&test_cond);
64 pthread_mutex_unlock(&test_mutex);
65 http_client_free(client);
66 test_finished =
1;
67 }
68
69 static ssize_t test_response_body_write(HttpClient *client,
void *buf,
size_t nbytes,
void *userdata) {
70 char *str = buf;
71 return cxBufferWrite(str,
1, nbytes, userdata);
72 }
73
74 static cxstring request_body_str;
75 static int request_body_pos =
0;
76 static size_t request_body_max_read =
16;
77
78 ssize_t test_request_body_read(HttpClient *client,
void *buf,
size_t nbytes,
void *userdata) {
79 if(nbytes > request_body_max_read) {
80 nbytes = request_body_max_read;
81 }
82 size_t available = request_body_str.length - request_body_pos;
83 if(nbytes > available) {
84 nbytes = available;
85 }
86 if(nbytes >
0) {
87 memcpy(buf, request_body_str.ptr + request_body_pos, nbytes);
88 request_body_pos += nbytes;
89 }
90 return nbytes;
91 }
92
93 CX_TEST_SUBROUTINE(test_httpclient, cxstring request_body,
int chunked_transfer, cxstring *response_blocks,
size_t num_blocks, CxBuffer *out) {
94 HttpClient *client = http_client_new(test_eventhandler->instances[
0]);
95 client->response_body_write = test_response_body_write;
96 client->response_body_write_userdata = out;
97 client->response_finished = test_response_finished;
98
99 int fds[
2];
100 util_socketpair(fds);
101 util_socket_setnonblock(fds[
0],
1);
102
103 http_client_set_socket(client, fds[
0]);
104 http_client_set_method(client,
"GET");
105 http_client_set_uri(client,
"/testx01");
106
107 request_body_str = request_body;
108 request_body_pos =
0;
109 if(request_body.length >
0) {
110 client->request_body_read = test_request_body_read;
111 if(chunked_transfer) {
112 http_client_enable_chunked_transfer_encoding(client);
113 }
else {
114 http_client_set_content_length(client, request_body.length);
115 }
116 }
117
118 test_finished =
0;
119 int ret = http_client_start(client);
120 CX_TEST_ASSERT(ret ==
0);
121
122 while(client->stage ==
0) {
123 char buf[
4096];
124 if(read(fds[
1], buf,
4096) <=
0) {
125 break;
126 }
127 }
128
129 for(
int i=
0;i<num_blocks;i++) {
130 size_t response_pos =
0;
131 cxstring response = response_blocks[i];
132 while(response_pos < response.length) {
133 size_t nbytes = response.length - response_pos;
134 const char *str = response.ptr + response_pos;
135 ssize_t w = write(fds[
1], str, nbytes);
136 CX_TEST_ASSERT(w >=
0);
137 response_pos += w;
138 }
139 }
140
141 pthread_mutex_lock(&test_mutex);
142 if(test_finished ==
0) {
143 pthread_cond_wait(&test_cond, &test_mutex);
144 }
145 pthread_mutex_unlock(&test_mutex);
146
147 close(fds[
1]);
148 }
149
150 CX_TEST(test_http_client_simple_get1) {
151 CX_TEST_DO {
152 cxstring response = cx_str(
153 "HTTP/1.1 200 OK\r\n"
154 "Content-length: 13\r\n"
155 "\r\n"
156 "Hello World!\n");
157 CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
158
159 CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, &response,
1, out);
160 CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!\n"));
161
162 cxBufferFree(out);
163 }
164 }
165
166 CX_TEST(test_http_client_simple_get_line_io) {
167 CX_TEST_DO {
168 cxstring response[
8];
169 response[
0] = cx_str(
"HTTP/1.1 200 OK\r\n");
170 response[
1] = cx_str(
"Content-length: 13\r\n");
171 response[
2] = cx_str(
"\r\n");
172 response[
3] = cx_str(
"Hello World!\n");
173
174 CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
175
176 CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, response,
4, out);
177 CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!\n"));
178
179 cxBufferFree(out);
180 }
181 }
182
183 CX_TEST(test_http_client_simple_get_small_blocksize) {
184 CX_TEST_DO {
185 cxstring response[
16];
186 response[
0] = cx_str(
"HTTP/1.1");
187 response[
1] = cx_str(
" 2");
188 response[
2] = cx_str(
"00 ");
189 response[
3] = cx_str(
" O");
190 response[
4] = cx_str(
"K\r\nContent-");
191 response[
5] = cx_str(
"length:");
192 response[
6] = cx_str(
" 26");
193 response[
7] = cx_str(
"\r");
194 response[
8] = cx_str(
"\nHost");
195 response[
9] = cx_str(
": localhost\r\n\r\nHello");
196 response[
10] = cx_str(
" World!\n");
197 response[
11] = cx_str(
"He");
198 response[
12] = cx_str(
"llo Wor");
199 response[
13] = cx_str(
"l");
200 response[
14] = cx_str(
"d!");
201 response[
15] = cx_str(
"\n");
202
203 CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
204
205 CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, response,
16, out);
206 CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!\nHello World!\n"));
207
208 cxBufferFree(out);
209 }
210 }
211
212 CX_TEST(test_http_client_simple_get_1b_blocksize) {
213 CX_TEST_DO {
214 cxstring response_str = cx_str(
215 "HTTP/1.1 200 OK\r\n"
216 "Content-length: 13\r\n"
217 "\r\n"
218 "Hello World!\n");
219 cxstring *response = calloc(response_str.length,
sizeof(cxstring));
220 for(
int i=
0;i<response_str.length;i++) {
221 response[i] = cx_strn(response_str.ptr+i,
1);
222 }
223
224 CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
225
226 CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, response, response_str.length, out);
227 CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!\n"));
228
229 cxBufferFree(out);
230 free(response);
231 }
232 }
233
234 CX_TEST(test_http_client_post_ctlen) {
235 CX_TEST_DO {
236 cxstring response_str = cx_str(
237 "HTTP/1.1 200 OK\r\n"
238 "Content-length: 21\r\n"
239 "\r\n"
240 "Hello World!---post1\n");
241 cxstring *response = calloc(response_str.length,
sizeof(cxstring));
242 for(
int i=
0;i<response_str.length;i++) {
243 response[i] = cx_strn(response_str.ptr+i,
1);
244 }
245
246 cxstring request_body = cx_str(
"test request body, needs more than one read (len > 16)\n");
247
248 CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
249
250 CX_TEST_CALL_SUBROUTINE(test_httpclient, request_body,
FALSE, response, response_str.length, out);
251 CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!---post1\n"));
252
253 cxBufferFree(out);
254 free(response);
255 }
256 }
257
258 CX_TEST(test_http_client_post_chunked) {
259 CX_TEST_DO {
260 cxstring response_str = cx_str(
261 "HTTP/1.1 200 OK\r\n"
262 "Content-length: 21\r\n"
263 "\r\n"
264 "Hello World!---post2\n");
265 cxstring *response = calloc(response_str.length,
sizeof(cxstring));
266 for(
int i=
0;i<response_str.length;i++) {
267 response[i] = cx_strn(response_str.ptr+i,
1);
268 }
269
270 cxstring request_body = cx_str(
"test request body, needs more than one read (len > 16)\n");
271
272 CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
273
274 CX_TEST_CALL_SUBROUTINE(test_httpclient, request_body,
TRUE, response, response_str.length, out);
275 CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!---post2\n"));
276
277 cxBufferFree(out);
278 free(response);
279 }
280 }
281
282 CX_TEST(test_http_client_post_ctlen_large) {
283 CX_TEST_DO {
284 cxstring response = cx_str(
285 "HTTP/1.1 200 OK\r\n"
286 "Content-length: 21\r\n"
287 "\r\n"
288 "Hello World!---post3\n");
289
290 size_t ctlen =
1024*
1024*
64;
291 char *req_body = malloc(ctlen);
292 cxstring request_body = cx_strn(req_body, ctlen);
293
294 CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
295
296 request_body_max_read =
4096;
297 CX_TEST_CALL_SUBROUTINE(test_httpclient, request_body,
FALSE, &response,
1, out);
298 CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello World!---post3\n"));
299
300 cxBufferFree(out);
301 free(req_body);
302 }
303 }
304
305 CX_TEST(test_http_client_get_incorrect_ctlen) {
306 CX_TEST_DO {
307 cxstring response = cx_str(
308 "HTTP/1.1 200 OK\r\n"
309 "Content-length: 5\r\n"
310 "\r\n"
311 "Hello World!\n");
312 CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
313
314 CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, &response,
1, out);
315 CX_TEST_ASSERT(!cx_strcmp(cx_strn(out->space, out->size),
"Hello"));
316
317 cxBufferFree(out);
318 }
319 }
320
321 CX_TEST(test_http_client_broken_response) {
322 CX_TEST_DO {
323 cxstring response = cx_str(
324 "broken response\r\n"
325 "Content-length: 5\r\n"
326 "\r\n"
327 "Hello World!\n");
328 CxBuffer *out = cxBufferCreate(
NULL,
NULL,
256,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
329
330 CX_TEST_CALL_SUBROUTINE(test_httpclient, cx_str(
NULL),
FALSE, &response,
1, out);
331 CX_TEST_ASSERT(out->size ==
0);
332 cxBufferFree(out);
333 }
334 }
335