mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-12 03:31:55 +00:00
This patch adds the HTTP Basic Authentication routine "http_auth". Authentication parameters are defined in the config.h file. The README file is also updated by this patch. Jira: ZEP-1543 Change-Id: I2d531966290e87a68c54fc1ac834491ac937df22 Signed-off-by: Flavio Santes <flavio.santes@intel.com>
448 lines
9.8 KiB
C
448 lines
9.8 KiB
C
/*
|
|
* 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 <net/http_parser.h>
|
|
#include <net/nbuf.h>
|
|
#include <stdio.h>
|
|
|
|
#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);
|
|
}
|