parent
ef94e04328
commit
568a287feb
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
贡献代码
|
||||
================================================================================
|
||||
|
||||
如果你想为[libuhttpd](https://github.com/zhaojh329/libuhttpd)贡献代码, 请按照如下步骤:
|
||||
如果你想为[libuhttp](https://github.com/zhaojh329/libuhttpd)贡献代码, 请按照如下步骤:
|
||||
|
||||
1. 点击fork按钮:
|
||||
|
||||
|
|
149
README.md
149
README.md
|
@ -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)
|
||||
|
|
147
README_ZH.md
147
README_ZH.md
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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%>
|
|
@ -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("")
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
83
src/buf.c
83
src/buf.c
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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_ */
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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, ...)
|
||||
{
|
||||
|
|
|
@ -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__)
|
||||
|
2374
src/parser.c
2374
src/parser.c
File diff suppressed because it is too large
Load Diff
219
src/ssl.c
219
src/ssl.c
|
@ -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
|
||||
}
|
||||
|
709
src/template.c
709
src/template.c
|
@ -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, """, 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
760
src/uhttpd.c
760
src/uhttpd.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue