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>
This commit is contained in:
parent
3c88970cec
commit
b7091ed439
7 changed files with 78 additions and 39 deletions
|
@ -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];
|
||||
|
||||
|
|
|
@ -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 {
|
|||
|
||||
#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), \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
|||
|
||||
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)
|
|||
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)
|
|||
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)
|
|||
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)
|
|||
|
||||
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)
|
|||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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:
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
@ -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)
|
|||
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;
|
||||
|
||||
|
|
|
@ -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…
Add table
Add a link
Reference in a new issue