Restructure with libubox

Signed-off-by: Jianhui Zhao <jianhuizhao329@gmail.com>
main
Jianhui Zhao 2017-12-29 14:40:58 +08:00
parent ef94e04328
commit 568a287feb
38 changed files with 1815 additions and 5578 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 2.8)
project(libuhttpd C)
project(libuhttp C)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules/")
@ -9,9 +9,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules/")
add_definitions(-O -Wall -Werror --std=gnu99 -D_GNU_SOURCE)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/src/include
${CMAKE_CURRENT_SOURCE_DIR}/src/include/uhttpd
${CMAKE_CURRENT_BINARY_DIR}/src/include
${CMAKE_CURRENT_SOURCE_DIR}/src/
${CMAKE_CURRENT_BINARY_DIR}/src/
)
add_subdirectory(src)

View File

@ -1,7 +1,7 @@
Contributing
================================================================================
If you want to contribute to [libuhttpd](https://github.com/zhaojh329/libuhttpd), please follow these simple rules:
If you want to contribute to [libuhttp](https://github.com/zhaojh329/libuhttpd), please follow these simple rules:
1. Press the fork button:

View File

@ -1,7 +1,7 @@
贡献代码
================================================================================
如果你想为[libuhttpd](https://github.com/zhaojh329/libuhttpd)贡献代码, 请按照如下步骤:
如果你想为[libuhttp](https://github.com/zhaojh329/libuhttpd)贡献代码, 请按照如下步骤:
1. 点击fork按钮:

149
README.md
View File

@ -2,111 +2,100 @@
![](https://img.shields.io/badge/license-GPLV3-brightgreen.svg?style=plastic "License")
[libev]: http://software.schmorp.de/pkg/libev.html
[http-parser]: https://github.com/nodejs/http-parser
[libubox]: https://git.lede-project.org/?p=project/libubox.git
[uhttpd]: https://git.lede-project.org/?p=project/uhttpd.git
[ustream-ssl]: https://git.lede-project.org/?p=project/ustream-ssl.git
[openssl]: https://github.com/openssl/openssl
[mbedtls]: https://github.com/ARMmbed/mbedtls
[CyaSSl(wolfssl)]: https://github.com/wolfSSL/wolfssl
A very tiny and fast HTTP server library based on [libev] and [http-parser] for Embedded Linux.
Support HTTPS(alternative OpenSSL and CyaSSl(wolfssl)) and if you're sensitive to size,
you can choose CyaSSl(wolfssl).
A very tiny and fast HTTP server library based on [libubox] and referenced from [uhttpd] for Embedded Linux.
`Keep Watching for More Actions on This Space`
# Features
* tiny and fast
* use [libev] as its event backend
* support HTTPS: alternative OpenSSL and CyaSSl(wolfssl)
* use [libubox] as its event backend
* support HTTPS: OpenSSL, mbedtls and CyaSSl(wolfssl)
* flexible and you can easily extend your application to have HTTP/HTTPS services
* Lua template: embed LUA code into HTML code, like embedding PHP to HTML
* code structure is concise and understandable, also suitable for learning
# Why use [libev] as its backend?
[libev] tries to do one thing only (POSIX event library), and this in the most efficient way possible.
Libevent tries to give you the full solution (event lib, non-blocking I/O library, http server, DNS client).
[libev] tries to follow the UNIX toolbox philosophy of doing one thing only, as good as possible.
# How to Build
## Install dependency Tools and Libraries
Ubuntu
~$ sudo apt install gcc cmake libev-dev libssl-dev libwolfssl-dev
CentOS
~$ sudo yum install gcc cmake libev-devel openssl-devel
## Clone the repository
~$ git clone https://github.com/zhaojh329/libuhttpd.git
~$ cd libuhttpd
## Create the build directory
~/libuhttpd$ mkdir build
~/libuhttpd$ cd build
# Dependencies
* [libubox]
* [ustream-ssl]: If you need to support SSL
* [mbedtls]: If you choose mbedtls as your SSL backend
* [CyaSSl(wolfssl)]: If you choose wolfssl as your SSL backend
* [openssl]: If you choose openssl as your SSL backend
## Configure
See which configuration are supported
~/libuhttpd/build$ cmake .. -L
~/libuhttpd/build$ cmake .. -LH
Default configure: automatically select the SSL library as its SSL backend(If there is a SSL library available)
~/libuhttpd/build$ cmake ..
Disable SSl support
~/libuhttpd/build$ cmake .. -DUHTTP_DISABLE_SSL=1
Explicit use OpenSSL as its SSL backend
~/libuhttpd/build$ cmake .. -DUHTTP_USE_OPENSSL=1
Explicit use CyaSSl(wolfssl) as its SSL backend
~/libuhttpd/build$ cmake .. -DUHTTP_USE_CYASSL=1
Turn on debug
~/libuhttpd/build$ cmake .. -DUHTTP_DEBUG=1
## Build and install libuhttpd
~/libuhttpd/build$ make && sudo make install
~/libuhttp/build$ cmake .. -L
~/libuhttp/build$ cmake .. -LH
## Run the Example
First generate the SSL certificate file
~/libuhttpd/build$ cd ..
~/libuhttpd$ ./gen_cert.sh
~/libuhttp/build$ cd ..
~/libuhttp$ ./gen_cert.sh
Run
~/libuhttpd$ ./build/example/helloworld
~/libuhttp$ ./build/example/helloworld
Then use the command curl or browser to test
$ curl -k 'https://127.0.0.1:8000/test?name=context%3d%7b"nid"%3a"test"%7d' -v
If use browser to test, it will be show
Hello World
Libuhttp v0.1
Url: /test?name=context%3d%7b%22nid%22%3a%22test%22%7d
Path: /test
Name: context%3d%7b%22nid%22%3a%22test%22%7d
Unescaped Name: context={"nid":"test"}
Host: 192.168.0.100:8000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
$ curl -k 'https://127.0.0.1:8000/hello?name=test' -v
# [Example](https://github.com/zhaojh329/libuhttpd/blob/master/example/helloworld.c)
# Example
``
#include <uhttpd.h>
#define port "8000"
static void hello_action(struct uh_client *cl)
{
cl->send_header(cl, 200, "OK", -1);
cl->append_header(cl, "Myheader", "Hello");
cl->header_end(cl);
cl->chunk_printf(cl, "<h1>Hello Libuhttpd %s</h1>", UHTTPD_VERSION_STRING);
cl->chunk_printf(cl, "<h1>REMOTE_ADDR: %s</h1>", cl->get_peer_addr(cl));
cl->chunk_printf(cl, "<h1>PATH: %s</h1>", cl->get_path(cl));
cl->chunk_printf(cl, "<h1>QUERY: %s</h1>", cl->get_query(cl));
cl->request_done(cl);
}
int main(int argc, char **argv)
{
struct uh_server *srv = NULL;
uh_log_debug("libuhttpd version: %s", UHTTPD_VERSION_STRING);
uloop_init();
srv = uh_server_new("0.0.0.0", port);
if (!srv)
goto done;
uh_log_debug("Listen on: *:%s", port);
#if (UHTTPD_SSL_SUPPORT)
if (srv->ssl_init(srv, "server-key.pem", "server-cert.pem") < 0)
goto done;
#endif
uh_add_action(srv, "/hello", hello_action);
uloop_run();
done:
uloop_done();
srv->free(srv);
return 0;
}
``
# Contributing
If you would like to help making [libuhttpd](https://github.com/zhaojh329/libuhttpd) better,
see the [CONTRIBUTING.md](https://github.com/zhaojh329/libuhttpd/blob/master/CONTRIBUTING.md) file.
# Thanks for the following project
* [libev]
* [http-parser]
* [mongoose](https://github.com/cesanta/mongoose)

View File

@ -1,104 +1,100 @@
# libuhttpd
# libuhttp
![](https://img.shields.io/badge/license-GPLV3-brightgreen.svg?style=plastic "License")
[libev]: http://software.schmorp.de/pkg/libev.html
[http-parser]: https://github.com/nodejs/http-parser
[libubox]: https://git.lede-project.org/?p=project/libubox.git
[uhttpd]: https://git.lede-project.org/?p=project/uhttpd.git
[ustream-ssl]: https://git.lede-project.org/?p=project/ustream-ssl.git
[openssl]: https://github.com/openssl/openssl
[mbedtls]: https://github.com/ARMmbed/mbedtls
[CyaSSl(wolfssl)]: https://github.com/wolfSSL/wolfssl
一个专门针对嵌入式Linux的非常小巧且快速的HTTP C库基于[libev]和[http-parser]。支持HTTPS
SSL后端可以选择OpenSSL和CyaSSl(wolfssl)如果你对大小敏感那么你可以选择CyaSSl(wolfssl)。
一个专门针对嵌入式Linux的非常小巧且快速的HTTP服务器C库基于[libubox],参考了[uhttpd]。
`请保持关注以获取最新的项目动态`
# 特性
* 小巧且快速
* 使用[libev]作为其事件后端
* 支持HTTPS: 可以选择OpenSSL和CyaSSl(wolfssl)
* 使用[libubox]作为其事件后端
* 支持HTTPS: OpenSSL, mbedtls and CyaSSl(wolfssl)
* 可伸缩你可以非常方便的扩展你的应用程序使之具备HTTP/HTTPS服务
* Lua模板嵌入LUA代码到HTML中就像嵌入PHP到HTML中一样
* 代码结构简洁通俗易懂,亦适合学习
# 为什么使用[libev]作为其事件后端?
[libev]试图做好一件事而已目标是成为POSIX的事件库这是最高效的方法。
libevent则尝试给你全套解决方案事件库非阻塞IO库http库DNS客户端
[libev]尝试追随UNIX工具箱哲学一次只干一件事每次都做到最好。
# 如何编译
## 安装依赖的工具和库
Ubuntu
~$ sudo apt install gcc cmake libev-dev libssl-dev libwolfssl-dev
CentOS
~$ sudo yum install gcc cmake libev-devel openssl-devel
## 克隆仓库代码
~$ git clone https://github.com/zhaojh329/libuhttpd.git
~$ cd libuhttpd
## 创建编译目录
~/libuhttpd$ mkdir build
~/libuhttpd$ cd build
# 依赖
* [libubox]
* [ustream-ssl]: 如果你需要支持SSL
* [mbedtls]: 如果你选择mbedtls作为你的SSL后端
* [CyaSSl(wolfssl)]: 如果你选择wolfssl作为你的SSL后端
* [openssl]: 如果你选择openssl作为你的SSL后端
## 配置
查看支持哪些配置选项
~/libuhttpd/build$ cmake .. -L
~/libuhttpd/build$ cmake .. -LH
默认配置: 自动选择SSL库(如果有可用的SSL库)
~/libuhttpd/build$ cmake ..
禁用SSL
~/libuhttpd/build$ cmake .. -DUHTTP_DISABLE_SSL=1
强制选择OpenSSL
~/libuhttpd/build$ cmake .. -DUHTTP_USE_OPENSSL=1
强制选择CyaSSl(wolfssl)
~/libuhttpd/build$ cmake .. -DUHTTP_USE_CYASSL=1
打开调试
~/libuhttpd/build$ cmake .. -DUHTTP_DEBUG=1
## 编译和安装libuhttpd
~/libuhttpd/build$ make && sudo make install
~/libuhttp/build$ cmake .. -L
~/libuhttp/build$ cmake .. -LH
## 运行例子
首先生成SSL证书文件
~/libuhttpd/build$ cd ..
~/libuhttpd$ ./gen_cert.sh
~/libuhttp/build$ cd ..
~/libuhttp$ ./gen_cert.sh
运行
~/libuhttpd$ ./build/example/helloworld
~/libuhttp$ ./build/example/helloworld
然后使用命令curl或者浏览器进行测试
$ curl -k 'https://127.0.0.1:8000/test?name=context%3d%7b"nid"%3a"test"%7d' -v
$ curl -k 'https://127.0.0.1:8000/hello?name=test' -v
如果使用浏览器测试,浏览器将会输出
# 示例程序]
``
#include <uhttpd.h>
Hello World
Libuhttp v0.1
Url: /test?name=context%3d%7b%22nid%22%3a%22test%22%7d
Path: /test
Name: context%3d%7b%22nid%22%3a%22test%22%7d
Unescaped Name: context={"nid":"test"}
Host: 192.168.0.100:8000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
# [示例程序](https://github.com/zhaojh329/libuhttpd/blob/master/example/helloworld.c)
#define port "8000"
static void hello_action(struct uh_client *cl)
{
cl->send_header(cl, 200, "OK", -1);
cl->append_header(cl, "Myheader", "Hello");
cl->header_end(cl);
cl->chunk_printf(cl, "<h1>Hello Libuhttpd %s</h1>", UHTTPD_VERSION_STRING);
cl->chunk_printf(cl, "<h1>REMOTE_ADDR: %s</h1>", cl->get_peer_addr(cl));
cl->chunk_printf(cl, "<h1>PATH: %s</h1>", cl->get_path(cl));
cl->chunk_printf(cl, "<h1>QUERY: %s</h1>", cl->get_query(cl));
cl->request_done(cl);
}
int main(int argc, char **argv)
{
struct uh_server *srv = NULL;
uh_log_debug("libuhttpd version: %s", UHTTPD_VERSION_STRING);
uloop_init();
srv = uh_server_new("0.0.0.0", port);
if (!srv)
goto done;
uh_log_debug("Listen on: *:%s", port);
#if (UHTTPD_SSL_SUPPORT)
if (srv->ssl_init(srv, "server-key.pem", "server-cert.pem") < 0)
goto done;
#endif
uh_add_action(srv, "/hello", hello_action);
uloop_run();
done:
uloop_done();
srv->free(srv);
return 0;
}
``
# 贡献代码
如果你想帮助[libuhttpd](https://github.com/zhaojh329/libuhttpd)变得更好,请参考
@ -106,8 +102,3 @@ CentOS
# 技术交流
QQ群153530783
# 感谢以下开源项目提供帮助
* [libev]
* [http-parser]
* [mongoose](https://github.com/cesanta/mongoose)

View File

@ -1,34 +0,0 @@
# - Try to find libev
# Once done this will define
# LIBEV_FOUND - System has libev
# LIBEV_INCLUDE_DIRS - The libev include directories
# LIBEV_LIBRARIES - The libraries needed to use libev
find_path(LIBEV_INCLUDE_DIR ev.h)
find_library(LIBEV_LIBRARY ev PATH_SUFFIXES lib64)
if(LIBEV_INCLUDE_DIR)
file(STRINGS "${LIBEV_INCLUDE_DIR}/ev.h"
LIBEV_VERSION_MAJOR REGEX "^#define[ \t]+EV_VERSION_MAJOR[ \t]+[0-9]+")
file(STRINGS "${LIBEV_INCLUDE_DIR}/ev.h"
LIBEV_VERSION_MINOR REGEX "^#define[ \t]+EV_VERSION_MINOR[ \t]+[0-9]+")
string(REGEX REPLACE "[^0-9]+" "" LIBEV_VERSION_MAJOR "${LIBEV_VERSION_MAJOR}")
string(REGEX REPLACE "[^0-9]+" "" LIBEV_VERSION_MINOR "${LIBEV_VERSION_MINOR}")
set(LIBEV_VERSION "${LIBEV_VERSION_MAJOR}.${LIBEV_VERSION_MINOR}")
unset(LIBEV_VERSION_MINOR)
unset(LIBEV_VERSION_MAJOR)
endif()
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBEV_FOUND to TRUE
# if all listed variables are TRUE and the requested version matches.
find_package_handle_standard_args(Libev REQUIRED_VARS
LIBEV_LIBRARY LIBEV_INCLUDE_DIR
VERSION_VAR LIBEV_VERSION)
if(LIBEV_FOUND)
set(LIBEV_LIBRARIES ${LIBEV_LIBRARY})
set(LIBEV_INCLUDE_DIRS ${LIBEV_INCLUDE_DIR})
endif()
mark_as_advanced(LIBEV_INCLUDE_DIR LIBEV_LIBRARY)

View File

@ -0,0 +1,22 @@
# - Try to find libubox
# Once done this will define
# LIBUBOX_FOUND - System has libubox
# LIBUBOX_INCLUDE_DIRS - The libubox include directories
# LIBUBOX_LIBRARIES - The libraries needed to use libubox
find_path(LIBUBOX_INCLUDE_DIR uloop.h PATH_SUFFIXES libubox)
find_library(LIBUBOX_LIBRARY ubox PATH_SUFFIXES lib64)
include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set LIBUBOX_FOUND to TRUE
# if all listed variables are TRUE and the requested version matches.
find_package_handle_standard_args(Libubox REQUIRED_VARS
LIBUBOX_LIBRARY LIBUBOX_INCLUDE_DIR
VERSION_VAR LIBUBOX_VERSION)
if(LIBUBOX_FOUND)
set(LIBUBOX_LIBRARIES ${LIBUBOX_LIBRARY})
set(LIBUBOX_INCLUDE_DIRS ${LIBUBOX_INCLUDE_DIR})
endif()
mark_as_advanced(LIBUBOX_INCLUDE_DIR LIBUBOX_LIBRARY)

View File

@ -1,6 +1,2 @@
add_executable(helloworld helloworld.c)
target_link_libraries(helloworld uhttpd)
add_executable(lua_template lua_template.c)
target_link_libraries(lua_template uhttpd)

View File

@ -1,80 +1,45 @@
#include <ev.h>
#include <stdio.h>
#include <uhttpd.h>
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
#define port "8000"
static void hello_action(struct uh_client *cl)
{
printf("Got signal: %d\n", w->signum);
ev_break(loop, EVBREAK_ALL);
}
cl->send_header(cl, 200, "OK", -1);
cl->append_header(cl, "Myheader", "Hello");
cl->header_end(cl);
void hook_test(struct uh_connection *con)
{
struct uh_str *url = uh_get_url(con);
struct uh_str *path = uh_get_path(con);
struct uh_str name = uh_get_var(con, "name");
struct uh_str *header_host = uh_get_header(con, "Host");
struct uh_str *header_ua = uh_get_header(con, "User-Agent");
char unescaped_name[128];
uh_send_head(con, HTTP_STATUS_OK, -1, NULL);
uh_printf_chunk(con, "<h1>Hello World</h1>");
uh_printf_chunk(con, "<h1>Libuhttp v%s</h1>", uh_version());
uh_printf_chunk(con, "<h1>Url: %.*s</h1>", (int)url->len, url->at);
uh_printf_chunk(con, "<h1>Path: %.*s</h1>", (int)path->len, path->at);
uh_printf_chunk(con, "<h1>Name: %.*s</h1>", (int)name.len, name.at);
if (name.at) {
uh_unescape(name.at, name.len, unescaped_name, sizeof(unescaped_name));
uh_printf_chunk(con, "<h1>Unescaped Name: %s</h1>", unescaped_name);
}
if (header_host)
uh_printf_chunk(con, "<h1>Host: %.*s</h1>", (int)header_host->len, header_host->at);
if (header_ua)
uh_printf_chunk(con, "<h1>User-Agent: %.*s</h1>", (int)header_ua->len, header_ua->at);
uh_send_chunk(con, NULL, 0);
cl->chunk_printf(cl, "<h1>Hello Libuhttpd %s</h1>", UHTTPD_VERSION_STRING);
cl->chunk_printf(cl, "<h1>REMOTE_ADDR: %s</h1>", cl->get_peer_addr(cl));
cl->chunk_printf(cl, "<h1>PATH: %s</h1>", cl->get_path(cl));
cl->chunk_printf(cl, "<h1>QUERY: %s</h1>", cl->get_query(cl));
cl->request_done(cl);
}
int main(int argc, char **argv)
{
struct ev_loop *loop = EV_DEFAULT;
ev_signal *sig_watcher = NULL;
struct uh_server *srv = NULL;
uh_log_info("libuhttp version: %s\n", uh_version());
sig_watcher = calloc(1, sizeof(ev_signal));
if (!sig_watcher)
return -1;
ev_signal_init(sig_watcher, signal_cb, SIGINT);
ev_signal_start(loop, sig_watcher);
uh_log_debug("libuhttpd version: %s", UHTTPD_VERSION_STRING);
srv = uh_server_new(loop, "0.0.0.0", 8000);
if (!srv) {
uh_log_err("uh_server_new failed\n");
goto err;
}
uloop_init();
#if (UHTTP_SSL_ENABLED)
if (uh_ssl_init(srv, "server-cert.pem", "server-key.pem") < 0)
goto err;
srv = uh_server_new("0.0.0.0", port);
if (!srv)
goto done;
uh_log_debug("Listen on: *:%s", port);
#if (UHTTPD_SSL_SUPPORT)
if (srv->ssl_init(srv, "server-key.pem", "server-cert.pem") < 0)
goto done;
#endif
uh_register_hook(srv, "/test", hook_test);
uh_add_action(srv, "/hello", hello_action);
uh_log_info("Listen on 8000...\n");
ev_run(loop, 0);
err:
free(sig_watcher);
uh_server_free(srv);
uloop_run();
done:
uloop_done();
srv->free(srv);
return 0;
}

View File

@ -1,56 +0,0 @@
#include <ev.h>
#include <stdio.h>
#include <uhttpd.h>
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
{
printf("Got signal: %d\n", w->signum);
ev_break(loop, EVBREAK_ALL);
}
static void lua_template(struct uh_connection *con)
{
uh_template(con);
}
int main(int argc, char **argv)
{
struct ev_loop *loop = EV_DEFAULT;
ev_signal *sig_watcher = NULL;
struct uh_server *srv = NULL;
uh_log_info("libuhttp version: %s", uh_version());
sig_watcher = calloc(1, sizeof(ev_signal));
if (!sig_watcher)
return -1;
ev_signal_init(sig_watcher, signal_cb, SIGINT);
ev_signal_start(loop, sig_watcher);
srv = uh_server_new(loop, "0.0.0.0", 8000);
if (!srv) {
uh_log_err("uh_server_new failed");
goto err;
}
#if (UHTTP_SSL_ENABLED)
if (uh_ssl_init(srv, "server-cert.pem", "server-key.pem") < 0)
goto err;
#endif
uh_register_default_hook(srv, lua_template);
uh_log_info("Listen on 8000...");
ev_run(loop, 0);
err:
free(sig_watcher);
uh_server_free(srv);
return 0;
}

View File

@ -1,16 +0,0 @@
<h1>Lua Template Test</h1>
<h1>Date: <%=os.date()%></h1>
<h1>Method: <%=_UHTTP["HTTP_METHOD"]%></h1>
<h1>Remote Host: <%=_UHTTP["REMOTE_HOST"]%></h1>
<h1>Url: <%=_UHTTP["HTTP_URL"]%></h1>
<h1>Path: <%=_UHTTP["HTTP_PATH"]%></h1>
<h1>------------Http Headers-------------</h1>
<%for k, v in pairs(_UHTTP["HEADERS"]) do%>
<h1><%=k%>: <%=v%></h1>
<%end%>
<h1>------------Http Variables-------------</h1>
<%for k, v in pairs(_UHTTP["VARIABLES"]) do%>
<h1><%=k%>: <%=v%></h1>
<%end%>

View File

@ -1,17 +1,17 @@
# The version number.
set(UHTTPD_VERSION_MAJOR 0)
set(UHTTPD_VERSION_MINOR 4)
set(UHTTPD_VERSION_MINOR 3)
set(UHTTPD_VERSION_PATCH 0)
# Check the third party Libraries
find_package(Libev REQUIRED)
find_package(Libubox REQUIRED)
find_package(Lua REQUIRED)
find_package(OpenSSL)
find_package(CyaSSL)
find_package(Lua)
include_directories(${LIBEV_INCLUDE_DIR} ${LUA_INCLUDE_DIR})
set(EXTRA_LIBS ${LIBEV_LIBRARIES} ${LUA_LIBRARIES})
set(SOURCE_FILES uhttpd.c log.c buf.c ssl.c parser.c template.c)
include_directories(${LIBUBOX_INCLUDE_DIR} ${LUA_INCLUDE_DIR})
set(EXTRA_LIBS ${LIBUBOX_LIBRARIES} ${LUA_LIBRARIES} dl)
set(SOURCE_FILES uhttpd.c client.c log.c utils.c file.c action.c)
set(UHTTPD_DEBUG_CONFIG 0)
option(UHTTPD_DEBUG "Turn on debug" OFF)
@ -19,57 +19,14 @@ if(UHTTPD_DEBUG)
set(UHTTPD_DEBUG_CONFIG 1)
endif()
option(UHTTPD_DISABLE_SSL "Disable ssl support" OFF)
option(UHTTPD_USE_OPENSSL "Explicit use OpenSSL as SSL backend" OFF)
option(UHTTPD_USE_CYASSL "Explicit use CyaSSL as SSL backend" OFF)
set(UHTTPD_SSL_SUPPORT_CONFIG 1)
option(UHTTPD_SSL_SUPPORT "SSL support" ON)
set(UHTTPD_SSL_ENABLED OFF)
set(UHTTPD_SSL_ENABLED_CONFIG 0)
set(UHTTPD_USE_OPENSSL_CONFIG 0)
set(UHTTPD_USE_CYASSL_CONFIG 0)
if(NOT UHTTPD_DISABLE_SSL)
if(UHTTPD_USE_OPENSSL)
if(NOT OPENSSL_FOUND)
message(FATAL_ERROR "Could NOT find OpenSSL")
endif()
set(UHTTPD_SSL_ENABLED ON)
set(UHTTPD_SSL_ENABLED_CONFIG 1)
set(UHTTPD_USE_OPENSSL_CONFIG 1)
list(APPEND EXTRA_LIBS ${OPENSSL_LIBRARIES})
include_directories(${OPENSSL_INCLUDE_DIR})
elseif(UHTTPD_USE_CYASSL)
if (NOT CYASSL_FOUND)
message(FATAL_ERROR "Could NOT find CyaSSL")
endif()
set(UHTTPD_SSL_ENABLED ON)
set(UHTTPD_SSL_ENABLED_CONFIG 1)
set(UHTTPD_USE_CYASSL_CONFIG 1)
list(APPEND EXTRA_LIBS ${CYASSL_LIBRARIES})
include_directories(${CYASSL_INCLUDE_DIR})
endif()
if(NOT UHTTPD_SSL_ENABLED)
if(OPENSSL_FOUND)
set(UHTTPD_SSL_ENABLED ON)
set(UHTTPD_SSL_ENABLED_CONFIG 1)
set(UHTTPD_USE_OPENSSL_CONFIG 1)
list(APPEND EXTRA_LIBS ${OPENSSL_LIBRARIES})
include_directories(${OPENSSL_INCLUDE_DIR})
elseif(CYASSL_FOUND)
set(UHTTPD_SSL_ENABLED ON)
set(UHTTPD_SSL_ENABLED_CONFIG 1)
set(UHTTPD_USE_CYASSL_CONFIG 1)
list(APPEND EXTRA_LIBS ${CYASSL_LIBRARIES})
include_directories(${CYASSL_INCLUDE_DIR})
endif()
endif()
if(UHTTPD_SSL_SUPPORT)
list(APPEND SOURCE_FILES uh_ssl.c)
else()
set(UHTTPD_SSL_SUPPORT_CONFIG 0)
endif()
add_library(uhttpd SHARED ${SOURCE_FILES})
@ -77,27 +34,20 @@ set_target_properties(uhttpd PROPERTIES VERSION ${UHTTPD_VERSION_MAJOR}.${UHTTPD
target_link_libraries(uhttpd ${EXTRA_LIBS})
# configure a header file to pass some of the CMake settings to the source code
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/uhttpd/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/uhttpd/config.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/include/uhttpd/config.h
${CMAKE_CURRENT_SOURCE_DIR}/include/uhttpd/uhttpd.h
${CMAKE_CURRENT_SOURCE_DIR}/include/uhttpd/log.h
${CMAKE_CURRENT_SOURCE_DIR}/include/uhttpd/buf.h
${CMAKE_CURRENT_SOURCE_DIR}/include/uhttpd/str.h
${CMAKE_CURRENT_SOURCE_DIR}/include/uhttpd/parser.h
${CMAKE_CURRENT_BINARY_DIR}/config.h
${CMAKE_CURRENT_SOURCE_DIR}/uhttpd.h
${CMAKE_CURRENT_SOURCE_DIR}/log.h
${CMAKE_CURRENT_SOURCE_DIR}/client.h
${CMAKE_CURRENT_SOURCE_DIR}/common.h
${CMAKE_CURRENT_SOURCE_DIR}/action.h
DESTINATION
include/uhttpd
)
install(
FILES
${CMAKE_CURRENT_SOURCE_DIR}/include/uhttpd.h
DESTINATION
include
)
install(
TARGETS uhttpd LIBRARY
DESTINATION lib
@ -105,12 +55,6 @@ install(
message("")
message(STATUS "UHTTPD_VERSION: ${UHTTPD_VERSION_MAJOR}.${UHTTPD_VERSION_MINOR}.${UHTTPD_VERSION_PATCH}")
if(UHTTPD_USE_OPENSSL_CONFIG)
message(STATUS "UHTTPD_SSL_ENABLED: OpenSSL")
elseif(UHTTPD_USE_CYASSL_CONFIG)
message(STATUS "UHTTPD_SSL_ENABLED: CyaSSL")
else()
message(STATUS "UHTTPD_SSL_ENABLED: None")
endif()
message(STATUS "DEBUG: ${UHTTPD_DEBUG}")
message(STATUS "UHTTPD_SSL_SUPPORT: ${UHTTPD_SSL_SUPPORT}")
message(STATUS "UHTTPD_DEBUG: ${UHTTPD_DEBUG}")
message("")

70
src/action.c 100755
View File

@ -0,0 +1,70 @@
/*
* The Action handler is a simple libuhttpd handler that processes requests
* by invoking registered C functions. The action handler is ideal for
* situations when you want to generate a simple response using C code.
*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "action.h"
#include "uhttpd.h"
int uh_add_action(struct uh_server *srv, const char *path, action_cb_t cb)
{
struct uh_action *a;
a = calloc(1, sizeof(struct uh_action));
if (!a) {
uh_log_err("calloc");
return -1;
}
if (strlen(path) > sizeof(a->path) - 1) {
uh_log_err("The given path is too long");
goto err;
}
a->avl.key = strcpy(a->path, path);
a->cb = cb;
avl_insert(&srv->actions, &a->avl);
return 0;
err:
free(a);
return -1;
}
bool handle_action_request(struct uh_client *cl, char *path)
{
struct uh_action *a;
a = avl_find_element(&cl->srv->actions, path, a, avl);
if (a) {
a->cb(cl);
return true;
}
return false;
}
void uh_action_free(struct uh_server *srv)
{
struct uh_action *node, *tmp;
avl_remove_all_elements(&srv->actions, node, avl, tmp)
free(node);
}

41
src/action.h 100755
View File

@ -0,0 +1,41 @@
/*
* The Action handler is a simple libuhttpd handler that processes requests
* by invoking registered C functions. The action handler is ideal for
* situations when you want to generate a simple response using C code.
*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ACTION_H
#define _ACTION_H
#include "client.h"
typedef void (*action_cb_t)(struct uh_client *cl);
struct uh_action {
struct avl_node avl;
char path[PATH_MAX];
action_cb_t cb;
};
int uh_add_action(struct uh_server *srv, const char *path, action_cb_t cb);
bool handle_action_request(struct uh_client *cl, char *url);
void uh_action_free(struct uh_server *srv);
#endif

View File

@ -1,83 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <string.h>
#include "uhttpd/buf.h"
#include "uhttpd/log.h"
int uh_buf_init(struct uh_buf *buf, size_t initial_size)
{
buf->len = buf->size = 0;
if (buf->base) {
free(buf->base);
buf->base = NULL;
}
if (initial_size > 0) {
buf->base = malloc(initial_size);
if (!buf->base)
return -1;
buf->size = initial_size;
}
return 0;
}
int uh_buf_grow(struct uh_buf *buf, size_t size)
{
void *base = realloc(buf->base, buf->size + size);
if (!base)
return -1;
buf->base = base;
buf->size += size;
return 0;
}
void uh_buf_free(struct uh_buf *buf)
{
uh_buf_init(buf, 0);
}
size_t uh_buf_append(struct uh_buf *buf, const void *data, size_t len)
{
assert(buf);
if (!data)
return 0;
if (buf->len + len > buf->size) {
if (uh_buf_grow(buf, len << UH_BUF_SIZE_MULTIPLIER) == -1)
len = buf->size - buf->len;
}
memcpy(buf->base + buf->len, data, len);
buf->len += len;
return len;
}
void uh_buf_remove(struct uh_buf *buf, size_t n)
{
if (n > 0 && n <= buf->len) {
memmove(buf->base, buf->base + n, buf->len - n);
buf->len -= n;
}
}

472
src/client.c 100755
View File

@ -0,0 +1,472 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "uhttpd.h"
#include "client.h"
#include "file.h"
#include "utils.h"
#include "uh_ssl.h"
const char *const http_versions[] = {
[UH_HTTP_VER_0_9] = "HTTP/0.9",
[UH_HTTP_VER_1_0] = "HTTP/1.0",
[UH_HTTP_VER_1_1] = "HTTP/1.1"
};
const char *const http_methods[] = {
[UH_HTTP_MSG_GET] = "GET",
[UH_HTTP_MSG_POST] = "POST",
[UH_HTTP_MSG_HEAD] = "HEAD"
};
static inline void client_send(struct uh_client *cl, const void *data, int len)
{
ustream_write(cl->us, data, len, true);
}
static void client_send_header(struct uh_client *cl, int code, const char *summary, int length)
{
struct http_request *r = &cl->request;
cl->printf(cl, "%s %03i %s\r\n", http_versions[cl->request.version], code, summary);
cl->printf(cl, "Server: Libuhttpd %s\r\n", UHTTPD_VERSION_STRING);
if (length < 0) {
r->chunked = true;
cl->printf(cl, "Transfer-Encoding: chunked\r\n");
} else {
cl->printf(cl, "Content-Length: %d\r\n", length);
}
}
static inline void client_append_header(struct uh_client *cl, const char *name, const char *value)
{
cl->printf(cl, "%s: %s\r\n", name, value);
}
static inline void client_header_end(struct uh_client *cl)
{
cl->printf(cl, "\r\n");
}
static void client_send_error(struct uh_client *cl, int code, const char *summary, const char *fmt, ...)
{
va_list arg;
cl->send_header(cl, code, summary, -1);
cl->printf(cl, "Content-Type: text/html\r\n\r\n");
cl->chunk_printf(cl, "<h1>%s</h1>", summary);
if (fmt) {
va_start(arg, fmt);
cl->chunk_vprintf(cl, fmt, arg);
va_end(arg);
}
cl->request_done(cl);
}
static inline const char *client_get_peer_addr(struct uh_client *cl)
{
return inet_ntoa(cl->peer_addr.sin_addr);
}
static inline const char *client_get_path(struct uh_client *cl)
{
return kvlist_get(&cl->request.hdr, "path");
}
static inline const char *client_get_query(struct uh_client *cl)
{
return kvlist_get(&cl->request.hdr, "query");
}
static inline const char *client_get_header(struct uh_client *cl, const char *name)
{
return kvlist_get(&cl->request.hdr, name);
}
static void uh_handle_request(struct uh_client *cl)
{
char *path = kvlist_get(&cl->request.hdr, "path");
#if (UHTTPD_DEBUG)
const char *name, *value;
kvlist_for_each(&cl->request.hdr, name, value) {
uh_log_debug("%s: %s", name, value);
}
#endif
if (handle_action_request(cl, path))
return;
if (handle_file_request(cl, path))
return;
cl->send_error(cl, 404, "Not Found", "The requested PATH %s was not found on this server.", path);
}
static inline void connection_close(struct uh_client *cl)
{
cl->us->eof = true;
cl->state = CLIENT_STATE_CLOSE;
ustream_state_change(cl->us);
}
static inline void keepalive_cb(struct uloop_timeout *timeout)
{
struct uh_client *cl = container_of(timeout, struct uh_client, timeout);
connection_close(cl);
}
static void dispatch_done(struct uh_client *cl)
{
if (cl->dispatch.free)
cl->dispatch.free(cl);
if (cl->dispatch.req_free)
cl->dispatch.req_free(cl);
}
static inline int hdr_get_len(struct kvlist *kv, const void *data)
{
return strlen(data);
}
static void client_request_done(struct uh_client *cl)
{
struct http_request *r = &cl->request;
if (r->chunked)
cl->printf(cl, "0\r\n\r\n");
dispatch_done(cl);
if (cl->connection_close) {
connection_close(cl);
return;
}
cl->state = CLIENT_STATE_INIT;
memset(&cl->request, 0, sizeof(cl->request));
memset(&cl->dispatch, 0, sizeof(cl->dispatch));
kvlist_init(&cl->request.hdr, hdr_get_len);
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
}
static void client_free(struct uh_client *cl)
{
if (cl) {
dispatch_done(cl);
uloop_timeout_cancel(&cl->timeout);
if (cl->srv->ssl)
uh_ssl_client_detach(cl);
ustream_free(&cl->sfd.stream);
shutdown(cl->sfd.fd.fd, SHUT_RDWR);
close(cl->sfd.fd.fd);
list_del(&cl->list);
kvlist_free(&cl->request.hdr);
cl->srv->nclients--;
uh_log_debug("client_free: %s:%d", inet_ntoa(cl->peer_addr.sin_addr), cl->peer_addr.sin_port);
free(cl);
}
}
static int client_parse_request(struct uh_client *cl, char *data)
{
struct http_request *req = &cl->request;
char *type, *url, *version, *p;
int h_method, h_version;
static char path[PATH_MAX];
type = strtok(data, " ");
url = strtok(NULL, " ");
version = strtok(NULL, " ");
if (!type || !url || !version)
return CLIENT_STATE_DONE;
h_method = find_idx(http_methods, ARRAY_SIZE(http_methods), type);
h_version = find_idx(http_versions, ARRAY_SIZE(http_versions), version);
if (h_method < 0 || h_version < 0) {
req->version = UH_HTTP_VER_1_0;
return CLIENT_STATE_DONE;
}
p = strchr(url, '?');
if (p) {
*p = 0;
if (p[1])
kvlist_set(&cl->request.hdr, "query", p + 1);
}
if (uh_urldecode(path, sizeof(path) - 1, url, strlen(url)) < 0)
return CLIENT_STATE_DONE;
kvlist_set(&cl->request.hdr, "path", path);
req->method = h_method;
req->version = h_version;
if (req->version < UH_HTTP_VER_1_1)
cl->connection_close = true;
uh_log_debug("http method: %s", http_methods[h_method]);
uh_log_debug("http version: %s", http_versions[h_version]);
return CLIENT_STATE_HEADER;
}
static bool client_init_cb(struct uh_client *cl, char *buf, int len)
{
char *newline;
newline = strstr(buf, "\r\n");
if (!newline)
return false;
if (newline == buf) {
ustream_consume(cl->us, 2);
return true;
}
*newline = 0;
cl->state = client_parse_request(cl, buf);
ustream_consume(cl->us, newline + 2 - buf);
if (cl->state == CLIENT_STATE_DONE)
cl->send_error(cl, 400, "Bad Request", NULL);
return true;
}
static void client_poll_post_data(struct uh_client *cl)
{
struct http_request *r = &cl->request;
if (cl->state == CLIENT_STATE_DONE)
return;
if (!r->content_length && !r->transfer_chunked &&
cl->state != CLIENT_STATE_DONE) {
if (cl->dispatch.data_done)
cl->dispatch.data_done(cl);
cl->state = CLIENT_STATE_DONE;
}
}
static inline bool client_data_cb(struct uh_client *cl, char *buf, int len)
{
client_poll_post_data(cl);
return false;
}
static void client_parse_header(struct uh_client *cl, char *data)
{
struct http_request *r = &cl->request;
char *err;
char *name;
char *val;
if (!*data) {
uloop_timeout_cancel(&cl->timeout);
cl->state = CLIENT_STATE_DATA;
uh_handle_request(cl);
return;
}
val = uh_split_header(data);
if (!val) {
cl->state = CLIENT_STATE_DONE;
return;
}
for (name = data; *name; name++)
if (isupper(*name))
*name = tolower(*name);
if (!strcmp(data, "content-length")) {
r->content_length = strtoul(val, &err, 0);
if (err && *err) {
cl->send_error(cl, 400, "Bad Request", NULL);
return;
}
} else if (!strcmp(data, "transfer-encoding") && !strcmp(val, "chunked")) {
r->transfer_chunked = true;
} else if (!strcmp(data, "connection") && !strcasecmp(val, "close")) {
cl->connection_close = true;
}
kvlist_set(&cl->request.hdr, data, val);
cl->state = CLIENT_STATE_HEADER;
}
static bool client_header_cb(struct uh_client *cl, char *buf, int len)
{
char *newline;
int line_len;
newline = strstr(buf, "\r\n");
if (!newline)
return false;
*newline = 0;
client_parse_header(cl, buf);
line_len = newline + 2 - buf;
ustream_consume(cl->us, line_len);
if (cl->state == CLIENT_STATE_DATA)
return client_data_cb(cl, newline + 2, len - line_len);
return true;
}
typedef bool (*read_cb_t)(struct uh_client *cl, char *buf, int len);
static read_cb_t read_cbs[] = {
[CLIENT_STATE_INIT] = client_init_cb,
[CLIENT_STATE_HEADER] = client_header_cb,
[CLIENT_STATE_DATA] = client_data_cb,
};
void uh_client_read_cb(struct uh_client *cl)
{
struct ustream *us = cl->us;
char *str;
int len;
do {
str = ustream_get_read_buf(us, &len);
if (!str || !len)
return;
if (cl->state >= ARRAY_SIZE(read_cbs) || !read_cbs[cl->state])
return;
if (!read_cbs[cl->state](cl, str, len)) {
if (len == us->r.buffer_len && cl->state != CLIENT_STATE_DATA)
cl->send_error(cl, 413, "Request Entity Too Large", NULL);
break;
}
} while(1);
}
static void client_ustream_read_cb(struct ustream *s, int bytes)
{
struct uh_client *cl = container_of(s, struct uh_client, sfd.stream);
uh_client_read_cb(cl);
}
static void client_ustream_write_cb(struct ustream *s, int bytes)
{
struct uh_client *cl = container_of(s, struct uh_client, sfd.stream);
if (cl->dispatch.write_cb)
cl->dispatch.write_cb(cl);
}
void uh_client_notify_state(struct uh_client *cl)
{
struct ustream *us = cl->us;
if (!us->write_error) {
if (cl->state == CLIENT_STATE_DATA)
return;
if (!us->eof || us->w.data_bytes)
return;
}
client_free(cl);
}
static void client_notify_state(struct ustream *s)
{
struct uh_client *cl = container_of(s, struct uh_client, sfd.stream);
uh_client_notify_state(cl);
}
void uh_accept_client(struct uh_server *srv, bool ssl)
{
struct uh_client *cl;
unsigned int sl;
int sfd;
struct sockaddr_in addr;
sl = sizeof(addr);
sfd = accept(srv->fd.fd, (struct sockaddr *)&addr, &sl);
if (sfd < 0) {
uh_log_err("accept");
return;
}
cl = calloc(1, sizeof(struct uh_client));
if (!cl) {
uh_log_err("calloc");
goto err;
}
memcpy(&cl->peer_addr, &addr, sizeof(addr));
cl->us = &cl->sfd.stream;
if (ssl) {
uh_ssl_client_attach(cl);
} else {
cl->us->notify_read = client_ustream_read_cb;
cl->us->notify_write = client_ustream_write_cb;
cl->us->notify_state = client_notify_state;
}
cl->us->string_data = true;
ustream_fd_init(&cl->sfd, sfd);
cl->timeout.cb = keepalive_cb;
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
list_add(&cl->list, &srv->clients);
kvlist_init(&cl->request.hdr, hdr_get_len);
cl->srv = srv;
cl->srv->nclients++;
cl->free = client_free;
cl->send_error = client_send_error;
cl->send_header = client_send_header;
cl->append_header = client_append_header;
cl->header_end = client_header_end;
cl->request_done = client_request_done;
cl->send = client_send;
cl->printf = uh_printf;
cl->vprintf = uh_vprintf;
cl->chunk_send = uh_chunk_send;
cl->chunk_printf = uh_chunk_printf;
cl->chunk_vprintf = uh_chunk_vprintf;
cl->get_peer_addr = client_get_peer_addr;
cl->get_path = client_get_path;
cl->get_query = client_get_query;
cl->get_header = client_get_header;
uh_log_debug("new connection: %s:%d", cl->get_peer_addr(cl), addr.sin_port);
return;
err:
close(sfd);
}

120
src/client.h 100755
View File

@ -0,0 +1,120 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CLIENT_H_
#define _CLIENT_H_
#include "common.h"
#if (UHTTPD_SSL_SUPPORT)
#include <libubox/ustream-ssl.h>
#endif
#define UHTTPD_CONNECTION_TIMEOUT 30
enum http_method {
UH_HTTP_MSG_GET,
UH_HTTP_MSG_POST,
UH_HTTP_MSG_HEAD
};
enum http_version {
UH_HTTP_VER_0_9,
UH_HTTP_VER_1_0,
UH_HTTP_VER_1_1
};
enum client_state {
CLIENT_STATE_INIT,
CLIENT_STATE_HEADER,
CLIENT_STATE_DATA,
CLIENT_STATE_DONE,
CLIENT_STATE_CLOSE
};
struct http_request {
enum http_method method;
enum http_version version;
int content_length;
bool transfer_chunked;
bool chunked;
struct kvlist hdr;
};
struct uh_client;
struct dispatch {
int (*data_send)(struct uh_client *cl, const char *data, int len);
void (*data_done)(struct uh_client *cl);
void (*write_cb)(struct uh_client *cl);
void (*close_fds)(struct uh_client *cl);
void (*free)(struct uh_client *cl);
void *req_data;
void (*req_free)(struct uh_client *cl);
bool data_blocked;
bool no_cache;
union {
struct {
int fd;
} file;
};
};
struct uh_client {
struct list_head list;
struct uh_server *srv;
struct ustream *us;
struct ustream_fd sfd;
#if (UHTTPD_SSL_SUPPORT)
struct ustream_ssl ssl;
#endif
struct uloop_timeout timeout;
enum client_state state;
struct http_request request;
struct sockaddr_in peer_addr;
struct dispatch dispatch;
bool connection_close;
void (*free)(struct uh_client *cl);
void (*send_error)(struct uh_client *cl, int code, const char *summary, const char *fmt, ...);
void (*send_header)(struct uh_client *cl, int code, const char *summary, int length);
void (*append_header)(struct uh_client *cl, const char *name, const char *value);
void (*header_end)(struct uh_client *cl);
void (*request_done)(struct uh_client *cl);
void (*send)(struct uh_client *cl, const void *data, int len);
void (*printf)(struct uh_client *cl, const char *format, ...);
void (*vprintf)(struct uh_client *cl, const char *format, va_list arg);
void (*chunk_send)(struct uh_client *cl, const void *data, int len);
void (*chunk_printf)(struct uh_client *cl, const char *format, ...);
void (*chunk_vprintf)(struct uh_client *cl, const char *format, va_list arg);
const char *(*get_peer_addr)(struct uh_client *cl);
const char *(*get_path)(struct uh_client *cl);
const char *(*get_query)(struct uh_client *cl);
const char *(*get_header)(struct uh_client *cl, const char *name);
};
void uh_client_read_cb(struct uh_client *cl);
void uh_client_notify_state(struct uh_client *cl);
void uh_accept_client(struct uh_server *srv, bool ssl);
#endif

28
src/common.h 100755
View File

@ -0,0 +1,28 @@
#ifndef _COMMON_H
#define _COMMON_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <stdbool.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/limits.h>
#include <libubox/uloop.h>
#include <libubox/utils.h>
#include <libubox/blobmsg.h>
#include <libubox/kvlist.h>
#include <libubox/ustream.h>
#include "config.h"
#include "log.h"
#endif

View File

@ -8,8 +8,6 @@
#define UHTTPD_DEBUG @UHTTPD_DEBUG_CONFIG@
#define UHTTPD_SSL_ENABLED @UHTTPD_SSL_ENABLED_CONFIG@
#define UHTTPD_USE_OPENSSL @UHTTPD_USE_OPENSSL_CONFIG@
#define UHTTPD_USE_CYASSL @UHTTPD_USE_CYASSL_CONFIG@
#define UHTTPD_SSL_SUPPORT @UHTTPD_SSL_SUPPORT_CONFIG@
#endif

418
src/file.c 100755
View File

@ -0,0 +1,418 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <dirent.h>
#include "file.h"
#include "utils.h"
#include "uhttpd.h"
static const struct mimetype uh_mime_types[] = {
{ "txt", "text/plain" },
{ "log", "text/plain" },
{ "js", "text/javascript" },
{ "css", "text/css" },
{ "htm", "text/html" },
{ "html", "text/html" },
{ "diff", "text/x-patch" },
{ "patch", "text/x-patch" },
{ "c", "text/x-csrc" },
{ "h", "text/x-chdr" },
{ "o", "text/x-object" },
{ "ko", "text/x-object" },
{ "bmp", "image/bmp" },
{ "gif", "image/gif" },
{ "png", "image/png" },
{ "jpg", "image/jpeg" },
{ "jpeg", "image/jpeg" },
{ "svg", "image/svg+xml" },
{ "json", "application/json" },
{ "jsonp", "application/javascript" },
{ "zip", "application/zip" },
{ "pdf", "application/pdf" },
{ "xml", "application/xml" },
{ "xsl", "application/xml" },
{ "doc", "application/msword" },
{ "ppt", "application/vnd.ms-powerpoint" },
{ "xls", "application/vnd.ms-excel" },
{ "odt", "application/vnd.oasis.opendocument.text" },
{ "odp", "application/vnd.oasis.opendocument.presentation" },
{ "pl", "application/x-perl" },
{ "sh", "application/x-shellscript" },
{ "php", "application/x-php" },
{ "deb", "application/x-deb" },
{ "iso", "application/x-cd-image" },
{ "tar.gz", "application/x-compressed-tar" },
{ "tgz", "application/x-compressed-tar" },
{ "gz", "application/x-gzip" },
{ "tar.bz2", "application/x-bzip-compressed-tar" },
{ "tbz", "application/x-bzip-compressed-tar" },
{ "bz2", "application/x-bzip" },
{ "tar", "application/x-tar" },
{ "rar", "application/x-rar-compressed" },
{ "mp3", "audio/mpeg" },
{ "ogg", "audio/x-vorbis+ogg" },
{ "wav", "audio/x-wav" },
{ "mpg", "video/mpeg" },
{ "mpeg", "video/mpeg" },
{ "avi", "video/x-msvideo" },
{ "README", "text/plain" },
{ "log", "text/plain" },
{ "cfg", "text/plain" },
{ "conf", "text/plain" },
{ "pac", "application/x-ns-proxy-autoconfig" },
{ "wpad.dat", "application/x-ns-proxy-autoconfig" },
{ NULL, NULL }
};
static char *canonpath(const char *path, char *path_resolved)
{
const char *path_cpy = path;
char *path_res = path_resolved;
/* normalize */
while ((*path_cpy != '\0') && (path_cpy < (path + PATH_MAX - 2))) {
if (*path_cpy != '/')
goto next;
/* skip repeating / */
if (path_cpy[1] == '/') {
path_cpy++;
continue;
}
/* /./ or /../ */
if (path_cpy[1] == '.') {
/* skip /./ */
if ((path_cpy[2] == '/') || (path_cpy[2] == '\0')) {
path_cpy += 2;
continue;
}
/* collapse /x/../ */
if ((path_cpy[2] == '.') &&
((path_cpy[3] == '/') || (path_cpy[3] == '\0'))) {
while ((path_res > path_resolved) && (*--path_res != '/'));
path_cpy += 3;
continue;
}
}
next:
*path_res++ = *path_cpy++;
}
/* remove trailing slash if not root / */
if ((path_res > (path_resolved+1)) && (path_res[-1] == '/'))
path_res--;
else if (path_res == path_resolved)
*path_res++ = '/';
*path_res = '\0';
return path_resolved;
}
/* Returns NULL on error.
** NB: improperly encoded URL should give client 400 [Bad Syntax]; returning
** NULL here causes 404 [Not Found], but that's not too unreasonable. */
struct path_info *uh_path_lookup(struct uh_client *cl, const char *url)
{
static char buf[PATH_MAX];
static char path_phys[PATH_MAX];
static char path_info[PATH_MAX];
static struct path_info p;
const char *path = cl->get_path(cl);
const char *query = cl->get_query(cl);
const char *docroot = cl->srv->docroot;
int docroot_len = strlen(docroot);
char *pathptr = NULL;
bool slash;
int i = 0;
int len;
/* back out early if url is undefined */
if (url == NULL)
return NULL;
memset(&p, 0, sizeof(p));
path_phys[0] = 0;
path_info[0] = 0;
strcpy(buf, docroot);
strcat(buf, path);
/* create canon path */
len = strlen(buf);
slash = len && buf[len - 1] == '/';
len = min(len, sizeof(path_phys) - 1);
for (i = len; i >= 0; i--) {
char ch = buf[i];
bool exists;
if (ch != 0 && ch != '/')
continue;
buf[i] = 0;
exists = !!canonpath(buf, path_phys);
buf[i] = ch;
if (!exists)
continue;
/* test current path */
if (stat(path_phys, &p.stat))
continue;
snprintf(path_info, sizeof(path_info), "%s", buf + i);
break;
}
/* check whether found path is within docroot */
if (strncmp(path_phys, docroot, docroot_len) != 0 ||
(path_phys[docroot_len] != 0 &&
path_phys[docroot_len] != '/'))
return NULL;
/* is a regular file */
if (p.stat.st_mode & S_IFREG) {
p.root = docroot;
p.phys = path_phys;
p.name = &path_phys[docroot_len];
p.info = path_info[0] ? path_info : NULL;
return &p;
}
if (!(p.stat.st_mode & S_IFDIR))
return NULL;
if (path_info[0])
return NULL;
pathptr = path_phys + strlen(path_phys);
/* ensure trailing slash */
if (pathptr[-1] != '/') {
pathptr[0] = '/';
pathptr[1] = 0;
pathptr++;
}
/* if requested url resolves to a directory and a trailing slash
is missing in the request url, redirect the client to the same
url with trailing slash appended */
if (!slash) {
cl->send_header(cl, 302, "Found", 0);
cl->printf(cl, "Location: %s%s%s\r\n\r\n", &path_phys[docroot_len],
query ? "?" : "", query ? query : "");
cl->request_done(cl);
p.redirected = 1;
return &p;
}
/* try to locate index file */
len = path_phys + sizeof(path_phys) - pathptr - 1;
strcpy(pathptr, cl->srv->index_file);
if (stat(path_phys, &p.stat) < 0)
return NULL;
p.root = docroot;
p.phys = path_phys;
p.name = &path_phys[docroot_len];
return p.phys ? &p : NULL;
}
static char *file_unix2date(time_t ts, char *buf, int len)
{
struct tm *t = gmtime(&ts);
strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT", t);
return buf;
}
static const char * uh_file_mime_lookup(const char *path)
{
const struct mimetype *m = &uh_mime_types[0];
const char *e;
while (m->extn) {
e = &path[strlen(path)-1];
while (e >= path) {
if ((*e == '.' || *e == '/') && !strcasecmp(&e[1], m->extn))
return m->mime;
e--;
}
m++;
}
return "application/octet-stream";
}
static void uh_file_response_ok_hdrs(struct uh_client *cl, struct stat *s)
{
char buf[128];
cl->printf(cl, "Last-Modified: %s\r\n", file_unix2date(s->st_mtime, buf, sizeof(buf)));
cl->printf(cl, "Date: %s\r\n", file_unix2date(time(NULL), buf, sizeof(buf)));
}
static void uh_file_response_304(struct uh_client *cl, struct stat *s)
{
cl->send_header(cl, 304, "Not Modified", 0);
uh_file_response_ok_hdrs(cl, s);
}
static void uh_file_response_200(struct uh_client *cl, struct stat *s)
{
cl->send_header(cl, 200, "OK", s->st_size);
uh_file_response_ok_hdrs(cl, s);
}
static int uh_file_if_modified_since(struct uh_client *cl, struct stat *s)
{
const char *date = kvlist_get(&cl->request.hdr, "if-modified-since");
struct tm t;
if (!date)
return true;
memset(&t, 0, sizeof(t));
if ((strptime(date, "%a, %d %b %Y %H:%M:%S %Z", &t) ? timegm(&t) : 0) >= s->st_mtime) {
uh_file_response_304(cl, s);
return false;
}
return true;
}
static void file_write_cb(struct uh_client *cl)
{
static char buf[4096];
int fd = cl->dispatch.file.fd;
int r;
while (cl->us->w.data_bytes < 256) {
r = read(fd, buf, sizeof(buf));
if (r < 0) {
if (errno == EINTR)
continue;
uh_log_err("read");
}
if (r <= 0) {
cl->request_done(cl);
return;
}
cl->send(cl, buf, r);
}
}
static void uh_file_free(struct uh_client *cl)
{
close(cl->dispatch.file.fd);
}
static void uh_file_data(struct uh_client *cl, struct path_info *pi, int fd)
{
/* test preconditions */
if ((!uh_file_if_modified_since(cl, &pi->stat))) {
cl->printf(cl, "\r\n");
cl->request_done(cl);
close(fd);
return;
}
/* write status */
uh_file_response_200(cl, &pi->stat);
cl->printf(cl, "Content-Type: %s\r\n\r\n", uh_file_mime_lookup(pi->name));
/* send header */
if (cl->request.method == UH_HTTP_MSG_HEAD) {
cl->request_done(cl);
close(fd);
return;
}
cl->state = CLIENT_STATE_DONE;
cl->dispatch.file.fd = fd;
cl->dispatch.write_cb = file_write_cb;
cl->dispatch.free = uh_file_free;
cl->dispatch.close_fds = uh_file_free;
file_write_cb(cl);
}
static void uh_file_request(struct uh_client *cl, const char *path, struct path_info *pi)
{
int fd;
if (!(pi->stat.st_mode & S_IROTH))
goto error;
if (pi->stat.st_mode & S_IFREG) {
fd = open(pi->phys, O_RDONLY);
if (fd < 0)
goto error;
uh_file_data(cl, pi, fd);
return;
}
error:
cl->send_error(cl, 403, "Forbidden", "You don't have permission to access %s on this server.", path);
}
bool handle_file_request(struct uh_client *cl, char *path)
{
struct path_info *pi;
pi = uh_path_lookup(cl, path);
if (!pi)
return false;
uh_log_debug("pi->root: %s", pi->root);
uh_log_debug("pi->phys: %s", pi->phys);
uh_log_debug("pi->name: %s", pi->name);
uh_log_debug("pi->info: %s", pi->info);
uh_log_debug("pi->redirected: %d", pi->redirected);
if (pi->redirected)
return true;
uh_file_request(cl, path, pi);
return true;
}

View File

@ -14,21 +14,26 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UHTTPD_STR_H
#define _UHTTPD_STR_H
struct uh_str {
const char *at;
size_t len;
#ifndef _FILE_H
#define _FILE_H
#include "client.h"
struct path_info {
const char *root;
const char *phys;
const char *name;
const char *info;
bool redirected;
struct stat stat;
};
/* Return 1 for equal */
static inline int uh_str_cmp(struct uh_str *uv, const char *str)
{
if (uv->len != strlen(str))
return 0;
return (!strncasecmp(uv->at, str, uv->len));
}
struct mimetype {
const char *extn;
const char *mime;
};
#endif
bool handle_file_request(struct uh_client *cl, char *url);
#endif

View File

@ -1,104 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UHTTP_INTERNAL_H
#define _UHTTP_INTERNAL_H
#include <lua.h>
#include "list.h"
#include "uhttpd/uhttpd.h"
#define UH_BUFFER_SIZE 2048
#define UH_CONNECTION_TIMEOUT 30
#define UH_URI_SIZE_LIMIT 1024
#define UH_HEAD_SIZE_LIMIT 1024
#define UH_BODY_SIZE_LIMIT (2 * 1024 * 1024)
#define UH_HEADER_NUM_LIMIT 20
#define UH_CON_CLOSE (1 << 0)
#define UH_CON_SSL_HANDSHAKE_DONE (1 << 1) /* SSL hanshake has completed */
#define UH_CON_REUSE (1 << 2)
#define likely(x) (__builtin_expect(!!(x), 1))
#define unlikely(x) (__builtin_expect(!!(x), 0))
#define ev_timer_mode(l,w,after,repeat) do { \
ev_timer_stop(l, w); \
ev_timer_init(w, ev_cb(w), after, repeat); \
ev_timer_start(l, w); \
} while (0)
enum uh_query_state {
s_query_key,
s_query_value
};
struct uh_hook {
char *path;
uh_hookfn_t cb;
struct list_head list;
};
struct uh_server {
int sock;
#if (UHTTP_SSL_ENABLED)
void *ssl_ctx;
#endif
char *docroot;
lua_State *L;
ev_io read_watcher;
struct ev_loop *loop;
uh_hookfn_t default_cb;
struct list_head hooks;
struct list_head connections;
};
struct uh_header {
struct uh_str field;
struct uh_str value;
};
struct uh_request {
struct uh_str url;
struct uh_str path;
struct uh_str query;
struct uh_str body;
int header_num;
struct uh_header header[UH_HEADER_NUM_LIMIT];
};
struct uh_connection {
int sock;
#if (UHTTP_SSL_ENABLED)
void *ssl;
#endif
unsigned char flags;
struct uh_buf read_buf;
struct uh_buf write_buf;
ev_child child_watcher;
ev_io read_watcher_lua;
ev_io read_watcher;
ev_io write_watcher;
ev_timer timer_watcher;
struct uh_request req;
http_parser parser;
struct list_head list;
struct uh_server *srv;
};
#endif

View File

@ -1,208 +0,0 @@
/*-
* Copyright (c) 2011 Felix Fietkau <nbd@openwrt.org>
* Copyright (c) 2010 Isilon Systems, Inc.
* Copyright (c) 2010 iX Systems, Inc.
* Copyright (c) 2010 Panasas, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _LINUX_LIST_H_
#define _LINUX_LIST_H_
#include <stddef.h>
#include <stdbool.h>
#define prefetch(x)
#ifndef container_of
#define container_of(ptr, type, member) \
({ \
const typeof(((type *) NULL)->member) *__mptr = (ptr); \
(type *) ((char *) __mptr - offsetof(type, member)); \
})
#endif
struct list_head {
struct list_head *next;
struct list_head *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#undef LIST_HEAD
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
static inline void
INIT_LIST_HEAD(struct list_head *list)
{
list->next = list->prev = list;
}
static inline bool
list_empty(const struct list_head *head)
{
return (head->next == head);
}
static inline bool
list_is_first(const struct list_head *list,
const struct list_head *head)
{
return list->prev == head;
}
static inline bool
list_is_last(const struct list_head *list,
const struct list_head *head)
{
return list->next == head;
}
static inline void
_list_del(struct list_head *entry)
{
entry->next->prev = entry->prev;
entry->prev->next = entry->next;
}
static inline void
list_del(struct list_head *entry)
{
_list_del(entry);
entry->next = entry->prev = NULL;
}
static inline void
_list_add(struct list_head *_new, struct list_head *prev,
struct list_head *next)
{
next->prev = _new;
_new->next = next;
_new->prev = prev;
prev->next = _new;
}
static inline void
list_del_init(struct list_head *entry)
{
_list_del(entry);
INIT_LIST_HEAD(entry);
}
#define list_entry(ptr, type, field) container_of(ptr, type, field)
#define list_first_entry(ptr, type, field) list_entry((ptr)->next, type, field)
#define list_last_entry(ptr, type, field) list_entry((ptr)->prev, type, field)
#define list_for_each(p, head) \
for (p = (head)->next; p != (head); p = p->next)
#define list_for_each_safe(p, n, head) \
for (p = (head)->next, n = p->next; p != (head); p = n, n = p->next)
#define list_for_each_entry(p, h, field) \
for (p = list_first_entry(h, typeof(*p), field); &p->field != (h); \
p = list_entry(p->field.next, typeof(*p), field))
#define list_for_each_entry_safe(p, n, h, field) \
for (p = list_first_entry(h, typeof(*p), field), \
n = list_entry(p->field.next, typeof(*p), field); &p->field != (h);\
p = n, n = list_entry(n->field.next, typeof(*n), field))
#define list_for_each_entry_reverse(p, h, field) \
for (p = list_last_entry(h, typeof(*p), field); &p->field != (h); \
p = list_entry(p->field.prev, typeof(*p), field))
#define list_for_each_prev(p, h) for (p = (h)->prev; p != (h); p = p->prev)
#define list_for_each_prev_safe(p, n, h) for (p = (h)->prev, n = p->prev; p != (h); p = n, n = p->prev)
static inline void
list_add(struct list_head *_new, struct list_head *head)
{
_list_add(_new, head, head->next);
}
static inline void
list_add_tail(struct list_head *_new, struct list_head *head)
{
_list_add(_new, head->prev, head);
}
static inline void
list_move(struct list_head *list, struct list_head *head)
{
_list_del(list);
list_add(list, head);
}
static inline void
list_move_tail(struct list_head *entry, struct list_head *head)
{
_list_del(entry);
list_add_tail(entry, head);
}
static inline void
_list_splice(const struct list_head *list, struct list_head *prev,
struct list_head *next)
{
struct list_head *first;
struct list_head *last;
if (list_empty(list))
return;
first = list->next;
last = list->prev;
first->prev = prev;
prev->next = first;
last->next = next;
next->prev = last;
}
static inline void
list_splice(const struct list_head *list, struct list_head *head)
{
_list_splice(list, head, head->next);
}
static inline void
list_splice_tail(struct list_head *list, struct list_head *head)
{
_list_splice(list, head->prev, head);
}
static inline void
list_splice_init(struct list_head *list, struct list_head *head)
{
_list_splice(list, head, head->next);
INIT_LIST_HEAD(list);
}
static inline void
list_splice_tail_init(struct list_head *list, struct list_head *head)
{
_list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
#endif /* _LINUX_LIST_H_ */

View File

@ -1,47 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UHTTPD_BUF_H
#define _UHTTPD_BUF_H
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#define UH_BUF_SIZE_MULTIPLIER 3
struct uh_buf {
char *base; /* Buffer pointer */
size_t len; /* Data length */
size_t size; /* Buffer size */
};
#define uh_buf_available(b) ((b)->size - (b)->len)
/* Return 0 for successful or -1 if out of memory */
int uh_buf_init(struct uh_buf *buf, size_t initial_size);
int uh_buf_grow(struct uh_buf *buf, size_t size);
void uh_buf_free(struct uh_buf *buf);
/* Append data to the buf. Return the number of bytes appended. */
size_t uh_buf_append(struct uh_buf *buf, const void *data, size_t len);
/* Remove n bytes of data from the beginning of the buffer. */
void uh_buf_remove(struct uh_buf *buf, size_t n);
#endif

View File

@ -1,432 +0,0 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef _UHTTPD_PARSER_H
#define _UHTTPD_PARSER_H
#ifdef __cplusplus
extern "C" {
#endif
/* Also update SONAME in the Makefile whenever you change these. */
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 7
#define HTTP_PARSER_VERSION_PATCH 1
#include <stddef.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#endif
/* Maximium header size allowed. If the macro is not defined
* before including this header then the default is used. To
* change the maximum header size, define the macro in the build
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
* the effective limit on the size of the header, define the macro
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
*/
#ifndef HTTP_MAX_HEADER_SIZE
# define HTTP_MAX_HEADER_SIZE (80*1024)
#endif
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
/* Callbacks should return non-zero to indicate an error. The parser will
* then halt execution.
*
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
* returning '1' from on_headers_complete will tell the parser that it
* should not expect a body. This is used when receiving a response to a
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
* chunked' headers that indicate the presence of a body.
*
* Returning `2` from on_headers_complete will tell parser that it should not
* expect neither a body nor any futher responses on this connection. This is
* useful for handling responses to a CONNECT request which may not contain
* `Upgrade` or `Connection: upgrade` headers.
*
* http_data_cb does not return data chunks. It will be called arbitrarily
* many times for each string. E.G. you might get 10 callbacks for "on_url"
* each providing just a few characters more data.
*/
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
/* Status Codes */
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
enum http_status
{
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
};
/* Request Methods */
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
/* pathological */ \
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
/* WebDAV */ \
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
/* subversion */ \
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
/* upnp */ \
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
/* RFC-5789 */ \
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
/* CalDAV */ \
XX(30, MKCALENDAR, MKCALENDAR) \
/* RFC-2068, section 19.6.1.2 */ \
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
enum http_method
{
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
, F_CONNECTION_UPGRADE = 1 << 3
, F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
};
/* Map for errno-related constants
*
* The provided argument should be a macro that takes 2 arguments.
*/
#define HTTP_ERRNO_MAP(XX) \
/* No error */ \
XX(OK, "success") \
\
/* Callback-related errors */ \
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
/* Parsing-related errors */ \
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred")
/* Define HPE_* values for each errno value above */
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
/* Get an http_errno value from an http_parser */
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
struct http_parser {
/** PRIVATE **/
unsigned int type : 2; /* enum http_parser_type */
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
unsigned int state : 7; /* enum state from http_parser.c */
unsigned int header_state : 7; /* enum header_state from http_parser.c */
unsigned int index : 7; /* index into current matcher */
unsigned int lenient_http_headers : 1;
const char *mark; /* Marks the start position of the current matcher */
uint32_t nread; /* # bytes read in various scenarios */
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
/** READ-ONLY **/
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16; /* responses only */
unsigned int method : 8; /* requests only */
unsigned int http_errno : 7;
/* 1 = Upgrade header was present and the parser has exited because of that.
* 0 = No upgrade header present.
* Should be checked when http_parser_execute() returns in addition to
* error checking.
*/
unsigned int upgrade : 1;
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
/* Result structure for http_parser_parse_url().
*
* Callers should index into field_data[] with UF_* values iff field_set
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
* because we probably have padding left over), we convert any port to
* a uint16_t.
*/
struct http_parser_url {
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
uint16_t port; /* Converted UF_PORT string */
struct {
uint16_t off; /* Offset into buffer in which field starts */
uint16_t len; /* Length of run in buffer */
} field_data[UF_MAX];
};
/* Returns the library version. Bits 16-23 contain the major version number,
* bits 8-15 the minor version number and bits 0-7 the patch level.
* Usage example:
*
* unsigned long version = http_parser_version();
* unsigned major = (version >> 16) & 255;
* unsigned minor = (version >> 8) & 255;
* unsigned patch = version & 255;
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
*/
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings);
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
/* If http_should_keep_alive() in the on_headers_complete or
* on_message_complete callback returns 0, then this should be
* the last message on the connection.
* If you are the server, respond with the "Connection: close" header.
* If you are the client, close the connection.
*/
int http_should_keep_alive(const http_parser *parser);
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m);
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err);
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err);
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u);
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,119 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UHTTPD_SSL_H
#define _UHTTPD_SSL_H
#include "internal.h"
#if (UHTTP_USE_OPENSSL)
#include <openssl/ssl.h>
#include <openssl/err.h>
#ifndef SSL_SUCCESS
#define SSL_SUCCESS 1
#endif
#elif (UHTTP_USE_CYASSL)
#include <wolfssl/ssl.h>
#ifndef SSL_CTX
#define SSL_CTX WOLFSSL_CTX
#endif
#ifndef SSL_library_init
#define SSL_library_init wolfSSL_library_init
#endif
#ifndef SSL_load_error_strings
#define SSL_load_error_strings wolfSSL_library_init
#endif
#ifndef SSLv23_server_method
#define SSLv23_server_method wolfSSLv23_server_method
#endif
#ifndef SSL_CTX_new
#define SSL_CTX_new wolfSSL_CTX_new
#endif
#ifndef SSL_CTX_free
#define SSL_CTX_free(ssl) do {wolfSSL_CTX_free(ssl);wolfSSL_Cleanup();} while(0)
#endif
#ifndef SSL_CTX_use_certificate_file
#define SSL_CTX_use_certificate_file wolfSSL_CTX_use_certificate_file
#endif
#ifndef SSL_CTX_use_RSAPrivateKey_file
#define SSL_CTX_use_RSAPrivateKey_file wolfSSL_CTX_use_PrivateKey_file
#endif
#ifndef SSL_shutdown
#define SSL_shutdown wolfSSL_shutdown
#endif
#ifndef SSL_free
#define SSL_free wolfSSL_free
#endif
#ifndef SSL_accept
#define SSL_accept wolfSSL_accept
#endif
#ifndef SSL_new
#define SSL_new wolfSSL_new
#endif
#ifndef SSL_set_fd
#define SSL_set_fd wolfSSL_set_fd
#endif
#ifndef SSL_set_accept_state
#define SSL_set_accept_state wolfSSL_set_accept_state
#endif
#ifndef SSL_write
#define SSL_write wolfSSL_write
#endif
#ifndef SSL_read
#define SSL_read wolfSSL_read
#endif
#ifndef SSL_get_error
#define SSL_get_error wolfSSL_get_error
#endif
#ifndef ERR_reason_error_string
#define ERR_reason_error_string wolfSSL_ERR_reason_error_string
#endif
#ifndef ERR_peek_error
#define ERR_peek_error wolfSSL_ERR_peek_error
#endif
#endif
void uh_ssl_ctx_free(struct uh_server *srv);
void uh_ssl_free(struct uh_connection *con);
int uh_ssl_read(struct uh_connection *con, void *buf, int count);
int uh_ssl_write(struct uh_connection *con, void *buf, int count);
int uh_ssl_accept(struct uh_connection *con);
void uh_ssl_handshake(struct uh_connection *con);
#endif

View File

@ -1,138 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UHTTPD_UHTTP_H
#define _UHTTPD_UHTTP_H
#include <ev.h>
#include <stdbool.h>
#include "uhttpd/config.h"
#include "uhttpd/log.h"
#include "uhttpd/buf.h"
#include "uhttpd/str.h"
#include "uhttpd/parser.h"
struct uh_server;
struct uh_connection;
typedef void (*uh_hookfn_t)(struct uh_connection *con);
const char *uh_version();
/* creates a new uhttp server instance. */
struct uh_server *uh_server_new(struct ev_loop *loop, const char *ipaddr, int port);
/* frees a uhttp server instance. */
void uh_server_free(struct uh_server *srv);
/* Sets document root. */
void uh_set_docroot(struct uh_server *srv, const char *path);
/* Processing Lua templates */
void uh_template(struct uh_connection *con);
/* Sends data to the connection. */
int uh_send(struct uh_connection *con, const void *buf, int len);
/* Sends printf-formatted data to the connection. */
int uh_printf(struct uh_connection *con, const char *fmt, ...);
/*
* Sends the response line and headers.
* This function sends the response line with the `status`, and
* automatically sends one header: either "Content-Length" or "Transfer-Encoding".
* If `length` is negative, then "Transfer-Encoding: chunked" is sent, otherwise,
* "Content-Length" is sent.
*
* NOTE: If `Transfer-Encoding` is `chunked`, then message body must be sent
* using `uh_send_chunk()` or `uh_printf_chunk()` functions.
* Otherwise, `uh_send()` or `uh_printf()` must be used.
* Extra headers could be set through `extra_headers`.
*
* NOTE: `extra_headers` must be terminated by a new line("\r\n").
*/
void uh_send_head(struct uh_connection *con, int status, int length, const char *extra_headers);
/*
* Sends a http error response. If reason is NULL, the message will be inferred
* from the error code (if supported).
*/
void uh_send_error(struct uh_connection *con, int code, const char *reason);
/*
* Sends a http redirect response. `code` should be either 301
* or 302 and `location` point to the new location.
*/
void uh_redirect(struct uh_connection *con, int code, const char *location);
/*
* Sends data to the connection using chunked HTTP encoding.
*
* NOTE: The HTTP header "Transfer-Encoding: chunked" should be sent prior to
* using this function.
*
* NOTE: do not forget to send an empty chunk at the end of the response,
* to tell the client that everything was sent.
*
* Example:
* char data[] = "Hello World";
* uh_send_chunk(con, data, strlen(data));
* uh_send_chunk(con, NULL, 0); // Tell the client we're finished
*/
int uh_send_chunk(struct uh_connection *con, const char *buf, int len);
/*
* Sends a printf-formatted HTTP chunk.
* Functionality is similar to `uh_send_chunk()`.
*/
int uh_printf_chunk(struct uh_connection *con, const char *fmt, ...);
/* Registers a callback to be executed on a specific path */
int uh_register_hook(struct uh_server *srv, const char *path, uh_hookfn_t cb);
/*
* Registers a callback to be executed when don't find a callback function
* that is specific to a path.
*/
void uh_register_default_hook(struct uh_server *srv, uh_hookfn_t cb);
enum http_method uh_get_method(struct uh_connection *con);
const char *uh_get_method_str(struct uh_connection *con);
struct uh_str *uh_get_url(struct uh_connection *con);
struct uh_str *uh_get_path(struct uh_connection *con);
struct uh_str *uh_get_query(struct uh_connection *con);
struct uh_str uh_get_var(struct uh_connection *con, const char *name);
struct uh_str *uh_get_header(struct uh_connection *con, const char *name);
int uh_get_con_sock(struct uh_connection *con);
/*
* Traversing all variables, when a variable is found, the callback function is called,
* If the callback function returns to true, the traversal is terminated.
*/
bool uh_foreach_var(struct uh_connection *con,
bool (*found)(struct uh_str *key, struct uh_str *val, void *udata), void *udata);
/* Unescapes strings like '%7B1,%202,%203%7D' would become '{1, 2, 3}' */
int uh_unescape(const char *str, int len, char *out, int olen);
#if (UHTTP_SSL_ENABLED)
/* Init ssl for the server */
int uh_ssl_init(struct uh_server *srv, const char *cert, const char *key);
#endif
#endif

View File

@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "uhttpd/log.h"
#include "log.h"
void __uh_log(const char *filename, int line, int priority, const char *format, ...)
{

View File

@ -15,15 +15,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UHTTPD_LOG_H
#define _UHTTPD_LOG_H
#ifndef _LOG_H
#define _LOG_H
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <syslog.h>
#include "uhttpd/config.h"
#include "common.h"
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

File diff suppressed because it is too large Load Diff

219
src/ssl.c
View File

@ -1,219 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "uhttpd/ssl.h"
#include <unistd.h>
#include <sys/socket.h>
#if (UHTTP_SSL_ENABLED)
int uh_ssl_init(struct uh_server *srv, const char *cert, const char *key)
{
SSL_CTX *ctx = NULL;
SSL_library_init();
/* registers the error strings for all libssl functions */
SSL_load_error_strings();
/* creates a new SSL_CTX object */
ctx = SSL_CTX_new(SSLv23_server_method());
if (!ctx) {
uh_log_err("Failed to create SSL context");
return -1;
}
/* loads the first certificate stored in file into ctx */
if (SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) != SSL_SUCCESS) {
uh_log_err("OpenSSL Error: loading certificate file failed");
goto err;
}
/*
* adds the first private RSA key found in file to ctx.
*
* checks the consistency of a private key with the corresponding
* certificate loaded into ctx. If more than one key/certificate
* pair (RSA/DSA) is installed, the last item installed will be checked.
*/
if (SSL_CTX_use_RSAPrivateKey_file(ctx, key, SSL_FILETYPE_PEM) != SSL_SUCCESS) {
uh_log_err("OpenSSL Error: loading key failed");
goto err;
}
srv->ssl_ctx = ctx;
return 0;
err:
SSL_CTX_free(ctx);
return -1;
}
#endif
void uh_ssl_ctx_free(struct uh_server *srv)
{
#if (UHTTP_SSL_ENABLED)
if (!srv->ssl_ctx)
return;
SSL_CTX_free(srv->ssl_ctx);
#endif
}
void uh_ssl_free(struct uh_connection *con)
{
#if (UHTTP_SSL_ENABLED)
if (!con->ssl)
return;
SSL_shutdown(con->ssl);
SSL_free(con->ssl);
#endif
}
#if (UHTTP_SSL_ENABLED)
static int uh_ssl_err(struct uh_connection *con, int ret, const char *fun)
{
int err;
err = SSL_get_error(con->ssl, ret);
if (err == SSL_ERROR_ZERO_RETURN || ERR_peek_error()) {
con->flags |= UH_CON_CLOSE;
return 0;
}
#if (UHTTP_USE_OPENSSL)
if (ret == 0) {
con->flags |= UH_CON_CLOSE;
return 0;
}
#endif
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
return -1;
if (err == SSL_ERROR_SYSCALL) {
if (errno > 0)
uh_log_err("%s", fun);
con->flags |= UH_CON_CLOSE;
return -1;
}
con->flags |= UH_CON_CLOSE;
uh_log_err("%s() Error: %s", fun, ERR_reason_error_string(err));
return -1;
}
#endif
int uh_ssl_read(struct uh_connection *con, void *buf, int count)
{
int ret = -1;
#if (UHTTP_SSL_ENABLED)
if (!con->ssl)
goto no_ssl;
ret = SSL_read(con->ssl, buf, count);
if (ret > 0)
return ret;
return uh_ssl_err(con, ret, "SSL_read");
no_ssl:
#endif
ret = read(con->sock, buf, count);
if (ret <= 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
return ret;
if (ret != 0)
uh_log_err("read");
else
uh_log_debug("peer closed");
con->flags |= UH_CON_CLOSE;
}
return ret;
}
int uh_ssl_write(struct uh_connection *con, void *buf, int count)
{
int ret = -1;
#if (UHTTP_SSL_ENABLED)
if (!con->ssl)
goto no_ssl;
ret = SSL_write(con->ssl, buf, count);
if (ret > 0)
return ret;
return uh_ssl_err(con, ret, "SSL_write");
no_ssl:
#endif
ret = write(con->sock, buf, count);
if (ret <= 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
return ret;
if (ret != 0) {
con->flags |= UH_CON_CLOSE;
uh_log_err("write");
}
}
return ret;
}
int uh_ssl_accept(struct uh_connection *con)
{
int sock = -1;
struct uh_server *srv = con->srv;
sock = accept4(srv->sock, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (unlikely(sock < 0)) {
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
uh_log_err("accept4");
return -1;
}
con->sock = sock;
#if (UHTTP_SSL_ENABLED)
if (!srv->ssl_ctx)
return sock;
con->ssl = SSL_new(srv->ssl_ctx);
if (!con->ssl)
return -1;
if (!SSL_set_fd(con->ssl, sock)) {
uh_log_err("SSL_set_fd() failed");
return -1;
}
SSL_set_accept_state(con->ssl);
#endif
return sock;
}
void uh_ssl_handshake(struct uh_connection *con)
{
#if (UHTTP_SSL_ENABLED)
int ret = SSL_accept(con->ssl);
if (ret == 1) {
con->flags |= UH_CON_SSL_HANDSHAKE_DONE;
return;
}
uh_ssl_err(con, ret, "SSL_accept");
#endif
}

View File

@ -1,709 +0,0 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* based on https://git.lede-project.org/project/luci.git modules/luci-base/src/template_parser.c
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "internal.h"
#include "uhttpd/uhttpd.h"
/* code types */
#define T_TYPE_INIT 0
#define T_TYPE_TEXT 1
#define T_TYPE_COMMENT 2
#define T_TYPE_EXPR 3
#define T_TYPE_INCLUDE 4
#define T_TYPE_CODE 5
#define T_TYPE_EOF 6
/* leading and trailing code for different types */
static const char *gen_code[9][2] = {
{ NULL, NULL }, /* T_TYPE_INIT */
{ "io.write(\"", "\")" }, /* T_TYPE_TEXT */
{ NULL, NULL }, /* T_TYPE_COMMENT */
{ "io.write(tostring(", " or \"\"))" }, /* T_TYPE_EXPR */
{ "include(\"", "\")" }, /* T_TYPE_INCLUDE */
{ NULL, " " }, /* T_TYPE_CODE */
{ NULL, NULL }, /* T_TYPE_EOF */
};
/* buffer object */
struct template_buffer {
char *data;
char *dptr;
unsigned int size;
unsigned int fill;
};
struct template_chunk {
const char *s;
const char *e;
int type;
int line;
};
/* parser state */
struct template_parser {
int fd;
uint32_t size;
char *data;
char *off;
char *gc;
int line;
int in_expr;
int strip_before;
int strip_after;
struct template_chunk prv_chunk;
struct template_chunk cur_chunk;
const char *file;
};
/* initialize a buffer object */
struct template_buffer *buf_init(int size)
{
struct template_buffer *buf;
if (size <= 0)
size = 1024;
buf = (struct template_buffer *)malloc(sizeof(struct template_buffer));
if (buf != NULL)
{
buf->fill = 0;
buf->size = size;
buf->data = malloc(buf->size);
if (buf->data != NULL) {
buf->dptr = buf->data;
buf->data[0] = 0;
return buf;
}
free(buf);
}
return NULL;
}
/* grow buffer */
static int buf_grow(struct template_buffer *buf, int size)
{
unsigned int off = (buf->dptr - buf->data);
char *data;
if (size <= 0)
size = 1024;
data = realloc(buf->data, buf->size + size);
if (data != NULL) {
buf->data = data;
buf->dptr = data + off;
buf->size += size;
return buf->size;
}
return 0;
}
/* put one char into buffer object */
static int buf_putchar(struct template_buffer *buf, char c)
{
if( ((buf->fill + 1) >= buf->size) && !buf_grow(buf, 0) )
return 0;
*(buf->dptr++) = c;
*(buf->dptr) = 0;
buf->fill++;
return 1;
}
/* append data to buffer */
static int buf_append(struct template_buffer *buf, const char *s, int len)
{
if ((buf->fill + len + 1) >= buf->size) {
if (!buf_grow(buf, len + 1))
return 0;
}
memcpy(buf->dptr, s, len);
buf->fill += len;
buf->dptr += len;
*(buf->dptr) = 0;
return len;
}
/* read buffer length */
static int buf_length(struct template_buffer *buf)
{
return buf->fill;
}
/* destroy buffer object and return pointer to data */
static char *buf_destroy(struct template_buffer *buf)
{
char *data = buf->data;
free(buf);
return data;
}
static void luastr_escape(struct template_buffer *out, const char *s, unsigned int l,
int escape_xml)
{
int esl;
char esq[8];
char *ptr;
for (ptr = (char *)s; ptr < (s + l); ptr++) {
switch (*ptr) {
case '\\':
buf_append(out, "\\\\", 2);
break;
case '"':
if (escape_xml)
buf_append(out, "&#34;", 5);
else
buf_append(out, "\\\"", 2);
break;
case '\n':
buf_append(out, "\\n", 2);
break;
case '\'':
case '&':
case '<':
case '>':
if (escape_xml) {
esl = snprintf(esq, sizeof(esq), "&#%i;", *ptr);
buf_append(out, esq, esl);
break;
}
default:
buf_putchar(out, *ptr);
}
}
}
/* Simple strstr() like function that takes len arguments for both haystack and needle. */
static char *strfind(char *haystack, int hslen, const char *needle, int ndlen)
{
int match = 0;
int i, j;
for( i = 0; i < hslen; i++ ) {
if( haystack[i] == needle[0] ) {
match = ((ndlen == 1) || ((i + ndlen) <= hslen));
for( j = 1; (j < ndlen) && ((i + j) < hslen); j++ ) {
if( haystack[i+j] != needle[j] ) {
match = 0;
break;
}
}
if( match )
return &haystack[i];
}
}
return NULL;
}
static int template_error(lua_State *L, struct template_parser *parser)
{
const char *err = luaL_checkstring(L, -1);
const char *off = parser->prv_chunk.s;
const char *ptr;
char msg[1024];
int line = 0;
int chunkline = 0;
if ((ptr = strfind((char *)err, strlen(err), "]:", 2)) != NULL) {
chunkline = atoi(ptr + 2) - parser->prv_chunk.line;
while (*ptr) {
if (*ptr++ == ' ') {
err = ptr;
break;
}
}
}
if (strfind((char *)err, strlen(err), "'char(27)'", 10) != NULL) {
off = parser->data + parser->size;
err = "'%>' expected before end of file";
chunkline = 0;
}
for (ptr = parser->data; ptr < off; ptr++)
if (*ptr == '\n')
line++;
snprintf(msg, sizeof(msg), "Syntax error in %s:%d: %s",
parser->file ? parser->file : "[string]", line + chunkline, err ? err : "(unknown error)");
lua_pushnil(L);
lua_pushinteger(L, line + chunkline);
lua_pushstring(L, msg);
return 3;
}
static void template_close(struct template_parser *parser)
{
if (!parser)
return;
if (parser->gc != NULL)
free(parser->gc);
/* if file is not set, we were parsing a string */
if (parser->file) {
if ((parser->data != NULL) && (parser->data != MAP_FAILED))
munmap(parser->data, parser->size);
if (parser->fd >= 0)
close(parser->fd);
}
free(parser);
}
static void template_text(struct template_parser *parser, const char *e)
{
const char *s = parser->off;
if (s < (parser->data + parser->size)) {
if (parser->strip_after) {
while ((s <= e) && isspace(*s))
s++;
}
parser->cur_chunk.type = T_TYPE_TEXT;
} else {
parser->cur_chunk.type = T_TYPE_EOF;
}
parser->cur_chunk.line = parser->line;
parser->cur_chunk.s = s;
parser->cur_chunk.e = e;
}
static void template_code(struct template_parser *parser, const char *e)
{
const char *s = parser->off;
parser->strip_before = 0;
parser->strip_after = 0;
if (*s == '-') {
parser->strip_before = 1;
for (s++; (s <= e) && (*s == ' ' || *s == '\t'); s++);
}
if (*(e-1) == '-') {
parser->strip_after = 1;
for (e--; (e >= s) && (*e == ' ' || *e == '\t'); e--);
}
switch (*s) {
/* comment */
case '#':
s++;
parser->cur_chunk.type = T_TYPE_COMMENT;
break;
/* include */
case '+':
s++;
parser->cur_chunk.type = T_TYPE_INCLUDE;
break;
/* expr */
case '=':
s++;
parser->cur_chunk.type = T_TYPE_EXPR;
break;
/* code */
default:
parser->cur_chunk.type = T_TYPE_CODE;
break;
}
parser->cur_chunk.line = parser->line;
parser->cur_chunk.s = s;
parser->cur_chunk.e = e;
}
static const char *template_format_chunk(struct template_parser *parser, size_t *sz)
{
const char *s, *p;
const char *head, *tail;
struct template_chunk *c = &parser->prv_chunk;
struct template_buffer *buf;
*sz = 0;
s = parser->gc = NULL;
if (parser->strip_before && c->type == T_TYPE_TEXT) {
while ((c->e > c->s) && isspace(*(c->e - 1)))
c->e--;
}
/* empty chunk */
if (c->s == c->e) {
if (c->type == T_TYPE_EOF) {
*sz = 0;
s = NULL;
} else {
*sz = 1;
s = " ";
}
} else if ((buf = buf_init(c->e - c->s)) != NULL) { /* format chunk */
if ((head = gen_code[c->type][0]) != NULL)
buf_append(buf, head, strlen(head));
switch (c->type) {
case T_TYPE_TEXT:
luastr_escape(buf, c->s, c->e - c->s, 0);
break;
case T_TYPE_EXPR:
buf_append(buf, c->s, c->e - c->s);
for (p = c->s; p < c->e; p++)
parser->line += (*p == '\n');
break;
case T_TYPE_INCLUDE:
luastr_escape(buf, c->s, c->e - c->s, 0);
break;
case T_TYPE_CODE:
buf_append(buf, c->s, c->e - c->s);
for (p = c->s; p < c->e; p++)
parser->line += (*p == '\n');
break;
}
if ((tail = gen_code[c->type][1]) != NULL)
buf_append(buf, tail, strlen(tail));
*sz = buf_length(buf);
s = parser->gc = buf_destroy(buf);
if (!*sz) {
*sz = 1;
s = " ";
}
}
return s;
}
static const char *template_reader(lua_State *L, void *ud, size_t *sz)
{
struct template_parser *parser = ud;
int rem = parser->size - (parser->off - parser->data);
char *tag;
parser->prv_chunk = parser->cur_chunk;
/* free previous string */
if (parser->gc) {
free(parser->gc);
parser->gc = NULL;
}
/* before tag */
if (!parser->in_expr) {
if ((tag = strfind(parser->off, rem, "<%", 2)) != NULL) {
template_text(parser, tag);
parser->off = tag + 2;
parser->in_expr = 1;
} else {
template_text(parser, parser->data + parser->size);
parser->off = parser->data + parser->size;
}
} else { /* inside tag */
if ((tag = strfind(parser->off, rem, "%>", 2)) != NULL) {
template_code(parser, tag);
parser->off = tag + 2;
parser->in_expr = 0;
} else {
/* unexpected EOF */
template_code(parser, parser->data + parser->size);
*sz = 1;
return "\033";
}
}
return template_format_chunk(parser, sz);
}
static int template_L_do_parse(lua_State *L, struct template_parser *parser, const char *chunkname)
{
int lua_status, rv;
if (!parser) {
lua_pushnil(L);
lua_pushinteger(L, errno);
lua_pushstring(L, strerror(errno));
return 3;
}
#if LUA_VERSION_NUM > 501
lua_status = lua_load(L, template_reader, parser, chunkname, NULL);
#else
lua_status = lua_load(L, template_reader, parser, chunkname);
#endif
if (lua_status == 0)
rv = 1;
else
rv = template_error(L, parser);
template_close(parser);
return rv;
}
struct template_parser * template_open(const char *file)
{
struct stat s;
struct template_parser *parser;
if (!(parser = malloc(sizeof(*parser))))
goto err;
memset(parser, 0, sizeof(*parser));
parser->fd = -1;
parser->file = file;
if (stat(file, &s))
goto err;
if ((parser->fd = open(file, O_RDONLY)) < 0)
goto err;
parser->size = s.st_size;
parser->data = mmap(NULL, parser->size, PROT_READ, MAP_PRIVATE,
parser->fd, 0);
if (parser->data != MAP_FAILED) {
parser->off = parser->data;
parser->cur_chunk.type = T_TYPE_INIT;
parser->cur_chunk.s = parser->data;
parser->cur_chunk.e = parser->data;
return parser;
}
err:
template_close(parser);
return NULL;
}
static void template_read_cb(struct ev_loop *loop, ev_io *w, int revents)
{
struct uh_connection *con = container_of(w, struct uh_connection, read_watcher_lua);
char buf[1024] = "";
int len;
len = read(w->fd, buf, sizeof(buf));
if (len > 0)
uh_send_chunk(con, buf, len);
else if (len == 0) {
uh_send_chunk(con, NULL, 0);
close(w->fd);
ev_io_stop(con->srv->loop, w);
if (!(con->flags & UH_CON_CLOSE))
con->flags |= UH_CON_REUSE;
}
}
static void child_cb(struct ev_loop *loop, ev_child *w, int revents)
{
struct uh_connection *con = container_of(w, struct uh_connection, child_watcher);
ev_child_stop(con->srv->loop, w);
}
static bool found_var(struct uh_str *key, struct uh_str *val, void *udata)
{
lua_State *L = (lua_State *)udata;
lua_pushlstring(L, key->at, key->len);
lua_pushlstring(L, val->at, val->len);
lua_settable(L, -3);
return false;
}
void uh_template(struct uh_connection *con)
{
struct template_parser *parser;
lua_State *L = con->srv->L;
pid_t pid;
int pipefd[2];
static char path[PATH_MAX] = "";
struct stat st;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
struct uh_str *ustr;
int i;
strcpy(path, con->srv->docroot);
if (con->req.path.len == 1 && con->req.path.at[0] == '/')
strncat(path, "/index.html", 11);
else
strncat(path, con->req.path.at, con->req.path.len);
if (stat(path, &st) < 0 || !S_ISREG(st.st_mode)) {
uh_send_error(con, HTTP_STATUS_NOT_FOUND, NULL);
return;
}
uh_log_debug("Path:%s", path);
if (!L) {
L = luaL_newstate();
if (!L) {
uh_log_err("cannot create LUA state: not enough memory");
goto err;
}
con->srv->L = L;
luaL_openlibs(L);
}
/*
* Add all variables to the global environment of LUA.
* eg. <%=_UHTTP["REMOTE_HOST"]%>
*/
lua_newtable(L);
lua_pushstring(L, uh_get_method_str(con));
lua_setfield(L, -2, "HTTP_METHOD");
getpeername(con->sock, (struct sockaddr *)&addr, &addrlen);
lua_pushstring(L, inet_ntoa(addr.sin_addr));
lua_setfield(L, -2, "REMOTE_HOST");
ustr = uh_get_url(con);
lua_pushlstring(L, ustr->at, ustr->len);
lua_setfield(L, -2, "HTTP_URL");
ustr = uh_get_path(con);
lua_pushlstring(L, ustr->at, ustr->len);
lua_setfield(L, -2, "HTTP_PATH");
lua_newtable(L);
for (i = 0; i < con->req.header_num; i ++) {
struct uh_header *h = &con->req.header[i];
lua_pushlstring(L, h->field.at, h->field.len);
lua_pushlstring(L, h->value.at, h->value.len);
lua_settable(L, -3);
}
lua_setfield(L, -2, "HEADERS");
lua_newtable(L);
uh_foreach_var(con, found_var, L);
lua_setfield(L, -2, "VARIABLES");
lua_setglobal(L, "_UHTTP");
if (pipe2(pipefd, O_CLOEXEC | O_NONBLOCK) < 0) {
uh_log_err("pipe");
goto err;
}
uh_send_head(con, HTTP_STATUS_OK, -1, "Pragma: no-cache\r\nCache-Control: no-cache\r\n");
pid = fork();
switch (pid) {
case -1:
uh_log_err("fork");
goto err;
break;
case 0:
close(0);
close(1);
close(pipefd[0]);
dup2(pipefd[1], 1);
parser = template_open(path);
if (!parser) {
uh_log_err("template_open failed");
return;
}
if ((template_L_do_parse(L, parser, path) != 1) || lua_pcall(L, 0, 0, 0)) {
uh_printf_chunk(con, "<h2><b>Lua Error</b></h2>\n%s\n", lua_tostring(L, -1));
uh_printf_chunk(con, "</body></html>\n");
uh_send_chunk(con, NULL, 0);
lua_pop(L, -1);
} else {
uh_send_chunk(con, NULL, 0);
}
exit(0);
break;
default:
close(pipefd[1]);
ev_io_init(&con->read_watcher_lua, template_read_cb, pipefd[0], EV_READ);
ev_io_start(con->srv->loop, &con->read_watcher_lua);
ev_child_init(&con->child_watcher, child_cb, pid, 0);
ev_child_start(con->srv->loop, &con->child_watcher);
break;
}
return;
err:
uh_send_error(con, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL);
}

105
src/uh_ssl.c 100755
View File

@ -0,0 +1,105 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
* Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org>
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <dlfcn.h>
#include "uhttpd.h"
#include "uh_ssl.h"
static bool _init = false;
static struct ustream_ssl_ops *ops;
static void *dlh;
static void *ctx;
int uh_ssl_init(struct uh_server *srv, const char *key, const char *crt)
{
srv->ssl = true;
if (_init)
return 0;
_init = true;
dlh = dlopen("libustream-ssl.so", RTLD_LAZY | RTLD_LOCAL);
if (!dlh) {
uh_log_err("Failed to load ustream-ssl library: %s", dlerror());
return -ENOENT;
}
ops = dlsym(dlh, "ustream_ssl_ops");
if (!ops) {
uh_log_err("Could not find required symbol 'ustream_ssl_ops' in ustream-ssl library");
return -ENOENT;
}
ctx = ops->context_new(true);
if (!ctx) {
uh_log_err("Failed to initialize ustream-ssl");
return -EINVAL;
}
if (ops->context_set_crt_file(ctx, crt) ||
ops->context_set_key_file(ctx, key)) {
uh_log_err("Failed to load certificate/key files");
return -EINVAL;
}
return 0;
}
void uh_ssl_free()
{
if (_init) {
_init = false;
ops->context_free(ctx);
}
}
static void ssl_ustream_read_cb(struct ustream *s, int bytes)
{
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
uh_client_read_cb(cl);
}
static void ssl_ustream_write_cb(struct ustream *s, int bytes)
{
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
if (cl->dispatch.write_cb)
cl->dispatch.write_cb(cl);
}
static void ssl_notify_state(struct ustream *s)
{
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
uh_client_notify_state(cl);
}
void uh_ssl_client_attach(struct uh_client *cl)
{
cl->us = &cl->ssl.stream;
ops->init(&cl->ssl, &cl->sfd.stream, ctx, true);
cl->us->notify_read = ssl_ustream_read_cb;
cl->us->notify_write = ssl_ustream_write_cb;
cl->us->notify_state = ssl_notify_state;
}
void uh_ssl_client_detach(struct uh_client *cl)
{
ustream_free(&cl->ssl.stream);
}

46
src/uh_ssl.h 100755
View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
* Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org>
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __UHTTPD_SSL_H
#define __UHTTPD_SSL_H
#if (UHTTPD_SSL_SUPPORT)
int uh_ssl_init(struct uh_server *srv, const char *key, const char *crt);
void uh_ssl_free();
void uh_ssl_client_attach(struct uh_client *cl);
void uh_ssl_client_detach(struct uh_client *cl);
#else
static inline int uh_ssl_init(const char *key, const char *crt)
{
return -1;
}
static inline void uh_ssl_client_attach(struct uh_client *cl)
{
}
static inline void uh_ssl_client_detach(struct uh_client *cl)
{
}
#endif
#endif

View File

@ -14,739 +14,87 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libubox/usock.h>
#include "uhttpd.h"
#include "uh_ssl.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <arpa/inet.h>
#include <signal.h>
#include "internal.h"
#include "uhttpd/uhttpd.h"
#include "uhttpd/ssl.h"
const char *uh_version()
static void uh_set_docroot(struct uh_server *srv, const char *docroot)
{
return UHTTPD_VERSION_STRING;
free(srv->docroot);
srv->docroot = strdup(".");
}
static const char *http_status_str(enum http_status s)
static void uh_set_index_file(struct uh_server *srv, const char *index_file)
{
switch (s) {
#define XX(num, name, string) case num : return #string;
HTTP_STATUS_MAP(XX)
#undef XX
}
return "<unknown>";
free(srv->index_file);
srv->index_file = strdup("index.html");
}
static void uh_connection_destroy(struct uh_connection *con)
static void uh_server_free(struct uh_server *srv)
{
if (con) {
struct ev_loop *loop = con->srv->loop;
struct uh_client *cl, *tmp;
if (con->sock > 0)
close(con->sock);
uh_buf_free(&con->read_buf);
uh_buf_free(&con->write_buf);
ev_io_stop(loop, &con->read_watcher);
ev_io_stop(loop, &con->write_watcher);
ev_timer_stop(loop, &con->timer_watcher);
list_del(&con->list);
uh_ssl_free(con);
uh_log_debug("destroy connection(%p)", con);
free(con);
}
}
static void connection_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents)
{
struct uh_connection *con = container_of(w, struct uh_connection, timer_watcher);
uh_log_debug("connection(%p) timeout", con);
uh_send_error(con, HTTP_STATUS_REQUEST_TIMEOUT, NULL);
}
static int uh_con_reuse(struct uh_connection *con)
{
con->flags = 0;
memset(&con->req, 0, sizeof(struct uh_request));
http_parser_init(&con->parser, HTTP_REQUEST);
ev_timer_mode(con->srv->loop, &con->timer_watcher, UH_CONNECTION_TIMEOUT, 0);
/* Retain pre allocated memory to improve performance */
uh_buf_remove(&con->read_buf, con->read_buf.len);
uh_buf_remove(&con->write_buf, con->write_buf.len);
return 0;
}
static int on_url(http_parser *parser, const char *at, size_t len)
{
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
struct http_parser_url url;
uh_log_debug("Url:[%.*s]", (int)len, at);
con->req.url.at = at;
con->req.url.len = len;
if (len > UH_URI_SIZE_LIMIT) {
uh_send_error(con, HTTP_STATUS_URI_TOO_LONG, NULL);
return -1;
}
if (http_parser_parse_url(at, len, 0, &url)) {
uh_log_err("http_parser_parse_url() failed");
uh_send_error(con, HTTP_STATUS_BAD_REQUEST, NULL);
return -1;
}
con->req.path.at = at + url.field_data[UF_PATH].off;
con->req.path.len = url.field_data[UF_PATH].len;
uh_log_debug("Path:[%.*s]", (int)con->req.path.len, con->req.path.at);
if (url.field_set & (1 << UF_QUERY)) {
con->req.query.at = at + url.field_data[UF_QUERY].off;
con->req.query.len = url.field_data[UF_QUERY].len;
uh_log_debug("Query:[%.*s]", (int)con->req.query.len, con->req.query.at);
}
return 0;
}
static int on_header_field(http_parser *parser, const char *at, size_t len)
{
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
struct uh_header *header = con->req.header;
uh_log_debug("header field:[%.*s]", (int)len, at);
header[con->req.header_num].field.at = at;
header[con->req.header_num].field.len = len;
return 0;
}
static int on_header_value(http_parser *parser, const char *at, size_t len)
{
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
struct uh_header *header = con->req.header;
uh_log_debug("header value:[%.*s]", (int)len, at);
header[con->req.header_num].value.at = at;
header[con->req.header_num].value.len = len;
con->req.header_num += 1;
return 0;
}
static int on_headers_complete(http_parser *parser)
{
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
if (parser->method != HTTP_GET && parser->method != HTTP_POST) {
uh_send_error(con, HTTP_STATUS_NOT_IMPLEMENTED, NULL);
return -1;
}
return 0;
}
static int on_body(http_parser *parser, const char *at, size_t len)
{
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
uh_log_debug("body:[%.*s]", (int)len, at);
if (!con->req.body.at)
con->req.body.at = at;
con->req.body.len += len;
if (con->req.body.len > UH_BODY_SIZE_LIMIT) {
uh_send_error(con, HTTP_STATUS_PAYLOAD_TOO_LARGE, NULL);
return -1;
}
return 0;
}
static int on_message_complete(http_parser *parser)
{
struct uh_connection *con = container_of(parser, struct uh_connection, parser);
struct uh_hook *h;
list_for_each_entry(h, &con->srv->hooks, list) {
if (uh_str_cmp(&con->req.path, h->path)) {
h->cb(con);
if (!(con->flags & UH_CON_CLOSE))
con->flags |= UH_CON_REUSE;
return 0;
}
}
if (con->srv->default_cb) {
con->srv->default_cb(con);
return 0;
}
uh_send_error(con, HTTP_STATUS_NOT_FOUND, NULL);
return 0;
}
static http_parser_settings parser_settings = {
.on_url = on_url,
.on_header_field = on_header_field,
.on_header_value = on_header_value,
.on_headers_complete = on_headers_complete,
.on_body = on_body,
.on_message_complete = on_message_complete
};
static void connection_read_cb(struct ev_loop *loop, ev_io *w, int revents)
{
struct uh_connection *con = container_of(w, struct uh_connection, read_watcher);
struct uh_buf *buf = &con->read_buf;
char *base;
int len, parsered;
#if (UHTTPD_SSL_ENABLED)
if (con->flags & UH_CON_SSL_HANDSHAKE_DONE)
goto handshake_done;
uh_ssl_handshake(con);
if (con->flags & UH_CON_CLOSE)
uh_connection_destroy(con);
return;
handshake_done:
#endif
if (unlikely(uh_buf_available(buf) < 1)) {
int off = con->parser.mark - buf->base;
uh_buf_grow(buf, UH_BUFFER_SIZE);
con->parser.mark = buf->base + off;
}
base = buf->base + buf->len;
len = uh_ssl_read(con, base, uh_buf_available(buf));
if (unlikely(len <= 0)) {
if (con->flags & UH_CON_CLOSE)
uh_connection_destroy(con);
return;
}
buf->len += len;
parsered = http_parser_execute(&con->parser, &parser_settings, base, len);
if (unlikely(con->flags & UH_CON_CLOSE))
return;
if (unlikely(parsered != len)) {
uh_log_err("http_parser_execute() failed:%s", http_errno_description(HTTP_PARSER_ERRNO(&con->parser)));
uh_send_error(con, HTTP_STATUS_BAD_REQUEST, NULL);
return;
}
ev_timer_mode(loop, &con->timer_watcher, UH_CONNECTION_TIMEOUT, 0);
}
static void connection_write_cb(struct ev_loop *loop, ev_io *w, int revents)
{
struct uh_connection *con = container_of(w, struct uh_connection, write_watcher);
struct uh_buf *buf = &con->write_buf;
if (buf->len > 0) {
int len = uh_ssl_write(con, buf->base, buf->len);
if (len > 0)
uh_buf_remove(buf, len);
}
if (buf->len == 0) {
ev_io_stop(loop, w);
if (!http_should_keep_alive(&con->parser))
con->flags |= UH_CON_CLOSE;
if (con->flags & UH_CON_REUSE)
uh_con_reuse(con);
}
if (con->flags & UH_CON_CLOSE)
uh_connection_destroy(con);
}
static void uh_accept_cb(struct ev_loop *loop, ev_io *w, int revents)
{
int sock = -1;
struct uh_server *srv = container_of(w, struct uh_server, read_watcher);
struct uh_connection *con = NULL;
ev_io *read_watcher, *write_watcher;
ev_timer *timer_watcher;
con = calloc(1, sizeof(struct uh_connection));
if (unlikely(!con)) {
uh_log_err("calloc");
return;
}
con->srv = srv;
list_add(&con->list, &srv->connections);
sock = uh_ssl_accept(con);
if (unlikely(sock < 0))
goto err;
read_watcher = &con->read_watcher;
ev_io_init(read_watcher, connection_read_cb, sock, EV_READ);
ev_io_start(loop,read_watcher);
write_watcher = &con->write_watcher;
ev_io_init(write_watcher, connection_write_cb, sock, EV_WRITE);
timer_watcher = &con->timer_watcher;
ev_timer_init(timer_watcher, connection_timeout_cb, UH_CONNECTION_TIMEOUT, 0);
ev_timer_start(loop, timer_watcher);
http_parser_init(&con->parser, HTTP_REQUEST);
uh_log_debug("new connection:%p", con);
return;
err:
uh_connection_destroy(con);
}
struct uh_server *uh_server_new(struct ev_loop *loop, const char *ipaddr, int port)
{
struct uh_server *srv = NULL;
struct sockaddr_in addr;
int sock = -1, on = 1;
ev_io *read_watcher;
char buf[PATH_MAX] = "";
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
signal(SIGPIPE, SIG_IGN);
if (inet_pton(AF_INET, ipaddr, &addr.sin_addr) <= 0) {
uh_log_err("invalid ipaddr");
return NULL;
}
srv = calloc(1, sizeof(struct uh_server));
if (!srv) {
uh_log_err("calloc");
return NULL;
}
if (!realpath(".", buf)) {
uh_log_err("Unable to determine work dir");
goto err;
}
srv->docroot = strdup(buf);
uh_log_debug("docroot:%s", srv->docroot);
INIT_LIST_HEAD(&srv->hooks);
INIT_LIST_HEAD(&srv->connections);
sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
if (sock < 0) {
uh_log_err("socket");
goto err;
}
srv->sock = sock;
srv->loop = loop;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
uh_log_err("bind");
goto err;
}
if (listen(sock, SOMAXCONN) < 0) {
uh_log_err("listen");
goto err;
}
read_watcher = &srv->read_watcher;
ev_io_init(read_watcher, uh_accept_cb, sock, EV_READ);
ev_io_start(loop, read_watcher);
return srv;
err:
uh_server_free(srv);
return NULL;
}
void uh_server_free(struct uh_server *srv)
{
if (srv) {
struct uh_connection *con, *tmp_c;
struct uh_hook *h, *tmp_h;
close(srv->fd.fd);
uloop_fd_delete(&srv->fd);
if (srv->docroot)
free(srv->docroot);
if (srv->sock > 0)
close(srv->sock);
ev_io_stop(srv->loop, &srv->read_watcher);
list_for_each_entry_safe(con, tmp_c, &srv->connections, list) {
uh_connection_destroy(con);
}
list_for_each_entry_safe(cl, tmp, &srv->clients, list)
cl->free(cl);
list_for_each_entry_safe(h, tmp_h, &srv->hooks, list) {
list_del(&h->list);
free(h->path);
free(h);
}
uh_ssl_ctx_free(srv);
uh_action_free(srv);
uh_ssl_free();
free(srv->docroot);
free(srv->index_file);
free(srv);
}
}
void uh_set_docroot(struct uh_server *srv, const char *path)
static void uh_accept_cb(struct uloop_fd *fd, unsigned int events)
{
srv->docroot = strdup(path);
uh_log_debug("docroot:%s", srv->docroot);
struct uh_server *srv = container_of(fd, struct uh_server, fd);
uh_accept_client(srv, srv->ssl);
}
int uh_send(struct uh_connection *con, const void *buf, int len)
struct uh_server *uh_server_new(const char *host, const char *port)
{
len = uh_buf_append(&con->write_buf, buf, len);
if (len > 0)
ev_io_start(con->srv->loop, &con->write_watcher);
return len;
}
struct uh_server *srv = NULL;
int sock = -1;
int uh_printf(struct uh_connection *con, const char *fmt, ...)
{
int len = 0;
va_list ap;
char *str = NULL;
assert(fmt);
if (*fmt) {
va_start(ap, fmt);
len = vasprintf(&str, fmt, ap);
va_end(ap);
}
if (len >= 0) {
len = uh_send(con, str, len);
free(str);
}
return len;
}
static void send_status_line(struct uh_connection *con, int code)
{
const char *reason = http_status_str(code);
uh_printf(con, "HTTP/1.1 %d %s\r\nServer: Libuhttp %s\r\n",
code, reason, UHTTPD_VERSION_STRING);
}
void uh_send_head(struct uh_connection *con, int status, int length, const char *extra_headers)
{
send_status_line(con, status);
if (length < 0)
uh_printf(con, "%s", "Transfer-Encoding: chunked\r\n");
else
uh_printf(con, "Content-Length: %d\r\n", length);
if (extra_headers)
uh_send(con, extra_headers, strlen(extra_headers));
uh_send(con, "\r\n", 2);
}
void uh_send_error(struct uh_connection *con, int code, const char *reason)
{
if (!reason)
reason = http_status_str(code);
if (http_should_keep_alive(&con->parser) && code < HTTP_STATUS_BAD_REQUEST) {
uh_send_head(con, code, strlen(reason), "Content-Type: text/plain\r\nConnection: keep-alive\r\n");
} else {
uh_send_head(con, code, strlen(reason), "Content-Type: text/plain\r\nConnection: close\r\n");
con->flags |= UH_CON_CLOSE;
sock = usock(USOCK_TCP | USOCK_SERVER | USOCK_IPV4ONLY, host, port);
if (sock < 0) {
uh_log_err("usock");
return NULL;
}
uh_send(con, reason, strlen(reason));
}
void uh_redirect(struct uh_connection *con, int code, const char *location)
{
char body[128] = "";
snprintf(body, sizeof(body), "<p>Moved <a href=\"%s\">here</a></p>", location);
send_status_line(con, code);
uh_printf(con,
"Location: %s\r\n"
"Content-Type: text/html\r\n"
"Content-Length: %zu\r\n"
"Cache-Control: no-cache\r\n", location, strlen(body));
uh_send(con, "\r\n", 2);
}
int uh_send_chunk(struct uh_connection *con, const char *buf, int len)
{
int slen = 0;
slen += uh_printf(con, "%X\r\n", len);
slen += uh_send(con, buf, len);
slen += uh_send(con, "\r\n", 2);
return slen;
}
int uh_printf_chunk(struct uh_connection *con, const char *fmt, ...)
{
int len = 0;
va_list ap;
char *str = NULL;
assert(fmt);
if (*fmt) {
va_start(ap, fmt);
len = vasprintf(&str, fmt, ap);
va_end(ap);
}
if (len >= 0) {
len = uh_send_chunk(con, str, len);
free(str);
}
return len;
}
int uh_register_hook(struct uh_server *srv, const char *path, uh_hookfn_t cb)
{
struct uh_hook *h;
assert(path);
h = calloc(1, sizeof(struct uh_hook));
if (!h) {
srv = calloc(1, sizeof(struct uh_server));
if (!srv) {
uh_log_err("calloc");
return -1;
goto err;
}
h->path = strdup(path);
if (!h->path) {
uh_log_err("strdup");
free(h);
return -1;
}
srv->docroot = strdup(".");
srv->index_file = strdup("index.html");
srv->fd.fd = sock;
srv->fd.cb = uh_accept_cb;
uloop_fd_add(&srv->fd, ULOOP_READ);
INIT_LIST_HEAD(&srv->clients);
avl_init(&srv->actions, avl_strcmp, false, NULL);
h->cb = cb;
list_add(&h->list, &srv->hooks);
srv->free = uh_server_free;
srv->set_docroot = uh_set_docroot;
srv->set_index_file = uh_set_index_file;
#if (UHTTPD_SSL_SUPPORT)
srv->ssl_init = uh_ssl_init;
#endif
return srv;
return 0;
}
void uh_register_default_hook(struct uh_server *srv, uh_hookfn_t cb)
{
srv->default_cb = cb;
}
inline enum http_method uh_get_method(struct uh_connection *con)
{
return con->parser.method;
}
inline const char *uh_get_method_str(struct uh_connection *con)
{
return http_method_str(con->parser.method);
}
inline struct uh_str *uh_get_url(struct uh_connection *con)
{
return &con->req.url;
}
inline struct uh_str *uh_get_path(struct uh_connection *con)
{
return &con->req.path;
}
inline struct uh_str *uh_get_query(struct uh_connection *con)
{
return &con->req.query;
}
inline int uh_get_con_sock(struct uh_connection *con)
{
return con->sock;
}
static inline char c2hex(char c)
{
return c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - 'a' + 10; /* accept small letters just in case */
}
int uh_unescape(const char *str, int len, char *out, int olen)
{
const char *p = str;
char *o = out;
assert(str && out && olen > 1);
olen -= 1;
while ((p - str < len) && (o - out < olen)) {
if (*p == '%') {
p++;
if (p + 1 - str < len) {
*o = c2hex(*p++) << 4;
*o += c2hex(*p++);
o++;
}
} else if (*p == '+') {
*o++ = ' ';
p++;
} else {
*o++ = *p++;
}
}
*o = 0;
return 0;
}
static bool __uh_foreach_var(struct uh_str *data,
bool (*found)(struct uh_str *key, struct uh_str *val, void *udata), void *udata)
{
struct uh_str key, val;
int state = s_query_key;
int i;
char ch;
key.len = 0;
val.len = 0;
for (i = 0; i < data->len; i++) {
ch = data->at[i];
switch(state) {
case s_query_key:
if (ch == '=') {
state = s_query_value;
break;
}
if (!key.len)
key.at = data->at + i;
key.len++;
break;
case s_query_value:
if (ch == '&') {
state = s_query_key;
if (found(&key, &val, udata))
return true;
key.len = 0;
val.len = 0;
break;
}
if (!val.len)
val.at = data->at + i;
val.len++;
default:
break;
}
}
if (state == s_query_value && key.len > 0 && val.len > 0)
if (found(&key, &val, udata))
return true;
return false;
}
bool uh_foreach_var(struct uh_connection *con,
bool (*found)(struct uh_str *key, struct uh_str *val, void *udata), void *udata)
{
struct uh_str *content_type = uh_get_header(con, "Content-Type");
if (__uh_foreach_var(&con->req.query, found, udata))
return true;
if (content_type && uh_str_cmp(content_type, "application/x-www-form-urlencoded"))
return __uh_foreach_var(&con->req.body, found, udata);
return false;
}
static bool found_var(struct uh_str *key, struct uh_str *val, void *udata)
{
struct uh_str *name = (struct uh_str *)udata;
if (key->len == name->len && !strncmp(key->at, name->at, key->len)) {
name->at = val->at;
name->len = val->len;
return true;
}
return false;
}
struct uh_str uh_get_var(struct uh_connection *con, const char *name)
{
struct uh_str val = {.at = name, .len = strlen(name)};
if (!uh_foreach_var(con, found_var, &val)) {
val.at = NULL;
val.len = 0;
}
return val;
}
struct uh_str *uh_get_header(struct uh_connection *con, const char *name)
{
int i;
struct uh_header *header = con->req.header;
for (i = 0; i < con->req.header_num; i++) {
if (uh_str_cmp(&header[i].field, name))
return &header[i].value;
}
err:
close(sock);
return NULL;
}

View File

@ -18,6 +18,27 @@
#ifndef _UHTTPD_H
#define _UHTTPD_H
#include "uhttpd/uhttpd.h"
#include "client.h"
#include "action.h"
struct uh_server {
bool ssl;
struct uloop_fd fd;
char *docroot;
char *index_file;
int nclients;
struct avl_tree actions;
struct list_head clients;
void (*free)(struct uh_server *srv);
void (*set_docroot)(struct uh_server *srv, const char *docroot);
void (*set_index_file)(struct uh_server *srv, const char *index_file);
#if (UHTTPD_SSL_SUPPORT)
int (*ssl_init)(struct uh_server *srv, const char *key, const char *crt);
#endif
};
struct uh_server *uh_server_new(const char *host, const char *port);
#endif

164
src/utils.c 100755
View File

@ -0,0 +1,164 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "utils.h"
void uh_printf(struct uh_client *cl, const char *format, ...)
{
va_list arg;
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
va_start(arg, format);
ustream_vprintf(cl->us, format, arg);
va_end(arg);
}
void uh_vprintf(struct uh_client *cl, const char *format, va_list arg)
{
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
ustream_vprintf(cl->us, format, arg);
}
void uh_chunk_send(struct uh_client *cl, const void *data, int len)
{
struct ustream *us = cl->us;
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
ustream_printf(us, "%X\r\n", len);
ustream_write(us, data, len, true);
ustream_printf(us, "\r\n", len);
}
void uh_chunk_printf(struct uh_client *cl, const char *format, ...)
{
va_list arg;
va_start(arg, format);
uh_chunk_vprintf(cl, format, arg);
va_end(arg);
}
void uh_chunk_vprintf(struct uh_client *cl, const char *format, va_list arg)
{
struct ustream *us = cl->us;
char buf[256];
va_list arg2;
int len;
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
va_copy(arg2, arg);
len = vsnprintf(buf, sizeof(buf), format, arg2);
va_end(arg2);
ustream_printf(us, "%X\r\n", len);
if (len < sizeof(buf))
ustream_write(cl->us, buf, len, true);
else
ustream_vprintf(cl->us, format, arg);
ustream_printf(us, "\r\n", len);
}
char *uh_split_header(char *str)
{
char *val;
val = strchr(str, ':');
if (!val)
return NULL;
*val = 0;
val++;
while (isspace(*val))
val++;
return val;
}
/* blen is the size of buf; slen is the length of src. The input-string need
** not be, and the output string will not be, null-terminated. Returns the
** length of the decoded string, -1 on buffer overflow, -2 on malformed string. */
int uh_urldecode(char *buf, int blen, const char *src, int slen)
{
int i;
int len = 0;
#define hex(x) \
(((x) <= '9') ? ((x) - '0') : \
(((x) <= 'F') ? ((x) - 'A' + 10) : \
((x) - 'a' + 10)))
for (i = 0; (i < slen) && (len < blen); i++)
{
if (src[i] != '%') {
buf[len++] = src[i];
continue;
}
if (i + 2 >= slen || !isxdigit(src[i + 1]) || !isxdigit(src[i + 2]))
return -2;
buf[len++] = (char)(16 * hex(src[i+1]) + hex(src[i+2]));
i += 2;
}
buf[len] = 0;
return (i == slen) ? len : -1;
}
/* blen is the size of buf; slen is the length of src. The input-string need
** not be, and the output string will not be, null-terminated. Returns the
** length of the encoded string, or -1 on error (buffer overflow) */
int uh_urlencode(char *buf, int blen, const char *src, int slen)
{
int i;
int len = 0;
static const char hex[] = "0123456789abcdef";
for (i = 0; (i < slen) && (len < blen); i++)
{
if( isalnum(src[i]) || (src[i] == '-') || (src[i] == '_') ||
(src[i] == '.') || (src[i] == '~') )
{
buf[len++] = src[i];
}
else if ((len+3) <= blen)
{
buf[len++] = '%';
buf[len++] = hex[(src[i] >> 4) & 15];
buf[len++] = hex[ src[i] & 15];
}
else
{
len = -1;
break;
}
}
return (i == slen) ? len : -1;
}
int find_idx(const char *const *list, int max, const char *str)
{
int i;
for (i = 0; i < max; i++)
if (!strcmp(list[i], str))
return i;
return -1;
}

38
src/utils.h 100755
View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2017 Jianhui Zhao <jianhuizhao329@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _UTILS_H
#define _UTILS_H
#include "client.h"
#define min(x, y) (((x) < (y)) ? (x) : (y))
#define max(x, y) (((x) > (y)) ? (x) : (y))
void uh_printf(struct uh_client *cl, const char *format, ...);
void uh_vprintf(struct uh_client *cl, const char *format, va_list arg);
void uh_chunk_send(struct uh_client *cl, const void *data, int len);
void uh_chunk_printf(struct uh_client *cl, const char *format, ...);
void uh_chunk_vprintf(struct uh_client *cl, const char *format, va_list arg);
char *uh_split_header(char *str);
int uh_urldecode(char *buf, int blen, const char *src, int slen);
int uh_urlencode(char *buf, int blen, const char *src, int slen);
int find_idx(const char *const *list, int max, const char *str);
#endif