Add:FxHttpServer module.
This commit is contained in:
parent
8a66f07266
commit
24de40d2f6
|
@ -1,9 +1,9 @@
|
||||||
# **关于使用git爬取、上传文件,以及创建本地分支的方法总结**
|
# 1. **关于使用git爬取、上传文件,以及创建本地分支的方法总结**
|
||||||
|
|
||||||
## 背景
|
## 1.1. 背景
|
||||||
为了使一个有多人的开发团队协作的项目拥有统一的规范管理,不至于杂乱无章,一个可用、好用的代码管理工具的引入是必要的。目前为止,笔者所接触到较多的两大代码管理工具分别是:gitee和github,本篇规范着重介绍gitee。与任何工具一样,相使用好gitee就必然需要按照相对应的方法和步骤。本规范用于介绍在gitee上进行文件获取、上传,以及本地分支的创建和提交的方法,以及以上步骤遇到问题时的解决方法。
|
为了使一个有多人的开发团队协作的项目拥有统一的规范管理,不至于杂乱无章,一个可用、好用的代码管理工具的引入是必要的。目前为止,笔者所接触到较多的两大代码管理工具分别是:gitee和github,本篇规范着重介绍gitee。与任何工具一样,相使用好gitee就必然需要按照相对应的方法和步骤。本规范用于介绍在gitee上进行文件获取、上传,以及本地分支的创建和提交的方法,以及以上步骤遇到问题时的解决方法。
|
||||||
|
|
||||||
## 操作方法/步骤及其解析
|
## 1.2. 操作方法/步骤及其解析
|
||||||
|
|
||||||
* 爬取文件:git clone (所爬取的地址)
|
* 爬取文件:git clone (所爬取的地址)
|
||||||
例如:git clone https://gitee.com/xxxxxx/git-test.git
|
例如:git clone https://gitee.com/xxxxxx/git-test.git
|
||||||
|
@ -40,13 +40,4 @@ remote: Total 13 (delta 0), reused 0 (delta 0), pack-reused 0
|
||||||
> git commit -m "fenzhi test.("本次提交内容的行为的备注信息,在一切上传行为中,该步骤都不可省略)
|
> git commit -m "fenzhi test.("本次提交内容的行为的备注信息,在一切上传行为中,该步骤都不可省略)
|
||||||
> git pull origin KAIFA(将服务器中的最新内容下拉,与本地的内容进行比较,此处的KAIFA可以替换为读者希望下拉到的分支;请注意,直至下一步之前,该步骤是上传前最后的纠错机会,请务必确保无误后再操作下一步)
|
> git pull origin KAIFA(将服务器中的最新内容下拉,与本地的内容进行比较,此处的KAIFA可以替换为读者希望下拉到的分支;请注意,直至下一步之前,该步骤是上传前最后的纠错机会,请务必确保无误后再操作下一步)
|
||||||
> git push origin xxxxxx:KAIFA(将缓存区的内容上传至服务器,此处的KAIFA可以替换为读者希望上传到的分支,xxxxx为操作者新创建的本地分区)
|
> git push origin xxxxxx:KAIFA(将缓存区的内容上传至服务器,此处的KAIFA可以替换为读者希望上传到的分支,xxxxx为操作者新创建的本地分区)
|
||||||
> git log(该步骤可以将操作者所进行过的步骤以log的形式列出来,以便操作者找到关键的信息,该步骤建议无论读者完成任何大型操作之后都进行一次,以便检查自己的操作有无失误)
|
> git log(该步骤可以将操作者所进行过的步骤以log的形式列出来,以便操作者找到关键的信息,该步骤建议无论读者完成任何大型操作之后都进行一次,以便检查自己的操作有无失误)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
89
doc/vscode_ssh_guide.md
Normal file
89
doc/vscode_ssh_guide.md
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
# 1. vscode使用ssh链接虚拟机服务器
|
||||||
|
|
||||||
|
# 2. 前言
|
||||||
|
|
||||||
|
   在vscode使用ssh工具远程登录虚拟机服务器进行代码编辑。
|
||||||
|
|
||||||
|
| 内容 | 时间 | 作者 | 备注 |
|
||||||
|
|----|----|----|----|
|
||||||
|
| 首版 | 2024-2-26 | xjz | - |
|
||||||
|
|
||||||
|
## 2.1. Windows系统
|
||||||
|
|
||||||
|
* 安装ssh
|
||||||
|
|
||||||
|
   直接安装git工具即可支持ssh
|
||||||
|
|
||||||
|
安装完后确认:
|
||||||
|
```
|
||||||
|
xiaojiazhu@ubuntu:~/project/rkipc/battery/ipc-rk1106/ipc-sdk/cmake-shell$ ssh
|
||||||
|
usage: ssh [-46AaCfGgKkMNnqsTtVvXxYy] [-B bind_interface]
|
||||||
|
[-b bind_address] [-c cipher_spec] [-D [bind_address:]port]
|
||||||
|
[-E log_file] [-e escape_char] [-F configfile] [-I pkcs11]
|
||||||
|
[-i identity_file] [-J [user@]host[:port]] [-L address]
|
||||||
|
[-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
|
||||||
|
[-Q query_option] [-R address] [-S ctl_path] [-W host:port]
|
||||||
|
[-w local_tun[:remote_tun]] destination [command]
|
||||||
|
```
|
||||||
|
|
||||||
|
* vscode安装ssh插件
|
||||||
|
|
||||||
|
   使用 Remote - SSH 插件
