Merge remote-tracking branch 'upstream/master' into main
commit
e29741fb1f
|
@ -0,0 +1,32 @@
|
|||
name: build
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- ssl: none
|
||||
name: none
|
||||
- ssl: openssl
|
||||
name: OPENSSL
|
||||
pkg: libssl-dev
|
||||
- ssl: mbedtls
|
||||
name: MBEDTLS
|
||||
pkg: libmbedtls-dev
|
||||
- ssl: wolfssl
|
||||
name: WOLFSSL
|
||||
pkg: libwolfssl-dev
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: build
|
||||
env:
|
||||
name: ${{ matrix.name }}
|
||||
pkg: ${{ matrix.pkg }}
|
||||
run: |
|
||||
sudo apt install -y libev-dev
|
||||
[ -n "$pkg" ] && sudo apt install -y $pkg
|
||||
[ "$name" = "none" ] && cmake . -DUHTTPD_SSL_SUPPORT=OFF || cmake . -DUHTTPD_USE_$name=ON
|
||||
make
|
|
@ -0,0 +1,45 @@
|
|||
name: release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- id: changelog
|
||||
uses: zhaojh329/auto-changelog@master
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- id: release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
draft: true
|
||||
body: ${{steps.changelog.outputs.changelog}}
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- id: get-version
|
||||
uses: battila7/get-version-action@v2
|
||||
- id: release-asset
|
||||
run: |
|
||||
version=${{ steps.get-version.outputs.version-without-v }}
|
||||
cd ..
|
||||
cp -r libuhttpd libuhttpd-$version
|
||||
rm -rf libuhttpd-$version/.git* libuhttpd-$version/src/buffer/.git* libuhttpd-$version/src/http-parser/{.git*,.mailmap,.travis.yml}
|
||||
tar zcfv libuhttpd-$version.tar.gz libuhttpd-$version
|
||||
echo "::set-output name=asset-path::../libuhttpd-$version.tar.gz"
|
||||
echo "::set-output name=asset-name::libuhttpd-$version.tar.gz"
|
||||
- id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.release.outputs.upload_url }}
|
||||
asset_path: ${{ steps.release-asset.outputs.asset-path }}
|
||||
asset_name: ${{ steps.release-asset.outputs.asset-name }}
|
||||
asset_content_type: application/gzip
|
|
@ -50,14 +50,3 @@ modules.order
|
|||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
server-cert.pem
|
||||
server-key.pem
|
||||
|
||||
build/
|
||||
|
||||
CMakeFiles
|
||||
CMakeCache.txt
|
||||
cmake_install.cmake
|
||||
Makefile
|
||||
helloworld
|
||||
|
|
13
.travis.yml
13
.travis.yml
|
@ -1,13 +0,0 @@
|
|||
language: c
|
||||
|
||||
os:
|
||||
- linux
|
||||
|
||||
before_install:
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get install -y libssl-dev libev-dev
|
||||
|
||||
script:
|
||||
- mkdir build && cd build
|
||||
- cmake ..
|
||||
- make && sudo make install
|
|
@ -1,11 +1,15 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
project(libuhttpd C)
|
||||
|
||||
option(BUILD_EXAMPLE "Build example" ON)
|
||||
|
||||
INCLUDE(CheckLibraryExists)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(example)
|
||||
if(BUILD_EXAMPLE)
|
||||
add_subdirectory(example)
|
||||
endif()
|
||||
|
||||
|
|
41
README.md
41
README.md
|
@ -9,16 +9,15 @@
|
|||
[4]: https://github.com/zhaojh329/libuhttpd/pulls
|
||||
[5]: https://img.shields.io/badge/Issues-welcome-brightgreen.svg?style=plastic
|
||||
[6]: https://github.com/zhaojh329/libuhttpd/issues/new
|
||||
[7]: https://img.shields.io/badge/release-3.7.0-blue.svg?style=plastic
|
||||
[7]: https://img.shields.io/badge/release-3.10.1-blue.svg?style=plastic
|
||||
[8]: https://github.com/zhaojh329/libuhttpd/releases
|
||||
[9]: https://travis-ci.org/zhaojh329/libuhttpd.svg?branch=master
|
||||
[10]: https://travis-ci.org/zhaojh329/libuhttpd
|
||||
[9]: https://github.com/zhaojh329/libuhttpd/workflows/build/badge.svg
|
||||
|
||||
[![license][1]][2]
|
||||
[![PRs Welcome][3]][4]
|
||||
[![Issue Welcome][5]][6]
|
||||
[![Release Version][7]][8]
|
||||
[![Build Status][9]][10]
|
||||
![Build Status][9]
|
||||
|
||||
[libev]: http://software.schmorp.de/pkg/libev.html
|
||||
[http-parser]: https://github.com/nodejs/http-parser
|
||||
|
@ -32,19 +31,47 @@ A very flexible, lightweight and fully asynchronous HTTP server library based on
|
|||
* Lightweight and fully asynchronous
|
||||
* Use [libev] as its event backend
|
||||
* Support HTTPS - OpenSSL, mbedtls and CyaSSl(wolfssl)
|
||||
* Support HTTP pipelining
|
||||
* Support IPv6
|
||||
* Support plugin
|
||||
* Support upload large file
|
||||
* Support HTTP range requests
|
||||
* Support multi-process model - The same multi-process model as Nginx
|
||||
* Flexible - you can easily extend your application to have HTTP/HTTPS services
|
||||
* Code structure is concise and understandable, also suitable for learning
|
||||
|
||||
# Dependencies
|
||||
* [libev]
|
||||
* [http-parser] - A parser for HTTP messages written in C
|
||||
* [libev] - A full-featured and high-performance event loop
|
||||
* [http-parser] - A high performance parser for HTTP messages written in C
|
||||
* [mbedtls] - If you choose mbedtls as your SSL backend
|
||||
* [wolfssl] - If you choose wolfssl as your SSL backend
|
||||
* [openssl] - If you choose openssl as your SSL backend
|
||||
|
||||
# Benchmark
|
||||
## Nginx
|
||||
|
||||
$ wrk -t4 -c400 -d10s http://localhost:80/test.html
|
||||
Running 10s test @ http://localhost:80/test.html
|
||||
4 threads and 400 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 3.54ms 7.32ms 224.58ms 93.30%
|
||||
Req/Sec 40.63k 12.49k 96.29k 74.50%
|
||||
1622012 requests in 10.05s, 385.09MB read
|
||||
Requests/sec: 161390.39
|
||||
Transfer/sec: 38.32MB
|
||||
|
||||
## libuhttpd
|
||||
|
||||
$ wrk -t4 -c400 -d10s http://localhost:8080/test.html
|
||||
Running 10s test @ http://localhost:8080/test.html
|
||||
4 threads and 400 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 2.12ms 3.01ms 31.30ms 89.26%
|
||||
Req/Sec 70.87k 12.53k 142.54k 79.75%
|
||||
2826394 requests in 10.05s, 547.18MB read
|
||||
Requests/sec: 281328.83
|
||||
Transfer/sec: 54.46MB
|
||||
|
||||
# Configure
|
||||
See which configuration are supported
|
||||
|
||||
|
@ -59,7 +86,7 @@ See which configuration are supported
|
|||
# Run Example
|
||||
Run
|
||||
|
||||
~/libuhttpd/build$ ./example/example
|
||||
~/libuhttpd/build$ ./example/simple_server -v
|
||||
|
||||
Then use the command curl or browser to test
|
||||
|
||||
|
|
41
README_ZH.md
41
README_ZH.md
|
@ -6,16 +6,15 @@
|
|||
[4]: https://github.com/zhaojh329/libuhttpd/pulls
|
||||
[5]: https://img.shields.io/badge/Issues-welcome-brightgreen.svg?style=plastic
|
||||
[6]: https://github.com/zhaojh329/libuhttpd/issues/new
|
||||
[7]: https://img.shields.io/badge/release-3.7.0-blue.svg?style=plastic
|
||||
[7]: https://img.shields.io/badge/release-3.10.1-blue.svg?style=plastic
|
||||
[8]: https://github.com/zhaojh329/libuhttpd/releases
|
||||
[9]: https://travis-ci.org/zhaojh329/libuhttpd.svg?branch=master
|
||||
[10]: https://travis-ci.org/zhaojh329/libuhttpd
|
||||
[9]: https://github.com/zhaojh329/libuhttpd/workflows/build/badge.svg
|
||||
|
||||
[![license][1]][2]
|
||||
[![PRs Welcome][3]][4]
|
||||
[![Issue Welcome][5]][6]
|
||||
[![Release Version][7]][8]
|
||||
[![Build Status][9]][10]
|
||||
![Build Status][9]
|
||||
|
||||
[libev]: http://software.schmorp.de/pkg/libev.html
|
||||
[http-parser]: https://github.com/nodejs/http-parser
|
||||
|
@ -29,19 +28,47 @@
|
|||
* 轻量、全异步
|
||||
* 使用[libev]作为其事件后端
|
||||
* 支持HTTPS - OpenSSL, mbedtls 和 CyaSSl(wolfssl)
|
||||
* 支持 HTTP 流水线
|
||||
* 支持 IPv6
|
||||
* 支持插件
|
||||
* 支持上传大文件
|
||||
* 支持 HTTP 范围请求
|
||||
* 支持多进程模型 - 和 Nginx 一样的多进程模型
|
||||
* 可伸缩 - 你可以非常方便的扩展你的应用程序,使之具备HTTP/HTTPS服务
|
||||
* 代码结构简洁通俗易懂,亦适合学习
|
||||
|
||||
# 依赖
|
||||
* [libev]
|
||||
* [http-parser] - 已经集成到源码里面
|
||||
* [libev] - 一个全功能和高性能的事件循环库
|
||||
* [http-parser] - 一个用 C 语言编写的高性能的 HTTP 消息解析器
|
||||
* [mbedtls] - 如果你选择mbedtls作为你的SSL后端
|
||||
* [wolfssl] - 如果你选择wolfssl作为你的SSL后端
|
||||
* [openssl] - 如果你选择openssl作为你的SSL后端
|
||||
|
||||
# 基准测试
|
||||
## Nginx
|
||||
|
||||
$ wrk -t4 -c400 -d10s http://localhost:80/test.html
|
||||
Running 10s test @ http://localhost:80/test.html
|
||||
4 threads and 400 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 3.54ms 7.32ms 224.58ms 93.30%
|
||||
Req/Sec 40.63k 12.49k 96.29k 74.50%
|
||||
1622012 requests in 10.05s, 385.09MB read
|
||||
Requests/sec: 161390.39
|
||||
Transfer/sec: 38.32MB
|
||||
|
||||
## libuhttpd
|
||||
|
||||
$ wrk -t4 -c400 -d10s http://localhost:8080/test.html
|
||||
Running 10s test @ http://localhost:8080/test.html
|
||||
4 threads and 400 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 2.12ms 3.01ms 31.30ms 89.26%
|
||||
Req/Sec 70.87k 12.53k 142.54k 79.75%
|
||||
2826394 requests in 10.05s, 547.18MB read
|
||||
Requests/sec: 281328.83
|
||||
Transfer/sec: 54.46MB
|
||||
|
||||
# 配置
|
||||
查看支持哪些配置选项
|
||||
|
||||
|
@ -56,7 +83,7 @@
|
|||
# 运行示例程序
|
||||
运行
|
||||
|
||||
~/libuhttpd/build$ ./example/example -v
|
||||
~/libuhttpd/build$ ./example/simple_server -v
|
||||
|
||||
然后使用命令curl或者浏览器进行测试
|
||||
|
||||
|
|
|
@ -5,14 +5,23 @@ include_directories(
|
|||
${CMAKE_BINARY_DIR}/src
|
||||
${LIBEV_INCLUDE_DIR})
|
||||
|
||||
add_executable(example example.c)
|
||||
set(LIBS ${LIBEV_LIBRARY})
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
target_link_libraries(example uhttpd ${LIBEV_LIBRARY})
|
||||
list(APPEND LIBS uhttpd)
|
||||
else()
|
||||
target_link_libraries(example uhttpd_s ${LIBEV_LIBRARY})
|
||||
list(APPEND LIBS uhttpd_s)
|
||||
endif()
|
||||
|
||||
add_executable(simple_server simple_server.c handler.c)
|
||||
target_link_libraries(simple_server ${LIBS})
|
||||
|
||||
add_executable(multi_process_server_reuseport multi_process_server_reuseport.c handler.c)
|
||||
target_link_libraries(multi_process_server_reuseport ${LIBS})
|
||||
|
||||
add_executable(multi_process_server multi_process_server.c handler.c)
|
||||
target_link_libraries(multi_process_server ${LIBS})
|
||||
|
||||
if(HAVE_DLOPEN)
|
||||
add_library(test_plugin MODULE test_plugin.c)
|
||||
set_target_properties(test_plugin PROPERTIES OUTPUT_NAME test_plugin PREFIX "")
|
||||
|
|
|
@ -1,210 +0,0 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "uhttpd.h"
|
||||
|
||||
static bool serve_file = false;
|
||||
static const char *docroot = ".";
|
||||
static const char *index_page = "index.html";
|
||||
|
||||
static void default_handler(struct uh_connection *conn, int event)
|
||||
{
|
||||
if (event != UH_EV_COMPLETE)
|
||||
return;
|
||||
|
||||
if (!serve_file) {
|
||||
struct uh_str path = conn->get_path(conn);
|
||||
struct uh_str query = conn->get_query(conn);
|
||||
struct uh_str ua = conn->get_header(conn, "User-Agent");
|
||||
struct uh_str body = conn->get_body(conn);
|
||||
|
||||
conn->send_head(conn, HTTP_STATUS_OK, -1, NULL);
|
||||
conn->chunk_printf(conn, "I'm Libuhttpd: %s\n", UHTTPD_VERSION_STRING);
|
||||
conn->chunk_printf(conn, "Method: %s\n", conn->get_method_str(conn));
|
||||
conn->chunk_printf(conn, "Path: %.*s\n", path.len ,path.p);
|
||||
conn->chunk_printf(conn, "Query: %.*s\n", query.len, query.p);
|
||||
conn->chunk_printf(conn, "User-Agent: %.*s\n", ua.len, ua.p);
|
||||
conn->chunk_printf(conn, "Body: %.*s\n", body.len, body.p);
|
||||
conn->chunk_end(conn);
|
||||
conn->done(conn);
|
||||
} else {
|
||||
conn->serve_file(conn, docroot, index_page);
|
||||
}
|
||||
}
|
||||
|
||||
static void upload_handler(struct uh_connection *conn, int event)
|
||||
{
|
||||
static int fd = -1;
|
||||
|
||||
if (event == UH_EV_HEAD_COMPLETE) {
|
||||
struct uh_str str = conn->get_header(conn, "Content-Length");
|
||||
int content_length;
|
||||
char buf[128];
|
||||
|
||||
sprintf(buf, "%.*s\n", (int)str.len, str.p);
|
||||
|
||||
content_length = atoi(buf);
|
||||
|
||||
if (content_length > 1024 * 1024 * 1024) {
|
||||
conn->error(conn, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Too big");
|
||||
return;
|
||||
}
|
||||
|
||||
} if (event == UH_EV_BODY) {
|
||||
struct uh_str body = conn->extract_body(conn);
|
||||
|
||||
if (fd < 0) {
|
||||
fd = open("upload.bin", O_RDWR | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd < 0) {
|
||||
conn->error(conn, HTTP_STATUS_INTERNAL_SERVER_ERROR, strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (write(fd, body.p, body.len) < 0) {
|
||||
conn->error(conn, HTTP_STATUS_INTERNAL_SERVER_ERROR, strerror(errno));
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
} else if (event == UH_EV_COMPLETE) {
|
||||
struct stat st;
|
||||
size_t size = 0;
|
||||
|
||||
conn->send_head(conn, HTTP_STATUS_OK, -1, NULL);
|
||||
|
||||
if (fd > 0) {
|
||||
fstat(fd, &st);
|
||||
close(fd);
|
||||
|
||||
fd = -1;
|
||||
size = st.st_size;
|
||||
}
|
||||
|
||||
conn->chunk_printf(conn, "Upload size: %zd\n", size);
|
||||
conn->chunk_end(conn);
|
||||
conn->done(conn);
|
||||
}
|
||||
}
|
||||
|
||||
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
|
||||
{
|
||||
if (w->signum == SIGINT) {
|
||||
ev_break(loop, EVBREAK_ALL);
|
||||
uh_log_info("Normal quit\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [option]\n"
|
||||
" -a addr # Default addr is localhost\n"
|
||||
" -p port # Default port is 8080\n"
|
||||
" -s # SSl on\n"
|
||||
" -f # Serve file\n"
|
||||
" -P # plugin path\n"
|
||||
" -v # verbose\n", prog);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct ev_loop *loop = EV_DEFAULT;
|
||||
struct ev_signal signal_watcher;
|
||||
struct uh_server *srv = NULL;
|
||||
const char *plugin_path = NULL;
|
||||
bool verbose = false;
|
||||
bool ssl = false;
|
||||
const char *addr = "localhost";
|
||||
int port = 8080;
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "a:p:sfP:v")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
addr = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
ssl = true;
|
||||
break;
|
||||
case 'f':
|
||||
serve_file = true;
|
||||
break;
|
||||
case 'P':
|
||||
plugin_path = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
default: /* '?' */
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
uh_log_threshold(LOG_ERR);
|
||||
|
||||
uh_log_info("libuhttpd version: %s\n", UHTTPD_VERSION_STRING);
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
srv = uh_server_new(loop, addr, port);
|
||||
if (!srv)
|
||||
return -1;
|
||||
|
||||
#if UHTTPD_SSL_SUPPORT
|
||||
if (ssl && srv->ssl_init(srv, "server-cert.pem", "server-key.pem") < 0)
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
srv->default_handler = default_handler;
|
||||
|
||||
srv->add_path_handler(srv, "/upload", upload_handler);
|
||||
|
||||
if (plugin_path)
|
||||
srv->load_plugin(srv, plugin_path);
|
||||
|
||||
ev_signal_init(&signal_watcher, signal_cb, SIGINT);
|
||||
ev_signal_start(loop, &signal_watcher);
|
||||
|
||||
ev_run(loop, 0);
|
||||
|
||||
err:
|
||||
srv->free(srv);
|
||||
free(srv);
|
||||
|
||||
ev_loop_destroy(loop);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "uhttpd.h"
|
||||
|
||||
void default_handler(struct uh_connection *conn, int event)
|
||||
{
|
||||
if (event != UH_EV_COMPLETE)
|
||||
return;
|
||||
|
||||
conn->serve_file(conn);
|
||||
}
|
||||
|
||||
void echo_handler(struct uh_connection *conn, int event)
|
||||
{
|
||||
if (event == UH_EV_COMPLETE) {
|
||||
struct uh_str path = conn->get_path(conn);
|
||||
struct uh_str query = conn->get_query(conn);
|
||||
struct uh_str ua = conn->get_header(conn, "User-Agent");
|
||||
struct uh_str body = conn->get_body(conn);
|
||||
|
||||
conn->send_head(conn, HTTP_STATUS_OK, -1, NULL);
|
||||
conn->chunk_printf(conn, "I'm Libuhttpd: %s\n", UHTTPD_VERSION_STRING);
|
||||
conn->chunk_printf(conn, "Method: %s\n", conn->get_method_str(conn));
|
||||
conn->chunk_printf(conn, "Path: %.*s\n", (int)path.len, path.p);
|
||||
conn->chunk_printf(conn, "Query: %.*s\n", (int)query.len, query.p);
|
||||
conn->chunk_printf(conn, "User-Agent: %.*s\n", (int)ua.len, ua.p);
|
||||
conn->chunk_printf(conn, "Body: %.*s\n", (int)body.len, body.p);
|
||||
conn->chunk_end(conn);
|
||||
conn->done(conn);
|
||||
}
|
||||
}
|
||||
|
||||
void upload_handler(struct uh_connection *conn, int event)
|
||||
{
|
||||
if (event == UH_EV_HEAD_COMPLETE) {
|
||||
uint64_t content_length = conn->get_content_length(conn);
|
||||
|
||||
if (content_length > 1024 * 1024 * 1024) {
|
||||
conn->error(conn, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Too big");
|
||||
return;
|
||||
}
|
||||
|
||||
conn->userdata = (void *)(intptr_t)-1;
|
||||
|
||||
} if (event == UH_EV_BODY) {
|
||||
struct uh_str body = conn->extract_body(conn);
|
||||
int fd = (intptr_t)conn->userdata;
|
||||
|
||||
if (fd < 0) {
|
||||
fd = open("upload.bin", O_RDWR | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd < 0) {
|
||||
conn->error(conn, HTTP_STATUS_INTERNAL_SERVER_ERROR, strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (write(fd, body.p, body.len) < 0) {
|
||||
conn->error(conn, HTTP_STATUS_INTERNAL_SERVER_ERROR, strerror(errno));
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
conn->userdata = (void *)(intptr_t)fd;
|
||||
} else if (event == UH_EV_COMPLETE) {
|
||||
int fd = (intptr_t)conn->userdata;
|
||||
struct stat st;
|
||||
size_t size = 0;
|
||||
|
||||
conn->send_head(conn, HTTP_STATUS_OK, -1, NULL);
|
||||
|
||||
if (fd > 0) {
|
||||
fstat(fd, &st);
|
||||
close(fd);
|
||||
|
||||
fd = -1;
|
||||
size = st.st_size;
|
||||
}
|
||||
|
||||
conn->chunk_printf(conn, "Upload size: %zd\n", size);
|
||||
conn->chunk_end(conn);
|
||||
conn->done(conn);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
|
||||
*
|
||||
* 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 _EXAMPLE_HANDLER_H
|
||||
#define _EXAMPLE_HANDLER_H
|
||||
|
||||
#include "uhttpd.h"
|
||||
|
||||
void default_handler(struct uh_connection *conn, int event);
|
||||
void echo_handler(struct uh_connection *conn, int event);
|
||||
void upload_handler(struct uh_connection *conn, int event);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "handler.h"
|
||||
|
||||
#define MAX_WORKER 10
|
||||
|
||||
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (w->signum == SIGINT) {
|
||||
pid_t *workers = w->data;
|
||||
|
||||
for (i = 0; i < MAX_WORKER; i++) {
|
||||
if (workers[i] == 0)
|
||||
break;
|
||||
kill(workers[i], SIGKILL);
|
||||
}
|
||||
|
||||
ev_break(loop, EVBREAK_ALL);
|
||||
uh_log_info("Normal quit\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [option]\n"
|
||||
" -h docroot # Document root, default is .\n"
|
||||
" -i index_page # Index page, default is index.html\n"
|
||||
" -a addr # address to listen\n"
|
||||
" -s addr # address to listen with ssl\n"
|
||||
" -P # plugin path\n"
|
||||
" -w # worker process number, default is equal to available CPUs\n"
|
||||
" -v # verbose\n", prog);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct ev_loop *loop = EV_DEFAULT;
|
||||
struct ev_signal signal_watcher;
|
||||
struct uh_server *srv = NULL;
|
||||
const char *plugin_path = NULL;
|
||||
bool verbose = false;
|
||||
const char *docroot = ".";
|
||||
const char *index_page = "index.html";
|
||||
pid_t workers[MAX_WORKER] = {};
|
||||
int nworker = get_nprocs();
|
||||
int opt, i;
|
||||
|
||||
srv = uh_server_new(loop);
|
||||
if (!srv)
|
||||
return -1;
|
||||
|
||||
while ((opt = getopt(argc, argv, "h:i:a:s:P:w:v")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
docroot = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
index_page = optarg;
|
||||
break;
|
||||
case 'a':
|
||||
if (srv->listen(srv, optarg, false) < 1)
|
||||
goto err;
|
||||
break;
|
||||
case 's':
|
||||
if (srv->listen(srv, optarg, true) < 1)
|
||||
goto err;
|
||||
break;
|
||||
case 'P':
|
||||
plugin_path = optarg;
|
||||
break;
|
||||
case 'w':
|
||||
nworker = atoi(optarg);
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
default: /* '?' */
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
uh_log_threshold(LOG_ERR);
|
||||
|
||||
uh_log_info("libuhttpd version: %s\n", UHTTPD_VERSION_STRING);
|
||||
|
||||
if (nworker < 1)
|
||||
return 0;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
#if UHTTPD_SSL_SUPPORT
|
||||
srv->ssl_init(srv, "cert.pem", "key.pem");
|
||||
#endif
|
||||
|
||||
srv->set_docroot(srv, docroot);
|
||||
srv->set_index_page(srv, index_page);
|
||||
|
||||
srv->set_default_handler(srv, default_handler);
|
||||
srv->add_path_handler(srv, "/echo", echo_handler);
|
||||
srv->add_path_handler(srv, "/upload", upload_handler);
|
||||
|
||||
if (plugin_path)
|
||||
srv->load_plugin(srv, plugin_path);
|
||||
|
||||
for (i = 0; i < nworker - 1; i++) {
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
uh_log_info("fork: %s\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
ev_loop_fork(loop);
|
||||
ev_run(loop, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
workers[i] = pid;
|
||||
}
|
||||
|
||||
ev_signal_init(&signal_watcher, signal_cb, SIGINT);
|
||||
signal_watcher.data = workers;
|
||||
ev_signal_start(loop, &signal_watcher);
|
||||
|
||||
ev_run(loop, 0);
|
||||
|
||||
err:
|
||||
srv->free(srv);
|
||||
free(srv);
|
||||
|
||||
ev_loop_destroy(loop);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "handler.h"
|
||||
|
||||
#define MAX_WORKER 10
|
||||
|
||||
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (w->signum == SIGINT) {
|
||||
pid_t *workers = w->data;
|
||||
|
||||
for (i = 0; i < MAX_WORKER; i++) {
|
||||
if (workers[i] == 0)
|
||||
break;
|
||||
kill(workers[i], SIGKILL);
|
||||
}
|
||||
|
||||
ev_break(loop, EVBREAK_ALL);
|
||||
uh_log_info("Normal quit\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [option]\n"
|
||||
" -h docroot # Document root, default is .\n"
|
||||
" -i index_page # Index page, default is index.html\n"
|
||||
" -a addr # address to listen\n"
|
||||
" -s addr # address to listen with ssl\n"
|
||||
" -P # plugin path\n"
|
||||
" -w # worker process number, default is equal to available CPUs\n"
|
||||
" -v # verbose\n", prog);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void start_server(const char *addr, const char *addrs, const char *docroot, const char *index_page, const char *plugin, bool ssl)
|
||||
{
|
||||
struct ev_loop *loop = ev_loop_new(0);
|
||||
struct uh_server *srv = NULL;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
srv = uh_server_new(loop);
|
||||
if (!srv)
|
||||
return;
|
||||
|
||||
if (addr) {
|
||||
if (srv->listen(srv, addrs, false) < 0)
|
||||
return;
|
||||
} else if (addrs) {
|
||||
if (srv->listen(srv, addrs, true) < 0)
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
#if UHTTPD_SSL_SUPPORT
|
||||
if (ssl && srv->ssl_init(srv, "cert.pem", "key.pem") < 0)
|
||||
return;
|
||||
#endif
|
||||
|
||||
srv->set_docroot(srv, docroot);
|
||||
srv->set_index_page(srv, index_page);
|
||||
|
||||
srv->set_default_handler(srv, default_handler);
|
||||
srv->add_path_handler(srv, "/echo", echo_handler);
|
||||
srv->add_path_handler(srv, "/upload", upload_handler);
|
||||
|
||||
if (plugin)
|
||||
srv->load_plugin(srv, plugin);
|
||||
|
||||
ev_run(loop, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct ev_loop *loop = EV_DEFAULT;
|
||||
struct ev_signal signal_watcher;
|
||||
const char *plugin_path = NULL;
|
||||
bool verbose = false;
|
||||
bool ssl = false;
|
||||
const char *docroot = ".";
|
||||
const char *index_page = "index.html";
|
||||
const char *addr = NULL;
|
||||
const char *addrs = NULL;
|
||||
pid_t workers[MAX_WORKER] = {};
|
||||
int nworker = get_nprocs();
|
||||
int opt, i;
|
||||
|
||||
while ((opt = getopt(argc, argv, "h:i:a:s:P:w:v")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
docroot = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
index_page = optarg;
|
||||
break;
|
||||
case 'a':
|
||||
addr = optarg;
|
||||
break;
|
||||
case 's':
|
||||
addrs = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
plugin_path = optarg;
|
||||
break;
|
||||
case 'w':
|
||||
nworker = atoi(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
default: /* '?' */
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
uh_log_threshold(LOG_ERR);
|
||||
|
||||
uh_log_info("libuhttpd version: %s\n", UHTTPD_VERSION_STRING);
|
||||
|
||||
if (!support_so_reuseport()) {
|
||||
uh_log_err("Not support SO_REUSEPORT\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nworker < 1)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < nworker; i++) {
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
uh_log_info("fork: %s\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
start_server(addr, addrs, docroot, index_page, plugin_path, ssl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
workers[i] = pid;
|
||||
}
|
||||
|
||||
ev_signal_init(&signal_watcher, signal_cb, SIGINT);
|
||||
signal_watcher.data = workers;
|
||||
ev_signal_start(loop, &signal_watcher);
|
||||
|
||||
ev_run(loop, 0);
|
||||
|
||||
ev_loop_destroy(loop);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2019 Jianhui Zhao <zhaojh329@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "handler.h"
|
||||
|
||||
static void conn_closed_cb(struct uh_connection *conn)
|
||||
{
|
||||
}
|
||||
|
||||
static void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
|
||||
{
|
||||
if (w->signum == SIGINT) {
|
||||
ev_break(loop, EVBREAK_ALL);
|
||||
uh_log_info("Normal quit\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [option]\n"
|
||||
" -h docroot # Document root, default is .\n"
|
||||
" -i index_page # Index page, default is index.html\n"
|
||||
" -a addr # address to listen\n"
|
||||
" -s addr # address to listen with ssl\n"
|
||||
" -P # plugin path\n"
|
||||
" -v # verbose\n", prog);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct ev_loop *loop = EV_DEFAULT;
|
||||
struct ev_signal signal_watcher;
|
||||
struct uh_server *srv = NULL;
|
||||
const char *plugin_path = NULL;
|
||||
bool verbose = false;
|
||||
const char *docroot = ".";
|
||||
const char *index_page = "index.html";
|
||||
int opt;
|
||||
|
||||
srv = uh_server_new(loop);
|
||||
if (!srv)
|
||||
return -1;
|
||||
|
||||
while ((opt = getopt(argc, argv, "h:i:a:s:P:v")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
docroot = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
index_page = optarg;
|
||||
break;
|
||||
case 'a':
|
||||
if (srv->listen(srv, optarg, false) < 1)
|
||||
goto err;
|
||||
break;
|
||||
case 's':
|
||||
if (srv->listen(srv, optarg, true) < 1)
|
||||
goto err;
|
||||
break;
|
||||
case 'P':
|
||||
plugin_path = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
uh_log_threshold(LOG_ERR);
|
||||
|
||||
uh_log_info("libuhttpd version: %s\n", UHTTPD_VERSION_STRING);
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
#if UHTTPD_SSL_SUPPORT
|
||||
srv->ssl_init(srv, "cert.pem", "key.pem");
|
||||
#endif
|
||||
|
||||
srv->set_docroot(srv, docroot);
|
||||
srv->set_index_page(srv, index_page);
|
||||
|
||||
srv->set_conn_closed_cb(srv, conn_closed_cb);
|
||||
srv->set_default_handler(srv, default_handler);
|
||||
srv->add_path_handler(srv, "/echo", echo_handler);
|
||||
srv->add_path_handler(srv, "/upload", upload_handler);
|
||||
|
||||
if (plugin_path)
|
||||
srv->load_plugin(srv, plugin_path);
|
||||
|
||||
ev_signal_init(&signal_watcher, signal_cb, SIGINT);
|
||||
ev_signal_start(loop, &signal_watcher);
|
||||
|
||||
ev_run(loop, 0);
|
||||
|
||||
err:
|
||||
srv->free(srv);
|
||||
free(srv);
|
||||
|
||||
ev_loop_destroy(loop);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -34,7 +34,7 @@ static void test_handler(struct uh_connection *conn, int event)
|
|||
path = conn->get_path(conn);
|
||||
|
||||
conn->send_head(conn, 200, -1, NULL);
|
||||
conn->chunk_printf(conn, "Path: %.*s\n", path.len, path.p);
|
||||
conn->chunk_printf(conn, "Path: %.*s\n", (int)path.len, path.p);
|
||||
conn->chunk_end(conn);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
openssl req -x509 -newkey rsa:2048 -nodes -keyout server-key.pem -out server-cert.pem -config openssl.cnf
|
||||
openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -subj "/C=CZ/O=Acme Inc./OU=ACME/CN=ACME-DEV-123"
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
[ req ]
|
||||
distinguished_name = req_distinguished_name
|
||||
prompt = no
|
||||
[ req_distinguished_name ]
|
||||
CN = DONT USE - test cert for libuhttp
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
add_definitions(-O -Wall -Werror --std=gnu99 -D_GNU_SOURCE)
|
||||
add_definitions(-O -Wall -Werror --std=gnu99 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64)
|
||||
|
||||
# The version number.
|
||||
set(UHTTPD_VERSION_MAJOR 3)
|
||||
set(UHTTPD_VERSION_MINOR 7)
|
||||
set(UHTTPD_VERSION_PATCH 0)
|
||||
set(UHTTPD_VERSION_MINOR 10)
|
||||
set(UHTTPD_VERSION_PATCH 1)
|
||||
|
||||
# Check the third party Libraries
|
||||
find_package(Libev REQUIRED)
|
||||
|
@ -141,8 +141,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DI
|
|||
install(
|
||||
FILES
|
||||
uhttpd.h
|
||||
connection.h
|
||||
log.h
|
||||
utils.h
|
||||
buffer/buffer.h
|
||||
http-parser/http_parser.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/config.h
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 55e4d3bddc1cd258766448788f8fa3e406e25065
|
||||
Subproject commit 1bd1b6d192eda9e5bc24582e7ef3a4a017f9b064
|
446
src/connection.c
446
src/connection.c
|
@ -30,67 +30,117 @@
|
|||
#include <assert.h>
|
||||
#include <sys/sendfile.h>
|
||||
|
||||
#include "connection.h"
|
||||
#include "uhttpd.h"
|
||||
#include "uhttpd_internal.h"
|
||||
#include "utils.h"
|
||||
#include "file.h"
|
||||
#include "ssl.h"
|
||||
|
||||
static void conn_done(struct uh_connection *conn)
|
||||
{
|
||||
struct ev_loop *loop = conn->srv->loop;
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
struct ev_loop *loop = conni->srv->loop;
|
||||
|
||||
buffer_pull(&conn->rb, NULL, buffer_length(&conn->rb));
|
||||
if (conni->flags & CONN_F_CLOSED)
|
||||
return;
|
||||
|
||||
if (!http_should_keep_alive(&conn->parser))
|
||||
conn->flags |= CONN_F_SEND_AND_CLOSE;
|
||||
if (!http_should_keep_alive(&conni->parser))
|
||||
conni->flags |= CONN_F_SEND_AND_CLOSE;
|
||||
|
||||
if (conn->flags & CONN_F_SEND_AND_CLOSE)
|
||||
ev_io_stop(loop, &conn->ior);
|
||||
if (conni->flags & CONN_F_SEND_AND_CLOSE)
|
||||
ev_io_stop(loop, &conni->ior);
|
||||
|
||||
ev_io_start(loop, &conn->iow);
|
||||
ev_io_start(loop, &conni->iow);
|
||||
|
||||
ev_timer_stop(loop, &conn->timer);
|
||||
ev_timer_stop(loop, &conni->timer);
|
||||
|
||||
/* This is needed for a connection requested multiple times */
|
||||
conn->handler = NULL;
|
||||
/* This is needed for a connection requested multiple times on different path */
|
||||
conni->handler = NULL;
|
||||
}
|
||||
|
||||
static void conn_send(struct uh_connection *conn, const void *data, ssize_t len)
|
||||
{
|
||||
buffer_put_data(&conn->wb, data, len);
|
||||
ev_io_start(conn->srv->loop, &conn->iow);
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
|
||||
if (conni->flags & CONN_F_CLOSED)
|
||||
return;
|
||||
|
||||
buffer_put_data(&conni->wb, data, len);
|
||||
ev_io_start(conni->srv->loop, &conni->iow);
|
||||
}
|
||||
|
||||
static void conn_send_file(struct uh_connection *conn, const char *path)
|
||||
static void conn_send_file(struct uh_connection *conn, const char *path, off_t offset, int64_t len)
|
||||
{
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
size_t min = 8192;
|
||||
struct stat st;
|
||||
int fd;
|
||||
|
||||
conn->file.fd = open(path, O_RDONLY);
|
||||
if (conni->flags & CONN_F_CLOSED)
|
||||
return;
|
||||
|
||||
fstat(conn->file.fd, &st);
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
conn->file.size = st.st_size;
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
uh_log_err("open: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
ev_io_start(conn->srv->loop, &conn->iow);
|
||||
fstat(fd, &st);
|
||||
|
||||
if (offset >= st.st_size) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
lseek(fd, offset, SEEK_SET);
|
||||
st.st_size -= offset;
|
||||
|
||||
if (len < 0 || len > st.st_size)
|
||||
len = st.st_size;
|
||||
|
||||
/* If the file is not greater than 8K, then append it to the HTTP head, send once */
|
||||
if (len <= min) {
|
||||
buffer_put_fd(&conni->wb, fd, len, NULL);
|
||||
close(fd);
|
||||
} else {
|
||||
conni->file.size = len;
|
||||
conni->file.fd = fd;
|
||||
#if UHTTPD_SSL_SUPPORT
|
||||
if (conni->ssl)
|
||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
|
||||
#endif
|
||||
}
|
||||
|
||||
ev_io_start(conni->srv->loop, &conni->iow);
|
||||
}
|
||||
|
||||
static void conn_printf(struct uh_connection *conn, const char *format, ...)
|
||||
{
|
||||
struct buffer *wb = &conn->wb;
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
struct buffer *wb = &conni->wb;
|
||||
va_list arg;
|
||||
|
||||
if (conni->flags & CONN_F_CLOSED)
|
||||
return;
|
||||
|
||||
va_start(arg, format);
|
||||
buffer_put_vprintf(wb, format, arg);
|
||||
va_end(arg);
|
||||
|
||||
ev_io_start(conn->srv->loop, &conn->iow);
|
||||
ev_io_start(conni->srv->loop, &conni->iow);
|
||||
}
|
||||
|
||||
static void conn_vprintf(struct uh_connection *conn, const char *format, va_list arg)
|
||||
{
|
||||
buffer_put_vprintf(&conn->wb, format, arg);
|
||||
ev_io_start(conn->srv->loop, &conn->iow);
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
|
||||
if (conni->flags & CONN_F_CLOSED)
|
||||
return;
|
||||
|
||||
buffer_put_vprintf(&conni->wb, format, arg);
|
||||
ev_io_start(conni->srv->loop, &conni->iow);
|
||||
}
|
||||
|
||||
static void conn_chunk_send(struct uh_connection *conn, const void *data, ssize_t len)
|
||||
|
@ -139,15 +189,17 @@ static void conn_send_status_line(struct uh_connection *conn, int code, const ch
|
|||
conn_send(conn, extra_headers, strlen(extra_headers));
|
||||
}
|
||||
|
||||
static void conn_send_head(struct uh_connection *conn, int code, int content_length, const char *extra_headers)
|
||||
static void conn_send_head(struct uh_connection *conn, int code, int64_t content_length, const char *extra_headers)
|
||||
{
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
|
||||
conn_send_status_line(conn, code, extra_headers);
|
||||
if (content_length < 0)
|
||||
conn_printf(conn, "%s", "Transfer-Encoding: chunked\r\n");
|
||||
else
|
||||
conn_printf(conn, "Content-Length: %d\r\n", content_length);
|
||||
conn_printf(conn, "Content-Length: %" PRIu64 "\r\n", content_length);
|
||||
|
||||
if (!http_should_keep_alive(&conn->parser))
|
||||
if (!http_should_keep_alive(&conni->parser))
|
||||
conn_printf(conn, "%s", "Connection: close\r\n");
|
||||
|
||||
conn_send(conn, "\r\n", 2);
|
||||
|
@ -155,7 +207,9 @@ static void conn_send_head(struct uh_connection *conn, int code, int content_len
|
|||
|
||||
static void conn_error(struct uh_connection *conn, int code, const char *reason)
|
||||
{
|
||||
if (conn->flags & CONN_F_SEND_AND_CLOSE)
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
|
||||
if (conni->flags & CONN_F_SEND_AND_CLOSE)
|
||||
return;
|
||||
|
||||
if (!reason)
|
||||
|
@ -163,14 +217,15 @@ static void conn_error(struct uh_connection *conn, int code, const char *reason)
|
|||
conn_send_head(conn, code, strlen(reason), "Content-Type: text/plain\r\nConnection: close\r\n");
|
||||
conn_send(conn, reason, strlen(reason));
|
||||
|
||||
conn->flags |= CONN_F_SEND_AND_CLOSE;
|
||||
conni->flags |= CONN_F_SEND_AND_CLOSE;
|
||||
|
||||
conn_done(conn);
|
||||
}
|
||||
|
||||
static void conn_redirect(struct uh_connection *conn, int code, const char *location, ...)
|
||||
{
|
||||
struct buffer *wb = &conn->wb;
|
||||
struct uh_connection_internal *conni = (struct uh_connection_internal *)conn;
|
||||
struct buffer *wb = &conni->wb;
|
||||
va_list arg;
|
||||
|
||||
assert((code == HTTP_STATUS_MOVED_PERMANENTLY || code == HTTP_STATUS_FOUND) && location);
|
||||
|
@ -191,17 +246,23 @@ static void conn_redirect(struct uh_connection *conn, int code, const char *loca
|
|||
|
||||
static const struct sockaddr *conn_get_addr(struct uh_connection *conn)
|
||||
{
|
||||
ret |