Browse Source

net: http_server: serve resources only for their defined services

Ensure that HTTP resources can only be served to a client connected on
the specific service(s) that the resource was registered against using
the HTTP_RESOURCE_DEFINE macro.

This allows different resources to be registered to different services,
for example to make some resources only available via an HTTPS service
and not via unencrypted HTTP.

Signed-off-by: Matt Rodgers <mrodgers@witekio.com>
pull/83756/head
Matt Rodgers 6 months ago committed by Benjamin Cabé
parent
commit
b7091ed439
  1. 3
      include/zephyr/net/http/server.h
  2. 5
      include/zephyr/net/http/service.h
  3. 3
      subsys/net/lib/http/headers/server_internal.h
  4. 64
      subsys/net/lib/http/http_server_core.c
  5. 4
      subsys/net/lib/http/http_server_http1.c
  6. 4
      subsys/net/lib/http/http_server_http2.c
  7. 34
      tests/net/lib/http_server/common/src/main.c

3
include/zephyr/net/http/server.h

@ -400,6 +400,9 @@ struct http_client_ctx { @@ -400,6 +400,9 @@ struct http_client_ctx {
/** Socket descriptor associated with the server. */
int fd;
/** HTTP service on which the client is connected */
const struct http_service_desc *service;
/** Client data buffer. */
unsigned char buffer[HTTP_SERVER_CLIENT_BUFFER_SIZE];

5
include/zephyr/net/http/service.h

@ -67,6 +67,7 @@ struct http_resource_desc { @@ -67,6 +67,7 @@ struct http_resource_desc {
struct http_service_desc {
const char *host;
uint16_t *port;
int *fd;
void *detail;
size_t concurrent;
size_t backlog;
@ -80,9 +81,11 @@ struct http_service_desc { @@ -80,9 +81,11 @@ struct http_service_desc {
#define __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, _res_begin, \
_res_end, ...) \
const STRUCT_SECTION_ITERABLE(http_service_desc, _name) = { \
static int _name##_fd = -1; \
const STRUCT_SECTION_ITERABLE(http_service_desc, _name) = { \
.host = _host, \
.port = (uint16_t *)(_port), \
.fd = &_name##_fd, \
.detail = (void *)(_detail), \
.concurrent = (_concurrent), \
.backlog = (_backlog), \

3
subsys/net/lib/http/headers/server_internal.h

@ -37,7 +37,8 @@ int enter_http2_request(struct http_client_ctx *client); @@ -37,7 +37,8 @@ int enter_http2_request(struct http_client_ctx *client);
int enter_http_done_state(struct http_client_ctx *client);
/* Others */
struct http_resource_detail *get_resource_detail(const char *path, int *len, bool is_ws);
struct http_resource_detail *get_resource_detail(const struct http_service_desc *service,
const char *path, int *len, bool is_ws);
int http_server_sendall(struct http_client_ctx *client, const void *buf, size_t len);
void http_server_get_content_type_from_extension(char *url, char *content_type,
size_t content_type_size);

64
subsys/net/lib/http/http_server_core.c

@ -237,6 +237,7 @@ int http_server_init(struct http_server_ctx *ctx) @@ -237,6 +237,7 @@ int http_server_init(struct http_server_ctx *ctx)
LOG_DBG("Initialized HTTP Service %s:%u",
svc->host ? svc->host : "<any>", *svc->port);
*svc->fd = fd;
ctx->fds[count].fd = fd;
ctx->fds[count].events = ZSOCK_POLLIN;
count++;
@ -299,6 +300,10 @@ static void close_all_sockets(struct http_server_ctx *ctx) @@ -299,6 +300,10 @@ static void close_all_sockets(struct http_server_ctx *ctx)
ctx->fds[i].fd = -1;
}
HTTP_SERVICE_FOREACH(svc) {
*svc->fd = -1;
}
}
static void client_release_resources(struct http_client_ctx *client)
@ -393,9 +398,22 @@ void http_client_timer_restart(struct http_client_ctx *client) @@ -393,9 +398,22 @@ void http_client_timer_restart(struct http_client_ctx *client)
k_work_reschedule(&client->inactivity_timer, INACTIVITY_TIMEOUT);
}
static void init_client_ctx(struct http_client_ctx *client, int new_socket)
static const struct http_service_desc *lookup_service(int server_fd)
{
HTTP_SERVICE_FOREACH(svc) {
if (*svc->fd == server_fd) {
return svc;
}
}
return NULL;
}
static void init_client_ctx(struct http_client_ctx *client, const struct http_service_desc *svc,
int new_socket)
{
client->fd = new_socket;
client->service = svc;
client->data_len = 0;
client->server_state = HTTP_SERVER_PREFACE_STATE;
client->has_upgrade_header = false;
@ -523,6 +541,7 @@ static int handle_http_request(struct http_client_ctx *client) @@ -523,6 +541,7 @@ static int handle_http_request(struct http_client_ctx *client)
static int http_server_run(struct http_server_ctx *ctx)
{
struct http_client_ctx *client;
const struct http_service_desc *service;
eventfd_t value;
bool found_slot;
int new_socket;
@ -600,6 +619,9 @@ static int http_server_run(struct http_server_ctx *ctx) @@ -600,6 +619,9 @@ static int http_server_run(struct http_server_ctx *ctx)
continue;
}
service = lookup_service(ctx->fds[i].fd);
__ASSERT(NULL != service, "fd not associated with a service");
found_slot = false;
for (j = ctx->listen_fds; j < ARRAY_SIZE(ctx->fds); j++) {
@ -615,7 +637,7 @@ static int http_server_run(struct http_server_ctx *ctx) @@ -615,7 +637,7 @@ static int http_server_run(struct http_server_ctx *ctx)
LOG_DBG("Init client #%d", j - ctx->listen_fds);
init_client_ctx(&ctx->clients[j - ctx->listen_fds],
init_client_ctx(&ctx->clients[j - ctx->listen_fds], service,
new_socket);
found_slot = true;
break;
@ -713,34 +735,30 @@ static bool skip_this(struct http_resource_desc *resource, bool is_websocket) @@ -713,34 +735,30 @@ static bool skip_this(struct http_resource_desc *resource, bool is_websocket)
return false;
}
struct http_resource_detail *get_resource_detail(const char *path,
int *path_len,
bool is_websocket)
struct http_resource_detail *get_resource_detail(const struct http_service_desc *service,
const char *path, int *path_len, bool is_websocket)
{
HTTP_SERVICE_FOREACH(service) {
HTTP_SERVICE_FOREACH_RESOURCE(service, resource) {
if (skip_this(resource, is_websocket)) {
continue;
}
if (IS_ENABLED(CONFIG_HTTP_SERVER_RESOURCE_WILDCARD)) {
int ret;
ret = fnmatch(resource->resource, path,
(FNM_PATHNAME | FNM_LEADING_DIR));
if (ret == 0) {
*path_len = strlen(resource->resource);
return resource->detail;
}
}
HTTP_SERVICE_FOREACH_RESOURCE(service, resource) {
if (skip_this(resource, is_websocket)) {
continue;
}
if (compare_strings(path, resource->resource) == 0) {
NET_DBG("Got match for %s", resource->resource);
if (IS_ENABLED(CONFIG_HTTP_SERVER_RESOURCE_WILDCARD)) {
int ret;
ret = fnmatch(resource->resource, path, (FNM_PATHNAME | FNM_LEADING_DIR));
if (ret == 0) {
*path_len = strlen(resource->resource);
return resource->detail;
}
}
if (compare_strings(path, resource->resource) == 0) {
NET_DBG("Got match for %s", resource->resource);
*path_len = strlen(resource->resource);
return resource->detail;
}
}
NET_DBG("No match for %s", path);

4
subsys/net/lib/http/http_server_http1.c

@ -859,7 +859,7 @@ int handle_http1_request(struct http_client_ctx *client) @@ -859,7 +859,7 @@ int handle_http1_request(struct http_client_ctx *client)
if (client->websocket_upgrade) {
if (IS_ENABLED(CONFIG_HTTP_SERVER_WEBSOCKET)) {
detail = get_resource_detail(client->url_buffer,
detail = get_resource_detail(client->service, client->url_buffer,
&path_len, true);
if (detail == NULL) {
goto not_found;
@ -900,7 +900,7 @@ upgrade_not_found: @@ -900,7 +900,7 @@ upgrade_not_found:
}
}
detail = get_resource_detail(client->url_buffer, &path_len, false);
detail = get_resource_detail(client->service, client->url_buffer, &path_len, false);
if (detail != NULL) {
detail->path_len = path_len;

4
subsys/net/lib/http/http_server_http2.c

@ -1029,7 +1029,7 @@ int handle_http1_to_http2_upgrade(struct http_client_ctx *client) @@ -1029,7 +1029,7 @@ int handle_http1_to_http2_upgrade(struct http_client_ctx *client)
client->preface_sent = true;
}
detail = get_resource_detail(client->url_buffer, &path_len, false);
detail = get_resource_detail(client->service, client->url_buffer, &path_len, false);
if (detail != NULL) {
detail->path_len = path_len;
@ -1509,7 +1509,7 @@ int handle_http_frame_headers(struct http_client_ctx *client) @@ -1509,7 +1509,7 @@ int handle_http_frame_headers(struct http_client_ctx *client)
return 0;
}
detail = get_resource_detail(client->url_buffer, &path_len, false);
detail = get_resource_detail(client->service, client->url_buffer, &path_len, false);
if (detail != NULL) {
detail->path_len = path_len;

34
tests/net/lib/http_server/common/src/main.c

@ -296,55 +296,69 @@ ZTEST(http_service, test_HTTP_RESOURCE_DEFINE) @@ -296,55 +296,69 @@ ZTEST(http_service, test_HTTP_RESOURCE_DEFINE)
}
}
extern struct http_resource_detail *get_resource_detail(const char *path,
extern struct http_resource_detail *get_resource_detail(const struct http_service_desc *service,
const char *path,
int *path_len,
bool is_websocket);
#define CHECK_PATH(path, len) ({ *len = 0; get_resource_detail(path, len, false); })
#define CHECK_PATH(svc, path, len) ({ *len = 0; get_resource_detail(&svc, path, len, false); })
ZTEST(http_service, test_HTTP_RESOURCE_WILDCARD)
{
struct http_resource_detail *res;
int len;
res = CHECK_PATH("/", &len);
res = CHECK_PATH(service_A, "/", &len);
zassert_not_null(res, "Cannot find resource");
zassert_true(len > 0, "Length not set");
zassert_equal(res, RES(0), "Resource mismatch");
res = CHECK_PATH("/f", &len);
res = CHECK_PATH(service_D, "/f", &len);
zassert_is_null(res, "Resource found");
zassert_equal(len, 0, "Length set");
res = CHECK_PATH("/foo1.html", &len);
res = CHECK_PATH(service_D, "/foo1.html", &len);
zassert_not_null(res, "Cannot find resource");
zassert_true(len > 0, "Length not set");
zassert_equal(res, RES(0), "Resource mismatch");
res = CHECK_PATH("/foo2222.html", &len);
res = CHECK_PATH(service_D, "/foo2222.html", &len);
zassert_not_null(res, "Cannot find resource");
zassert_true(len > 0, "Length not set");
zassert_equal(res, RES(1), "Resource mismatch");
res = CHECK_PATH("/fbo3.html", &len);
res = CHECK_PATH(service_D, "/fbo3.html", &len);
zassert_not_null(res, "Cannot find resource");
zassert_true(len > 0, "Length not set");
zassert_equal(res, RES(1), "Resource mismatch");
res = CHECK_PATH("/fbo3.htm", &len);
res = CHECK_PATH(service_D, "/fbo3.htm", &len);
zassert_not_null(res, "Cannot find resource");
zassert_true(len > 0, "Length not set");
zassert_equal(res, RES(0), "Resource mismatch");
res = CHECK_PATH("/fbo4.html", &len);
res = CHECK_PATH(service_D, "/fbo4.html", &len);
zassert_not_null(res, "Cannot find resource");
zassert_true(len > 0, "Length not set");
zassert_equal(res, RES(3), "Resource mismatch");
res = CHECK_PATH("/fs/index.html", &len);
res = CHECK_PATH(service_A, "/fs/index.html", &len);
zassert_not_null(res, "Cannot find resource");
zassert_true(len > 0, "Length not set");
zassert_equal(res, RES(5), "Resource mismatch");
/* Resources that only exist on one service should not be found on another */
res = CHECK_PATH(service_A, "/foo1.htm", &len);
zassert_is_null(res, "Resource found");
zassert_equal(len, 0, "Length set");
res = CHECK_PATH(service_A, "/foo2222.html", &len);
zassert_is_null(res, "Resource found");
zassert_equal(len, 0, "Length set");
res = CHECK_PATH(service_A, "/fbo3.htm", &len);
zassert_is_null(res, "Resource found");
zassert_equal(len, 0, "Length set");
}
extern void http_server_get_content_type_from_extension(char *url, char *content_type,

Loading…
Cancel
Save