From 87f45d153fc567d95a157c1fa41760ac8f7999eb Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 9 Nov 2023 20:13:41 +0200 Subject: [PATCH] tests: net: http_server: Add tests for the HTTP server Tests for HTTP server support. Signed-off-by: Emna Rekik Signed-off-by: Jukka Rissanen Signed-off-by: Robert Lubos --- tests/net/lib/http_server/common/prj.conf | 5 + .../net/lib/http_server/common/testcase.yaml | 4 +- .../net/lib/http_server/crime/CMakeLists.txt | 24 ++ tests/net/lib/http_server/crime/prj.conf | 54 +++ .../net/lib/http_server/crime/sections-rom.ld | 3 + .../net/lib/http_server/crime/src/index.html | 10 + tests/net/lib/http_server/crime/src/main.c | 139 ++++++ .../http_server/crime/src/not_found_page.html | 10 + tests/net/lib/http_server/crime/testcase.yaml | 16 + .../lib/http_server/prototype/CMakeLists.txt | 18 + tests/net/lib/http_server/prototype/prj.conf | 53 +++ .../lib/http_server/prototype/sections-rom.ld | 3 + .../net/lib/http_server/prototype/src/main.c | 402 ++++++++++++++++++ .../lib/http_server/prototype/testcase.yaml | 16 + tests/net/lib/http_server/tls/CMakeLists.txt | 54 +++ tests/net/lib/http_server/tls/prj.conf | 61 +++ tests/net/lib/http_server/tls/sections-rom.ld | 3 + tests/net/lib/http_server/tls/src/index.html | 10 + tests/net/lib/http_server/tls/src/main.c | 265 ++++++++++++ tests/net/lib/http_server/tls/testcase.yaml | 19 + 20 files changed, 1168 insertions(+), 1 deletion(-) create mode 100644 tests/net/lib/http_server/crime/CMakeLists.txt create mode 100644 tests/net/lib/http_server/crime/prj.conf create mode 100644 tests/net/lib/http_server/crime/sections-rom.ld create mode 100644 tests/net/lib/http_server/crime/src/index.html create mode 100644 tests/net/lib/http_server/crime/src/main.c create mode 100644 tests/net/lib/http_server/crime/src/not_found_page.html create mode 100644 tests/net/lib/http_server/crime/testcase.yaml create mode 100644 tests/net/lib/http_server/prototype/CMakeLists.txt create mode 100644 tests/net/lib/http_server/prototype/prj.conf create mode 100644 tests/net/lib/http_server/prototype/sections-rom.ld create mode 100644 tests/net/lib/http_server/prototype/src/main.c create mode 100644 tests/net/lib/http_server/prototype/testcase.yaml create mode 100644 tests/net/lib/http_server/tls/CMakeLists.txt create mode 100644 tests/net/lib/http_server/tls/prj.conf create mode 100644 tests/net/lib/http_server/tls/sections-rom.ld create mode 100644 tests/net/lib/http_server/tls/src/index.html create mode 100644 tests/net/lib/http_server/tls/src/main.c create mode 100644 tests/net/lib/http_server/tls/testcase.yaml diff --git a/tests/net/lib/http_server/common/prj.conf b/tests/net/lib/http_server/common/prj.conf index 7fa5d3d6dd..626a805048 100644 --- a/tests/net/lib/http_server/common/prj.conf +++ b/tests/net/lib/http_server/common/prj.conf @@ -7,3 +7,8 @@ CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_ZTEST_STACK_SIZE=1024 CONFIG_HTTP_SERVER=y +CONFIG_EVENTFD=y +CONFIG_POSIX_API=y + +# Networking config +CONFIG_NET_SOCKETS=y diff --git a/tests/net/lib/http_server/common/testcase.yaml b/tests/net/lib/http_server/common/testcase.yaml index b86f2dc0c9..11d720068c 100644 --- a/tests/net/lib/http_server/common/testcase.yaml +++ b/tests/net/lib/http_server/common/testcase.yaml @@ -6,6 +6,8 @@ common: - server integration_platforms: - native_sim - + platform_exclude: + - native_posix + - native_posix/native/64 tests: net.http.server.common: {} diff --git a/tests/net/lib/http_server/crime/CMakeLists.txt b/tests/net/lib/http_server/crime/CMakeLists.txt new file mode 100644 index 0000000000..9ccfbe570b --- /dev/null +++ b/tests/net/lib/http_server/crime/CMakeLists.txt @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(crime) + +set(BASE_PATH "../../../../../subsys/net/lib/http/") +include_directories(${BASE_PATH}/headers) + +FILE(GLOB app_sources src/main.c) +target_sources(app PRIVATE ${app_sources}) + +set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) + +set(source_file_index src/index.html) +generate_inc_file_for_target(app ${source_file_index} ${gen_dir}/index.html.inc) +generate_inc_file_for_target(app ${source_file_index} ${gen_dir}/index.html.gz.inc --gzip) + +set(source_file_not_found src/not_found_page.html) +generate_inc_file_for_target(app ${source_file_not_found} ${gen_dir}/not_found_page.html.inc) +generate_inc_file_for_target(app ${source_file_not_found} ${gen_dir}/not_found_page.html.gz.inc --gzip) + +zephyr_linker_sources(SECTIONS sections-rom.ld) +zephyr_iterable_section(NAME http_resource_desc_test_http_service KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4) diff --git a/tests/net/lib/http_server/crime/prj.conf b/tests/net/lib/http_server/crime/prj.conf new file mode 100644 index 0000000000..0596a54e47 --- /dev/null +++ b/tests/net/lib/http_server/crime/prj.conf @@ -0,0 +1,54 @@ +CONFIG_ZTEST=y +CONFIG_NET_TEST=y + +# Eventfd +CONFIG_EVENTFD=y +CONFIG_POSIX_API=y + +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_ZTEST_STACK_SIZE=1024 + +CONFIG_POSIX_MAX_FDS=10 +CONFIG_REQUIRES_FULL_LIBC=y +CONFIG_EVENTFD_MAX=10 +CONFIG_NET_MAX_CONTEXTS=10 +CONFIG_NET_MAX_CONN=10 + +# Networking config +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_LOOPBACK=y +CONFIG_NET_LOOPBACK_MTU=1280 +CONFIG_NET_DRIVERS=y +CONFIG_NET_SOCKETS_POLL_MAX=8 +CONFIG_NET_BUF_RX_COUNT=32 +CONFIG_NET_BUF_TX_COUNT=32 +CONFIG_NET_PKT_RX_COUNT=16 +CONFIG_NET_PKT_TX_COUNT=16 + +# Reduce the retry count, so the close always finishes within a second +CONFIG_NET_TCP_RETRY_COUNT=3 +CONFIG_NET_TCP_INIT_RETRANSMISSION_TIMEOUT=120 + +# JSON +CONFIG_JSON_LIBRARY=y + +# HTTP parser +CONFIG_HTTP_PARSER_URL=y +CONFIG_HTTP_PARSER=y +CONFIG_HTTP_SERVER=y + +CONFIG_HTTP_SERVER_MAX_CLIENTS=5 +CONFIG_HTTP_SERVER_MAX_STREAMS=5 + +# Network address config +CONFIG_NET_CONFIG_SETTINGS=n + +CONFIG_MAIN_STACK_SIZE=2048 + +# Network debug config +CONFIG_NET_LOG=y diff --git a/tests/net/lib/http_server/crime/sections-rom.ld b/tests/net/lib/http_server/crime/sections-rom.ld new file mode 100644 index 0000000000..512251641b --- /dev/null +++ b/tests/net/lib/http_server/crime/sections-rom.ld @@ -0,0 +1,3 @@ +#include + +ITERABLE_SECTION_ROM(http_resource_desc_test_http_service, 4) diff --git a/tests/net/lib/http_server/crime/src/index.html b/tests/net/lib/http_server/crime/src/index.html new file mode 100644 index 0000000000..7f82a1e397 --- /dev/null +++ b/tests/net/lib/http_server/crime/src/index.html @@ -0,0 +1,10 @@ + + + + My Simple Server + + +

