samples: net: Add civetweb HTTP sample

This commit adds civetweb as a west module and a sample that uses it.

Signed-off-by: Tomasz Gorochowik <tgorochowik@antmicro.com>
Signed-off-by: Piotr Zierhoffer <pzierhoffer@antmicro.com>
This commit is contained in:
Tomasz Gorochowik 2019-06-18 13:08:54 +02:00 committed by Jukka Rissanen
commit f2bf9a17c5
10 changed files with 623 additions and 0 deletions

8
modules/Kconfig.civetweb Normal file
View file

@ -0,0 +1,8 @@
# Copyright (c) 2019 Antmicro Ltd
#
# SPDX-License-Identifier: Apache-2.0
config CIVETWEB
bool "Civetweb Support"
help
This option enables the civetweb HTTP API.

View file

@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(hello_world)
target_sources(app PRIVATE src/main.c src/libc_extensions.c)

View file

@ -0,0 +1,34 @@
# General config
CONFIG_CIVETWEB=y
CONFIG_JSON_LIBRARY=y
# pthreads
CONFIG_POSIX_API=y
CONFIG_PTHREAD_IPC=y
CONFIG_POSIX_MQUEUE=y
# networking
CONFIG_NETWORKING=y
CONFIG_NET_IPV4=y
# CONFIG_NET_IPV6 is not set
CONFIG_NET_TCP=y
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_POSIX_NAMES=y
CONFIG_MINIMAL_LIBC_MALLOC_ARENA_SIZE=131072
CONFIG_NET_TX_STACK_SIZE=8192
CONFIG_NET_RX_STACK_SIZE=8192
CONFIG_ISR_STACK_SIZE=8192
CONFIG_MAIN_STACK_SIZE=8192
CONFIG_IDLE_STACK_SIZE=2048
CONFIG_SOC_SERIES_SAME70=y
CONFIG_DNS_RESOLVER=y
CONFIG_NET_CONFIG_SETTINGS=y
CONFIG_NET_CONFIG_MY_IPV4_ADDR="10.0.0.111"
CONFIG_NET_CONFIG_MY_IPV4_NETMASK="255.255.255.0"
CONFIG_NET_CONFIG_MY_IPV4_GW="10.0.0.116"
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="10.0.0.116"
# logging
CONFIG_NET_LOG=y

View file

@ -0,0 +1,6 @@
sample:
description: Civetweb HTTP API sample
name: civetweb
tests:
sample.net.sockets.civetweb:
platform_whitelist: sam_e70_xplained

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2019 Antmicro Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
static void log_access(const struct mg_connection *conn)
{
const struct mg_request_info *ri;
char src_addr[IP_ADDR_STR_LEN];
if (!conn || !conn->dom_ctx) {
return;
}
ri = &conn->request_info;
sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
printf("%s - \"%s %s%s%s HTTP/%s\" %d\n",
src_addr,
ri->request_method ? ri->request_method : "-",
ri->request_uri ? ri->request_uri : "-",
ri->query_string ? "?" : "",
ri->query_string ? ri->query_string : "",
ri->http_version,
conn->status_code);
}

View file

