/* * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include "http_types.h" #include "http_server.h" #include "http_utils.h" #include "http_write_utils.h" #include "config.h" #include #include #include #define URL_DEFAULT_HANDLER_INDEX 0 #define HTTP_BUF_CTR HTTP_MAX_NUMBER_SERVER_CTX #define HTTP_BUF_SIZE 1024 NET_BUF_POOL_DEFINE(http_msg_pool, HTTP_BUF_CTR, HTTP_BUF_SIZE, 0, NULL); void http_accept_cb(struct net_context *net_ctx, struct sockaddr *addr, socklen_t addr_len, int status, void *data) { struct http_server_ctx *http_ctx = NULL; ARG_UNUSED(addr_len); ARG_UNUSED(data); if (status != 0) { net_context_put(net_ctx); return; } print_client_banner(addr); http_ctx = http_ctx_get(); if (!http_ctx) { net_context_put(net_ctx); return; } http_ctx_set(http_ctx, net_ctx); net_context_recv(net_ctx, http_rx_tx, K_NO_WAIT, http_ctx); } /** * @brief http_ctx_release Releases an HTTP context * @return 0, future versions may return error codes */ static int http_ctx_release(struct http_server_ctx *http_ctx); /** * @brief parser_init Initializes some parser-related fields at the * http_server_ctx structure * @param ctx HTTP server context * @param net_ctx Network context * @return 0 on success * @return -EINVAL on error */ static int parser_init(struct http_server_ctx *ctx); /** * @brief parser_parse_request Parses an HTTP REQUEST * @param ctx HTTP server context * @param rx Input buffer * @return 0 on success * @return -EINVAL on error */ static int parser_parse_request(struct http_server_ctx *ctx, struct net_buf *rx); static struct http_root_url *http_url_find(struct http_server_ctx *http_ctx); static int http_url_cmp(const char *url, uint16_t url_len, const char *root_url, uint16_t root_url_len); static void http_tx(struct http_server_ctx *http_ctx); void http_rx_tx(struct net_context *net_ctx, struct net_buf *rx, int status, void *user_data) { struct http_server_ctx *http_ctx = NULL; struct net_buf *data = NULL; uint16_t rcv_len; uint16_t offset; int parsed_len; int rc; if (status) { printf("[%s:%d] Status code: %d, <%s>\n", __func__, __LINE__, status, RC_STR(status)); goto lb_exit; } if (!user_data) { printf("[%s:%d] User data is null\n", __func__, __LINE__); goto lb_exit; } http_ctx = (struct http_server_ctx *)user_data; if (http_ctx->net_ctx != net_ctx) { printf("[%s:%d] Wrong network context received\n", __func__, __LINE__); goto lb_exit; } if (!rx) { printf("[%s:%d] Connection closed by peer\n", __func__, __LINE__); goto lb_exit; } rcv_len = net_nbuf_appdatalen(rx); if (rcv_len == 0) { /* don't print info about zero-length app data buffers */ goto lb_exit; } data = net_buf_alloc(&http_msg_pool, APP_SLEEP_MSECS); if (data == NULL) { printf("[%s:%d] Data buffer alloc error\n", __func__, __LINE__); goto lb_exit; } offset = net_buf_frags_len(rx) - rcv_len; rc = net_nbuf_linear_copy(data, rx, offset, rcv_len); if (rc != 0) { printf("[%s:%d] Linear copy error\n", __func__, __LINE__); goto lb_exit; } data->data[min(data->size - 1, rcv_len)] = 0; parser_init(http_ctx); parsed_len = parser_parse_request(http_ctx, data); if (parsed_len <= 0) { printf("[%s:%d] Received: %u bytes, only parsed: %d bytes\n", __func__, __LINE__, rcv_len, parsed_len); } if (http_ctx->parser.http_errno != HPE_OK) { http_response_400(http_ctx, NULL); } else { http_tx(http_ctx); } lb_exit: net_buf_unref(data); net_buf_unref(rx); http_ctx_release(http_ctx); } /** * @brief on_header_field HTTP Parser callback for header fields * @param parser HTTP Parser * @param at Points to where the field begins * @param length Field's length * @return 0 (always) */ static int on_header_field(struct http_parser *parser, const char *at, size_t length) { struct http_server_ctx *ctx = (struct http_server_ctx *)parser->data; if (ctx->field_values_ctr >= HTTP_PARSER_MAX_FIELD_VALUES) { return 0; } ctx->field_values[ctx->field_values_ctr].key = at; ctx->field_values[ctx->field_values_ctr].key_len = length; return 0; } /** * @brief on_header_value HTTP Parser callback for header values * @param parser HTTP Parser * @param at Points to where the value begins * @param length Value's length * @return 0 (always) */ static int on_header_value(struct http_parser *parser, const char *at, size_t length) { struct http_server_ctx *ctx = (struct http_server_ctx *)parser->data; if (ctx->field_values_ctr >= HTTP_PARSER_MAX_FIELD_VALUES) { return 0; } ctx->field_values[ctx->field_values_ctr].value = at; ctx->field_values[ctx->field_values_ctr].value_len = length; ctx->field_values_ctr++; return 0; } /** * @brief on_url HTTP Parser callback for URLs * @param parser HTTP Parser * @param at Points to where the value begins * @param length Value's length * @return 0 (always) */ static int on_url(struct http_parser *parser, const char *at, size_t length) { struct http_server_ctx *ctx = (struct http_server_ctx *)parser->data; ctx->url = at; ctx->url_len = length; return 0; } static int parser_init(struct http_server_ctx *ctx) { memset(ctx->field_values, 0x00, sizeof(ctx->field_values)); ctx->parser_settings.on_header_field = on_header_field; ctx->parser_settings.on_header_value = on_header_value; ctx->parser_settings.on_url = on_url; http_parser_init(&ctx->parser, HTTP_REQUEST); /* This circular reference is useful when some parser callbacks * want to access some internal data structures */ ctx->parser.data = ctx; return 0; } static int parser_parse_request(struct http_server_ctx *ctx, struct net_buf *rx) { int rc; ctx->field_values_ctr = 0; rc = http_parser_execute(&ctx->parser, &ctx->parser_settings, rx->data, rx->len); if (rc < 0) { printf("[%s:%d] http_parser_execute: %s\n\t%s\n", __func__, __LINE__, http_errno_name(ctx->parser.http_errno), http_errno_description(ctx->parser.http_errno)); rc = -EINVAL; goto exit_routine; } exit_routine: return rc; } /** * @brief server_collection This is a collection of server ctx structs */ static struct http_server_ctx server_collection[HTTP_MAX_NUMBER_SERVER_CTX]; /** * @brief http_url_ctx Just one URL context per application */ static struct http_url_ctx url_ctx; int http_ctx_init(void) { memset(server_collection, 0x00, sizeof(server_collection)); memset(&url_ctx, 0x00, sizeof(url_ctx)); /* 0 is reserved for the default handler */ url_ctx.urls_ctr = 1; return 0; } struct http_server_ctx *http_ctx_get(void) { int i; for (i = 0; i < HTTP_MAX_NUMBER_SERVER_CTX; i++) { if (server_collection[i].state == HTTP_CTX_FREE) { printf("[%s:%d] Free ctx found, index: %d\n", __func__, __LINE__, i); memset(server_collection + i, 0x00, sizeof(struct http_server_ctx)); return server_collection + i; } } return NULL; } int http_ctx_set(struct http_server_ctx *http_ctx, struct net_context *net_ctx) { if (http_ctx == NULL || net_ctx == NULL) { return -EINVAL; } http_ctx->state = HTTP_CTX_IN_USE; http_ctx->net_ctx = net_ctx; return 0; } static int http_ctx_release(struct http_server_ctx *http_ctx) { if (http_ctx == NULL) { return 0; } http_ctx->state = HTTP_CTX_FREE; return 0; } int http_url_default_handler(int (*write_cb)(struct http_server_ctx *)) { if (write_cb == NULL) { return -EINVAL; } url_ctx.urls[URL_DEFAULT_HANDLER_INDEX].flags = 0x00; url_ctx.urls[URL_DEFAULT_HANDLER_INDEX].root = NULL; url_ctx.urls[URL_DEFAULT_HANDLER_INDEX].root_len = 0; url_ctx.urls[URL_DEFAULT_HANDLER_INDEX].write_cb = write_cb; return 0; } int http_url_add(const char *url, uint8_t flags, int (*write_cb)(struct http_server_ctx *http_ctx)) { struct http_root_url *root = NULL; if (url_ctx.urls_ctr >= HTTP_MAX_NUMBER_URL) { return -ENOMEM; } root = &url_ctx.urls[url_ctx.urls_ctr]; root->root = url; /* this will speed-up some future operations */ root->root_len = strlen(url); root->flags = flags; root->write_cb = write_cb; url_ctx.urls_ctr++; return 0; } static int http_url_cmp(const char *url, uint16_t url_len, const char *root_url, uint16_t root_url_len) { if (url_len < root_url_len) { return -EINVAL; } if (memcmp(url, root_url, root_url_len) == 0) { if (url_len == root_url_len) { return 0; } /* Here we evlaute the following conditions: * root_url = /images, url = /images/ -> OK * root_url = /images/, url = /images/img.png -> OK * root_url = /images/, url = /images_and_docs -> ERROR */ if (url_len > root_url_len) { if (root_url[root_url_len - 1] == '/') { return 0; } if (url[root_url_len] == '/') { return 0; } } } return -EINVAL; } static struct http_root_url *http_url_find(struct http_server_ctx *http_ctx) { uint16_t url_len = http_ctx->url_len; const char *url = http_ctx->url; struct http_root_url *root_url; uint8_t i; int rc; /* at some point we must come up with something better */ for (i = 1; i < url_ctx.urls_ctr; i++) { root_url = &url_ctx.urls[i]; rc = http_url_cmp(url, url_len, root_url->root, root_url->root_len); if (rc == 0) { return root_url; } } return NULL; } static void http_tx(struct http_server_ctx *http_ctx) { struct http_root_url *root_url; root_url = http_url_find(http_ctx); if (!root_url) { root_url = &url_ctx.urls[URL_DEFAULT_HANDLER_INDEX]; } if (root_url->write_cb) { root_url->write_cb(http_ctx); } else { printf("[%s:%d] No default handler for %.*s\n", __func__, __LINE__, http_ctx->url_len, http_ctx->url); } } int http_auth(struct http_server_ctx *ctx) { const char *auth_str = "Authorization: Basic "HTTP_AUTH_CREDENTIALS; if (strstr(ctx->field_values[0].key, auth_str)) { return http_response_auth(ctx); } return http_response_401(ctx); }