|
||||||
|
|
||||||
|
* ssh密钥配置
|
||||||
|
|
||||||
|
1. Windows生成密钥;
|
||||||
|
2. 把xxx.pub文件内容拷贝到虚拟机的ssh目录下的authorized_keys文件;
|
||||||
|
此处是:
|
||||||
|
|
||||||
|
```
|
||||||
|
xiaojiazhu@ubuntu:~/project/.ssh$ pwd
|
||||||
|
/home/xiaojiazhu/project/.ssh
|
||||||
|
xiaojiazhu@ubuntu:~/project/.ssh$ ls
|
||||||
|
authorized_keys id_rsa id_rsa.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
这样设置后,每次登录ssh无需手动输入密码;
|
||||||
|
|
||||||
|
3. 在Windows远程登录虚拟机:
|
||||||
|
|
||||||
|
参考命令:ssh xiaojiazhu@192.168.1.29
|
||||||
|
|
||||||
|
```
|
||||||
|
PS C:\Users\xjz\.ssh> ssh xiaojiazhu@192.168.1.29
|
||||||
|
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.15.0-94-generic x86_64)
|
||||||
|
|
||||||
|
* Documentation: https://help.ubuntu.com
|
||||||
|
* Management: https://landscape.canonical.com
|
||||||
|
* Support: https://ubuntu.com/advantage
|
||||||
|
|
||||||
|
Expanded Security Maintenance for Applications is not enabled.
|
||||||
|
|
||||||
|
75 updates can be applied immediately.
|
||||||
|
To see these additional updates run: apt list --upgradable
|
||||||
|
|
||||||
|
9 additional security updates can be applied with ESM Apps.
|
||||||
|
Learn more about enabling ESM Apps service at https://ubuntu.com/esm
|
||||||
|
|
||||||
|
New release '22.04.3 LTS' available.
|
||||||
|
Run 'do-release-upgrade' to upgrade to it.
|
||||||
|
|
||||||
|
Your Hardware Enablement Stack (HWE) is supported until April 2025.
|
||||||
|
*** System restart required ***
|
||||||
|
Last login: Sun Feb 25 17:20:04 2024 from 192.168.1.29
|
||||||
|
xiaojiazhu@ubuntu:~$
|
||||||
|
```
|
||||||
|
## 2.2. vscode设置
|
||||||
|
|
||||||
|
配置文件参考
|
||||||
|
|
||||||
|
```
|
||||||
|
Host dgiot // 自定义的主机名
|
||||||
|
HostName 192.168.1.29 // 远端的IP地址
|
||||||
|
User xiaojiazhu 用户名
|
||||||
|
Port 22
|
||||||
|
IdentityFile "C:\Users\xjz\.ssh\id_ed25519" // 本端的私钥文件路径
|
||||||
|
ForwardAgent yes // 忽略
|
||||||
|
```
|
||||||
|
|
||||||
|
多个远端IP复制即可。
|
3
external/CMakeLists.txt
vendored
3
external/CMakeLists.txt
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
add_subdirectory(sqlite3/sqlite-3430000)
|
add_subdirectory(sqlite3/sqlite-3430000)
|
||||||
add_subdirectory(goahead-5.2.0)
|
add_subdirectory(goahead-5.2.0)
|
||||||
|
add_subdirectory(httpserver.h-master/src)
|
16
external/httpserver.h-master/.gitignore
vendored
Normal file
16
external/httpserver.h-master/.gitignore
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
http-server
|
||||||
|
valgrind-log.txt
|
||||||
|
test-results.txt
|
||||||
|
valgrind-results.txt
|
||||||
|
*.dSYM/
|
||||||
|
http-server-cpp
|
||||||
|
test-results-cpp.txt
|
||||||
|
http-server-unit
|
||||||
|
a.out
|
||||||
|
test/main.cpp
|
||||||
|
src/parser.c
|
||||||
|
hs-integration
|
||||||
|
html
|
||||||
|
latex
|
||||||
|
build
|
||||||
|
.cache
|
14
external/httpserver.h-master/CMakeLists.txt
vendored
Normal file
14
external/httpserver.h-master/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
# set the project name
|
||||||
|
project(httpserver.h)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
add_subdirectory(test/unit)
|
||||||
|
add_subdirectory(test/functional)
|
||||||
|
|
||||||
|
add_test(NAME Unit COMMAND httpsrv-unit)
|
||||||
|
|
2658
external/httpserver.h-master/Doxyfile
vendored
Normal file
2658
external/httpserver.h-master/Doxyfile
vendored
Normal file
File diff suppressed because it is too large
Load Diff
21
external/httpserver.h-master/LICENSE
vendored
Normal file
21
external/httpserver.h-master/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Jeremy Williams
|
||||||
|
|
||||||
|
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.
|
30
external/httpserver.h-master/Makefile
vendored
Normal file
30
external/httpserver.h-master/Makefile
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
.PHONY: test clean format check-format debug
|
||||||
|
|
||||||
|
test: test-unit test-functional test-functional-cpp
|
||||||
|
|
||||||
|
test-unit: debug
|
||||||
|
./build/test/unit/unit-test-runner
|
||||||
|
|
||||||
|
test-functional: debug
|
||||||
|
./test/functional/functional-test-runner
|
||||||
|
|
||||||
|
test-functional-cpp: debug
|
||||||
|
./test/functional/functional-test-runner -cpp
|
||||||
|
|
||||||
|
debug: build
|
||||||
|
cd build; \
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=$(shell pwd)/iwyu.imp" ..; \
|
||||||
|
make; \
|
||||||
|
cd ..;
|
||||||
|
|
||||||
|
build:
|
||||||
|
mkdir build
|
||||||
|
|
||||||
|
format:
|
||||||
|
find src -name "*.[h|c]" -exec sh -c 'clang-format --style=LLVM $$0 > $$0.frmt; mv $$0.frmt $$0' {} \;
|
||||||
|
|
||||||
|
check-format:
|
||||||
|
clang-format --style=LLVM --dry-run -Werror src/*.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -rf build
|
222
external/httpserver.h-master/README.md
vendored
Normal file
222
external/httpserver.h-master/README.md
vendored
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
[](https://travis-ci.com/jeremycw/httpserver.h)
|
||||||
|
|
||||||
|
See `httpserver.h` for API documentation
|
||||||
|
|
||||||
|
# Description
|
||||||
|
|
||||||
|
httpserver.h is a single header C library for building event driven non-blocking HTTP servers
|
||||||
|
|
||||||
|
Supports Linux with epoll and BSD/Mac with kqueue.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define HTTPSERVER_IMPL
|
||||||
|
#include "httpserver.h"
|
||||||
|
|
||||||
|
#define RESPONSE "Hello, World!"
|
||||||
|
|
||||||
|
void handle_request(struct http_request_s* request) {
|
||||||
|
struct http_response_s* response = http_response_init();
|
||||||
|
http_response_status(response, 200);
|
||||||
|
http_response_header(response, "Content-Type", "text/plain");
|
||||||
|
http_response_body(response, RESPONSE, sizeof(RESPONSE) - 1);
|
||||||
|
http_respond(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
struct http_server_s* server = http_server_init(8080, handle_request);
|
||||||
|
http_server_listen(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
See full documentation in `httpserver.h`
|
||||||
|
|
||||||
|
# Benchmark
|
||||||
|
|
||||||
|
I ran some micro-benchmarks with httpserver.h and NGINX serving a simple Hello, World!
|
||||||
|
The purpose here was just to get a performance reference point, not to try and make
|
||||||
|
any statement of superiority since httpserver.h is not a competitor or replacement for
|
||||||
|
NGINX.
|
||||||
|
|
||||||
|
## With `keep-alive`
|
||||||
|
`ab -k -c 200 -n 100000 http://localhost:8080/`
|
||||||
|
|
||||||
|
### NGINX 74441.26 requests/sec
|
||||||
|
```
|
||||||
|
Server Software: nginx/1.14.0
|
||||||
|
Server Hostname: localhost
|
||||||
|
Server Port: 8000
|
||||||
|
|
||||||
|
Document Path: /
|
||||||
|
Document Length: 13 bytes
|
||||||
|
|
||||||
|
Concurrency Level: 200
|
||||||
|
Time taken for tests: 1.343 seconds
|
||||||
|
Complete requests: 100000
|
||||||
|
Failed requests: 0
|
||||||
|
Keep-Alive requests: 99052
|
||||||
|
Total transferred: 20995260 bytes
|
||||||
|
HTML transferred: 1300000 bytes
|
||||||
|
Requests per second: 74441.26 [#/sec] (mean)
|
||||||
|
Time per request: 2.687 [ms] (mean)
|
||||||
|
Time per request: 0.013 [ms] (mean, across all concurrent requests)
|
||||||
|
Transfer rate: 15262.83 [Kbytes/sec] received
|
||||||
|
|
||||||
|
Connection Times (ms)
|
||||||
|
min mean[+/-sd] median max
|
||||||
|
Connect: 0 0 17.8 0 1029
|
||||||
|
Processing: 0 2 8.8 1 148
|
||||||
|
Waiting: 0 2 8.8 1 148
|
||||||
|
Total: 0 3 21.4 1 1150
|
||||||
|
|
||||||
|
Percentage of the requests served within a certain time (ms)
|
||||||
|
50% 1
|
||||||
|
66% 2
|
||||||
|
75% 2
|
||||||
|
80% 2
|
||||||
|
90% 2
|
||||||
|
95% 2
|
||||||
|
98% 3
|
||||||
|
99% 35
|
||||||
|
100% 1150 (longest request)
|
||||||
|
```
|
||||||
|
|
||||||
|
### httpserver.h 123907.91 requests/sec
|
||||||
|
```
|
||||||
|
Server Software:
|
||||||
|
Server Hostname: localhost
|
||||||
|
Server Port: 8080
|
||||||
|
|
||||||
|
Document Path: /
|
||||||
|
Document Length: 13 bytes
|
||||||
|
|
||||||
|
Concurrency Level: 200
|
||||||
|
Time taken for tests: 0.807 seconds
|
||||||
|
Complete requests: 100000
|
||||||
|
Failed requests: 0
|
||||||
|
Keep-Alive requests: 100000
|
||||||
|
Total transferred: 13400000 bytes
|
||||||
|
HTML transferred: 1300000 bytes
|
||||||
|
Requests per second: 123907.91 [#/sec] (mean)
|
||||||
|
Time per request: 1.614 [ms] (mean)
|
||||||
|
Time per request: 0.008 [ms] (mean, across all concurrent requests)
|
||||||
|
Transfer rate: 16214.51 [Kbytes/sec] received
|
||||||
|
|
||||||
|
Connection Times (ms)
|
||||||
|
min mean[+/-sd] median max
|
||||||
|
Connect: 0 0 0.1 0 4
|
||||||
|
Processing: 1 2 0.3 2 6
|
||||||
|
Waiting: 1 2 0.3 2 6
|
||||||
|
Total: 1 2 0.4 2 6
|
||||||
|
|
||||||
|
Percentage of the requests served within a certain time (ms)
|
||||||
|
50% 2
|
||||||
|
66% 2
|
||||||
|
75% 2
|
||||||
|
80% 2
|
||||||
|
90% 2
|
||||||
|
95% 2
|
||||||
|
98% 3
|
||||||
|
99% 3
|
||||||
|
100% 6 (longest request)
|
||||||
|
```
|
||||||
|
|
||||||
|
## With `Connection: close`
|
||||||
|
`ab -c 200 -n 100000 http://localhost:8080/`
|
||||||
|
|
||||||
|
### NGINX 15773.47 requests/sec
|
||||||
|
```
|
||||||
|
Server Software: nginx/1.14.0
|
||||||
|
Server Hostname: localhost
|
||||||
|
Server Port: 8000
|
||||||
|
|
||||||
|
Document Path: /
|
||||||
|
Document Length: 13 bytes
|
||||||
|
|
||||||
|
Concurrency Level: 200
|
||||||
|
Time taken for tests: 6.340 seconds
|
||||||
|
Complete requests: 100000
|
||||||
|
Failed requests: 0
|
||||||
|
Total transferred: 20500000 bytes
|
||||||
|
HTML transferred: 1300000 bytes
|
||||||
|
Requests per second: 15773.47 [#/sec] (mean)
|
||||||
|
Time per request: 12.680 [ms] (mean)
|
||||||
|
Time per request: 0.063 [ms] (mean, across all concurrent requests)
|
||||||
|
Transfer rate: 3157.77 [Kbytes/sec] received
|
||||||
|
|
||||||
|
Connection Times (ms)
|
||||||
|
min mean[+/-sd] median max
|
||||||
|
Connect: 0 4 40.6 3 1123
|
||||||
|
Processing: 1 8 46.7 4 508
|
||||||
|
Waiting: 1 7 46.7 3 508
|
||||||
|
Total: 3 12 64.5 6 1460
|
||||||
|
|
||||||
|
Percentage of the requests served within a certain time (ms)
|
||||||
|
50% 6
|
||||||
|
66% 7
|
||||||
|
75% 7
|
||||||
|
80% 8
|
||||||
|
90% 8
|
||||||
|
95% 9
|
||||||
|
98% 10
|
||||||
|
99% 503
|
||||||
|
100% 1460 (longest request)
|
||||||
|
```
|
||||||
|
|
||||||
|
### httpserver.h 27605.45 requests/sec
|
||||||
|
```
|
||||||
|
Server Software:
|
||||||
|
Server Hostname: localhost
|
||||||
|
Server Port: 8080
|
||||||
|
|
||||||
|
Document Path: /
|
||||||
|
Document Length: 13 bytes
|
||||||
|
|
||||||
|
Concurrency Level: 200
|
||||||
|
Time taken for tests: 3.622 seconds
|
||||||
|
Complete requests: 100000
|
||||||
|
Failed requests: 0
|
||||||
|
Total transferred: 12900000 bytes
|
||||||
|
HTML transferred: 1300000 bytes
|
||||||
|
Requests per second: 27605.45 [#/sec] (mean)
|
||||||
|
Time per request: 7.245 [ms] (mean)
|
||||||
|
Time per request: 0.036 [ms] (mean, across all concurrent requests)
|
||||||
|
Transfer rate: 3477.64 [Kbytes/sec] received
|
||||||
|
|
||||||
|
Connection Times (ms)
|
||||||
|
min mean[+/-sd] median max
|
||||||
|
Connect: 2 3 0.5 3 6
|
||||||
|
Processing: 1 4 0.7 4 8
|
||||||
|
Waiting: 1 3 0.8 3 7
|
||||||
|
Total: 4 7 0.6 7 11
|
||||||
|
|
||||||
|
Percentage of the requests served within a certain time (ms)
|
||||||
|
50% 7
|
||||||
|
66% 7
|
||||||
|
75% 8
|
||||||
|
80% 8
|
||||||
|
90% 8
|
||||||
|
95% 8
|
||||||
|
98% 9
|
||||||
|
99% 9
|
||||||
|
100% 11 (longest request)
|
||||||
|
```
|
||||||
|
|
||||||
|
### NGINX conf
|
||||||
|
|
||||||
|
```
|
||||||
|
worker_processes 1;
|
||||||
|
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 8000;
|
||||||
|
location / {
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
return 200 'Hello, World!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
77
external/httpserver.h-master/compile_commands.json
vendored
Normal file
77
external/httpserver.h-master/compile_commands.json
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/src",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -I/Users/jeremywilliams/code/httpserver.h/build/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -Wall -Wextra -Werror -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -o CMakeFiles/evhttpserv.dir/api.c.o -c /Users/jeremywilliams/code/httpserver.h/src/api.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/src/api.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/src",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -I/Users/jeremywilliams/code/httpserver.h/build/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -Wall -Wextra -Werror -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -o CMakeFiles/evhttpserv.dir/connection.c.o -c /Users/jeremywilliams/code/httpserver.h/src/connection.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/src/connection.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/src",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -I/Users/jeremywilliams/code/httpserver.h/build/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -Wall -Wextra -Werror -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -o CMakeFiles/evhttpserv.dir/io_events.c.o -c /Users/jeremywilliams/code/httpserver.h/src/io_events.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/src/io_events.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/src",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -I/Users/jeremywilliams/code/httpserver.h/build/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -Wall -Wextra -Werror -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -Wno-unused-variable -o CMakeFiles/evhttpserv.dir/parser.c.o -c /Users/jeremywilliams/code/httpserver.h/build/src/parser.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/build/src/parser.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/src",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -I/Users/jeremywilliams/code/httpserver.h/build/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -Wall -Wextra -Werror -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -o CMakeFiles/evhttpserv.dir/read_socket.c.o -c /Users/jeremywilliams/code/httpserver.h/src/read_socket.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/src/read_socket.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/src",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -I/Users/jeremywilliams/code/httpserver.h/build/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -Wall -Wextra -Werror -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -o CMakeFiles/evhttpserv.dir/request_util.c.o -c /Users/jeremywilliams/code/httpserver.h/src/request_util.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/src/request_util.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/src",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -I/Users/jeremywilliams/code/httpserver.h/build/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -Wall -Wextra -Werror -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -o CMakeFiles/evhttpserv.dir/respond.c.o -c /Users/jeremywilliams/code/httpserver.h/src/respond.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/src/respond.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/src",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -I/Users/jeremywilliams/code/httpserver.h/build/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -Wall -Wextra -Werror -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -o CMakeFiles/evhttpserv.dir/server.c.o -c /Users/jeremywilliams/code/httpserver.h/src/server.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/src/server.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/src",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -I/Users/jeremywilliams/code/httpserver.h/build/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -Wall -Wextra -Werror -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all -o CMakeFiles/evhttpserv.dir/write_socket.c.o -c /Users/jeremywilliams/code/httpserver.h/src/write_socket.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/src/write_socket.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/test/unit",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -o CMakeFiles/evhs-unit.dir/munit.c.o -c /Users/jeremywilliams/code/httpserver.h/test/unit/munit.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/test/unit/munit.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/test/unit",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -o CMakeFiles/evhs-unit.dir/test_parser.c.o -c /Users/jeremywilliams/code/httpserver.h/test/unit/test_parser.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/test/unit/test_parser.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/test/unit",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -o CMakeFiles/evhs-unit.dir/test_read_socket.c.o -c /Users/jeremywilliams/code/httpserver.h/test/unit/test_read_socket.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/test/unit/test_read_socket.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/test/unit",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -o CMakeFiles/evhs-unit.dir/test_write_socket.c.o -c /Users/jeremywilliams/code/httpserver.h/test/unit/test_write_socket.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/test/unit/test_write_socket.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/test/unit",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -o CMakeFiles/evhs-unit.dir/main.c.o -c /Users/jeremywilliams/code/httpserver.h/test/unit/main.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/test/unit/main.c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"directory": "/Users/jeremywilliams/code/httpserver.h/build/test/integration",
|
||||||
|
"command": "/Library/Developer/CommandLineTools/usr/bin/cc -DKQUEUE -I/Users/jeremywilliams/code/httpserver.h/src -g -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -o CMakeFiles/evhs-int.dir/main.c.o -c /Users/jeremywilliams/code/httpserver.h/test/integration/main.c",
|
||||||
|
"file": "/Users/jeremywilliams/code/httpserver.h/test/integration/main.c"
|
||||||
|
}
|
||||||
|
]
|
2818
external/httpserver.h-master/httpserver.h
vendored
Normal file
2818
external/httpserver.h-master/httpserver.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
external/httpserver.h-master/iwyu.imp
vendored
Normal file
6
external/httpserver.h-master/iwyu.imp
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[
|
||||||
|
{ include: ["<bits/types/struct_itimerspec.h>", "private", "<time.h>", "public"] },
|
||||||
|
{ include: ["<sys/fcntl.h>", "private", "<fcntl.h>", "public"] },
|
||||||
|
{ include: ["<sys/signal.h>", "private", "<signal.h>", "public"] },
|
||||||
|
{ include: ["<sys/errno.h>", "private", "<errno.h>", "public"] }
|
||||||
|
]
|
77
external/httpserver.h-master/src/CMakeLists.txt
vendored
Normal file
77
external/httpserver.h-master/src/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
add_library(
|
||||||
|
httpsrv
|
||||||
|
api.c connection.c io_events.c ${CMAKE_CURRENT_BINARY_DIR}/parser.c
|
||||||
|
read_socket.c request_util.c respond.c server.c write_socket.c
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET httpsrv PROPERTY C_STANDARD 99)
|
||||||
|
|
||||||
|
target_compile_options(
|
||||||
|
httpsrv
|
||||||
|
PRIVATE -Wall -Wextra -Werror
|
||||||
|
# $<$<CONFIG:DEBUG>:-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all>
|
||||||
|
)
|
||||||
|
|
||||||
|
# target_link_options(
|
||||||
|
# httpsrv
|
||||||
|
# PUBLIC $<$<CONFIG:DEBUG>:-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all>
|
||||||
|
# )
|
||||||
|
|
||||||
|
set_source_files_properties(
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/parser.c
|
||||||
|
PROPERTIES COMPILE_FLAGS -Wno-unused-variable
|
||||||
|
)
|
||||||
|
|
||||||
|
find_program(M4 m4)
|
||||||
|
if(NOT M4)
|
||||||
|
message(FATAL_ERROR "m4 not found. Please install before continuing.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_target(
|
||||||
|
httpserver.h
|
||||||
|
ALL
|
||||||
|
COMMAND ${M4} -I${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/httpserver.m4 > ${CMAKE_CURRENT_BINARY_DIR}/httpserver.h
|
||||||
|
DEPENDS api.h api.c buffer_util.h common.h connection.h connection.c
|
||||||
|
io_events.h io_events.c parser.h ${CMAKE_CURRENT_BINARY_DIR}/parser.c
|
||||||
|
read_socket.h read_socket.c request_util.h request_util.c respond.h respond.c
|
||||||
|
server.h server.c write_socket.h write_socket.c httpserver.m4
|
||||||
|
)
|
||||||
|
|
||||||
|
find_program(RAGEL ragel)
|
||||||
|
if(NOT RAGEL)
|
||||||
|
message(FATAL_ERROR "ragel not found. Please install before continuing.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/parser.c
|
||||||
|
COMMAND ${RAGEL} ${CMAKE_CURRENT_SOURCE_DIR}/parser.rl -o ${CMAKE_CURRENT_BINARY_DIR}/parser.c
|
||||||
|
DEPENDS parser.rl
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
httpsrv
|
||||||
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
include(CheckFunctionExists)
|
||||||
|
check_function_exists(epoll_wait EPOLL)
|
||||||
|
check_function_exists(kqueue KQUEUE)
|
||||||
|
|
||||||
|
# target_compile_definitions(httpsrv PRIVATE $<$<CONFIG:DEBUG>:DEBUG>)
|
||||||
|
|
||||||
|
if(KQUEUE)
|
||||||
|
target_compile_definitions(httpsrv PRIVATE KQUEUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(EPOLL)
|
||||||
|
target_compile_definitions(httpsrv PRIVATE EPOLL)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message("${PLATFORM_PATH}/cmake-shell/external${SUBMODULE_PATH_OF_IPC_SDK}/httpserver.h-master/src/libhttpsrv.a")
|
||||||
|
add_custom_command(
|
||||||
|
TARGET httpsrv
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND cp ${PLATFORM_PATH}/cmake-shell${SUBMODULE_PATH_OF_IPC_SDK}/external/httpserver.h-master/src/libhttpsrv.a ${EXTERNAL_LIBS_OUTPUT_PATH}
|
||||||
|
WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/
|
||||||
|
)
|
||||||
|
|
132
external/httpserver.h-master/src/api.c
vendored
Normal file
132
external/httpserver.h-master/src/api.c
vendored
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "api.h"
|
||||||
|
#include "buffer_util.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "io_events.h"
|
||||||
|
#include "request_util.h"
|
||||||
|
#include "respond.h"
|
||||||
|
#include "server.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int http_request_has_flag(http_request_t *request, int flag) {
|
||||||
|
return HTTP_FLAG_CHECK(request->flags, flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
int http_server_loop(http_server_t *server) { return server->loop; }
|
||||||
|
|
||||||
|
http_server_t *http_server_init(int port, void (*handler)(http_request_t *)) {
|
||||||
|
#ifdef KQUEUE
|
||||||
|
return hs_server_init(port, handler, hs_on_kqueue_server_event, NULL);
|
||||||
|
#else
|
||||||
|
return hs_server_init(port, handler, hs_on_epoll_server_connection_event,
|
||||||
|
hs_on_epoll_server_timer_event);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_request_free_buffer(http_request_t *request) {
|
||||||
|
_hs_buffer_free(&request->buffer, &request->server->memused);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *http_request_userdata(http_request_t *request) { return request->data; }
|
||||||
|
|
||||||
|
void http_request_set_userdata(http_request_t *request, void *data) {
|
||||||
|
request->data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_server_set_userdata(struct http_server_s *serv, void *data) {
|
||||||
|
serv->data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *http_request_server_userdata(struct http_request_s *request) {
|
||||||
|
return request->server->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int http_request_iterate_headers(http_request_t *request, http_string_t *key,
|
||||||
|
http_string_t *val, int *iter) {
|
||||||
|
return hs_request_iterate_headers(request, key, val, iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
http_string_t http_request_header(http_request_t *request, char const *key) {
|
||||||
|
return hs_request_header(request, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_request_connection(http_request_t *request, int directive) {
|
||||||
|
hs_request_set_keep_alive_flag(request, directive);
|
||||||
|
}
|
||||||
|
|
||||||
|
http_string_t http_request_chunk(struct http_request_s *request) {
|
||||||
|
return hs_request_chunk(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_t *http_response_init() { return hs_response_init(); }
|
||||||
|
|
||||||
|
void http_response_header(http_response_t *response, char const *key,
|
||||||
|
char const *value) {
|
||||||
|
return hs_response_set_header(response, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_response_status(http_response_t *response, int status) {
|
||||||
|
hs_response_set_status(response, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_response_body(http_response_t *response, char const *body,
|
||||||
|
int length) {
|
||||||
|
hs_response_set_body(response, body, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_respond(http_request_t *request, http_response_t *response) {
|
||||||
|
hs_request_respond(request, response, hs_request_begin_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_respond_chunk(http_request_t *request, http_response_t *response,
|
||||||
|
void (*cb)(http_request_t *)) {
|
||||||
|
hs_request_respond_chunk(request, response, cb, hs_request_begin_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_respond_chunk_end(http_request_t *request,
|
||||||
|
http_response_t *response) {
|
||||||
|
hs_request_respond_chunk_end(request, response, hs_request_begin_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
http_string_t http_request_method(http_request_t *request) {
|
||||||
|
return hs_get_token_string(request, HSH_TOK_METHOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
http_string_t http_request_target(http_request_t *request) {
|
||||||
|
return hs_get_token_string(request, HSH_TOK_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
http_string_t http_request_body(http_request_t *request) {
|
||||||
|
return hs_get_token_string(request, HSH_TOK_BODY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int http_server_listen(http_server_t *serv) {
|
||||||
|
return hs_server_run_event_loop(serv, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int http_server_listen_addr(http_server_t *serv, const char *ipaddr) {
|
||||||
|
return hs_server_run_event_loop(serv, ipaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int http_server_poll(http_server_t *serv) {
|
||||||
|
return hs_server_poll_events(serv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int http_server_listen_poll(http_server_t *serv) {
|
||||||
|
hs_server_listen_on_addr(serv, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int http_server_listen_addr_poll(http_server_t *serv, const char *ipaddr) {
|
||||||
|
hs_server_listen_on_addr(serv, ipaddr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_request_read_chunk(struct http_request_s *request,
|
||||||
|
void (*chunk_cb)(struct http_request_s *)) {
|
||||||
|
request->state = HTTP_SESSION_READ;
|
||||||
|
request->chunk_cb = chunk_cb;
|
||||||
|
hs_request_begin_read(request);
|
||||||
|
}
|
489
external/httpserver.h-master/src/api.h
vendored
Normal file
489
external/httpserver.h-master/src/api.h
vendored
Normal file
|
@ -0,0 +1,489 @@
|
||||||
|
#ifndef HS_API_H
|
||||||
|
#define HS_API_H
|
||||||
|
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* @file api.h
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Jeremy Williams
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* httpserver.h (0.9.0)
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
*
|
||||||
|
* A single header C library for building non-blocking event driven HTTP
|
||||||
|
* servers
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* Do this:
|
||||||
|
* #define HTTPSERVER_IMPL
|
||||||
|
* before you include this file in *one* C or C++ file to create the
|
||||||
|
* implementation.
|
||||||
|
*
|
||||||
|
* // i.e. it should look like this:
|
||||||
|
* #include ...
|
||||||
|
* #include ...
|
||||||
|
* #include ...
|
||||||
|
* #define HTTPSERVER_IMPL
|
||||||
|
* #include "httpserver.h"
|
||||||
|
*
|
||||||
|
* There are some #defines that can be configured. This must be done in the
|
||||||
|
* same file that you define HTTPSERVER_IMPL These defines have default values
|
||||||
|
* and will need to be #undef'd and redefined to configure them.
|
||||||
|
*
|
||||||
|
* HTTP_REQUEST_BUF_SIZE - default 1024 - The initial size in bytes of the
|
||||||
|
* read buffer for the request. This buffer grows automatically if it's
|
||||||
|
* capacity is reached but it certain environments it may be optimal to
|
||||||
|
* change this value.
|
||||||
|
*
|
||||||
|
* HTTP_RESPONSE_BUF_SIZE - default 1024 - Same as above except for the
|
||||||
|
* response buffer.
|
||||||
|
*
|
||||||
|
* HTTP_REQUEST_TIMEOUT - default 20 - The amount of seconds the request
|
||||||
|
* will wait for activity on the socket before closing. This only applies mid
|
||||||
|
* request. For the amount of time to hold onto keep-alive connections see
|
||||||
|
* below.
|
||||||
|
*
|
||||||
|
* HTTP_KEEP_ALIVE_TIMEOUT - default 120 - The amount of seconds to keep a
|
||||||
|
* connection alive a keep-alive request has completed.
|
||||||
|
*
|
||||||
|
* HTTP_MAX_TOTAL_EST_MEM_USAGE - default 4294967296 (4GB) - This is the
|
||||||
|
* amount of read/write buffer space that is allowed to be allocated
|
||||||
|
* across all requests before new requests will get 503 responses.
|
||||||
|
*
|
||||||
|
* HTTP_MAX_TOKEN_LENGTH - default 8192 (8KB) - This is the max size of any
|
||||||
|
* non body http tokens. i.e: header names, header values, url length,
|
||||||
|
* etc.
|
||||||
|
*
|
||||||
|
* HTTP_MAX_REQUEST_BUF_SIZE - default 8388608 (8MB) - This is the maximum
|
||||||
|
* amount of bytes that the request buffer will grow to. If the body of
|
||||||
|
* the request + headers cannot fit in this size the request body will be
|
||||||
|
* streamed in.
|
||||||
|
*
|
||||||
|
* For more details see the documentation of the interface and the example
|
||||||
|
* below.
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// String type used to read the request details. The char pointer is NOT null
|
||||||
|
// terminated.
|
||||||
|
struct http_string_s;
|
||||||
|
|
||||||
|
struct http_server_s;
|
||||||
|
struct http_request_s;
|
||||||
|
struct http_response_s;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the event loop descriptor that the server is running on.
|
||||||
|
*
|
||||||
|
* This will be an epoll fd when running on Linux or a kqueue on BSD. This can
|
||||||
|
* be used to listen for activity on sockets, etc. The only caveat is that the
|
||||||
|
* user data must be set to a struct where the first member is the function
|
||||||
|
* pointer to a callback that will handle the event. i.e:
|
||||||
|
*
|
||||||
|
* For kevent:
|
||||||
|
*
|
||||||
|
* struct foo {
|
||||||
|
* void (*handler)(struct kevent*);
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Set ev.udata to a foo pointer when registering the event.
|
||||||
|
*
|
||||||
|
* For epoll:
|
||||||
|
*
|
||||||
|
* struct foo {
|
||||||
|
* void (*handler)(struct epoll_event*);
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Set ev.data.ptr to a foo pointer when registering the event.
|
||||||
|
*
|
||||||
|
* @param server The server.
|
||||||
|
*
|
||||||
|
* @return The descriptor of the event loop.
|
||||||
|
*/
|
||||||
|
int http_server_loop(struct http_server_s *server);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates and initializes the http server.
|
||||||
|
*
|
||||||
|
* @param port The port to listen on.
|
||||||
|
* @param handler The callback that will fire to handle requests.
|
||||||
|
*
|
||||||
|
* @return Pointer to the allocated server.
|
||||||
|
*/
|
||||||
|
struct http_server_s *
|
||||||
|
http_server_init(int port, void (*handler)(struct http_request_s *));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens on the server socket and starts an event loop.
|
||||||
|
*
|
||||||
|
* During normal operation this function will not return.
|
||||||
|
*
|
||||||
|
* @param server The server.
|
||||||
|
* @param ipaddr The ip to bind to if NULL binds to all interfaces.
|
||||||
|
*
|
||||||
|
* @return Error code if the server fails.
|
||||||
|
*/
|
||||||
|
int http_server_listen_addr(struct http_server_s *server, const char *ipaddr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See http_server_listen_addr
|
||||||
|
*/
|
||||||
|
int http_server_listen(struct http_server_s *server);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Poll the server socket on specific interface.
|
||||||
|
*
|
||||||
|
* Use this listen call in place of the one above when you want to integrate
|
||||||
|
* an http server into an existing application that has a loop already and you
|
||||||
|
* want to use the polling functionality instead. This works well for
|
||||||
|
* applications like games that have a constant update loop.
|
||||||
|
*
|
||||||
|
* @param server The server.
|
||||||
|
* @param ipaddr The ip to bind to if NULL bind to all.
|
||||||
|
*
|
||||||
|
* @return Error code if the poll fails.
|
||||||
|
*/
|
||||||
|
int http_server_listen_addr_poll(struct http_server_s *server,
|
||||||
|
const char *ipaddr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Poll the server socket on all interfaces. See http_server_listen_addr_poll
|
||||||
|
*
|
||||||
|
* @param server The server.
|
||||||
|
*
|
||||||
|
* @return Error code if the poll fails.
|
||||||
|
*/
|
||||||
|
int http_server_listen_poll(struct http_server_s *server);
|
||||||
|
/**
|
||||||
|
* Poll of the request sockets.
|
||||||
|
*
|
||||||
|
* Call this function in your update loop. It will trigger the request handler
|
||||||
|
* once if there is a request ready. It should be called in a loop until it
|
||||||
|
* returns 0.
|
||||||
|
*
|
||||||
|
* @param server The server.
|
||||||
|
*
|
||||||
|
* @return Returns 1 if a request was handled and 0 if no requests were handled.
|
||||||
|
*/
|
||||||
|
int http_server_poll(struct http_server_s *server);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a request flag is set.
|
||||||
|
*
|
||||||
|
* The flags that can be queried are listed below:
|
||||||
|
*
|
||||||
|
* HTTP_FLG_STREAMED
|
||||||
|
*
|
||||||
|
* This flag will be set when the request body is chunked or the body is too
|
||||||
|
* large to fit in memory are once. This means that the
|
||||||
|
* http_request_read_chunk function must be used to read the body piece by
|
||||||
|
* piece.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
* @param flag One of the flags listed above.
|
||||||
|
*
|
||||||
|
* @return 1 or 0 if the flag is set or not respectively.
|
||||||
|
*/
|
||||||
|
int http_request_has_flag(struct http_request_s *request, int flag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request method as it was read from the HTTP request line.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
*
|
||||||
|
* @return The HTTP method.
|
||||||
|
*/
|
||||||
|
struct http_string_s http_request_method(struct http_request_s *request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full request target (url) from the HTTP request line.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
*
|
||||||
|
* @return The target.
|
||||||
|
*/
|
||||||
|
struct http_string_s http_request_target(struct http_request_s *request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the request body.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
*
|
||||||
|
* @return The request body. If no request body was sent buf and len of the
|
||||||
|
* string will be set to 0.
|
||||||
|
*/
|
||||||
|
struct http_string_s http_request_body(struct http_request_s *request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request header value for the given header key.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
* @param key The case insensitive header key to search for.
|
||||||
|
*
|
||||||
|
* @return The value for the header matching the key. Will be length 0 if not
|
||||||
|
* found.
|
||||||
|
*/
|
||||||
|
struct http_string_s http_request_header(struct http_request_s *request,
|
||||||
|
char const *key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over the request headers.
|
||||||
|
*
|
||||||
|
* Each call will set key and val to the key and value of the next header.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
* @param[out] key The key of the header.
|
||||||
|
* @param[out] value The key of the header.
|
||||||
|
* @param[inout] iter Should be initialized to 0 before calling. Pass back in
|
||||||
|
* with each consecutive call.
|
||||||
|
*
|
||||||
|
* @return 0 when there are no more headers.
|
||||||
|
*/
|
||||||
|
int http_request_iterate_headers(struct http_request_s *request,
|
||||||
|
struct http_string_s *key,
|
||||||
|
struct http_string_s *val, int *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores an arbitrary userdata pointer for this request.
|
||||||
|
*
|
||||||
|
* This is not used by the library in any way and is strictly for you, the
|
||||||
|
* application programmer to make use of.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
* @param data Opaque pointer to user data.
|
||||||
|
*/
|
||||||
|
void http_request_set_userdata(struct http_request_s *request, void *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the opaque data pointer that was set with http_request_set_userdata.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
*/
|
||||||
|
void *http_request_userdata(struct http_request_s *request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a server wide opaque pointer for future retrieval.
|
||||||
|
*
|
||||||
|
* This is not used by the library in any way and is strictly for you, the
|
||||||
|
* application programmer to make use of.
|
||||||
|
*
|
||||||
|
* @param server The server.
|
||||||
|
* @param data Opaque data pointer.
|
||||||
|
*/
|
||||||
|
void http_server_set_userdata(struct http_server_s *server, void *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the server wide userdata pointer.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
*/
|
||||||
|
void *http_request_server_userdata(struct http_request_s *request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets how the request will handle it's connection
|
||||||
|
*
|
||||||
|
* By default the server will inspect the Connection header and the HTTP
|
||||||
|
* version to determine whether the connection should be kept alive or not.
|
||||||
|
* Use this function to override that behaviour to force the connection to
|
||||||
|
* keep-alive or close by passing in the HTTP_KEEP_ALIVE or HTTP_CLOSE
|
||||||
|
* directives respectively. This may provide a minor performance improvement
|
||||||
|
* in cases where you control client and server and want to always close or
|
||||||
|
* keep the connection alive.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
* @param directive One of HTTP_KEEP_ALIVE or HTTP_CLOSE
|
||||||
|
*/
|
||||||
|
void http_request_connection(struct http_request_s *request, int directive);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the buffer of a request.
|
||||||
|
*
|
||||||
|
* When reading in the HTTP request the server allocates a buffer to store
|
||||||
|
* the request details such as the headers, method, body, etc. By default this
|
||||||
|
* memory will be freed when http_respond is called. This function lets you
|
||||||
|
* free that memory before the http_respond call. This can be useful if you
|
||||||
|
* have requests that take a long time to complete and you don't require the
|
||||||
|
* request data. Accessing any http_string_s's will be invalid after this call.
|
||||||
|
*
|
||||||
|
* @param request The request to free the buffer of.
|
||||||
|
*/
|
||||||
|
void http_request_free_buffer(struct http_request_s *request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates an http response.
|
||||||
|
*
|
||||||
|
* This memory will be freed when http_respond is called.
|
||||||
|
*
|
||||||
|
* @return Allocated response.
|
||||||
|
*/
|
||||||
|
struct http_response_s *http_response_init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the response status.
|
||||||
|
*
|
||||||
|
* Accepts values between 100 and 599 inclusive. Any other value will map to
|
||||||
|
* 500.
|
||||||
|
*
|
||||||
|
* @param response The response struct to set status on.
|
||||||
|
* @param status The HTTP status code.
|
||||||
|
*/
|
||||||
|
void http_response_status(struct http_response_s *response, int status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an HTTP response header.
|
||||||
|
*
|
||||||
|
* @param response The response struct to set the header on.
|
||||||
|
* @param key The null-terminated key of the header eg: Content-Type
|
||||||
|
* @param value The null-terminated value of the header eg: application/json
|
||||||
|
*/
|
||||||
|
void http_response_header(struct http_response_s *response, char const *key,
|
||||||
|
char const *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the response body.
|
||||||
|
*
|
||||||
|
* The caller is responsible for freeing any memory that
|
||||||
|
* may have been allocated for the body. It is safe to free this memory AFTER
|
||||||
|
* http_respond has been called. If responding with chunked transfer encoding
|
||||||
|
* this will become a single chunk. This procedure can be used again to set
|
||||||
|
* subsequent chunks.
|
||||||
|
*
|
||||||
|
* @param response The response struct to set the body for.
|
||||||
|
* @param body The body of the response.
|
||||||
|
* @param length The length of the body
|
||||||
|
*/
|
||||||
|
void http_response_body(struct http_response_s *response, char const *body,
|
||||||
|
int length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts writing the response to the client.
|
||||||
|
*
|
||||||
|
* Any memory allocated for the response body or response headers is safe to
|
||||||
|
* free after this call. Adds the default HTTP response headers, Date and
|
||||||
|
* Connection.
|
||||||
|
*
|
||||||
|
* @param request The request to respond to.
|
||||||
|
* @param response The response to respond with.
|
||||||
|
*/
|
||||||
|
void http_respond(struct http_request_s *request,
|
||||||
|
struct http_response_s *response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a chunk to the client.
|
||||||
|
*
|
||||||
|
* The notify_done callback will be called when the write is complete. This call
|
||||||
|
* consumes the response so a new response will need to be initialized for each
|
||||||
|
* chunk. The response status of the request will be the response status that is
|
||||||
|
* set when http_respond_chunk is called the first time. Any headers set for the
|
||||||
|
* first call will be sent as the response headers. Transfer-Encoding header
|
||||||
|
* will automatically be set to chunked. Headers set for subsequent calls will
|
||||||
|
* be ignored.
|
||||||
|
*
|
||||||
|
* @param request The request to respond to.
|
||||||
|
* @param response The response to respond with.
|
||||||
|
* @param notify_done The callback that's used to signal user code that another
|
||||||
|
* chunk is ready to be written out.
|
||||||
|
*/
|
||||||
|
void http_respond_chunk(struct http_request_s *request,
|
||||||
|
struct http_response_s *response,
|
||||||
|
void (*notify_done)(struct http_request_s *));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends the chunked response.
|
||||||
|
*
|
||||||
|
* Used to signal end of transmission on chunked requests. Any headers set
|
||||||
|
* before this call will be included as what the HTTP spec refers to as
|
||||||
|
* 'trailers' which are essentially more response headers.
|
||||||
|
*
|
||||||
|
* @param request The request to respond to.
|
||||||
|
* @param response The response to respond with.
|
||||||
|
*/
|
||||||
|
void http_respond_chunk_end(struct http_request_s *request,
|
||||||
|
struct http_response_s *response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a chunk of the request body.
|
||||||
|
*
|
||||||
|
* If a request has Transfer-Encoding: chunked or the body is too big to fit in
|
||||||
|
* memory all at once you cannot read the body in the typical way. Instead you
|
||||||
|
* need to call this function to read one chunk at a time. To check if the
|
||||||
|
* request requires this type of reading you can call the http_request_has_flag
|
||||||
|
* function to check if the HTTP_FLG_STREAMED flag is set. To read a streamed
|
||||||
|
* body you pass a callback that will be called when the chunk is ready. When
|
||||||
|
* the callback is called you can use 'http_request_chunk' to get the current
|
||||||
|
* chunk. When done with that chunk call this function again to request the
|
||||||
|
* next chunk. If the chunk has size 0 then the request body has been completely
|
||||||
|
* read and you can now respond.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
* @param chunk_cb Callback for when the chunk is ready.
|
||||||
|
*/
|
||||||
|
void http_request_read_chunk(struct http_request_s *request,
|
||||||
|
void (*chunk_cb)(struct http_request_s *));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current chunk of the request body.
|
||||||
|
*
|
||||||
|
* This chunk is only valid until the next call to 'http_request_read_chunk'.
|
||||||
|
*
|
||||||
|
* @param request The request.
|
||||||
|
*
|
||||||
|
* @return The chunk data.
|
||||||
|
*/
|
||||||
|
struct http_string_s http_request_chunk(struct http_request_s *request);
|
||||||
|
|
||||||
|
#define http_request_read_body http_request_read_chunk
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Minimal example usage.
|
||||||
|
#ifdef HTTPSERVER_EXAMPLE
|
||||||
|
|
||||||
|
#define RESPONSE "Hello, World!"
|
||||||
|
|
||||||
|
void handle_request(struct http_request_s *request) {
|
||||||
|
struct http_response_s *response = http_response_init();
|
||||||
|
http_response_status(response, 200);
|
||||||
|
http_response_header(response, "Content-Type", "text/plain");
|
||||||
|
http_response_body(response, RESPONSE, sizeof(RESPONSE) - 1);
|
||||||
|
http_respond(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
struct http_server_s *server = http_server_init(8080, handle_request);
|
||||||
|
http_server_listen(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
19
external/httpserver.h-master/src/buffer_util.h
vendored
Normal file
19
external/httpserver.h-master/src/buffer_util.h
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef HS_BUFFER_UTIL_H
|
||||||
|
#define HS_BUFFER_UTIL_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "common.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void _hs_buffer_free(struct hsh_buffer_s *buffer,
|
||||||
|
int64_t *memused) {
|
||||||
|
if (buffer->buf) {
|
||||||
|
free(buffer->buf);
|
||||||
|
*memused -= buffer->capacity;
|
||||||
|
buffer->buf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
127
external/httpserver.h-master/src/common.h
vendored
Normal file
127
external/httpserver.h-master/src/common.h
vendored
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
#ifndef HS_COMMON_H
|
||||||
|
#define HS_COMMON_H
|
||||||
|
|
||||||
|
// http session states
|
||||||
|
#define HTTP_SESSION_INIT 0
|
||||||
|
#define HTTP_SESSION_READ 1
|
||||||
|
#define HTTP_SESSION_WRITE 2
|
||||||
|
#define HTTP_SESSION_NOP 3
|
||||||
|
|
||||||
|
#define HTTP_REQUEST_TIMEOUT 20
|
||||||
|
|
||||||
|
#define HTTP_FLAG_SET(var, flag) var |= flag
|
||||||
|
#define HTTP_FLAG_CLEAR(var, flag) var &= ~flag
|
||||||
|
#define HTTP_FLAG_CHECK(var, flag) (var & flag)
|
||||||
|
|
||||||
|
#define HTTP_AUTOMATIC 0x8
|
||||||
|
#define HTTP_CHUNKED_RESPONSE 0x20
|
||||||
|
|
||||||
|
#define HTTP_KEEP_ALIVE 1
|
||||||
|
#define HTTP_CLOSE 0
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#ifdef KQUEUE
|
||||||
|
#include <sys/event.h>
|
||||||
|
#else
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef EPOLL
|
||||||
|
typedef void (*epoll_cb_t)(struct epoll_event *);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct http_ev_cb_s {
|
||||||
|
#ifdef KQUEUE
|
||||||
|
void (*handler)(struct kevent *ev);
|
||||||
|
#else
|
||||||
|
epoll_cb_t handler;
|
||||||
|
#endif
|
||||||
|
} ev_cb_t;
|
||||||
|
|
||||||
|
struct hsh_buffer_s {
|
||||||
|
char *buf;
|
||||||
|
int32_t capacity;
|
||||||
|
int32_t length;
|
||||||
|
int32_t index;
|
||||||
|
int32_t after_headers_index;
|
||||||
|
int8_t sequence_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum hsh_token_e {
|
||||||
|
HSH_TOK_METHOD,
|
||||||
|
HSH_TOK_TARGET,
|
||||||
|
HSH_TOK_VERSION,
|
||||||
|
HSH_TOK_HEADER_KEY,
|
||||||
|
HSH_TOK_HEADER_VALUE,
|
||||||
|
HSH_TOK_HEADERS_DONE,
|
||||||
|
HSH_TOK_BODY,
|
||||||
|
HSH_TOK_NONE,
|
||||||
|
HSH_TOK_ERR
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hsh_token_s {
|
||||||
|
enum hsh_token_e type;
|
||||||
|
uint8_t flags;
|
||||||
|
int len;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hsh_parser_s {
|
||||||
|
int64_t content_length;
|
||||||
|
int64_t content_remaining;
|
||||||
|
struct hsh_token_s token;
|
||||||
|
int16_t limit_count;
|
||||||
|
int16_t limit_max;
|
||||||
|
int8_t state;
|
||||||
|
int8_t flags;
|
||||||
|
int8_t sequence_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hs_token_array_s {
|
||||||
|
struct hsh_token_s *buf;
|
||||||
|
int capacity;
|
||||||
|
int size;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct http_request_s {
|
||||||
|
#ifdef KQUEUE
|
||||||
|
void (*handler)(struct kevent *ev);
|
||||||
|
#else
|
||||||
|
epoll_cb_t handler;
|
||||||
|
epoll_cb_t timer_handler;
|
||||||
|
int timerfd;
|
||||||
|
#endif
|
||||||
|
void (*chunk_cb)(struct http_request_s *);
|
||||||
|
void *data;
|
||||||
|
struct hsh_buffer_s buffer;
|
||||||
|
struct hsh_parser_s parser;
|
||||||
|
struct hs_token_array_s tokens;
|
||||||
|
int state;
|
||||||
|
int socket;
|
||||||
|
int timeout;
|
||||||
|
int64_t bytes_written;
|
||||||
|
struct http_server_s *server;
|
||||||
|
char flags;
|
||||||
|
} http_request_t;
|
||||||
|
|
||||||
|
typedef struct http_server_s {
|
||||||
|
#ifdef KQUEUE
|
||||||
|
void (*handler)(struct kevent *ev);
|
||||||
|
#else
|
||||||
|
epoll_cb_t handler;
|
||||||
|
epoll_cb_t timer_handler;
|
||||||
|
#endif
|
||||||
|
int64_t memused;
|
||||||
|
int socket;
|
||||||
|
int port;
|
||||||
|
int loop;
|
||||||
|
int timerfd;
|
||||||
|
socklen_t len;
|
||||||
|
void (*request_handler)(http_request_t *);
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
void *data;
|
||||||
|
char date[32];
|
||||||
|
} http_server_t;
|
||||||
|
|
||||||
|
#endif
|
119
external/httpserver.h-master/src/connection.c
vendored
Normal file
119
external/httpserver.h-master/src/connection.c
vendored
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef KQUEUE
|
||||||
|
#include <sys/event.h>
|
||||||
|
#else
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "buffer_util.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "connection.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef KQUEUE
|
||||||
|
|
||||||
|
void _hs_delete_events(http_request_t *request) {
|
||||||
|
struct kevent ev_set;
|
||||||
|
EV_SET(&ev_set, request->socket, EVFILT_TIMER, EV_DELETE, 0, 0, request);
|
||||||
|
kevent(request->server->loop, &ev_set, 1, NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hs_add_timer_event(http_request_t *request, hs_io_cb_t unused) {
|
||||||
|
(void)unused;
|
||||||
|
|
||||||
|
struct kevent ev_set;
|
||||||
|
EV_SET(&ev_set, request->socket, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0, 1000,
|
||||||
|
request);
|
||||||
|
kevent(request->server->loop, &ev_set, 1, NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void _hs_delete_events(http_request_t *request) {
|
||||||
|
epoll_ctl(request->server->loop, EPOLL_CTL_DEL, request->socket, NULL);
|
||||||
|
epoll_ctl(request->server->loop, EPOLL_CTL_DEL, request->timerfd, NULL);
|
||||||
|
close(request->timerfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hs_add_timer_event(http_request_t *request, hs_io_cb_t timer_cb) {
|
||||||
|
request->timer_handler = timer_cb;
|
||||||
|
|
||||||
|
// Watch for read events
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN | EPOLLET;
|
||||||
|
ev.data.ptr = request;
|
||||||
|
epoll_ctl(request->server->loop, EPOLL_CTL_ADD, request->socket, &ev);
|
||||||
|
|
||||||
|
// Add timer to timeout requests.
|
||||||
|
int tfd = timerfd_create(CLOCK_MONOTONIC, 0);
|
||||||
|
struct itimerspec ts = {};
|
||||||
|
ts.it_value.tv_sec = 1;
|
||||||
|
ts.it_interval.tv_sec = 1;
|
||||||
|
timerfd_settime(tfd, 0, &ts, NULL);
|
||||||
|
|
||||||
|
ev.events = EPOLLIN | EPOLLET;
|
||||||
|
ev.data.ptr = &request->timer_handler;
|
||||||
|
epoll_ctl(request->server->loop, EPOLL_CTL_ADD, tfd, &ev);
|
||||||
|
request->timerfd = tfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void hs_request_terminate_connection(http_request_t *request) {
|
||||||
|
_hs_delete_events(request);
|
||||||
|
close(request->socket);
|
||||||
|
_hs_buffer_free(&request->buffer, &request->server->memused);
|
||||||
|
free(request->tokens.buf);
|
||||||
|
request->tokens.buf = NULL;
|
||||||
|
free(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hs_token_array_init(struct hs_token_array_s *array, int capacity) {
|
||||||
|
array->buf =
|
||||||
|
(struct hsh_token_s *)malloc(sizeof(struct hsh_token_s) * capacity);
|
||||||
|
assert(array->buf != NULL);
|
||||||
|
array->size = 0;
|
||||||
|
array->capacity = capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_request_t *_hs_request_init(int sock, http_server_t *server,
|
||||||
|
hs_io_cb_t io_cb) {
|
||||||
|
http_request_t *request = (http_request_t *)calloc(1, sizeof(http_request_t));
|
||||||
|
assert(request != NULL);
|
||||||
|
request->socket = sock;
|
||||||
|
request->server = server;
|
||||||
|
request->handler = io_cb;
|
||||||
|
request->timeout = HTTP_REQUEST_TIMEOUT;
|
||||||
|
request->flags = HTTP_AUTOMATIC;
|
||||||
|
request->parser = (struct hsh_parser_s){};
|
||||||
|
request->buffer = (struct hsh_buffer_s){};
|
||||||
|
request->tokens.buf = NULL;
|
||||||
|
_hs_token_array_init(&request->tokens, 32);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_request_t *hs_server_accept_connection(http_server_t *server,
|
||||||
|
hs_io_cb_t io_cb,
|
||||||
|
hs_io_cb_t epoll_timer_cb) {
|
||||||
|
http_request_t *request = NULL;
|
||||||
|
int sock = 0;
|
||||||
|
|
||||||
|
sock = accept(server->socket, (struct sockaddr *)&server->addr, &server->len);
|
||||||
|
|
||||||
|
if (sock > 0) {
|
||||||
|
int flags = fcntl(sock, F_GETFL, 0);
|
||||||
|
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
request = _hs_request_init(sock, server, io_cb);
|
||||||
|
_hs_add_timer_event(request, epoll_timer_cb);
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
47
external/httpserver.h-master/src/connection.h
vendored
Normal file
47
external/httpserver.h-master/src/connection.h
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef HS_CONNECTION_H
|
||||||
|
#define HS_CONNECTION_H
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
struct http_request_s;
|
||||||
|
struct http_server_s;
|
||||||
|
|
||||||
|
#ifdef KQUEUE
|
||||||
|
struct kevent;
|
||||||
|
typedef void (*hs_io_cb_t)(struct kevent *ev);
|
||||||
|
#else
|
||||||
|
struct epoll_event;
|
||||||
|
typedef void (*hs_io_cb_t)(struct epoll_event *ev);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Closes the requests socket and frees its resources.
|
||||||
|
*
|
||||||
|
* Removes all event watchers from the request socket and frees any allocated
|
||||||
|
* buffers associated with the request struct.
|
||||||
|
*
|
||||||
|
* @param request The request to close
|
||||||
|
*/
|
||||||
|
void hs_request_terminate_connection(struct http_request_s *request);
|
||||||
|
|
||||||
|
/* Accepts connections on the server socket in a loop until it would block.
|
||||||
|
*
|
||||||
|
* When a connection is accepted a request struct is allocated and initialized
|
||||||
|
* and the request socket is set to non-blocking mode. Event watchers are set
|
||||||
|
* on the socket to call io_cb with a read/write ready event occurs. If the
|
||||||
|
* server has reached max_mem_usage the err_responder function is called to
|
||||||
|
* handle the issue.
|
||||||
|
*
|
||||||
|
* @param server The http server struct.
|
||||||
|
* @param io_cb The callback function to respond to events on the request socket
|
||||||
|
* @param epoll_timer_cb The callback function to respond to timer events for
|
||||||
|
* epoll. Can be NULL if not using epoll.
|
||||||
|
* @param err_responder The procedure to call when memory usage has reached the
|
||||||
|
* given limit. Typically this could respond with a 503 error and close the
|
||||||
|
* connection.
|
||||||
|
* @param max_mem_usage The limit at which err_responder should be called
|
||||||
|
* instead of regular operation.
|
||||||
|
*/
|
||||||
|
struct http_request_s *hs_server_accept_connection(struct http_server_s *server,
|
||||||
|
hs_io_cb_t io_cb,
|
||||||
|
hs_io_cb_t epoll_timer_cb);
|
||||||
|
|
||||||
|
#endif
|
50
external/httpserver.h-master/src/httpserver.m4
vendored
Normal file
50
external/httpserver.h-master/src/httpserver.m4
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// httpserver.h has been automatically generated from httpserver.m4 and the
|
||||||
|
// source files under /src
|
||||||
|
#ifndef HTTPSERVER_H
|
||||||
|
#define HTTPSERVER_H
|
||||||
|
#line 1 "api.h"
|
||||||
|
include(`api.h')
|
||||||
|
#line 1 "common.h"
|
||||||
|
include(`common.h')
|
||||||
|
#line 1 "buffer_util.h"
|
||||||
|
include(`buffer_util.h')
|
||||||
|
#line 1 "request_util.h"
|
||||||
|
include(`request_util.h')
|
||||||
|
#line 1 "parser.h"
|
||||||
|
include(`parser.h')
|
||||||
|
#line 1 "read_socket.h"
|
||||||
|
include(`read_socket.h')
|
||||||
|
#line 1 "respond.h"
|
||||||
|
include(`respond.h')
|
||||||
|
#line 1 "server.h"
|
||||||
|
include(`server.h')
|
||||||
|
#line 1 "write_socket.h"
|
||||||
|
include(`write_socket.h')
|
||||||
|
#line 1 "connection.h"
|
||||||
|
include(`connection.h')
|
||||||
|
#line 1 "io_events.h"
|
||||||
|
include(`io_events.h')
|
||||||
|
#ifdef HTTPSERVER_IMPL
|
||||||
|
#ifndef HTTPSERVER_IMPL_ONCE
|
||||||
|
#define HTTPSERVER_IMPL_ONCE
|
||||||
|
#line 1 "api.c"
|
||||||
|
include(`api.c')
|
||||||
|
#line 1 "request_util.c"
|
||||||
|
include(`request_util.c')
|
||||||
|
#line 1 "parser.c"
|
||||||
|
include(`parser.c')
|
||||||
|
#line 1 "read_socket.c"
|
||||||
|
include(`read_socket.c')
|
||||||
|
#line 1 "respond.c"
|
||||||
|
include(`respond.c')
|
||||||
|
#line 1 "server.c"
|
||||||
|
include(`server.c')
|
||||||
|
#line 1 "write_socket.c"
|
||||||
|
include(`write_socket.c')
|
||||||
|
#line 1 "connection.c"
|
||||||
|
include(`connection.c')
|
||||||
|
#line 1 "io_events.c"
|
||||||
|
include(`io_events.c')
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
195
external/httpserver.h-master/src/io_events.c
vendored
Normal file
195
external/httpserver.h-master/src/io_events.c
vendored
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef KQUEUE
|
||||||
|
#include <sys/event.h>
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "buffer_util.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "connection.h"
|
||||||
|
#include "io_events.h"
|
||||||
|
#include "read_socket.h"
|
||||||
|
#include "respond.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include "write_socket.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void _hs_read_socket_and_handle_return_code(http_request_t *request) {
|
||||||
|
struct hs_read_opts_s opts;
|
||||||
|
opts.initial_request_buf_capacity = HTTP_REQUEST_BUF_SIZE;
|
||||||
|
opts.max_request_buf_capacity = HTTP_MAX_REQUEST_BUF_SIZE;
|
||||||
|
opts.eof_rc = 0;
|
||||||
|
|
||||||
|
enum hs_read_rc_e rc = hs_read_request_and_exec_user_cb(request, opts);
|
||||||
|
switch (rc) {
|
||||||
|
case HS_READ_RC_PARSE_ERR:
|
||||||
|
hs_request_respond_error(request, 400, "Bad Request",
|
||||||
|
hs_request_begin_write);
|
||||||
|
break;
|
||||||
|
case HS_READ_RC_SOCKET_ERR:
|
||||||
|
hs_request_terminate_connection(request);
|
||||||
|
break;
|
||||||
|
case HS_READ_RC_SUCCESS:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hs_request_begin_read(http_request_t *request);
|
||||||
|
|
||||||
|
void _hs_write_socket_and_handle_return_code(http_request_t *request) {
|
||||||
|
enum hs_write_rc_e rc = hs_write_socket(request);
|
||||||
|
|
||||||
|
request->timeout = rc == HS_WRITE_RC_SUCCESS ? HTTP_KEEP_ALIVE_TIMEOUT
|
||||||
|
: HTTP_REQUEST_TIMEOUT;
|
||||||
|
|
||||||
|
if (rc != HS_WRITE_RC_CONTINUE)
|
||||||
|
_hs_buffer_free(&request->buffer, &request->server->memused);
|
||||||
|
|
||||||
|
switch (rc) {
|
||||||
|
case HS_WRITE_RC_SUCCESS_CLOSE:
|
||||||
|
case HS_WRITE_RC_SOCKET_ERR:
|
||||||
|
// Error or response complete, connection: close
|
||||||
|
hs_request_terminate_connection(request);
|
||||||
|
break;
|
||||||
|
case HS_WRITE_RC_SUCCESS:
|
||||||
|
// Response complete, keep-alive connection
|
||||||
|
hs_request_begin_read(request);
|
||||||
|
break;
|
||||||
|
case HS_WRITE_RC_SUCCESS_CHUNK:
|
||||||
|
// Finished writing chunk, request next
|
||||||
|
request->state = HTTP_SESSION_NOP;
|
||||||
|
request->chunk_cb(request);
|
||||||
|
break;
|
||||||
|
case HS_WRITE_RC_CONTINUE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hs_accept_and_begin_request_cycle(http_server_t *server,
|
||||||
|
hs_io_cb_t on_client_connection_cb,
|
||||||
|
hs_io_cb_t on_timer_event_cb) {
|
||||||
|
http_request_t *request = NULL;
|
||||||
|
while ((request = hs_server_accept_connection(server, on_client_connection_cb,
|
||||||
|
on_timer_event_cb))) {
|
||||||
|
if (server->memused > HTTP_MAX_TOTAL_EST_MEM_USAGE) {
|
||||||
|
hs_request_respond_error(request, 503, "Service Unavailable",
|
||||||
|
hs_request_begin_write);
|
||||||
|
} else {
|
||||||
|
hs_request_begin_read(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef KQUEUE
|
||||||
|
|
||||||
|
void _hs_on_kqueue_client_connection_event(struct kevent *ev) {
|
||||||
|
http_request_t *request = (http_request_t *)ev->udata;
|
||||||
|
if (ev->filter == EVFILT_TIMER) {
|
||||||
|
request->timeout -= 1;
|
||||||
|
if (request->timeout == 0)
|
||||||
|
hs_request_terminate_connection(request);
|
||||||
|
} else {
|
||||||
|
if (request->state == HTTP_SESSION_READ) {
|
||||||
|
_hs_read_socket_and_handle_return_code(request);
|
||||||
|
} else if (request->state == HTTP_SESSION_WRITE) {
|
||||||
|
_hs_write_socket_and_handle_return_code(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hs_on_kqueue_server_event(struct kevent *ev) {
|
||||||
|
http_server_t *server = (http_server_t *)ev->udata;
|
||||||
|
if (ev->filter == EVFILT_TIMER) {
|
||||||
|
hs_generate_date_time(server->date);
|
||||||
|
} else {
|
||||||
|
_hs_accept_and_begin_request_cycle(
|
||||||
|
server, _hs_on_kqueue_client_connection_event, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void _hs_on_epoll_client_connection_event(struct epoll_event *ev) {
|
||||||
|
http_request_t *request = (http_request_t *)ev->data.ptr;
|
||||||
|
if (request->state == HTTP_SESSION_READ) {
|
||||||
|
_hs_read_socket_and_handle_return_code(request);
|
||||||
|
} else if (request->state == HTTP_SESSION_WRITE) {
|
||||||
|
_hs_write_socket_and_handle_return_code(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hs_on_epoll_request_timer_event(struct epoll_event *ev) {
|
||||||
|
http_request_t *request =
|
||||||
|
(http_request_t *)((char *)ev->data.ptr - sizeof(epoll_cb_t));
|
||||||
|
uint64_t res;
|
||||||
|
int bytes = read(request->timerfd, &res, sizeof(res));
|
||||||
|
(void)bytes; // suppress warning
|
||||||
|
request->timeout -= 1;
|
||||||
|
if (request->timeout == 0)
|
||||||
|
hs_request_terminate_connection(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hs_on_epoll_server_connection_event(struct epoll_event *ev) {
|
||||||
|
_hs_accept_and_begin_request_cycle((http_server_t *)ev->data.ptr,
|
||||||
|
_hs_on_epoll_client_connection_event,
|
||||||
|
_hs_on_epoll_request_timer_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hs_on_epoll_server_timer_event(struct epoll_event *ev) {
|
||||||
|
http_server_t *server =
|
||||||
|
(http_server_t *)((char *)ev->data.ptr - sizeof(epoll_cb_t));
|
||||||
|
uint64_t res;
|
||||||
|
int bytes = read(server->timerfd, &res, sizeof(res));
|
||||||
|
(void)bytes; // suppress warning
|
||||||
|
hs_generate_date_time(server->date);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void _hs_add_write_event(http_request_t *request) {
|
||||||
|
#ifdef KQUEUE
|
||||||
|
struct kevent ev_set[2];
|
||||||
|
EV_SET(&ev_set[0], request->socket, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0,
|
||||||
|
request);
|
||||||
|
EV_SET(&ev_set[1], request->socket, EVFILT_READ, EV_DISABLE, 0, 0, request);
|
||||||
|
kevent(request->server->loop, ev_set, 2, NULL, 0, NULL);
|
||||||
|
#else
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLOUT | EPOLLET;
|
||||||
|
ev.data.ptr = request;
|
||||||
|
epoll_ctl(request->server->loop, EPOLL_CTL_MOD, request->socket, &ev);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void hs_request_begin_write(http_request_t *request) {
|
||||||
|
request->state = HTTP_SESSION_WRITE;
|
||||||
|
_hs_add_write_event(request);
|
||||||
|
_hs_write_socket_and_handle_return_code(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hs_add_read_event(http_request_t *request) {
|
||||||
|
#ifdef KQUEUE
|
||||||
|
// No action needed for kqueue since it's read event stays active. Should
|
||||||
|
// it be disabled during write?
|
||||||
|
struct kevent ev_set;
|
||||||
|
EV_SET(&ev_set, request->socket, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0,
|
||||||
|
request);
|
||||||
|
kevent(request->server->loop, &ev_set, 1, NULL, 0, NULL);
|
||||||
|
#else
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN | EPOLLET;
|
||||||
|
ev.data.ptr = request;
|
||||||
|
epoll_ctl(request->server->loop, EPOLL_CTL_MOD, request->socket, &ev);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void hs_request_begin_read(http_request_t *request) {
|
||||||
|
request->state = HTTP_SESSION_READ;
|
||||||
|
_hs_add_read_event(request);
|
||||||
|
_hs_read_socket_and_handle_return_code(request);
|
||||||
|
}
|
28
external/httpserver.h-master/src/io_events.h
vendored
Normal file
28
external/httpserver.h-master/src/io_events.h
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef HS_IO_EVENTS_H
|
||||||
|
#define HS_IO_EVENTS_H
|
||||||
|
|
||||||
|
#define HTTP_REQUEST_BUF_SIZE 1024
|
||||||
|
#define HTTP_MAX_REQUEST_BUF_SIZE 8388608 // 8mb
|
||||||
|
#define HTTP_MAX_TOTAL_EST_MEM_USAGE 4294967296 // 4gb
|
||||||
|
|
||||||
|
struct http_request_s;
|
||||||
|
|
||||||
|
void hs_request_begin_write(struct http_request_s *request);
|
||||||
|
void hs_request_begin_read(struct http_request_s *request);
|
||||||
|
|
||||||
|
#ifdef KQUEUE
|
||||||
|
|
||||||
|
struct kevent;
|
||||||
|
|
||||||
|
void hs_on_kqueue_server_event(struct kevent *ev);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
struct epoll_event;
|
||||||
|
|
||||||
|
void hs_on_epoll_server_connection_event(struct epoll_event *ev);
|
||||||
|
void hs_on_epoll_server_timer_event(struct epoll_event *ev);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
17
external/httpserver.h-master/src/parser.h
vendored
Normal file
17
external/httpserver.h-master/src/parser.h
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef HTTP_PARSER_H
|
||||||
|
#define HTTP_PARSER_H
|
||||||
|
|
||||||
|
// HSH_TOK_HEADERS_DONE flags
|
||||||
|
#define HSH_TOK_FLAG_NO_BODY 0x1
|
||||||
|
#define HSH_TOK_FLAG_STREAMED_BODY 0x2
|
||||||
|
|
||||||
|
// HSH_TOK_BODY flags
|
||||||
|
#define HSH_TOK_FLAG_BODY_FINAL 0x1
|
||||||
|
#define HSH_TOK_FLAG_SMALL_BODY 0x2
|
||||||
|
|
||||||
|
struct hsh_token_s hsh_parser_exec(struct hsh_parser_s *parser,
|
||||||
|
struct hsh_buffer_s *buffer,
|
||||||
|
int max_buf_capacity);
|
||||||
|
void hsh_parser_init(struct hsh_parser_s *parser);
|
||||||
|
|
||||||
|
#endif
|
261
external/httpserver.h-master/src/parser.rl
vendored
Normal file
261
external/httpserver.h-master/src/parser.rl
vendored
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "common.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define HSH_P_FLAG_CHUNKED 0x1
|
||||||
|
#define HSH_P_FLAG_TOKEN_READY 0x2
|
||||||
|
#define HSH_P_FLAG_DONE 0x4
|
||||||
|
|
||||||
|
#define HSH_ENTER_TOKEN(tok_type, max_len) \
|
||||||
|
parser->token.type = tok_type; \
|
||||||
|
parser->token.index = p - buffer->buf; \
|
||||||
|
parser->token.flags = 0; \
|
||||||
|
parser->limit_count = 0; \
|
||||||
|
parser->limit_max = max_len;
|
||||||
|
|
||||||
|
%%{
|
||||||
|
machine hsh_http;
|
||||||
|
|
||||||
|
action method { HSH_ENTER_TOKEN(HSH_TOK_METHOD, 32) }
|
||||||
|
action target { HSH_ENTER_TOKEN(HSH_TOK_TARGET, 1024) }
|
||||||
|
action version { HSH_ENTER_TOKEN(HSH_TOK_VERSION, 16) }
|
||||||
|
action header_key { HSH_ENTER_TOKEN(HSH_TOK_HEADER_KEY, 256) }
|
||||||
|
action header_value { HSH_ENTER_TOKEN(HSH_TOK_HEADER_VALUE, 4096) }
|
||||||
|
action body {
|
||||||
|
parser->token.type = HSH_TOK_BODY;
|
||||||
|
parser->token.flags = 0;
|
||||||
|
parser->token.index = p - buffer->buf;
|
||||||
|
}
|
||||||
|
action emit_token {
|
||||||
|
parser->token.len = p - (buffer->buf + parser->token.index);
|
||||||
|
// hsh_token_array_push(&parser->tokens, parser->token);
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_TOKEN_READY);
|
||||||
|
fbreak;
|
||||||
|
}
|
||||||
|
|
||||||
|
action content_length_digit {
|
||||||
|
parser->content_length *= 10;
|
||||||
|
parser->content_length += fc - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
action transfer_encoding {
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_CHUNKED);
|
||||||
|
}
|
||||||
|
|
||||||
|
action reset_count {
|
||||||
|
parser->limit_count = 0;
|
||||||
|
parser->limit_max = 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
action inc_count {
|
||||||
|
parser->limit_count++;
|
||||||
|
if (parser->limit_count > parser->limit_max) {
|
||||||
|
// parser->rc = (int8_t)HSH_PARSER_ERR;
|
||||||
|
fbreak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action done_headers {
|
||||||
|
buffer->after_headers_index = p - buffer->buf + 1;
|
||||||
|
parser->content_remaining = parser->content_length;
|
||||||
|
parser->token = (struct hsh_token_s){ };
|
||||||
|
parser->token.type = HSH_TOK_HEADERS_DONE;
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_TOKEN_READY);
|
||||||
|
if (HTTP_FLAG_CHECK(parser->flags, HSH_P_FLAG_CHUNKED)) {
|
||||||
|
HTTP_FLAG_SET(parser->token.flags, HSH_TOK_FLAG_STREAMED_BODY);
|
||||||
|
fnext chunked_body;
|
||||||
|
fbreak;
|
||||||
|
} else if (parser->content_length == 0) {
|
||||||
|
HTTP_FLAG_SET(parser->token.flags, HSH_TOK_FLAG_NO_BODY);
|
||||||
|
fbreak;
|
||||||
|
// The body won't fit into the buffer at maximum capacity.
|
||||||
|
} else if (parser->content_length > max_buf_capacity - buffer->after_headers_index) {
|
||||||
|
HTTP_FLAG_SET(parser->token.flags, HSH_TOK_FLAG_STREAMED_BODY);
|
||||||
|
fnext large_body;
|
||||||
|
fbreak;
|
||||||
|
} else {
|
||||||
|
// Resize the buffer to hold the full body
|
||||||
|
if (parser->content_length + buffer->after_headers_index > buffer->capacity) {
|
||||||
|
buffer->buf = (char*)realloc(buffer->buf, parser->content_length + buffer->after_headers_index);
|
||||||
|
buffer->capacity = parser->content_length + buffer->after_headers_index;
|
||||||
|
}
|
||||||
|
fnext small_body;
|
||||||
|
fbreak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action chunk_start {
|
||||||
|
parser->content_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
action chunk_size {
|
||||||
|
if (fc >= 'A' && fc <= 'F') {
|
||||||
|
parser->content_length *= 0x10;
|
||||||
|
parser->content_length += fc - 55;
|
||||||
|
} else if (fc >= 'a' && fc <= 'f') {
|
||||||
|
parser->content_length *= 0x10;
|
||||||
|
parser->content_length += fc - 87;
|
||||||
|
} else if (fc >= '0' && fc <= '9') {
|
||||||
|
parser->content_length *= 0x10;
|
||||||
|
parser->content_length += fc - '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action chunk_read {
|
||||||
|
char* last_body_byte = buffer->buf + parser->token.index + parser->content_length - 1;
|
||||||
|
if (pe >= last_body_byte) {
|
||||||
|
p = last_body_byte;
|
||||||
|
parser->token.len = parser->content_length;
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_TOKEN_READY);
|
||||||
|
fnext chunk_end;
|
||||||
|
fbreak;
|
||||||
|
// The current chunk is at the end of the buffer and the buffer cannot be expanded.
|
||||||
|
// Move the remaining contents of the buffer to just after the headers to free up
|
||||||
|
// capacity in the buffer.
|
||||||
|
} else if (p - buffer->buf + parser->content_length > max_buf_capacity) {
|
||||||
|
memcpy(buffer->buf + buffer->after_headers_index, p, pe - p);
|
||||||
|
buffer->length = buffer->after_headers_index + pe - p;
|
||||||
|
p = buffer->buf + buffer->after_headers_index;
|
||||||
|
parser->token.index = buffer->after_headers_index;
|
||||||
|
parser->sequence_id = buffer->sequence_id;
|
||||||
|
fhold;
|
||||||
|
fbreak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
action end_stream {
|
||||||
|
// write 0 byte body to tokens
|
||||||
|
parser->token.type = HSH_TOK_BODY;
|
||||||
|
parser->token.index = 0;
|
||||||
|
parser->token.len = 0;
|
||||||
|
parser->token.flags = HSH_TOK_FLAG_BODY_FINAL;
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_TOKEN_READY);
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_DONE);
|
||||||
|
fbreak;
|
||||||
|
}
|
||||||
|
|
||||||
|
action small_body_read {
|
||||||
|
parser->token.index = buffer->after_headers_index;
|
||||||
|
parser->token.len = parser->content_length;
|
||||||
|
HTTP_FLAG_SET(parser->token.flags, HSH_TOK_FLAG_SMALL_BODY);
|
||||||
|
char* last_body_byte = buffer->buf + parser->token.index + parser->content_length - 1;
|
||||||
|
if (pe >= last_body_byte) {
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_TOKEN_READY);
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_DONE);
|
||||||
|
}
|
||||||
|
p = pe;
|
||||||
|
fhold;
|
||||||
|
fbreak;
|
||||||
|
}
|
||||||
|
|
||||||
|
action large_body_read {
|
||||||
|
parser->token.index = buffer->after_headers_index;
|
||||||
|
char* last_body_byte = buffer->buf + buffer->after_headers_index + parser->content_remaining - 1;
|
||||||
|
if (pe >= last_body_byte) {
|
||||||
|
parser->token.flags = HSH_TOK_FLAG_BODY_FINAL;
|
||||||
|
parser->token.len = parser->content_remaining;
|
||||||
|
parser->content_remaining = 0;
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_TOKEN_READY);
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_DONE);
|
||||||
|
} else {
|
||||||
|
parser->token.len = pe - p;
|
||||||
|
parser->content_remaining -= parser->token.len;
|
||||||
|
HTTP_FLAG_SET(parser->flags, HSH_P_FLAG_TOKEN_READY);
|
||||||
|
p = buffer->buf + buffer->after_headers_index;
|
||||||
|
buffer->length = buffer->after_headers_index;
|
||||||
|
parser->sequence_id = buffer->sequence_id;
|
||||||
|
}
|
||||||
|
fhold;
|
||||||
|
fbreak;
|
||||||
|
}
|
||||||
|
|
||||||
|
action error {
|
||||||
|
// parser->rc = (int8_t)HSH_PARSER_ERR;
|
||||||
|
fbreak;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = ^[()<>@,;:\\"/\[\]?={} \t]+ >reset_count @inc_count;
|
||||||
|
crlf = '\r\n';
|
||||||
|
lws = crlf [ \t]+;
|
||||||
|
ows = [ \t]+;
|
||||||
|
|
||||||
|
method = [a-zA-Z]+ @inc_count ' ';
|
||||||
|
target = [^ ]+ @inc_count ' ';
|
||||||
|
version = 'HTTP/1.' [01] '\r';
|
||||||
|
|
||||||
|
request_line = (
|
||||||
|
method >method @emit_token
|
||||||
|
target >target @emit_token
|
||||||
|
version >version @emit_token '\n'
|
||||||
|
);
|
||||||
|
|
||||||
|
generic_header = (
|
||||||
|
( token ':' ) >header_key @emit_token
|
||||||
|
ows ( ^[ \t]? @inc_count ^[\r\n]* @inc_count '\r' ) >header_value @emit_token '\n'
|
||||||
|
);
|
||||||
|
|
||||||
|
content_length = (
|
||||||
|
( 'Content-Length'i ':' ) >header_key @emit_token
|
||||||
|
ows digit+ $content_length_digit ows crlf
|
||||||
|
);
|
||||||
|
|
||||||
|
transfer_encoding = (
|
||||||
|
( 'Transfer-Encoding'i ':' ) >header_key @emit_token
|
||||||
|
ows 'chunked' @transfer_encoding ows crlf
|
||||||
|
);
|
||||||
|
|
||||||
|
header = content_length | transfer_encoding | generic_header;
|
||||||
|
|
||||||
|
headers = ( header+ >reset_count @inc_count crlf ) @done_headers;
|
||||||
|
|
||||||
|
chunk = (
|
||||||
|
( ^[0] xdigit* ) >chunk_start $chunk_size crlf any+ >body $chunk_read
|
||||||
|
);
|
||||||
|
|
||||||
|
zero_chunk = '0' crlf @end_stream;
|
||||||
|
|
||||||
|
chunk_end := ( crlf ( chunk | zero_chunk ) ) $!error;
|
||||||
|
|
||||||
|
chunked_body := ( chunk* zero_chunk ) $!error;
|
||||||
|
|
||||||
|
small_body := any+ >body $small_body_read;
|
||||||
|
|
||||||
|
large_body := any+ >body $large_body_read;
|
||||||
|
|
||||||
|
main :=
|
||||||
|
request_line $!error
|
||||||
|
headers $!error;
|
||||||
|
}%%
|
||||||
|
|
||||||
|
%% write data;
|
||||||
|
|
||||||
|
void hsh_parser_init(struct hsh_parser_s* parser) {
|
||||||
|
memset(parser, 0, sizeof(struct hsh_parser_s));
|
||||||
|
parser->state = hsh_http_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hsh_token_s hsh_parser_exec(struct hsh_parser_s* parser, struct hsh_buffer_s* buffer, int max_buf_capacity) {
|
||||||
|
struct hsh_token_s none = {};
|
||||||
|
none.type = HSH_TOK_NONE;
|
||||||
|
if (HTTP_FLAG_CHECK(parser->flags, HSH_P_FLAG_DONE) || parser->sequence_id == buffer->sequence_id) {
|
||||||
|
return none;
|
||||||
|
}
|
||||||
|
int cs = parser->state;
|
||||||
|
char* eof = NULL;
|
||||||
|
char *p = buffer->buf + buffer->index;
|
||||||
|
char *pe = buffer->buf + buffer->length;
|
||||||
|
%% write exec;
|
||||||
|
parser->state = cs;
|
||||||
|
buffer->index = p - buffer->buf;
|
||||||
|
if (HTTP_FLAG_CHECK(parser->flags, HSH_P_FLAG_TOKEN_READY)) {
|
||||||
|
HTTP_FLAG_CLEAR(parser->flags, HSH_P_FLAG_TOKEN_READY);
|
||||||
|
return parser->token;
|
||||||
|
} else {
|
||||||
|
parser->sequence_id = buffer->sequence_id;
|
||||||
|
return none;
|
||||||
|
}
|
||||||
|
}
|
155
external/httpserver.h-master/src/read_socket.c
vendored
Normal file
155
external/httpserver.h-master/src/read_socket.c
vendored
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "common.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "read_socket.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void _hs_token_array_push(struct hs_token_array_s *array,
|
||||||
|
struct hsh_token_s a) {
|
||||||
|
if (array->size == array->capacity) {
|
||||||
|
array->capacity *= 2;
|
||||||
|
array->buf = (struct hsh_token_s *)realloc(
|
||||||
|
array->buf, array->capacity * sizeof(struct hsh_token_s));
|
||||||
|
assert(array->buf != NULL);
|
||||||
|
}
|
||||||
|
array->buf[array->size] = a;
|
||||||
|
array->size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hs_buffer_init(struct hsh_buffer_s *buffer, int initial_capacity,
|
||||||
|
int64_t *memused) {
|
||||||
|
*buffer = (struct hsh_buffer_s){0};
|
||||||
|
buffer->buf = (char *)calloc(1, initial_capacity);
|
||||||
|
*memused += initial_capacity;
|
||||||
|
assert(buffer->buf != NULL);
|
||||||
|
buffer->capacity = initial_capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _hs_read_into_buffer(struct hsh_buffer_s *buffer, int request_socket,
|
||||||
|
int64_t *server_memused,
|
||||||
|
int64_t max_request_buf_capacity) {
|
||||||
|
int bytes;
|
||||||
|
do {
|
||||||
|
bytes = read(request_socket, buffer->buf + buffer->length,
|
||||||
|
buffer->capacity - buffer->length);
|
||||||
|
if (bytes > 0)
|
||||||
|
buffer->length += bytes;
|
||||||
|
|
||||||
|
if (buffer->length == buffer->capacity &&
|
||||||
|
buffer->capacity != max_request_buf_capacity) {
|
||||||
|
*server_memused -= buffer->capacity;
|
||||||
|
buffer->capacity *= 2;
|
||||||
|
if (buffer->capacity > max_request_buf_capacity) {
|
||||||
|
buffer->capacity = max_request_buf_capacity;
|
||||||
|
}
|
||||||
|
*server_memused += buffer->capacity;
|
||||||
|
buffer->buf = (char *)realloc(buffer->buf, buffer->capacity);
|
||||||
|
assert(buffer->buf != NULL);
|
||||||
|
}
|
||||||
|
} while (bytes > 0 && buffer->capacity < max_request_buf_capacity);
|
||||||
|
|
||||||
|
buffer->sequence_id++;
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _hs_buffer_requires_read(struct hsh_buffer_s *buffer) {
|
||||||
|
return buffer->index >= buffer->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hs_exec_callback(http_request_t *request,
|
||||||
|
void (*cb)(struct http_request_s *)) {
|
||||||
|
request->state = HTTP_SESSION_NOP;
|
||||||
|
cb(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum hs_read_rc_e
|
||||||
|
_hs_parse_buffer_and_exec_user_cb(http_request_t *request,
|
||||||
|
int max_request_buf_capacity) {
|
||||||
|
enum hs_read_rc_e rc = HS_READ_RC_SUCCESS;
|
||||||
|
|
||||||
|
do {
|
||||||
|
struct hsh_token_s token = hsh_parser_exec(
|
||||||
|
&request->parser, &request->buffer, max_request_buf_capacity);
|
||||||
|
|
||||||
|
switch (token.type) {
|
||||||
|
case HSH_TOK_HEADERS_DONE:
|
||||||
|
_hs_token_array_push(&request->tokens, token);
|
||||||
|
if (HTTP_FLAG_CHECK(token.flags, HSH_TOK_FLAG_STREAMED_BODY) ||
|
||||||
|
HTTP_FLAG_CHECK(token.flags, HSH_TOK_FLAG_NO_BODY)) {
|
||||||
|
HTTP_FLAG_SET(request->flags, HTTP_FLG_STREAMED);
|
||||||
|
_hs_exec_callback(request, request->server->request_handler);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case HSH_TOK_BODY:
|
||||||
|
_hs_token_array_push(&request->tokens, token);
|
||||||
|
if (HTTP_FLAG_CHECK(token.flags, HSH_TOK_FLAG_SMALL_BODY)) {
|
||||||
|
_hs_exec_callback(request, request->server->request_handler);
|
||||||
|
} else {
|
||||||
|
if (HTTP_FLAG_CHECK(token.flags, HSH_TOK_FLAG_BODY_FINAL) &&
|
||||||
|
token.len > 0) {
|
||||||
|
_hs_exec_callback(request, request->chunk_cb);
|
||||||
|
|
||||||
|
// A zero length body is used to indicate to the user code that the
|
||||||
|
// body has finished streaming. This is natural when dealing with
|
||||||
|
// chunked request bodies but requires us to inject a zero length
|
||||||
|
// body for non-chunked requests.
|
||||||
|
struct hsh_token_s token = {};
|
||||||
|
memset(&token, 0, sizeof(struct hsh_token_s));
|
||||||
|
token.type = HSH_TOK_BODY;
|
||||||
|
_hs_token_array_push(&request->tokens, token);
|
||||||
|
_hs_exec_callback(request, request->chunk_cb);
|
||||||
|
} else {
|
||||||
|
_hs_exec_callback(request, request->chunk_cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
case HSH_TOK_ERR:
|
||||||
|
return HS_READ_RC_PARSE_ERR;
|
||||||
|
case HSH_TOK_NONE:
|
||||||
|
return rc;
|
||||||
|
default:
|
||||||
|
_hs_token_array_push(&request->tokens, token);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads the request socket if required and parses HTTP in a non-blocking
|
||||||
|
// manner.
|
||||||
|
//
|
||||||
|
// It should be called when a new connection is established and when a read
|
||||||
|
// ready event occurs for the request socket. It parses the HTTP request and
|
||||||
|
// fills the tokens array of the request struct. It will also invoke the
|
||||||
|
// request_hander callback and the chunk_cb callback in the appropriate
|
||||||
|
// scenarios.
|
||||||
|
enum hs_read_rc_e hs_read_request_and_exec_user_cb(http_request_t *request,
|
||||||
|
struct hs_read_opts_s opts) {
|
||||||
|
request->state = HTTP_SESSION_READ;
|
||||||
|
request->timeout = HTTP_REQUEST_TIMEOUT;
|
||||||
|
|
||||||
|
if (request->buffer.buf == NULL) {
|
||||||
|
_hs_buffer_init(&request->buffer, opts.initial_request_buf_capacity,
|
||||||
|
&request->server->memused);
|
||||||
|
hsh_parser_init(&request->parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_hs_buffer_requires_read(&request->buffer)) {
|
||||||
|
int bytes = _hs_read_into_buffer(&request->buffer, request->socket,
|
||||||
|
&request->server->memused,
|
||||||
|
opts.max_request_buf_capacity);
|
||||||
|
|
||||||
|
if (bytes == opts.eof_rc) {
|
||||||
|
return HS_READ_RC_SOCKET_ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _hs_parse_buffer_and_exec_user_cb(request,
|
||||||
|
opts.max_request_buf_capacity);
|
||||||
|
}
|
37
external/httpserver.h-master/src/read_socket.h
vendored
Normal file
37
external/httpserver.h-master/src/read_socket.h
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef HS_READ_SOCKET_H
|
||||||
|
#define HS_READ_SOCKET_H
|
||||||
|
|
||||||
|
#define HTTP_FLG_STREAMED 0x1
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct http_request_s;
|
||||||
|
|
||||||
|
// Response code for hs_read_socket
|
||||||
|
enum hs_read_rc_e {
|
||||||
|
// Execution was successful
|
||||||
|
HS_READ_RC_SUCCESS,
|
||||||
|
// There was an error parsing the HTTP request
|
||||||
|
HS_READ_RC_PARSE_ERR,
|
||||||
|
// There was an error reading the socket
|
||||||
|
HS_READ_RC_SOCKET_ERR
|
||||||
|
};
|
||||||
|
|
||||||
|
// Holds configuration options for the hs_read_socket procedure.
|
||||||
|
struct hs_read_opts_s {
|
||||||
|
// Restricts the request buffer from ever growing larger than this size
|
||||||
|
int64_t max_request_buf_capacity;
|
||||||
|
// The value to be compared to the return of the read call to determine if
|
||||||
|
// the connection has been closed. Should generally be 0 in normal operation
|
||||||
|
// using sockets but can be useful to change if you want to use files instead
|
||||||
|
// of sockets for testing.
|
||||||
|
int eof_rc;
|
||||||
|
// The initial capacity that is allocated for the request buffer
|
||||||
|
int initial_request_buf_capacity;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum hs_read_rc_e
|
||||||
|
hs_read_request_and_exec_user_cb(struct http_request_s *request,
|
||||||
|
struct hs_read_opts_s opts);
|
||||||
|
|
||||||
|
#endif
|
112
external/httpserver.h-master/src/request_util.c
vendored
Normal file
112
external/httpserver.h-master/src/request_util.c
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "common.h"
|
||||||
|
#include "request_util.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int _hs_case_insensitive_cmp(char const *a, char const *b, int len) {
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
char c1 = a[i] >= 'A' && a[i] <= 'Z' ? a[i] + 32 : a[i];
|
||||||
|
char c2 = b[i] >= 'A' && b[i] <= 'Z' ? b[i] + 32 : b[i];
|
||||||
|
if (c1 != c2)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_string_t hs_get_token_string(http_request_t *request,
|
||||||
|
enum hsh_token_e token_type) {
|
||||||
|
http_string_t str = {0, 0};
|
||||||
|
if (request->tokens.buf == NULL)
|
||||||
|
return str;
|
||||||
|
for (int i = 0; i < request->tokens.size; i++) {
|
||||||
|
struct hsh_token_s token = request->tokens.buf[i];
|
||||||
|
if (token.type == token_type) {
|
||||||
|
str.buf = &request->buffer.buf[token.index];
|
||||||
|
str.len = token.len;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_string_t hs_request_header(http_request_t *request, char const *key) {
|
||||||
|
int len = strlen(key);
|
||||||
|
for (int i = 0; i < request->tokens.size; i++) {
|
||||||
|
struct hsh_token_s token = request->tokens.buf[i];
|
||||||
|
if (token.type == HSH_TOK_HEADER_KEY && token.len == len) {
|
||||||
|
if (_hs_case_insensitive_cmp(&request->buffer.buf[token.index], key,
|
||||||
|
len)) {
|
||||||
|
token = request->tokens.buf[i + 1];
|
||||||
|
return (http_string_t){.buf = &request->buffer.buf[token.index],
|
||||||
|
.len = token.len};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (http_string_t){};
|
||||||
|
}
|
||||||
|
|
||||||
|
void hs_request_detect_keep_alive_flag(http_request_t *request) {
|
||||||
|
http_string_t str = hs_get_token_string(request, HSH_TOK_VERSION);
|
||||||
|
if (str.buf == NULL)
|
||||||
|
return;
|
||||||
|
int version = str.buf[str.len - 1] == '1';
|
||||||
|
str = hs_request_header(request, "Connection");
|
||||||
|
if ((str.len == 5 && _hs_case_insensitive_cmp(str.buf, "close", 5)) ||
|
||||||
|
(str.len == 0 && version == HTTP_1_0)) {
|
||||||
|
HTTP_FLAG_CLEAR(request->flags, HTTP_KEEP_ALIVE);
|
||||||
|
} else {
|
||||||
|
HTTP_FLAG_SET(request->flags, HTTP_KEEP_ALIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _hs_get_header_key_val(http_request_t *request, http_string_t *key,
|
||||||
|
http_string_t *val, int iter) {
|
||||||
|
struct hsh_token_s token = request->tokens.buf[iter];
|
||||||
|
if (request->tokens.buf[iter].type == HSH_TOK_HEADERS_DONE)
|
||||||
|
return 0;
|
||||||
|
*key = (http_string_t){.buf = &request->buffer.buf[token.index],
|
||||||
|
.len = token.len};
|
||||||
|
token = request->tokens.buf[iter + 1];
|
||||||
|
*val = (http_string_t){.buf = &request->buffer.buf[token.index],
|
||||||
|
.len = token.len};
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hs_request_iterate_headers(http_request_t *request, http_string_t *key,
|
||||||
|
http_string_t *val, int *iter) {
|
||||||
|
if (*iter == 0) {
|
||||||
|
for (; *iter < request->tokens.size; (*iter)++) {
|
||||||
|
struct hsh_token_s token = request->tokens.buf[*iter];
|
||||||
|
if (token.type == HSH_TOK_HEADER_KEY) {
|
||||||
|
int more = _hs_get_header_key_val(request, key, val, *iter);
|
||||||
|
(*iter)++;
|
||||||
|
return more;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
(*iter)++;
|
||||||
|
int more = _hs_get_header_key_val(request, key, val, *iter);
|
||||||
|
(*iter)++;
|
||||||
|
return more;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hs_request_set_keep_alive_flag(http_request_t *request, int directive) {
|
||||||
|
if (directive == HTTP_KEEP_ALIVE) {
|
||||||
|
HTTP_FLAG_CLEAR(request->flags, HTTP_AUTOMATIC);
|
||||||
|
HTTP_FLAG_SET(request->flags, HTTP_KEEP_ALIVE);
|
||||||
|
} else if (directive == HTTP_CLOSE) {
|
||||||
|
HTTP_FLAG_CLEAR(request->flags, HTTP_AUTOMATIC);
|
||||||
|
HTTP_FLAG_CLEAR(request->flags, HTTP_KEEP_ALIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http_string_t hs_request_chunk(struct http_request_s *request) {
|
||||||
|
struct hsh_token_s token = request->tokens.buf[request->tokens.size - 1];
|
||||||
|
return (http_string_t){.buf = &request->buffer.buf[token.index],
|
||||||
|
.len = token.len};
|
||||||
|
}
|
28
external/httpserver.h-master/src/request_util.h
vendored
Normal file
28
external/httpserver.h-master/src/request_util.h
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef HS_REQUEST_UTIL_H
|
||||||
|
#define HS_REQUEST_UTIL_H
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "common.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// http version indicators
|
||||||
|
#define HTTP_1_0 0
|
||||||
|
#define HTTP_1_1 1
|
||||||
|
|
||||||
|
struct http_string_s {
|
||||||
|
char const *buf;
|
||||||
|
int len;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct http_string_s http_string_t;
|
||||||
|
|
||||||
|
http_string_t hs_get_token_string(http_request_t *request,
|
||||||
|
enum hsh_token_e token_type);
|
||||||
|
http_string_t hs_request_header(http_request_t *request, char const *key);
|
||||||
|
void hs_request_detect_keep_alive_flag(http_request_t *request);
|
||||||
|
int hs_request_iterate_headers(http_request_t *request, http_string_t *key,
|
||||||
|
http_string_t *val, int *iter);
|
||||||
|
void hs_request_set_keep_alive_flag(http_request_t *request, int directive);
|
||||||
|
http_string_t hs_request_chunk(struct http_request_s *request);
|
||||||
|
|
||||||
|
#endif
|
256
external/httpserver.h-master/src/respond.c
vendored
Normal file
256
external/httpserver.h-master/src/respond.c
vendored
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "buffer_util.h"
|
||||||
|
#include "common.h"
|
||||||
|
#include "request_util.h"
|
||||||
|
#include "respond.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char const *hs_status_text[] = {
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "",
|
||||||
|
|
||||||
|
// 100s
|
||||||
|
"Continue", "Switching Protocols", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
|
||||||
|
// 200s
|
||||||
|
"OK", "Created", "Accepted", "Non-Authoritative Information", "No Content",
|
||||||
|
"Reset Content", "Partial Content", "", "", "",
|
||||||
|
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
|
||||||
|
// 300s
|
||||||
|
"Multiple Choices", "Moved Permanently", "Found", "See Other",
|
||||||
|
"Not Modified", "Use Proxy", "", "Temporary Redirect", "", "",
|
||||||
|
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
|
||||||
|
// 400s
|
||||||
|
"Bad Request", "Unauthorized", "Payment Required", "Forbidden", "Not Found",
|
||||||
|
"Method Not Allowed", "Not Acceptable", "Proxy Authentication Required",
|
||||||
|
"Request Timeout", "Conflict",
|
||||||
|
|
||||||
|
"Gone", "Length Required", "", "Payload Too Large", "", "", "", "", "", "",
|
||||||
|
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "",
|
||||||
|
|
||||||
|
// 500s
|
||||||
|
"Internal Server Error", "Not Implemented", "Bad Gateway",
|
||||||
|
"Service Unavailable", "Gateway Timeout", "", "", "", "", "",
|
||||||
|
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "", "", "", "", "", "", ""};
|
||||||
|
typedef struct {
|
||||||
|
char *buf;
|
||||||
|
int capacity;
|
||||||
|
int size;
|
||||||
|
int64_t *memused;
|
||||||
|
} grwprintf_t;
|
||||||
|
|
||||||
|
void _grwprintf_init(grwprintf_t *ctx, int capacity, int64_t *memused) {
|
||||||
|
ctx->memused = memused;
|
||||||
|
ctx->size = 0;
|
||||||
|
ctx->buf = (char *)malloc(capacity);
|
||||||
|
*ctx->memused += capacity;
|
||||||
|
assert(ctx->buf != NULL);
|
||||||
|
ctx->capacity = capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _grwmemcpy(grwprintf_t *ctx, char const *src, int size) {
|
||||||
|
if (ctx->size + size > ctx->capacity) {
|
||||||
|
*ctx->memused -= ctx->capacity;
|
||||||
|
ctx->capacity = ctx->size + size;
|
||||||
|
*ctx->memused += ctx->capacity;
|
||||||
|
ctx->buf = (char *)realloc(ctx->buf, ctx->capacity);
|
||||||
|
assert(ctx->buf != NULL);
|
||||||
|
}
|
||||||
|
memcpy(ctx->buf + ctx->size, src, size);
|
||||||
|
ctx->size += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _grwprintf(grwprintf_t *ctx, char const *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
|
||||||
|
int bytes =
|
||||||
|
vsnprintf(ctx->buf + ctx->size, ctx->capacity - ctx->size, fmt, args);
|
||||||
|
if (bytes + ctx->size > ctx->capacity) {
|
||||||
|
*ctx->memused -= ctx->capacity;
|
||||||
|
while (bytes + ctx->size > ctx->capacity)
|
||||||
|
ctx->capacity *= 2;
|
||||||
|
*ctx->memused += ctx->capacity;
|
||||||
|
ctx->buf = (char *)realloc(ctx->buf, ctx->capacity);
|
||||||
|
assert(ctx->buf != NULL);
|
||||||
|
bytes +=
|
||||||
|
vsnprintf(ctx->buf + ctx->size, ctx->capacity - ctx->size, fmt, args);
|
||||||
|
}
|
||||||
|
ctx->size += bytes;
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _http_serialize_headers_list(http_response_t *response,
|
||||||
|
grwprintf_t *printctx) {
|
||||||
|
http_header_t *header = response->headers;
|
||||||
|
while (header) {
|
||||||
|
_grwprintf(printctx, "%s: %s\r\n", header->key, header->value);
|
||||||
|
header = header->next;
|
||||||
|
}
|
||||||
|
_grwprintf(printctx, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void _http_serialize_headers(http_request_t *request, http_response_t *response,
|
||||||
|
grwprintf_t *printctx) {
|
||||||
|
if (HTTP_FLAG_CHECK(request->flags, HTTP_AUTOMATIC)) {
|
||||||
|
hs_request_detect_keep_alive_flag(request);
|
||||||
|
}
|
||||||
|
if (HTTP_FLAG_CHECK(request->flags, HTTP_KEEP_ALIVE)) {
|
||||||
|
hs_response_set_header(response, "Connection", "keep-alive");
|
||||||
|
} else {
|
||||||
|
hs_response_set_header(response, "Connection", "close");
|
||||||
|
}
|
||||||
|
_grwprintf(printctx, "HTTP/1.1 %d %s\r\nDate: %s\r\n", response->status,
|
||||||
|
hs_status_text[response->status], request->server->date);
|
||||||
|
if (!HTTP_FLAG_CHECK(request->flags, HTTP_CHUNKED_RESPONSE)) {
|
||||||
|
_grwprintf(printctx, "Content-Length: %d\r\n", response->content_length);
|
||||||
|
}
|
||||||
|
_http_serialize_headers_list(response, printctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _http_perform_response(http_request_t *request, http_response_t *response,
|
||||||
|
grwprintf_t *printctx, hs_req_fn_t http_write) {
|
||||||
|
http_header_t *header = response->headers;
|
||||||
|
while (header) {
|
||||||
|
http_header_t *tmp = header;
|
||||||
|
header = tmp->next;
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
_hs_buffer_free(&request->buffer, &request->server->memused);
|
||||||
|
free(response);
|
||||||
|
request->buffer.buf = printctx->buf;
|
||||||
|
request->buffer.length = printctx->size;
|
||||||
|
request->buffer.capacity = printctx->capacity;
|
||||||
|
request->bytes_written = 0;
|
||||||
|
request->state = HTTP_SESSION_WRITE;
|
||||||
|
http_write(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See api.h http_response_header
|
||||||
|
void hs_response_set_header(http_response_t *response, char const *key,
|
||||||
|
char const *value) {
|
||||||
|
http_header_t *header = (http_header_t *)malloc(sizeof(http_header_t));
|
||||||
|
assert(header != NULL);
|
||||||
|
header->key = key;
|
||||||
|
header->value = value;
|
||||||
|
http_header_t *prev = response->headers;
|
||||||
|
header->next = prev;
|
||||||
|
response->headers = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serializes the response into the request buffer and calls http_write.
|
||||||
|
// See api.h http_respond for more details
|
||||||
|
void hs_request_respond(http_request_t *request, http_response_t *response,
|
||||||
|
hs_req_fn_t http_write) {
|
||||||
|
grwprintf_t printctx;
|
||||||
|
_grwprintf_init(&printctx, HTTP_RESPONSE_BUF_SIZE, &request->server->memused);
|
||||||
|
_http_serialize_headers(request, response, &printctx);
|
||||||
|
if (response->body) {
|
||||||
|
_grwmemcpy(&printctx, response->body, response->content_length);
|
||||||
|
}
|
||||||
|
_http_perform_response(request, response, &printctx, http_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serializes a chunk into the request buffer and calls http_write.
|
||||||
|
// See api.h http_respond_chunk for more details.
|
||||||
|
void hs_request_respond_chunk(http_request_t *request,
|
||||||
|
http_response_t *response, hs_req_fn_t cb,
|
||||||
|
hs_req_fn_t http_write) {
|
||||||
|
grwprintf_t printctx;
|
||||||
|
_grwprintf_init(&printctx, HTTP_RESPONSE_BUF_SIZE, &request->server->memused);
|
||||||
|
if (!HTTP_FLAG_CHECK(request->flags, HTTP_CHUNKED_RESPONSE)) {
|
||||||
|
HTTP_FLAG_SET(request->flags, HTTP_CHUNKED_RESPONSE);
|
||||||
|
hs_response_set_header(response, "Transfer-Encoding", "chunked");
|
||||||
|
_http_serialize_headers(request, response, &printctx);
|
||||||
|
}
|
||||||
|
request->chunk_cb = cb;
|
||||||
|
_grwprintf(&printctx, "%X\r\n", response->content_length);
|
||||||
|
_grwmemcpy(&printctx, response->body, response->content_length);
|
||||||
|
_grwprintf(&printctx, "\r\n");
|
||||||
|
_http_perform_response(request, response, &printctx, http_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serializes the zero sized final chunk into the request buffer and calls
|
||||||
|
// http_write. See api.h http_respond_chunk_end for more details.
|
||||||
|
void hs_request_respond_chunk_end(http_request_t *request,
|
||||||
|
http_response_t *response,
|
||||||
|
hs_req_fn_t http_write) {
|
||||||
|
grwprintf_t printctx;
|
||||||
|
_grwprintf_init(&printctx, HTTP_RESPONSE_BUF_SIZE, &request->server->memused);
|
||||||
|
_grwprintf(&printctx, "0\r\n");
|
||||||
|
_http_serialize_headers_list(response, &printctx);
|
||||||
|
_grwprintf(&printctx, "\r\n");
|
||||||
|
HTTP_FLAG_CLEAR(request->flags, HTTP_CHUNKED_RESPONSE);
|
||||||
|
_http_perform_response(request, response, &printctx, http_write);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See api.h http_response_status
|
||||||
|
void hs_response_set_status(http_response_t *response, int status) {
|
||||||
|
response->status = status > 599 || status < 100 ? 500 : status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See api.h http_response_body
|
||||||
|
void hs_response_set_body(http_response_t *response, char const *body,
|
||||||
|
int length) {
|
||||||
|
response->body = body;
|
||||||
|
response->content_length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See api.h http_response_init
|
||||||
|
http_response_t *hs_response_init() {
|
||||||
|
http_response_t *response =
|
||||||
|
(http_response_t *)calloc(1, sizeof(http_response_t));
|
||||||
|
assert(response != NULL);
|
||||||
|
response->status = 200;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple less flexible interface for responses, used for errors.
|
||||||
|
void hs_request_respond_error(http_request_t *request, int code,
|
||||||
|
char const *message, hs_req_fn_t http_write) {
|
||||||
|
struct http_response_s *response = hs_response_init();
|
||||||
|
hs_response_set_status(response, code);
|
||||||
|
hs_response_set_header(response, "Content-Type", "text/plain");
|
||||||
|
hs_response_set_body(response, message, strlen(message));
|
||||||
|
hs_request_respond(request, response, http_write);
|
||||||
|
http_write(request);
|
||||||
|
}
|
51
external/httpserver.h-master/src/respond.h
vendored
Normal file
51
external/httpserver.h-master/src/respond.h
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef HS_RESPOND_H
|
||||||
|
#define HS_RESPOND_H
|
||||||
|
|
||||||
|
#define HTTP_RESPONSE_BUF_SIZE 1024
|
||||||
|
|
||||||
|
struct http_request_s;
|
||||||
|
|
||||||
|
typedef void (*hs_req_fn_t)(struct http_request_s *);
|
||||||
|
|
||||||
|
// Represents a single header of an HTTP response.
|
||||||
|
typedef struct http_header_s {
|
||||||
|
// The key of the header eg: Content-Type
|
||||||
|
char const *key;
|
||||||
|
// The value of the header eg: application/json
|
||||||
|
char const *value;
|
||||||
|
// Pointer to the next header in the linked list.
|
||||||
|
struct http_header_s *next;
|
||||||
|
} http_header_t;
|
||||||
|
|
||||||
|
// Represents the response of an HTTP request before it is serialized on the
|
||||||
|
// wire.
|
||||||
|
typedef struct http_response_s {
|
||||||
|
// Head of the linked list of response headers
|
||||||
|
http_header_t *headers;
|
||||||
|
// The complete body of the response or the chunk if generating a chunked
|
||||||
|
// response.
|
||||||
|
char const *body;
|
||||||
|
// The length of the body or chunk.
|
||||||
|
int content_length;
|
||||||
|
// The HTTP status code for the response.
|
||||||
|
int status;
|
||||||
|
} http_response_t;
|
||||||
|
|
||||||
|
http_response_t *hs_response_init();
|
||||||
|
void hs_response_set_header(http_response_t *response, char const *key,
|
||||||
|
char const *value);
|
||||||
|
void hs_response_set_status(http_response_t *response, int status);
|
||||||
|
void hs_response_set_body(http_response_t *response, char const *body,
|
||||||
|
int length);
|
||||||
|
void hs_request_respond(struct http_request_s *request,
|
||||||
|
http_response_t *response, hs_req_fn_t http_write);
|
||||||
|
void hs_request_respond_chunk(struct http_request_s *request,
|
||||||
|
http_response_t *response, hs_req_fn_t cb,
|
||||||
|
hs_req_fn_t http_write);
|
||||||
|
void hs_request_respond_chunk_end(struct http_request_s *request,
|
||||||
|
http_response_t *response,
|
||||||
|
hs_req_fn_t http_write);
|
||||||
|
void hs_request_respond_error(struct http_request_s *request, int code,
|
||||||
|
char const *message, hs_req_fn_t http_write);
|
||||||
|
|
||||||
|
#endif
|
165
external/httpserver.h-master/src/server.c
vendored
Normal file
165
external/httpserver.h-master/src/server.c
vendored
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifdef EPOLL
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
#else
|
||||||
|
#include <sys/event.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "common.h"
|
||||||
|
#include "server.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void _hs_bind_localhost(int s, struct sockaddr_in *addr, const char *ipaddr,
|
||||||
|
int port) {
|
||||||
|
addr->sin_family = AF_INET;
|
||||||
|
if (ipaddr == NULL) {
|
||||||
|
addr->sin_addr.s_addr = INADDR_ANY;
|
||||||
|
} else {
|
||||||
|
addr->sin_addr.s_addr = inet_addr(ipaddr);
|
||||||
|
}
|
||||||
|
addr->sin_port = htons(port);
|
||||||
|
int rc = bind(s, (struct sockaddr *)addr, sizeof(struct sockaddr_in));
|
||||||
|
if (rc < 0) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef KQUEUE
|
||||||
|
|
||||||
|
void _hs_add_server_sock_events(http_server_t *serv) {
|
||||||
|
struct kevent ev_set;
|
||||||
|
EV_SET(&ev_set, serv->socket, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, serv);
|
||||||
|
kevent(serv->loop, &ev_set, 1, NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hs_server_init_events(http_server_t *serv, hs_evt_cb_t unused) {
|
||||||
|
(void)unused;
|
||||||
|
|
||||||
|
serv->loop = kqueue();
|
||||||
|
struct kevent ev_set;
|
||||||
|
EV_SET(&ev_set, 1, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_SECONDS, 1, serv);
|
||||||
|
kevent(serv->loop, &ev_set, 1, NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hs_server_run_event_loop(http_server_t *serv, const char *ipaddr) {
|
||||||
|
hs_server_listen_on_addr(serv, ipaddr);
|
||||||
|
|
||||||
|
struct kevent ev_list[1];
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int nev = kevent(serv->loop, NULL, 0, ev_list, 1, NULL);
|
||||||
|
for (int i = 0; i < nev; i++) {
|
||||||
|
ev_cb_t *ev_cb = (ev_cb_t *)ev_list[i].udata;
|
||||||
|
ev_cb->handler(&ev_list[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hs_server_poll_events(http_server_t *serv) {
|
||||||
|
struct kevent ev;
|
||||||
|
struct timespec ts = {0, 0};
|
||||||
|
int nev = kevent(serv->loop, NULL, 0, &ev, 1, &ts);
|
||||||
|
if (nev <= 0)
|
||||||
|
return nev;
|
||||||
|
ev_cb_t *ev_cb = (ev_cb_t *)ev.udata;
|
||||||
|
ev_cb->handler(&ev);
|
||||||
|
return nev;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void _hs_server_init_events(http_server_t *serv, hs_evt_cb_t timer_cb) {
|
||||||
|
serv->loop = epoll_create1(0);
|
||||||
|
serv->timer_handler = timer_cb;
|
||||||
|
|
||||||
|
int tfd = timerfd_create(CLOCK_MONOTONIC, 0);
|
||||||
|
struct itimerspec ts = {};
|
||||||
|
ts.it_value.tv_sec = 1;
|
||||||
|
ts.it_interval.tv_sec = 1;
|
||||||
|
timerfd_settime(tfd, 0, &ts, NULL);
|
||||||
|
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN | EPOLLET;
|
||||||
|
ev.data.ptr = &serv->timer_handler;
|
||||||
|
epoll_ctl(serv->loop, EPOLL_CTL_ADD, tfd, &ev);
|
||||||
|
serv->timerfd = tfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _hs_add_server_sock_events(http_server_t *serv) {
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN | EPOLLET;
|
||||||
|
ev.data.ptr = serv;
|
||||||
|
epoll_ctl(serv->loop, EPOLL_CTL_ADD, serv->socket, &ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hs_server_run_event_loop(http_server_t *serv, const char *ipaddr) {
|
||||||
|
hs_server_listen_on_addr(serv, ipaddr);
|
||||||
|
struct epoll_event ev_list[1];
|
||||||
|
while (1) {
|
||||||
|
int nev = epoll_wait(serv->loop, ev_list, 1, -1);
|
||||||
|
for (int i = 0; i < nev; i++) {
|
||||||
|
ev_cb_t *ev_cb = (ev_cb_t *)ev_list[i].data.ptr;
|
||||||
|
ev_cb->handler(&ev_list[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hs_server_poll_events(http_server_t *serv) {
|
||||||
|
struct epoll_event ev;
|
||||||
|
int nev = epoll_wait(serv->loop, &ev, 1, 0);
|
||||||
|
if (nev <= 0)
|
||||||
|
return nev;
|
||||||
|
ev_cb_t *ev_cb = (ev_cb_t *)ev.data.ptr;
|
||||||
|
ev_cb->handler(&ev);
|
||||||
|
return nev;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void hs_server_listen_on_addr(http_server_t *serv, const char *ipaddr) {
|
||||||
|
// Ignore SIGPIPE. We handle these errors at the call site.
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
serv->socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
int flag = 1;
|
||||||
|
setsockopt(serv->socket, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag));
|
||||||
|
_hs_bind_localhost(serv->socket, &serv->addr, ipaddr, serv->port);
|
||||||
|
serv->len = sizeof(serv->addr);
|
||||||
|
int flags = fcntl(serv->socket, F_GETFL, 0);
|
||||||
|
fcntl(serv->socket, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
listen(serv->socket, 128);
|
||||||
|
_hs_add_server_sock_events(serv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hs_generate_date_time(char *datetime) {
|
||||||
|
time_t rawtime;
|
||||||
|
struct tm *timeinfo;
|
||||||
|
time(&rawtime);
|
||||||
|
timeinfo = gmtime(&rawtime);
|
||||||
|
strftime(datetime, 32, "%a, %d %b %Y %T GMT", timeinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
http_server_t *hs_server_init(int port, void (*handler)(http_request_t *),
|
||||||
|
hs_evt_cb_t accept_cb,
|
||||||
|
hs_evt_cb_t epoll_timer_cb) {
|
||||||
|
http_server_t *serv = (http_server_t *)malloc(sizeof(http_server_t));
|
||||||
|
assert(serv != NULL);
|
||||||
|
serv->port = port;
|
||||||
|
serv->memused = 0;
|
||||||
|
serv->handler = accept_cb;
|
||||||
|
_hs_server_init_events(serv, epoll_timer_cb);
|
||||||
|
hs_generate_date_time(serv->date);
|
||||||
|
serv->request_handler = handler;
|
||||||
|
return serv;
|
||||||
|
}
|
30
external/httpserver.h-master/src/server.h
vendored
Normal file
30
external/httpserver.h-master/src/server.h
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef HS_SERVER_H
|
||||||
|
#define HS_SERVER_H
|
||||||
|
|
||||||
|
#ifdef KQUEUE
|
||||||
|
|
||||||
|
struct kevent;
|
||||||
|
|
||||||
|
typedef void (*hs_evt_cb_t)(struct kevent *ev);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
struct epoll_event;
|
||||||
|
|
||||||
|
typedef void (*hs_evt_cb_t)(struct epoll_event *ev);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct http_request_s;
|
||||||
|
struct http_server_s;
|
||||||
|
|
||||||
|
void hs_server_listen_on_addr(struct http_server_s *serv, const char *ipaddr);
|
||||||
|
int hs_server_run_event_loop(struct http_server_s *serv, const char *ipaddr);
|
||||||
|
void hs_generate_date_time(char *datetime);
|
||||||
|
struct http_server_s *hs_server_init(int port,
|
||||||
|
void (*handler)(struct http_request_s *),
|
||||||
|
hs_evt_cb_t accept_cb,
|
||||||
|
hs_evt_cb_t timer_cb);
|
||||||
|
int hs_server_poll_events(struct http_server_s *serv);
|
||||||
|
|
||||||
|
#endif
|
50
external/httpserver.h-master/src/write_socket.c
vendored
Normal file
50
external/httpserver.h-master/src/write_socket.c
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_IMPL
|
||||||
|
#include "common.h"
|
||||||
|
#include "write_socket.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define write hs_test_write
|
||||||
|
ssize_t hs_test_write(int fd, char const *data, size_t size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Writes response bytes from the buffer out to the socket.
|
||||||
|
//
|
||||||
|
// Runs when we get a socket ready to write event or when initiating an HTTP
|
||||||
|
// response and writing to the socket for the first time. If the response is
|
||||||
|
// chunked the chunk_cb callback will be invoked signalling to the user code
|
||||||
|
// that another chunk is ready to be written.
|
||||||
|
enum hs_write_rc_e hs_write_socket(http_request_t *request) {
|
||||||
|
int bytes =
|
||||||
|
write(request->socket, request->buffer.buf + request->bytes_written,
|
||||||
|
request->buffer.length - request->bytes_written);
|
||||||
|
if (bytes > 0)
|
||||||
|
request->bytes_written += bytes;
|
||||||
|
|
||||||
|
enum hs_write_rc_e rc = HS_WRITE_RC_SUCCESS;
|
||||||
|
|
||||||
|
if (errno == EPIPE) {
|
||||||
|
rc = HS_WRITE_RC_SOCKET_ERR;
|
||||||
|
} else {
|
||||||
|
if (request->bytes_written != request->buffer.length) {
|
||||||
|
// All bytes of the body were not written and we need to wait until the
|
||||||
|
// socket is writable again to complete the write
|
||||||
|
rc = HS_WRITE_RC_CONTINUE;
|
||||||
|
} else if (HTTP_FLAG_CHECK(request->flags, HTTP_CHUNKED_RESPONSE)) {
|
||||||
|
// All bytes of the chunk were written and we need to get the next chunk
|
||||||
|
// from the application.
|
||||||
|
rc = HS_WRITE_RC_SUCCESS_CHUNK;
|
||||||
|
} else {
|
||||||
|
if (HTTP_FLAG_CHECK(request->flags, HTTP_KEEP_ALIVE)) {
|
||||||
|
rc = HS_WRITE_RC_SUCCESS;
|
||||||
|
} else {
|
||||||
|
rc = HS_WRITE_RC_SUCCESS_CLOSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
24
external/httpserver.h-master/src/write_socket.h
vendored
Normal file
24
external/httpserver.h-master/src/write_socket.h
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef HS_WRITE_SOCKET_H
|
||||||
|
#define HS_WRITE_SOCKET_H
|
||||||
|
|
||||||
|
#define HTTP_KEEP_ALIVE_TIMEOUT 120
|
||||||
|
|
||||||
|
struct http_request_s;
|
||||||
|
|
||||||
|
// Response code for hs_write_socket
|
||||||
|
enum hs_write_rc_e {
|
||||||
|
// Successful and has written the full response
|
||||||
|
HS_WRITE_RC_SUCCESS,
|
||||||
|
// Successful and has written the full chunk
|
||||||
|
HS_WRITE_RC_SUCCESS_CHUNK,
|
||||||
|
// Successful, has written the full response and the socket should be closed
|
||||||
|
HS_WRITE_RC_SUCCESS_CLOSE,
|
||||||
|
// Successful but has not written the full response, wait for write ready
|
||||||
|
HS_WRITE_RC_CONTINUE,
|
||||||
|
// Error writing to the socket
|
||||||
|
HS_WRITE_RC_SOCKET_ERR
|
||||||
|
};
|
||||||
|
|
||||||
|
enum hs_write_rc_e hs_write_socket(struct http_request_s *request);
|
||||||
|
|
||||||
|
#endif
|
34
external/httpserver.h-master/state_diagram.uml
vendored
Normal file
34
external/httpserver.h-master/state_diagram.uml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
@startuml
|
||||||
|
[*] --> Read : Connection accepted, hs_read
|
||||||
|
|
||||||
|
Read : Read the socket into the buffer
|
||||||
|
Read : until EWOULDBLOCK
|
||||||
|
Read --> ParseHttp : _hs_parse
|
||||||
|
|
||||||
|
WaitRead --> Read : hs_connection_io_cb
|
||||||
|
WaitRead : Wait for epoll/kqueue read ready
|
||||||
|
|
||||||
|
ParseHttp : Parse the new buffered input
|
||||||
|
ParseHttp --> RequestCallback : Request ready
|
||||||
|
ParseHttp --> ChunkCallback : Chunk ready
|
||||||
|
ParseHttp --> WaitRead : Chunk/Request not ready
|
||||||
|
|
||||||
|
RequestCallback : Execute server wide request handler
|
||||||
|
RequestCallback --> Write : http_respond(_chunk)
|
||||||
|
RequestCallback --> Read : http_request_read_chunk
|
||||||
|
|
||||||
|
ChunkCallback : Execute request chunk handler
|
||||||
|
ChunkCallback --> Read : http_request_read_chunk
|
||||||
|
ChunkCallback --> Write : http_respond(_chunk)
|
||||||
|
|
||||||
|
Write : Write out the buffer to the socket
|
||||||
|
Write : until done or EWOULDBLOCK
|
||||||
|
Write --> [*] : Connection: Close
|
||||||
|
Write --> WaitWrite : EWOULDBLOCK
|
||||||
|
Write --> Read : Connection Keep-Alive
|
||||||
|
Write --> ChunkCallback : Transfer-Encoding: chunked
|
||||||
|
|
||||||
|
WaitWrite : Wait for epoll/kqueue write ready
|
||||||
|
WaitWrite --> Write : hs_connection_io_cb
|
||||||
|
|
||||||
|
@enduml
|
174
external/httpserver.h-master/test/debugbreak.h
vendored
Normal file
174
external/httpserver.h-master/test/debugbreak.h
vendored
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
/* Copyright (c) 2011-2021, Scott Tsai
|
||||||
|
*
|
||||||
|
* 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,
|
||||||
|
* 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 DEBUG_BREAK_H
|
||||||
|
#define DEBUG_BREAK_H
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
|
#define debug_break __debugbreak
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEBUG_BREAK_USE_TRAP_INSTRUCTION 1
|
||||||
|
#define DEBUG_BREAK_USE_BULTIN_TRAP 2
|
||||||
|
#define DEBUG_BREAK_USE_SIGTRAP 3
|
||||||
|
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
|
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||||
|
__inline__ static void trap_instruction(void)
|
||||||
|
{
|
||||||
|
__asm__ volatile("int $0x03");
|
||||||
|
}
|
||||||
|
#elif defined(__thumb__)
|
||||||
|
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||||
|
/* FIXME: handle __THUMB_INTERWORK__ */
|
||||||
|
__attribute__((always_inline))
|
||||||
|
__inline__ static void trap_instruction(void)
|
||||||
|
{
|
||||||
|
/* See 'arm-linux-tdep.c' in GDB source.
|
||||||
|
* Both instruction sequences below work. */
|
||||||
|
#if 1
|
||||||
|
/* 'eabi_linux_thumb_le_breakpoint' */
|
||||||
|
__asm__ volatile(".inst 0xde01");
|
||||||
|
#else
|
||||||
|
/* 'eabi_linux_thumb2_le_breakpoint' */
|
||||||
|
__asm__ volatile(".inst.w 0xf7f0a000");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Known problem:
|
||||||
|
* After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB.
|
||||||
|
* 'step' would keep getting stuck on the same instruction.
|
||||||
|
*
|
||||||
|
* Workaround: use the new GDB commands 'debugbreak-step' and
|
||||||
|
* 'debugbreak-continue' that become available
|
||||||
|
* after you source the script from GDB:
|
||||||
|
*
|
||||||
|
* $ gdb -x debugbreak-gdb.py <... USUAL ARGUMENTS ...>
|
||||||
|
*
|
||||||
|
* 'debugbreak-step' would jump over the breakpoint instruction with
|
||||||
|
* roughly equivalent of:
|
||||||
|
* (gdb) set $instruction_len = 2
|
||||||
|
* (gdb) tbreak *($pc + $instruction_len)
|
||||||
|
* (gdb) jump *($pc + $instruction_len)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
#elif defined(__arm__) && !defined(__thumb__)
|
||||||
|
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||||
|
__attribute__((always_inline))
|
||||||
|
__inline__ static void trap_instruction(void)
|
||||||
|
{
|
||||||
|
/* See 'arm-linux-tdep.c' in GDB source,
|
||||||
|
* 'eabi_linux_arm_le_breakpoint' */
|
||||||
|
__asm__ volatile(".inst 0xe7f001f0");
|
||||||
|
/* Known problem:
|
||||||
|
* Same problem and workaround as Thumb mode */
|
||||||
|
}
|
||||||
|
#elif defined(__aarch64__) && defined(__APPLE__)
|
||||||
|
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_BULTIN_DEBUGTRAP
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||||
|
__attribute__((always_inline))
|
||||||
|
__inline__ static void trap_instruction(void)
|
||||||
|
{
|
||||||
|
/* See 'aarch64-tdep.c' in GDB source,
|
||||||
|
* 'aarch64_default_breakpoint' */
|
||||||
|
__asm__ volatile(".inst 0xd4200000");
|
||||||
|
}
|
||||||
|
#elif defined(__powerpc__)
|
||||||
|
/* PPC 32 or 64-bit, big or little endian */
|
||||||
|
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||||
|
__attribute__((always_inline))
|
||||||
|
__inline__ static void trap_instruction(void)
|
||||||
|
{
|
||||||
|
/* See 'rs6000-tdep.c' in GDB source,
|
||||||
|
* 'rs6000_breakpoint' */
|
||||||
|
__asm__ volatile(".4byte 0x7d821008");
|
||||||
|
|
||||||
|
/* Known problem:
|
||||||
|
* After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB.
|
||||||
|
* 'step' stuck on the same instruction ("twge r2,r2").
|
||||||
|
*
|
||||||
|
* The workaround is the same as ARM Thumb mode: use debugbreak-gdb.py
|
||||||
|
* or manually jump over the instruction. */
|
||||||
|
}
|
||||||
|
#elif defined(__riscv)
|
||||||
|
/* RISC-V 32 or 64-bit, whether the "C" extension
|
||||||
|
* for compressed, 16-bit instructions are supported or not */
|
||||||
|
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||||
|
__attribute__((always_inline))
|
||||||
|
__inline__ static void trap_instruction(void)
|
||||||
|
{
|
||||||
|
/* See 'riscv-tdep.c' in GDB source,
|
||||||
|
* 'riscv_sw_breakpoint_from_kind' */
|
||||||
|
__asm__ volatile(".4byte 0x00100073");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_SIGTRAP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DEBUG_BREAK_IMPL
|
||||||
|
#error "debugbreak.h is not supported on this target"
|
||||||
|
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_TRAP_INSTRUCTION
|
||||||
|
__attribute__((always_inline))
|
||||||
|
__inline__ static void debug_break(void)
|
||||||
|
{
|
||||||
|
trap_instruction();
|
||||||
|
}
|
||||||
|
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_DEBUGTRAP
|
||||||
|
__attribute__((always_inline))
|
||||||
|
__inline__ static void debug_break(void)
|
||||||
|
{
|
||||||
|
__builtin_debugtrap();
|
||||||
|
}
|
||||||
|
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_TRAP
|
||||||
|
__attribute__((always_inline))
|
||||||
|
__inline__ static void debug_break(void)
|
||||||
|
{
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_SIGTRAP
|
||||||
|
#include <signal.h>
|
||||||
|
__attribute__((always_inline))
|
||||||
|
__inline__ static void debug_break(void)
|
||||||
|
{
|
||||||
|
raise(SIGTRAP);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error "invalid DEBUG_BREAK_IMPL value"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ifdef _MSC_VER */
|
||||||
|
|
||||||
|
#endif /* ifndef DEBUG_BREAK_H */
|
16
external/httpserver.h-master/test/functional/CMakeLists.txt
vendored
Normal file
16
external/httpserver.h-master/test/functional/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
add_executable(functional-test-server main.c)
|
||||||
|
add_executable(functional-test-server-cpp main.cpp)
|
||||||
|
|
||||||
|
include(CheckFunctionExists)
|
||||||
|
check_function_exists(epoll_wait EPOLL)
|
||||||
|
check_function_exists(kqueue KQUEUE)
|
||||||
|
|
||||||
|
if(KQUEUE)
|
||||||
|
target_compile_definitions(functional-test-server PRIVATE "KQUEUE")
|
||||||
|
target_compile_definitions(functional-test-server-cpp PRIVATE "KQUEUE")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(EPOLL)
|
||||||
|
target_compile_definitions(functional-test-server PRIVATE "EPOLL")
|
||||||
|
target_compile_definitions(functional-test-server-cpp PRIVATE "EPOLL")
|
||||||
|
endif()
|
99
external/httpserver.h-master/test/functional/functional-test-runner
vendored
Executable file
99
external/httpserver.h-master/test/functional/functional-test-runner
vendored
Executable file
|
@ -0,0 +1,99 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
run_functional_tests() {
|
||||||
|
echo "Small Response Body:"
|
||||||
|
curl http://localhost:8080/
|
||||||
|
|
||||||
|
echo "\n\nEmpty Response:"
|
||||||
|
curl http://localhost:8080/empty
|
||||||
|
|
||||||
|
echo "\n\nEcho Body:"
|
||||||
|
curl -XPOST http://localhost:8080/echo -d'Echo test'
|
||||||
|
|
||||||
|
echo "\n\nGet Header:"
|
||||||
|
curl http://localhost:8080/host
|
||||||
|
|
||||||
|
echo "\n\nRequest Body larger than max in mem size:"
|
||||||
|
dd if=/dev/urandom of=test.dat bs=25165824 count=1 2> /dev/null
|
||||||
|
curl -H'Expect:' --data-binary @test.dat -o r1.dat http://localhost:8080/large
|
||||||
|
diff r1.dat test.dat
|
||||||
|
|
||||||
|
echo "\n\nChunked Response:"
|
||||||
|
curl http://localhost:8080/chunked
|
||||||
|
|
||||||
|
echo "\n\nChunked Response keep-alive:"
|
||||||
|
curl http://localhost:8080/chunked http://localhost:8080/chunked
|
||||||
|
|
||||||
|
echo "\n\nChunked Response close:"
|
||||||
|
curl -H'Connection: close' http://localhost:8080/chunked http://localhost:8080/chunked
|
||||||
|
|
||||||
|
echo "\n\nChunked Request keep-alive: (expect empty)"
|
||||||
|
dd if=/dev/urandom of=test.dat bs=262144 count=1 2> /dev/null
|
||||||
|
curl -H'Expect:' -H'Transfer-Encoding: chunked' -XPOST --data-binary @test.dat -o r1.dat http://localhost:8080/chunked-req -o r2.dat http://localhost:8080/chunked-req
|
||||||
|
diff r1.dat test.dat
|
||||||
|
diff r2.dat test.dat
|
||||||
|
|
||||||
|
echo "\n\nChunked Request close: (expect empty)"
|
||||||
|
curl -H'Expect:' -H'Transfer-Encoding: chunked' -XPOST --data-binary @test.dat -o r3.dat http://localhost:8080/chunked-req -o r4.dat http://localhost:8080/chunked-req
|
||||||
|
diff r3.dat test.dat
|
||||||
|
diff r4.dat test.dat
|
||||||
|
|
||||||
|
echo "\n\nPoll Server:"
|
||||||
|
curl http://localhost:8081/ &
|
||||||
|
sleep 1
|
||||||
|
curl http://localhost:8080/poll
|
||||||
|
sleep 1
|
||||||
|
curl http://localhost:8080/poll
|
||||||
|
|
||||||
|
echo "\n\nIterate Headers:"
|
||||||
|
curl -H'User-Agent: test-ua' -H'Foo-Header: foo-bar' http://localhost:8080/headers
|
||||||
|
|
||||||
|
echo "\nStress Test keep-alive:"
|
||||||
|
ab -k -c 200 -n 100000 http://127.0.0.1:8080/ > keep-alive.bench
|
||||||
|
cat keep-alive.bench | grep Complete
|
||||||
|
cat keep-alive.bench | grep Non-2xx
|
||||||
|
cat keep-alive.bench 1>&2
|
||||||
|
|
||||||
|
echo "\nStress Test close:"
|
||||||
|
ab -c 20 -n 1000 http://127.0.0.1:8080/ > connection-close.bench
|
||||||
|
cat connection-close.bench | grep Complete
|
||||||
|
cat connection-close.bench | grep Non-2xx
|
||||||
|
cat connection-close.bench 1>&2
|
||||||
|
|
||||||
|
echo "\nStress Test large body:"
|
||||||
|
dd if=/dev/urandom of=test.dat bs=7388608 count=1 2> /dev/null
|
||||||
|
ab -c 2 -n 10 -p test.dat http://127.0.0.1:8080/echo > large-file.bench
|
||||||
|
cat large-file.bench | grep Complete
|
||||||
|
cat large-file.bench | grep Non-2xx
|
||||||
|
cat large-file.bench 1>&2
|
||||||
|
rm *.dat *.bench
|
||||||
|
}
|
||||||
|
|
||||||
|
run_valgrind_functional_tests() {
|
||||||
|
valgrind --leak-check=full \
|
||||||
|
--show-leak-kinds=all \
|
||||||
|
--track-origins=yes \
|
||||||
|
--verbose \
|
||||||
|
--log-file=valgrind-log.txt \
|
||||||
|
./http-server &
|
||||||
|
serverpid=$!
|
||||||
|
sleep 3
|
||||||
|
run_functional_tests
|
||||||
|
kill $serverpid
|
||||||
|
sleep 2
|
||||||
|
cat valgrind-log.txt | cut -d = -f 5 | grep ERROR > valgrind-results.txt
|
||||||
|
cat valgrind-log.txt | cut -d = -f 5 | grep 'All heap blocks were freed' >> valgrind-results.txt
|
||||||
|
cat valgrind-log.txt 1>&2
|
||||||
|
diff valgrind-results.txt test/valgrind.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
run_base_functional_tests() {
|
||||||
|
./build/test/functional/functional-test-server$1 &
|
||||||
|
serverpid=$!
|
||||||
|
sleep 1
|
||||||
|
run_functional_tests > test-results$1.txt
|
||||||
|
kill $serverpid
|
||||||
|
diff test-results$1.txt test/functional/results.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
run_base_functional_tests $1
|
128
external/httpserver.h-master/test/functional/main.c
vendored
Normal file
128
external/httpserver.h-master/test/functional/main.c
vendored
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
#define HTTPSERVER_IMPL
|
||||||
|
#include "../../build/src/httpserver.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define RESPONSE "Hello, World!"
|
||||||
|
|
||||||
|
ssize_t hs_test_write(int fd, char const *data, size_t size) {
|
||||||
|
return write(fd, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int request_target_is(struct http_request_s* request, char const * target) {
|
||||||
|
http_string_t url = http_request_target(request);
|
||||||
|
int len = strlen(target);
|
||||||
|
return len == url.len && memcmp(url.buf, target, url.len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int chunk_count = 0;
|
||||||
|
|
||||||
|
void chunk_cb(struct http_request_s* request) {
|
||||||
|
chunk_count++;
|
||||||
|
struct http_response_s* response = http_response_init();
|
||||||
|
http_response_body(response, RESPONSE, sizeof(RESPONSE) - 1);
|
||||||
|
if (chunk_count < 3) {
|
||||||
|
http_respond_chunk(request, response, chunk_cb);
|
||||||
|
} else {
|
||||||
|
http_response_header(response, "Foo-Header", "bar");
|
||||||
|
http_respond_chunk_end(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* buf;
|
||||||
|
struct http_response_s* response;
|
||||||
|
int index;
|
||||||
|
} chunk_buf_t;
|
||||||
|
|
||||||
|
void chunk_req_cb(struct http_request_s* request) {
|
||||||
|
http_string_t str = http_request_chunk(request);
|
||||||
|
chunk_buf_t* chunk_buffer = (chunk_buf_t*)http_request_userdata(request);
|
||||||
|
if (str.len > 0) {
|
||||||
|
memcpy(chunk_buffer->buf + chunk_buffer->index, str.buf, str.len);
|
||||||
|
chunk_buffer->index += str.len;
|
||||||
|
http_request_read_chunk(request, chunk_req_cb);
|
||||||
|
} else {
|
||||||
|
http_response_body(chunk_buffer->response, chunk_buffer->buf, chunk_buffer->index);
|
||||||
|
http_respond(request, chunk_buffer->response);
|
||||||
|
free(chunk_buffer->buf);
|
||||||
|
free(chunk_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct http_server_s* poll_server;
|
||||||
|
|
||||||
|
void handle_request(struct http_request_s* request) {
|
||||||
|
chunk_count = 0;
|
||||||
|
http_request_connection(request, HTTP_AUTOMATIC);
|
||||||
|
struct http_response_s* response = http_response_init();
|
||||||
|
http_response_status(response, 200);
|
||||||
|
if (request_target_is(request, "/echo")) {
|
||||||
|
http_string_t body = http_request_body(request);
|
||||||
|
http_response_header(response, "Content-Type", "text/plain");
|
||||||
|
http_response_body(response, body.buf, body.len);
|
||||||
|
} else if (request_target_is(request, "/host")) {
|
||||||
|
http_string_t ua = http_request_header(request, "Host");
|
||||||
|
http_response_header(response, "Content-Type", "text/plain");
|
||||||
|
http_response_body(response, ua.buf, ua.len);
|
||||||
|
} else if (request_target_is(request, "/poll")) {
|
||||||
|
while (http_server_poll(poll_server) > 0);
|
||||||
|
http_response_header(response, "Content-Type", "text/plain");
|
||||||
|
http_response_body(response, RESPONSE, sizeof(RESPONSE) - 1);
|
||||||
|
} else if (request_target_is(request, "/empty")) {
|
||||||
|
// No Body
|
||||||
|
} else if (request_target_is(request, "/chunked")) {
|
||||||
|
http_response_header(response, "Content-Type", "text/plain");
|
||||||
|
http_response_body(response, RESPONSE, sizeof(RESPONSE) - 1);
|
||||||
|
http_respond_chunk(request, response, chunk_cb);
|
||||||
|
return;
|
||||||
|
} else if (request_target_is(request, "/chunked-req")) {
|
||||||
|
chunk_buf_t* chunk_buffer = (chunk_buf_t*)calloc(1, sizeof(chunk_buf_t));
|
||||||
|
chunk_buffer->buf = (char*)malloc(512 * 1024);
|
||||||
|
chunk_buffer->response = response;
|
||||||
|
http_request_set_userdata(request, chunk_buffer);
|
||||||
|
http_request_read_chunk(request, chunk_req_cb);
|
||||||
|
return;
|
||||||
|
} else if (request_target_is(request, "/large")) {
|
||||||
|
chunk_buf_t* chunk_buffer = (chunk_buf_t*)calloc(1, sizeof(chunk_buf_t));
|
||||||
|
chunk_buffer->buf = (char*)malloc(25165824);
|
||||||
|
chunk_buffer->response = response;
|
||||||
|
http_request_set_userdata(request, chunk_buffer);
|
||||||
|
http_request_read_chunk(request, chunk_req_cb);
|
||||||
|
return;
|
||||||
|
} else if (request_target_is(request, "/headers")) {
|
||||||
|
int iter = 0, i = 0;
|
||||||
|
http_string_t key, val;
|
||||||
|
char buf[512];
|
||||||
|
while (http_request_iterate_headers(request, &key, &val, &iter)) {
|
||||||
|
i += snprintf(buf + i, 512 - i, "%.*s: %.*s\n", key.len, key.buf, val.len, val.buf);
|
||||||
|
}
|
||||||
|
http_response_header(response, "Content-Type", "text/plain");
|
||||||
|
http_response_body(response, buf, i);
|
||||||
|
return http_respond(request, response);
|
||||||
|
} else {
|
||||||
|
http_response_header(response, "Content-Type", "text/plain");
|
||||||
|
http_response_body(response, RESPONSE, sizeof(RESPONSE) - 1);
|
||||||
|
}
|
||||||
|
http_respond(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct http_server_s* server;
|
||||||
|
|
||||||
|
void handle_sigterm(int signum) {
|
||||||
|
(void)signum;
|
||||||
|
free(server);
|
||||||
|
free(poll_server);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
signal(SIGTERM, handle_sigterm);
|
||||||
|
server = http_server_init(8080, handle_request);
|
||||||
|
poll_server = http_server_init(8081, handle_request);
|
||||||
|
http_server_listen_poll(poll_server);
|
||||||
|
http_server_listen(server);
|
||||||
|
}
|
1
external/httpserver.h-master/test/functional/main.cpp
vendored
Symbolic link
1
external/httpserver.h-master/test/functional/main.cpp
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
main.c
|
47
external/httpserver.h-master/test/functional/results.txt
vendored
Normal file
47
external/httpserver.h-master/test/functional/results.txt
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
Small Response Body:
|
||||||
|
Hello, World!
|
||||||
|
|
||||||
|
Empty Response:
|
||||||
|
|
||||||
|
|
||||||
|
Echo Body:
|
||||||
|
Echo test
|
||||||
|
|
||||||
|
Get Header:
|
||||||
|
localhost:8080
|
||||||
|
|
||||||
|
Request Body larger than max in mem size:
|
||||||
|
|
||||||
|
|
||||||
|
Chunked Response:
|
||||||
|
Hello, World!Hello, World!Hello, World!
|
||||||
|
|
||||||
|
Chunked Response keep-alive:
|
||||||
|
Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!
|
||||||
|
|
||||||
|
Chunked Response close:
|
||||||
|
Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!
|
||||||
|
|
||||||
|
Chunked Request keep-alive: (expect empty)
|
||||||
|
|
||||||
|
|
||||||
|
Chunked Request close: (expect empty)
|
||||||
|
|
||||||
|
|
||||||
|
Poll Server:
|
||||||
|
Hello, World!Hello, World!Hello, World!
|
||||||
|
|
||||||
|
Iterate Headers:
|
||||||
|
Host: localhost:8080
|
||||||
|
Accept: */*
|
||||||
|
User-Agent: test-ua
|
||||||
|
Foo-Header: foo-bar
|
||||||
|
|
||||||
|
Stress Test keep-alive:
|
||||||
|
Complete requests: 100000
|
||||||
|
|
||||||
|
Stress Test close:
|
||||||
|
Complete requests: 1000
|
||||||
|
|
||||||
|
Stress Test large body:
|
||||||
|
Complete requests: 10
|
3
external/httpserver.h-master/test/functional/valgrind.txt
vendored
Normal file
3
external/httpserver.h-master/test/functional/valgrind.txt
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
|
||||||
|
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
|
||||||
|
All heap blocks were freed -- no leaks are possible
|
18
external/httpserver.h-master/test/unit/CMakeLists.txt
vendored
Normal file
18
external/httpserver.h-master/test/unit/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
add_executable(
|
||||||
|
unit-test-runner
|
||||||
|
munit.c test_parser.c test_read_socket.c test_write_socket.c main.c
|
||||||
|
)
|
||||||
|
|
||||||
|
include(CheckFunctionExists)
|
||||||
|
check_function_exists(epoll_wait EPOLL)
|
||||||
|
check_function_exists(kqueue KQUEUE)
|
||||||
|
|
||||||
|
if(KQUEUE)
|
||||||
|
target_compile_definitions(unit-test-runner PRIVATE "KQUEUE")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(EPOLL)
|
||||||
|
target_compile_definitions(unit-test-runner PRIVATE "EPOLL")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(unit-test-runner PUBLIC httpsrv)
|
33
external/httpserver.h-master/test/unit/main.c
vendored
Normal file
33
external/httpserver.h-master/test/unit/main.c
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "munit.h"
|
||||||
|
#include "test_parser.h"
|
||||||
|
#include "test_read_socket.h"
|
||||||
|
#include "test_write_socket.h"
|
||||||
|
|
||||||
|
static MunitTest tests[] = {
|
||||||
|
// definition order: test-name, test-func, setup-func, teardown-func, options, params
|
||||||
|
{ (char*) "/parser/small_body/complete", test_parser_small_body_complete, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char*) "/parser/small_body/partial", test_parser_small_body_partial, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char*) "/parser/large_body", test_parser_large_body, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char*) "/parser/chunked_body", test_parser_chunked_body, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char*) "/parser/chunked_body/partial", test_parser_chunked_body_partial, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char*) "/read_socket/small_body", test_read_socket_small_body, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char*) "/read_socket/small_body/expand_buffer", test_read_socket_small_body_expand_buffer, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char*) "/read_socket/large_body", test_read_socket_large_body, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char*) "/write_socket/partial", test_write_socket_partial, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
// end
|
||||||
|
{ NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const MunitSuite test_suite = {
|
||||||
|
(char*) "/hs", // Test suite name prefix
|
||||||
|
tests, // Tests
|
||||||
|
NULL, // Sub test suites
|
||||||
|
1, // Execution mode (normal)
|
||||||
|
MUNIT_SUITE_OPTION_NONE // Options
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
return munit_suite_main(&test_suite, (void*) "httpserver.h", argc, argv);
|
||||||
|
}
|
2055
external/httpserver.h-master/test/unit/munit.c
vendored
Normal file
2055
external/httpserver.h-master/test/unit/munit.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
535
external/httpserver.h-master/test/unit/munit.h
vendored
Normal file
535
external/httpserver.h-master/test/unit/munit.h
vendored
Normal file
|
@ -0,0 +1,535 @@
|
||||||
|
/* µnit Testing Framework
|
||||||
|
* Copyright (c) 2013-2017 Evan Nemerson <evan@nemerson.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(MUNIT_H)
|
||||||
|
#define MUNIT_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define MUNIT_VERSION(major, minor, revision) \
|
||||||
|
(((major) << 16) | ((minor) << 8) | (revision))
|
||||||
|
|
||||||
|
#define MUNIT_CURRENT_VERSION MUNIT_VERSION(0, 4, 1)
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||||
|
# define munit_int8_t __int8
|
||||||
|
# define munit_uint8_t unsigned __int8
|
||||||
|
# define munit_int16_t __int16
|
||||||
|
# define munit_uint16_t unsigned __int16
|
||||||
|
# define munit_int32_t __int32
|
||||||
|
# define munit_uint32_t unsigned __int32
|
||||||
|
# define munit_int64_t __int64
|
||||||
|
# define munit_uint64_t unsigned __int64
|
||||||
|
#else
|
||||||
|
# include <stdint.h>
|
||||||
|
# define munit_int8_t int8_t
|
||||||
|
# define munit_uint8_t uint8_t
|
||||||
|
# define munit_int16_t int16_t
|
||||||
|
# define munit_uint16_t uint16_t
|
||||||
|
# define munit_int32_t int32_t
|
||||||
|
# define munit_uint32_t uint32_t
|
||||||
|
# define munit_int64_t int64_t
|
||||||
|
# define munit_uint64_t uint64_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1800)
|
||||||
|
# if !defined(PRIi8)
|
||||||
|
# define PRIi8 "i"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIi16)
|
||||||
|
# define PRIi16 "i"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIi32)
|
||||||
|
# define PRIi32 "i"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIi64)
|
||||||
|
# define PRIi64 "I64i"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRId8)
|
||||||
|
# define PRId8 "d"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRId16)
|
||||||
|
# define PRId16 "d"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRId32)
|
||||||
|
# define PRId32 "d"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRId64)
|
||||||
|
# define PRId64 "I64d"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIx8)
|
||||||
|
# define PRIx8 "x"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIx16)
|
||||||
|
# define PRIx16 "x"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIx32)
|
||||||
|
# define PRIx32 "x"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIx64)
|
||||||
|
# define PRIx64 "I64x"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIu8)
|
||||||
|
# define PRIu8 "u"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIu16)
|
||||||
|
# define PRIu16 "u"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIu32)
|
||||||
|
# define PRIu32 "u"
|
||||||
|
# endif
|
||||||
|
# if !defined(PRIu64)
|
||||||
|
# define PRIu64 "I64u"
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# include <inttypes.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(munit_bool)
|
||||||
|
# if defined(bool)
|
||||||
|
# define munit_bool bool
|
||||||
|
# elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||||
|
# define munit_bool _Bool
|
||||||
|
# else
|
||||||
|
# define munit_bool int
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
# define MUNIT_LIKELY(expr) (__builtin_expect ((expr), 1))
|
||||||
|
# define MUNIT_UNLIKELY(expr) (__builtin_expect ((expr), 0))
|
||||||
|
# define MUNIT_UNUSED __attribute__((__unused__))
|
||||||
|
#else
|
||||||
|
# define MUNIT_LIKELY(expr) (expr)
|
||||||
|
# define MUNIT_UNLIKELY(expr) (expr)
|
||||||
|
# define MUNIT_UNUSED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__PGI)
|
||||||
|
# define MUNIT_ARRAY_PARAM(name) name
|
||||||
|
#else
|
||||||
|
# define MUNIT_ARRAY_PARAM(name)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
# define MUNIT_SIZE_MODIFIER "z"
|
||||||
|
# define MUNIT_CHAR_MODIFIER "hh"
|
||||||
|
# define MUNIT_SHORT_MODIFIER "h"
|
||||||
|
#else
|
||||||
|
# if defined(_M_X64) || defined(__amd64__)
|
||||||
|
# define MUNIT_SIZE_MODIFIER "I64"
|
||||||
|
# else
|
||||||
|
# define MUNIT_SIZE_MODIFIER ""
|
||||||
|
# endif
|
||||||
|
# define MUNIT_CHAR_MODIFIER ""
|
||||||
|
# define MUNIT_SHORT_MODIFIER ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||||
|
# define MUNIT_NO_RETURN _Noreturn
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# define MUNIT_NO_RETURN __attribute__((__noreturn__))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
# define MUNIT_NO_RETURN __declspec(noreturn)
|
||||||
|
#else
|
||||||
|
# define MUNIT_NO_RETURN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER >= 1500)
|
||||||
|
# define MUNIT_PUSH_DISABLE_MSVC_C4127_ __pragma(warning(push)) __pragma(warning(disable:4127))
|
||||||
|
# define MUNIT_POP_DISABLE_MSVC_C4127_ __pragma(warning(pop))
|
||||||
|
#else
|
||||||
|
# define MUNIT_PUSH_DISABLE_MSVC_C4127_
|
||||||
|
# define MUNIT_POP_DISABLE_MSVC_C4127_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MUNIT_LOG_DEBUG,
|
||||||
|
MUNIT_LOG_INFO,
|
||||||
|
MUNIT_LOG_WARNING,
|
||||||
|
MUNIT_LOG_ERROR
|
||||||
|
} MunitLogLevel;
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && !defined(__MINGW32__)
|
||||||
|
# define MUNIT_PRINTF(string_index, first_to_check) __attribute__((format (printf, string_index, first_to_check)))
|
||||||
|
#else
|
||||||
|
# define MUNIT_PRINTF(string_index, first_to_check)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MUNIT_PRINTF(4, 5)
|
||||||
|
void munit_logf_ex(MunitLogLevel level, const char* filename, int line, const char* format, ...);
|
||||||
|
|
||||||
|
#define munit_logf(level, format, ...) \
|
||||||
|
munit_logf_ex(level, __FILE__, __LINE__, format, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define munit_log(level, msg) \
|
||||||
|
munit_logf(level, "%s", msg)
|
||||||
|
|
||||||
|
MUNIT_NO_RETURN
|
||||||
|
MUNIT_PRINTF(3, 4)
|
||||||
|
void munit_errorf_ex(const char* filename, int line, const char* format, ...);
|
||||||
|
|
||||||
|
#define munit_errorf(format, ...) \
|
||||||
|
munit_errorf_ex(__FILE__, __LINE__, format, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define munit_error(msg) \
|
||||||
|
munit_errorf("%s", msg)
|
||||||
|
|
||||||
|
#define munit_assert(expr) \
|
||||||
|
do { \
|
||||||
|
if (!MUNIT_LIKELY(expr)) { \
|
||||||
|
munit_error("assertion failed: " #expr); \
|
||||||
|
} \
|
||||||
|
MUNIT_PUSH_DISABLE_MSVC_C4127_ \
|
||||||
|
} while (0) \
|
||||||
|
MUNIT_POP_DISABLE_MSVC_C4127_
|
||||||
|
|
||||||
|
#define munit_assert_true(expr) \
|
||||||
|
do { \
|
||||||
|
if (!MUNIT_LIKELY(expr)) { \
|
||||||
|
munit_error("assertion failed: " #expr " is not true"); \
|
||||||
|
} \
|
||||||
|
MUNIT_PUSH_DISABLE_MSVC_C4127_ \
|
||||||
|
} while (0) \
|
||||||
|
MUNIT_POP_DISABLE_MSVC_C4127_
|
||||||
|
|
||||||
|
#define munit_assert_false(expr) \
|
||||||
|
do { \
|
||||||
|
if (!MUNIT_LIKELY(!(expr))) { \
|
||||||
|
munit_error("assertion failed: " #expr " is not false"); \
|
||||||
|
} \
|
||||||
|
MUNIT_PUSH_DISABLE_MSVC_C4127_ \
|
||||||
|
} while (0) \
|
||||||
|
MUNIT_POP_DISABLE_MSVC_C4127_
|
||||||
|
|
||||||
|
#define munit_assert_type_full(prefix, suffix, T, fmt, a, op, b) \
|
||||||
|
do { \
|
||||||
|
T munit_tmp_a_ = (a); \
|
||||||
|
T munit_tmp_b_ = (b); \
|
||||||
|
if (!(munit_tmp_a_ op munit_tmp_b_)) { \
|
||||||
|
munit_errorf("assertion failed: %s %s %s (" prefix "%" fmt suffix " %s " prefix "%" fmt suffix ")", \
|
||||||
|
#a, #op, #b, munit_tmp_a_, #op, munit_tmp_b_); \
|
||||||
|
} \
|
||||||
|
MUNIT_PUSH_DISABLE_MSVC_C4127_ \
|
||||||
|
} while (0) \
|
||||||
|
MUNIT_POP_DISABLE_MSVC_C4127_
|
||||||
|
|
||||||
|
#define munit_assert_type(T, fmt, a, op, b) \
|
||||||
|
munit_assert_type_full("", "", T, fmt, a, op, b)
|
||||||
|
|
||||||
|
#define munit_assert_char(a, op, b) \
|
||||||
|
munit_assert_type_full("'\\x", "'", char, "02" MUNIT_CHAR_MODIFIER "x", a, op, b)
|
||||||
|
#define munit_assert_uchar(a, op, b) \
|
||||||
|
munit_assert_type_full("'\\x", "'", unsigned char, "02" MUNIT_CHAR_MODIFIER "x", a, op, b)
|
||||||
|
#define munit_assert_short(a, op, b) \
|
||||||
|
munit_assert_type(short, MUNIT_SHORT_MODIFIER "d", a, op, b)
|
||||||
|
#define munit_assert_ushort(a, op, b) \
|
||||||
|
munit_assert_type(unsigned short, MUNIT_SHORT_MODIFIER "u", a, op, b)
|
||||||
|
#define munit_assert_int(a, op, b) \
|
||||||
|
munit_assert_type(int, "d", a, op, b)
|
||||||
|
#define munit_assert_uint(a, op, b) \
|
||||||
|
munit_assert_type(unsigned int, "u", a, op, b)
|
||||||
|
#define munit_assert_long(a, op, b) \
|
||||||
|
munit_assert_type(long int, "ld", a, op, b)
|
||||||
|
#define munit_assert_ulong(a, op, b) \
|
||||||
|
munit_assert_type(unsigned long int, "lu", a, op, b)
|
||||||
|
#define munit_assert_llong(a, op, b) \
|
||||||
|
munit_assert_type(long long int, "lld", a, op, b)
|
||||||
|
#define munit_assert_ullong(a, op, b) \
|
||||||
|
munit_assert_type(unsigned long long int, "llu", a, op, b)
|
||||||
|
|
||||||
|
#define munit_assert_size(a, op, b) \
|
||||||
|
munit_assert_type(size_t, MUNIT_SIZE_MODIFIER "u", a, op, b)
|
||||||
|
|
||||||
|
#define munit_assert_float(a, op, b) \
|
||||||
|
munit_assert_type(float, "f", a, op, b)
|
||||||
|
#define munit_assert_double(a, op, b) \
|
||||||
|
munit_assert_type(double, "g", a, op, b)
|
||||||
|
#define munit_assert_ptr(a, op, b) \
|
||||||
|
munit_assert_type(const void*, "p", a, op, b)
|
||||||
|
|
||||||
|
#define munit_assert_int8(a, op, b) \
|
||||||
|
munit_assert_type(munit_int8_t, PRIi8, a, op, b)
|
||||||
|
#define munit_assert_uint8(a, op, b) \
|
||||||
|
munit_assert_type(munit_uint8_t, PRIu8, a, op, b)
|
||||||
|
#define munit_assert_int16(a, op, b) \
|
||||||
|
munit_assert_type(munit_int16_t, PRIi16, a, op, b)
|
||||||
|
#define munit_assert_uint16(a, op, b) \
|
||||||
|
munit_assert_type(munit_uint16_t, PRIu16, a, op, b)
|
||||||
|
#define munit_assert_int32(a, op, b) \
|
||||||
|
munit_assert_type(munit_int32_t, PRIi32, a, op, b)
|
||||||
|
#define munit_assert_uint32(a, op, b) \
|
||||||
|
munit_assert_type(munit_uint32_t, PRIu32, a, op, b)
|
||||||
|
#define munit_assert_int64(a, op, b) \
|
||||||
|
munit_assert_type(munit_int64_t, PRIi64, a, op, b)
|
||||||
|
#define munit_assert_uint64(a, op, b) \
|
||||||
|
munit_assert_type(munit_uint64_t, PRIu64, a, op, b)
|
||||||
|
|
||||||
|
#define munit_assert_double_equal(a, b, precision) \
|
||||||
|
do { \
|
||||||
|
const double munit_tmp_a_ = (a); \
|
||||||
|
const double munit_tmp_b_ = (b); \
|
||||||
|
const double munit_tmp_diff_ = ((munit_tmp_a_ - munit_tmp_b_) < 0) ? \
|
||||||
|
-(munit_tmp_a_ - munit_tmp_b_) : \
|
||||||
|
(munit_tmp_a_ - munit_tmp_b_); \
|
||||||
|
if (MUNIT_UNLIKELY(munit_tmp_diff_ > 1e-##precision)) { \
|
||||||
|
munit_errorf("assertion failed: %s == %s (%0." #precision "g == %0." #precision "g)", \
|
||||||
|
#a, #b, munit_tmp_a_, munit_tmp_b_); \
|
||||||
|
} \
|
||||||
|
MUNIT_PUSH_DISABLE_MSVC_C4127_ \
|
||||||
|
} while (0) \
|
||||||
|
MUNIT_POP_DISABLE_MSVC_C4127_
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#define munit_assert_string_equal(a, b) \
|
||||||
|
do { \
|
||||||
|
const char* munit_tmp_a_ = a; \
|
||||||
|
const char* munit_tmp_b_ = b; \
|
||||||
|
if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) != 0)) { \
|
||||||
|
munit_errorf("assertion failed: string %s == %s (\"%s\" == \"%s\")", \
|
||||||
|
#a, #b, munit_tmp_a_, munit_tmp_b_); \
|
||||||
|
} \
|
||||||
|
MUNIT_PUSH_DISABLE_MSVC_C4127_ \
|
||||||
|
} while (0) \
|
||||||
|
MUNIT_POP_DISABLE_MSVC_C4127_
|
||||||
|
|
||||||
|
#define munit_assert_string_not_equal(a, b) \
|
||||||
|
do { \
|
||||||
|
const char* munit_tmp_a_ = a; \
|
||||||
|
const char* munit_tmp_b_ = b; \
|
||||||
|
if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) == 0)) { \
|
||||||
|
munit_errorf("assertion failed: string %s != %s (\"%s\" == \"%s\")", \
|
||||||
|
#a, #b, munit_tmp_a_, munit_tmp_b_); \
|
||||||
|
} \
|
||||||
|
MUNIT_PUSH_DISABLE_MSVC_C4127_ \
|
||||||
|
} while (0) \
|
||||||
|
MUNIT_POP_DISABLE_MSVC_C4127_
|
||||||
|
|
||||||
|
#define munit_assert_memory_equal(size, a, b) \
|
||||||
|
do { \
|
||||||
|
const unsigned char* munit_tmp_a_ = (const unsigned char*) (a); \
|
||||||
|
const unsigned char* munit_tmp_b_ = (const unsigned char*) (b); \
|
||||||
|
const size_t munit_tmp_size_ = (size); \
|
||||||
|
if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) != 0) { \
|
||||||
|
size_t munit_tmp_pos_; \
|
||||||
|
for (munit_tmp_pos_ = 0 ; munit_tmp_pos_ < munit_tmp_size_ ; munit_tmp_pos_++) { \
|
||||||
|
if (munit_tmp_a_[munit_tmp_pos_] != munit_tmp_b_[munit_tmp_pos_]) { \
|
||||||
|
munit_errorf("assertion failed: memory %s == %s, at offset %" MUNIT_SIZE_MODIFIER "u", \
|
||||||
|
#a, #b, munit_tmp_pos_); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
MUNIT_PUSH_DISABLE_MSVC_C4127_ \
|
||||||
|
} while (0) \
|
||||||
|
MUNIT_POP_DISABLE_MSVC_C4127_
|
||||||
|
|
||||||
|
#define munit_assert_memory_not_equal(size, a, b) \
|
||||||
|
do { \
|
||||||
|
const unsigned char* munit_tmp_a_ = (const unsigned char*) (a); \
|
||||||
|
const unsigned char* munit_tmp_b_ = (const unsigned char*) (b); \
|
||||||
|
const size_t munit_tmp_size_ = (size); \
|
||||||
|
if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) == 0) { \
|
||||||
|
munit_errorf("assertion failed: memory %s != %s (%zu bytes)", \
|
||||||
|
#a, #b, munit_tmp_size_); \
|
||||||
|
} \
|
||||||
|
MUNIT_PUSH_DISABLE_MSVC_C4127_ \
|
||||||
|
} while (0) \
|
||||||
|
MUNIT_POP_DISABLE_MSVC_C4127_
|
||||||
|
|
||||||
|
#define munit_assert_ptr_equal(a, b) \
|
||||||
|
munit_assert_ptr(a, ==, b)
|
||||||
|
#define munit_assert_ptr_not_equal(a, b) \
|
||||||
|
munit_assert_ptr(a, !=, b)
|
||||||
|
#define munit_assert_null(ptr) \
|
||||||
|
munit_assert_ptr(ptr, ==, NULL)
|
||||||
|
#define munit_assert_not_null(ptr) \
|
||||||
|
munit_assert_ptr(ptr, !=, NULL)
|
||||||
|
#define munit_assert_ptr_null(ptr) \
|
||||||
|
munit_assert_ptr(ptr, ==, NULL)
|
||||||
|
#define munit_assert_ptr_not_null(ptr) \
|
||||||
|
munit_assert_ptr(ptr, !=, NULL)
|
||||||
|
|
||||||
|
/*** Memory allocation ***/
|
||||||
|
|
||||||
|
void* munit_malloc_ex(const char* filename, int line, size_t size);
|
||||||
|
|
||||||
|
#define munit_malloc(size) \
|
||||||
|
munit_malloc_ex(__FILE__, __LINE__, (size))
|
||||||
|
|
||||||
|
#define munit_new(type) \
|
||||||
|
((type*) munit_malloc(sizeof(type)))
|
||||||
|
|
||||||
|
#define munit_calloc(nmemb, size) \
|
||||||
|
munit_malloc((nmemb) * (size))
|
||||||
|
|
||||||
|
#define munit_newa(type, nmemb) \
|
||||||
|
((type*) munit_calloc((nmemb), sizeof(type)))
|
||||||
|
|
||||||
|
/*** Random number generation ***/
|
||||||
|
|
||||||
|
void munit_rand_seed(munit_uint32_t seed);
|
||||||
|
munit_uint32_t munit_rand_uint32(void);
|
||||||
|
int munit_rand_int_range(int min, int max);
|
||||||
|
double munit_rand_double(void);
|
||||||
|
void munit_rand_memory(size_t size, munit_uint8_t buffer[MUNIT_ARRAY_PARAM(size)]);
|
||||||
|
|
||||||
|
/*** Tests and Suites ***/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/* Test successful */
|
||||||
|
MUNIT_OK,
|
||||||
|
/* Test failed */
|
||||||
|
MUNIT_FAIL,
|
||||||
|
/* Test was skipped */
|
||||||
|
MUNIT_SKIP,
|
||||||
|
/* Test failed due to circumstances not intended to be tested
|
||||||
|
* (things like network errors, invalid parameter value, failure to
|
||||||
|
* allocate memory in the test harness, etc.). */
|
||||||
|
MUNIT_ERROR
|
||||||
|
} MunitResult;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* name;
|
||||||
|
char** values;
|
||||||
|
} MunitParameterEnum;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* name;
|
||||||
|
char* value;
|
||||||
|
} MunitParameter;
|
||||||
|
|
||||||
|
const char* munit_parameters_get(const MunitParameter params[], const char* key);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MUNIT_TEST_OPTION_NONE = 0,
|
||||||
|
MUNIT_TEST_OPTION_SINGLE_ITERATION = 1 << 0,
|
||||||
|
MUNIT_TEST_OPTION_TODO = 1 << 1
|
||||||
|
} MunitTestOptions;
|
||||||
|
|
||||||
|
typedef MunitResult (* MunitTestFunc)(const MunitParameter params[], void* user_data_or_fixture);
|
||||||
|
typedef void* (* MunitTestSetup)(const MunitParameter params[], void* user_data);
|
||||||
|
typedef void (* MunitTestTearDown)(void* fixture);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* name;
|
||||||
|
MunitTestFunc test;
|
||||||
|
MunitTestSetup setup;
|
||||||
|
MunitTestTearDown tear_down;
|
||||||
|
MunitTestOptions options;
|
||||||
|
MunitParameterEnum* parameters;
|
||||||
|
} MunitTest;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
MUNIT_SUITE_OPTION_NONE = 0
|
||||||
|
} MunitSuiteOptions;
|
||||||
|
|
||||||
|
typedef struct MunitSuite_ MunitSuite;
|
||||||
|
|
||||||
|
struct MunitSuite_ {
|
||||||
|
char* prefix;
|
||||||
|
MunitTest* tests;
|
||||||
|
MunitSuite* suites;
|
||||||
|
unsigned int iterations;
|
||||||
|
MunitSuiteOptions options;
|
||||||
|
};
|
||||||
|
|
||||||
|
int munit_suite_main(const MunitSuite* suite, void* user_data, int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)]);
|
||||||
|
|
||||||
|
/* Note: I'm not very happy with this API; it's likely to change if I
|
||||||
|
* figure out something better. Suggestions welcome. */
|
||||||
|
|
||||||
|
typedef struct MunitArgument_ MunitArgument;
|
||||||
|
|
||||||
|
struct MunitArgument_ {
|
||||||
|
char* name;
|
||||||
|
munit_bool (* parse_argument)(const MunitSuite* suite, void* user_data, int* arg, int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)]);
|
||||||
|
void (* write_help)(const MunitArgument* argument, void* user_data);
|
||||||
|
};
|
||||||
|
|
||||||
|
int munit_suite_main_custom(const MunitSuite* suite,
|
||||||
|
void* user_data,
|
||||||
|
int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)],
|
||||||
|
const MunitArgument arguments[]);
|
||||||
|
|
||||||
|
#if defined(MUNIT_ENABLE_ASSERT_ALIASES)
|
||||||
|
|
||||||
|
#define assert_true(expr) munit_assert_true(expr)
|
||||||
|
#define assert_false(expr) munit_assert_false(expr)
|
||||||
|
#define assert_char(a, op, b) munit_assert_char(a, op, b)
|
||||||
|
#define assert_uchar(a, op, b) munit_assert_uchar(a, op, b)
|
||||||
|
#define assert_short(a, op, b) munit_assert_short(a, op, b)
|
||||||
|
#define assert_ushort(a, op, b) munit_assert_ushort(a, op, b)
|
||||||
|
#define assert_int(a, op, b) munit_assert_int(a, op, b)
|
||||||
|
#define assert_uint(a, op, b) munit_assert_uint(a, op, b)
|
||||||
|
#define assert_long(a, op, b) munit_assert_long(a, op, b)
|
||||||
|
#define assert_ulong(a, op, b) munit_assert_ulong(a, op, b)
|
||||||
|
#define assert_llong(a, op, b) munit_assert_llong(a, op, b)
|
||||||
|
#define assert_ullong(a, op, b) munit_assert_ullong(a, op, b)
|
||||||
|
#define assert_size(a, op, b) munit_assert_size(a, op, b)
|
||||||
|
#define assert_float(a, op, b) munit_assert_float(a, op, b)
|
||||||
|
#define assert_double(a, op, b) munit_assert_double(a, op, b)
|
||||||
|
#define assert_ptr(a, op, b) munit_assert_ptr(a, op, b)
|
||||||
|
|
||||||
|
#define assert_int8(a, op, b) munit_assert_int8(a, op, b)
|
||||||
|
#define assert_uint8(a, op, b) munit_assert_uint8(a, op, b)
|
||||||
|
#define assert_int16(a, op, b) munit_assert_int16(a, op, b)
|
||||||
|
#define assert_uint16(a, op, b) munit_assert_uint16(a, op, b)
|
||||||
|
#define assert_int32(a, op, b) munit_assert_int32(a, op, b)
|
||||||
|
#define assert_uint32(a, op, b) munit_assert_uint32(a, op, b)
|
||||||
|
#define assert_int64(a, op, b) munit_assert_int64(a, op, b)
|
||||||
|
#define assert_uint64(a, op, b) munit_assert_uint64(a, op, b)
|
||||||
|
|
||||||
|
#define assert_double_equal(a, b, precision) munit_assert_double_equal(a, b, precision)
|
||||||
|
#define assert_string_equal(a, b) munit_assert_string_equal(a, b)
|
||||||
|
#define assert_string_not_equal(a, b) munit_assert_string_not_equal(a, b)
|
||||||
|
#define assert_memory_equal(size, a, b) munit_assert_memory_equal(size, a, b)
|
||||||
|
#define assert_memory_not_equal(size, a, b) munit_assert_memory_not_equal(size, a, b)
|
||||||
|
#define assert_ptr_equal(a, b) munit_assert_ptr_equal(a, b)
|
||||||
|
#define assert_ptr_not_equal(a, b) munit_assert_ptr_not_equal(a, b)
|
||||||
|
#define assert_ptr_null(ptr) munit_assert_null_equal(ptr)
|
||||||
|
#define assert_ptr_not_null(ptr) munit_assert_not_null(ptr)
|
||||||
|
|
||||||
|
#define assert_null(ptr) munit_assert_null(ptr)
|
||||||
|
#define assert_not_null(ptr) munit_assert_not_null(ptr)
|
||||||
|
|
||||||
|
#endif /* defined(MUNIT_ENABLE_ASSERT_ALIASES) */
|
||||||
|
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !defined(MUNIT_H) */
|
||||||
|
|
||||||
|
#if defined(MUNIT_ENABLE_ASSERT_ALIASES)
|
||||||
|
# if defined(assert)
|
||||||
|
# undef assert
|
||||||
|
# endif
|
||||||
|
# define assert(expr) munit_assert(expr)
|
||||||
|
#endif
|
5
external/httpserver.h-master/test/unit/read_socket.txt
vendored
Normal file
5
external/httpserver.h-master/test/unit/read_socket.txt
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
GET /foo HTTP/1.1
|
||||||
|
Host: www.jeremycw.com
|
||||||
|
Content-Length: 13
|
||||||
|
|
||||||
|
Hello, World!
|
264
external/httpserver.h-master/test/unit/test_parser.c
vendored
Normal file
264
external/httpserver.h-master/test/unit/test_parser.c
vendored
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
#include "munit.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
void setup_buffer_and_parser(struct hsh_parser_s *parser,
|
||||||
|
struct hsh_buffer_s *buffer, char const *req_str) {
|
||||||
|
// setup parser
|
||||||
|
hsh_parser_init(parser);
|
||||||
|
|
||||||
|
// setup input buffer
|
||||||
|
int len = strlen(req_str);
|
||||||
|
buffer->buf = malloc(1024);
|
||||||
|
buffer->capacity = 1024;
|
||||||
|
memcpy(buffer->buf, req_str, len);
|
||||||
|
buffer->length = len;
|
||||||
|
buffer->sequence_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
MunitResult test_parser_small_body_complete(const MunitParameter params[],
|
||||||
|
void *data) {
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
char const *request_string =
|
||||||
|
"GET /foo HTTP/1.1\r\nHost: www.jeremycw.com\r\nContent-Length: "
|
||||||
|
"16\r\n\r\naaaaaaaaaaaaaaaa";
|
||||||
|
|
||||||
|
struct hsh_parser_s parser = {0};
|
||||||
|
struct hsh_buffer_s buffer = {0};
|
||||||
|
setup_buffer_and_parser(&parser, &buffer, request_string);
|
||||||
|
|
||||||
|
enum hsh_token_e expected_types[] = {
|
||||||
|
HSH_TOK_METHOD, HSH_TOK_TARGET, HSH_TOK_VERSION,
|
||||||
|
HSH_TOK_HEADER_KEY, HSH_TOK_HEADER_VALUE, HSH_TOK_HEADER_KEY,
|
||||||
|
HSH_TOK_HEADER_VALUE, HSH_TOK_HEADERS_DONE, HSH_TOK_BODY};
|
||||||
|
|
||||||
|
char const *expected_values[] = {"GET",
|
||||||
|
"/foo",
|
||||||
|
"HTTP/1.1",
|
||||||
|
"Host",
|
||||||
|
"www.jeremycw.com",
|
||||||
|
"Content-Length",
|
||||||
|
"16",
|
||||||
|
"",
|
||||||
|
"aaaaaaaaaaaaaaaa"};
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
struct hsh_token_s out;
|
||||||
|
while ((out = hsh_parser_exec(&parser, &buffer, 1024)).type != HSH_TOK_NONE) {
|
||||||
|
munit_assert_memory_equal(out.len, expected_values[i],
|
||||||
|
&buffer.buf[out.index]);
|
||||||
|
munit_assert(expected_types[i] == out.type);
|
||||||
|
if (out.type == HSH_TOK_HEADERS_DONE) {
|
||||||
|
munit_assert(out.flags == 0);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
munit_assert(i == 9);
|
||||||
|
|
||||||
|
free(buffer.buf);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
MunitResult test_parser_small_body_partial(const MunitParameter params[],
|
||||||
|
void *data) {
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
char const *request_string =
|
||||||
|
"GET /foo HTTP/1.1\r\nHost: www.jeremycw.com\r\nContent-Length: "
|
||||||
|
"16\r\n\r\naaaaaaaa";
|
||||||
|
|
||||||
|
struct hsh_parser_s parser = {0};
|
||||||
|
struct hsh_buffer_s buffer = {0};
|
||||||
|
setup_buffer_and_parser(&parser, &buffer, request_string);
|
||||||
|
|
||||||
|
enum hsh_token_e expected_types[] = {
|
||||||
|
HSH_TOK_METHOD, HSH_TOK_TARGET, HSH_TOK_VERSION,
|
||||||
|
HSH_TOK_HEADER_KEY, HSH_TOK_HEADER_VALUE, HSH_TOK_HEADER_KEY,
|
||||||
|
HSH_TOK_HEADER_VALUE, HSH_TOK_HEADERS_DONE};
|
||||||
|
|
||||||
|
char const *expected_values[] = {
|
||||||
|
"GET", "/foo", "HTTP/1.1", "Host", "www.jeremycw.com", "Content-Length",
|
||||||
|
"16", ""};
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
struct hsh_token_s out;
|
||||||
|
while ((out = hsh_parser_exec(&parser, &buffer, 1024)).type != HSH_TOK_NONE) {
|
||||||
|
munit_assert_memory_equal(out.len, expected_values[i],
|
||||||
|
&buffer.buf[out.index]);
|
||||||
|
munit_assert(expected_types[i] == out.type);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
munit_assert(i == 8);
|
||||||
|
|
||||||
|
memcpy(buffer.buf + buffer.length, "aaaaaaaa", 8);
|
||||||
|
buffer.length += 8;
|
||||||
|
buffer.sequence_id++;
|
||||||
|
|
||||||
|
out = hsh_parser_exec(&parser, &buffer, 1024);
|
||||||
|
|
||||||
|
munit_assert(out.type == HSH_TOK_BODY);
|
||||||
|
munit_assert_memory_equal(out.len, "aaaaaaaaaaaaaaaa",
|
||||||
|
&buffer.buf[out.index]);
|
||||||
|
|
||||||
|
free(buffer.buf);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
MunitResult test_parser_large_body(const MunitParameter params[], void *data) {
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
char const *request_string =
|
||||||
|
"GET /foo HTTP/1.1\r\nHost: www.jeremycw.com\r\nContent-Length: "
|
||||||
|
"16\r\n\r\naaaaaaaa";
|
||||||
|
|
||||||
|
struct hsh_parser_s parser = {0};
|
||||||
|
struct hsh_buffer_s buffer = {0};
|
||||||
|
setup_buffer_and_parser(&parser, &buffer, request_string);
|
||||||
|
|
||||||
|
enum hsh_token_e expected_types[] = {
|
||||||
|
HSH_TOK_METHOD, HSH_TOK_TARGET, HSH_TOK_VERSION,
|
||||||
|
HSH_TOK_HEADER_KEY, HSH_TOK_HEADER_VALUE, HSH_TOK_HEADER_KEY,
|
||||||
|
HSH_TOK_HEADER_VALUE, HSH_TOK_HEADERS_DONE, HSH_TOK_BODY};
|
||||||
|
|
||||||
|
char const *expected_values[] = {
|
||||||
|
"GET", "/foo", "HTTP/1.1", "Host", "www.jeremycw.com", "Content-Length",
|
||||||
|
"16", "", "aaaaaaaa"};
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
struct hsh_token_s out;
|
||||||
|
int max_buf_capacity = strlen(request_string);
|
||||||
|
|
||||||
|
while ((out = hsh_parser_exec(&parser, &buffer, max_buf_capacity)).type !=
|
||||||
|
HSH_TOK_NONE) {
|
||||||
|
munit_assert_memory_equal(out.len, expected_values[i],
|
||||||
|
&buffer.buf[out.index]);
|
||||||
|
munit_assert(expected_types[i] == out.type);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
munit_assert(i == 9);
|
||||||
|
|
||||||
|
memcpy(buffer.buf + buffer.length, "bbbbbbbb", 8);
|
||||||
|
buffer.length += 8;
|
||||||
|
buffer.sequence_id++;
|
||||||
|
|
||||||
|
out = hsh_parser_exec(&parser, &buffer, max_buf_capacity);
|
||||||
|
|
||||||
|
munit_assert(out.type == HSH_TOK_BODY);
|
||||||
|
munit_assert_memory_equal(out.len, "bbbbbbbb", &buffer.buf[out.index]);
|
||||||
|
|
||||||
|
free(buffer.buf);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
MunitResult test_parser_chunked_body(const MunitParameter params[],
|
||||||
|
void *data) {
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
char const *request_string =
|
||||||
|
"POST /chunked/test HTTP/1.0\r\nHost: "
|
||||||
|
"www.jeremycw.com\r\nTransfer-Encoding: "
|
||||||
|
"chunked\r\n\r\n5\r\nabcde\r\na\r\n1234567890\r\n0\r\n\r\n";
|
||||||
|
|
||||||
|
struct hsh_parser_s parser = {0};
|
||||||
|
struct hsh_buffer_s buffer = {0};
|
||||||
|
setup_buffer_and_parser(&parser, &buffer, request_string);
|
||||||
|
|
||||||
|
enum hsh_token_e expected_types[] = {
|
||||||
|
HSH_TOK_METHOD, HSH_TOK_TARGET, HSH_TOK_VERSION,
|
||||||
|
HSH_TOK_HEADER_KEY, HSH_TOK_HEADER_VALUE, HSH_TOK_HEADER_KEY,
|
||||||
|
HSH_TOK_HEADER_VALUE, HSH_TOK_HEADERS_DONE, HSH_TOK_BODY,
|
||||||
|
HSH_TOK_BODY, HSH_TOK_BODY};
|
||||||
|
|
||||||
|
char const *expected_values[] = {"POST",
|
||||||
|
"/chunked/test",
|
||||||
|
"HTTP/1.0",
|
||||||
|
"Host",
|
||||||
|
"www.jeremycw.com",
|
||||||
|
"Transfer-Encoding",
|
||||||
|
"chunked",
|
||||||
|
"",
|
||||||
|
"abcde",
|
||||||
|
"1234567890",
|
||||||
|
""};
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
struct hsh_token_s out;
|
||||||
|
int max_buf_capacity = strlen(request_string);
|
||||||
|
while ((out = hsh_parser_exec(&parser, &buffer, max_buf_capacity)).type !=
|
||||||
|
HSH_TOK_NONE) {
|
||||||
|
munit_assert_memory_equal(out.len, expected_values[i],
|
||||||
|
&buffer.buf[out.index]);
|
||||||
|
munit_assert(expected_types[i] == out.type);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
munit_assert(i == 11);
|
||||||
|
|
||||||
|
free(buffer.buf);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
MunitResult test_parser_chunked_body_partial(const MunitParameter params[],
|
||||||
|
void *data) {
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
char const *request_string = "POST /chunked/test HTTP/1.0\r\nHost: "
|
||||||
|
"www.jeremycw.com\r\nTransfer-Encoding: "
|
||||||
|
"chunked\r\n\r\n5\r\nabcde\r\na\r\n12345678";
|
||||||
|
|
||||||
|
struct hsh_parser_s parser = {0};
|
||||||
|
struct hsh_buffer_s buffer = {0};
|
||||||
|
setup_buffer_and_parser(&parser, &buffer, request_string);
|
||||||
|
|
||||||
|
enum hsh_token_e expected_types[] = {
|
||||||
|
HSH_TOK_METHOD, HSH_TOK_TARGET, HSH_TOK_VERSION,
|
||||||
|
HSH_TOK_HEADER_KEY, HSH_TOK_HEADER_VALUE, HSH_TOK_HEADER_KEY,
|
||||||
|
HSH_TOK_HEADER_VALUE, HSH_TOK_HEADERS_DONE, HSH_TOK_BODY};
|
||||||
|
|
||||||
|
char const *expected_values[] = {"POST",
|
||||||
|
"/chunked/test",
|
||||||
|
"HTTP/1.0",
|
||||||
|
"Host",
|
||||||
|
"www.jeremycw.com",
|
||||||
|
"Transfer-Encoding",
|
||||||
|
"chunked",
|
||||||
|
"",
|
||||||
|
"abcde"};
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
struct hsh_token_s out;
|
||||||
|
int max_buf_capacity = strlen(request_string);
|
||||||
|
while ((out = hsh_parser_exec(&parser, &buffer, max_buf_capacity)).type !=
|
||||||
|
HSH_TOK_NONE) {
|
||||||
|
munit_assert_memory_equal(out.len, expected_values[i],
|
||||||
|
&buffer.buf[out.index]);
|
||||||
|
munit_assert(expected_types[i] == out.type);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
munit_assert(i == 9);
|
||||||
|
|
||||||
|
memcpy(buffer.buf + buffer.length, "90\r\n0\r\n\r\n", 9);
|
||||||
|
buffer.length += 9;
|
||||||
|
buffer.sequence_id++;
|
||||||
|
|
||||||
|
out = hsh_parser_exec(&parser, &buffer, max_buf_capacity);
|
||||||
|
|
||||||
|
munit_assert(out.type == HSH_TOK_BODY);
|
||||||
|
munit_assert_memory_equal(out.len, "1234567890", &buffer.buf[out.index]);
|
||||||
|
|
||||||
|
free(buffer.buf);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
10
external/httpserver.h-master/test/unit/test_parser.h
vendored
Normal file
10
external/httpserver.h-master/test/unit/test_parser.h
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef TEST_PARSER_H
|
||||||
|
#define TEST_PARSER_H
|
||||||
|
|
||||||
|
MunitResult test_parser_small_body_complete(const MunitParameter params[], void* data);
|
||||||
|
MunitResult test_parser_small_body_partial(const MunitParameter params[], void* data);
|
||||||
|
MunitResult test_parser_large_body(const MunitParameter params[], void* data);
|
||||||
|
MunitResult test_parser_chunked_body(const MunitParameter params[], void* data);
|
||||||
|
MunitResult test_parser_chunked_body_partial(const MunitParameter params[], void* data);
|
||||||
|
|
||||||
|
#endif
|
153
external/httpserver.h-master/test/unit/test_read_socket.c
vendored
Normal file
153
external/httpserver.h-master/test/unit/test_read_socket.c
vendored
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "munit.h"
|
||||||
|
|
||||||
|
#include "read_socket.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
void token_array_init(struct hs_token_array_s *array, int capacity) {
|
||||||
|
array->buf =
|
||||||
|
(struct hsh_token_s *)malloc(sizeof(struct hsh_token_s) * capacity);
|
||||||
|
array->capacity = capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_callback(struct http_request_s* request) {
|
||||||
|
*((int*)request->data) = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct http_request_s* setup_test_request() {
|
||||||
|
struct http_request_s* request = calloc(1, sizeof(struct http_request_s));
|
||||||
|
request->server = calloc(1, sizeof(struct http_server_s));
|
||||||
|
|
||||||
|
token_array_init(&request->tokens, 32);
|
||||||
|
|
||||||
|
request->server->request_handler = &test_callback;
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_test_request(struct http_request_s* request) {
|
||||||
|
free(request->server);
|
||||||
|
free(request->buffer.buf);
|
||||||
|
free(request->tokens.buf);
|
||||||
|
free(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
MunitResult test_read_socket_small_body(const MunitParameter params[], void* data) {
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
struct http_request_s* request = setup_test_request();
|
||||||
|
|
||||||
|
int callback_ran = 0;
|
||||||
|
request->data = (void*)&callback_ran;
|
||||||
|
|
||||||
|
struct hs_read_opts_s opts = {
|
||||||
|
.max_request_buf_capacity = 4096,
|
||||||
|
.initial_request_buf_capacity = 1024,
|
||||||
|
.eof_rc = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
int fd = openat(AT_FDCWD, "test/unit/read_socket.txt", O_RDONLY);
|
||||||
|
request->socket = fd;
|
||||||
|
|
||||||
|
hs_read_request_and_exec_user_cb(request, opts);
|
||||||
|
|
||||||
|
munit_assert_int(request->timeout, ==, HTTP_REQUEST_TIMEOUT);
|
||||||
|
munit_assert_int64(request->server->memused, ==, 1024);
|
||||||
|
munit_assert(callback_ran);
|
||||||
|
|
||||||
|
struct hsh_token_s tok = request->tokens.buf[request->tokens.size-1];
|
||||||
|
|
||||||
|
munit_assert_memory_equal(tok.len, "Hello, World!",
|
||||||
|
&request->buffer.buf[tok.index]);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
destroy_test_request(request);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
MunitResult test_read_socket_small_body_expand_buffer(const MunitParameter params[], void* data) {
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
struct http_request_s* request = setup_test_request();
|
||||||
|
|
||||||
|
int callback_ran = 0;
|
||||||
|
request->data = (void*)&callback_ran;
|
||||||
|
|
||||||
|
struct hs_read_opts_s opts = {
|
||||||
|
.max_request_buf_capacity = 4096,
|
||||||
|
.initial_request_buf_capacity = 8,
|
||||||
|
.eof_rc = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
int fd = openat(AT_FDCWD, "test/unit/read_socket.txt", O_RDONLY);
|
||||||
|
request->socket = fd;
|
||||||
|
|
||||||
|
hs_read_request_and_exec_user_cb(request, opts);
|
||||||
|
munit_assert_int64(request->server->memused, ==, 128);
|
||||||
|
|
||||||
|
struct hsh_token_s tok = request->tokens.buf[request->tokens.size-1];
|
||||||
|
|
||||||
|
munit_assert_memory_equal(tok.len, "Hello, World!",
|
||||||
|
&request->buffer.buf[tok.index]);
|
||||||
|
|
||||||
|
munit_assert(callback_ran);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
destroy_test_request(request);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_chunk_callback(struct http_request_s* request) {
|
||||||
|
*((int*)request->data) = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
MunitResult test_read_socket_large_body(const MunitParameter params[], void* data) {
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
struct http_request_s* request = setup_test_request();
|
||||||
|
|
||||||
|
int callback_ran = 0;
|
||||||
|
request->data = (void*)&callback_ran;
|
||||||
|
|
||||||
|
request->chunk_cb = &test_chunk_callback;
|
||||||
|
|
||||||
|
struct hs_read_opts_s opts = {
|
||||||
|
.max_request_buf_capacity = 72,
|
||||||
|
.initial_request_buf_capacity = 72,
|
||||||
|
.eof_rc = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
int fd = openat(AT_FDCWD, "test/unit/read_socket.txt", O_RDONLY);
|
||||||
|
request->socket = fd;
|
||||||
|
|
||||||
|
hs_read_request_and_exec_user_cb(request, opts);
|
||||||
|
|
||||||
|
munit_assert(HTTP_FLAG_CHECK(request->flags, HTTP_FLG_STREAMED));
|
||||||
|
munit_assert_int(callback_ran, ==, 1);
|
||||||
|
|
||||||
|
hs_read_request_and_exec_user_cb(request, opts);
|
||||||
|
munit_assert_int(callback_ran, ==, 2);
|
||||||
|
|
||||||
|
struct hsh_token_s tok = request->tokens.buf[request->tokens.size-1];
|
||||||
|
munit_assert_memory_equal(tok.len, "Hello, ",
|
||||||
|
&request->buffer.buf[tok.index]);
|
||||||
|
|
||||||
|
hs_read_request_and_exec_user_cb(request, opts);
|
||||||
|
tok = request->tokens.buf[request->tokens.size-1];
|
||||||
|
munit_assert_memory_equal(tok.len, "World!",
|
||||||
|
&request->buffer.buf[tok.index]);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
destroy_test_request(request);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
3
external/httpserver.h-master/test/unit/test_read_socket.h
vendored
Normal file
3
external/httpserver.h-master/test/unit/test_read_socket.h
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
MunitResult test_read_socket_small_body(const MunitParameter params[], void* data);
|
||||||
|
MunitResult test_read_socket_small_body_expand_buffer(const MunitParameter params[], void* data);
|
||||||
|
MunitResult test_read_socket_large_body(const MunitParameter params[], void* data);
|
68
external/httpserver.h-master/test/unit/test_write_socket.c
vendored
Normal file
68
external/httpserver.h-master/test/unit/test_write_socket.c
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "munit.h"
|
||||||
|
|
||||||
|
#include "write_socket.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
enum hs_test_write_mode_e {
|
||||||
|
HS_TEST_WRITE_SUCCESS,
|
||||||
|
HS_TEST_WRITE_PARTIAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum hs_test_write_mode_e hs_test_write_mode;
|
||||||
|
static int write_stub_enabled = 0;
|
||||||
|
|
||||||
|
void hs_test_enable_write_stub(int enabled) {
|
||||||
|
write_stub_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t hs_test_write(int fd, char const *data, size_t size) {
|
||||||
|
if (write_stub_enabled) {
|
||||||
|
switch (hs_test_write_mode) {
|
||||||
|
case HS_TEST_WRITE_SUCCESS:
|
||||||
|
return size;
|
||||||
|
case HS_TEST_WRITE_PARTIAL:
|
||||||
|
return size / 2;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return write(fd, data, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct http_request_s* setup_test_request() {
|
||||||
|
struct http_request_s* request = calloc(1, sizeof(struct http_request_s));
|
||||||
|
request->server = calloc(1, sizeof(struct http_server_s));
|
||||||
|
|
||||||
|
request->buffer.buf = (char *)calloc(1, 1024);
|
||||||
|
request->buffer.capacity = 1024;
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroy_test_request(struct http_request_s* request) {
|
||||||
|
free(request->server);
|
||||||
|
free(request->buffer.buf);
|
||||||
|
free(request->tokens.buf);
|
||||||
|
free(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
MunitResult test_write_socket_partial(const MunitParameter params[], void* data) {
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
struct http_request_s* request = setup_test_request();
|
||||||
|
|
||||||
|
request->buffer.length = 512;
|
||||||
|
|
||||||
|
hs_test_write_mode = HS_TEST_WRITE_PARTIAL;
|
||||||
|
hs_test_enable_write_stub(1);
|
||||||
|
enum hs_write_rc_e rc = hs_write_socket(request);
|
||||||
|
|
||||||
|
munit_assert_int(rc, ==, HS_WRITE_RC_CONTINUE);
|
||||||
|
|
||||||
|
destroy_test_request(request);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
1
external/httpserver.h-master/test/unit/test_write_socket.h
vendored
Normal file
1
external/httpserver.h-master/test/unit/test_write_socket.h
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
MunitResult test_write_socket_partial(const MunitParameter params[], void* data);
|
|
@ -6,5 +6,6 @@ add_subdirectory(WebServer)
|
||||||
add_subdirectory(UartDevice)
|
add_subdirectory(UartDevice)
|
||||||
add_subdirectory(LinuxApiMock)
|
add_subdirectory(LinuxApiMock)
|
||||||
add_subdirectory(McuProtocol)
|
add_subdirectory(McuProtocol)
|
||||||
|
add_subdirectory(FxHttpServer)
|
||||||
|
|
||||||
|
|
||||||
|
|
58
test/utils/FxHttpServer/CMakeLists.txt
Normal file
58
test/utils/FxHttpServer/CMakeLists.txt
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# include(${CMAKE_SOURCE_DIR}/build/independent_source.cmake)
|
||||||
|
include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake)
|
||||||
|
set(EXECUTABLE_OUTPUT_PATH ${TEST_OUTPUT_PATH}/bin)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${UTILS_SOURCE_PATH}/StatusCode/include
|
||||||
|
${UTILS_SOURCE_PATH}/Log/include
|
||||||
|
${UTILS_SOURCE_PATH}/FxHttpServer/include
|
||||||
|
${EXTERNAL_SOURCE_PATH}/gtest/googletest-release-1.11.0/googletest/include
|
||||||
|
${EXTERNAL_SOURCE_PATH}/gtest/googletest-release-1.11.0/googlemock/include
|
||||||
|
)
|
||||||
|
|
||||||
|
link_directories(
|
||||||
|
${LIBS_OUTPUT_PATH}
|
||||||
|
${EXTERNAL_LIBS_OUTPUT_PATH}
|
||||||
|
)
|
||||||
|
|
||||||
|
aux_source_directory(. SRC_FILES)
|
||||||
|
aux_source_directory(./src SRC_FILES)
|
||||||
|
|
||||||
|
set(TARGET_NAME FxHttpServerTest)
|
||||||
|
add_executable(${TARGET_NAME} ${SRC_FILES})
|
||||||
|
target_link_libraries(${TARGET_NAME} FxHttpServer gtest gmock pthread Log)
|
||||||
|
if(${TEST_COVERAGE} MATCHES "true")
|
||||||
|
target_link_libraries(${TARGET_NAME} gcov)
|
||||||
|
endif()
|
||||||
|
if ("${CLANG_TIDY_SUPPORT}" MATCHES "true")
|
||||||
|
add_custom_target(
|
||||||
|
FxHttpServerTest_code_check
|
||||||
|
COMMAND ${CLANG_TIDY_EXE}
|
||||||
|
-checks='${CLANG_TIDY_CHECKS}'
|
||||||
|
--header-filter=.*
|
||||||
|
--system-headers=false
|
||||||
|
${SRC_FILES}
|
||||||
|
${CLANG_TIDY_CONFIG}
|
||||||
|
# --line-filter='[{\"name\":\"${EXTERNAL_SOURCE_PATH}/gtest/googletest-release-1.11.0/googletest/include/getest/gtest.h\"}]'
|
||||||
|
--line-filter='[{\"name\":\"${EXTERNAL_SOURCE_PATH}/gtest/googletest-release-1.11.0/googletest/include/getest/*.h\"}]'
|
||||||
|
-p ${PLATFORM_PATH}/cmake-shell
|
||||||
|
WORKING_DIRECTORY ${TEST_SOURCE_PATH}/utils/FxHttpServer
|
||||||
|
)
|
||||||
|
file(GLOB_RECURSE HEADER_FILES *.h)
|
||||||
|
add_custom_target(
|
||||||
|
FxHttpServerTest_code_format
|
||||||
|
COMMAND ${CLANG_FORMAT_EXE}
|
||||||
|
-style=file
|
||||||
|
-i ${SRC_FILES} ${HEADER_FILES}
|
||||||
|
WORKING_DIRECTORY ${TEST_SOURCE_PATH}/utils/FxHttpServer
|
||||||
|
)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${TARGET_NAME}
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND make FxHttpServerTest_code_check
|
||||||
|
COMMAND make FxHttpServerTest_code_format
|
||||||
|
WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
define_file_name(${TARGET_NAME})
|
23
test/utils/FxHttpServer/mainTest.cpp
Normal file
23
test/utils/FxHttpServer/mainTest.cpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Fancy Code.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <unistd.h>
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
32
test/utils/FxHttpServer/src/FxHttpServer_Test.cpp
Normal file
32
test/utils/FxHttpServer/src/FxHttpServer_Test.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Fancy Code.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#include "FxHttpServer.h"
|
||||||
|
#include "ILog.h"
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <thread>
|
||||||
|
namespace FxHttpServerTest
|
||||||
|
{
|
||||||
|
// ../output_files/test/bin/FxHttpServerTest --gtest_filter=FxHttpServerTest.Demo
|
||||||
|
TEST(FxHttpServerTest, Demo)
|
||||||
|
{
|
||||||
|
CreateLogModule();
|
||||||
|
ILogInit(LOG_INSTANCE_TYPE_END);
|
||||||
|
FxHttpServerInit();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000 * 10));
|
||||||
|
FxHttpServerUnInit();
|
||||||
|
ILogUnInit();
|
||||||
|
}
|
||||||
|
} // namespace FxHttpServerTest
|
|
@ -15,10 +15,6 @@ link_directories(
|
||||||
${EXTERNAL_LIBS_OUTPUT_PATH}
|
${EXTERNAL_LIBS_OUTPUT_PATH}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
aux_source_directory(. SRC_FILES)
|
aux_source_directory(. SRC_FILES)
|
||||||
aux_source_directory(./src SRC_FILES)
|
aux_source_directory(./src SRC_FILES)
|
||||||
|
|
||||||
|
|
|
@ -12,4 +12,5 @@ add_subdirectory(McuProtocol)
|
||||||
add_subdirectory(ModBusCRC16)
|
add_subdirectory(ModBusCRC16)
|
||||||
add_subdirectory(LedControl)
|
add_subdirectory(LedControl)
|
||||||
add_subdirectory(KeyControl)
|
add_subdirectory(KeyControl)
|
||||||
add_subdirectory(MediaAdapter)
|
add_subdirectory(MediaAdapter)
|
||||||
|
add_subdirectory(FxHttpServer)
|
65
utils/FxHttpServer/CMakeLists.txt
Normal file
65
utils/FxHttpServer/CMakeLists.txt
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake)
|
||||||
|
include(${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/goahead.cmake)
|
||||||
|
set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH})
|
||||||
|
set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH})
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
./src
|
||||||
|
./include
|
||||||
|
${UTILS_SOURCE_PATH}/StatusCode/include
|
||||||
|
${UTILS_SOURCE_PATH}/Log/include
|
||||||
|
${EXTERNAL_SOURCE_PATH}/httpserver.h-master
|
||||||
|
# ${UTILS_SOURCE_PATH}/LinuxApi/include
|
||||||
|
)
|
||||||
|
# link_directories(
|
||||||
|
# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs
|
||||||
|
# )
|
||||||
|
|
||||||
|
include(CheckFunctionExists)
|
||||||
|
check_function_exists(epoll_wait EPOLL)
|
||||||
|
check_function_exists(kqueue KQUEUE)
|
||||||
|
|
||||||
|
aux_source_directory(./src SRC_FILES)
|
||||||
|
|
||||||
|
set(TARGET_NAME FxHttpServer)
|
||||||
|
add_library(${TARGET_NAME} STATIC ${SRC_FILES})
|
||||||
|
target_link_libraries(${TARGET_NAME} StatusCode Log -Wl,--start-group httpsrv -Wl,--end-group)
|
||||||
|
|
||||||
|
if ("${CLANG_TIDY_SUPPORT}" MATCHES "true")
|
||||||
|
add_custom_target(
|
||||||
|
FxHttpServer_code_check
|
||||||
|
COMMAND ${CLANG_TIDY_EXE}
|
||||||
|
-checks='${CLANG_TIDY_CHECKS}'
|
||||||
|
--header-filter=.*
|
||||||
|
--system-headers=false
|
||||||
|
${SRC_FILES}
|
||||||
|
${CLANG_TIDY_CONFIG}
|
||||||
|
-p ${PLATFORM_PATH}/cmake-shell
|
||||||
|
WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/FxHttpServer
|
||||||
|
)
|
||||||
|
file(GLOB_RECURSE HEADER_FILES *.h)
|
||||||
|
add_custom_target(
|
||||||
|
FxHttpServer_code_format
|
||||||
|
COMMAND ${CLANG_FORMAT_EXE}
|
||||||
|
-style=file
|
||||||
|
-i ${SRC_FILES} ${HEADER_FILES}
|
||||||
|
WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/FxHttpServer
|
||||||
|
)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${TARGET_NAME}
|
||||||
|
PRE_BUILD
|
||||||
|
COMMAND make FxHttpServer_code_check
|
||||||
|
COMMAND make FxHttpServer_code_format
|
||||||
|
WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(KQUEUE)
|
||||||
|
target_compile_definitions(${TARGET_NAME} PRIVATE KQUEUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(EPOLL)
|
||||||
|
target_compile_definitions(${TARGET_NAME} PRIVATE EPOLL)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
define_file_name(${TARGET_NAME})
|
24
utils/FxHttpServer/README.md
Normal file
24
utils/FxHttpServer/README.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# 1. httpserver组件库
|
||||||
|
|
||||||
|
   使用<sdk>/external/httpserver.h-master/src/CMakeLists.txt文件编译的开源库,二次封装接口给到应用层使用。
|
||||||
|
|
||||||
|
## 1.1. 注意事项
|
||||||
|
1. 编译libhttpsrv时,可能会提示 ragel 工具未安装,需要安装 ragel 工具:
|
||||||
|
```
|
||||||
|
$ sudo apt install ragel
|
||||||
|
```
|
||||||
|
2. 对开源库的CMakeLists.txt文件增加拷贝命令:
|
||||||
|
```
|
||||||
|
message("${PLATFORM_PATH}/cmake-shell/external${SUBMODULE_PATH_OF_IPC_SDK}/httpserver.h-master/src/libhttpsrv.a")
|
||||||
|
add_custom_command(
|
||||||
|
TARGET httpsrv
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND cp ${PLATFORM_PATH}/cmake-shell${SUBMODULE_PATH_OF_IPC_SDK}/external/httpserver.h-master/src/libhttpsrv.a ${EXTERNAL_LIBS_OUTPUT_PATH}
|
||||||
|
WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/
|
||||||
|
)
|
||||||
|
```
|
||||||
|
3. 取消开源库的CMakeLists.txt文件debug配置,未知会产生什么不良后果;
|
||||||
|
```
|
||||||
|
PUBLIC $<$<CONFIG:DEBUG>:-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all>
|
||||||
|
```
|
||||||
|
4.
|
26
utils/FxHttpServer/include/FxHttpServer.h
Normal file
26
utils/FxHttpServer/include/FxHttpServer.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Fancy Code.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#ifndef FX_HTTP_SERVER_H
|
||||||
|
#define FX_HTTP_SERVER_H
|
||||||
|
#include "StatusCode.h"
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
StatusCode FxHttpServerInit(void);
|
||||||
|
StatusCode FxHttpServerUnInit(void);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
54
utils/FxHttpServer/src/FxHttpServer.c
Normal file
54
utils/FxHttpServer/src/FxHttpServer.c
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Fancy Code.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#include "FxHttpServer.h"
|
||||||
|
#include "ILog.h"
|
||||||
|
#include "httpserver.h"
|
||||||
|
#include <string.h>
|
||||||
|
static struct http_server_s *server = NULL;
|
||||||
|
static struct http_server_s *poll_server = NULL;
|
||||||
|
|
||||||
|
static int request_target_is(struct http_request_s *request, char const *target)
|
||||||
|
{
|
||||||
|
http_string_t url = http_request_target(request);
|
||||||
|
LogInfo("sssssssssssssssssssssss url.buf = %s\n", url.buf);
|
||||||
|
int len = strlen(target);
|
||||||
|
return len == url.len && memcmp(url.buf, target, url.len) == 0;
|
||||||
|
}
|
||||||
|
static void handle_request(struct http_request_s *request)
|
||||||
|
{
|
||||||
|
http_request_connection(request, HTTP_AUTOMATIC);
|
||||||
|
struct http_response_s *response = http_response_init();
|
||||||
|
http_response_status(response, 200);
|
||||||
|
if (request_target_is(request, "/set")) {
|
||||||
|
LogInfo("======================================== set\n");
|
||||||
|
}
|
||||||
|
http_response_header(response, "Content-Type", "text/plain");
|
||||||
|
http_response_body(response, "RESPONSE", sizeof("RESPONSE") - 1);
|
||||||
|
http_respond(request, response);
|
||||||
|
}
|
||||||
|
StatusCode FxHttpServerInit(void)
|
||||||
|
{
|
||||||
|
server = http_server_init(8080, handle_request);
|
||||||
|
poll_server = http_server_init(8081, handle_request);
|
||||||
|
http_server_listen_poll(poll_server);
|
||||||
|
http_server_listen(server);
|
||||||
|
return CreateStatusCode(STATUS_CODE_OK);
|
||||||
|
}
|
||||||
|
StatusCode FxHttpServerUnInit(void)
|
||||||
|
{
|
||||||
|
free(server);
|
||||||
|
free(poll_server);
|
||||||
|
return CreateStatusCode(STATUS_CODE_OK);
|
||||||
|
}
|
|
@ -18,7 +18,13 @@
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
void *CreateMediaAdapter(void);
|
enum SENSOR_NUM
|
||||||
|
{
|
||||||
|
SENSOR_NUM_1 = 0,
|
||||||
|
SENSOR_NUM_2,
|
||||||
|
SENSOR_NUM_END
|
||||||
|
};
|
||||||
|
void *CreateMediaAdapter(const SENSOR_NUM num);
|
||||||
void TestApi(void *object);
|
void TestApi(void *object);
|
||||||
void IMediaAdapterFree(void *object);
|
void IMediaAdapterFree(void *object);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
static const char *MEDIA_ADAPTER_NAME = "media_adapter";
|
static const char *MEDIA_ADAPTER_NAME = "media_adapter";
|
||||||
const char inline *GetMediaAdapterModuleName(void) { return MEDIA_ADAPTER_NAME; }
|
const char inline *GetMediaAdapterModuleName(void) { return MEDIA_ADAPTER_NAME; }
|
||||||
std::shared_ptr<IMediaAdapter> *NewIMediaAdapter(void)
|
std::shared_ptr<IMediaAdapter> *NewIMediaAdapter(const SENSOR_NUM &num)
|
||||||
{
|
{
|
||||||
LogInfo("Create the uart device object.\n");
|
LogInfo("Create the uart device object.\n");
|
||||||
MeidaAdapter *impl = (MeidaAdapter *)malloc(sizeof(MeidaAdapter));
|
MeidaAdapter *impl = (MeidaAdapter *)malloc(sizeof(MeidaAdapter));
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
*/
|
*/
|
||||||
#ifndef I_MEDIA_ADAPTER_H
|
#ifndef I_MEDIA_ADAPTER_H
|
||||||
#define I_MEDIA_ADAPTER_H
|
#define I_MEDIA_ADAPTER_H
|
||||||
|
#include "MediaAdapter.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
class IMediaAdapter
|
class IMediaAdapter
|
||||||
{
|
{
|
||||||
|
@ -32,5 +33,5 @@ typedef struct media_adapter
|
||||||
std::shared_ptr<IMediaAdapter> mIMediaAdapter;
|
std::shared_ptr<IMediaAdapter> mIMediaAdapter;
|
||||||
} MeidaAdapter;
|
} MeidaAdapter;
|
||||||
const char *GetMediaAdapterModuleName(void);
|
const char *GetMediaAdapterModuleName(void);
|
||||||
std::shared_ptr<IMediaAdapter> *NewIMediaAdapter(void);
|
std::shared_ptr<IMediaAdapter> *NewIMediaAdapter(const SENSOR_NUM &num);
|
||||||
#endif
|
#endif
|
|
@ -15,7 +15,7 @@
|
||||||
#include "MediaAdapter.h"
|
#include "MediaAdapter.h"
|
||||||
#include "ILog.h"
|
#include "ILog.h"
|
||||||
#include "IMediaAdapter.h"
|
#include "IMediaAdapter.h"
|
||||||
void *CreateMediaAdapter(void) { return NewIMediaAdapter(); }
|
void *CreateMediaAdapter(const SENSOR_NUM num) { return NewIMediaAdapter(num); }
|
||||||
static bool ObjectCheck(void *object)
|
static bool ObjectCheck(void *object)
|
||||||
{
|
{
|
||||||
if (nullptr == object) {
|
if (nullptr == object) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user