@ -0,0 +1,18 @@
/*
* Copyright (c) 2019 Antmicro Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
static void mg_cry_internal_impl(const struct mg_connection *conn,
const char *func,
unsigned line,
const char *fmt,
va_list ap)
{
(void)conn;
printf("[INTERNAL ERROR]: %s @ %d\n", func, line);
vprintf(fmt, ap);
printf("\n");
}

View file

@ -0,0 +1,262 @@
/*
* Copyright (c) 2019 Antmicro Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "libc_extensions.h"
#define FN_MISSING() printf("[IMPLEMENTATION MISSING : %s]\n", __func__)
size_t strcspn(const char *s1, const char *s2)
{
int i, j;
for (i = 0; i < strlen(s2); ++i) {
for (j = 0; j < strlen(s1); ++j) {
if (s1[j] == s2[i]) {
return j;
}
}
}
return strlen(s1);
}
int iscntrl(int c)
{
/* All the characters placed before the space on the ASCII table
* and the 0x7F character (DEL) are control characters.
*/
return (int)(c < ' ' || c == 0x7F);
}
size_t strftime(char *dst, size_t dst_size,
const char *fmt,
const struct tm *tm)
{
FN_MISSING();
return 0;
}
double difftime(time_t end, time_t beg)
{
return end - beg;
}
struct __strerr_wrap {
int err;
const char *errstr;
};
/* Implementation suggested by @rakons in #16527 */
#define STRERR_DEFINE(e) {e, #e}
static const struct __strerr_wrap error_strings[] = {
STRERR_DEFINE(EILSEQ),
STRERR_DEFINE(EDOM),
STRERR_DEFINE(ERANGE),
STRERR_DEFINE(ENOTTY),
STRERR_DEFINE(EACCES),
STRERR_DEFINE(EPERM),
STRERR_DEFINE(ENOENT),
STRERR_DEFINE(ESRCH),
STRERR_DEFINE(EEXIST),
STRERR_DEFINE(ENOSPC),
STRERR_DEFINE(ENOMEM),
STRERR_DEFINE(EBUSY),
STRERR_DEFINE(EINTR),
STRERR_DEFINE(EAGAIN),
STRERR_DEFINE(ESPIPE),
STRERR_DEFINE(EXDEV),
STRERR_DEFINE(EROFS),
STRERR_DEFINE(ENOTEMPTY),
STRERR_DEFINE(ECONNRESET),
STRERR_DEFINE(ETIMEDOUT),
STRERR_DEFINE(ECONNREFUSED),
STRERR_DEFINE(EHOSTDOWN),
STRERR_DEFINE(EHOSTUNREACH),
STRERR_DEFINE(EADDRINUSE),
STRERR_DEFINE(EPIPE),
STRERR_DEFINE(EIO),
STRERR_DEFINE(ENXIO),
STRERR_DEFINE(ENOTBLK),
STRERR_DEFINE(ENODEV),
STRERR_DEFINE(ENOTDIR),
STRERR_DEFINE(EISDIR),
STRERR_DEFINE(ETXTBSY),
STRERR_DEFINE(ENOEXEC),
STRERR_DEFINE(EINVAL),
STRERR_DEFINE(E2BIG),
STRERR_DEFINE(ELOOP),
STRERR_DEFINE(ENAMETOOLONG),
STRERR_DEFINE(ENFILE),
STRERR_DEFINE(EMFILE),
STRERR_DEFINE(EBADF),
STRERR_DEFINE(ECHILD),
STRERR_DEFINE(EFAULT),
STRERR_DEFINE(EFBIG),
STRERR_DEFINE(EMLINK),
STRERR_DEFINE(ENOLCK),
STRERR_DEFINE(EDEADLK),
STRERR_DEFINE(ECANCELED),
STRERR_DEFINE(ENOSYS),
STRERR_DEFINE(ENOMSG),
STRERR_DEFINE(ENOSTR),
STRERR_DEFINE(ENODATA),
STRERR_DEFINE(ETIME),
STRERR_DEFINE(ENOSR),
STRERR_DEFINE(EPROTO),
STRERR_DEFINE(EBADMSG),
STRERR_DEFINE(ENOTSOCK),
STRERR_DEFINE(EDESTADDRREQ),
STRERR_DEFINE(EMSGSIZE),
STRERR_DEFINE(EPROTOTYPE),
STRERR_DEFINE(ENOPROTOOPT),
STRERR_DEFINE(EPROTONOSUPPORT),
STRERR_DEFINE(ESOCKTNOSUPPORT),
STRERR_DEFINE(ENOTSUP),
STRERR_DEFINE(EPFNOSUPPORT),
STRERR_DEFINE(EAFNOSUPPORT),
STRERR_DEFINE(EADDRNOTAVAIL),
STRERR_DEFINE(ENETDOWN),
STRERR_DEFINE(ENETUNREACH),
STRERR_DEFINE(ENETRESET),
STRERR_DEFINE(ECONNABORTED),
STRERR_DEFINE(ENOBUFS),
STRERR_DEFINE(EISCONN),
STRERR_DEFINE(ENOTCONN),
STRERR_DEFINE(ESHUTDOWN),
STRERR_DEFINE(EALREADY),
STRERR_DEFINE(EINPROGRESS),
};
static char *strerr_unknown = "UNKNOWN";
char *strerror(int err)
{
int i;
for (i = 0; i < ARRAY_SIZE(error_strings); ++i) {
if (error_strings[i].err == err) {
return (char *)error_strings[i].errstr;
}
}
return strerr_unknown;
}
int sscanf(const char *s, const char *format, ...)
{
FN_MISSING();
return 0;
}
double atof(const char *str)
{
/* XXX good enough for civetweb uses */
return (double)atoi(str);
}
long long int strtoll(const char *str, char **endptr, int base)
{
/* XXX good enough for civetweb uses */
return (long long int)strtol(str, endptr, base);
}
time_t time(time_t *t)
{
return 0;
}
/*
* Most of the wrappers below are copies of the wrappers in net/sockets.h,
* but they are available only if CONFIG_NET_SOCKETS_POSIX_NAMES is enabled
* which is impossible here.
*/
int getsockname(int sock, struct sockaddr *addr,
socklen_t *addrlen)
{
return zsock_getsockname(sock, addr, addrlen);
}
int poll(struct zsock_pollfd *fds, int nfds, int timeout)
{
return zsock_poll(fds, nfds, timeout);
}
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
char *host, socklen_t hostlen,
char *serv, socklen_t servlen, int flags)
{
return zsock_getnameinfo(addr, addrlen, host, hostlen,
serv, servlen, flags);
}
ssize_t send(int sock, const void *buf, size_t len, int flags)
{
return zsock_send(sock, buf, len, flags);
}
ssize_t recv(int sock, void *buf, size_t max_len, int flags)
{
return zsock_recv(sock, buf, max_len, flags);
}
int socket(int family, int type, int proto)
{
return zsock_socket(family, type, proto);
}
int getaddrinfo(const char *host, const char *service,
const struct zsock_addrinfo *hints,
struct zsock_addrinfo **res)
{
return zsock_getaddrinfo(host, service, hints, res);
}
void freeaddrinfo(struct zsock_addrinfo *ai)
{
zsock_freeaddrinfo(ai);
}
int connect(int sock, const struct sockaddr *addr,
socklen_t addrlen)
{
return zsock_connect(sock, addr, addrlen);
}
int getsockopt(int sock, int level, int optname,
void *optval, socklen_t *optlen)
{
return zsock_getsockopt(sock, level, optname, optval, optlen);
}
int setsockopt(int sock, int level, int optname,
const void *optval, socklen_t optlen)
{
return zsock_setsockopt(sock, level, optname, optval, optlen);
}
int listen(int sock, int backlog)
{
return zsock_listen(sock, backlog);
}
int bind(int sock, const struct sockaddr *addr, socklen_t addrlen)
{
return zsock_bind(sock, addr, addrlen);
}
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen)
{
return zsock_accept(sock, addr, addrlen);
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2019 Antmicro Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <net/socket.h>
#define pollfd zsock_pollfd
#define fcntl zsock_fcntl
#define POLLIN ZSOCK_POLLIN
#define POLLOUT ZSOCK_POLLOUT
#define addrinfo zsock_addrinfo
#define F_SETFD 2
#define FD_CLOEXEC 1
size_t strcspn(const char *s1, const char *s2);
size_t strspn(const char *s1, const char *s2);
int iscntrl(int c);
double atof(const char *str);
long long int strtoll(const char *str, char **endptr, int base);
int sscanf(const char *s, const char *format, ...);
char *strerror(int err);
unsigned long long int strtoull(const char *str, char **endptr, int base);
time_t time(time_t *t);
struct tm *gmtime(const time_t *ptime);
size_t strftime(char *dst, size_t dst_size, const char *fmt,
const struct tm *tm);
double difftime(time_t end, time_t beg);
struct tm *localtime(const time_t *timer);
int fileno(FILE *stream);
int ferror(FILE *stream);
int fclose(FILE *stream);
int fseeko(FILE *stream, off_t offset, int whence);
FILE *fopen(const char *filename, const char *mode);
char *fgets(char *str, int num, FILE *stream);
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
int remove(const char *filename);
int getsockname(int sock, struct sockaddr *addr, socklen_t *addrlen);
int poll(struct zsock_pollfd *fds, int nfds, int timeout);
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
char *host, socklen_t hostlen,
char *serv, socklen_t servlen, int flags);
ssize_t send(int sock, const void *buf, size_t len, int flags);
ssize_t recv(int sock, void *buf, size_t max_len, int flags);
int socket(int family, int type, int proto);
int getaddrinfo(const char *host, const char *service,
const struct zsock_addrinfo *hints,
struct zsock_addrinfo **res);
void freeaddrinfo(struct zsock_addrinfo *ai);
int connect(int sock, const struct sockaddr *addr, socklen_t addrlen);
int getsockopt(int sock, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname,
const void *optval, socklen_t optlen);
int listen(int sock, int backlog);
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
int bind(int sock, const struct sockaddr *addr, socklen_t addrlen);

