Add tests for connection reuse
This commit is contained in:
+34
-67
@@ -2,47 +2,8 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "test.h"
|
||||
#include "picohttpparser.h"
|
||||
#include "../tinyhttp.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// TYPES, MACROS, PROTOTYPES
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct {
|
||||
|
||||
int minor;
|
||||
|
||||
int status_code;
|
||||
TinyHTTPString status_text;
|
||||
|
||||
int num_headers;
|
||||
TinyHTTPHeader headers[TINYHTTP_HEADER_LIMIT];
|
||||
|
||||
char *body;
|
||||
int body_len;
|
||||
} Response;
|
||||
|
||||
// Memory function used to initialize TinyHTTPStream
|
||||
static void *memfunc(TinyHTTPMemoryFuncTag tag, void *ptr, int len, void *data);
|
||||
|
||||
// Moves the request "str" into the stream, checks that the stream
|
||||
// became ready and that it parsed the request correctly. When this
|
||||
// functions returns the stream is ready for a response.
|
||||
static void send_request(TinyHTTPStream *stream, const char *str);
|
||||
|
||||
// Copies into the "dst" buffer the output bytes from the stream
|
||||
// (up to "cap" bytes) and parses them as an HTTP response into
|
||||
// "res".
|
||||
static void recv_response(TinyHTTPStream *stream, Response *res, char *dst, int cap);
|
||||
|
||||
static int header_exists(Response *res, TinyHTTPString name);
|
||||
static int header_exists_with_value(Response *res, TinyHTTPString name, TinyHTTPString value);
|
||||
|
||||
#define TEST(X) {if (!(X)) { printf("Test failed at %s:%d\n", __FILE__, __LINE__); fflush(stdout); __builtin_trap(); }}
|
||||
|
||||
#define TEST_START printf("Test %s:%d\n", __FILE__, __LINE__);
|
||||
#define TEST_END
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// TEST CASES
|
||||
@@ -104,6 +65,23 @@ static void test_setreuse(void)
|
||||
TEST_END
|
||||
}
|
||||
|
||||
static void test_kill(void)
|
||||
{
|
||||
TinyHTTPStream stream;
|
||||
|
||||
TEST_START
|
||||
tinyhttp_stream_init(&stream, memfunc, NULL);
|
||||
|
||||
TEST(!(tinyhttp_stream_state(&stream) & TINYHTTP_STREAM_DIED));
|
||||
|
||||
tinyhttp_stream_kill(&stream);
|
||||
|
||||
TEST(tinyhttp_stream_state(&stream) & TINYHTTP_STREAM_DIED);
|
||||
|
||||
tinyhttp_stream_free(&stream);
|
||||
TEST_END
|
||||
}
|
||||
|
||||
static void
|
||||
test_recv_started_flag(void)
|
||||
{
|
||||
@@ -127,23 +105,6 @@ test_recv_started_flag(void)
|
||||
TEST_END
|
||||
}
|
||||
|
||||
static void test_kill(void)
|
||||
{
|
||||
TinyHTTPStream stream;
|
||||
|
||||
TEST_START
|
||||
tinyhttp_stream_init(&stream, memfunc, NULL);
|
||||
|
||||
TEST(!(tinyhttp_stream_state(&stream) & TINYHTTP_STREAM_DIED));
|
||||
|
||||
tinyhttp_stream_kill(&stream);
|
||||
|
||||
TEST(tinyhttp_stream_state(&stream) & TINYHTTP_STREAM_DIED);
|
||||
|
||||
tinyhttp_stream_free(&stream);
|
||||
TEST_END
|
||||
}
|
||||
|
||||
static void
|
||||
test_send_started_flag(void)
|
||||
{
|
||||
@@ -211,6 +172,16 @@ static void test_exchange(int reuse)
|
||||
TEST_END
|
||||
}
|
||||
|
||||
/*
|
||||
KEEP ALIVE TESTS
|
||||
no connection header in request
|
||||
invalid connection header in request
|
||||
keep-alive value in request
|
||||
close value in request
|
||||
|
||||
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// ENTRY POINT
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -224,6 +195,7 @@ int main(void)
|
||||
test_send_started_flag();
|
||||
test_exchange(0);
|
||||
test_exchange(1);
|
||||
test_reuse();
|
||||
printf("OK\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -232,8 +204,7 @@ int main(void)
|
||||
// Helper Functions
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void*
|
||||
memfunc(TinyHTTPMemoryFuncTag tag, void *ptr, int len, void *data)
|
||||
void *memfunc(TinyHTTPMemoryFuncTag tag, void *ptr, int len, void *data)
|
||||
{
|
||||
(void) data;
|
||||
switch (tag) {
|
||||
@@ -426,8 +397,7 @@ parse_response(TinyHTTPString txt, Response *res)
|
||||
res->body_len = 0; // TODO
|
||||
}
|
||||
|
||||
static void
|
||||
send_request(TinyHTTPStream *stream, const char *str)
|
||||
void send_request(TinyHTTPStream *stream, const char *str)
|
||||
{
|
||||
int received = buffer_into_stream(stream, str, strlen(str));
|
||||
TEST(received == (int) strlen(str));
|
||||
@@ -438,8 +408,7 @@ send_request(TinyHTTPStream *stream, const char *str)
|
||||
expect_request(stream, req);
|
||||
}
|
||||
|
||||
static void
|
||||
recv_response(TinyHTTPStream *stream, Response *res, char *dst, int cap)
|
||||
void recv_response(TinyHTTPStream *stream, Response *res, char *dst, int cap)
|
||||
{
|
||||
int len = stream_into_buffer(stream, dst, cap);
|
||||
|
||||
@@ -449,8 +418,7 @@ recv_response(TinyHTTPStream *stream, Response *res, char *dst, int cap)
|
||||
parse_response((TinyHTTPString) { dst, len }, res);
|
||||
}
|
||||
|
||||
static int
|
||||
header_exists(Response *res, TinyHTTPString name)
|
||||
int header_exists(Response *res, TinyHTTPString name)
|
||||
{
|
||||
for (int i = 0; i < res->num_headers; i++)
|
||||
if (tinyhttp_streqcase(res->headers[i].name, name))
|
||||
@@ -458,8 +426,7 @@ header_exists(Response *res, TinyHTTPString name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
header_exists_with_value(Response *res, TinyHTTPString name, TinyHTTPString value)
|
||||
int header_exists_with_value(Response *res, TinyHTTPString name, TinyHTTPString value)
|
||||
{
|
||||
for (int i = 0; i < res->num_headers; i++)
|
||||
if (tinyhttp_streqcase(res->headers[i].name, name))
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <stdio.h>
|
||||
#include "../tinyhttp.h"
|
||||
|
||||
typedef struct {
|
||||
|
||||
int minor;
|
||||
|
||||
int status_code;
|
||||
TinyHTTPString status_text;
|
||||
|
||||
int num_headers;
|
||||
TinyHTTPHeader headers[TINYHTTP_HEADER_LIMIT];
|
||||
|
||||
char *body;
|
||||
int body_len;
|
||||
} Response;
|
||||
|
||||
// Memory function used to initialize TinyHTTPStream
|
||||
// TODO: Maybe choose a better name for this
|
||||
void *memfunc(TinyHTTPMemoryFuncTag tag, void *ptr, int len, void *data);
|
||||
|
||||
// Moves the request "str" into the stream, checks that the stream
|
||||
// became ready and that it parsed the request correctly. When this
|
||||
// functions returns the stream is ready for a response.
|
||||
void send_request(TinyHTTPStream *stream, const char *str);
|
||||
|
||||
// Copies into the "dst" buffer the output bytes from the stream
|
||||
// (up to "cap" bytes) and parses them as an HTTP response into
|
||||
// "res".
|
||||
void recv_response(TinyHTTPStream *stream, Response *res, char *dst, int cap);
|
||||
|
||||
int header_exists(Response *res, TinyHTTPString name);
|
||||
int header_exists_with_value(Response *res, TinyHTTPString name, TinyHTTPString value);
|
||||
|
||||
#define TEST(X) {if (!(X)) { printf("Test failed at %s:%d\n", __FILE__, __LINE__); fflush(stdout); __builtin_trap(); }}
|
||||
|
||||
#define TEST_START printf("Test %s:%d\n", __FILE__, __LINE__);
|
||||
#define TEST_START2(file, line) printf("Test %s:%d\n", (file), (line));
|
||||
#define TEST_END
|
||||
|
||||
void test_reuse(void);
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "test.h"
|
||||
|
||||
// This file tests the behavior of the "Connection" header
|
||||
|
||||
// HTTP/1.1, No "Connection" header
|
||||
#define REQUEST_HTTP1_1_NO_CONNECTION_HEADER \
|
||||
"GET / HTTP/1.1\r\n" \
|
||||
"Host: 127.0.0.1:8080\r\n" \
|
||||
"\r\n"
|
||||
|
||||
// HTTP/1.1, "Connection" header with invalid value
|
||||
#define REQUEST_HTTP1_1_CONNECTION_INVALID \
|
||||
"GET / HTTP/1.1\r\n" \
|
||||
"Host: 127.0.0.1:8080\r\n" \
|
||||
"Connection: zzz\r\n" \
|
||||
"\r\n"
|
||||
|
||||
// HTTP/1.1, "Connection: Keep-Alive" header
|
||||
#define REQUEST_HTTP1_1_CONNECTION_KEEPALIVE \
|
||||
"GET / HTTP/1.1\r\n" \
|
||||
"Host: 127.0.0.1:8080\r\n" \
|
||||
"Connection: Keep-Alive\r\n" \
|
||||
"\r\n"
|
||||
|
||||
// HTTP/1.1, "Connection: Close" header
|
||||
#define REQUEST_HTTP1_1_CONNECTION_CLOSE \
|
||||
"GET / HTTP/1.1\r\n" \
|
||||
"Host: 127.0.0.1:8080\r\n" \
|
||||
"Connection: Close\r\n" \
|
||||
"\r\n"
|
||||
|
||||
// HTTP/1.0, No "Connection" header
|
||||
#define REQUEST_HTTP1_0_NO_CONNECTION_HEADER \
|
||||
"GET / HTTP/1.0\r\n" \
|
||||
"\r\n"
|
||||
|
||||
// HTTP/1.0, "Connection" header with invalid value
|
||||
#define REQUEST_HTTP1_0_CONNECTION_INVALID \
|
||||
"GET / HTTP/1.0\r\n" \
|
||||
"Connection: zzz\r\n" \
|
||||
"\r\n"
|
||||
|
||||
// HTTP/1.0, "Connection: Keep-Alive" header
|
||||
#define REQUEST_HTTP1_0_CONNECTION_KEEPALIVE \
|
||||
"GET / HTTP/1.0\r\n" \
|
||||
"Connection: Keep-Alive\r\n" \
|
||||
"\r\n"
|
||||
|
||||
// HTTP/1.0, "Connection: Close" header
|
||||
#define REQUEST_HTTP1_0_CONNECTION_CLOSE \
|
||||
"GET / HTTP/1.0\r\n" \
|
||||
"Connection: Close\r\n" \
|
||||
"\r\n"
|
||||
|
||||
typedef enum {
|
||||
DONT_REUSE,
|
||||
ALLOW_REUSE,
|
||||
} ServerReuse;
|
||||
|
||||
typedef enum {
|
||||
INCONNHDR_NONE,
|
||||
INCONNHDR_KEEPALIVE,
|
||||
INCONNHDR_CLOSE,
|
||||
INCONNHDR_INVALID,
|
||||
} InputConnectionHeader;
|
||||
|
||||
typedef enum {
|
||||
OUTCONNHDR_MISSING_OR_KEEPALIVE,
|
||||
OUTCONNHDR_CLOSE,
|
||||
} OutputConnectionHeader;
|
||||
|
||||
static void test_reuse_helper(
|
||||
ServerReuse server_reuse,
|
||||
InputConnectionHeader input_conn_header,
|
||||
int input_http_minor_version,
|
||||
OutputConnectionHeader expect_output_conn_header,
|
||||
int expect_output_http_minor_version,
|
||||
const char *file, int line)
|
||||
{
|
||||
TinyHTTPStream stream;
|
||||
|
||||
TEST_START2(file, line)
|
||||
tinyhttp_stream_init(&stream, memfunc, NULL);
|
||||
tinyhttp_stream_setreuse(&stream, server_reuse);
|
||||
|
||||
if (input_http_minor_version == 1) {
|
||||
switch (input_conn_header) {
|
||||
case INCONNHDR_NONE : send_request(&stream, REQUEST_HTTP1_1_NO_CONNECTION_HEADER); break;
|
||||
case INCONNHDR_KEEPALIVE: send_request(&stream, REQUEST_HTTP1_1_CONNECTION_KEEPALIVE); break;
|
||||
case INCONNHDR_CLOSE : send_request(&stream, REQUEST_HTTP1_1_CONNECTION_CLOSE); break;
|
||||
case INCONNHDR_INVALID : send_request(&stream, REQUEST_HTTP1_1_CONNECTION_INVALID); break;
|
||||
}
|
||||
} else {
|
||||
switch (input_conn_header) {
|
||||
case INCONNHDR_NONE : send_request(&stream, REQUEST_HTTP1_0_NO_CONNECTION_HEADER); break;
|
||||
case INCONNHDR_KEEPALIVE: send_request(&stream, REQUEST_HTTP1_0_CONNECTION_KEEPALIVE); break;
|
||||
case INCONNHDR_CLOSE : send_request(&stream, REQUEST_HTTP1_0_CONNECTION_CLOSE); break;
|
||||
case INCONNHDR_INVALID : send_request(&stream, REQUEST_HTTP1_0_CONNECTION_INVALID); break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build a dummy response
|
||||
tinyhttp_stream_response_status(&stream, 200);
|
||||
tinyhttp_stream_response_send(&stream);
|
||||
|
||||
Response res;
|
||||
char buf[1<<10];
|
||||
recv_response(&stream, &res, buf, sizeof(buf));
|
||||
|
||||
TEST(res.minor == expect_output_http_minor_version);
|
||||
|
||||
int state = tinyhttp_stream_state(&stream);
|
||||
switch (expect_output_conn_header) {
|
||||
|
||||
case OUTCONNHDR_MISSING_OR_KEEPALIVE:
|
||||
{
|
||||
TEST(!header_exists(&res, TINYHTTP_STRING("Connection"))
|
||||
|| header_exists_with_value(&res, TINYHTTP_STRING("Connection"), TINYHTTP_STRING("Keep-Alive")));
|
||||
TEST((state & TINYHTTP_STREAM_DIED) == 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case OUTCONNHDR_CLOSE:
|
||||
{
|
||||
TEST(header_exists_with_value(&res, TINYHTTP_STRING("Connection"), TINYHTTP_STRING("Close")));
|
||||
TEST(state & TINYHTTP_STREAM_DIED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
tinyhttp_stream_free(&stream);
|
||||
TEST_END
|
||||
}
|
||||
|
||||
void test_reuse(void)
|
||||
{
|
||||
// Relevant specs:
|
||||
// RFC 9112, Section 9.3. (Persistence)
|
||||
// RFC 9112, Section 9.6. (Tear-down)
|
||||
// RFC 9110, Section 7.6.1. (Connection)
|
||||
|
||||
test_reuse_helper(DONT_REUSE, INCONNHDR_NONE, 1, OUTCONNHDR_CLOSE, 1, __FILE__, __LINE__);
|
||||
test_reuse_helper(DONT_REUSE, INCONNHDR_KEEPALIVE, 1, OUTCONNHDR_CLOSE, 1, __FILE__, __LINE__);
|
||||
test_reuse_helper(DONT_REUSE, INCONNHDR_CLOSE, 1, OUTCONNHDR_CLOSE, 1, __FILE__, __LINE__);
|
||||
test_reuse_helper(DONT_REUSE, INCONNHDR_INVALID, 1, OUTCONNHDR_CLOSE, 1, __FILE__, __LINE__);
|
||||
test_reuse_helper(ALLOW_REUSE, INCONNHDR_NONE, 1, OUTCONNHDR_MISSING_OR_KEEPALIVE, 1, __FILE__, __LINE__);
|
||||
test_reuse_helper(ALLOW_REUSE, INCONNHDR_KEEPALIVE, 1, OUTCONNHDR_MISSING_OR_KEEPALIVE, 1, __FILE__, __LINE__);
|
||||
test_reuse_helper(ALLOW_REUSE, INCONNHDR_CLOSE, 1, OUTCONNHDR_CLOSE, 1, __FILE__, __LINE__);
|
||||
test_reuse_helper(ALLOW_REUSE, INCONNHDR_INVALID, 1, OUTCONNHDR_MISSING_OR_KEEPALIVE, 1, __FILE__, __LINE__);
|
||||
test_reuse_helper(DONT_REUSE, INCONNHDR_NONE, 0, OUTCONNHDR_CLOSE, 0, __FILE__, __LINE__);
|
||||
test_reuse_helper(DONT_REUSE, INCONNHDR_KEEPALIVE, 0, OUTCONNHDR_CLOSE, 0, __FILE__, __LINE__);
|
||||
test_reuse_helper(DONT_REUSE, INCONNHDR_CLOSE, 0, OUTCONNHDR_CLOSE, 0, __FILE__, __LINE__);
|
||||
test_reuse_helper(DONT_REUSE, INCONNHDR_INVALID, 0, OUTCONNHDR_CLOSE, 0, __FILE__, __LINE__);
|
||||
test_reuse_helper(ALLOW_REUSE, INCONNHDR_NONE, 0, OUTCONNHDR_CLOSE, 0, __FILE__, __LINE__);
|
||||
test_reuse_helper(ALLOW_REUSE, INCONNHDR_KEEPALIVE, 0, OUTCONNHDR_CLOSE, 0, __FILE__, __LINE__);
|
||||
test_reuse_helper(ALLOW_REUSE, INCONNHDR_CLOSE, 0, OUTCONNHDR_CLOSE, 0, __FILE__, __LINE__);
|
||||
test_reuse_helper(ALLOW_REUSE, INCONNHDR_INVALID, 0, OUTCONNHDR_CLOSE, 0, __FILE__, __LINE__);
|
||||
}
|
||||
Reference in New Issue
Block a user