Add tests for connection reuse

This commit is contained in:
2025-04-20 18:01:41 +02:00
parent 3a4f4b5351
commit a86ded5035
5 changed files with 254 additions and 78 deletions
+158
View File
@@ -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__);
}