View file

@ -0,0 +1,181 @@
/*
* Copyright (c) 2019 Antmicro Ltd
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <posix/pthread.h>
#include <json.h>
#include "civetweb.h"
#define CIVETWEB_MAIN_THREAD_STACK_SIZE 4096
K_THREAD_STACK_DEFINE(civetweb_stack, CIVETWEB_MAIN_THREAD_STACK_SIZE);
struct civetweb_info {
const char *version;
const char *os;
u32_t features;
const char *feature_list;
const char *build;
const char *compiler;
const char *data_model;
};
#define FIELD(struct_, member_, type_) { \
.field_name = #member_, \
.field_name_len = sizeof(#member_) - 1, \
.offset = offsetof(struct_, member_), \
.type = type_ \
}
void send_ok(struct mg_connection *conn)
{
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n\r\n");
}
int hello_world_handler(struct mg_connection *conn, void *cbdata)
{
send_ok(conn);
mg_printf(conn, "<html><body>");
mg_printf(conn, "<h3>Hello World from Zephyr!</h3>");
mg_printf(conn, "See also:\n");
mg_printf(conn, "<ul>\n");
mg_printf(conn, "<li><a href=/info>system info</a></li>\n");
mg_printf(conn, "<li><a href=/history>cookie demo</a></li>\n");
mg_printf(conn, "</ul>\n");
mg_printf(conn, "</body></html>\n");
return 200;
}
int system_info_handler(struct mg_connection *conn, void *cbdata)
{
static const struct json_obj_descr descr[] = {
FIELD(struct civetweb_info, version, JSON_TOK_STRING),
FIELD(struct civetweb_info, os, JSON_TOK_STRING),
FIELD(struct civetweb_info, feature_list, JSON_TOK_STRING),
FIELD(struct civetweb_info, build, JSON_TOK_STRING),
FIELD(struct civetweb_info, compiler, JSON_TOK_STRING),
FIELD(struct civetweb_info, data_model, JSON_TOK_STRING),
};
struct civetweb_info info = {};
char info_str[1024] = {};
int ret;
int size;
size = mg_get_system_info(info_str, sizeof(info_str));
ret = json_obj_parse(info_str, size, descr, ARRAY_SIZE(descr), &info);
send_ok(conn);
if (ret < 0) {
mg_printf(conn, "Could not retrieve: %d\n", ret);
return 500;
}
mg_printf(conn, "<html><body>");
mg_printf(conn, "<h3>Server info</h3>");
mg_printf(conn, "<ul>\n");
mg_printf(conn, "<li>host os - %s</li>\n", info.os);
mg_printf(conn, "<li>server - civetweb %s</li>\n", info.version);
mg_printf(conn, "<li>compiler - %s</li>\n", info.compiler);
mg_printf(conn, "</ul>\n");
mg_printf(conn, "</body></html>\n");
return 200;
}
int history_handler(struct mg_connection *conn, void *cbdata)
{
const struct mg_request_info *req_info = mg_get_request_info(conn);
const char *cookie = mg_get_header(conn, "Cookie");
char history_str[64];
mg_get_cookie(cookie, "history", history_str, sizeof(history_str));
mg_printf(conn, "HTTP/1.1 200 OK\r\n");
mg_printf(conn, "Connection: close\r\n");
mg_printf(conn, "Set-Cookie: history='%s'\r\n", req_info->local_uri);
mg_printf(conn, "Content-Type: text/html\r\n\r\n");
mg_printf(conn, "<html><body>");
mg_printf(conn, "<h3>Your URI is: %s<h3>\n", req_info->local_uri);
if (history_str[0] == 0) {
mg_printf(conn, "<h5>This is your first visit.</h5>\n");
} else {
mg_printf(conn, "<h5>your last /history visit was: %s</h5>\n",
history_str);
}
mg_printf(conn, "Some cookie-saving links to try:\n");
mg_printf(conn, "<ul>\n");
mg_printf(conn, "<li><a href=/history/first>first</a></li>\n");
mg_printf(conn, "<li><a href=/history/second>second</a></li>\n");
mg_printf(conn, "<li><a href=/history/third>third</a></li>\n");
mg_printf(conn, "<li><a href=/history/fourth>fourth</a></li>\n");
mg_printf(conn, "<li><a href=/history/fifth>fifth</a></li>\n");
mg_printf(conn, "</ul>\n");
mg_printf(conn, "</body></html>\n");
return 200;
}
void *main_pthread(void *arg)
{
static const char * const options[] = {
"listening_ports",
"8080",
"num_threads",
"1",
"max_request_size",
"2048",
0
};
struct mg_callbacks callbacks;
struct mg_context *ctx;
(void)arg;
memset(&callbacks, 0, sizeof(callbacks));
ctx = mg_start(&callbacks, 0, (const char **)options);
if (ctx == NULL) {
printf("Unable to start the server.");
return 0;
}
mg_set_request_handler(ctx, "/$", hello_world_handler, 0);
mg_set_request_handler(ctx, "/info$", system_info_handler, 0);
mg_set_request_handler(ctx, "/history", history_handler, 0);
return 0;
}
int main(void)
{
pthread_attr_t civetweb_attr;
pthread_t civetweb_thread;
pthread_attr_init(&civetweb_attr);
pthread_attr_setstack(&civetweb_attr, &civetweb_stack,
CIVETWEB_MAIN_THREAD_STACK_SIZE);
pthread_create(&civetweb_thread, &civetweb_attr, &main_pthread, 0);
return 0;
}

View file

@ -25,6 +25,8 @@ manifest:
remotes:
- name: upstream
url-base: https://github.com/zephyrproject-rtos
- name: civetweb
url-base: https://github.com/antmicro
#
# Please add items below based on alphabetical order
@ -32,6 +34,10 @@ manifest:
- name: ci-tools
revision: d56f2dd3510e20fa8cf4aad442495c08a658113f
path: tools/ci-tools
- name: civetweb
remote: civetweb
revision: 7ffad765f9a63a7bab3432ca45248981f559d559
path: modules/lib/civetweb
- name: esp-idf
revision: 6835bfc741bf15e98fb7971293913f770df6081f
path: modules/hal/esp-idf