src/server/proxy/httpclient.c

changeset 686
9adf57ddcd0f
parent 684
48da20bde908
child 687
4bded456b4a7
equal deleted inserted replaced
685:349d62bfae29 686:9adf57ddcd0f
482 headers = NULL; 482 headers = NULL;
483 break; 483 break;
484 } 484 }
485 } 485 }
486 } 486 }
487
488 if(headers) {
489 headers = headers->next;
490 }
487 } 491 }
488 492
489 if(contentlength > 0 || chunkedtransferenc) { 493 if(contentlength > 0 || chunkedtransferenc) {
490 IOStream *fd = Sysstream_new(NULL, client->socketfd); 494 IOStream *fd = Sysstream_new(NULL, client->socketfd);
491 if(!fd) { 495 if(!fd) {
620 624
621 static ssize_t test_response_body_write(HttpClient *client, void *buf, size_t size, void *userdata) { 625 static ssize_t test_response_body_write(HttpClient *client, void *buf, size_t size, void *userdata) {
622 TestResponse *test = userdata; 626 TestResponse *test = userdata;
623 cxBufferWrite(buf, 1, size, test->response); 627 cxBufferWrite(buf, 1, size, test->response);
624 return size; 628 return size;
625 }
626
627 static CX_TEST(test_http_client_io_simple) {
628 CX_TEST_DO {
629 EventHandler dummy;
630 HttpClient *client = http_client_new(&dummy);
631
632 int fds[2];
633 util_socketpair(fds);
634 util_socket_setnonblock(fds[0], 1);
635 util_socket_setnonblock(fds[1], 1);
636 client->socketfd = fds[0];
637 int sock = fds[1];
638
639 // setup client
640 http_client_set_uri(client, "/test/uri/");
641 http_client_set_method(client, "GET");
642 http_client_add_request_header(client, cx_mutstr("Host"), cx_mutstr("localhost"));
643 http_client_add_request_header(client, cx_mutstr("Test1"), cx_mutstr("value1"));
644 http_client_add_request_header(client, cx_mutstr("Test2"), cx_mutstr("value2"));
645 create_req_buffer(client);
646
647 size_t req_header_len = client->transfer_buffer_len;
648
649 // response buffer
650 CxBuffer buf;
651 cxBufferInit(&buf, cxDefaultAllocator, NULL, 1024, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
652
653 TestResponse testr = { 0 };
654 testr.response = &buf;
655 client->response_start = test_response_start;
656 client->response_start_userdata = &testr;
657 client->response_body_write = test_response_body_write;
658 client->response_body_write_userdata = &testr;
659
660 // test IO
661 Event event;
662 event.cookie = client;
663 int ret = client_io(&dummy, &event);
664 CX_TEST_ASSERT(!client->error);
665 CX_TEST_ASSERT(ret == 1);
666
667 // do IO and read request until the header is processed
668 size_t req_header_pos = 0;
669 char req_buf[4];
670 while(req_header_pos < req_header_len) {
671 ssize_t r = read(sock, req_buf, 4);
672 if(r == 0) {
673 break;
674 }
675 CX_TEST_ASSERT(r > 0);
676 req_header_pos += r;
677 ret = client_io(&dummy, &event);
678 CX_TEST_ASSERT(!client->error);
679 CX_TEST_ASSERT(ret == 1);
680 }
681 CX_TEST_ASSERT(req_header_pos == req_header_len);
682
683 char *response_str =
684 "HTTP/1.1 200 OK\r\n"
685 "Host: localhost\r\n"
686 "Content-length: 13\r\n"
687 "\r\n"
688 "Hello World!\n";
689 size_t response_str_len = strlen(response_str);
690 size_t response_str_pos = 0;
691
692 // send response and do IO
693 while(response_str_pos < response_str_len) {
694 size_t len = response_str_len - response_str_pos;
695 if(len > 3) {
696 //len = 3;
697 }
698 ssize_t w = write(sock, response_str + response_str_pos, len);
699 if(w == 0) {
700 break;
701 }
702 CX_TEST_ASSERT(w > 0);
703 response_str_pos += w;
704
705 ret = client_io(&dummy, &event);
706
707 CX_TEST_ASSERT(!client->error);
708 }
709 CX_TEST_ASSERT(response_str_pos == response_str_len);
710 CX_TEST_ASSERT(testr.status == 200);
711 CX_TEST_ASSERT(testr.msg);
712 CX_TEST_ASSERT(!strcmp(testr.msg, "OK"));
713 CX_TEST_ASSERT(testr.response->size == 13);
714 CX_TEST_ASSERT(!cx_strcmp(cx_strn(testr.response->space, testr.response->size), "Hello World!\n"));
715
716 // cleanup
717 free(testr.msg);
718 close(fds[0]);
719 close(fds[1]);
720 http_client_free(client);
721 cxBufferDestroy(&buf);
722 }
723 } 629 }
724 630
725 631
726 typedef struct TestRequestBody { 632 typedef struct TestRequestBody {
727 char *content; 633 char *content;
857 http_client_free(client); 763 http_client_free(client);
858 cxBufferDestroy(&buf); 764 cxBufferDestroy(&buf);
859 } 765 }
860 } 766 }
861 767
768 static CX_TEST_SUBROUTINE(test_read_response, cxstring response_str, CxBuffer *response_body) {
769 EventHandler dummy;
770 HttpClient *client = http_client_new(&dummy);
771 create_req_buffer(client);
772 client->req_content_length = -1;
773
774 int fds[2];
775 util_socketpair(fds);
776 util_socket_setnonblock(fds[0], 1);
777 util_socket_setnonblock(fds[1], 1);
778 client->socketfd = fds[0];
779 int sock = fds[1];
780
781 TestResponse testr = { 0 };
782 testr.response = response_body;
783 client->response_body_write = test_response_body_write;
784 client->response_body_write_userdata = &testr;
785
786 // test
787
788 size_t response_pos = 0;
789 while(response_pos < response_str.length) {
790 size_t nbytes = response_str.length - response_pos;
791 ssize_t w = write(sock, response_str.ptr + response_pos, nbytes);
792 if(w > 0) {
793 response_pos += w;
794 }
795
796 if(!client->response_header_complete) {
797 int ret = client_read_response_header(client);
798 CX_TEST_ASSERT(client->error == 0);
799 if(ret == 1) {
800 continue;
801 }
802 }
803
804 if(response_body != NULL) {
805 CX_TEST_ASSERT(client->stream != NULL);
806
807 int ret = client_read_response_body(client);
808 CX_TEST_ASSERT(client->error == 0);
809 if(ret == 1) {
810 continue;
811 }
812 } else {
813 CX_TEST_ASSERT(client->stream == NULL);
814 }
815
816 break;
817 }
818
819 // cleanup
820 close(fds[0]);
821 close(fds[1]);
822 http_client_free(client);
823 }
824
862 static CX_TEST(test_http_client_read_response_head) { 825 static CX_TEST(test_http_client_read_response_head) {
863 CX_TEST_DO { 826 CX_TEST_DO {
864 EventHandler dummy;
865 HttpClient *client = http_client_new(&dummy);
866 create_req_buffer(client);
867 client->req_content_length = -1;
868
869 int fds[2];
870 util_socketpair(fds);
871 util_socket_setnonblock(fds[0], 1);
872 util_socket_setnonblock(fds[1], 1);
873 client->socketfd = fds[0];
874 int sock = fds[1];
875
876 // test
877 char *response_str = 827 char *response_str =
878 "HTTP/1.1 204 OK\r\n" 828 "HTTP/1.1 204 OK\r\n"
879 "Host: localhost\r\n" 829 "Host: localhost\r\n"
880 "Content-length: 0\r\n" 830 "Content-length: 0\r\n"
881 "\r\n"; 831 "\r\n";
882 832
883 size_t response_len = strlen(response_str); 833 CX_TEST_CALL_SUBROUTINE(test_read_response, cx_str(response_str), NULL);
884 size_t response_pos = 0; 834
885 while(response_pos < response_len) { 835 response_str =
886 size_t nbytes = response_len - response_pos; 836 "HTTP/1.1 204 OK\r\n"
887 ssize_t w = write(sock, response_str + response_pos, nbytes); 837 "Host: localhost\r\n"
888 if(w > 0) { 838 "\r\n";
889 response_pos += w; 839
890 } 840 CX_TEST_CALL_SUBROUTINE(test_read_response, cx_str(response_str), NULL);
891 841 }
892 if(!client->response_header_complete) { 842 }
893 int ret = client_read_response_header(client); 843
894 CX_TEST_ASSERT(client->error == 0); 844 static CX_TEST(test_http_client_read_response_ctlen) {
895 if(ret == 1) { 845 CX_TEST_DO {
896 continue; 846 char *response_str =
897 } 847 "HTTP/1.1 200 OK\r\n"
898 848 "Host: localhost\r\n"
899 CX_TEST_ASSERT(client->stream == NULL); 849 "Content-length: 13\r\n"
900 } 850 "\r\n"
901 851 "Hello World!\n";
852 CxBuffer *buf = cxBufferCreate(NULL, NULL, 1024, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
853
854 CX_TEST_CALL_SUBROUTINE(test_read_response, cx_str(response_str), buf);
855 CX_TEST_ASSERT(buf->size == 13);
856 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf->space, buf->size), "Hello World!\n"));
857
858 cxBufferFree(buf);
859 }
860 }
861
862 static CX_TEST(test_http_client_read_response_ctlen_big) {
863 CX_TEST_DO {
864 // create response body
865 size_t len = 1024*1024*32;
866 char *response_str = malloc(len);
867 char *str = response_str;
868 for(size_t i=0;i<len;i+=sizeof(int)) {
869 int *p = (int*)(str+i);
870 *p = rand();
871 }
872 cxstring body = cx_strn(response_str, len);
873
874 // create request string
875 CxBuffer *req = cxBufferCreate(NULL, NULL, 1024, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
876 cxBufferPutString(req,
877 "HTTP/1.1 200 OK\r\n"
878 "Host: localhost\r\n"
879 "Content-length: ");
880 char ctlen[32];
881 snprintf(ctlen, 32, "%d\r\n\r\n", (int)len);
882 cxBufferPutString(req, ctlen);
883 cxBufferPutString(req, body);
884 cxBufferTerminate(req);
885
886 // response buffer
887 CxBuffer *buf = cxBufferCreate(NULL, NULL, 1024, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
888
889 // test
890 CX_TEST_CALL_SUBROUTINE(test_read_response, cx_strn(req->space, req->size), buf);
891 CX_TEST_ASSERT(buf->size == len);
892 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf->space, buf->size), body));
893
894 cxBufferFree(req);
895 cxBufferFree(buf);
896 }
897 }
898
899 static CX_TEST_SUBROUTINE(test_http_client_io, const char *response_str, int status_code, const char *msg, CxBuffer *out_buf, size_t write_blocksz) {
900 EventHandler dummy;
901 HttpClient *client = http_client_new(&dummy);
902
903 int fds[2];
904 util_socketpair(fds);
905 util_socket_setnonblock(fds[0], 1);
906 util_socket_setnonblock(fds[1], 1);
907 client->socketfd = fds[0];
908 int sock = fds[1];
909
910 // setup client
911 http_client_set_uri(client, "/test/uri/");
912 http_client_set_method(client, "GET");
913 http_client_add_request_header(client, cx_mutstr("Host"), cx_mutstr("localhost"));
914 http_client_add_request_header(client, cx_mutstr("Test1"), cx_mutstr("value1"));
915 http_client_add_request_header(client, cx_mutstr("Test2"), cx_mutstr("value2"));
916 create_req_buffer(client);
917
918 size_t req_header_len = client->transfer_buffer_len;
919
920 TestResponse testr = { 0 };
921 testr.response = out_buf;
922 client->response_start = test_response_start;
923 client->response_start_userdata = &testr;
924 client->response_body_write = test_response_body_write;
925 client->response_body_write_userdata = &testr;
926
927 // test IO
928 Event event;
929 event.cookie = client;
930 int ret = client_io(&dummy, &event);
931 CX_TEST_ASSERT(!client->error);
932 CX_TEST_ASSERT(ret == 1);
933
934 // do IO and read request until the header is processed
935 size_t req_header_pos = 0;
936 char req_buf[4];
937 while(req_header_pos < req_header_len) {
938 ssize_t r = read(sock, req_buf, 4);
939 if(r == 0) {
902 break; 940 break;
903 } 941 }
904 942 CX_TEST_ASSERT(r > 0);
905 // cleanup 943 req_header_pos += r;
906 close(fds[0]); 944 ret = client_io(&dummy, &event);
907 close(fds[1]); 945 CX_TEST_ASSERT(!client->error);
908 http_client_free(client); 946 CX_TEST_ASSERT(ret == 1);
909 } 947 }
948 CX_TEST_ASSERT(req_header_pos == req_header_len);
949
950 size_t response_str_len = strlen(response_str);
951 size_t response_str_pos = 0;
952
953 // send response and do IO
954 while(response_str_pos < response_str_len) {
955 size_t len = response_str_len - response_str_pos;
956 if(len > write_blocksz) {
957 len = write_blocksz;
958 }
959 ssize_t w = write(sock, response_str + response_str_pos, len);
960 if(w == 0) {
961 break;
962 }
963 CX_TEST_ASSERT(w > 0);
964 response_str_pos += w;
965
966 ret = client_io(&dummy, &event);
967
968 CX_TEST_ASSERT(!client->error);
969 }
970 CX_TEST_ASSERT(response_str_pos == response_str_len);
971 CX_TEST_ASSERT(testr.status == status_code);
972 CX_TEST_ASSERT(testr.msg);
973 CX_TEST_ASSERT(!strcmp(testr.msg, msg));
974
975 // cleanup
976 free(testr.msg);
977 close(fds[0]);
978 close(fds[1]);
979 http_client_free(client);
980 }
981
982 static CX_TEST(test_http_client_io_simple) {
983 char *response_str =
984 "HTTP/1.1 200 OK\r\n"
985 "Host: localhost\r\n"
986 "Content-length: 13\r\n"
987 "\r\n"
988 "Hello World!\n";
989 CxBuffer *buf = cxBufferCreate(NULL, NULL, 1024, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
990
991 CX_TEST_DO {
992 CX_TEST_CALL_SUBROUTINE(test_http_client_io, response_str, 200, "OK", buf, 1024);
993 CX_TEST_ASSERT(buf->size == 13);
994 CX_TEST_ASSERT(!cx_strcmp(cx_strn(buf->space, buf->size), "Hello World!\n"));
995 }
996
997 cxBufferFree(buf);
910 } 998 }
911 999
912 void http_client_add_tests(CxTestSuite *suite) { 1000 void http_client_add_tests(CxTestSuite *suite) {
913 cx_test_register(suite, test_http_client_send_request); 1001 cx_test_register(suite, test_http_client_send_request);
914 cx_test_register(suite, test_http_client_send_request_body_chunked); 1002 cx_test_register(suite, test_http_client_send_request_body_chunked);
915 cx_test_register(suite, test_http_client_read_response_head); 1003 cx_test_register(suite, test_http_client_read_response_head);
1004 cx_test_register(suite, test_http_client_read_response_ctlen);
1005 cx_test_register(suite, test_http_client_read_response_ctlen_big);
916 cx_test_register(suite, test_http_client_io_simple); 1006 cx_test_register(suite, test_http_client_io_simple);
917 } 1007 }

mercurial