Welcome to my simple server!

+

This is a simple HTML file.

+ + diff --git a/tests/net/lib/http_server/crime/src/main.c b/tests/net/lib/http_server/crime/src/main.c new file mode 100644 index 0000000000..992d64eff5 --- /dev/null +++ b/tests/net/lib/http_server/crime/src/main.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2023, Emna Rekik + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "server_internal.h" + +#include + +#include +#include +#include + +#define BUFFER_SIZE 256 +#define MY_IPV4_ADDR "127.0.0.1" +#define SERVER_PORT 8080 +#define TIMEOUT 1000 + +static const unsigned char index_html_gz[] = { +#include "index.html.gz.inc" +}; + +static const unsigned char compressed_inc_file[] = { + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0xff, 0x35, 0x8e, 0xc1, 0x0a, 0xc2, 0x30, + 0x0c, 0x86, 0xef, 0x7d, 0x8a, 0xec, 0x05, 0x2c, + 0xbb, 0x87, 0x5c, 0x54, 0xf0, 0xe0, 0x50, 0x58, + 0x41, 0x3c, 0x4e, 0x17, 0x69, 0x21, 0xa5, 0x65, + 0x2d, 0x42, 0xdf, 0xde, 0xba, 0x6e, 0x21, 0x10, + 0xf8, 0xf9, 0xbe, 0x9f, 0x60, 0x77, 0xba, 0x1d, + 0xcd, 0xf3, 0x7e, 0x06, 0x9b, 0xbd, 0x90, 0xc2, + 0xfd, 0xf0, 0x34, 0x93, 0x82, 0x3a, 0x98, 0x5d, + 0x16, 0xa6, 0xa1, 0xc0, 0xe8, 0x7c, 0x14, 0x86, + 0x91, 0x97, 0x2f, 0x2f, 0xa8, 0x5b, 0xae, 0x50, + 0x37, 0x16, 0x5f, 0x61, 0x2e, 0x9b, 0x62, 0x7b, + 0x7a, 0xb0, 0xbc, 0x83, 0x67, 0xc8, 0x01, 0x7c, + 0x81, 0xd4, 0xd4, 0xb4, 0xaa, 0x5d, 0x55, 0xfa, + 0x8d, 0x8c, 0x64, 0xac, 0x4b, 0x50, 0x77, 0xda, + 0xa1, 0x8b, 0x19, 0xae, 0xf0, 0x71, 0xc2, 0x07, + 0xd4, 0xf1, 0xdf, 0xdf, 0x8a, 0xab, 0xb4, 0xbe, + 0xf6, 0x03, 0xea, 0x2d, 0x11, 0x5c, 0xb2, 0x00, + 0x00, 0x00, +}; + +static uint16_t test_http_service_port = SERVER_PORT; +HTTP_SERVICE_DEFINE(test_http_service, MY_IPV4_ADDR, + &test_http_service_port, 1, + 10, NULL); + +struct http_resource_detail_static index_html_gz_resource_detail = { + .common = { + .type = HTTP_RESOURCE_TYPE_STATIC, + .bitmask_of_supported_http_methods = BIT(HTTP_GET), + }, + .static_data = index_html_gz, + .static_data_len = sizeof(index_html_gz), +}; + +HTTP_RESOURCE_DEFINE(index_html_gz_resource, test_http_service, "/", + &index_html_gz_resource_detail); + +static void test_crime(void) +{ + int ret, recv_len; + int client_fd; + int proto = IPPROTO_TCP; + char *ptr; + const char *data; + size_t len; + struct sockaddr_in sa; + static unsigned char buf[512]; + + zassert_ok(http_server_start(), "Failed to start the server"); + + ret = zsock_socket(AF_INET, SOCK_STREAM, proto); + zassert_not_equal(ret, -1, "failed to create client socket (%d)", errno); + client_fd = ret; + + sa.sin_family = AF_INET; + sa.sin_port = htons(SERVER_PORT); + + ret = zsock_inet_pton(AF_INET, MY_IPV4_ADDR, &sa.sin_addr.s_addr); + zassert_not_equal(-1, ret, "inet_pton() failed (%d)", errno); + zassert_not_equal(0, ret, "%s is not a valid IPv4 address", MY_IPV4_ADDR); + zassert_equal(1, ret, "inet_pton() failed to convert %s", MY_IPV4_ADDR); + + memset(buf, '\0', sizeof(buf)); + ptr = (char *)zsock_inet_ntop(AF_INET, &sa.sin_addr, buf, sizeof(buf)); + zassert_not_equal(ptr, NULL, "inet_ntop() failed (%d)", errno); + + ret = zsock_connect(client_fd, (struct sockaddr *)&sa, sizeof(sa)); + zassert_not_equal(ret, -1, "failed to connect (%s/%d)", strerror(errno), errno); + + char *http1_request = "GET / HTTP/1.1\r\n" + "Host: 127.0.0.1:8080\r\n" + "Accept: */*\r\n" + "Accept-Encoding: deflate, gzip, br\r\n" + "\r\n"; + + ret = zsock_send(client_fd, http1_request, strlen(http1_request), 0); + zassert_not_equal(ret, -1, "send() failed (%d)", errno); + + memset(buf, 0, sizeof(buf)); + recv_len = zsock_recv(client_fd, buf, sizeof(buf), 0); + zassert_not_equal(recv_len, -1, "recv() failed (%d)", errno); + + len = sizeof(index_html_gz); + + while (recv_len < len) { + ret = zsock_recv(client_fd, buf + recv_len, sizeof(buf) - recv_len, 0); + zassert_not_equal(ret, -1, "recv() failed (%d)", errno); + + recv_len += ret; + } + + data = strstr(buf, "\r\n\r\n"); + zassert_not_null(data, "Header not found"); + + data += 4; + + zassert_equal(len, sizeof(compressed_inc_file), "Invalid compressed file size"); + + ret = memcmp(data, compressed_inc_file, len); + zassert_equal(ret, 0, + "inc_file and compressed_inc_file contents are not identical (%d)", ret); + + ret = zsock_close(client_fd); + zassert_not_equal(-1, ret, "close() failed on the client fd (%d)", errno); + + zassert_ok(http_server_stop(), "Failed to stop the server"); +} + +ZTEST(framework_tests_crime, test_gen_gz_inc_file) +{ + test_crime(); +} + +ZTEST_SUITE(framework_tests_crime, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/net/lib/http_server/crime/src/not_found_page.html b/tests/net/lib/http_server/crime/src/not_found_page.html new file mode 100644 index 0000000000..c4bf66f08e --- /dev/null +++ b/tests/net/lib/http_server/crime/src/not_found_page.html @@ -0,0 +1,10 @@ + + + + 404 Not Found + + +

