| 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 } |