diff --git a/examples/stream_api_blocking.c b/examples/stream_api_blocking.c new file mode 100644 index 0000000..fcb9f03 --- /dev/null +++ b/examples/stream_api_blocking.c @@ -0,0 +1,67 @@ +#include "../tinyhttp.h" + +// TODO: Complete this example + +int main(void) +{ + SOCKET listen_fd = socket(AF_INET, SOCK_STREAM, 0); + if (listen_fd == INVALID_SOCKET) + return -1; + + for (;;) { + + SOCKET client_fd = accept(listen_fd, NULL, NULL); + if (client_fd == INVALID_SOCKET) + continue; + + TinyHTTPStream stream; + tinyhttp_stream_init(&stream, memfunc, NULL); + + int state = tinyhttp_stream_state(&stream); + while (!(state & TINYHTTP_STREAM_READY)) { + + ASSERT(state & TINYHTTP_STREAM_RECV); + + char *dst; + ptrdiff_t cap; + + dst = tinyhttp_stream_recv_buf(&stream, &cap); + + int ret = recv(client_fd, dst, cap, 0); + if (ret < 0) { + // TODO + } + + tinyhttp_stream_recv_ack(&stream, ret); + state = tinyhttp_stream_state(&stream); + } + + TinyHTTPRequest *request = tinyhttp_stream_request(&stream); + + tinyhttp_stream_response_status(&stream, 200); + tinyhttp_stream_response_body(&stream, "Hello, world!", -1); + tinyhttp_stream_response_send(&stream); + + while (state & TINYHTTP_STREAM_SEND) { + + char *src; + ptrdiff_t len; + + src = tinyhttp_stream_send_buf(&stream, &len); + + int ret = send(client_fd, src, len, 0); + if (ret < 0) { + // TODO + } + + tinyhttp_stream_send_ack(&stream, ret); + state = tinyhttp_stream_state(&stream); + } + + tinyhttp_stream_free(&stream); + CLOSESOCKET(client_fd); + } + + CLOSESOCKET(listen_fd); + return 0; +} \ No newline at end of file diff --git a/tests/test.c b/tests/test.c index ad27d06..4e97bb0 100644 --- a/tests/test.c +++ b/tests/test.c @@ -254,7 +254,7 @@ expect_request(TinyHTTPStream *stream, TinyHTTPRequest expreq) } static int -parse_request(TinyHTTPString txt, TinyHTTPRequest *req) +parse_request(TinyHTTPString txt, TinyHTTPRequest *req, char *buf, int max) { const char *method; size_t method_len; @@ -274,7 +274,7 @@ parse_request(TinyHTTPString txt, TinyHTTPRequest *req) &minor, headers, &num_headers, 0); - TEST(ret == txt.len); + ptrdiff_t head_len = ret; if (method_len == 3 && !memcmp("GET", method, 3)) { req->method = TINYHTTP_METHOD_GET; @@ -299,9 +299,39 @@ parse_request(TinyHTTPString txt, TinyHTTPRequest *req) }; } - req->body = NULL; // TODO - req->body_len = 0; // TODO + int transfer_encoding_index = tinyhttp_findheader(req, TINYHTTP_STRING("Transfer-Encoding")); + if (transfer_encoding_index != -1) { + // TODO: For now, we consider request as chunked just for having the Transfer-Encoding header + if (txt.len - head_len > max) + return -1; + memcpy(buf, txt.ptr + head_len, txt.len - head_len); + + struct phr_chunked_decoder decoder; + memset(&decoder, 0, sizeof(decoder)); + decoder.consume_trailer = 1; // Process any trailing headers + + // Decode the chunked body + size_t decoded_size = txt.len - head_len; + ssize_t ret = phr_decode_chunked(&decoder, buf, &decoded_size); + if (ret < 0) + return -1; + + req->body = buf; + req->body_len = decoded_size; + return 0; + } + + int content_length_index = tinyhttp_findheader(req, TINYHTTP_STRING("Content-Length")); + if (content_length_index != -1) { + + __builtin_trap(); // TODO + + return 0; + } + + req->body = NULL; + req->body_len = 0; return 0; } @@ -351,7 +381,8 @@ void send_request(TinyHTTPStream *stream, const char *str) TEST(received == (int) strlen(str)); TinyHTTPRequest req; - TEST(parse_request((TinyHTTPString) {str, strlen(str)}, &req) == 0); + char buf[1<<12]; + TEST(parse_request((TinyHTTPString) {str, strlen(str)}, &req, buf, sizeof(buf)) == 0); expect_request(stream, req); } diff --git a/tests/test_chunking.c b/tests/test_chunking.c index a60261e..d452117 100644 --- a/tests/test_chunking.c +++ b/tests/test_chunking.c @@ -2,5 +2,43 @@ void test_chunking(void) { - // TODO + { + TinyHTTPStream stream; + + TEST_START + tinyhttp_stream_init(&stream, memfunc, NULL); + + // Send request + send_request(&stream, + "POST / HTTP/1.1\r\n" + "Host: 127.0.0.1:8080\r\n" + "User-Agent: curl/7.81.0\r\n" + "Accept: */*\r\n" + "Transfer-Encoding: Chunked\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n" + "d\r\n" + "Hello, world!\r\n" + "0\r\n" + "\r\n" + ); + + // Build response + tinyhttp_stream_response_status(&stream, 200); + tinyhttp_stream_response_send(&stream); + + // Receive response + char buf[1<<12]; + Response res; + recv_response(&stream, &res, buf, sizeof(buf)); + + // We expect the status line: + // HTTP/1.1 200 OK + TEST(res.minor == 1); + TEST(res.status_code == 200); + TEST(tinyhttp_streq(res.status_text, TINYHTTP_STRING("OK"))); + + tinyhttp_stream_free(&stream); + TEST_END + } } \ No newline at end of file diff --git a/tinyhttp.c b/tinyhttp.c index 02874f8..4a4117c 100644 --- a/tinyhttp.c +++ b/tinyhttp.c @@ -358,8 +358,7 @@ parse_request_head(char *src, ptrdiff_t len, TinyHTTPRequest *req) return cur; } -static int -find_header(TinyHTTPRequest *req, TinyHTTPString name) +int tinyhttp_findheader(TinyHTTPRequest *req, TinyHTTPString name) { for (int i = 0; i < req->num_headers; i++) { TinyHTTPHeader *header = &req->headers[i]; @@ -481,7 +480,7 @@ parse_request(char *src, ptrdiff_t len, return ret; ptrdiff_t head_len = ret; - int transfer_encoding_index = find_header(req, TINYHTTP_STRING("Transfer-Encoding")); + int transfer_encoding_index = tinyhttp_findheader(req, TINYHTTP_STRING("Transfer-Encoding")); if (transfer_encoding_index >= 0) { TinyHTTPHeader *header = &req->headers[transfer_encoding_index]; @@ -569,7 +568,7 @@ parse_request(char *src, ptrdiff_t len, return cur; } - int content_length_index = find_header(req, TINYHTTP_STRING("Content-Length")); + int content_length_index = tinyhttp_findheader(req, TINYHTTP_STRING("Content-Length")); if (content_length_index >= 0) { TinyHTTPHeader *header = &req->headers[content_length_index]; @@ -1204,7 +1203,7 @@ should_keep_alive(TinyHTTPStream *stream) // TODO: This assumes "Connection" can only hold a single token, // but this is not true. - int i = find_header(req, TINYHTTP_STRING("Connection")); + int i = tinyhttp_findheader(req, TINYHTTP_STRING("Connection")); if (i >= 0 && tinyhttp_streqcase(req->headers[i].value, TINYHTTP_STRING("Close"))) return 0; diff --git a/tinyhttp.h b/tinyhttp.h index 5a52893..00637d1 100644 --- a/tinyhttp.h +++ b/tinyhttp.h @@ -191,6 +191,8 @@ void tinyhttp_printstate_(int state, const char *file, const char *line); // TODO: Comment #define tinyhttp_printstate(state) tinyhttp_printstate_(state, __FILE__, TINYHTTP_LINESTR) +int tinyhttp_findheader(TinyHTTPRequest *req, TinyHTTPString name); + // Initializes an HTTP stream // // TODO: Comment on memfunc