404 Not Found

+

The requested resource was not found.

+ + diff --git a/tests/net/lib/http_server/crime/testcase.yaml b/tests/net/lib/http_server/crime/testcase.yaml new file mode 100644 index 0000000000..2c75b4f1ca --- /dev/null +++ b/tests/net/lib/http_server/crime/testcase.yaml @@ -0,0 +1,16 @@ +common: + harness: net + min_ram: 16 + tags: + - http + - net + - server + - socket + integration_platforms: + - native_sim + - qemu_x86 + platform_exclude: + - native_posix + - native_posix/native/64 +tests: + net.http.server.crime: {} diff --git a/tests/net/lib/http_server/prototype/CMakeLists.txt b/tests/net/lib/http_server/prototype/CMakeLists.txt new file mode 100644 index 0000000000..4842b30949 --- /dev/null +++ b/tests/net/lib/http_server/prototype/CMakeLists.txt @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(prototype) + +set(BASE_PATH "../../../../../subsys/net/lib/http/") +include_directories(${BASE_PATH}/headers) + +FILE(GLOB app_sources src/main.c) +target_sources(app PRIVATE ${app_sources}) + +set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) + +target_link_libraries(app PRIVATE zephyr_interface zephyr) + +zephyr_linker_sources(SECTIONS sections-rom.ld) +zephyr_iterable_section(NAME http_resource_desc_test_http_service KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4) diff --git a/tests/net/lib/http_server/prototype/prj.conf b/tests/net/lib/http_server/prototype/prj.conf new file mode 100644 index 0000000000..20570610ef --- /dev/null +++ b/tests/net/lib/http_server/prototype/prj.conf @@ -0,0 +1,53 @@ +CONFIG_ZTEST=y +CONFIG_NET_TEST=y + +# Eventfd +CONFIG_EVENTFD=y +CONFIG_POSIX_API=y + +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_POSIX_MAX_FDS=10 +CONFIG_REQUIRES_FULL_LIBC=y +CONFIG_EVENTFD_MAX=10 +CONFIG_NET_MAX_CONTEXTS=10 +CONFIG_NET_MAX_CONN=10 + +# Networking config +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_LOOPBACK=y +CONFIG_NET_LOOPBACK_MTU=1280 +CONFIG_NET_DRIVERS=y +CONFIG_NET_SOCKETS_POLL_MAX=8 +CONFIG_NET_BUF_RX_COUNT=32 +CONFIG_NET_BUF_TX_COUNT=32 +CONFIG_NET_PKT_RX_COUNT=16 +CONFIG_NET_PKT_TX_COUNT=16 + +# Reduce the retry count, so the close always finishes within a second +CONFIG_NET_TCP_RETRY_COUNT=3 +CONFIG_NET_TCP_INIT_RETRANSMISSION_TIMEOUT=120 + +# JSON +CONFIG_JSON_LIBRARY=y + +# HTTP parser +CONFIG_HTTP_PARSER_URL=y +CONFIG_HTTP_PARSER=y +CONFIG_HTTP_SERVER=y + +CONFIG_HTTP_SERVER_MAX_CLIENTS=5 +CONFIG_HTTP_SERVER_MAX_STREAMS=5 + +# Network address config +CONFIG_NET_CONFIG_SETTINGS=n + +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_ZTEST_STACK_SIZE=18192 + +# Network debug config +CONFIG_NET_LOG=y diff --git a/tests/net/lib/http_server/prototype/sections-rom.ld b/tests/net/lib/http_server/prototype/sections-rom.ld new file mode 100644 index 0000000000..512251641b --- /dev/null +++ b/tests/net/lib/http_server/prototype/sections-rom.ld @@ -0,0 +1,3 @@ +#include + +ITERABLE_SECTION_ROM(http_resource_desc_test_http_service, 4) diff --git a/tests/net/lib/http_server/prototype/src/main.c b/tests/net/lib/http_server/prototype/src/main.c new file mode 100644 index 0000000000..99db7f81f7 --- /dev/null +++ b/tests/net/lib/http_server/prototype/src/main.c @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2023, Emna Rekik + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "server_internal.h" + +#include + +#include +#include +#include +#include + +#define SUPPORT_BACKWARD_COMPATIBILITY 1 +#define SUPPORT_HTTP_SERVER_UPGRADE 2 +#define BUFFER_SIZE 256 +#define MY_IPV4_ADDR "127.0.0.1" +#define SERVER_PORT 8080 +#define TIMEOUT 1000 + + +/* Magic, SETTINGS[0], HEADERS[1]: GET /, HEADERS[3]: GET /index.html, SETTINGS[0], GOAWAY[0]*/ +static const unsigned char frame[] = { + /* Magic */ + 0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32, + 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a, + /* SETTINGS[0] */ + 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x04, 0x00, 0x00, 0xff, 0xff, + /* HEADERS[1]: GET / */ + 0x00, 0x00, 0x21, 0x01, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x82, 0x84, 0x86, 0x41, 0x8a, 0x0b, 0xe2, 0x5c, 0x0b, 0x89, 0x70, 0xdc, + 0x78, 0x0f, 0x03, 0x53, 0x03, 0x2a, 0x2f, 0x2a, 0x90, 0x7a, 0x8a, 0xaa, + 0x69, 0xd2, 0x9a, 0xc4, 0xc0, 0x57, 0x68, 0x0b, 0x83, + /* HEADERS[3]: GET /index.html */ + 0x00, 0x00, 0x21, 0x01, 0x05, 0x00, 0x00, 0x00, 0x03, + 0x82, 0x85, 0x86, 0x41, 0x8a, 0x0b, 0xe2, 0x5c, 0x0b, 0x89, 0x70, 0xdc, + 0x78, 0x0f, 0x03, 0x53, 0x03, 0x2a, 0x2f, 0x2a, 0x90, 0x7a, 0x8a, 0xaa, + 0x69, 0xd2, 0x9a, 0xc4, 0xc0, 0x57, 0x68, 0x0b, 0x83, + /* SETTINGS[0] */ + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, + /* GOAWAY[0] */ + 0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static uint16_t test_http_service_port = SERVER_PORT; +HTTP_SERVICE_DEFINE(test_http_service, MY_IPV4_ADDR, + &test_http_service_port, 1, 10, NULL); + +static const char index_html_gz[] = "Hello, World!"; +struct http_resource_detail_static index_html_gz_resource_detail = { + .common = { + .type = HTTP_RESOURCE_TYPE_STATIC, + .bitmask_of_supported_http_methods = BIT(HTTP_GET), + }, + .static_data = index_html_gz, + .static_data_len = sizeof(index_html_gz), +}; + +HTTP_RESOURCE_DEFINE(index_html_gz_resource, test_http_service, "/", + &index_html_gz_resource_detail); + +static void test_streams(void) +{ + int ret; + int client_fd; + int proto = IPPROTO_TCP; + char *ptr; + struct sockaddr_in sa; + static unsigned char buf[512]; + unsigned int length; + uint8_t type; + size_t offset; + uint32_t stream_id; + + zassert_ok(http_server_start(), "Failed to start the server"); + + ret = zsock_socket(AF_INET, SOCK_STREAM, proto); + zassert_not_equal(ret, -1, "failed to create client socket (%d)", errno); + client_fd = ret; + + sa.sin_family = AF_INET; + sa.sin_port = htons(SERVER_PORT); + + ret = zsock_inet_pton(AF_INET, MY_IPV4_ADDR, &sa.sin_addr.s_addr); + zassert_not_equal(-1, ret, "inet_pton() failed (%d)", errno); + zassert_not_equal(0, ret, "%s is not a valid IPv4 address", MY_IPV4_ADDR); + zassert_equal(1, ret, "inet_pton() failed to convert %s", MY_IPV4_ADDR); + + memset(buf, '\0', sizeof(buf)); + ptr = (char *)zsock_inet_ntop(AF_INET, &sa.sin_addr, buf, sizeof(buf)); + zassert_not_equal(ptr, NULL, "inet_ntop() failed (%d)", errno); + + ret = zsock_connect(client_fd, (struct sockaddr *)&sa, sizeof(sa)); + zassert_not_equal(ret, -1, "failed to connect (%d)", errno); + + ret = zsock_send(client_fd, frame, sizeof(frame), 0); + zassert_not_equal(ret, -1, "send() failed (%d)", errno); + + memset(buf, 0, sizeof(buf)); + offset = 0; + do { + ret = zsock_recv(client_fd, buf + offset, sizeof(buf) - offset, 0); + zassert_not_equal(ret, -1, "recv() failed (%d)", errno); + + offset += ret; + } while (ret > 0); + + /* Settings frame is expected twice (server settings + settings ACK) */ + length = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + length += 9; + type = buf[3]; + stream_id = (buf[5] << 24) | (buf[6] << 16) | (buf[7] << 8) | buf[8]; + stream_id &= 0x7fffffff; + + zassert_true((type == 0x4 && stream_id == 0), + "Expected a SETTINGS frame with stream ID 0"); + zassert_true(offset > length, "Parsing error, buffer exceeded"); + + offset -= length; + memmove(buf, buf + length, offset); + + length = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + length += 9; + type = buf[3]; + stream_id = (buf[5] << 24) | (buf[6] << 16) | (buf[7] << 8) | buf[8]; + stream_id &= 0x7fffffff; + + zassert_true((type == 0x4 && stream_id == 0), + "Expected a SETTINGS frame with stream ID 0"); + zassert_true(offset > length, "Parsing error, buffer exceeded"); + + offset -= length; + memmove(buf, buf + length, offset); + + length = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + length += 9; + type = buf[3]; + stream_id = (buf[5] << 24) | (buf[6] << 16) | (buf[7] << 8) | buf[8]; + stream_id &= 0x7fffffff; + + zassert_true((type == 0x1 && stream_id == 1), + "Expected a HEADERS frame with stream ID 1, got %d", stream_id); + zassert_true(offset > length, "Parsing error, buffer exceeded"); + + offset -= length; + memmove(buf, buf + length, offset); + + length = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + length += 9; + type = buf[3]; + stream_id = (buf[5] << 24) | (buf[6] << 16) | (buf[7] << 8) | buf[8]; + stream_id &= 0x7fffffff; + buf[9] = 0; + + zassert_true((type == 0x0 && stream_id == 1), + "Expected a DATA frame with stream ID 1, got %d", stream_id); + zassert_true(offset > length, "Parsing error, buffer exceeded"); + + offset -= length; + memmove(buf, buf + length, offset); + + length = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + length += 9; + type = buf[3]; + stream_id = (buf[5] << 24) | (buf[6] << 16) | (buf[7] << 8) | buf[8]; + stream_id &= 0x7fffffff; + + zassert_true((type == 0x1 && stream_id == 3), + "Expected a HEADERS frame with stream ID 3"); + zassert_true(offset >= length, "Parsing error, buffer exceeded"); + + offset -= length; + memmove(buf, buf + length, offset); + + length = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + length += 9; + type = buf[3]; + stream_id = (buf[5] << 24) | (buf[6] << 16) | (buf[7] << 8) | buf[8]; + stream_id &= 0x7fffffff; + + zassert_true((type == 0x0 && stream_id == 3), + "Expected a DATA frame with stream ID 3"); + + ret = zsock_close(client_fd); + zassert_not_equal(-1, ret, "close() failed on the client fd (%d)", errno); + + zassert_ok(http_server_stop(), "Failed to stop the server"); +} + +ZTEST(server_function_tests, test_http_concurrent_streams) +{ + test_streams(); +} + +static void test_common(int test_support) +{ + int ret; + int client_fd; + int proto = IPPROTO_TCP; + char *ptr; + struct sockaddr_in sa = { 0 }; + static unsigned char buf[512]; + + zassert_ok(http_server_start(), "Failed to start the server"); + + ret = zsock_socket(AF_INET, SOCK_STREAM, proto); + zassert_not_equal(ret, -1, "failed to create client socket (%d)", errno); + client_fd = ret; + + sa.sin_family = AF_INET; + sa.sin_port = htons(SERVER_PORT); + + ret = zsock_inet_pton(AF_INET, MY_IPV4_ADDR, &sa.sin_addr.s_addr); + zassert_not_equal(-1, ret, "inet_pton() failed (%d)", errno); + zassert_not_equal(0, ret, "%s is not a valid IPv4 address", MY_IPV4_ADDR); + zassert_equal(1, ret, "inet_pton() failed to convert %s", MY_IPV4_ADDR); + + memset(buf, '\0', sizeof(buf)); + ptr = (char *)zsock_inet_ntop(AF_INET, &sa.sin_addr, buf, sizeof(buf)); + zassert_not_equal(ptr, NULL, "inet_ntop() failed (%d)", errno); + + ret = zsock_connect(client_fd, (struct sockaddr *)&sa, sizeof(sa)); + zassert_not_equal(ret, -1, "failed to connect (%s/%d)", strerror(errno), errno); + + if (test_support == SUPPORT_BACKWARD_COMPATIBILITY) { + + char *http1_request = "GET / HTTP/1.1\r\n" + "Host: 127.0.0.1:8080\r\n" + "User-Agent: curl/7.68.0\r\n" + "Accept: */*\r\n" + "Accept-Encoding: deflate, gzip, br\r\n" + "\r\n"; + + ret = zsock_send(client_fd, http1_request, strlen(http1_request), 0); + zassert_not_equal(ret, -1, "send() failed (%d)", errno); + + char expected_response[] = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 14\r\n" + "\r\n"; + + memset(buf, 0, sizeof(buf)); + ret = zsock_recv(client_fd, buf, sizeof(buf), 0); + zassert_not_equal(ret, -1, "recv() failed (%d)", errno); + + zassert_equal(strncmp(buf, expected_response, + strlen(expected_response)), 0, + "Received data doesn't match expected response"); + + } else if (test_support == SUPPORT_HTTP_SERVER_UPGRADE) { + + ret = zsock_send(client_fd, frame, sizeof(frame), 0); + zassert_not_equal(ret, -1, "send() failed (%d)", errno); + + memset(buf, 0, sizeof(buf)); + ret = zsock_recv(client_fd, buf, sizeof(buf), 0); + zassert_not_equal(ret, -1, "recv() failed (%d)", errno); + + uint8_t type = buf[3]; + + zassert_true(type == 0x4, "Expected a SETTINGS frame"); + } + + ret = zsock_close(client_fd); + zassert_not_equal(-1, ret, "close() failed on the client fd (%d)", errno); + + zassert_ok(http_server_stop(), "Failed to stop the server"); +} + +ZTEST(server_function_tests, test_http_upgrade) +{ + test_common(SUPPORT_HTTP_SERVER_UPGRADE); +} + +ZTEST(server_function_tests, test_backward_compatibility) +{ + test_common(SUPPORT_BACKWARD_COMPATIBILITY); +} + +ZTEST(server_function_tests, test_http_server_start_stop) +{ + struct sockaddr_in sa = { 0 }; + int client_fd; + int ret; + + sa.sin_family = AF_INET; + sa.sin_port = htons(SERVER_PORT); + + ret = zsock_inet_pton(AF_INET, MY_IPV4_ADDR, &sa.sin_addr.s_addr); + zassert_not_equal(-1, ret, "inet_pton() failed (%d)", errno); + zassert_not_equal(0, ret, "%s is not a valid IPv4 address", MY_IPV4_ADDR); + zassert_equal(1, ret, "inet_pton() failed to convert %s", MY_IPV4_ADDR); + + zassert_ok(http_server_start(), "Failed to start the server"); + zassert_not_ok(http_server_start(), "Server start should report na error."); + + zassert_ok(http_server_stop(), "Failed to stop the server"); + zassert_not_ok(http_server_stop(), "Server stop should report na error."); + + zassert_ok(http_server_start(), "Failed to start the server"); + + /* Server should be listening now. */ + ret = zsock_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + zassert_not_equal(ret, -1, "failed to create client socket (%d)", errno); + client_fd = ret; + + ret = zsock_connect(client_fd, (struct sockaddr *)&sa, sizeof(sa)); + zassert_not_equal(ret, -1, "failed to connect to the server (%d)", errno); + + ret = zsock_close(client_fd); + zassert_not_equal(-1, ret, "close() failed on the client fd (%d)", errno); + + zassert_ok(http_server_stop(), "Failed to stop the server"); +} + +ZTEST(server_function_tests, test_get_frame_type_name) +{ + zassert_equal(strcmp(get_frame_type_name(HTTP_SERVER_DATA_FRAME), "DATA"), 0, + "Unexpected frame type"); + zassert_equal(strcmp(get_frame_type_name(HTTP_SERVER_HEADERS_FRAME), "HEADERS"), 0, + "Unexpected frame type"); + zassert_equal(strcmp(get_frame_type_name(HTTP_SERVER_PRIORITY_FRAME), "PRIORITY"), 0, + "Unexpected frame type"); + zassert_equal(strcmp(get_frame_type_name(HTTP_SERVER_RST_STREAM_FRAME), "RST_STREAM"), 0, + "Unexpected frame type"); + zassert_equal(strcmp(get_frame_type_name(HTTP_SERVER_SETTINGS_FRAME), "SETTINGS"), 0, + "Unexpected frame type"); + zassert_equal(strcmp(get_frame_type_name(HTTP_SERVER_PUSH_PROMISE_FRAME), "PUSH_PROMISE"), + 0, "Unexpected frame type"); + zassert_equal(strcmp(get_frame_type_name(HTTP_SERVER_PING_FRAME), "PING"), 0, + "Unexpected frame type"); + zassert_equal(strcmp(get_frame_type_name(HTTP_SERVER_GOAWAY_FRAME), "GOAWAY"), 0, + "Unexpected frame type"); + zassert_equal(strcmp(get_frame_type_name(HTTP_SERVER_WINDOW_UPDATE_FRAME), "WINDOW_UPDATE"), + 0, "Unexpected frame type"); + zassert_equal(strcmp(get_frame_type_name(HTTP_SERVER_CONTINUATION_FRAME), "CONTINUATION"), + 0, "Unexpected frame type"); +} + +ZTEST(server_function_tests, test_parse_http_frames) +{ + static struct http_client_ctx ctx_client1; + static struct http_client_ctx ctx_client2; + struct http_frame *frame; + + unsigned char buffer1[] = { + 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, + 0x04, 0x00, 0x00, 0xff, 0xff, 0x00 + }; + unsigned char buffer2[] = { + 0x00, 0x00, 0x21, 0x01, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x82, 0x84, 0x86, 0x41, 0x8a, 0x0b, 0xe2, + 0x5c, 0x0b, 0x89, 0x70, 0xdc, 0x78, 0x0f, 0x03, + 0x53, 0x03, 0x2a, 0x2f, 0x2a, 0x90, 0x7a, 0x8a, + 0xaa, 0x69, 0xd2, 0x9a, 0xc4, 0xc0, 0x57, 0x68, + 0x0b, 0x83 + }; + + memcpy(ctx_client1.buffer, buffer1, sizeof(buffer1)); + memcpy(ctx_client2.buffer, buffer2, sizeof(buffer2)); + + ctx_client1.cursor = ctx_client1.buffer; + ctx_client1.data_len = ARRAY_SIZE(buffer1); + + ctx_client2.cursor = ctx_client2.buffer; + ctx_client2.data_len = ARRAY_SIZE(buffer2); + + /* Test: Buffer with the first frame */ + int parser1 = parse_http_frame_header(&ctx_client1); + + zassert_equal(parser1, 1, "Failed to parse the first frame"); + + frame = &ctx_client1.current_frame; + + /* Validate frame details for the 1st frame */ + zassert_equal(frame->length, 0x0C, "Expected length for the 1st frame doesn't match"); + zassert_equal(frame->type, 0x04, "Expected type for the 1st frame doesn't match"); + zassert_equal(frame->flags, 0x00, "Expected flags for the 1st frame doesn't match"); + zassert_equal(frame->stream_identifier, 0x00, + "Expected stream_identifier for the 1st frame doesn't match"); + + /* Test: Buffer with the second frame */ + int parser2 = parse_http_frame_header(&ctx_client2); + + zassert_equal(parser2, 1, "Failed to parse the second frame"); + + frame = &ctx_client2.current_frame; + + /* Validate frame details for the 2nd frame */ + zassert_equal(frame->length, 0x21, "Expected length for the 2nd frame doesn't match"); + zassert_equal(frame->type, 0x01, "Expected type for the 2nd frame doesn't match"); + zassert_equal(frame->flags, 0x05, "Expected flags for the 2nd frame doesn't match"); + zassert_equal(frame->stream_identifier, 0x01, + "Expected stream_identifier for the 2nd frame doesn't match"); +} + +ZTEST_SUITE(server_function_tests, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/net/lib/http_server/prototype/testcase.yaml b/tests/net/lib/http_server/prototype/testcase.yaml new file mode 100644 index 0000000000..d5683174bc --- /dev/null +++ b/tests/net/lib/http_server/prototype/testcase.yaml @@ -0,0 +1,16 @@ +common: + harness: net + min_ram: 16 + tags: + - http + - net + - server + - socket + integration_platforms: + - native_sim + - qemu_x86 + platform_exclude: + - native_posix + - native_posix/native/64 +tests: + net.http.server.prototype: {} diff --git a/tests/net/lib/http_server/tls/CMakeLists.txt b/tests/net/lib/http_server/tls/CMakeLists.txt new file mode 100644 index 0000000000..f72aa8b447 --- /dev/null +++ b/tests/net/lib/http_server/tls/CMakeLists.txt @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(tls) + +set(BASE_PATH "../../../../../subsys/net/lib/http/") +include_directories(${BASE_PATH}/headers) + +set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) + +if (${CONFIG_TLS_CREDENTIALS}) + generate_inc_file_for_target( + app + ${ZEPHYR_BASE}/samples/net/sockets/http_server/src/ca.der + ${gen_dir}/ca.inc + ) + + generate_inc_file_for_target( + app + ${ZEPHYR_BASE}/samples/net/sockets/http_server/src/server.der + ${gen_dir}/server.inc + ) + + generate_inc_file_for_target( + app + ${ZEPHYR_BASE}/samples/net/sockets/http_server/src/server_privkey.der + ${gen_dir}/server_privkey.inc + ) + + # we reuse the same certificate / private key for client + # since it seems to be the only one that is signed by a ca + generate_inc_file_for_target( + app + ${ZEPHYR_BASE}/samples/net/sockets/http_server/src/server.der + ${gen_dir}/client.inc + ) + + generate_inc_file_for_target( + app + ${ZEPHYR_BASE}/samples/net/sockets/http_server/src/server_privkey.der + ${gen_dir}/client_privkey.inc + ) +endif() + +set(source_file_index src/index.html) +generate_inc_file_for_target(app ${source_file_index} ${gen_dir}/index.html.inc) +generate_inc_file_for_target(app ${source_file_index} ${gen_dir}/index.html.gz.inc --gzip) + +FILE(GLOB app_sources src/main.c) +target_sources(app PRIVATE ${app_sources}) + +zephyr_linker_sources(SECTIONS sections-rom.ld) +zephyr_iterable_section(NAME http_resource_desc_test_http_service KVMA RAM_REGION GROUP RODATA_REGION SUBALIGN 4) diff --git a/tests/net/lib/http_server/tls/prj.conf b/tests/net/lib/http_server/tls/prj.conf new file mode 100644 index 0000000000..997a3b6426 --- /dev/null +++ b/tests/net/lib/http_server/tls/prj.conf @@ -0,0 +1,61 @@ +# General config +CONFIG_ZTEST=y +CONFIG_NET_TEST=y + +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_FDTABLE=y +CONFIG_EVENTFD=y +CONFIG_POSIX_API=y + +# Networking config +CONFIG_NETWORKING=y +CONFIG_NET_LOOPBACK=y +CONFIG_NET_LOOPBACK_MTU=1280 +CONFIG_NET_DRIVERS=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y + +# Logging / Debugging options +CONFIG_NET_LOG=y + +# TLS Options +CONFIG_TLS_CREDENTIALS=y +CONFIG_NET_SOCKETS_SOCKOPT_TLS=y +CONFIG_TLS_MAX_CREDENTIALS_NUMBER=5 +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=60000 +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=2048 +CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=6 + +# Network buffers / packets / sizes +CONFIG_NET_BUF_TX_COUNT=128 +CONFIG_NET_BUF_RX_COUNT=128 +CONFIG_NET_PKT_TX_COUNT=16 +CONFIG_NET_PKT_RX_COUNT=16 +CONFIG_POSIX_MAX_FDS=32 +CONFIG_NET_SOCKETS_POLL_MAX=32 +CONFIG_POSIX_MAX_FDS=32 +CONFIG_REQUIRES_FULL_LIBC=y +CONFIG_EVENTFD_MAX=10 +CONFIG_NET_MAX_CONTEXTS=10 +CONFIG_NET_MAX_CONN=10 + +# Stack sizes +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_ZTEST_STACK_SIZE=4096 + +# JSON +CONFIG_JSON_LIBRARY=y + +# HTTP parser +CONFIG_HTTP_PARSER_URL=y +CONFIG_HTTP_PARSER=y +CONFIG_HTTP_SERVER=y + +# Network address config +CONFIG_NET_CONFIG_SETTINGS=n diff --git a/tests/net/lib/http_server/tls/sections-rom.ld b/tests/net/lib/http_server/tls/sections-rom.ld new file mode 100644 index 0000000000..512251641b --- /dev/null +++ b/tests/net/lib/http_server/tls/sections-rom.ld @@ -0,0 +1,3 @@ +#include + +ITERABLE_SECTION_ROM(http_resource_desc_test_http_service, 4) diff --git a/tests/net/lib/http_server/tls/src/index.html b/tests/net/lib/http_server/tls/src/index.html new file mode 100644 index 0000000000..7f82a1e397 --- /dev/null +++ b/tests/net/lib/http_server/tls/src/index.html @@ -0,0 +1,10 @@ + + + + My Simple Server + + +

