blob: 9cdf2c66fdf33e7d27eab15c8a2fb7f136edc8b7 [file]
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(net_shell);
#include "net_shell_private.h"
#include <zephyr/net/http/service.h>
#include <zephyr/net/http/server.h>
#include <zephyr/net/http/method.h>
#include <zephyr/net/http/parser.h>
#include <zephyr/sys/util.h>
#if defined(CONFIG_HTTP_SERVER)
static const char *http_protocols_str(const struct http_service_desc *svc)
{
static char buf[sizeof("HTTPS/1.x, HTTPS/2.0, HTTPS/3.0")];
const struct http_service_config *cfg = svc->config;
int pos = 0;
bool print_sep = false;
const char *https;
if (COND_CODE_1(CONFIG_NET_SOCKETS_SOCKOPT_TLS,
(svc->sec_tag_list != NULL),
(0))) {
https = "S";
} else {
https = "";
}
if (cfg != NULL) {
if (cfg->http_ver & HTTP_VERSION_1) {
pos += snprintk(buf + pos, sizeof(buf) - pos, "HTTP%s/1.x", https);
print_sep = true;
}
if (cfg->http_ver & HTTP_VERSION_2) {
pos += snprintk(buf + pos, sizeof(buf) - pos, "%sHTTP%s/2.0",
print_sep ? ", " : "", https);
print_sep = true;
}
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
if ((cfg->http_ver & HTTP_VERSION_3) && svc->sec_tag_list != NULL) {
pos += snprintk(buf + pos, sizeof(buf) - pos, "%sHTTPS/3.0",
print_sep ? ", " : "");
}
#endif
} else {
if (IS_ENABLED(CONFIG_HTTP_SERVER_VERSION_1)) {
pos += snprintk(buf + pos, sizeof(buf) - pos, "HTTP%s/1.x", https);
print_sep = true;
}
if (IS_ENABLED(CONFIG_HTTP_SERVER_VERSION_2)) {
pos += snprintk(buf + pos, sizeof(buf) - pos, "%sHTTP%s/2.0",
print_sep ? ", " : "", https);
print_sep = true;
}
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
if (IS_ENABLED(CONFIG_HTTP_SERVER_VERSION_3) && svc->sec_tag_list != NULL) {
pos += snprintk(buf + pos, sizeof(buf) - pos, "%sHTTPS/3.0",
print_sep ? ", " : "");
}
#endif
}
return buf;
}
#endif
static int cmd_net_http(const struct shell *sh, size_t argc, char *argv[])
{
#if defined(CONFIG_HTTP_SERVER)
int res_count = 0, serv_count = 0;
PR("%-15s\t%-19s\t%s\n",
"Host:Port", "Concurrent/Backlog", "Protocols");
PR("\tResource type\tMethods\t\tEndpoint\n");
HTTP_SERVICE_FOREACH(svc) {
char backlog_buf[sizeof("xxxxx/xxxxx")];
snprintk(backlog_buf, sizeof(backlog_buf), "%zu/%zu",
svc->concurrent, svc->backlog);
PR("\n");
PR("%s:%d\t%-19s\t%s\n",
svc->host == NULL || svc->host[0] == '\0' ?
"<any>" : svc->host, svc->port ? *svc->port : 0,
backlog_buf,
http_protocols_str(svc));
HTTP_SERVICE_FOREACH_RESOURCE(svc, res) {
struct http_resource_detail *detail = res->detail;
const char *detail_type = "<unknown>";
int method_count = 0;
bool print_comma;
switch (detail->type) {
case HTTP_RESOURCE_TYPE_STATIC:
detail_type = "static";
break;
case HTTP_RESOURCE_TYPE_STATIC_FS:
detail_type = "static_fs";
break;
case HTTP_RESOURCE_TYPE_DYNAMIC:
detail_type = "dynamic";
break;
case HTTP_RESOURCE_TYPE_WEBSOCKET:
detail_type = "websocket";
break;
}
PR("\t%12s\t", detail_type);
print_comma = false;
for (int i = 0; i < NUM_BITS(uint32_t); i++) {
if (IS_BIT_SET(detail->bitmask_of_supported_http_methods, i)) {
PR("%s%s", print_comma ? "," : "", http_method_str(i));
print_comma = true;
method_count++;
}
}
if (method_count < 2) {
/* make columns line up better */
PR("\t");
}
PR("\t%s\n", res->resource);
res_count++;
}
serv_count++;
}
if (res_count == 0 && serv_count == 0) {
PR("No HTTP services and resources found.\n");
} else {
PR("\n%d service%sand %d resource%sfound.\n",
serv_count, serv_count > 1 ? "s " : " ",
res_count, res_count > 1 ? "s " : " ");
}
#else /* CONFIG_HTTP_SERVER */
ARG_UNUSED(argc);
ARG_UNUSED(argv);
PR_INFO("Set %s to enable %s support.\n",
"CONFIG_HTTP_SERVER",
"HTTP information");
#endif
return 0;
}
/* clang-format off */
SHELL_SUBCMD_SET_CREATE(sub_http, (net, http));
SHELL_SUBCMD_ADD((net, http), services, NULL,
SHELL_HELP("Show HTTP services", NULL),
cmd_net_http, 1, 0);
SHELL_SUBCMD_ADD((net), http, &sub_http,
SHELL_HELP("HTTP commands", NULL),
NULL, 1, 0);
/* clang-format on */