src/server/proxy/httpclient.c

changeset 680
02935baa186b
parent 679
4885cd4c3754
child 681
e9705d51866a
equal deleted inserted replaced
679:4885cd4c3754 680:02935baa186b
39 static int client_connected(EventHandler *ev, Event *event); 39 static int client_connected(EventHandler *ev, Event *event);
40 static int client_io(EventHandler *ev, Event *event); 40 static int client_io(EventHandler *ev, Event *event);
41 static int client_finished(EventHandler *ev, Event *event); 41 static int client_finished(EventHandler *ev, Event *event);
42 42
43 static int client_send_request(HttpClient *client); 43 static int client_send_request(HttpClient *client);
44 static int client_send_request_body(HttpClient *client);
44 45
45 HttpClient* http_client_new(EventHandler *ev) { 46 HttpClient* http_client_new(EventHandler *ev) {
46 CxMempool *mp = cxMempoolCreate(32, CX_MEMPOOL_TYPE_PURE); 47 CxMempool *mp = cxMempoolCreate(32, CX_MEMPOOL_TYPE_PURE);
47 if(!mp) { 48 if(!mp) {
48 return NULL; 49 return NULL;
248 return 0; // end 249 return 0; // end
249 } 250 }
250 event->fn = client_io; 251 event->fn = client_io;
251 252
252 return client_io(ev, event); 253 return client_io(ev, event);
253 }
254
255 static int client_send_request_body(HttpClient *client) {
256 size_t rbody_readsize = client->req_buffer_alloc;
257 size_t rbody_buf_offset = 0;
258 if(client->req_content_length == -1) {
259 // chunked transfer encoding:
260 // don't fill req_buffer completely, reserve some space for
261 // a chunk header, that will be inserted at the beginning
262 rbody_readsize -= 16;
263 rbody_buf_offset = 16;
264 }
265 while(!client->request_body_complete) {
266 ssize_t r = client->request_body_read(client, client->req_buffer + rbody_buf_offset, rbody_readsize, client->request_body_read_userdata);
267 if(r <= 0) {
268 if(r == HTTP_CLIENT_CALLBACK_WOULD_BLOCK) {
269 return 1;
270 } else if(r == 0) {
271 // EOF
272 client->request_body_complete = 1;
273 break;
274 } else {
275 // error
276 client->error = 1;
277 return 1;
278 }
279 }
280
281 size_t startpos = 0;
282 if(client->req_content_length == -1) {
283 char chunkheader[16];
284 int chunkheaderlen = snprintf(chunkheader, 16, "%zx\r\n", (size_t)r);
285 startpos = 16 - chunkheaderlen;
286 memcpy(client->req_buffer + startpos, chunkheader, chunkheaderlen);
287 }
288
289 client->req_contentlength_pos += r;
290 client->req_buffer_pos = startpos;
291 client->req_buffer_len = r;
292 if(client_send_request(client)) {
293 return 1;
294 }
295 }
296
297 if(client->req_content_length > 0 && client->req_content_length != client->req_contentlength_pos) {
298 // incomplete request body
299 client->error = 1;
300 return 1;
301 }
302
303 return 0;
304 } 254 }
305 255
306 static int client_io(EventHandler *ev, Event *event) { 256 static int client_io(EventHandler *ev, Event *event) {
307 HttpClient *client = event->cookie; 257 HttpClient *client = event->cookie;
308 if(client->req_buffer_pos < client->req_buffer_len) { 258 if(client->req_buffer_pos < client->req_buffer_len) {
432 } 382 }
433 383
434 return client->req_buffer_pos < client->req_buffer_len; 384 return client->req_buffer_pos < client->req_buffer_len;
435 } 385 }
436 386
437 387 static int client_send_request_body(HttpClient *client) {
388 size_t rbody_readsize = client->req_buffer_alloc;
389 size_t rbody_buf_offset = 0;
390 if(client->req_content_length == -1) {
391 // chunked transfer encoding:
392 // don't fill req_buffer completely, reserve some space for
393 // a chunk header, that will be inserted at the beginning
394 rbody_readsize -= 16;
395 rbody_buf_offset = 16;
396 }
397 while(!client->request_body_complete) {
398 ssize_t r = client->request_body_read(client, client->req_buffer + rbody_buf_offset, rbody_readsize, client->request_body_read_userdata);
399 if(r <= 0) {
400 if(r == HTTP_CLIENT_CALLBACK_WOULD_BLOCK) {
401 return 1;
402 } else if(r == 0) {
403 // EOF
404 client->request_body_complete = 1;
405 break;
406 } else {
407 // error
408 client->error = 1;
409 return 1;
410 }
411 }
412
413 size_t startpos = 0;
414 if(client->req_content_length == -1) {
415 char chunkheader[16];
416 int chunkheaderlen = snprintf(chunkheader, 16, "%zx\r\n", (size_t)r);
417 startpos = 16 - chunkheaderlen;
418 memcpy(client->req_buffer + startpos, chunkheader, chunkheaderlen);
419 }
420
421 client->req_contentlength_pos += r;
422 client->req_buffer_pos = startpos;
423 client->req_buffer_len = rbody_buf_offset + r;
424 if(client_send_request(client)) {
425 return 1;
426 }
427 }
428
429 // chunked transfer encoding: terminate
430 if(client->req_content_length == -1 && client->request_body_complete != 2) {
431 memcpy(client->req_buffer, "0\r\n", 3);
432 client->req_buffer_pos = 0;
433 client->req_buffer_len = 3;
434 if(client_send_request(client)) {
435 return 1;
436 }
437
438 } else if(client->req_content_length != client->req_contentlength_pos) {
439 // incomplete request body
440 client->error = 1;
441 return 1;
442 }
443
444 return 0;
445 }
438 446
439 /* --------------------------------- Tests --------------------------------- */ 447 /* --------------------------------- Tests --------------------------------- */
440 448
441 static CX_TEST(test_http_client_send_request) { 449 static CX_TEST(test_http_client_send_request) {
442 CX_TEST_DO { 450 CX_TEST_DO {
623 http_client_free(client); 631 http_client_free(client);
624 cxBufferDestroy(&buf); 632 cxBufferDestroy(&buf);
625 } 633 }
626 } 634 }
627 635
636
637 typedef struct TestRequestBody {
638 char *content;
639 size_t length;
640 size_t pos;
641 int chunksize;
642 int max_reads; // max number of reads until test_request_body_read returns 0
643 int cur_reads; // current number of read-attempts
644 } TestRequestBody;
645
646 static ssize_t test_request_body_read(HttpClient *client, void *buf, size_t size, void *userdata) {
647 TestRequestBody *req = userdata;
648 req->cur_reads++;
649 if(req->chunksize == 0 || req->cur_reads > req->max_reads) {
650 return -1;
651 }
652 size_t max = req->length - req->pos;
653 if(max == 0) {
654 return 0;
655 }
656
657 size_t sz = req->chunksize > size ? size : req->chunksize;
658 if(sz > max) {
659 sz = max;
660 }
661 memcpy(buf, req->content + req->pos, sz);
662 req->pos += sz;
663 return sz;
664 }
665
666 static CX_TEST(test_http_client_send_request_body_chunked) {
667 CX_TEST_DO {
668 EventHandler dummy;
669 HttpClient *client = http_client_new(&dummy);
670 create_req_buffer(client);
671 client->req_content_length = -1;
672
673 int fds[2];
674 util_socketpair(fds);
675 util_socket_setnonblock(fds[0], 1);
676 util_socket_setnonblock(fds[1], 1);
677 client->socketfd = fds[0];
678 int sock = fds[1];
679
680 // response buffer
681 CxBuffer buf;
682 cxBufferInit(&buf, cxDefaultAllocator, NULL, 1024, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
683
684 // test
685 char request_body[1024];
686 memset(request_body, 'x', 1024);
687 memset(request_body+128, 'y', 128);
688 memset(request_body+384, 'z', 128);
689 memset(request_body+640, ':', 128);
690 memset(request_body+896, '!', 128);
691
692 TestRequestBody req;
693 req.content = request_body;
694 req.length = 1024;
695 req.pos = 0;
696 req.chunksize = 16;
697 req.max_reads = 8;
698 req.cur_reads = 0;
699 client->request_body_read = test_request_body_read;
700 client->request_body_read_userdata = &req;
701
702 memset(client->req_buffer, '_', client->req_buffer_alloc);
703 client->req_buffer_pos = 0;
704 client->req_buffer_len = 0;
705
706 // send the first 128 bytes
707 while(req.cur_reads <= req.max_reads) {
708 int ret = client_send_request_body(client);
709 CX_TEST_ASSERT(ret == 1);
710 CX_TEST_ASSERT(!client->error);
711 char buf2[1024];
712 ssize_t r = read(sock, buf2, 1024);
713 if(r > 0) {
714 cxBufferWrite(buf2, 1, r, &buf);
715 }
716 }
717
718 // because we are using chunked transfer encoding, the result buffer
719 // (buf) should contain more than 128 bytes (additional chunk headers)
720 CX_TEST_ASSERT(buf.pos == 160);
721 // check for chunk headers
722 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space, 4), "10\r\n"));
723 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space+20, 4), "10\r\n"));
724 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space+40, 4), "10\r\n"));
725 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space+60, 4), "10\r\n"));
726 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space+80, 4), "10\r\n"));
727 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space+100, 4), "10\r\n"));
728 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space+120, 4), "10\r\n"));
729 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf.space+140, 4), "10\r\n"));
730
731 // change chunk size to 128
732 req.max_reads = 9999;
733 req.chunksize = 128;
734 while(req.cur_reads <= req.max_reads) {
735 int ret = client_send_request_body(client);
736 CX_TEST_ASSERT(!client->error);
737 char buf2[2048];
738 ssize_t r;
739 while((r = read(sock, buf2, 2048)) > 0) {
740 cxBufferWrite(buf2, 1, r, &buf);
741 }
742 if(ret == 0) {
743 break;
744 }
745 }
746 CX_TEST_ASSERT(req.cur_reads < req.max_reads);
747 CX_TEST_ASSERT(buf.size == 1084 + 3);
748
749 // check chunks
750 char testbuf[128];
751 memset(testbuf, 'x', 128);
752 cxstring x1 = cx_strn(buf.space + 296, 128);
753 CX_TEST_ASSERT(!cx_strcmp(x1, cx_strn(testbuf, 128)));
754 cxstring x2 = cx_strn(buf.space + 560, 128);
755 CX_TEST_ASSERT(!cx_strcmp(x2, cx_strn(testbuf, 128)));
756 cxstring x3 = cx_strn(buf.space + 824, 128);
757 CX_TEST_ASSERT(!cx_strcmp(x3, cx_strn(testbuf, 128)));
758 memset(testbuf, 'y', 128);
759 cxstring y1 = cx_strn(buf.space + 164, 128);
760 CX_TEST_ASSERT(!cx_strcmp(y1, cx_strn(testbuf, 128)));
761 cxstring z1 = cx_strn(buf.space + 428, 128);
762 memset(testbuf, 'z', 128);
763 CX_TEST_ASSERT(!cx_strcmp(z1, cx_strn(testbuf, 128)));
764
765 // cleanup
766 close(fds[0]);
767 close(fds[1]);
768 http_client_free(client);
769 cxBufferDestroy(&buf);
770 }
771 }
772
628 void http_client_add_tests(CxTestSuite *suite) { 773 void http_client_add_tests(CxTestSuite *suite) {
629 cx_test_register(suite, test_http_client_send_request); 774 cx_test_register(suite, test_http_client_send_request);
775 cx_test_register(suite, test_http_client_send_request_body_chunked);
630 cx_test_register(suite, test_http_client_io_simple); 776 cx_test_register(suite, test_http_client_io_simple);
631 } 777 }

mercurial