Welcome to my simple server!

+

This is a simple HTML file.

+ + diff --git a/tests/net/lib/http_server/tls/src/main.c b/tests/net/lib/http_server/tls/src/main.c new file mode 100644 index 0000000000..34c5c8fe7f --- /dev/null +++ b/tests/net/lib/http_server/tls/src/main.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2023, Emna Rekik + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "server_internal.h" +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL); + +/** @brief Stack size for the server thread */ +#define STACK_SIZE 8192 + +#define MY_IPV4_ADDR "127.0.0.1" + +/** @brief arbitrary timeout value in ms */ +#define TIMEOUT 1000 + +#define BUFFER_SIZE 256 +#define SERVER_PORT 8000 + +enum tls_tag { + /** The Certificate Authority public key */ + CA_CERTIFICATE_TAG, + /** Used for both the public and private server keys */ + SERVER_CERTIFICATE_TAG, + /** Used for both the public and private client keys */ + CLIENT_CERTIFICATE_TAG, +}; + +static const sec_tag_t server_tag_list_verify[] = { + CA_CERTIFICATE_TAG, + SERVER_CERTIFICATE_TAG, +}; + +static uint16_t test_http_service_port = SERVER_PORT; +HTTPS_SERVICE_DEFINE(test_http_service, MY_IPV4_ADDR, &test_http_service_port, + 1, 10, NULL, server_tag_list_verify, + sizeof(server_tag_list_verify)); + +static const unsigned char ca[] = { +#include "ca.inc" +}; + +/** + * @brief The Server Certificate + * + * This is the public key of the server. + */ +static const unsigned char server[] = { +#include "server.inc" +}; + +/** + * @brief The Server Private Key + * + * This is the private key of the server. + */ +static const unsigned char server_privkey[] = { +#include "server_privkey.inc" +}; + +/** + * @brief The Client Certificate + * + * This is the public key of the client. + */ +static const unsigned char client[] = { +#include "client.inc" +}; + +/** + * @brief The Client Private Key + * + * This is the private key of the client. + */ +static const unsigned char client_privkey[] = { +#include "client_privkey.inc" +}; + +static const unsigned char index_html_gz[] = { +#include "index.html.gz.inc" +}; + +static const unsigned char compressed_inc_file[] = { + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0xff, 0x35, 0x8e, 0xc1, 0x0a, 0xc2, 0x30, + 0x0c, 0x86, 0xef, 0x7d, 0x8a, 0xec, 0x05, 0x2c, + 0xbb, 0x87, 0x5c, 0x54, 0xf0, 0xe0, 0x50, 0x58, + 0x41, 0x3c, 0x4e, 0x17, 0x69, 0x21, 0xa5, 0x65, + 0x2d, 0x42, 0xdf, 0xde, 0xba, 0x6e, 0x21, 0x10, + 0xf8, 0xf9, 0xbe, 0x9f, 0x60, 0x77, 0xba, 0x1d, + 0xcd, 0xf3, 0x7e, 0x06, 0x9b, 0xbd, 0x90, 0xc2, + 0xfd, 0xf0, 0x34, 0x93, 0x82, 0x3a, 0x98, 0x5d, + 0x16, 0xa6, 0xa1, 0xc0, 0xe8, 0x7c, 0x14, 0x86, + 0x91, 0x97, 0x2f, 0x2f, 0xa8, 0x5b, 0xae, 0x50, + 0x37, 0x16, 0x5f, 0x61, 0x2e, 0x9b, 0x62, 0x7b, + 0x7a, 0xb0, 0xbc, 0x83, 0x67, 0xc8, 0x01, 0x7c, + 0x81, 0xd4, 0xd4, 0xb4, 0xaa, 0x5d, 0x55, 0xfa, + 0x8d, 0x8c, 0x64, 0xac, 0x4b, 0x50, 0x77, 0xda, + 0xa1, 0x8b, 0x19, 0xae, 0xf0, 0x71, 0xc2, 0x07, + 0xd4, 0xf1, 0xdf, 0xdf, 0x8a, 0xab, 0xb4, 0xbe, + 0xf6, 0x03, 0xea, 0x2d, 0x11, 0x5c, 0xb2, 0x00, + 0x00, 0x00, +}; + +struct http_resource_detail_static index_html_gz_resource_detail = { + .common = { + .type = HTTP_RESOURCE_TYPE_STATIC, + .bitmask_of_supported_http_methods = BIT(HTTP_GET), + }, + .static_data = index_html_gz, + .static_data_len = sizeof(index_html_gz), +}; + +HTTP_RESOURCE_DEFINE(index_html_gz_resource, test_http_service, "/", + &index_html_gz_resource_detail); + +static void test_tls(void) +{ + int ret, recv_len; + int client_fd; + int proto = IPPROTO_TCP; + size_t len; + char *ptr; + const char *data; + struct sockaddr_in sa; + static unsigned char buf[512]; + char http1_request[] = + "GET / HTTP/1.1\r\n" + "Host: 127.0.0.1:8080\r\n" + "Accept: */*\r\n" + "Accept-Encoding: deflate, gzip, br\r\n" + "\r\n"; + + /* set the common protocol for both client and server */ + if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS)) { + proto = IPPROTO_TLS_1_2; + } + + zassert_ok(http_server_start(), "Failed to start the server"); + + ret = zsock_socket(AF_INET, SOCK_STREAM, proto); + zassert_not_equal(ret, -1, "failed to create client socket (%d)", errno); + client_fd = ret; + + if (IS_ENABLED(CONFIG_TLS_CREDENTIALS) && + IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS)) { + static const sec_tag_t sec_tag_list_verify_none[] = { + CA_CERTIFICATE_TAG, + }; + const sec_tag_t *sec_tag_list; + size_t sec_tag_list_size; + + sec_tag_list_size = sizeof(sec_tag_list); + sec_tag_list = sec_tag_list_verify_none; + + ret = zsock_setsockopt(client_fd, SOL_TLS, TLS_SEC_TAG_LIST, + sec_tag_list, sec_tag_list_size); + zassert_not_equal(ret, -1, "failed to set TLS_SEC_TAG_LIST (%d)", errno); + + ret = zsock_setsockopt(client_fd, SOL_TLS, TLS_HOSTNAME, + "localhost", sizeof("localhost")); + zassert_not_equal(ret, -1, "failed to set TLS_HOSTNAME (%d)", errno); + } + + sa.sin_family = AF_INET; + sa.sin_port = htons(SERVER_PORT); + + ret = zsock_inet_pton(AF_INET, MY_IPV4_ADDR, &sa.sin_addr.s_addr); + zassert_not_equal(-1, ret, "inet_pton() failed (%d)", errno); + zassert_not_equal(ret, 0, "%s is not a valid IPv4 address", MY_IPV4_ADDR); + zassert_equal(ret, 1, "inet_pton() failed to convert %s", MY_IPV4_ADDR); + + memset(buf, '\0', sizeof(buf)); + ptr = (char *)zsock_inet_ntop(AF_INET, &sa.sin_addr, buf, sizeof(buf)); + zassert_not_equal(ptr, NULL, "inet_ntop() failed (%d)", errno); + + ret = zsock_connect(client_fd, (struct sockaddr *)&sa, sizeof(sa)); + zassert_not_equal(ret, -1, "failed to connect (%d)", errno); + + ret = zsock_send(client_fd, http1_request, sizeof(http1_request) - 1, 0); + zassert_not_equal(ret, -1, "send() failed (%d)", errno); + zassert_equal(ret, sizeof(http1_request) - 1, "expected: %zu actual: %d", + sizeof(http1_request) - 1, ret); + + memset(buf, 0, sizeof(buf)); + recv_len = zsock_recv(client_fd, buf, sizeof(buf), 0); + zassert_not_equal(recv_len, -1, "recv() failed (%d)", errno); + + len = sizeof(index_html_gz); + + while (recv_len < len) { + ret = zsock_recv(client_fd, buf + recv_len, sizeof(buf) - recv_len, 0); + zassert_not_equal(ret, -1, "recv() failed (%d)", errno); + + recv_len += ret; + } + + data = strstr(buf, "\r\n\r\n"); + zassert_not_null(data, "Header not found"); + + data += 4; + + zassert_equal(len, sizeof(compressed_inc_file), "Invalid compressed file size"); + + ret = memcmp(data, compressed_inc_file, len); + zassert_equal(ret, 0, + "inc_file and compressed_inc_file contents are not identical (%d)", ret); + + ret = zsock_close(client_fd); + zassert_not_equal(ret, -1, "close() failed on the client fd (%d)", errno); + + zassert_ok(http_server_stop(), "Failed to stop the server"); +} + +ZTEST(framework_tests_tls, test_tls) +{ + test_tls(); +} + +static void *setup(void) +{ + int ret; + + if (IS_ENABLED(CONFIG_TLS_CREDENTIALS)) { + NET_DBG("Loading credentials"); + ret = tls_credential_add(CA_CERTIFICATE_TAG, + TLS_CREDENTIAL_CA_CERTIFICATE, + ca, sizeof(ca)); + zassert_equal(ret, 0, "failed to add CA Certificate (%d)", ret); + + ret = tls_credential_add(SERVER_CERTIFICATE_TAG, + TLS_CREDENTIAL_SERVER_CERTIFICATE, + server, sizeof(server)); + zassert_equal(ret, 0, "failed to add Server Certificate (%d)", ret); + + ret = tls_credential_add(SERVER_CERTIFICATE_TAG, + TLS_CREDENTIAL_PRIVATE_KEY, + server_privkey, sizeof(server_privkey)); + zassert_equal(ret, 0, "failed to add Server Private Key (%d)", ret); + + ret = tls_credential_add(CLIENT_CERTIFICATE_TAG, + TLS_CREDENTIAL_SERVER_CERTIFICATE, + client, sizeof(client)); + zassert_equal(ret, 0, "failed to add Client Certificate (%d)", ret); + + ret = tls_credential_add(CLIENT_CERTIFICATE_TAG, + TLS_CREDENTIAL_PRIVATE_KEY, + client_privkey, sizeof(client_privkey)); + zassert_equal(ret, 0, "failed to add Client Private Key (%d)", ret); + } + + return NULL; +} + +ZTEST_SUITE(framework_tests_tls, NULL, setup, NULL, NULL, NULL); diff --git a/tests/net/lib/http_server/tls/testcase.yaml b/tests/net/lib/http_server/tls/testcase.yaml new file mode 100644 index 0000000000..13f6ded54d --- /dev/null +++ b/tests/net/lib/http_server/tls/testcase.yaml @@ -0,0 +1,19 @@ +common: + harness: net + min_ram: 192 + tags: + - http + - net + - server + - socket + integration_platforms: + - native_sim + - qemu_x86 + platform_exclude: + - native_posix + - native_posix/native/64 + platform_allow: + - native_sim + - qemu_x86 +tests: + net.http.server.tls: {}