Basic chunk encoding test, draft of an example using the stream API with blocking sockets
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
+36
-5
@@ -254,7 +254,7 @@ expect_request(TinyHTTPStream *stream, TinyHTTPRequest expreq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
parse_request(TinyHTTPString txt, TinyHTTPRequest *req)
|
parse_request(TinyHTTPString txt, TinyHTTPRequest *req, char *buf, int max)
|
||||||
{
|
{
|
||||||
const char *method;
|
const char *method;
|
||||||
size_t method_len;
|
size_t method_len;
|
||||||
@@ -274,7 +274,7 @@ parse_request(TinyHTTPString txt, TinyHTTPRequest *req)
|
|||||||
&minor,
|
&minor,
|
||||||
headers, &num_headers,
|
headers, &num_headers,
|
||||||
0);
|
0);
|
||||||
TEST(ret == txt.len);
|
ptrdiff_t head_len = ret;
|
||||||
|
|
||||||
if (method_len == 3 && !memcmp("GET", method, 3)) {
|
if (method_len == 3 && !memcmp("GET", method, 3)) {
|
||||||
req->method = TINYHTTP_METHOD_GET;
|
req->method = TINYHTTP_METHOD_GET;
|
||||||
@@ -299,9 +299,39 @@ parse_request(TinyHTTPString txt, TinyHTTPRequest *req)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
req->body = NULL; // TODO
|
int transfer_encoding_index = tinyhttp_findheader(req, TINYHTTP_STRING("Transfer-Encoding"));
|
||||||
req->body_len = 0; // TODO
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,7 +381,8 @@ void send_request(TinyHTTPStream *stream, const char *str)
|
|||||||
TEST(received == (int) strlen(str));
|
TEST(received == (int) strlen(str));
|
||||||
|
|
||||||
TinyHTTPRequest req;
|
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);
|
expect_request(stream, req);
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-1
@@ -2,5 +2,43 @@
|
|||||||
|
|
||||||
void test_chunking(void)
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+4
-5
@@ -358,8 +358,7 @@ parse_request_head(char *src, ptrdiff_t len, TinyHTTPRequest *req)
|
|||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int tinyhttp_findheader(TinyHTTPRequest *req, TinyHTTPString name)
|
||||||
find_header(TinyHTTPRequest *req, TinyHTTPString name)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < req->num_headers; i++) {
|
for (int i = 0; i < req->num_headers; i++) {
|
||||||
TinyHTTPHeader *header = &req->headers[i];
|
TinyHTTPHeader *header = &req->headers[i];
|
||||||
@@ -481,7 +480,7 @@ parse_request(char *src, ptrdiff_t len,
|
|||||||
return ret;
|
return ret;
|
||||||
ptrdiff_t head_len = 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) {
|
if (transfer_encoding_index >= 0) {
|
||||||
|
|
||||||
TinyHTTPHeader *header = &req->headers[transfer_encoding_index];
|
TinyHTTPHeader *header = &req->headers[transfer_encoding_index];
|
||||||
@@ -569,7 +568,7 @@ parse_request(char *src, ptrdiff_t len,
|
|||||||
return cur;
|
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) {
|
if (content_length_index >= 0) {
|
||||||
|
|
||||||
TinyHTTPHeader *header = &req->headers[content_length_index];
|
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,
|
// TODO: This assumes "Connection" can only hold a single token,
|
||||||
// but this is not true.
|
// 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")))
|
if (i >= 0 && tinyhttp_streqcase(req->headers[i].value, TINYHTTP_STRING("Close")))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|||||||
@@ -191,6 +191,8 @@ void tinyhttp_printstate_(int state, const char *file, const char *line);
|
|||||||
// TODO: Comment
|
// TODO: Comment
|
||||||
#define tinyhttp_printstate(state) tinyhttp_printstate_(state, __FILE__, TINYHTTP_LINESTR)
|
#define tinyhttp_printstate(state) tinyhttp_printstate_(state, __FILE__, TINYHTTP_LINESTR)
|
||||||
|
|
||||||
|
int tinyhttp_findheader(TinyHTTPRequest *req, TinyHTTPString name);
|
||||||
|
|
||||||
// Initializes an HTTP stream
|
// Initializes an HTTP stream
|
||||||
//
|
//
|
||||||
// TODO: Comment on memfunc
|
// TODO: Comment on memfunc
|
||||||
|
|||||||
Reference in New Issue
Block a user