Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71b19ac358 | ||
|
|
b8eaeedd58 | ||
|
|
7f0c638b62 | ||
|
|
02d8206e9d | ||
|
|
2eb6a5fe9d | ||
|
|
e2d52f8b63 | ||
|
|
f3345ba670 | ||
|
|
c681590fa2 | ||
|
|
1f9411119a | ||
|
|
b361125bc3 | ||
|
|
04deae62d6 | ||
|
|
db030b3834 | ||
|
|
e686e477e2 | ||
|
|
f6d81c1a87 | ||
|
|
f1fd633356 | ||
|
|
64b40dfac7 | ||
|
|
954bcb7e3e | ||
|
|
008772aef1 |
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
CFLAGS = -ggdb
|
CFLAGS = -ggdb -Wall -Wextra
|
||||||
LFLAGS =
|
LFLAGS =
|
||||||
|
|
||||||
ifeq ($(shell uname -s),Linux)
|
ifeq ($(shell uname -s),Linux)
|
||||||
|
|||||||
@@ -1,12 +1,156 @@
|
|||||||
# cHTTP
|
# cHTTP
|
||||||
This is an HTTP library for C, featuring an HTTP(S) server, HTTP(S) client, and much more!
|
cHTTP is an HTTP client and server library distributed as a single file with support for HTTPS, virtual hosts, fully non-blocking operations.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Your first request
|
||||||
|
|
||||||
|
The simplest way to perform a GET request looks like this:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "chttp.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
CHTTP_Response *response;
|
||||||
|
|
||||||
|
int ret = chttp_get(CHTTP_STR("http://coz.is/"), NULL, 0, &response);
|
||||||
|
if (ret == CHTTP_OK) {
|
||||||
|
printf("Received %d bytes\n", response->body.len);
|
||||||
|
chttp_free_response(response);
|
||||||
|
} else {
|
||||||
|
printf("Request failure: %s\n", chttp_strerror(ret));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
(Note the `http:` schema. If you want HTTPS, you'll have to enable it explicitly! Refer to the HTTPS section.)
|
||||||
|
|
||||||
|
Copy this code to `first_request.c` near `chttp.c` and compile it by running:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Linux
|
||||||
|
gcc chttp.c first_request.c -o first_request
|
||||||
|
|
||||||
|
# Windows (mingw)
|
||||||
|
gcc chttp.c first_request.c -o first_request.exe -lws2_32
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, run the program
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Linux
|
||||||
|
./first_request
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
.\first_request.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
Done!
|
||||||
|
|
||||||
|
### Your first server
|
||||||
|
|
||||||
|
The setup for a basic server looks like this:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "chttp.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
CHTTP_Server server;
|
||||||
|
ret = chttp_server_init(&server);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Couldn't initialize server (%s)\n", chttp_strerror(ret));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
chttp_server_set_reuse_addr(&server, true);
|
||||||
|
chttp_server_set_trace_bytes(&server, true);
|
||||||
|
|
||||||
|
ret = chttp_server_listen_tcp(&server, CHTTP_STR("127.0.0.1"), 8080);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Couldn't start listening (%s)\n", chttp_strerror(ret));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
CHTTP_Request *request;
|
||||||
|
CHTTP_ResponseBuilder builder;
|
||||||
|
chttp_server_wait_request(&server, &request, &builder);
|
||||||
|
|
||||||
|
chttp_response_builder_status(builder, 200);
|
||||||
|
chttp_response_builder_body(builder, CHTTP_STR("Hello, world!"));
|
||||||
|
chttp_response_builder_send(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
chttp_server_free(&server);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy this code to a `first_server.c` file and compile it by running
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Linux
|
||||||
|
gcc chttp.c first_server.c -o first_server
|
||||||
|
|
||||||
|
# Windows (mingw)
|
||||||
|
gcc chttp.c first_server.c -o first_server.exe -lws2_32
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, run the program
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Linux
|
||||||
|
./first_server
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
.\first_server.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
While the program is running, open a browser and visit `http://127.0.0.1:8080/`. You should see the text "Hello, world!" sent by the server and a log of the HTTP requests and responses processed by the server in the console.
|
||||||
|
|
||||||
|
## HTTPS
|
||||||
|
|
||||||
|
HTTPS is supported via OpenSSL, which is easily available on Linux and less so on Windows.
|
||||||
|
|
||||||
|
First, install the OpenSSL development libraries:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Ubuntu/Debian Linux
|
||||||
|
sudo apt install libssl-dev gcc
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, enable HTTPS by compiling your program with the following flags:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Linux
|
||||||
|
gcc chttp.c main.c -lssl -lcrypto -DHTTPS_ENABLED
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
gcc chttp.c main.c -lws2_32 -lssl -lcrypto -DHTTPS_ENABLED
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Status
|
||||||
|
|
||||||
|
The major limitation of cHTTP is HTTPS on Windows. For that to work correctly it will be necessary to port the OpenSSL code to SChannel.
|
||||||
|
|
||||||
|
Other limitations:
|
||||||
|
* HTTP client doesn't follow redirections (responses with code 3xx)
|
||||||
|
* Support for HTTP client cookies is limited
|
||||||
|
* HTTP server adherence to the spec can be improved
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Contributions are welcome! The following are some notes on how to work with the codebase. Don't worry if you get something wrong. I will remind you.
|
Contributions are welcome! The following are some notes on how to work with the codebase. Don't worry if you get something wrong. I will remind you.
|
||||||
|
|
||||||
The source code in the `src/` directory is intended to be be amalgamated into a single file before compilation. The amalgamation is not only intended as a distribution method, but also as easy-access documentation, and therefore need to be readable. For this reasons:
|
The source code in the src/ directory is intended to be be amalgamated into a single file before compilation. The amalgamation is not only intended as a distribution method, but also as easy-access documentation, and therefore need to be readable. For this reasons:
|
||||||
1. You never need need to include other cHTTP source files
|
|
||||||
2. All inclusions of third-party headers are to be placed inside `src/includes.h`
|
* You never need need to include other cHTTP source files
|
||||||
3. All files must start with a single empty line, unless they start with an overview comment of the file, in which case they must have no empty lines at the beginning of the file.
|
* All inclusions of third-party headers are to be placed inside src/includes.h
|
||||||
4. All files must end with a single empty line.
|
* All files must start with a single empty line, unless they start with an overview comment of the file, in which case they must have no empty lines at the beginning of the file.
|
||||||
|
* All files must end with a single empty line.
|
||||||
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
HTTP_Response *response;
|
CHTTP_Response *response;
|
||||||
|
|
||||||
int ret = http_get(HTTP_STR("http://coz.is/"), NULL, 0, &response);
|
int ret = chttp_get(CHTTP_STR("http://coz.is/"), NULL, 0, &response);
|
||||||
if (ret == HTTP_OK) {
|
if (ret == CHTTP_OK) {
|
||||||
printf("Received %d bytes\n", response->body.len);
|
printf("Received %d bytes\n", response->body.len);
|
||||||
http_free_response(response);
|
chttp_free_response(response);
|
||||||
} else {
|
} else {
|
||||||
printf("Request failure: %s\n", http_strerror(ret));
|
printf("Request failure: %s\n", chttp_strerror(ret));
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,33 +4,33 @@ int main(void)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
HTTP_Server server;
|
CHTTP_Server server;
|
||||||
ret = http_server_init(&server);
|
ret = chttp_server_init(&server);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Couldn't initialize server (%s)\n", http_strerror(ret));
|
fprintf(stderr, "Couldn't initialize server (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_set_reuse_addr(&server, true);
|
chttp_server_set_reuse_addr(&server, true);
|
||||||
http_server_set_trace_bytes(&server, true);
|
chttp_server_set_trace_bytes(&server, true);
|
||||||
|
|
||||||
ret = http_server_listen_tcp(&server, HTTP_STR("127.0.0.1"), 8080);
|
ret = chttp_server_listen_tcp(&server, CHTTP_STR("127.0.0.1"), 8080);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Couldn't start listening (%s)\n", http_strerror(ret));
|
fprintf(stderr, "Couldn't start listening (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
HTTP_Request *request;
|
CHTTP_Request *request;
|
||||||
HTTP_ResponseBuilder builder;
|
CHTTP_ResponseBuilder builder;
|
||||||
http_server_wait_request(&server, &request, &builder);
|
chttp_server_wait_request(&server, &request, &builder);
|
||||||
|
|
||||||
http_response_builder_status(builder, 200);
|
chttp_response_builder_status(builder, 200);
|
||||||
http_response_builder_body(builder, HTTP_STR("Hello, world!"));
|
chttp_response_builder_body(builder, CHTTP_STR("Hello, world!"));
|
||||||
http_response_builder_send(builder);
|
chttp_response_builder_send(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_free(&server);
|
chttp_server_free(&server);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+63
-54
@@ -9,45 +9,52 @@
|
|||||||
|
|
||||||
#include "../chttp.h"
|
#include "../chttp.h"
|
||||||
|
|
||||||
|
static int pick_timeout(int t1, int t2)
|
||||||
|
{
|
||||||
|
if (t1 < 0) return t2;
|
||||||
|
if (t2 < 0) return t1;
|
||||||
|
return (t1 < t2) ? t1 : t2;
|
||||||
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
HTTP_String remote = HTTP_STR("http://coz.is");
|
CHTTP_String remote = CHTTP_STR("http://coz.is");
|
||||||
|
|
||||||
HTTP_Client client;
|
CHTTP_Client client;
|
||||||
ret = http_client_init(&client);
|
ret = chttp_client_init(&client);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printf("Couldn't initialize client (%s)\n", http_strerror(ret));
|
printf("Couldn't initialize client (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_Server server;
|
CHTTP_Server server;
|
||||||
ret = http_server_init(&server);
|
ret = chttp_server_init(&server);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Couldn't initialize server (%s)\n", http_strerror(ret));
|
fprintf(stderr, "Couldn't initialize server (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_set_reuse_addr(&server, true);
|
chttp_server_set_reuse_addr(&server, true);
|
||||||
http_server_set_trace_bytes(&server, true);
|
chttp_server_set_trace_bytes(&server, true);
|
||||||
|
|
||||||
ret = http_server_listen_tcp(&server, HTTP_STR("127.0.0.1"), 8080);
|
ret = chttp_server_listen_tcp(&server, CHTTP_STR("127.0.0.1"), 8080);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Couldn't start listening (%s)\n", http_strerror(ret));
|
fprintf(stderr, "Couldn't start listening (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool used[HTTP_SERVER_CAPACITY];
|
bool used[CHTTP_SERVER_CAPACITY];
|
||||||
for (int i = 0; i < HTTP_SERVER_CAPACITY; i++)
|
for (int i = 0; i < CHTTP_SERVER_CAPACITY; i++)
|
||||||
used[i] = false;
|
used[i] = false;
|
||||||
|
|
||||||
HTTP_ResponseBuilder pending[HTTP_SERVER_CAPACITY];
|
CHTTP_ResponseBuilder pending[CHTTP_SERVER_CAPACITY];
|
||||||
int num_pending = 0;
|
int num_pending = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
#define POLL_CAPACITY (HTTP_CLIENT_POLL_CAPACITY + HTTP_SERVER_POLL_CAPACITY)
|
#define POLL_CAPACITY (CHTTP_CLIENT_POLL_CAPACITY + CHTTP_SERVER_POLL_CAPACITY)
|
||||||
|
|
||||||
void *ptrs[POLL_CAPACITY];
|
void *ptrs[POLL_CAPACITY];
|
||||||
struct pollfd polled[POLL_CAPACITY];
|
struct pollfd polled[POLL_CAPACITY];
|
||||||
@@ -55,82 +62,84 @@ int main()
|
|||||||
EventRegister server_reg = {
|
EventRegister server_reg = {
|
||||||
ptrs,
|
ptrs,
|
||||||
polled,
|
polled,
|
||||||
0
|
0,
|
||||||
|
-1
|
||||||
};
|
};
|
||||||
http_server_register_events(&server, &server_reg);
|
chttp_server_register_events(&server, &server_reg);
|
||||||
|
|
||||||
EventRegister client_reg = {
|
EventRegister client_reg = {
|
||||||
ptrs + server_reg.num_polled,
|
ptrs + server_reg.num_polled,
|
||||||
polled + server_reg.num_polled,
|
polled + server_reg.num_polled,
|
||||||
0
|
0,
|
||||||
|
-1
|
||||||
};
|
};
|
||||||
http_client_register_events(&client, &client_reg);
|
chttp_client_register_events(&client, &client_reg);
|
||||||
|
|
||||||
if (server_reg.num_polled > 0 ||
|
|
||||||
client_reg.num_polled > 0) {
|
|
||||||
int num_polled = server_reg.num_polled
|
int num_polled = server_reg.num_polled
|
||||||
+ client_reg.num_polled;
|
+ client_reg.num_polled;
|
||||||
POLL(polled, num_polled, -1);
|
|
||||||
}
|
int timeout = pick_timeout(server_reg.timeout, client_reg.timeout);
|
||||||
|
|
||||||
|
POLL(polled, num_polled, timeout);
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
void *user;
|
void *user;
|
||||||
HTTP_Response *response;
|
CHTTP_Response *response;
|
||||||
http_client_process_events(&client, client_reg);
|
chttp_client_process_events(&client, client_reg);
|
||||||
while (http_client_next_response(&client, &result, &user, &response)) {
|
while (chttp_client_next_response(&client, &result, &user, &response)) {
|
||||||
|
|
||||||
HTTP_ResponseBuilder *builder = (HTTP_ResponseBuilder*) user;
|
CHTTP_ResponseBuilder *builder = (CHTTP_ResponseBuilder*) user;
|
||||||
|
|
||||||
int i = builder - pending;
|
int i = builder - pending;
|
||||||
assert(i > -1);
|
assert(i > -1);
|
||||||
assert(i < HTTP_SERVER_CAPACITY);
|
assert(i < CHTTP_SERVER_CAPACITY);
|
||||||
|
|
||||||
if (result == HTTP_OK) {
|
if (result == CHTTP_OK) {
|
||||||
http_response_builder_status(*builder, response->status);
|
chttp_response_builder_status(*builder, response->status);
|
||||||
http_response_builder_body(*builder, response->body);
|
chttp_response_builder_body(*builder, response->body);
|
||||||
http_response_builder_send(*builder);
|
chttp_response_builder_send(*builder);
|
||||||
} else {
|
} else {
|
||||||
http_response_builder_status(*builder, 500);
|
chttp_response_builder_status(*builder, 500);
|
||||||
http_response_builder_send(*builder);
|
chttp_response_builder_send(*builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
used[i] = false;
|
used[i] = false;
|
||||||
num_pending--;
|
num_pending--;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_Request *request;
|
CHTTP_Request *request;
|
||||||
HTTP_ResponseBuilder response_builder;
|
CHTTP_ResponseBuilder response_builder;
|
||||||
http_server_process_events(&server, server_reg);
|
chttp_server_process_events(&server, server_reg);
|
||||||
while (http_server_next_request(&server, &request, &response_builder)) {
|
while (chttp_server_next_request(&server, &request, &response_builder)) {
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (used[i]) {
|
while (used[i]) {
|
||||||
i++;
|
i++;
|
||||||
assert(i < HTTP_SERVER_CAPACITY);
|
assert(i < CHTTP_SERVER_CAPACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String path = request->url.path;
|
CHTTP_String path = request->url.path;
|
||||||
if (path.len == 0)
|
if (path.len == 0)
|
||||||
path = HTTP_STR("/");
|
path = CHTTP_STR("/");
|
||||||
|
|
||||||
char target[1<<10];
|
char target[1<<10];
|
||||||
int target_len = snprintf(target, sizeof(target),
|
int target_len = snprintf(target, sizeof(target),
|
||||||
"%.*s%.*s", HTTP_UNPACK(remote), HTTP_UNPACK(path));
|
"%.*s%.*s", CHTTP_UNPACK(remote), CHTTP_UNPACK(path));
|
||||||
if (target_len < 0 || target_len >= (int) sizeof(target)) {
|
if (target_len < 0 || target_len >= (int) sizeof(target)) {
|
||||||
http_response_builder_status(response_builder, 500);
|
chttp_response_builder_status(response_builder, 500);
|
||||||
http_response_builder_send(response_builder);
|
chttp_response_builder_send(response_builder);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_RequestBuilder request_builder = http_client_get_builder(&client);
|
CHTTP_RequestBuilder request_builder = chttp_client_get_builder(&client);
|
||||||
http_request_builder_set_user(request_builder, &pending[i]);
|
chttp_request_builder_set_user(request_builder, &pending[i]);
|
||||||
http_request_builder_trace(request_builder, true);
|
chttp_request_builder_trace(request_builder, true);
|
||||||
http_request_builder_method(request_builder, request->method);
|
chttp_request_builder_method(request_builder, request->method);
|
||||||
http_request_builder_target(request_builder, (HTTP_String) { target, target_len });
|
chttp_request_builder_target(request_builder, (CHTTP_String) { target, target_len });
|
||||||
ret = http_request_builder_send(request_builder);
|
ret = chttp_request_builder_send(request_builder);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
http_response_builder_status(response_builder, 500);
|
chttp_response_builder_status(response_builder, 500);
|
||||||
http_response_builder_send(response_builder);
|
chttp_response_builder_send(response_builder);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +149,7 @@ int main()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_free(&server);
|
chttp_server_free(&server);
|
||||||
http_client_free(&client);
|
chttp_client_free(&client);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,21 +16,21 @@ int main(void)
|
|||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
HTTP_Server server;
|
CHTTP_Server server;
|
||||||
ret = http_server_init(&server);
|
ret = chttp_server_init(&server);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Couldn't initialize server (%s)\n", http_strerror(ret));
|
fprintf(stderr, "Couldn't initialize server (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_set_reuse_addr(&server, true);
|
chttp_server_set_reuse_addr(&server, true);
|
||||||
http_server_set_trace_bytes(&server, true);
|
chttp_server_set_trace_bytes(&server, true);
|
||||||
|
|
||||||
HTTP_String local_addr = HTTP_STR("127.0.0.1");
|
CHTTP_String local_addr = CHTTP_STR("127.0.0.1");
|
||||||
uint16_t local_port = 8080;
|
uint16_t local_port = 8080;
|
||||||
ret = http_server_listen_tcp(&server, local_addr, local_port);
|
ret = chttp_server_listen_tcp(&server, local_addr, local_port);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Couldn't start listening (%s)\n", http_strerror(ret));
|
fprintf(stderr, "Couldn't start listening (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,29 +48,29 @@ int main(void)
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
HTTP_Request *req;
|
CHTTP_Request *req;
|
||||||
HTTP_ResponseBuilder builder;
|
CHTTP_ResponseBuilder builder;
|
||||||
http_server_wait_request(&server, &req, &builder);
|
chttp_server_wait_request(&server, &req, &builder);
|
||||||
|
|
||||||
if (http_match_host(req, HTTP_STR("websiteB.com"), local_port)) {
|
if (chttp_match_host(req, CHTTP_STR("websiteB.com"), local_port)) {
|
||||||
// Website B
|
// Website B
|
||||||
http_response_builder_status(builder, 200);
|
chttp_response_builder_status(builder, 200);
|
||||||
http_response_builder_body(builder, HTTP_STR("Hello from websiteB.com!"));
|
chttp_response_builder_body(builder, CHTTP_STR("Hello from websiteB.com!"));
|
||||||
http_response_builder_send(builder);
|
chttp_response_builder_send(builder);
|
||||||
|
|
||||||
} else if (http_match_host(req, HTTP_STR("websiteC.com"), local_port)) {
|
} else if (chttp_match_host(req, CHTTP_STR("websiteC.com"), local_port)) {
|
||||||
// Website C
|
// Website C
|
||||||
http_response_builder_status(builder, 200);
|
chttp_response_builder_status(builder, 200);
|
||||||
http_response_builder_body(builder, HTTP_STR("Hello from websiteC.com!"));
|
chttp_response_builder_body(builder, CHTTP_STR("Hello from websiteC.com!"));
|
||||||
http_response_builder_send(builder);
|
chttp_response_builder_send(builder);
|
||||||
} else {
|
} else {
|
||||||
// Serve websiteA by default
|
// Serve websiteA by default
|
||||||
http_response_builder_status(builder, 200);
|
chttp_response_builder_status(builder, 200);
|
||||||
http_response_builder_body(builder, HTTP_STR("Hello from websiteA.com!"));
|
chttp_response_builder_body(builder, CHTTP_STR("Hello from websiteA.com!"));
|
||||||
http_response_builder_send(builder);
|
chttp_response_builder_send(builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_free(&server);
|
chttp_server_free(&server);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-17
@@ -4,39 +4,39 @@
|
|||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
HTTP_String local_addr = HTTP_STR("127.0.0.1");
|
CHTTP_String local_addr = CHTTP_STR("127.0.0.1");
|
||||||
uint16_t local_port = 8443;
|
uint16_t local_port = 8443;
|
||||||
|
|
||||||
HTTP_String cert_file = HTTP_STR("websiteA_cert.pem");
|
CHTTP_String cert_file = CHTTP_STR("websiteA_cert.pem");
|
||||||
HTTP_String key_file = HTTP_STR("websiteA_key.pem");
|
CHTTP_String key_file = CHTTP_STR("websiteA_key.pem");
|
||||||
|
|
||||||
HTTP_Server server;
|
CHTTP_Server server;
|
||||||
int ret = http_server_init(&server);
|
int ret = chttp_server_init(&server);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Couldn't initialize server (%s)\n", http_strerror(ret));
|
fprintf(stderr, "Couldn't initialize server (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_set_reuse_addr(&server, true);
|
chttp_server_set_reuse_addr(&server, true);
|
||||||
http_server_set_trace_bytes(&server, true);
|
chttp_server_set_trace_bytes(&server, true);
|
||||||
|
|
||||||
ret = http_server_listen_tls(&server, local_addr, local_port, cert_file, key_file);
|
ret = chttp_server_listen_tls(&server, local_addr, local_port, cert_file, key_file);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Couldn't start listening (%s)\n", http_strerror(ret));
|
fprintf(stderr, "Couldn't start listening (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
HTTP_Request *req;
|
CHTTP_Request *req;
|
||||||
HTTP_ResponseBuilder builder;
|
CHTTP_ResponseBuilder builder;
|
||||||
http_server_wait_request(&server, &req, &builder);
|
chttp_server_wait_request(&server, &req, &builder);
|
||||||
|
|
||||||
http_response_builder_status(builder, 200);
|
chttp_response_builder_status(builder, 200);
|
||||||
http_response_builder_body(builder, HTTP_STR("Hello from websiteA.com!"));
|
chttp_response_builder_body(builder, CHTTP_STR("Hello from websiteA.com!"));
|
||||||
http_response_builder_send(builder);
|
chttp_response_builder_send(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_free(&server);
|
chttp_server_free(&server);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,47 +38,47 @@ int main(void)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
HTTP_Server server;
|
CHTTP_Server server;
|
||||||
ret = http_server_init(&server);
|
ret = chttp_server_init(&server);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Couldn't initialize server (%s)\n", http_strerror(ret));
|
fprintf(stderr, "Couldn't initialize server (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_set_reuse_addr(&server, true);
|
chttp_server_set_reuse_addr(&server, true);
|
||||||
http_server_set_trace_bytes(&server, true);
|
chttp_server_set_trace_bytes(&server, true);
|
||||||
|
|
||||||
|
|
||||||
// First, set up an HTTPS server instance with one
|
// First, set up an HTTPS server instance with one
|
||||||
// of the certificate. This will act as default certificate
|
// of the certificate. This will act as default certificate
|
||||||
// when ecrypted connections don't target a specific domain.
|
// when ecrypted connections don't target a specific domain.
|
||||||
|
|
||||||
HTTP_String local_addr = HTTP_STR("127.0.0.1");
|
CHTTP_String local_addr = CHTTP_STR("127.0.0.1");
|
||||||
uint16_t local_port = 8443;
|
uint16_t local_port = 8443;
|
||||||
|
|
||||||
HTTP_String cert_file = HTTP_STR("websiteA_cert.pem");
|
CHTTP_String cert_file = CHTTP_STR("websiteA_cert.pem");
|
||||||
HTTP_String key_file = HTTP_STR("websiteA_key.pem");
|
CHTTP_String key_file = CHTTP_STR("websiteA_key.pem");
|
||||||
|
|
||||||
ret = http_server_listen_tls(&server, local_addr, local_port, cert_file, key_file);
|
ret = chttp_server_listen_tls(&server, local_addr, local_port, cert_file, key_file);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "Couldn't start listening (%s)\n", http_strerror(ret));
|
fprintf(stderr, "Couldn't start listening (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then we can add an arbitrary number of additional
|
// Then we can add an arbitrary number of additional
|
||||||
// certificates using the add_website function
|
// certificates using the add_website function
|
||||||
|
|
||||||
ret = http_server_add_certificate(&server,
|
ret = chttp_server_add_certificate(&server,
|
||||||
HTTP_STR("websiteB.com"),
|
CHTTP_STR("websiteB.com"),
|
||||||
HTTP_STR("websiteB_cert.pem"),
|
CHTTP_STR("websiteB_cert.pem"),
|
||||||
HTTP_STR("websiteB_key.pem"));
|
CHTTP_STR("websiteB_key.pem"));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ret = http_server_add_certificate(&server,
|
ret = chttp_server_add_certificate(&server,
|
||||||
HTTP_STR("websiteC.com"),
|
CHTTP_STR("websiteC.com"),
|
||||||
HTTP_STR("websiteC_cert.pem"),
|
CHTTP_STR("websiteC_cert.pem"),
|
||||||
HTTP_STR("websiteC_key.pem"));
|
CHTTP_STR("websiteC_key.pem"));
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@@ -109,67 +109,67 @@ int main(void)
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
HTTP_Request *req;
|
CHTTP_Request *req;
|
||||||
HTTP_ResponseBuilder builder;
|
CHTTP_ResponseBuilder builder;
|
||||||
http_server_wait_request(&server, &req, &builder);
|
chttp_server_wait_request(&server, &req, &builder);
|
||||||
|
|
||||||
if (http_match_host(req, HTTP_STR("websiteB.com"), 8080) ||
|
if (chttp_match_host(req, CHTTP_STR("websiteB.com"), 8080) ||
|
||||||
http_match_host(req, HTTP_STR("websiteB.com"), 8443)) {
|
chttp_match_host(req, CHTTP_STR("websiteB.com"), 8443)) {
|
||||||
|
|
||||||
http_response_builder_status(builder, 200);
|
chttp_response_builder_status(builder, 200);
|
||||||
http_response_builder_body(builder, HTTP_STR("Hello from websiteB.com!"));
|
chttp_response_builder_body(builder, CHTTP_STR("Hello from websiteB.com!"));
|
||||||
http_response_builder_send(builder);
|
chttp_response_builder_send(builder);
|
||||||
|
|
||||||
} else if (http_match_host(req, HTTP_STR("websiteC.com"), 8080) ||
|
} else if (chttp_match_host(req, CHTTP_STR("websiteC.com"), 8080) ||
|
||||||
http_match_host(req, HTTP_STR("websiteC.com"), 8443)) {
|
chttp_match_host(req, CHTTP_STR("websiteC.com"), 8443)) {
|
||||||
|
|
||||||
http_response_builder_status(builder, 200);
|
chttp_response_builder_status(builder, 200);
|
||||||
http_response_builder_body(builder, HTTP_STR("Hello from websiteC.com!"));
|
chttp_response_builder_body(builder, CHTTP_STR("Hello from websiteC.com!"));
|
||||||
http_response_builder_send(builder);
|
chttp_response_builder_send(builder);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Serve websiteA.com by default to be consistent
|
// Serve websiteA.com by default to be consistent
|
||||||
// with the certificate setup
|
// with the certificate setup
|
||||||
|
|
||||||
http_response_builder_status(builder, 200);
|
chttp_response_builder_status(builder, 200);
|
||||||
http_response_builder_body(builder, HTTP_STR("Hello from websiteA.com!"));
|
chttp_response_builder_body(builder, CHTTP_STR("Hello from websiteA.com!"));
|
||||||
http_response_builder_send(builder);
|
chttp_response_builder_send(builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_free(&server);
|
chttp_server_free(&server);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int setup_test_certificates(void)
|
int setup_test_certificates(void)
|
||||||
{
|
{
|
||||||
int ret = http_create_test_certificate(
|
int ret = chttp_create_test_certificate(
|
||||||
HTTP_STR("IT"),
|
CHTTP_STR("IT"),
|
||||||
HTTP_STR("Organization A"),
|
CHTTP_STR("Organization A"),
|
||||||
HTTP_STR("websiteA.com"),
|
CHTTP_STR("websiteA.com"),
|
||||||
HTTP_STR("websiteA_cert.pem"),
|
CHTTP_STR("websiteA_cert.pem"),
|
||||||
HTTP_STR("websiteA_key.pem")
|
CHTTP_STR("websiteA_key.pem")
|
||||||
);
|
);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ret = http_create_test_certificate(
|
ret = chttp_create_test_certificate(
|
||||||
HTTP_STR("IT"),
|
CHTTP_STR("IT"),
|
||||||
HTTP_STR("Organization B"),
|
CHTTP_STR("Organization B"),
|
||||||
HTTP_STR("websiteB.com"),
|
CHTTP_STR("websiteB.com"),
|
||||||
HTTP_STR("websiteB_cert.pem"),
|
CHTTP_STR("websiteB_cert.pem"),
|
||||||
HTTP_STR("websiteB_key.pem")
|
CHTTP_STR("websiteB_key.pem")
|
||||||
);
|
);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ret = http_create_test_certificate(
|
ret = chttp_create_test_certificate(
|
||||||
HTTP_STR("IT"),
|
CHTTP_STR("IT"),
|
||||||
HTTP_STR("Organization C"),
|
CHTTP_STR("Organization C"),
|
||||||
HTTP_STR("websiteC.com"),
|
CHTTP_STR("websiteC.com"),
|
||||||
HTTP_STR("websiteC_cert.pem"),
|
CHTTP_STR("websiteC_cert.pem"),
|
||||||
HTTP_STR("websiteC_key.pem")
|
CHTTP_STR("websiteC_key.pem")
|
||||||
);
|
);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -4,37 +4,37 @@ int main(void)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
HTTP_Client client;
|
CHTTP_Client client;
|
||||||
ret = http_client_init(&client);
|
ret = chttp_client_init(&client);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printf("Couldn't initialize client (%s)\n", http_strerror(ret));
|
printf("Couldn't initialize client (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_RequestBuilder builder = http_client_get_builder(&client);
|
CHTTP_RequestBuilder builder = chttp_client_get_builder(&client);
|
||||||
|
|
||||||
http_request_builder_method(builder, HTTP_METHOD_GET);
|
chttp_request_builder_method(builder, CHTTP_METHOD_GET);
|
||||||
http_request_builder_target(builder, HTTP_STR("http://coz.is"));
|
chttp_request_builder_target(builder, CHTTP_STR("http://coz.is"));
|
||||||
http_request_builder_header(builder, HTTP_STR("Greeting: Hello from the cHTTP example!"));
|
chttp_request_builder_header(builder, CHTTP_STR("Greeting: Hello from the cHTTP example!"));
|
||||||
|
|
||||||
ret = http_request_builder_send(builder);
|
ret = chttp_request_builder_send(builder);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printf("Couldn't build request (%s)\n", http_strerror(ret));
|
printf("Couldn't build request (%s)\n", chttp_strerror(ret));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
void *user;
|
void *user;
|
||||||
HTTP_Response *response;
|
CHTTP_Response *response;
|
||||||
http_client_wait_response(&client, &result, &user, &response);
|
chttp_client_wait_response(&client, &result, &user, &response);
|
||||||
|
|
||||||
if (result == HTTP_OK) {
|
if (result == CHTTP_OK) {
|
||||||
printf("Received %d bytes\n", response->body.len);
|
printf("Received %d bytes\n", response->body.len);
|
||||||
} else {
|
} else {
|
||||||
printf("Couldn't receive response (%s)\n", http_strerror(result));
|
printf("Couldn't receive response (%s)\n", chttp_strerror(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
http_free_response(response);
|
chttp_free_response(response);
|
||||||
http_client_free(&client);
|
chttp_client_free(&client);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -1,3 +1,3 @@
|
|||||||
- Add connection timeouts
|
[ ] Per RFC 6265, cookie values can be quoted. This parser would include the quotes in the value.
|
||||||
- Per RFC 6265, cookie values can be quoted. This parser would include the quotes in the value.
|
[ ] Return an appropriate error code when the user waits on a client with no pending requests
|
||||||
- Make sure that the proxy example doesn't stall
|
[ ] Add connection reuse limit
|
||||||
+6
-1
@@ -53,10 +53,13 @@ license = """
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
header = Amalgamator()
|
header = Amalgamator()
|
||||||
|
header.append_text("#ifndef CHTTP_INCLUDED\n")
|
||||||
|
header.append_text("#define CHTTP_INCLUDED\n")
|
||||||
header.append_text(desc)
|
header.append_text(desc)
|
||||||
header.append_file("src/includes.h")
|
header.append_file("src/includes.h")
|
||||||
header.append_file("src/basic.h")
|
header.append_file("src/basic.h")
|
||||||
header.append_file("src/parse.h")
|
header.append_file("src/parse.h")
|
||||||
|
header.append_file("src/time.h")
|
||||||
header.append_file("src/secure_context.h")
|
header.append_file("src/secure_context.h")
|
||||||
header.append_file("src/socket.h")
|
header.append_file("src/socket.h")
|
||||||
header.append_file("src/byte_queue.h")
|
header.append_file("src/byte_queue.h")
|
||||||
@@ -64,16 +67,18 @@ header.append_file("src/cert.h")
|
|||||||
header.append_file("src/client.h")
|
header.append_file("src/client.h")
|
||||||
header.append_file("src/server.h")
|
header.append_file("src/server.h")
|
||||||
header.append_text(license)
|
header.append_text(license)
|
||||||
|
header.append_text("#endif // CHTTP_INCLUDED\n")
|
||||||
header.save("chttp.h")
|
header.save("chttp.h")
|
||||||
|
|
||||||
source = Amalgamator()
|
source = Amalgamator()
|
||||||
source.append_text(desc)
|
source.append_text(desc)
|
||||||
source.append_text("\n")
|
source.append_text("\n")
|
||||||
source.append_text("#ifndef HTTP_DONT_INCLUDE\n")
|
source.append_text("#ifndef CHTTP_DONT_INCLUDE\n")
|
||||||
source.append_text('#include "chttp.h"\n')
|
source.append_text('#include "chttp.h"\n')
|
||||||
source.append_text("#endif\n")
|
source.append_text("#endif\n")
|
||||||
source.append_file("src/basic.c")
|
source.append_file("src/basic.c")
|
||||||
source.append_file("src/parse.c")
|
source.append_file("src/parse.c")
|
||||||
|
source.append_file("src/time.c")
|
||||||
source.append_file("src/secure_context.c")
|
source.append_file("src/secure_context.c")
|
||||||
source.append_file("src/socket.c")
|
source.append_file("src/socket.c")
|
||||||
source.append_file("src/byte_queue.c")
|
source.append_file("src/byte_queue.c")
|
||||||
|
|||||||
+12
-12
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
bool http_streq(HTTP_String s1, HTTP_String s2)
|
bool chttp_streq(CHTTP_String s1, CHTTP_String s2)
|
||||||
{
|
{
|
||||||
if (s1.len != s2.len)
|
if (s1.len != s2.len)
|
||||||
return false;
|
return false;
|
||||||
@@ -18,7 +18,7 @@ static char to_lower(char c)
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool http_streqcase(HTTP_String s1, HTTP_String s2)
|
bool chttp_streqcase(CHTTP_String s1, CHTTP_String s2)
|
||||||
{
|
{
|
||||||
if (s1.len != s2.len)
|
if (s1.len != s2.len)
|
||||||
return false;
|
return false;
|
||||||
@@ -30,7 +30,7 @@ bool http_streqcase(HTTP_String s1, HTTP_String s2)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String http_trim(HTTP_String s)
|
CHTTP_String chttp_trim(CHTTP_String s)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < s.len && (s.ptr[i] == ' ' || s.ptr[i] == '\t'))
|
while (i < s.len && (s.ptr[i] == ' ' || s.ptr[i] == '\t'))
|
||||||
@@ -54,7 +54,7 @@ static bool is_printable(char c)
|
|||||||
return c >= ' ' && c <= '~';
|
return c >= ' ' && c <= '~';
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_bytes(HTTP_String prefix, HTTP_String src)
|
void print_bytes(CHTTP_String prefix, CHTTP_String src)
|
||||||
{
|
{
|
||||||
if (src.len == 0)
|
if (src.len == 0)
|
||||||
return;
|
return;
|
||||||
@@ -95,16 +95,16 @@ void print_bytes(HTTP_String prefix, HTTP_String src)
|
|||||||
putc('\n', stream);
|
putc('\n', stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *http_strerror(int code)
|
char *chttp_strerror(int code)
|
||||||
{
|
{
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case HTTP_OK: return "No error";
|
case CHTTP_OK: return "No error";
|
||||||
case HTTP_ERROR_UNSPECIFIED: return "Unspecified error";
|
case CHTTP_ERROR_UNSPECIFIED: return "Unspecified error";
|
||||||
case HTTP_ERROR_OOM: return "Out of memory";
|
case CHTTP_ERROR_OOM: return "Out of memory";
|
||||||
case HTTP_ERROR_BADURL: return "Invalid URL";
|
case CHTTP_ERROR_BADURL: return "Invalid URL";
|
||||||
case HTTP_ERROR_REQLIMIT: return "Parallel request limit reached";
|
case CHTTP_ERROR_REQLIMIT: return "Parallel request limit reached";
|
||||||
case HTTP_ERROR_BADHANDLE: return "Invalid handle";
|
case CHTTP_ERROR_BADHANDLE: return "Invalid handle";
|
||||||
case HTTP_ERROR_NOTLS: return "TLS support not built-in";
|
case CHTTP_ERROR_NOTLS: return "TLS support not built-in";
|
||||||
}
|
}
|
||||||
return "???";
|
return "???";
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-22
@@ -1,56 +1,56 @@
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
||||||
HTTP_OK = 0,
|
CHTTP_OK = 0,
|
||||||
|
|
||||||
// A generic error occurred
|
// A generic error occurred
|
||||||
HTTP_ERROR_UNSPECIFIED = -1,
|
CHTTP_ERROR_UNSPECIFIED = -1,
|
||||||
|
|
||||||
// Out of memory
|
// Out of memory
|
||||||
HTTP_ERROR_OOM = -2,
|
CHTTP_ERROR_OOM = -2,
|
||||||
|
|
||||||
// Invalid URL
|
// Invalid URL
|
||||||
HTTP_ERROR_BADURL = -3,
|
CHTTP_ERROR_BADURL = -3,
|
||||||
|
|
||||||
// Parallel request limit reached
|
// Parallel request limit reached
|
||||||
HTTP_ERROR_REQLIMIT = -4,
|
CHTTP_ERROR_REQLIMIT = -4,
|
||||||
|
|
||||||
// Invalid handle
|
// Invalid handle
|
||||||
HTTP_ERROR_BADHANDLE = -5,
|
CHTTP_ERROR_BADHANDLE = -5,
|
||||||
|
|
||||||
// TLS support not built-in
|
// TLS support not built-in
|
||||||
HTTP_ERROR_NOTLS = -6,
|
CHTTP_ERROR_NOTLS = -6,
|
||||||
};
|
};
|
||||||
|
|
||||||
// String type used throughout cHTTP.
|
// String type used throughout cHTTP.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int len;
|
int len;
|
||||||
} HTTP_String;
|
} CHTTP_String;
|
||||||
|
|
||||||
// Compare two strings and return true iff they have
|
// Compare two strings and return true iff they have
|
||||||
// the same contents.
|
// the same contents.
|
||||||
bool http_streq(HTTP_String s1, HTTP_String s2);
|
bool chttp_streq(CHTTP_String s1, CHTTP_String s2);
|
||||||
|
|
||||||
// Compre two strings case-insensitively (uppercase and
|
// Compre two strings case-insensitively (uppercase and
|
||||||
// lowercase versions of a letter are considered the same)
|
// lowercase versions of a letter are considered the same)
|
||||||
// and return true iff they have the same contents.
|
// and return true iff they have the same contents.
|
||||||
bool http_streqcase(HTTP_String s1, HTTP_String s2);
|
bool chttp_streqcase(CHTTP_String s1, CHTTP_String s2);
|
||||||
|
|
||||||
// Remove spaces and tabs from the start and the end of
|
// Remove spaces and tabs from the start and the end of
|
||||||
// a string. This doesn't change the original string and
|
// a string. This doesn't change the original string and
|
||||||
// the new one references the contents of the original one.
|
// the new one references the contents of the original one.
|
||||||
HTTP_String http_trim(HTTP_String s);
|
CHTTP_String chttp_trim(CHTTP_String s);
|
||||||
|
|
||||||
// Print the contents of a byte string with the given prefix.
|
// Print the contents of a byte string with the given prefix.
|
||||||
// This is primarily used for debugging purposes.
|
// This is primarily used for debugging purposes.
|
||||||
void print_bytes(HTTP_String prefix, HTTP_String src);
|
void print_bytes(CHTTP_String prefix, CHTTP_String src);
|
||||||
|
|
||||||
// TODO: comment
|
// TODO: comment
|
||||||
char *http_strerror(int code);
|
char *chttp_strerror(int code);
|
||||||
|
|
||||||
// Macro to simplify converting string literals to
|
// Macro to simplify converting string literals to
|
||||||
// HTTP_String.
|
// CHTTP_String.
|
||||||
//
|
//
|
||||||
// Instead of doing this:
|
// Instead of doing this:
|
||||||
//
|
//
|
||||||
@@ -58,7 +58,7 @@ char *http_strerror(int code);
|
|||||||
//
|
//
|
||||||
// You do this:
|
// You do this:
|
||||||
//
|
//
|
||||||
// HTTP_String s = HTTP_STR("some string")
|
// CHTTP_String s = CHTTP_STR("some string")
|
||||||
//
|
//
|
||||||
// This is a bit cumbersome, but better than null-terminated
|
// This is a bit cumbersome, but better than null-terminated
|
||||||
// strings, having a pointer and length variable pairs whenever
|
// strings, having a pointer and length variable pairs whenever
|
||||||
@@ -68,15 +68,15 @@ char *http_strerror(int code);
|
|||||||
// #define S(X) ...
|
// #define S(X) ...
|
||||||
//
|
//
|
||||||
// But I don't want to cause collisions with user code.
|
// But I don't want to cause collisions with user code.
|
||||||
#define HTTP_STR(X) ((HTTP_String) {(X), sizeof(X)-1})
|
#define CHTTP_STR(X) ((CHTTP_String) {(X), sizeof(X)-1})
|
||||||
|
|
||||||
// Returns the number of items of a static array.
|
// Returns the number of items of a static array.
|
||||||
#define HTTP_COUNT(X) (sizeof(X) / sizeof((X)[0]))
|
#define CHTTP_COUNT(X) (int) (sizeof(X) / sizeof((X)[0]))
|
||||||
|
|
||||||
// Macro to unpack an HTTP_String into its length and pointer components.
|
// Macro to unpack an CHTTP_String into its length and pointer components.
|
||||||
// Useful for passing HTTP_String to printf-style functions with "%.*s" format.
|
// Useful for passing CHTTP_String to printf-style functions with "%.*s" format.
|
||||||
// Example: printf("%.*s", HTTP_UNPACK(str));
|
// Example: printf("%.*s", CHTTP_UNPACK(str));
|
||||||
#define HTTP_UNPACK(X) (X).len, (X).ptr
|
#define CHTTP_UNPACK(X) (X).len, (X).ptr
|
||||||
|
|
||||||
// TODO: comment
|
// TODO: comment
|
||||||
#define HTTP_UNREACHABLE __builtin_trap()
|
#define CHTTP_UNREACHABLE __builtin_trap()
|
||||||
|
|||||||
+3
-3
@@ -179,7 +179,7 @@ int byte_queue_write_setmincap(ByteQueue *queue, uint32_t mincap)
|
|||||||
if (size > queue->limit)
|
if (size > queue->limit)
|
||||||
size = queue->limit;
|
size = queue->limit;
|
||||||
|
|
||||||
uint8_t *data = malloc(size);
|
char *data = malloc(size);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
queue->flags |= BYTE_QUEUE_ERROR;
|
queue->flags |= BYTE_QUEUE_ERROR;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -236,7 +236,7 @@ void byte_queue_write_fmt2(ByteQueue *queue,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len > dst.len) {
|
if ((size_t) len > dst.len) {
|
||||||
byte_queue_write_ack(queue, 0);
|
byte_queue_write_ack(queue, 0);
|
||||||
byte_queue_write_setmincap(queue, len+1);
|
byte_queue_write_setmincap(queue, len+1);
|
||||||
dst = byte_queue_write_buf(queue);
|
dst = byte_queue_write_buf(queue);
|
||||||
@@ -277,7 +277,7 @@ void byte_queue_patch(ByteQueue *queue, ByteQueueOffset off,
|
|||||||
assert(len <= queue->used - (off - queue->curs));
|
assert(len <= queue->used - (off - queue->curs));
|
||||||
|
|
||||||
// Perform the patch
|
// Perform the patch
|
||||||
uint8_t *dst = queue->data + queue->head + (off - queue->curs);
|
char *dst = queue->data + queue->head + (off - queue->curs);
|
||||||
memcpy(dst, src, len);
|
memcpy(dst, src, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -15,19 +15,19 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t *ptr;
|
char *ptr;
|
||||||
size_t len;
|
size_t len;
|
||||||
} ByteView;
|
} ByteView;
|
||||||
|
|
||||||
// Fields are for internal use only
|
// Fields are for internal use only
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t curs;
|
uint64_t curs;
|
||||||
uint8_t* data;
|
char* data;
|
||||||
uint32_t head;
|
uint32_t head;
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t used;
|
uint32_t used;
|
||||||
uint32_t limit;
|
uint32_t limit;
|
||||||
uint8_t* read_target;
|
char* read_target;
|
||||||
uint32_t read_target_size;
|
uint32_t read_target_size;
|
||||||
int flags;
|
int flags;
|
||||||
} ByteQueue;
|
} ByteQueue;
|
||||||
@@ -41,7 +41,7 @@ typedef uint64_t ByteQueueOffset;
|
|||||||
// Initialize the queue with a given capacity limit.
|
// Initialize the queue with a given capacity limit.
|
||||||
// This is just a soft limit. The queue will allocate
|
// This is just a soft limit. The queue will allocate
|
||||||
// dynamically as needed up to this limit and won't
|
// dynamically as needed up to this limit and won't
|
||||||
// grow further. When the limit is reached, http_queue_full
|
// grow further. When the limit is reached, chttp_queue_full
|
||||||
// returns true.
|
// returns true.
|
||||||
void byte_queue_init(ByteQueue *queue, uint32_t limit);
|
void byte_queue_init(ByteQueue *queue, uint32_t limit);
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -27,7 +27,7 @@ static EVP_PKEY *generate_rsa_key_pair(int key_bits)
|
|||||||
return pkey;
|
return pkey;
|
||||||
}
|
}
|
||||||
|
|
||||||
static X509 *create_certificate(EVP_PKEY *pkey, HTTP_String C, HTTP_String O, HTTP_String CN, int days)
|
static X509 *create_certificate(EVP_PKEY *pkey, CHTTP_String C, CHTTP_String O, CHTTP_String CN, int days)
|
||||||
{
|
{
|
||||||
X509 *x509 = X509_new();
|
X509 *x509 = X509_new();
|
||||||
if (!x509)
|
if (!x509)
|
||||||
@@ -63,7 +63,7 @@ static X509 *create_certificate(EVP_PKEY *pkey, HTTP_String C, HTTP_String O, HT
|
|||||||
return x509;
|
return x509;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int save_private_key(EVP_PKEY *pkey, HTTP_String file)
|
static int save_private_key(EVP_PKEY *pkey, CHTTP_String file)
|
||||||
{
|
{
|
||||||
char copy[1<<10];
|
char copy[1<<10];
|
||||||
if (file.len >= (int) sizeof(copy))
|
if (file.len >= (int) sizeof(copy))
|
||||||
@@ -85,7 +85,7 @@ static int save_private_key(EVP_PKEY *pkey, HTTP_String file)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int save_certificate(X509 *x509, HTTP_String file)
|
static int save_certificate(X509 *x509, CHTTP_String file)
|
||||||
{
|
{
|
||||||
char copy[1<<10];
|
char copy[1<<10];
|
||||||
if (file.len >= (int) sizeof(copy))
|
if (file.len >= (int) sizeof(copy))
|
||||||
@@ -107,8 +107,8 @@ static int save_certificate(X509 *x509, HTTP_String file)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
|
int chttp_create_test_certificate(CHTTP_String C, CHTTP_String O, CHTTP_String CN,
|
||||||
HTTP_String cert_file, HTTP_String key_file)
|
CHTTP_String cert_file, CHTTP_String key_file)
|
||||||
{
|
{
|
||||||
EVP_PKEY *pkey = generate_rsa_key_pair(2048);
|
EVP_PKEY *pkey = generate_rsa_key_pair(2048);
|
||||||
if (pkey == NULL)
|
if (pkey == NULL)
|
||||||
@@ -139,8 +139,8 @@ int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
|
int chttp_create_test_certificate(CHTTP_String C, CHTTP_String O, CHTTP_String CN,
|
||||||
HTTP_String cert_file, HTTP_String key_file)
|
CHTTP_String cert_file, CHTTP_String key_file)
|
||||||
{
|
{
|
||||||
(void) C;
|
(void) C;
|
||||||
(void) O;
|
(void) O;
|
||||||
|
|||||||
+2
-2
@@ -12,5 +12,5 @@
|
|||||||
//
|
//
|
||||||
// The output is a certificate file in PEM format and a private
|
// The output is a certificate file in PEM format and a private
|
||||||
// key file with the key used to sign the certificate.
|
// key file with the key used to sign the certificate.
|
||||||
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
|
int chttp_create_test_certificate(CHTTP_String C, CHTTP_String O, CHTTP_String CN,
|
||||||
HTTP_String cert_file, HTTP_String key_file);
|
CHTTP_String cert_file, CHTTP_String key_file);
|
||||||
|
|||||||
+246
-347
File diff suppressed because it is too large
Load Diff
+87
-77
@@ -1,26 +1,26 @@
|
|||||||
|
|
||||||
#ifndef HTTP_CLIENT_CAPACITY
|
#ifndef CHTTP_CLIENT_CAPACITY
|
||||||
// The maximum ammount of requests that can be performed
|
// The maximum ammount of requests that can be performed
|
||||||
// in parallel.
|
// in parallel.
|
||||||
#define HTTP_CLIENT_CAPACITY (1<<7)
|
#define CHTTP_CLIENT_CAPACITY (1<<7)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Maximum number of descriptors the client will want
|
// Maximum number of descriptors the client will want
|
||||||
// to wait on. It's one per connection plus the wakeup
|
// to wait on. It's one per connection plus the wakeup
|
||||||
// self-pipe.
|
// self-pipe.
|
||||||
#define HTTP_CLIENT_POLL_CAPACITY (HTTP_CLIENT_CAPACITY+1)
|
#define CHTTP_CLIENT_POLL_CAPACITY (CHTTP_CLIENT_CAPACITY+1)
|
||||||
|
|
||||||
#ifndef HTTP_COOKIE_JAR_CAPACITY
|
#ifndef CHTTP_COOKIE_JAR_CAPACITY
|
||||||
// Maximum number of cookies that can be associated to a
|
// Maximum number of cookies that can be associated to a
|
||||||
// single client.
|
// single client.
|
||||||
#define HTTP_COOKIE_JAR_CAPACITY 128
|
#define CHTTP_COOKIE_JAR_CAPACITY 128
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
// Cookie name and value
|
// Cookie name and value
|
||||||
HTTP_String name;
|
CHTTP_String name;
|
||||||
HTTP_String value;
|
CHTTP_String value;
|
||||||
|
|
||||||
// If the "exact_domain" is true, the cookie
|
// If the "exact_domain" is true, the cookie
|
||||||
// can only be sent to the exact domain referred
|
// can only be sent to the exact domain referred
|
||||||
@@ -28,7 +28,7 @@ typedef struct {
|
|||||||
// "exact_domain" is false, then the cookie is
|
// "exact_domain" is false, then the cookie is
|
||||||
// compatible with subdomains.
|
// compatible with subdomains.
|
||||||
bool exact_domain;
|
bool exact_domain;
|
||||||
HTTP_String domain;
|
CHTTP_String domain;
|
||||||
|
|
||||||
// If "exact_path" is set, the cookie is only
|
// If "exact_path" is set, the cookie is only
|
||||||
// compatible with requests to paths that match
|
// compatible with requests to paths that match
|
||||||
@@ -36,47 +36,40 @@ typedef struct {
|
|||||||
// then any path that starts with "path" is
|
// then any path that starts with "path" is
|
||||||
// compatible with the cookie.
|
// compatible with the cookie.
|
||||||
bool exact_path;
|
bool exact_path;
|
||||||
HTTP_String path;
|
CHTTP_String path;
|
||||||
|
|
||||||
// This cookie can only be sent over HTTPS
|
// This cookie can only be sent over HTTPS
|
||||||
bool secure;
|
bool secure;
|
||||||
|
|
||||||
// Expiration information
|
} CHTTP_CookieJarEntry;
|
||||||
bool have_expires;
|
|
||||||
HTTP_Date expires;
|
|
||||||
bool have_max_age;
|
|
||||||
uint32_t max_age;
|
|
||||||
time_t creation_time; // Unix timestamp when cookie was created
|
|
||||||
|
|
||||||
} HTTP_CookieJarEntry;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int count;
|
int count;
|
||||||
HTTP_CookieJarEntry items[HTTP_COOKIE_JAR_CAPACITY];
|
CHTTP_CookieJarEntry items[CHTTP_COOKIE_JAR_CAPACITY];
|
||||||
} HTTP_CookieJar;
|
} CHTTP_CookieJar;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
HTTP_CLIENT_CONN_FREE,
|
CHTTP_CLIENT_CONN_FREE,
|
||||||
HTTP_CLIENT_CONN_WAIT_METHOD,
|
CHTTP_CLIENT_CONN_WAIT_METHOD,
|
||||||
HTTP_CLIENT_CONN_WAIT_URL,
|
CHTTP_CLIENT_CONN_WAIT_URL,
|
||||||
HTTP_CLIENT_CONN_WAIT_HEADER,
|
CHTTP_CLIENT_CONN_WAIT_HEADER,
|
||||||
HTTP_CLIENT_CONN_WAIT_BODY,
|
CHTTP_CLIENT_CONN_WAIT_BODY,
|
||||||
HTTP_CLIENT_CONN_FLUSHING,
|
CHTTP_CLIENT_CONN_FLUSHING,
|
||||||
HTTP_CLIENT_CONN_BUFFERING,
|
CHTTP_CLIENT_CONN_BUFFERING,
|
||||||
HTTP_CLIENT_CONN_COMPLETE,
|
CHTTP_CLIENT_CONN_COMPLETE,
|
||||||
} HTTP_ClientConnState;
|
} CHTTP_ClientConnState;
|
||||||
|
|
||||||
// Fields of this struct are private
|
// Fields of this struct are private
|
||||||
typedef struct HTTP_Client HTTP_Client;
|
typedef struct CHTTP_Client CHTTP_Client;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HTTP_ClientConnState state;
|
CHTTP_ClientConnState state;
|
||||||
|
|
||||||
// Handle to the socket
|
// Handle to the socket
|
||||||
SocketHandle handle;
|
SocketHandle handle;
|
||||||
|
|
||||||
// Pointer back to the client
|
// Pointer back to the client
|
||||||
HTTP_Client *client;
|
CHTTP_Client *client;
|
||||||
|
|
||||||
// Generation counter for request builder validation
|
// Generation counter for request builder validation
|
||||||
uint16_t gen;
|
uint16_t gen;
|
||||||
@@ -88,12 +81,15 @@ typedef struct {
|
|||||||
// TODO: comment
|
// TODO: comment
|
||||||
bool trace_bytes;
|
bool trace_bytes;
|
||||||
|
|
||||||
|
// TODO: comment
|
||||||
|
bool dont_verify_cert;
|
||||||
|
|
||||||
// Allocated copy of the URL string
|
// Allocated copy of the URL string
|
||||||
HTTP_String url_buffer;
|
CHTTP_String url_buffer;
|
||||||
|
|
||||||
// Parsed URL for connection establishment
|
// Parsed URL for connection establishment
|
||||||
// All url.* pointers reference into url_buffer
|
// All url.* pointers reference into url_buffer
|
||||||
HTTP_URL url;
|
CHTTP_URL url;
|
||||||
|
|
||||||
// Data received from the server
|
// Data received from the server
|
||||||
ByteQueue input;
|
ByteQueue input;
|
||||||
@@ -108,11 +104,21 @@ typedef struct {
|
|||||||
int result;
|
int result;
|
||||||
|
|
||||||
// Parsed response once complete
|
// Parsed response once complete
|
||||||
HTTP_Response response;
|
CHTTP_Response response;
|
||||||
} HTTP_ClientConn;
|
|
||||||
|
// This offset points to the first byte that comes
|
||||||
|
// after the string "Content-Length: ".
|
||||||
|
ByteQueueOffset content_length_value_offset;
|
||||||
|
|
||||||
|
// This one points to the first byte of the body.
|
||||||
|
// This allows calculating the length of the request
|
||||||
|
// content byte subtracting it from the offset reached
|
||||||
|
// when the request is marked as done.
|
||||||
|
ByteQueueOffset content_length_offset;
|
||||||
|
} CHTTP_ClientConn;
|
||||||
|
|
||||||
// Fields of this struct are private
|
// Fields of this struct are private
|
||||||
struct HTTP_Client {
|
struct CHTTP_Client {
|
||||||
|
|
||||||
// Size limit of the input and output buffer of each
|
// Size limit of the input and output buffer of each
|
||||||
// connection.
|
// connection.
|
||||||
@@ -120,18 +126,18 @@ struct HTTP_Client {
|
|||||||
uint32_t output_buffer_limit;
|
uint32_t output_buffer_limit;
|
||||||
|
|
||||||
// List of cookies created during this session
|
// List of cookies created during this session
|
||||||
HTTP_CookieJar cookie_jar;
|
CHTTP_CookieJar cookie_jar;
|
||||||
|
|
||||||
// Array of connections. The counter contains the
|
// Array of connections. The counter contains the
|
||||||
// number of structs such that state!=FREE.
|
// number of structs such that state!=FREE.
|
||||||
int num_conns;
|
int num_conns;
|
||||||
HTTP_ClientConn conns[HTTP_CLIENT_CAPACITY];
|
CHTTP_ClientConn conns[CHTTP_CLIENT_CAPACITY];
|
||||||
|
|
||||||
// Queue of indices referring to connections that
|
// Queue of indices referring to connections that
|
||||||
// are in the COMPLETE state.
|
// are in the COMPLETE state.
|
||||||
int num_ready;
|
int num_ready;
|
||||||
int ready_head;
|
int ready_head;
|
||||||
int ready[HTTP_CLIENT_CAPACITY];
|
int ready[CHTTP_CLIENT_CAPACITY];
|
||||||
|
|
||||||
// Asynchronous TCP and TLS socket abstraction
|
// Asynchronous TCP and TLS socket abstraction
|
||||||
SocketManager sockets;
|
SocketManager sockets;
|
||||||
@@ -141,76 +147,80 @@ struct HTTP_Client {
|
|||||||
// manager with a pointer to it. This allows
|
// manager with a pointer to it. This allows
|
||||||
// allocating the exact number of sockets we
|
// allocating the exact number of sockets we
|
||||||
// will need.
|
// will need.
|
||||||
Socket socket_pool[HTTP_CLIENT_CAPACITY];
|
Socket socket_pool[CHTTP_CLIENT_CAPACITY];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize an HTTP client object. This allows one to
|
// Initialize an HTTP client object. This allows one to
|
||||||
// perform a number of requests in parallel.
|
// perform a number of requests in parallel.
|
||||||
int http_client_init(HTTP_Client *client);
|
int chttp_client_init(CHTTP_Client *client);
|
||||||
|
|
||||||
// Release resources associated to a client object.
|
// Release resources associated to a client object.
|
||||||
void http_client_free(HTTP_Client *client);
|
void chttp_client_free(CHTTP_Client *client);
|
||||||
|
|
||||||
// Set input and output buffer size limit for any
|
// Set input and output buffer size limit for any
|
||||||
// given connection. The default value is 1MB
|
// given connection. The default value is 1MB
|
||||||
void http_client_set_input_limit(HTTP_Client *client, uint32_t limit);
|
void chttp_client_set_input_limit(CHTTP_Client *client, uint32_t limit);
|
||||||
void http_client_set_output_limit(HTTP_Client *client, uint32_t limit);
|
void chttp_client_set_output_limit(CHTTP_Client *client, uint32_t limit);
|
||||||
|
|
||||||
// When a thread is blocked waiting for client events,
|
// When a thread is blocked waiting for client events,
|
||||||
// other threads can call this function to wake it up.
|
// other threads can call this function to wake it up.
|
||||||
int http_client_wakeup(HTTP_Client *client);
|
int chttp_client_wakeup(CHTTP_Client *client);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HTTP_Client *client;
|
CHTTP_Client *client;
|
||||||
uint16_t index;
|
uint16_t index;
|
||||||
uint16_t gen;
|
uint16_t gen;
|
||||||
} HTTP_RequestBuilder;
|
} CHTTP_RequestBuilder;
|
||||||
|
|
||||||
// Create a new request builder object.
|
// Create a new request builder object.
|
||||||
HTTP_RequestBuilder http_client_get_builder(HTTP_Client *client);
|
CHTTP_RequestBuilder chttp_client_get_builder(CHTTP_Client *client);
|
||||||
|
|
||||||
// TODO: comment
|
// TODO: comment
|
||||||
void http_request_builder_set_user(HTTP_RequestBuilder builder,
|
void chttp_request_builder_set_user(CHTTP_RequestBuilder builder,
|
||||||
void *user);
|
void *user);
|
||||||
|
|
||||||
// TODO: comment
|
// TODO: comment
|
||||||
void http_request_builder_trace(HTTP_RequestBuilder builder,
|
void chttp_request_builder_trace(CHTTP_RequestBuilder builder,
|
||||||
bool trace_bytes);
|
bool trace_bytes);
|
||||||
|
|
||||||
|
// TODO: comment
|
||||||
|
void chttp_request_builder_insecure(CHTTP_RequestBuilder builder,
|
||||||
|
bool insecure);
|
||||||
|
|
||||||
// Set the method of the current request. This is the first
|
// Set the method of the current request. This is the first
|
||||||
// function of the request builder that the user must call.
|
// function of the request builder that the user must call.
|
||||||
void http_request_builder_method(HTTP_RequestBuilder builder,
|
void chttp_request_builder_method(CHTTP_RequestBuilder builder,
|
||||||
HTTP_Method method);
|
CHTTP_Method method);
|
||||||
|
|
||||||
// Set the URL of the current request. This must be set after
|
// Set the URL of the current request. This must be set after
|
||||||
// the method and before any header/body
|
// the method and before any header/body
|
||||||
void http_request_builder_target(HTTP_RequestBuilder builder,
|
void chttp_request_builder_target(CHTTP_RequestBuilder builder,
|
||||||
HTTP_String url);
|
CHTTP_String url);
|
||||||
|
|
||||||
// After the URL, the user may set zero or more headers.
|
// After the URL, the user may set zero or more headers.
|
||||||
void http_request_builder_header(HTTP_RequestBuilder builder,
|
void chttp_request_builder_header(CHTTP_RequestBuilder builder,
|
||||||
HTTP_String str);
|
CHTTP_String str);
|
||||||
|
|
||||||
// Append bytes to the request's body. You can call this
|
// Append bytes to the request's body. You can call this
|
||||||
// any amount of times, as long as it's after having set
|
// any amount of times, as long as it's after having set
|
||||||
// the URL.
|
// the URL.
|
||||||
void http_request_builder_body(HTTP_RequestBuilder builder,
|
void chttp_request_builder_body(CHTTP_RequestBuilder builder,
|
||||||
HTTP_String str);
|
CHTTP_String str);
|
||||||
|
|
||||||
// Mark this request as complete. This invalidates the
|
// Mark this request as complete. This invalidates the
|
||||||
// builder.
|
// builder.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int http_request_builder_send(HTTP_RequestBuilder builder);
|
int chttp_request_builder_send(CHTTP_RequestBuilder builder);
|
||||||
|
|
||||||
// Resets the event register with the list of descriptors
|
// Resets the event register with the list of descriptors
|
||||||
// the client wants monitored.
|
// the client wants monitored.
|
||||||
void http_client_register_events(HTTP_Client *client,
|
void chttp_client_register_events(CHTTP_Client *client,
|
||||||
EventRegister *reg);
|
EventRegister *reg);
|
||||||
|
|
||||||
// The caller has waited for poll() to return and some
|
// The caller has waited for poll() to return and some
|
||||||
// I/O events to be triggered, so now the HTTP client
|
// I/O events to be triggered, so now the HTTP client
|
||||||
// can continue its buffering and flushing operations.
|
// can continue its buffering and flushing operations.
|
||||||
void http_client_process_events(HTTP_Client *client,
|
void chttp_client_process_events(CHTTP_Client *client,
|
||||||
EventRegister reg);
|
EventRegister reg);
|
||||||
|
|
||||||
// After some I/O events were processes, some responses
|
// After some I/O events were processes, some responses
|
||||||
@@ -218,33 +228,33 @@ void http_client_process_events(HTTP_Client *client,
|
|||||||
// buffered responses. If a request was available, true
|
// buffered responses. If a request was available, true
|
||||||
// is returned. If no more are avaiable, false is returned.
|
// is returned. If no more are avaiable, false is returned.
|
||||||
// The returned response must be freed using the
|
// The returned response must be freed using the
|
||||||
// http_free_response function.
|
// chttp_free_response function.
|
||||||
// TODO: Better comment talking about output arguments
|
// TODO: Better comment talking about output arguments
|
||||||
bool http_client_next_response(HTTP_Client *client,
|
bool chttp_client_next_response(CHTTP_Client *client,
|
||||||
int *result, void **user, HTTP_Response **response);
|
int *result, void **user, CHTTP_Response **response);
|
||||||
|
|
||||||
// TODO: comment
|
// TODO: comment
|
||||||
void http_client_wait_response(HTTP_Client *client,
|
void chttp_client_wait_response(CHTTP_Client *client,
|
||||||
int *result, void **user, HTTP_Response **response);
|
int *result, void **user, CHTTP_Response **response);
|
||||||
|
|
||||||
// Free a response object. You can't access its fields
|
// Free a response object. You can't access its fields
|
||||||
// again after this.
|
// again after this.
|
||||||
void http_free_response(HTTP_Response *response);
|
void chttp_free_response(CHTTP_Response *response);
|
||||||
|
|
||||||
// Perform a blocking GET request
|
// Perform a blocking GET request
|
||||||
int http_get(HTTP_String url, HTTP_String *headers,
|
int chttp_get(CHTTP_String url, CHTTP_String *headers,
|
||||||
int num_headers, HTTP_Response **response);
|
int num_headers, CHTTP_Response **response);
|
||||||
|
|
||||||
// Perform a blocking POST request
|
// Perform a blocking POST request
|
||||||
int http_post(HTTP_String url, HTTP_String *headers,
|
int chttp_post(CHTTP_String url, CHTTP_String *headers,
|
||||||
int num_headers, HTTP_String body,
|
int num_headers, CHTTP_String body,
|
||||||
HTTP_Response **response);
|
CHTTP_Response **response);
|
||||||
|
|
||||||
// Perform a blocking PUT request
|
// Perform a blocking PUT request
|
||||||
int http_put(HTTP_String url, HTTP_String *headers,
|
int chttp_put(CHTTP_String url, CHTTP_String *headers,
|
||||||
int num_headers, HTTP_String body,
|
int num_headers, CHTTP_String body,
|
||||||
HTTP_Response **response);
|
CHTTP_Response **response);
|
||||||
|
|
||||||
// Perform a blocking DELETE request
|
// Perform a blocking DELETE request
|
||||||
int http_delete(HTTP_String url, HTTP_String *headers,
|
int chttp_delete(CHTTP_String url, CHTTP_String *headers,
|
||||||
int num_headers, HTTP_Response **response);
|
int num_headers, CHTTP_Response **response);
|
||||||
|
|||||||
+2
-1
@@ -5,7 +5,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
@@ -13,6 +12,7 @@
|
|||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#else
|
#else
|
||||||
|
#include <time.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -27,4 +27,5 @@
|
|||||||
|
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/x509v3.h>
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+159
-188
@@ -128,7 +128,7 @@ consume_absolute_path(Scanner *s)
|
|||||||
// path-absolute = "/" [ segment-nz *( "/" segment ) ]
|
// path-absolute = "/" [ segment-nz *( "/" segment ) ]
|
||||||
// path-rootless = segment-nz *( "/" segment )
|
// path-rootless = segment-nz *( "/" segment )
|
||||||
// path-empty = 0<pchar>
|
// path-empty = 0<pchar>
|
||||||
static int parse_path(Scanner *s, HTTP_String *path, int abempty)
|
static int parse_path(Scanner *s, CHTTP_String *path, int abempty)
|
||||||
{
|
{
|
||||||
int start = s->cur;
|
int start = s->cur;
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ static int parse_path(Scanner *s, HTTP_String *path, int abempty)
|
|||||||
// (do nothing)
|
// (do nothing)
|
||||||
}
|
}
|
||||||
|
|
||||||
*path = (HTTP_String) {
|
*path = (CHTTP_String) {
|
||||||
s->src + start,
|
s->src + start,
|
||||||
s->cur - start,
|
s->cur - start,
|
||||||
};
|
};
|
||||||
@@ -215,7 +215,7 @@ static void invert_bytes(void *p, int len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_ipv4(Scanner *s, HTTP_IPv4 *ipv4)
|
static int parse_ipv4(Scanner *s, CHTTP_IPv4 *ipv4)
|
||||||
{
|
{
|
||||||
unsigned int out = 0;
|
unsigned int out = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -289,7 +289,7 @@ static int parse_ipv6_comp(Scanner *s)
|
|||||||
return (int) buf;
|
return (int) buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_ipv6(Scanner *s, HTTP_IPv6 *ipv6)
|
static int parse_ipv6(Scanner *s, CHTTP_IPv6 *ipv6)
|
||||||
{
|
{
|
||||||
unsigned short head[8];
|
unsigned short head[8];
|
||||||
unsigned short tail[8];
|
unsigned short tail[8];
|
||||||
@@ -359,7 +359,7 @@ static int is_regname(char c)
|
|||||||
return is_unreserved(c) || is_sub_delim(c);
|
return is_unreserved(c) || is_sub_delim(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_regname(Scanner *s, HTTP_String *regname)
|
static int parse_regname(Scanner *s, CHTTP_String *regname)
|
||||||
{
|
{
|
||||||
if (s->cur == s->len || !is_regname(s->src[s->cur]))
|
if (s->cur == s->len || !is_regname(s->src[s->cur]))
|
||||||
return -1;
|
return -1;
|
||||||
@@ -372,7 +372,7 @@ static int parse_regname(Scanner *s, HTTP_String *regname)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_host(Scanner *s, HTTP_Host *host)
|
static int parse_host(Scanner *s, CHTTP_Host *host)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
if (s->cur < s->len && s->src[s->cur] == '[') {
|
if (s->cur < s->len && s->src[s->cur] == '[') {
|
||||||
@@ -380,13 +380,13 @@ static int parse_host(Scanner *s, HTTP_Host *host)
|
|||||||
s->cur++;
|
s->cur++;
|
||||||
|
|
||||||
int start = s->cur;
|
int start = s->cur;
|
||||||
HTTP_IPv6 ipv6;
|
CHTTP_IPv6 ipv6;
|
||||||
ret = parse_ipv6(s, &ipv6);
|
ret = parse_ipv6(s, &ipv6);
|
||||||
if (ret < 0) return ret;
|
if (ret < 0) return ret;
|
||||||
|
|
||||||
host->mode = HTTP_HOST_MODE_IPV6;
|
host->mode = CHTTP_HOST_MODE_IPV6;
|
||||||
host->ipv6 = ipv6;
|
host->ipv6 = ipv6;
|
||||||
host->text = (HTTP_String) { s->src + start, s->cur - start };
|
host->text = (CHTTP_String) { s->src + start, s->cur - start };
|
||||||
|
|
||||||
if (s->cur == s->len || s->src[s->cur] != ']')
|
if (s->cur == s->len || s->src[s->cur] != ']')
|
||||||
return -1;
|
return -1;
|
||||||
@@ -395,22 +395,22 @@ static int parse_host(Scanner *s, HTTP_Host *host)
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
int start = s->cur;
|
int start = s->cur;
|
||||||
HTTP_IPv4 ipv4;
|
CHTTP_IPv4 ipv4;
|
||||||
ret = parse_ipv4(s, &ipv4);
|
ret = parse_ipv4(s, &ipv4);
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
host->mode = HTTP_HOST_MODE_IPV4;
|
host->mode = CHTTP_HOST_MODE_IPV4;
|
||||||
host->ipv4 = ipv4;
|
host->ipv4 = ipv4;
|
||||||
} else {
|
} else {
|
||||||
s->cur = start;
|
s->cur = start;
|
||||||
|
|
||||||
HTTP_String regname;
|
CHTTP_String regname;
|
||||||
ret = parse_regname(s, ®name);
|
ret = parse_regname(s, ®name);
|
||||||
if (ret < 0) return ret;
|
if (ret < 0) return ret;
|
||||||
|
|
||||||
host->mode = HTTP_HOST_MODE_NAME;
|
host->mode = CHTTP_HOST_MODE_NAME;
|
||||||
host->name = regname;
|
host->name = regname;
|
||||||
}
|
}
|
||||||
host->text = (HTTP_String) { s->src + start, s->cur - start };
|
host->text = (CHTTP_String) { s->src + start, s->cur - start };
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -439,16 +439,16 @@ static int is_userinfo(char c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// authority = [ userinfo "@" ] host [ ":" port ]
|
// authority = [ userinfo "@" ] host [ ":" port ]
|
||||||
static int parse_authority(Scanner *s, HTTP_Authority *authority)
|
static int parse_authority(Scanner *s, CHTTP_Authority *authority)
|
||||||
{
|
{
|
||||||
HTTP_String userinfo;
|
CHTTP_String userinfo;
|
||||||
{
|
{
|
||||||
int start = s->cur;
|
int start = s->cur;
|
||||||
|
|
||||||
CONSUME_OPTIONAL_SEQUENCE(s, is_userinfo);
|
CONSUME_OPTIONAL_SEQUENCE(s, is_userinfo);
|
||||||
|
|
||||||
if (s->cur < s->len && s->src[s->cur] == '@') {
|
if (s->cur < s->len && s->src[s->cur] == '@') {
|
||||||
userinfo = (HTTP_String) {
|
userinfo = (CHTTP_String) {
|
||||||
s->src + start,
|
s->src + start,
|
||||||
s->cur - start
|
s->cur - start
|
||||||
};
|
};
|
||||||
@@ -456,11 +456,11 @@ static int parse_authority(Scanner *s, HTTP_Authority *authority)
|
|||||||
} else {
|
} else {
|
||||||
// Rollback
|
// Rollback
|
||||||
s->cur = start;
|
s->cur = start;
|
||||||
userinfo = (HTTP_String) {NULL, 0};
|
userinfo = (CHTTP_String) {NULL, 0};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_Host host;
|
CHTTP_Host host;
|
||||||
{
|
{
|
||||||
int ret = parse_host(s, &host);
|
int ret = parse_host(s, &host);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@@ -487,9 +487,9 @@ static int parse_authority(Scanner *s, HTTP_Authority *authority)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_uri(Scanner *s, HTTP_URL *url, int allow_fragment)
|
static int parse_uri(Scanner *s, CHTTP_URL *url, int allow_fragment)
|
||||||
{
|
{
|
||||||
HTTP_String scheme = {0};
|
CHTTP_String scheme = {0};
|
||||||
{
|
{
|
||||||
int start = s->cur;
|
int start = s->cur;
|
||||||
if (s->cur == s->len || !is_scheme_head(s->src[s->cur]))
|
if (s->cur == s->len || !is_scheme_head(s->src[s->cur]))
|
||||||
@@ -497,7 +497,7 @@ static int parse_uri(Scanner *s, HTTP_URL *url, int allow_fragment)
|
|||||||
do
|
do
|
||||||
s->cur++;
|
s->cur++;
|
||||||
while (s->cur < s->len && is_scheme_body(s->src[s->cur]));
|
while (s->cur < s->len && is_scheme_body(s->src[s->cur]));
|
||||||
scheme = (HTTP_String) {
|
scheme = (CHTTP_String) {
|
||||||
s->src + start,
|
s->src + start,
|
||||||
s->cur - start,
|
s->cur - start,
|
||||||
};
|
};
|
||||||
@@ -508,7 +508,7 @@ static int parse_uri(Scanner *s, HTTP_URL *url, int allow_fragment)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int abempty = 0;
|
int abempty = 0;
|
||||||
HTTP_Authority authority = {0};
|
CHTTP_Authority authority = {0};
|
||||||
if (s->len - s->cur > 1
|
if (s->len - s->cur > 1
|
||||||
&& s->src[s->cur+0] == '/'
|
&& s->src[s->cur+0] == '/'
|
||||||
&& s->src[s->cur+1] == '/') {
|
&& s->src[s->cur+1] == '/') {
|
||||||
@@ -521,29 +521,29 @@ static int parse_uri(Scanner *s, HTTP_URL *url, int allow_fragment)
|
|||||||
abempty = 1;
|
abempty = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String path;
|
CHTTP_String path;
|
||||||
int ret = parse_path(s, &path, abempty);
|
int ret = parse_path(s, &path, abempty);
|
||||||
if (ret < 0) return ret;
|
if (ret < 0) return ret;
|
||||||
|
|
||||||
HTTP_String query = {0};
|
CHTTP_String query = {0};
|
||||||
if (s->cur < s->len && s->src[s->cur] == '?') {
|
if (s->cur < s->len && s->src[s->cur] == '?') {
|
||||||
int start = s->cur;
|
int start = s->cur;
|
||||||
do
|
do
|
||||||
s->cur++;
|
s->cur++;
|
||||||
while (s->cur < s->len && is_query(s->src[s->cur]));
|
while (s->cur < s->len && is_query(s->src[s->cur]));
|
||||||
query = (HTTP_String) {
|
query = (CHTTP_String) {
|
||||||
s->src + start,
|
s->src + start,
|
||||||
s->cur - start,
|
s->cur - start,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String fragment = {0};
|
CHTTP_String fragment = {0};
|
||||||
if (allow_fragment && s->cur < s->len && s->src[s->cur] == '#') {
|
if (allow_fragment && s->cur < s->len && s->src[s->cur] == '#') {
|
||||||
int start = s->cur;
|
int start = s->cur;
|
||||||
do
|
do
|
||||||
s->cur++;
|
s->cur++;
|
||||||
while (s->cur < s->len && is_fragment(s->src[s->cur]));
|
while (s->cur < s->len && is_fragment(s->src[s->cur]));
|
||||||
fragment = (HTTP_String) {
|
fragment = (CHTTP_String) {
|
||||||
s->src + start,
|
s->src + start,
|
||||||
s->cur - start,
|
s->cur - start,
|
||||||
};
|
};
|
||||||
@@ -562,7 +562,7 @@ static int parse_uri(Scanner *s, HTTP_URL *url, int allow_fragment)
|
|||||||
// host = IP-literal / IPv4address / reg-name
|
// host = IP-literal / IPv4address / reg-name
|
||||||
// IP-literal = "[" ( IPv6address / IPvFuture ) "]"
|
// IP-literal = "[" ( IPv6address / IPvFuture ) "]"
|
||||||
// reg-name = *( unreserved / pct-encoded / sub-delims )
|
// reg-name = *( unreserved / pct-encoded / sub-delims )
|
||||||
static int parse_authority_form(Scanner *s, HTTP_Host *host, int *port)
|
static int parse_authority_form(Scanner *s, CHTTP_Host *host, int *port)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -591,23 +591,23 @@ static int parse_authority_form(Scanner *s, HTTP_Host *host, int *port)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_origin_form(Scanner *s, HTTP_String *path, HTTP_String *query)
|
static int parse_origin_form(Scanner *s, CHTTP_String *path, CHTTP_String *query)
|
||||||
{
|
{
|
||||||
int ret, start;
|
int ret, start;
|
||||||
|
|
||||||
start = s->cur;
|
start = s->cur;
|
||||||
ret = consume_absolute_path(s);
|
ret = consume_absolute_path(s);
|
||||||
if (ret < 0) return ret;
|
if (ret < 0) return ret;
|
||||||
*path = (HTTP_String) { s->src + start, s->cur - start };
|
*path = (CHTTP_String) { s->src + start, s->cur - start };
|
||||||
|
|
||||||
if (s->cur < s->len && s->src[s->cur] == '?') {
|
if (s->cur < s->len && s->src[s->cur] == '?') {
|
||||||
start = s->cur;
|
start = s->cur;
|
||||||
do
|
do
|
||||||
s->cur++;
|
s->cur++;
|
||||||
while (s->cur < s->len && is_query(s->src[s->cur]));
|
while (s->cur < s->len && is_query(s->src[s->cur]));
|
||||||
*query = (HTTP_String) { s->src + start, s->cur - start };
|
*query = (CHTTP_String) { s->src + start, s->cur - start };
|
||||||
} else
|
} else
|
||||||
*query = (HTTP_String) { NULL, 0 };
|
*query = (CHTTP_String) { NULL, 0 };
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -622,11 +622,11 @@ static int parse_asterisk_form(Scanner *s)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_request_target(Scanner *s, HTTP_URL *url)
|
static int parse_request_target(Scanner *s, CHTTP_URL *url)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
memset(url, 0, sizeof(HTTP_URL));
|
memset(url, 0, sizeof(CHTTP_URL));
|
||||||
|
|
||||||
// asterisk-form
|
// asterisk-form
|
||||||
ret = parse_asterisk_form(s);
|
ret = parse_asterisk_form(s);
|
||||||
@@ -644,7 +644,7 @@ static int parse_request_target(Scanner *s, HTTP_URL *url)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool consume_str(Scanner *scan, HTTP_String token)
|
bool consume_str(Scanner *scan, CHTTP_String token)
|
||||||
{
|
{
|
||||||
assert(token.len > 0);
|
assert(token.len > 0);
|
||||||
|
|
||||||
@@ -664,10 +664,10 @@ static int is_header_body(char c)
|
|||||||
return is_vchar(c) || c == ' ' || c == '\t';
|
return is_vchar(c) || c == ' ' || c == '\t';
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_headers(Scanner *s, HTTP_Header *headers, int max_headers)
|
static int parse_headers(Scanner *s, CHTTP_Header *headers, int max_headers)
|
||||||
{
|
{
|
||||||
int num_headers = 0;
|
int num_headers = 0;
|
||||||
while (!consume_str(s, HTTP_STR("\r\n"))) {
|
while (!consume_str(s, CHTTP_STR("\r\n"))) {
|
||||||
|
|
||||||
// RFC 9112:
|
// RFC 9112:
|
||||||
// field-line = field-name ":" OWS field-value OWS
|
// field-line = field-name ":" OWS field-value OWS
|
||||||
@@ -687,7 +687,7 @@ static int parse_headers(Scanner *s, HTTP_Header *headers, int max_headers)
|
|||||||
do
|
do
|
||||||
s->cur++;
|
s->cur++;
|
||||||
while (s->cur < s->len && is_tchar(s->src[s->cur]));
|
while (s->cur < s->len && is_tchar(s->src[s->cur]));
|
||||||
HTTP_String name = { s->src + start, s->cur - start };
|
CHTTP_String name = { s->src + start, s->cur - start };
|
||||||
|
|
||||||
if (s->cur == s->len || s->src[s->cur] != ':')
|
if (s->cur == s->len || s->src[s->cur] != ':')
|
||||||
return -1; // ERROR
|
return -1; // ERROR
|
||||||
@@ -695,13 +695,13 @@ static int parse_headers(Scanner *s, HTTP_Header *headers, int max_headers)
|
|||||||
|
|
||||||
start = s->cur;
|
start = s->cur;
|
||||||
CONSUME_OPTIONAL_SEQUENCE(s, is_header_body);
|
CONSUME_OPTIONAL_SEQUENCE(s, is_header_body);
|
||||||
HTTP_String body = { s->src + start, s->cur - start };
|
CHTTP_String body = { s->src + start, s->cur - start };
|
||||||
body = http_trim(body);
|
body = chttp_trim(body);
|
||||||
|
|
||||||
if (num_headers < max_headers)
|
if (num_headers < max_headers)
|
||||||
headers[num_headers++] = (HTTP_Header) { name, body };
|
headers[num_headers++] = (CHTTP_Header) { name, body };
|
||||||
|
|
||||||
if (!consume_str(s, HTTP_STR("\r\n"))) {
|
if (!consume_str(s, CHTTP_STR("\r\n"))) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -722,7 +722,7 @@ static bool is_space(char c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
parse_transfer_encoding(HTTP_String src, TransferEncodingOption *dst, int max)
|
parse_transfer_encoding(CHTTP_String src, TransferEncodingOption *dst, int max)
|
||||||
{
|
{
|
||||||
Scanner s = { src.ptr, src.len, 0 };
|
Scanner s = { src.ptr, src.len, 0 };
|
||||||
|
|
||||||
@@ -733,10 +733,10 @@ parse_transfer_encoding(HTTP_String src, TransferEncodingOption *dst, int max)
|
|||||||
|
|
||||||
TransferEncodingOption opt;
|
TransferEncodingOption opt;
|
||||||
if (0) {}
|
if (0) {}
|
||||||
else if (consume_str(&s, HTTP_STR("chunked"))) opt = TRANSFER_ENCODING_OPTION_CHUNKED;
|
else if (consume_str(&s, CHTTP_STR("chunked"))) opt = TRANSFER_ENCODING_OPTION_CHUNKED;
|
||||||
else if (consume_str(&s, HTTP_STR("compress"))) opt = TRANSFER_ENCODING_OPTION_COMPRESS;
|
else if (consume_str(&s, CHTTP_STR("compress"))) opt = TRANSFER_ENCODING_OPTION_COMPRESS;
|
||||||
else if (consume_str(&s, HTTP_STR("deflate"))) opt = TRANSFER_ENCODING_OPTION_DEFLATE;
|
else if (consume_str(&s, CHTTP_STR("deflate"))) opt = TRANSFER_ENCODING_OPTION_DEFLATE;
|
||||||
else if (consume_str(&s, HTTP_STR("gzip"))) opt = TRANSFER_ENCODING_OPTION_GZIP;
|
else if (consume_str(&s, CHTTP_STR("gzip"))) opt = TRANSFER_ENCODING_OPTION_GZIP;
|
||||||
else return -1; // Invalid option
|
else return -1; // Invalid option
|
||||||
|
|
||||||
if (num == max)
|
if (num == max)
|
||||||
@@ -778,8 +778,8 @@ parse_content_length(const char *src, int len, uint64_t *out)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int parse_body(Scanner *s,
|
static int parse_body(Scanner *s,
|
||||||
HTTP_Header *headers, int num_headers,
|
CHTTP_Header *headers, int num_headers,
|
||||||
HTTP_String *body, bool body_expected)
|
CHTTP_String *body, bool body_expected)
|
||||||
{
|
{
|
||||||
|
|
||||||
// RFC 9112 section 6:
|
// RFC 9112 section 6:
|
||||||
@@ -787,7 +787,7 @@ static int parse_body(Scanner *s,
|
|||||||
// Transfer-Encoding header field. Request message framing is independent of method
|
// Transfer-Encoding header field. Request message framing is independent of method
|
||||||
// semantics.
|
// semantics.
|
||||||
|
|
||||||
int header_index = http_find_header(headers, num_headers, HTTP_STR("Transfer-Encoding"));
|
int header_index = chttp_find_header(headers, num_headers, CHTTP_STR("Transfer-Encoding"));
|
||||||
if (header_index != -1) {
|
if (header_index != -1) {
|
||||||
|
|
||||||
// RFC 9112 section 6.1:
|
// RFC 9112 section 6.1:
|
||||||
@@ -795,10 +795,10 @@ static int parse_body(Scanner *s,
|
|||||||
// or process such a request in accordance with the Transfer-Encoding alone. Regardless,
|
// or process such a request in accordance with the Transfer-Encoding alone. Regardless,
|
||||||
// the server MUST close the connection after responding to such a request to avoid the
|
// the server MUST close the connection after responding to such a request to avoid the
|
||||||
// potential attacks.
|
// potential attacks.
|
||||||
if (http_find_header(headers, num_headers, HTTP_STR("Content-Length")) != -1)
|
if (chttp_find_header(headers, num_headers, CHTTP_STR("Content-Length")) != -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
HTTP_String value = headers[header_index].value;
|
CHTTP_String value = headers[header_index].value;
|
||||||
|
|
||||||
// RFC 9112 section 6.1:
|
// RFC 9112 section 6.1:
|
||||||
// If any transfer coding other than chunked is applied to a request's content, the
|
// If any transfer coding other than chunked is applied to a request's content, the
|
||||||
@@ -808,14 +808,14 @@ static int parse_body(Scanner *s,
|
|||||||
// coding or terminate the message by closing the connection.
|
// coding or terminate the message by closing the connection.
|
||||||
|
|
||||||
TransferEncodingOption opts[8];
|
TransferEncodingOption opts[8];
|
||||||
int num = parse_transfer_encoding(value, opts, HTTP_COUNT(opts));
|
int num = parse_transfer_encoding(value, opts, CHTTP_COUNT(opts));
|
||||||
if (num != 1 || opts[0] != TRANSFER_ENCODING_OPTION_CHUNKED)
|
if (num != 1 || opts[0] != TRANSFER_ENCODING_OPTION_CHUNKED)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
HTTP_String chunks_maybe[128];
|
CHTTP_String chunks_maybe[128];
|
||||||
HTTP_String *chunks = chunks_maybe;
|
CHTTP_String *chunks = chunks_maybe;
|
||||||
int num_chunks = 0;
|
int num_chunks = 0;
|
||||||
int max_chunks = HTTP_COUNT(chunks_maybe);
|
int max_chunks = CHTTP_COUNT(chunks_maybe);
|
||||||
|
|
||||||
#define FREE_CHUNK_LIST \
|
#define FREE_CHUNK_LIST \
|
||||||
if (chunks != chunks_maybe) \
|
if (chunks != chunks_maybe) \
|
||||||
@@ -905,7 +905,7 @@ static int parse_body(Scanner *s,
|
|||||||
|
|
||||||
max_chunks *= 2;
|
max_chunks *= 2;
|
||||||
|
|
||||||
HTTP_String *new_chunks = malloc(max_chunks * sizeof(HTTP_String));
|
CHTTP_String *new_chunks = malloc(max_chunks * sizeof(CHTTP_String));
|
||||||
if (new_chunks == NULL) {
|
if (new_chunks == NULL) {
|
||||||
if (chunks != chunks_maybe)
|
if (chunks != chunks_maybe)
|
||||||
free(chunks);
|
free(chunks);
|
||||||
@@ -920,7 +920,7 @@ static int parse_body(Scanner *s,
|
|||||||
|
|
||||||
chunks = new_chunks;
|
chunks = new_chunks;
|
||||||
}
|
}
|
||||||
chunks[num_chunks++] = (HTTP_String) { chunk_ptr, chunk_len };
|
chunks[num_chunks++] = (CHTTP_String) { chunk_ptr, chunk_len };
|
||||||
}
|
}
|
||||||
|
|
||||||
char *content_ptr = content_start;
|
char *content_ptr = content_start;
|
||||||
@@ -929,7 +929,7 @@ static int parse_body(Scanner *s,
|
|||||||
content_ptr += chunks[i].len;
|
content_ptr += chunks[i].len;
|
||||||
}
|
}
|
||||||
|
|
||||||
*body = (HTTP_String) {
|
*body = (CHTTP_String) {
|
||||||
content_start,
|
content_start,
|
||||||
content_ptr - content_start
|
content_ptr - content_start
|
||||||
};
|
};
|
||||||
@@ -944,11 +944,11 @@ static int parse_body(Scanner *s,
|
|||||||
// If a valid Content-Length header field is present without Transfer-Encoding,
|
// If a valid Content-Length header field is present without Transfer-Encoding,
|
||||||
// its decimal value defines the expected message body length in octets.
|
// its decimal value defines the expected message body length in octets.
|
||||||
|
|
||||||
header_index = http_find_header(headers, num_headers, HTTP_STR("Content-Length"));
|
header_index = chttp_find_header(headers, num_headers, CHTTP_STR("Content-Length"));
|
||||||
if (header_index != -1) {
|
if (header_index != -1) {
|
||||||
|
|
||||||
// Have Content-Length
|
// Have Content-Length
|
||||||
HTTP_String value = headers[header_index].value;
|
CHTTP_String value = headers[header_index].value;
|
||||||
|
|
||||||
uint64_t tmp;
|
uint64_t tmp;
|
||||||
if (parse_content_length(value.ptr, value.len, &tmp) < 0)
|
if (parse_content_length(value.ptr, value.len, &tmp) < 0)
|
||||||
@@ -960,7 +960,7 @@ static int parse_body(Scanner *s,
|
|||||||
if (len > s->len - s->cur)
|
if (len > s->len - s->cur)
|
||||||
return 0; // Incomplete request
|
return 0; // Incomplete request
|
||||||
|
|
||||||
*body = (HTTP_String) { s->src + s->cur, len };
|
*body = (CHTTP_String) { s->src + s->cur, len };
|
||||||
|
|
||||||
s->cur += len;
|
s->cur += len;
|
||||||
return 1;
|
return 1;
|
||||||
@@ -969,7 +969,7 @@ static int parse_body(Scanner *s,
|
|||||||
// No Content-Length or Transfer-Encoding
|
// No Content-Length or Transfer-Encoding
|
||||||
if (body_expected) return -1;
|
if (body_expected) return -1;
|
||||||
|
|
||||||
*body = (HTTP_String) { NULL, 0 };
|
*body = (CHTTP_String) { NULL, 0 };
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -987,7 +987,7 @@ static int contains_head(char *src, int len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_request(Scanner *s, HTTP_Request *req)
|
static int parse_request(Scanner *s, CHTTP_Request *req)
|
||||||
{
|
{
|
||||||
if (!contains_head(s->src + s->cur, s->len - s->cur))
|
if (!contains_head(s->src + s->cur, s->len - s->cur))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -995,15 +995,15 @@ static int parse_request(Scanner *s, HTTP_Request *req)
|
|||||||
req->secure = false;
|
req->secure = false;
|
||||||
|
|
||||||
if (0) {}
|
if (0) {}
|
||||||
else if (consume_str(s, HTTP_STR("GET "))) req->method = HTTP_METHOD_GET;
|
else if (consume_str(s, CHTTP_STR("GET "))) req->method = CHTTP_METHOD_GET;
|
||||||
else if (consume_str(s, HTTP_STR("POST "))) req->method = HTTP_METHOD_POST;
|
else if (consume_str(s, CHTTP_STR("POST "))) req->method = CHTTP_METHOD_POST;
|
||||||
else if (consume_str(s, HTTP_STR("PUT "))) req->method = HTTP_METHOD_PUT;
|
else if (consume_str(s, CHTTP_STR("PUT "))) req->method = CHTTP_METHOD_PUT;
|
||||||
else if (consume_str(s, HTTP_STR("HEAD "))) req->method = HTTP_METHOD_HEAD;
|
else if (consume_str(s, CHTTP_STR("HEAD "))) req->method = CHTTP_METHOD_HEAD;
|
||||||
else if (consume_str(s, HTTP_STR("DELETE "))) req->method = HTTP_METHOD_DELETE;
|
else if (consume_str(s, CHTTP_STR("DELETE "))) req->method = CHTTP_METHOD_DELETE;
|
||||||
else if (consume_str(s, HTTP_STR("CONNECT "))) req->method = HTTP_METHOD_CONNECT;
|
else if (consume_str(s, CHTTP_STR("CONNECT "))) req->method = CHTTP_METHOD_CONNECT;
|
||||||
else if (consume_str(s, HTTP_STR("OPTIONS "))) req->method = HTTP_METHOD_OPTIONS;
|
else if (consume_str(s, CHTTP_STR("OPTIONS "))) req->method = CHTTP_METHOD_OPTIONS;
|
||||||
else if (consume_str(s, HTTP_STR("TRACE "))) req->method = HTTP_METHOD_TRACE;
|
else if (consume_str(s, CHTTP_STR("TRACE "))) req->method = CHTTP_METHOD_TRACE;
|
||||||
else if (consume_str(s, HTTP_STR("PATCH "))) req->method = HTTP_METHOD_PATCH;
|
else if (consume_str(s, CHTTP_STR("PATCH "))) req->method = CHTTP_METHOD_PATCH;
|
||||||
else return -1;
|
else return -1;
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -1021,47 +1021,47 @@ static int parse_request(Scanner *s, HTTP_Request *req)
|
|||||||
s->cur = s2.cur;
|
s->cur = s2.cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consume_str(s, HTTP_STR(" HTTP/1.1\r\n"))) {
|
if (consume_str(s, CHTTP_STR(" HTTP/1.1\r\n"))) {
|
||||||
req->minor = 1;
|
req->minor = 1;
|
||||||
} else if (consume_str(s, HTTP_STR(" HTTP/1.0\r\n")) || consume_str(s, HTTP_STR(" HTTP/1\r\n"))) {
|
} else if (consume_str(s, CHTTP_STR(" HTTP/1.0\r\n")) || consume_str(s, CHTTP_STR(" HTTP/1\r\n"))) {
|
||||||
req->minor = 0;
|
req->minor = 0;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_headers = parse_headers(s, req->headers, HTTP_MAX_HEADERS);
|
int num_headers = parse_headers(s, req->headers, CHTTP_MAX_HEADERS);
|
||||||
if (num_headers < 0)
|
if (num_headers < 0)
|
||||||
return num_headers;
|
return num_headers;
|
||||||
req->num_headers = num_headers;
|
req->num_headers = num_headers;
|
||||||
|
|
||||||
// Request methods that typically don't have a body
|
// Request methods that typically don't have a body
|
||||||
bool body_expected = true;
|
bool body_expected = true;
|
||||||
if (req->method == HTTP_METHOD_GET ||
|
if (req->method == CHTTP_METHOD_GET ||
|
||||||
req->method == HTTP_METHOD_HEAD ||
|
req->method == CHTTP_METHOD_HEAD ||
|
||||||
req->method == HTTP_METHOD_DELETE ||
|
req->method == CHTTP_METHOD_DELETE ||
|
||||||
req->method == HTTP_METHOD_OPTIONS ||
|
req->method == CHTTP_METHOD_OPTIONS ||
|
||||||
req->method == HTTP_METHOD_TRACE)
|
req->method == CHTTP_METHOD_TRACE)
|
||||||
body_expected = false;
|
body_expected = false;
|
||||||
|
|
||||||
return parse_body(s, req->headers, req->num_headers, &req->body, body_expected);
|
return parse_body(s, req->headers, req->num_headers, &req->body, body_expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_find_header(HTTP_Header *headers, int num_headers, HTTP_String name)
|
int chttp_find_header(CHTTP_Header *headers, int num_headers, CHTTP_String name)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < num_headers; i++)
|
for (int i = 0; i < num_headers; i++)
|
||||||
if (http_streqcase(name, headers[i].name))
|
if (chttp_streqcase(name, headers[i].name))
|
||||||
return i;
|
return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_response(Scanner *s, HTTP_Response *res)
|
static int parse_response(Scanner *s, CHTTP_Response *res)
|
||||||
{
|
{
|
||||||
if (!contains_head(s->src + s->cur, s->len - s->cur))
|
if (!contains_head(s->src + s->cur, s->len - s->cur))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (consume_str(s, HTTP_STR("HTTP/1.1 "))) {
|
if (consume_str(s, CHTTP_STR("HTTP/1.1 "))) {
|
||||||
res->minor = 1;
|
res->minor = 1;
|
||||||
} else if (consume_str(s, HTTP_STR("HTTP/1.0 ")) || consume_str(s, HTTP_STR("HTTP/1 "))) {
|
} else if (consume_str(s, CHTTP_STR("HTTP/1.0 ")) || consume_str(s, CHTTP_STR("HTTP/1 "))) {
|
||||||
res->minor = 0;
|
res->minor = 0;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1093,7 +1093,7 @@ static int parse_response(Scanner *s, HTTP_Response *res)
|
|||||||
return -1;
|
return -1;
|
||||||
s->cur += 2;
|
s->cur += 2;
|
||||||
|
|
||||||
int num_headers = parse_headers(s, res->headers, HTTP_MAX_HEADERS);
|
int num_headers = parse_headers(s, res->headers, CHTTP_MAX_HEADERS);
|
||||||
if (num_headers < 0)
|
if (num_headers < 0)
|
||||||
return num_headers;
|
return num_headers;
|
||||||
res->num_headers = num_headers;
|
res->num_headers = num_headers;
|
||||||
@@ -1113,7 +1113,7 @@ static int parse_response(Scanner *s, HTTP_Response *res)
|
|||||||
return parse_body(s, res->headers, res->num_headers, &res->body, body_expected);
|
return parse_body(s, res->headers, res->num_headers, &res->body, body_expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_parse_ipv4(char *src, int len, HTTP_IPv4 *ipv4)
|
int chttp_parse_ipv4(char *src, int len, CHTTP_IPv4 *ipv4)
|
||||||
{
|
{
|
||||||
Scanner s = {src, len, 0};
|
Scanner s = {src, len, 0};
|
||||||
int ret = parse_ipv4(&s, ipv4);
|
int ret = parse_ipv4(&s, ipv4);
|
||||||
@@ -1121,7 +1121,7 @@ int http_parse_ipv4(char *src, int len, HTTP_IPv4 *ipv4)
|
|||||||
return s.cur;
|
return s.cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_parse_ipv6(char *src, int len, HTTP_IPv6 *ipv6)
|
int chttp_parse_ipv6(char *src, int len, CHTTP_IPv6 *ipv6)
|
||||||
{
|
{
|
||||||
Scanner s = {src, len, 0};
|
Scanner s = {src, len, 0};
|
||||||
int ret = parse_ipv6(&s, ipv6);
|
int ret = parse_ipv6(&s, ipv6);
|
||||||
@@ -1129,7 +1129,7 @@ int http_parse_ipv6(char *src, int len, HTTP_IPv6 *ipv6)
|
|||||||
return s.cur;
|
return s.cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_parse_url(char *src, int len, HTTP_URL *url)
|
int chttp_parse_url(char *src, int len, CHTTP_URL *url)
|
||||||
{
|
{
|
||||||
Scanner s = {src, len, 0};
|
Scanner s = {src, len, 0};
|
||||||
int ret = parse_uri(&s, url, 1);
|
int ret = parse_uri(&s, url, 1);
|
||||||
@@ -1138,7 +1138,7 @@ int http_parse_url(char *src, int len, HTTP_URL *url)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_parse_request(char *src, int len, HTTP_Request *req)
|
int chttp_parse_request(char *src, int len, CHTTP_Request *req)
|
||||||
{
|
{
|
||||||
Scanner s = {src, len, 0};
|
Scanner s = {src, len, 0};
|
||||||
int ret = parse_request(&s, req);
|
int ret = parse_request(&s, req);
|
||||||
@@ -1147,7 +1147,7 @@ int http_parse_request(char *src, int len, HTTP_Request *req)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_parse_response(char *src, int len, HTTP_Response *res)
|
int chttp_parse_response(char *src, int len, CHTTP_Response *res)
|
||||||
{
|
{
|
||||||
Scanner s = {src, len, 0};
|
Scanner s = {src, len, 0};
|
||||||
int ret = parse_response(&s, res);
|
int ret = parse_response(&s, res);
|
||||||
@@ -1156,14 +1156,14 @@ int http_parse_response(char *src, int len, HTTP_Response *res)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String http_get_cookie(HTTP_Request *req, HTTP_String name)
|
CHTTP_String chttp_get_cookie(CHTTP_Request *req, CHTTP_String name)
|
||||||
{
|
{
|
||||||
// Simple cookie parsing - does not handle quoted values or special characters
|
// Simple cookie parsing - does not handle quoted values or special characters
|
||||||
// See RFC 6265 for full cookie specification
|
// See RFC 6265 for full cookie specification
|
||||||
|
|
||||||
for (int i = 0; i < req->num_headers; i++) {
|
for (int i = 0; i < req->num_headers; i++) {
|
||||||
|
|
||||||
if (!http_streqcase(req->headers[i].name, HTTP_STR("Cookie")))
|
if (!chttp_streqcase(req->headers[i].name, CHTTP_STR("Cookie")))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
char *src = req->headers[i].value.ptr;
|
char *src = req->headers[i].value.ptr;
|
||||||
@@ -1181,7 +1181,7 @@ HTTP_String http_get_cookie(HTTP_Request *req, HTTP_String name)
|
|||||||
while (cur < len && src[cur] != '=')
|
while (cur < len && src[cur] != '=')
|
||||||
cur++;
|
cur++;
|
||||||
|
|
||||||
HTTP_String cookie_name = { src + off, cur - off };
|
CHTTP_String cookie_name = { src + off, cur - off };
|
||||||
|
|
||||||
if (cur == len)
|
if (cur == len)
|
||||||
break;
|
break;
|
||||||
@@ -1191,9 +1191,9 @@ HTTP_String http_get_cookie(HTTP_Request *req, HTTP_String name)
|
|||||||
while (cur < len && src[cur] != ';')
|
while (cur < len && src[cur] != ';')
|
||||||
cur++;
|
cur++;
|
||||||
|
|
||||||
HTTP_String cookie_value = { src + off, cur - off };
|
CHTTP_String cookie_value = { src + off, cur - off };
|
||||||
|
|
||||||
if (http_streq(name, cookie_name))
|
if (chttp_streq(name, cookie_name))
|
||||||
return cookie_value;
|
return cookie_value;
|
||||||
|
|
||||||
if (cur == len)
|
if (cur == len)
|
||||||
@@ -1202,10 +1202,10 @@ HTTP_String http_get_cookie(HTTP_Request *req, HTTP_String name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return HTTP_STR("");
|
return CHTTP_STR("");
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String http_get_param(HTTP_String body, HTTP_String str, char *mem, int cap)
|
CHTTP_String chttp_get_param(CHTTP_String body, CHTTP_String str, char *mem, int cap)
|
||||||
{
|
{
|
||||||
// This is just a best-effort implementation
|
// This is just a best-effort implementation
|
||||||
|
|
||||||
@@ -1218,29 +1218,29 @@ HTTP_String http_get_param(HTTP_String body, HTTP_String str, char *mem, int cap
|
|||||||
|
|
||||||
while (cur < len) {
|
while (cur < len) {
|
||||||
|
|
||||||
HTTP_String name;
|
CHTTP_String name;
|
||||||
{
|
{
|
||||||
int off = cur;
|
int off = cur;
|
||||||
while (cur < len && src[cur] != '=' && src[cur] != '&')
|
while (cur < len && src[cur] != '=' && src[cur] != '&')
|
||||||
cur++;
|
cur++;
|
||||||
name = (HTTP_String) { src + off, cur - off };
|
name = (CHTTP_String) { src + off, cur - off };
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String body = HTTP_STR("");
|
CHTTP_String body = CHTTP_STR("");
|
||||||
if (cur < len) {
|
if (cur < len) {
|
||||||
cur++;
|
cur++;
|
||||||
if (src[cur-1] == '=') {
|
if (src[cur-1] == '=') {
|
||||||
int off = cur;
|
int off = cur;
|
||||||
while (cur < len && src[cur] != '&')
|
while (cur < len && src[cur] != '&')
|
||||||
cur++;
|
cur++;
|
||||||
body = (HTTP_String) { src + off, cur - off };
|
body = (CHTTP_String) { src + off, cur - off };
|
||||||
|
|
||||||
if (cur < len)
|
if (cur < len)
|
||||||
cur++;
|
cur++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (http_streq(str, name)) {
|
if (chttp_streq(str, name)) {
|
||||||
|
|
||||||
bool percent_encoded = false;
|
bool percent_encoded = false;
|
||||||
for (int i = 0; i < body.len; i++)
|
for (int i = 0; i < body.len; i++)
|
||||||
@@ -1253,9 +1253,9 @@ HTTP_String http_get_param(HTTP_String body, HTTP_String str, char *mem, int cap
|
|||||||
return body;
|
return body;
|
||||||
|
|
||||||
if (body.len > cap)
|
if (body.len > cap)
|
||||||
return (HTTP_String) { NULL, 0 };
|
return (CHTTP_String) { NULL, 0 };
|
||||||
|
|
||||||
HTTP_String decoded = { mem, 0 };
|
CHTTP_String decoded = { mem, 0 };
|
||||||
for (int i = 0; i < body.len; i++) {
|
for (int i = 0; i < body.len; i++) {
|
||||||
|
|
||||||
char c = body.ptr[i];
|
char c = body.ptr[i];
|
||||||
@@ -1266,7 +1266,7 @@ HTTP_String http_get_param(HTTP_String body, HTTP_String str, char *mem, int cap
|
|||||||
if (body.len - i < 3
|
if (body.len - i < 3
|
||||||
|| !is_hex_digit(body.ptr[i+1])
|
|| !is_hex_digit(body.ptr[i+1])
|
||||||
|| !is_hex_digit(body.ptr[i+2]))
|
|| !is_hex_digit(body.ptr[i+2]))
|
||||||
return (HTTP_String) { NULL, 0 };
|
return (CHTTP_String) { NULL, 0 };
|
||||||
|
|
||||||
int h = hex_digit_to_int(body.ptr[i+1]);
|
int h = hex_digit_to_int(body.ptr[i+1]);
|
||||||
int l = hex_digit_to_int(body.ptr[i+2]);
|
int l = hex_digit_to_int(body.ptr[i+2]);
|
||||||
@@ -1283,13 +1283,13 @@ HTTP_String http_get_param(HTTP_String body, HTTP_String str, char *mem, int cap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return HTTP_STR("");
|
return CHTTP_STR("");
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_get_param_i(HTTP_String body, HTTP_String str)
|
int chttp_get_param_i(CHTTP_String body, CHTTP_String str)
|
||||||
{
|
{
|
||||||
char buf[128];
|
char buf[128];
|
||||||
HTTP_String out = http_get_param(body, str, buf, (int) sizeof(buf));
|
CHTTP_String out = chttp_get_param(body, str, buf, (int) sizeof(buf));
|
||||||
if (out.len == 0 || !is_digit(out.ptr[0]))
|
if (out.len == 0 || !is_digit(out.ptr[0]))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@@ -1305,38 +1305,39 @@ int http_get_param_i(HTTP_String body, HTTP_String str)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool http_match_host(HTTP_Request *req, HTTP_String domain, int port)
|
bool chttp_match_host(CHTTP_Request *req, CHTTP_String domain, int port)
|
||||||
{
|
{
|
||||||
int idx = http_find_header(req->headers, req->num_headers, HTTP_STR("Host"));
|
int idx = chttp_find_header(req->headers, req->num_headers, CHTTP_STR("Host"));
|
||||||
assert(idx != -1); // Requests without the host header are always rejected
|
if (idx < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
char tmp[1<<8];
|
char tmp[1<<8];
|
||||||
if (port > -1 && port != 80) {
|
if (port > -1 && port != 80) {
|
||||||
int ret = snprintf(tmp, sizeof(tmp), "%.*s:%d", domain.len, domain.ptr, port);
|
int ret = snprintf(tmp, sizeof(tmp), "%.*s:%d", domain.len, domain.ptr, port);
|
||||||
assert(ret > 0);
|
assert(ret > 0);
|
||||||
domain = (HTTP_String) { tmp, ret };
|
domain = (CHTTP_String) { tmp, ret };
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String host = req->headers[idx].value;
|
CHTTP_String host = req->headers[idx].value;
|
||||||
return http_streqcase(host, domain);
|
return chttp_streqcase(host, domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
|
// <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
|
||||||
static int parse_date(Scanner *s, HTTP_Date *out)
|
static int parse_date(Scanner *s, CHTTP_Date *out)
|
||||||
{
|
{
|
||||||
struct { HTTP_String str; HTTP_WeekDay val; } week_day_table[] = {
|
struct { CHTTP_String str; CHTTP_WeekDay val; } week_day_table[] = {
|
||||||
{ HTTP_STR("Mon, "), HTTP_WEEKDAY_MON },
|
{ CHTTP_STR("Mon, "), CHTTP_WEEKDAY_MON },
|
||||||
{ HTTP_STR("Tue, "), HTTP_WEEKDAY_TUE },
|
{ CHTTP_STR("Tue, "), CHTTP_WEEKDAY_TUE },
|
||||||
{ HTTP_STR("Wed, "), HTTP_WEEKDAY_WED },
|
{ CHTTP_STR("Wed, "), CHTTP_WEEKDAY_WED },
|
||||||
{ HTTP_STR("Thu, "), HTTP_WEEKDAY_THU },
|
{ CHTTP_STR("Thu, "), CHTTP_WEEKDAY_THU },
|
||||||
{ HTTP_STR("Fri, "), HTTP_WEEKDAY_FRI },
|
{ CHTTP_STR("Fri, "), CHTTP_WEEKDAY_FRI },
|
||||||
{ HTTP_STR("Sat, "), HTTP_WEEKDAY_SAT },
|
{ CHTTP_STR("Sat, "), CHTTP_WEEKDAY_SAT },
|
||||||
{ HTTP_STR("Sun, "), HTTP_WEEKDAY_SUN },
|
{ CHTTP_STR("Sun, "), CHTTP_WEEKDAY_SUN },
|
||||||
};
|
};
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (int i = 0; i < HTTP_COUNT(week_day_table); i++)
|
for (int i = 0; i < CHTTP_COUNT(week_day_table); i++)
|
||||||
if (consume_str(s, week_day_table[i].str)) {
|
if (consume_str(s, week_day_table[i].str)) {
|
||||||
out->week_day = week_day_table[i].val;
|
out->week_day = week_day_table[i].val;
|
||||||
found = true;
|
found = true;
|
||||||
@@ -1354,23 +1355,23 @@ static int parse_date(Scanner *s, HTTP_Date *out)
|
|||||||
+ (s->src[s->cur+1] - '0') * 1;
|
+ (s->src[s->cur+1] - '0') * 1;
|
||||||
s->cur += 2;
|
s->cur += 2;
|
||||||
|
|
||||||
struct { HTTP_String str; HTTP_Month val; } month_table[] = {
|
struct { CHTTP_String str; CHTTP_Month val; } month_table[] = {
|
||||||
{ HTTP_STR(" Jan "), HTTP_MONTH_JAN },
|
{ CHTTP_STR(" Jan "), CHTTP_MONTH_JAN },
|
||||||
{ HTTP_STR(" Feb "), HTTP_MONTH_FEB },
|
{ CHTTP_STR(" Feb "), CHTTP_MONTH_FEB },
|
||||||
{ HTTP_STR(" Mar "), HTTP_MONTH_MAR },
|
{ CHTTP_STR(" Mar "), CHTTP_MONTH_MAR },
|
||||||
{ HTTP_STR(" Apr "), HTTP_MONTH_APR },
|
{ CHTTP_STR(" Apr "), CHTTP_MONTH_APR },
|
||||||
{ HTTP_STR(" May "), HTTP_MONTH_MAY },
|
{ CHTTP_STR(" May "), CHTTP_MONTH_MAY },
|
||||||
{ HTTP_STR(" Jun "), HTTP_MONTH_JUN },
|
{ CHTTP_STR(" Jun "), CHTTP_MONTH_JUN },
|
||||||
{ HTTP_STR(" Jul "), HTTP_MONTH_JUL },
|
{ CHTTP_STR(" Jul "), CHTTP_MONTH_JUL },
|
||||||
{ HTTP_STR(" Aug "), HTTP_MONTH_AUG },
|
{ CHTTP_STR(" Aug "), CHTTP_MONTH_AUG },
|
||||||
{ HTTP_STR(" Sep "), HTTP_MONTH_SEP },
|
{ CHTTP_STR(" Sep "), CHTTP_MONTH_SEP },
|
||||||
{ HTTP_STR(" Oct "), HTTP_MONTH_OCT },
|
{ CHTTP_STR(" Oct "), CHTTP_MONTH_OCT },
|
||||||
{ HTTP_STR(" Nov "), HTTP_MONTH_NOV },
|
{ CHTTP_STR(" Nov "), CHTTP_MONTH_NOV },
|
||||||
{ HTTP_STR(" Dec "), HTTP_MONTH_DEC },
|
{ CHTTP_STR(" Dec "), CHTTP_MONTH_DEC },
|
||||||
};
|
};
|
||||||
|
|
||||||
found = false;
|
found = false;
|
||||||
for (int i = 0; i < HTTP_COUNT(month_table); i++)
|
for (int i = 0; i < CHTTP_COUNT(month_table); i++)
|
||||||
if (consume_str(s, month_table[i].str)) {
|
if (consume_str(s, month_table[i].str)) {
|
||||||
out->month = month_table[i].val;
|
out->month = month_table[i].val;
|
||||||
found = true;
|
found = true;
|
||||||
@@ -1436,7 +1437,7 @@ static bool is_cookie_octet(char c)
|
|||||||
(c >= 0x5D && c <= 0x7E);
|
(c >= 0x5D && c <= 0x7E);
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
|
int chttp_parse_set_cookie(CHTTP_String str, CHTTP_SetCookie *out)
|
||||||
{
|
{
|
||||||
Scanner s = { str.ptr, str.len, 0 };
|
Scanner s = { str.ptr, str.len, 0 };
|
||||||
|
|
||||||
@@ -1447,7 +1448,7 @@ int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
|
|||||||
do
|
do
|
||||||
s.cur++;
|
s.cur++;
|
||||||
while (s.cur < s.len && is_tchar(s.src[s.cur]));
|
while (s.cur < s.len && is_tchar(s.src[s.cur]));
|
||||||
out->name = (HTTP_String) { s.src + off, s.cur - off };
|
out->name = (CHTTP_String) { s.src + off, s.cur - off };
|
||||||
|
|
||||||
// cookie-pair = cookie-name "=" cookie-value
|
// cookie-pair = cookie-name "=" cookie-value
|
||||||
if (s.cur == s.len || s.src[s.cur] != '=')
|
if (s.cur == s.len || s.src[s.cur] != '=')
|
||||||
@@ -1462,36 +1463,35 @@ int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
|
|||||||
s.cur++;
|
s.cur++;
|
||||||
if (s.cur == s.len || s.src[s.cur] != '"')
|
if (s.cur == s.len || s.src[s.cur] != '"')
|
||||||
return -1; // Missing closing double quote
|
return -1; // Missing closing double quote
|
||||||
out->value = (HTTP_String) { s.src + off, s.cur - off };
|
out->value = (CHTTP_String) { s.src + off, s.cur - off };
|
||||||
s.cur++; // Consume closing double quote
|
s.cur++; // Consume closing double quote
|
||||||
} else {
|
} else {
|
||||||
int off = s.cur;
|
int off = s.cur;
|
||||||
while (s.cur < s.len && is_cookie_octet(s.src[s.cur]))
|
while (s.cur < s.len && is_cookie_octet(s.src[s.cur]))
|
||||||
s.cur++;
|
s.cur++;
|
||||||
out->value = (HTTP_String) { s.src + off, s.cur - off };
|
out->value = (CHTTP_String) { s.src + off, s.cur - off };
|
||||||
}
|
}
|
||||||
|
|
||||||
// *( ";" SP cookie-av )
|
// *( ";" SP cookie-av )
|
||||||
//
|
//
|
||||||
// cookie-av = expires-av / max-age-av / domain-av /
|
// cookie-av = expires-av / max-age-av / domain-av /
|
||||||
// path-av / secure-av / httponly-av /
|
// path-av / secure-av / httponly-av /
|
||||||
// samesite-av / extension-av
|
// extension-av
|
||||||
out->secure = false;
|
out->secure = false;
|
||||||
out->http_only = false;
|
out->chttp_only = false;
|
||||||
out->have_date = false;
|
out->have_date = false;
|
||||||
out->have_max_age = false;
|
out->have_max_age = false;
|
||||||
out->have_domain = false;
|
out->have_domain = false;
|
||||||
out->have_path = false;
|
out->have_path = false;
|
||||||
out->have_samesite = false;
|
while (consume_str(&s, CHTTP_STR("; "))) {
|
||||||
while (consume_str(&s, HTTP_STR("; "))) {
|
if (consume_str(&s, CHTTP_STR("Expires="))) {
|
||||||
if (consume_str(&s, HTTP_STR("Expires="))) {
|
|
||||||
|
|
||||||
// expires-av = "Expires=" sane-cookie-date
|
// expires-av = "Expires=" sane-cookie-date
|
||||||
if (parse_date(&s, &out->date) < 0)
|
if (parse_date(&s, &out->date) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
out->have_date = true;
|
out->have_date = true;
|
||||||
|
|
||||||
} else if (consume_str(&s, HTTP_STR("Max-Age="))) {
|
} else if (consume_str(&s, CHTTP_STR("Max-Age="))) {
|
||||||
|
|
||||||
// max-age-av = "Max-Age=" non-zero-digit *DIGIT
|
// max-age-av = "Max-Age=" non-zero-digit *DIGIT
|
||||||
|
|
||||||
@@ -1508,7 +1508,7 @@ int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
|
|||||||
out->have_max_age = true;
|
out->have_max_age = true;
|
||||||
out->max_age = value;
|
out->max_age = value;
|
||||||
|
|
||||||
} else if (consume_str(&s, HTTP_STR("Domain="))) {
|
} else if (consume_str(&s, CHTTP_STR("Domain="))) {
|
||||||
|
|
||||||
// domain-av = "Domain=" domain-value
|
// domain-av = "Domain=" domain-value
|
||||||
// domain-value = <subdomain>
|
// domain-value = <subdomain>
|
||||||
@@ -1527,13 +1527,6 @@ int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
|
|||||||
// concatenated by dots. Each label may contain letters, digits,
|
// concatenated by dots. Each label may contain letters, digits,
|
||||||
// hyphens, but the first character must be a letter and the last
|
// hyphens, but the first character must be a letter and the last
|
||||||
// one can't be a hyphen.
|
// one can't be a hyphen.
|
||||||
//
|
|
||||||
// RFC 6265 Section 5.2.3: If the first character of the attribute-value
|
|
||||||
// string is ".", then ignore the leading "."
|
|
||||||
|
|
||||||
// Skip leading dot if present
|
|
||||||
if (s.cur < s.len && s.src[s.cur] == '.')
|
|
||||||
s.cur++;
|
|
||||||
|
|
||||||
int off = s.cur;
|
int off = s.cur;
|
||||||
if (s.cur == s.len || !is_alpha(s.src[s.cur]))
|
if (s.cur == s.len || !is_alpha(s.src[s.cur]))
|
||||||
@@ -1565,9 +1558,9 @@ int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
|
|||||||
}
|
}
|
||||||
|
|
||||||
out->have_domain = true;
|
out->have_domain = true;
|
||||||
out->domain = (HTTP_String) { s.src + off, s.cur - off };
|
out->domain = (CHTTP_String) { s.src + off, s.cur - off };
|
||||||
|
|
||||||
} else if (consume_str(&s, HTTP_STR("Path="))) {
|
} else if (consume_str(&s, CHTTP_STR("Path="))) {
|
||||||
|
|
||||||
// path-av = "Path=" path-value
|
// path-av = "Path=" path-value
|
||||||
// path-value = <any CHAR except CTLs or ";">
|
// path-value = <any CHAR except CTLs or ";">
|
||||||
@@ -1577,42 +1570,20 @@ int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
|
|||||||
s.cur++;
|
s.cur++;
|
||||||
|
|
||||||
out->have_path = true;
|
out->have_path = true;
|
||||||
out->path = (HTTP_String) { s.src + off, s.cur - off };
|
out->path = (CHTTP_String) { s.src + off, s.cur - off };
|
||||||
|
|
||||||
} else if (consume_str(&s, HTTP_STR("Secure"))) {
|
} else if (consume_str(&s, CHTTP_STR("Secure"))) {
|
||||||
|
|
||||||
// secure-av = "Secure"
|
// secure-av = "Secure"
|
||||||
out->secure = true;
|
out->secure = true;
|
||||||
|
|
||||||
} else if (consume_str(&s, HTTP_STR("HttpOnly"))) {
|
} else if (consume_str(&s, CHTTP_STR("HttpOnly"))) {
|
||||||
|
|
||||||
// httponly-av = "HttpOnly"
|
// httponly-av = "HttpOnly"
|
||||||
out->http_only = true;
|
out->chttp_only = true;
|
||||||
|
|
||||||
} else if (consume_str(&s, HTTP_STR("SameSite="))) {
|
|
||||||
|
|
||||||
// samesite-av = "SameSite=" samesite-value
|
|
||||||
// samesite-value = "Strict" / "Lax" / "None"
|
|
||||||
if (consume_str(&s, HTTP_STR("Strict"))) {
|
|
||||||
out->have_samesite = true;
|
|
||||||
out->samesite = HTTP_SAMESITE_STRICT;
|
|
||||||
} else if (consume_str(&s, HTTP_STR("Lax"))) {
|
|
||||||
out->have_samesite = true;
|
|
||||||
out->samesite = HTTP_SAMESITE_LAX;
|
|
||||||
} else if (consume_str(&s, HTTP_STR("None"))) {
|
|
||||||
out->have_samesite = true;
|
|
||||||
out->samesite = HTTP_SAMESITE_NONE;
|
|
||||||
} else {
|
|
||||||
// Invalid SameSite value, skip to next attribute
|
|
||||||
while (s.cur < s.len && s.src[s.cur] != ';')
|
|
||||||
s.cur++;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// extension-av: Unknown attribute, skip to next semicolon
|
return -1; // Invalid attribute
|
||||||
// This allows forward compatibility with new cookie attributes
|
|
||||||
while (s.cur < s.len && s.src[s.cur] != ';')
|
|
||||||
s.cur++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+87
-96
@@ -1,164 +1,155 @@
|
|||||||
|
|
||||||
#define HTTP_MAX_HEADERS 32
|
#define CHTTP_MAX_HEADERS 32
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned int data;
|
unsigned int data;
|
||||||
} HTTP_IPv4;
|
} CHTTP_IPv4;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned short data[8];
|
unsigned short data[8];
|
||||||
} HTTP_IPv6;
|
} CHTTP_IPv6;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
HTTP_HOST_MODE_VOID = 0,
|
CHTTP_HOST_MODE_VOID = 0,
|
||||||
HTTP_HOST_MODE_NAME,
|
CHTTP_HOST_MODE_NAME,
|
||||||
HTTP_HOST_MODE_IPV4,
|
CHTTP_HOST_MODE_IPV4,
|
||||||
HTTP_HOST_MODE_IPV6,
|
CHTTP_HOST_MODE_IPV6,
|
||||||
} HTTP_HostMode;
|
} CHTTP_HostMode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HTTP_HostMode mode;
|
CHTTP_HostMode mode;
|
||||||
HTTP_String text;
|
CHTTP_String text;
|
||||||
union {
|
union {
|
||||||
HTTP_String name;
|
CHTTP_String name;
|
||||||
HTTP_IPv4 ipv4;
|
CHTTP_IPv4 ipv4;
|
||||||
HTTP_IPv6 ipv6;
|
CHTTP_IPv6 ipv6;
|
||||||
};
|
};
|
||||||
} HTTP_Host;
|
} CHTTP_Host;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HTTP_String userinfo;
|
CHTTP_String userinfo;
|
||||||
HTTP_Host host;
|
CHTTP_Host host;
|
||||||
int port;
|
int port;
|
||||||
} HTTP_Authority;
|
} CHTTP_Authority;
|
||||||
|
|
||||||
// ZII
|
// ZII
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HTTP_String scheme;
|
CHTTP_String scheme;
|
||||||
HTTP_Authority authority;
|
CHTTP_Authority authority;
|
||||||
HTTP_String path;
|
CHTTP_String path;
|
||||||
HTTP_String query;
|
CHTTP_String query;
|
||||||
HTTP_String fragment;
|
CHTTP_String fragment;
|
||||||
} HTTP_URL;
|
} CHTTP_URL;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
HTTP_METHOD_GET,
|
CHTTP_METHOD_GET,
|
||||||
HTTP_METHOD_HEAD,
|
CHTTP_METHOD_HEAD,
|
||||||
HTTP_METHOD_POST,
|
CHTTP_METHOD_POST,
|
||||||
HTTP_METHOD_PUT,
|
CHTTP_METHOD_PUT,
|
||||||
HTTP_METHOD_DELETE,
|
CHTTP_METHOD_DELETE,
|
||||||
HTTP_METHOD_CONNECT,
|
CHTTP_METHOD_CONNECT,
|
||||||
HTTP_METHOD_OPTIONS,
|
CHTTP_METHOD_OPTIONS,
|
||||||
HTTP_METHOD_TRACE,
|
CHTTP_METHOD_TRACE,
|
||||||
HTTP_METHOD_PATCH,
|
CHTTP_METHOD_PATCH,
|
||||||
} HTTP_Method;
|
} CHTTP_Method;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HTTP_String name;
|
CHTTP_String name;
|
||||||
HTTP_String value;
|
CHTTP_String value;
|
||||||
} HTTP_Header;
|
} CHTTP_Header;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool secure;
|
bool secure;
|
||||||
HTTP_Method method;
|
CHTTP_Method method;
|
||||||
HTTP_URL url;
|
CHTTP_URL url;
|
||||||
int minor;
|
int minor;
|
||||||
int num_headers;
|
int num_headers;
|
||||||
HTTP_Header headers[HTTP_MAX_HEADERS];
|
CHTTP_Header headers[CHTTP_MAX_HEADERS];
|
||||||
HTTP_String body;
|
CHTTP_String body;
|
||||||
} HTTP_Request;
|
} CHTTP_Request;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* context;
|
void* context;
|
||||||
int minor;
|
int minor;
|
||||||
int status;
|
int status;
|
||||||
HTTP_String reason;
|
CHTTP_String reason;
|
||||||
int num_headers;
|
int num_headers;
|
||||||
HTTP_Header headers[HTTP_MAX_HEADERS];
|
CHTTP_Header headers[CHTTP_MAX_HEADERS];
|
||||||
HTTP_String body;
|
CHTTP_String body;
|
||||||
} HTTP_Response;
|
} CHTTP_Response;
|
||||||
|
|
||||||
int http_parse_ipv4 (char *src, int len, HTTP_IPv4 *ipv4);
|
int chttp_parse_ipv4 (char *src, int len, CHTTP_IPv4 *ipv4);
|
||||||
int http_parse_ipv6 (char *src, int len, HTTP_IPv6 *ipv6);
|
int chttp_parse_ipv6 (char *src, int len, CHTTP_IPv6 *ipv6);
|
||||||
int http_parse_url (char *src, int len, HTTP_URL *url);
|
int chttp_parse_url (char *src, int len, CHTTP_URL *url);
|
||||||
int http_parse_request (char *src, int len, HTTP_Request *req);
|
int chttp_parse_request (char *src, int len, CHTTP_Request *req);
|
||||||
int http_parse_response (char *src, int len, HTTP_Response *res);
|
int chttp_parse_response (char *src, int len, CHTTP_Response *res);
|
||||||
|
|
||||||
int http_find_header (HTTP_Header *headers, int num_headers, HTTP_String name);
|
int chttp_find_header (CHTTP_Header *headers, int num_headers, CHTTP_String name);
|
||||||
|
|
||||||
HTTP_String http_get_cookie (HTTP_Request *req, HTTP_String name);
|
CHTTP_String chttp_get_cookie (CHTTP_Request *req, CHTTP_String name);
|
||||||
HTTP_String http_get_param (HTTP_String body, HTTP_String str, char *mem, int cap);
|
CHTTP_String chttp_get_param (CHTTP_String body, CHTTP_String str, char *mem, int cap);
|
||||||
int http_get_param_i (HTTP_String body, HTTP_String str);
|
int chttp_get_param_i (CHTTP_String body, CHTTP_String str);
|
||||||
|
|
||||||
// Checks whether the request was meant for the host with the given
|
// Checks whether the request was meant for the host with the given
|
||||||
// domain an port. If port is -1, the default value of 80 is assumed.
|
// domain an port. If port is -1, the default value of 80 is assumed.
|
||||||
bool http_match_host(HTTP_Request *req, HTTP_String domain, int port);
|
bool chttp_match_host(CHTTP_Request *req, CHTTP_String domain, int port);
|
||||||
|
|
||||||
// Date and cookie types for Set-Cookie header parsing
|
// Date and cookie types for Set-Cookie header parsing
|
||||||
typedef enum {
|
typedef enum {
|
||||||
HTTP_WEEKDAY_MON,
|
CHTTP_WEEKDAY_MON,
|
||||||
HTTP_WEEKDAY_TUE,
|
CHTTP_WEEKDAY_TUE,
|
||||||
HTTP_WEEKDAY_WED,
|
CHTTP_WEEKDAY_WED,
|
||||||
HTTP_WEEKDAY_THU,
|
CHTTP_WEEKDAY_THU,
|
||||||
HTTP_WEEKDAY_FRI,
|
CHTTP_WEEKDAY_FRI,
|
||||||
HTTP_WEEKDAY_SAT,
|
CHTTP_WEEKDAY_SAT,
|
||||||
HTTP_WEEKDAY_SUN,
|
CHTTP_WEEKDAY_SUN,
|
||||||
} HTTP_WeekDay;
|
} CHTTP_WeekDay;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
HTTP_MONTH_JAN,
|
CHTTP_MONTH_JAN,
|
||||||
HTTP_MONTH_FEB,
|
CHTTP_MONTH_FEB,
|
||||||
HTTP_MONTH_MAR,
|
CHTTP_MONTH_MAR,
|
||||||
HTTP_MONTH_APR,
|
CHTTP_MONTH_APR,
|
||||||
HTTP_MONTH_MAY,
|
CHTTP_MONTH_MAY,
|
||||||
HTTP_MONTH_JUN,
|
CHTTP_MONTH_JUN,
|
||||||
HTTP_MONTH_JUL,
|
CHTTP_MONTH_JUL,
|
||||||
HTTP_MONTH_AUG,
|
CHTTP_MONTH_AUG,
|
||||||
HTTP_MONTH_SEP,
|
CHTTP_MONTH_SEP,
|
||||||
HTTP_MONTH_OCT,
|
CHTTP_MONTH_OCT,
|
||||||
HTTP_MONTH_NOV,
|
CHTTP_MONTH_NOV,
|
||||||
HTTP_MONTH_DEC,
|
CHTTP_MONTH_DEC,
|
||||||
} HTTP_Month;
|
} CHTTP_Month;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HTTP_WeekDay week_day;
|
CHTTP_WeekDay week_day;
|
||||||
int day;
|
int day;
|
||||||
HTTP_Month month;
|
CHTTP_Month month;
|
||||||
int year;
|
int year;
|
||||||
int hour;
|
int hour;
|
||||||
int minute;
|
int minute;
|
||||||
int second;
|
int second;
|
||||||
} HTTP_Date;
|
} CHTTP_Date;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
HTTP_SAMESITE_NONE,
|
|
||||||
HTTP_SAMESITE_LAX,
|
|
||||||
HTTP_SAMESITE_STRICT,
|
|
||||||
} HTTP_SameSite;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HTTP_String name;
|
CHTTP_String name;
|
||||||
HTTP_String value;
|
CHTTP_String value;
|
||||||
|
|
||||||
bool secure;
|
bool secure;
|
||||||
bool http_only;
|
bool chttp_only;
|
||||||
|
|
||||||
bool have_date;
|
bool have_date;
|
||||||
HTTP_Date date;
|
CHTTP_Date date;
|
||||||
|
|
||||||
bool have_max_age;
|
bool have_max_age;
|
||||||
uint32_t max_age;
|
uint32_t max_age;
|
||||||
|
|
||||||
bool have_domain;
|
bool have_domain;
|
||||||
HTTP_String domain;
|
CHTTP_String domain;
|
||||||
|
|
||||||
bool have_path;
|
bool have_path;
|
||||||
HTTP_String path;
|
CHTTP_String path;
|
||||||
|
} CHTTP_SetCookie;
|
||||||
bool have_samesite;
|
|
||||||
HTTP_SameSite samesite;
|
|
||||||
} HTTP_SetCookie;
|
|
||||||
|
|
||||||
// Parses a Set-Cookie header value
|
// Parses a Set-Cookie header value
|
||||||
// Returns 0 on success, -1 on error
|
// Returns 0 on success, -1 on error
|
||||||
int http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out);
|
int chttp_parse_set_cookie(CHTTP_String str, CHTTP_SetCookie *out);
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ void client_secure_context_free(ClientSecureContext *ctx)
|
|||||||
{
|
{
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
SSL_CTX_free(ctx->p);
|
SSL_CTX_free(ctx->p);
|
||||||
|
#else
|
||||||
|
(void) ctx;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +76,7 @@ static int servername_callback(SSL *ssl, int *ad, void *arg)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int server_secure_context_init(ServerSecureContext *ctx,
|
int server_secure_context_init(ServerSecureContext *ctx,
|
||||||
HTTP_String cert_file, HTTP_String key_file)
|
CHTTP_String cert_file, CHTTP_String key_file)
|
||||||
{
|
{
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
SSL_CTX *p = SSL_CTX_new(TLS_server_method());
|
SSL_CTX *p = SSL_CTX_new(TLS_server_method());
|
||||||
@@ -101,7 +103,7 @@ int server_secure_context_init(ServerSecureContext *ctx,
|
|||||||
key_buffer[key_file.len] = '\0';
|
key_buffer[key_file.len] = '\0';
|
||||||
|
|
||||||
// Load certificate and private key
|
// Load certificate and private key
|
||||||
if (SSL_CTX_use_certificate_file(p, cert_buffer, SSL_FILETYPE_PEM) != 1) {
|
if (SSL_CTX_use_certificate_chain_file(p, cert_buffer) != 1) {
|
||||||
SSL_CTX_free(p);
|
SSL_CTX_free(p);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -143,7 +145,7 @@ void server_secure_context_free(ServerSecureContext *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
||||||
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
|
CHTTP_String domain, CHTTP_String cert_file, CHTTP_String key_file)
|
||||||
{
|
{
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
if (ctx->num_certs == SERVER_CERTIFICATE_LIMIT)
|
if (ctx->num_certs == SERVER_CERTIFICATE_LIMIT)
|
||||||
@@ -171,7 +173,7 @@ int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
|||||||
memcpy(key_buffer, key_file.ptr, key_file.len);
|
memcpy(key_buffer, key_file.ptr, key_file.len);
|
||||||
key_buffer[key_file.len] = '\0';
|
key_buffer[key_file.len] = '\0';
|
||||||
|
|
||||||
if (SSL_CTX_use_certificate_file(p, cert_buffer, SSL_FILETYPE_PEM) != 1) {
|
if (SSL_CTX_use_certificate_chain_file(p, cert_buffer) != 1) {
|
||||||
SSL_CTX_free(p);
|
SSL_CTX_free(p);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ typedef struct {
|
|||||||
} ServerSecureContext;
|
} ServerSecureContext;
|
||||||
|
|
||||||
int server_secure_context_init(ServerSecureContext *ctx,
|
int server_secure_context_init(ServerSecureContext *ctx,
|
||||||
HTTP_String cert_file, HTTP_String key_file);
|
CHTTP_String cert_file, CHTTP_String key_file);
|
||||||
void server_secure_context_free(ServerSecureContext *ctx);
|
void server_secure_context_free(ServerSecureContext *ctx);
|
||||||
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
int server_secure_context_add_certificate(ServerSecureContext *ctx,
|
||||||
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
|
CHTTP_String domain, CHTTP_String cert_file, CHTTP_String key_file);
|
||||||
|
|||||||
+145
-123
@@ -1,33 +1,34 @@
|
|||||||
|
|
||||||
static void http_server_conn_init(HTTP_ServerConn *conn,
|
static void chttp_server_conn_init(CHTTP_ServerConn *conn,
|
||||||
SocketHandle handle, uint32_t input_buffer_limit,
|
SocketHandle handle, uint32_t input_buffer_limit,
|
||||||
uint32_t output_buffer_limit)
|
uint32_t output_buffer_limit)
|
||||||
{
|
{
|
||||||
conn->state = HTTP_SERVER_CONN_BUFFERING;
|
conn->state = CHTTP_SERVER_CONN_BUFFERING;
|
||||||
conn->handle = handle;
|
conn->handle = handle;
|
||||||
conn->closing = false;
|
conn->closing = false;
|
||||||
byte_queue_init(&conn->input, input_buffer_limit);
|
byte_queue_init(&conn->input, input_buffer_limit);
|
||||||
byte_queue_init(&conn->output, output_buffer_limit);
|
byte_queue_init(&conn->output, output_buffer_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void http_server_conn_free(HTTP_ServerConn *conn)
|
static void chttp_server_conn_free(CHTTP_ServerConn *conn)
|
||||||
{
|
{
|
||||||
byte_queue_free(&conn->output);
|
byte_queue_free(&conn->output);
|
||||||
byte_queue_free(&conn->input);
|
byte_queue_free(&conn->input);
|
||||||
|
conn->state = CHTTP_SERVER_CONN_FREE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_server_init(HTTP_Server *server)
|
int chttp_server_init(CHTTP_Server *server)
|
||||||
{
|
{
|
||||||
server->input_buffer_limit = 1<<20;
|
server->input_buffer_limit = 1<<20;
|
||||||
server->output_buffer_limit = 1<<20;
|
server->output_buffer_limit = 1<<24;
|
||||||
|
|
||||||
server->trace_bytes = false;
|
server->trace_bytes = false;
|
||||||
server->reuse_addr = false;
|
server->reuse_addr = false;
|
||||||
server->backlog = 32;
|
server->backlog = 32;
|
||||||
|
|
||||||
server->num_conns = 0;
|
server->num_conns = 0;
|
||||||
for (int i = 0; i < HTTP_SERVER_CAPACITY; i++) {
|
for (int i = 0; i < CHTTP_SERVER_CAPACITY; i++) {
|
||||||
server->conns[i].state = HTTP_SERVER_CONN_FREE;
|
server->conns[i].state = CHTTP_SERVER_CONN_FREE;
|
||||||
server->conns[i].gen = 0;
|
server->conns[i].gen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,77 +36,77 @@ int http_server_init(HTTP_Server *server)
|
|||||||
server->ready_head = 0;
|
server->ready_head = 0;
|
||||||
|
|
||||||
return socket_manager_init(&server->sockets,
|
return socket_manager_init(&server->sockets,
|
||||||
server->socket_pool, HTTP_SERVER_CAPACITY);
|
server->socket_pool, CHTTP_SERVER_CAPACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_server_free(HTTP_Server *server)
|
void chttp_server_free(CHTTP_Server *server)
|
||||||
{
|
{
|
||||||
socket_manager_free(&server->sockets);
|
socket_manager_free(&server->sockets);
|
||||||
|
|
||||||
for (int i = 0, j = 0; j < server->num_conns; i++) {
|
for (int i = 0, j = 0; j < server->num_conns; i++) {
|
||||||
HTTP_ServerConn *conn = &server->conns[i];
|
CHTTP_ServerConn *conn = &server->conns[i];
|
||||||
if (conn->state == HTTP_SERVER_CONN_FREE)
|
if (conn->state == CHTTP_SERVER_CONN_FREE)
|
||||||
continue;
|
continue;
|
||||||
j++;
|
j++;
|
||||||
|
|
||||||
http_server_conn_free(conn);
|
chttp_server_conn_free(conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_server_set_input_limit(HTTP_Server *server, uint32_t limit)
|
void chttp_server_set_input_limit(CHTTP_Server *server, uint32_t limit)
|
||||||
{
|
{
|
||||||
server->input_buffer_limit = limit;
|
server->input_buffer_limit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_server_set_output_limit(HTTP_Server *server, uint32_t limit)
|
void chttp_server_set_output_limit(CHTTP_Server *server, uint32_t limit)
|
||||||
{
|
{
|
||||||
server->output_buffer_limit = limit;
|
server->output_buffer_limit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_server_set_trace_bytes(HTTP_Server *server, bool value)
|
void chttp_server_set_trace_bytes(CHTTP_Server *server, bool value)
|
||||||
{
|
{
|
||||||
server->trace_bytes = value;
|
server->trace_bytes = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_server_set_reuse_addr(HTTP_Server *server, bool reuse)
|
void chttp_server_set_reuse_addr(CHTTP_Server *server, bool reuse)
|
||||||
{
|
{
|
||||||
server->reuse_addr = reuse;
|
server->reuse_addr = reuse;
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_server_set_backlog(HTTP_Server *server, int backlog)
|
void chttp_server_set_backlog(CHTTP_Server *server, int backlog)
|
||||||
{
|
{
|
||||||
server->backlog = backlog;
|
server->backlog = backlog;
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_server_listen_tcp(HTTP_Server *server,
|
int chttp_server_listen_tcp(CHTTP_Server *server,
|
||||||
HTTP_String addr, Port port)
|
CHTTP_String addr, Port port)
|
||||||
{
|
{
|
||||||
return socket_manager_listen_tcp(&server->sockets,
|
return socket_manager_listen_tcp(&server->sockets,
|
||||||
addr, port, server->backlog, server->reuse_addr);
|
addr, port, server->backlog, server->reuse_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_server_listen_tls(HTTP_Server *server,
|
int chttp_server_listen_tls(CHTTP_Server *server,
|
||||||
HTTP_String addr, Port port, HTTP_String cert_file_name,
|
CHTTP_String addr, Port port, CHTTP_String cert_file_name,
|
||||||
HTTP_String key_file_name)
|
CHTTP_String key_file_name)
|
||||||
{
|
{
|
||||||
return socket_manager_listen_tls(&server->sockets,
|
return socket_manager_listen_tls(&server->sockets,
|
||||||
addr, port, server->backlog, server->reuse_addr,
|
addr, port, server->backlog, server->reuse_addr,
|
||||||
cert_file_name, key_file_name);
|
cert_file_name, key_file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_server_add_certificate(HTTP_Server *server,
|
int chttp_server_add_certificate(CHTTP_Server *server,
|
||||||
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
|
CHTTP_String domain, CHTTP_String cert_file, CHTTP_String key_file)
|
||||||
{
|
{
|
||||||
return socket_manager_add_certificate(&server->sockets,
|
return socket_manager_add_certificate(&server->sockets,
|
||||||
domain, cert_file, key_file);
|
domain, cert_file, key_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
int http_server_wakeup(HTTP_Server *server)
|
int chttp_server_wakeup(CHTTP_Server *server)
|
||||||
{
|
{
|
||||||
return socket_manager_wakeup(&server->sockets);
|
return socket_manager_wakeup(&server->sockets);
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_server_register_events(HTTP_Server *server,
|
void chttp_server_register_events(CHTTP_Server *server,
|
||||||
EventRegister *reg)
|
EventRegister *reg)
|
||||||
{
|
{
|
||||||
socket_manager_register_events(&server->sockets, reg);
|
socket_manager_register_events(&server->sockets, reg);
|
||||||
@@ -117,12 +118,12 @@ void http_server_register_events(HTTP_Server *server,
|
|||||||
// to the ready queue. If the request is invalid,
|
// to the ready queue. If the request is invalid,
|
||||||
// close the socket.
|
// close the socket.
|
||||||
static void
|
static void
|
||||||
check_request_buffer(HTTP_Server *server, HTTP_ServerConn *conn)
|
check_request_buffer(CHTTP_Server *server, CHTTP_ServerConn *conn)
|
||||||
{
|
{
|
||||||
assert(conn->state == HTTP_SERVER_CONN_BUFFERING);
|
assert(conn->state == CHTTP_SERVER_CONN_BUFFERING);
|
||||||
|
|
||||||
ByteView src = byte_queue_read_buf(&conn->input);
|
ByteView src = byte_queue_read_buf(&conn->input);
|
||||||
int ret = http_parse_request(src.ptr, src.len, &conn->request);
|
int ret = chttp_parse_request(src.ptr, src.len, &conn->request);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
||||||
// Invalid request
|
// Invalid request
|
||||||
@@ -144,22 +145,25 @@ check_request_buffer(HTTP_Server *server, HTTP_ServerConn *conn)
|
|||||||
// Ready
|
// Ready
|
||||||
assert(ret > 0);
|
assert(ret > 0);
|
||||||
|
|
||||||
conn->state = HTTP_SERVER_CONN_WAIT_STATUS;
|
// Stop receiving I/O events while we are building the response
|
||||||
|
socket_silent(&server->sockets, conn->handle, true);
|
||||||
|
|
||||||
|
conn->state = CHTTP_SERVER_CONN_WAIT_STATUS;
|
||||||
conn->request_len = ret;
|
conn->request_len = ret;
|
||||||
conn->response_offset = byte_queue_offset(&conn->output);
|
conn->response_offset = byte_queue_offset(&conn->output);
|
||||||
|
|
||||||
// Push to the ready queue
|
// Push to the ready queue
|
||||||
assert(server->num_ready < HTTP_SERVER_CAPACITY);
|
assert(server->num_ready < CHTTP_SERVER_CAPACITY);
|
||||||
int tail = (server->ready_head + server->num_ready) % HTTP_SERVER_CAPACITY;
|
int tail = (server->ready_head + server->num_ready) % CHTTP_SERVER_CAPACITY;
|
||||||
server->ready[tail] = conn - server->conns;
|
server->ready[tail] = conn - server->conns;
|
||||||
server->num_ready++;
|
server->num_ready++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
http_server_conn_process_events(HTTP_Server *server, HTTP_ServerConn *conn)
|
chttp_server_conn_process_events(CHTTP_Server *server, CHTTP_ServerConn *conn)
|
||||||
{
|
{
|
||||||
if (conn->state == HTTP_SERVER_CONN_FLUSHING) {
|
if (conn->state == CHTTP_SERVER_CONN_FLUSHING) {
|
||||||
|
|
||||||
ByteView src = byte_queue_read_buf(&conn->output);
|
ByteView src = byte_queue_read_buf(&conn->output);
|
||||||
|
|
||||||
@@ -168,7 +172,7 @@ http_server_conn_process_events(HTTP_Server *server, HTTP_ServerConn *conn)
|
|||||||
num = socket_send(&server->sockets, conn->handle, src.ptr, src.len);
|
num = socket_send(&server->sockets, conn->handle, src.ptr, src.len);
|
||||||
|
|
||||||
if (server->trace_bytes)
|
if (server->trace_bytes)
|
||||||
print_bytes(HTTP_STR("<< "), (HTTP_String) { src.ptr, num });
|
print_bytes(CHTTP_STR("<< "), (CHTTP_String) { src.ptr, num });
|
||||||
|
|
||||||
byte_queue_read_ack(&conn->output, num);
|
byte_queue_read_ack(&conn->output, num);
|
||||||
|
|
||||||
@@ -185,13 +189,13 @@ http_server_conn_process_events(HTTP_Server *server, HTTP_ServerConn *conn)
|
|||||||
socket_close(&server->sockets, conn->handle);
|
socket_close(&server->sockets, conn->handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
conn->state = HTTP_SERVER_CONN_BUFFERING;
|
conn->state = CHTTP_SERVER_CONN_BUFFERING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->state == HTTP_SERVER_CONN_BUFFERING) {
|
if (conn->state == CHTTP_SERVER_CONN_BUFFERING) {
|
||||||
|
|
||||||
int min_recv = 1<<10;
|
int min_recv = 1<<9;
|
||||||
byte_queue_write_setmincap(&conn->input, min_recv);
|
byte_queue_write_setmincap(&conn->input, min_recv);
|
||||||
|
|
||||||
// Note that it's extra important that we don't
|
// Note that it's extra important that we don't
|
||||||
@@ -205,7 +209,7 @@ http_server_conn_process_events(HTTP_Server *server, HTTP_ServerConn *conn)
|
|||||||
num = socket_recv(&server->sockets, conn->handle, dst.ptr, dst.len);
|
num = socket_recv(&server->sockets, conn->handle, dst.ptr, dst.len);
|
||||||
|
|
||||||
if (server->trace_bytes)
|
if (server->trace_bytes)
|
||||||
print_bytes(HTTP_STR(">> "), (HTTP_String) { dst.ptr, num });
|
print_bytes(CHTTP_STR(">> "), (CHTTP_String) { dst.ptr, num });
|
||||||
|
|
||||||
byte_queue_write_ack(&conn->input, num);
|
byte_queue_write_ack(&conn->input, num);
|
||||||
|
|
||||||
@@ -217,38 +221,50 @@ http_server_conn_process_events(HTTP_Server *server, HTTP_ServerConn *conn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_server_process_events(HTTP_Server *server,
|
void chttp_server_process_events(CHTTP_Server *server,
|
||||||
EventRegister reg)
|
EventRegister reg)
|
||||||
{
|
{
|
||||||
SocketEvent events[HTTP_SERVER_CAPACITY];
|
SocketEvent events[CHTTP_SERVER_CAPACITY];
|
||||||
int num_events = socket_manager_translate_events(&server->sockets, events, reg);
|
int num_events = socket_manager_translate_events(&server->sockets, events, reg);
|
||||||
|
|
||||||
for (int i = 0; i < num_events; i++) {
|
for (int i = 0; i < num_events; i++) {
|
||||||
|
|
||||||
HTTP_ServerConn *conn = events[i].user;
|
CHTTP_ServerConn *conn = events[i].user;
|
||||||
|
|
||||||
if (events[i].type == SOCKET_EVENT_DISCONNECT) {
|
if (events[i].type == SOCKET_EVENT_DISCONNECT) {
|
||||||
|
|
||||||
http_server_conn_free(conn);
|
if (conn) {
|
||||||
|
chttp_server_conn_free(conn); // TODO: what if this was in the ready queue?
|
||||||
server->num_conns--;
|
server->num_conns--;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (events[i].type == SOCKET_EVENT_CREATION_TIMEOUT) {
|
||||||
|
|
||||||
|
// TODO: This is too abrupt
|
||||||
|
socket_close(&server->sockets, events[i].handle);
|
||||||
|
|
||||||
|
} else if (events[i].type == SOCKET_EVENT_RECV_TIMEOUT) {
|
||||||
|
|
||||||
|
// TODO: This is too abrupt
|
||||||
|
socket_close(&server->sockets, events[i].handle);
|
||||||
|
|
||||||
} else if (events[i].type == SOCKET_EVENT_READY) {
|
} else if (events[i].type == SOCKET_EVENT_READY) {
|
||||||
|
|
||||||
if (events[i].user == NULL) {
|
if (events[i].user == NULL) {
|
||||||
|
|
||||||
if (server->num_conns == HTTP_SERVER_CAPACITY) {
|
if (server->num_conns == CHTTP_SERVER_CAPACITY) {
|
||||||
socket_close(&server->sockets, events[i].handle);
|
socket_close(&server->sockets, events[i].handle);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int j = 0;
|
int j = 0;
|
||||||
while (server->conns[j].state != HTTP_SERVER_CONN_FREE) {
|
while (server->conns[j].state != CHTTP_SERVER_CONN_FREE) {
|
||||||
j++;
|
j++;
|
||||||
assert(i < HTTP_SERVER_CAPACITY);
|
assert(j < CHTTP_SERVER_CAPACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
conn = &server->conns[j];
|
conn = &server->conns[j];
|
||||||
http_server_conn_init(conn,
|
chttp_server_conn_init(conn,
|
||||||
events[i].handle,
|
events[i].handle,
|
||||||
server->input_buffer_limit,
|
server->input_buffer_limit,
|
||||||
server->output_buffer_limit);
|
server->output_buffer_limit);
|
||||||
@@ -258,44 +274,43 @@ void http_server_process_events(HTTP_Server *server,
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (socket_ready(&server->sockets, events[i].handle)
|
while (socket_ready(&server->sockets, events[i].handle)
|
||||||
&& conn->state != HTTP_SERVER_CONN_WAIT_STATUS)
|
&& conn->state != CHTTP_SERVER_CONN_WAIT_STATUS)
|
||||||
http_server_conn_process_events(server, conn);
|
chttp_server_conn_process_events(server, conn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool http_server_next_request(HTTP_Server *server,
|
bool chttp_server_next_request(CHTTP_Server *server,
|
||||||
HTTP_Request **request, HTTP_ResponseBuilder *builder)
|
CHTTP_Request **request, CHTTP_ResponseBuilder *builder)
|
||||||
{
|
{
|
||||||
if (server->num_ready == 0)
|
if (server->num_ready == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
HTTP_ServerConn *conn = &server->conns[server->ready[server->ready_head]];
|
CHTTP_ServerConn *conn = &server->conns[server->ready[server->ready_head]];
|
||||||
server->ready_head = (server->ready_head + 1) % HTTP_SERVER_CAPACITY;
|
server->ready_head = (server->ready_head + 1) % CHTTP_SERVER_CAPACITY;
|
||||||
server->num_ready--;
|
server->num_ready--;
|
||||||
|
|
||||||
assert(conn->state == HTTP_SERVER_CONN_WAIT_STATUS);
|
assert(conn->state == CHTTP_SERVER_CONN_WAIT_STATUS);
|
||||||
*request = &conn->request;
|
*request = &conn->request;
|
||||||
*builder = (HTTP_ResponseBuilder) { server, conn - server->conns, conn->gen };
|
*builder = (CHTTP_ResponseBuilder) { server, conn - server->conns, conn->gen };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_server_wait_request(HTTP_Server *server,
|
void chttp_server_wait_request(CHTTP_Server *server,
|
||||||
HTTP_Request **request, HTTP_ResponseBuilder *builder)
|
CHTTP_Request **request, CHTTP_ResponseBuilder *builder)
|
||||||
{
|
{
|
||||||
for (;;) {
|
for (;;) {
|
||||||
void *ptrs[HTTP_SERVER_POLL_CAPACITY];
|
void *ptrs[CHTTP_SERVER_POLL_CAPACITY];
|
||||||
struct pollfd polled[HTTP_SERVER_POLL_CAPACITY];
|
struct pollfd polled[CHTTP_SERVER_POLL_CAPACITY];
|
||||||
|
|
||||||
EventRegister reg = { ptrs, polled, 0 };
|
EventRegister reg = { ptrs, polled, 0, -1 };
|
||||||
http_server_register_events(server, ®);
|
chttp_server_register_events(server, ®);
|
||||||
|
|
||||||
if (reg.num_polled > 0)
|
POLL(reg.polled, reg.num_polled, reg.timeout);
|
||||||
POLL(reg.polled, reg.num_polled, -1);
|
|
||||||
|
|
||||||
http_server_process_events(server, reg);
|
chttp_server_process_events(server, reg);
|
||||||
|
|
||||||
if (http_server_next_request(server, request, builder))
|
if (chttp_server_next_request(server, request, builder))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,17 +321,17 @@ void http_server_wait_request(HTTP_Server *server,
|
|||||||
// can be returned, as any builder is invalidated by
|
// can be returned, as any builder is invalidated by
|
||||||
// incrementing the connection's generation counter
|
// incrementing the connection's generation counter
|
||||||
// when a response is completed.
|
// when a response is completed.
|
||||||
static HTTP_ServerConn*
|
static CHTTP_ServerConn*
|
||||||
builder_to_conn(HTTP_ResponseBuilder builder)
|
builder_to_conn(CHTTP_ResponseBuilder builder)
|
||||||
{
|
{
|
||||||
HTTP_Server *server = builder.server;
|
CHTTP_Server *server = builder.server;
|
||||||
if (server == NULL)
|
if (server == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (builder.index > HTTP_SERVER_CAPACITY)
|
if (builder.index > CHTTP_SERVER_CAPACITY)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
HTTP_ServerConn *conn = &server->conns[builder.index];
|
CHTTP_ServerConn *conn = &server->conns[builder.index];
|
||||||
if (builder.gen != conn->gen)
|
if (builder.gen != conn->gen)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -391,41 +406,32 @@ get_status_text(int code)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
write_status(HTTP_ServerConn *conn, int status)
|
write_status(CHTTP_ServerConn *conn, int status)
|
||||||
{
|
{
|
||||||
byte_queue_write_fmt(&conn->output,
|
byte_queue_write_fmt(&conn->output,
|
||||||
"HTTP/1.1 %d %s\r\n",
|
"HTTP/1.1 %d %s\r\n",
|
||||||
status, get_status_text(status));
|
status, get_status_text(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_response_builder_status(HTTP_ResponseBuilder builder, int status)
|
void chttp_response_builder_status(CHTTP_ResponseBuilder builder, int status)
|
||||||
{
|
{
|
||||||
HTTP_ServerConn *conn = builder_to_conn(builder);
|
CHTTP_ServerConn *conn = builder_to_conn(builder);
|
||||||
if (conn == NULL)
|
if (conn == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (conn->state != HTTP_SERVER_CONN_WAIT_STATUS) {
|
if (conn->state != CHTTP_SERVER_CONN_WAIT_STATUS) {
|
||||||
// Reset all response content and start from scrach.
|
// Reset all response content and start from scrach.
|
||||||
byte_queue_remove_from_offset(&conn->output, conn->response_offset);
|
byte_queue_remove_from_offset(&conn->output, conn->response_offset);
|
||||||
conn->state = HTTP_SERVER_CONN_WAIT_STATUS;
|
conn->state = CHTTP_SERVER_CONN_WAIT_STATUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_status(conn, status);
|
write_status(conn, status);
|
||||||
|
|
||||||
conn->state = HTTP_SERVER_CONN_WAIT_HEADER;
|
conn->state = CHTTP_SERVER_CONN_WAIT_HEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_response_builder_header(HTTP_ResponseBuilder builder, HTTP_String str)
|
static bool is_header_valid(CHTTP_String str)
|
||||||
{
|
{
|
||||||
HTTP_ServerConn *conn = builder_to_conn(builder);
|
|
||||||
if (conn == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (conn->state != HTTP_SERVER_CONN_WAIT_HEADER)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Validate header: must contain a colon and no control characters
|
|
||||||
// (to prevent HTTP response splitting attacks)
|
|
||||||
bool has_colon = false;
|
bool has_colon = false;
|
||||||
for (int i = 0; i < str.len; i++) {
|
for (int i = 0; i < str.len; i++) {
|
||||||
char c = str.ptr[i];
|
char c = str.ptr[i];
|
||||||
@@ -433,43 +439,56 @@ void http_response_builder_header(HTTP_ResponseBuilder builder, HTTP_String str)
|
|||||||
has_colon = true;
|
has_colon = true;
|
||||||
// Reject control characters (especially \r and \n)
|
// Reject control characters (especially \r and \n)
|
||||||
if (c < 0x20 && c != '\t')
|
if (c < 0x20 && c != '\t')
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (!has_colon)
|
return has_colon;
|
||||||
|
}
|
||||||
|
|
||||||
|
void chttp_response_builder_header(CHTTP_ResponseBuilder builder, CHTTP_String str)
|
||||||
|
{
|
||||||
|
CHTTP_ServerConn *conn = builder_to_conn(builder);
|
||||||
|
if (conn == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (conn->state != CHTTP_SERVER_CONN_WAIT_HEADER)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Header must contain a colon and no control characters
|
||||||
|
// to prevent HTTP response splitting attacks
|
||||||
|
if (!is_header_valid(str)) return; // Silently drop it
|
||||||
|
|
||||||
byte_queue_write(&conn->output, str.ptr, str.len);
|
byte_queue_write(&conn->output, str.ptr, str.len);
|
||||||
byte_queue_write(&conn->output, "\r\n", 2);
|
byte_queue_write(&conn->output, "\r\n", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void append_special_headers(HTTP_ServerConn *conn)
|
static void append_special_headers(CHTTP_ServerConn *conn)
|
||||||
{
|
{
|
||||||
HTTP_String s;
|
CHTTP_String s;
|
||||||
|
|
||||||
if (conn->closing) {
|
if (conn->closing) {
|
||||||
s = HTTP_STR("Connection: Close\r\n");
|
s = CHTTP_STR("Connection: Close\r\n");
|
||||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||||
} else {
|
} else {
|
||||||
s = HTTP_STR("Connection: Keep-Alive\r\n");
|
s = CHTTP_STR("Connection: Keep-Alive\r\n");
|
||||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
s = HTTP_STR("Content-Length: ");
|
s = CHTTP_STR("Content-Length: ");
|
||||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||||
|
|
||||||
conn->content_length_value_offset = byte_queue_offset(&conn->output);
|
conn->content_length_value_offset = byte_queue_offset(&conn->output);
|
||||||
|
|
||||||
#define TEN_SPACES " "
|
#define TEN_SPACES " "
|
||||||
_Static_assert(sizeof(TEN_SPACES) == 10+1);
|
_Static_assert(sizeof(TEN_SPACES) == 10+1, "");
|
||||||
|
|
||||||
s = HTTP_STR(TEN_SPACES "\r\n");
|
s = CHTTP_STR(TEN_SPACES "\r\n");
|
||||||
byte_queue_write(&conn->output, s.ptr, s.len);
|
byte_queue_write(&conn->output, s.ptr, s.len);
|
||||||
|
|
||||||
byte_queue_write(&conn->output, "\r\n", 2);
|
byte_queue_write(&conn->output, "\r\n", 2);
|
||||||
conn->content_length_offset = byte_queue_offset(&conn->output);
|
conn->content_length_offset = byte_queue_offset(&conn->output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void patch_special_headers(HTTP_ServerConn *conn)
|
static void patch_special_headers(CHTTP_ServerConn *conn)
|
||||||
{
|
{
|
||||||
int content_length = byte_queue_size_from_offset(&conn->output, conn->content_length_offset);
|
int content_length = byte_queue_size_from_offset(&conn->output, conn->content_length_offset);
|
||||||
|
|
||||||
@@ -480,52 +499,52 @@ static void patch_special_headers(HTTP_ServerConn *conn)
|
|||||||
byte_queue_patch(&conn->output, conn->content_length_value_offset, tmp, len);
|
byte_queue_patch(&conn->output, conn->content_length_value_offset, tmp, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_response_builder_body(HTTP_ResponseBuilder builder, HTTP_String str)
|
void chttp_response_builder_body(CHTTP_ResponseBuilder builder, CHTTP_String str)
|
||||||
{
|
{
|
||||||
HTTP_ServerConn *conn = builder_to_conn(builder);
|
CHTTP_ServerConn *conn = builder_to_conn(builder);
|
||||||
if (conn == NULL)
|
if (conn == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (conn->state == HTTP_SERVER_CONN_WAIT_HEADER) {
|
if (conn->state == CHTTP_SERVER_CONN_WAIT_HEADER) {
|
||||||
append_special_headers(conn);
|
append_special_headers(conn);
|
||||||
conn->state = HTTP_SERVER_CONN_WAIT_BODY;
|
conn->state = CHTTP_SERVER_CONN_WAIT_BODY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->state != HTTP_SERVER_CONN_WAIT_BODY)
|
if (conn->state != CHTTP_SERVER_CONN_WAIT_BODY)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
byte_queue_write(&conn->output, str.ptr, str.len);
|
byte_queue_write(&conn->output, str.ptr, str.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_response_builder_body_cap(HTTP_ResponseBuilder builder, int cap)
|
void chttp_response_builder_body_cap(CHTTP_ResponseBuilder builder, int cap)
|
||||||
{
|
{
|
||||||
HTTP_ServerConn *conn = builder_to_conn(builder);
|
CHTTP_ServerConn *conn = builder_to_conn(builder);
|
||||||
if (conn == NULL)
|
if (conn == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (conn->state != HTTP_SERVER_CONN_WAIT_HEADER) {
|
if (conn->state == CHTTP_SERVER_CONN_WAIT_HEADER) {
|
||||||
append_special_headers(conn);
|
append_special_headers(conn);
|
||||||
conn->state = HTTP_SERVER_CONN_WAIT_BODY;
|
conn->state = CHTTP_SERVER_CONN_WAIT_BODY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->state != HTTP_SERVER_CONN_WAIT_BODY)
|
if (conn->state != CHTTP_SERVER_CONN_WAIT_BODY)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
byte_queue_write_setmincap(&conn->output, cap);
|
byte_queue_write_setmincap(&conn->output, cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *http_response_builder_body_buf(HTTP_ResponseBuilder builder, int *cap)
|
char *chttp_response_builder_body_buf(CHTTP_ResponseBuilder builder, int *cap)
|
||||||
{
|
{
|
||||||
HTTP_ServerConn *conn = builder_to_conn(builder);
|
CHTTP_ServerConn *conn = builder_to_conn(builder);
|
||||||
if (conn == NULL)
|
if (conn == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (conn->state != HTTP_SERVER_CONN_WAIT_HEADER) {
|
if (conn->state == CHTTP_SERVER_CONN_WAIT_HEADER) {
|
||||||
append_special_headers(conn);
|
append_special_headers(conn);
|
||||||
conn->state = HTTP_SERVER_CONN_WAIT_BODY;
|
conn->state = CHTTP_SERVER_CONN_WAIT_BODY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->state != HTTP_SERVER_CONN_WAIT_BODY)
|
if (conn->state != CHTTP_SERVER_CONN_WAIT_BODY)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ByteView tmp = byte_queue_write_buf(&conn->output);
|
ByteView tmp = byte_queue_write_buf(&conn->output);
|
||||||
@@ -533,42 +552,45 @@ char *http_response_builder_body_buf(HTTP_ResponseBuilder builder, int *cap)
|
|||||||
return tmp.ptr;
|
return tmp.ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_response_builder_body_ack(HTTP_ResponseBuilder builder, int num)
|
void chttp_response_builder_body_ack(CHTTP_ResponseBuilder builder, int num)
|
||||||
{
|
{
|
||||||
HTTP_ServerConn *conn = builder_to_conn(builder);
|
CHTTP_ServerConn *conn = builder_to_conn(builder);
|
||||||
if (conn == NULL)
|
if (conn == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (conn->state != HTTP_SERVER_CONN_WAIT_BODY)
|
if (conn->state != CHTTP_SERVER_CONN_WAIT_BODY)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
byte_queue_write_ack(&conn->output, num);
|
byte_queue_write_ack(&conn->output, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
void http_response_builder_send(HTTP_ResponseBuilder builder)
|
void chttp_response_builder_send(CHTTP_ResponseBuilder builder)
|
||||||
{
|
{
|
||||||
HTTP_ServerConn *conn = builder_to_conn(builder);
|
CHTTP_ServerConn *conn = builder_to_conn(builder);
|
||||||
if (conn == NULL)
|
if (conn == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (conn->state == HTTP_SERVER_CONN_WAIT_STATUS) {
|
if (conn->state == CHTTP_SERVER_CONN_WAIT_STATUS) {
|
||||||
write_status(conn, 500);
|
write_status(conn, 500);
|
||||||
conn->state = HTTP_SERVER_CONN_WAIT_HEADER;
|
conn->state = CHTTP_SERVER_CONN_WAIT_HEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->state == HTTP_SERVER_CONN_WAIT_HEADER) {
|
if (conn->state == CHTTP_SERVER_CONN_WAIT_HEADER) {
|
||||||
append_special_headers(conn);
|
append_special_headers(conn);
|
||||||
conn->state = HTTP_SERVER_CONN_WAIT_BODY;
|
conn->state = CHTTP_SERVER_CONN_WAIT_BODY;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(conn->state == HTTP_SERVER_CONN_WAIT_BODY);
|
assert(conn->state == CHTTP_SERVER_CONN_WAIT_BODY);
|
||||||
patch_special_headers(conn);
|
patch_special_headers(conn);
|
||||||
|
|
||||||
// Remove the buffered request
|
// Remove the buffered request
|
||||||
byte_queue_read_ack(&conn->input, conn->request_len);
|
byte_queue_read_ack(&conn->input, conn->request_len);
|
||||||
|
|
||||||
conn->state = HTTP_SERVER_CONN_FLUSHING;
|
conn->state = CHTTP_SERVER_CONN_FLUSHING;
|
||||||
conn->gen++;
|
conn->gen++;
|
||||||
|
|
||||||
http_server_conn_process_events(builder.server, conn);
|
// Enable back I/O events
|
||||||
|
socket_silent(&builder.server->sockets, conn->handle, false);
|
||||||
|
|
||||||
|
chttp_server_conn_process_events(builder.server, conn);
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-48
@@ -1,49 +1,49 @@
|
|||||||
|
|
||||||
#ifndef HTTP_SERVER_CAPACITY
|
#ifndef CHTTP_SERVER_CAPACITY
|
||||||
// The maximum ammount of requests that can be handled
|
// The maximum ammount of requests that can be handled
|
||||||
// in parallel.
|
// in parallel.
|
||||||
#define HTTP_SERVER_CAPACITY (1<<9)
|
#define CHTTP_SERVER_CAPACITY (1<<9)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Maximum number of descriptors the server will want
|
// Maximum number of descriptors the server will want
|
||||||
// to wait on. It's one per connection plus two for the
|
// to wait on. It's one per connection plus two for the
|
||||||
// TCP and TLS listener, plus one for the wakeup self-pipe.
|
// TCP and TLS listener, plus one for the wakeup self-pipe.
|
||||||
#define HTTP_SERVER_POLL_CAPACITY (HTTP_SERVER_CAPACITY+3)
|
#define CHTTP_SERVER_POLL_CAPACITY (CHTTP_SERVER_CAPACITY+3)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
||||||
// This struct is unused
|
// This struct is unused
|
||||||
HTTP_SERVER_CONN_FREE,
|
CHTTP_SERVER_CONN_FREE,
|
||||||
|
|
||||||
// No request was buffered yet.
|
// No request was buffered yet.
|
||||||
HTTP_SERVER_CONN_BUFFERING,
|
CHTTP_SERVER_CONN_BUFFERING,
|
||||||
|
|
||||||
// A request was just buffered and is waiting for
|
// A request was just buffered and is waiting for
|
||||||
// the user to build a response. To be specific,
|
// the user to build a response. To be specific,
|
||||||
// it's waiting for the user to set a response status.
|
// it's waiting for the user to set a response status.
|
||||||
HTTP_SERVER_CONN_WAIT_STATUS,
|
CHTTP_SERVER_CONN_WAIT_STATUS,
|
||||||
|
|
||||||
// A request is buffered and a status was set. Now
|
// A request is buffered and a status was set. Now
|
||||||
// the user can set a header or append the first
|
// the user can set a header or append the first
|
||||||
// bytes of the response body.
|
// bytes of the response body.
|
||||||
HTTP_SERVER_CONN_WAIT_HEADER,
|
CHTTP_SERVER_CONN_WAIT_HEADER,
|
||||||
|
|
||||||
// A request is buffered and some bytes were appended
|
// A request is buffered and some bytes were appended
|
||||||
// to the response. Now the user can either append more
|
// to the response. Now the user can either append more
|
||||||
// bytes or send out the response.
|
// bytes or send out the response.
|
||||||
HTTP_SERVER_CONN_WAIT_BODY,
|
CHTTP_SERVER_CONN_WAIT_BODY,
|
||||||
|
|
||||||
// A response has been produced and it's being flushed.
|
// A response has been produced and it's being flushed.
|
||||||
HTTP_SERVER_CONN_FLUSHING,
|
CHTTP_SERVER_CONN_FLUSHING,
|
||||||
|
|
||||||
} HTTP_ServerConnState;
|
} CHTTP_ServerConnState;
|
||||||
|
|
||||||
// This structure represents the HTTP connection to
|
// This structure represents the HTTP connection to
|
||||||
// a client.
|
// a client.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
// If false, this struct is unused
|
// If false, this struct is unused
|
||||||
HTTP_ServerConnState state;
|
CHTTP_ServerConnState state;
|
||||||
|
|
||||||
// Handle to the socket
|
// Handle to the socket
|
||||||
SocketHandle handle;
|
SocketHandle handle;
|
||||||
@@ -68,7 +68,7 @@ typedef struct {
|
|||||||
// When the state is WAIT_STATUS, WAIT_HEADER,
|
// When the state is WAIT_STATUS, WAIT_HEADER,
|
||||||
// or WAIT_BODY, this contains the parsed version
|
// or WAIT_BODY, this contains the parsed version
|
||||||
// of the buffered request.
|
// of the buffered request.
|
||||||
HTTP_Request request;
|
CHTTP_Request request;
|
||||||
|
|
||||||
// Length of the buffered request when the request
|
// Length of the buffered request when the request
|
||||||
// field is valid.
|
// field is valid.
|
||||||
@@ -93,7 +93,7 @@ typedef struct {
|
|||||||
// it from the offset reached when the response is marked
|
// it from the offset reached when the response is marked
|
||||||
// as done.
|
// as done.
|
||||||
ByteQueueOffset content_length_offset;
|
ByteQueueOffset content_length_offset;
|
||||||
} HTTP_ServerConn;
|
} CHTTP_ServerConn;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
@@ -109,13 +109,13 @@ typedef struct {
|
|||||||
// Array of connections. The counter contains the
|
// Array of connections. The counter contains the
|
||||||
// number of structs such that state=FREE.
|
// number of structs such that state=FREE.
|
||||||
int num_conns;
|
int num_conns;
|
||||||
HTTP_ServerConn conns[HTTP_SERVER_CAPACITY];
|
CHTTP_ServerConn conns[CHTTP_SERVER_CAPACITY];
|
||||||
|
|
||||||
// Queue of indices referring to connections that
|
// Queue of indices referring to connections that
|
||||||
// are in the WAIT_STATUS state.
|
// are in the WAIT_STATUS state.
|
||||||
int num_ready;
|
int num_ready;
|
||||||
int ready_head;
|
int ready_head;
|
||||||
int ready[HTTP_SERVER_CAPACITY];
|
int ready[CHTTP_SERVER_CAPACITY];
|
||||||
|
|
||||||
// Asynchronous TCP and TLS socket abstraction
|
// Asynchronous TCP and TLS socket abstraction
|
||||||
SocketManager sockets;
|
SocketManager sockets;
|
||||||
@@ -125,74 +125,74 @@ typedef struct {
|
|||||||
// manager with a pointer to it. This allows
|
// manager with a pointer to it. This allows
|
||||||
// allocating the exact number of sockets we
|
// allocating the exact number of sockets we
|
||||||
// will need.
|
// will need.
|
||||||
Socket socket_pool[HTTP_SERVER_CAPACITY];
|
Socket socket_pool[CHTTP_SERVER_CAPACITY];
|
||||||
|
|
||||||
} HTTP_Server;
|
} CHTTP_Server;
|
||||||
|
|
||||||
// Initialize the HTTP server object. By default, it won't
|
// Initialize the HTTP server object. By default, it won't
|
||||||
// listen for connections. You need to call
|
// listen for connections. You need to call
|
||||||
//
|
//
|
||||||
// http_server_listen_tcp
|
// chttp_server_listen_tcp
|
||||||
// http_server_listen_tls
|
// chttp_server_listen_tls
|
||||||
//
|
//
|
||||||
// to listen for connection. Note that you can have a
|
// to listen for connection. Note that you can have a
|
||||||
// single server listening for HTTP and HTTPS requests
|
// single server listening for HTTP and HTTPS requests
|
||||||
// by calling both.
|
// by calling both.
|
||||||
int http_server_init(HTTP_Server *server);
|
int chttp_server_init(CHTTP_Server *server);
|
||||||
|
|
||||||
// Release resources associated to the server.
|
// Release resources associated to the server.
|
||||||
void http_server_free(HTTP_Server *server);
|
void chttp_server_free(CHTTP_Server *server);
|
||||||
|
|
||||||
// Set input and output buffer size limit for any
|
// Set input and output buffer size limit for any
|
||||||
// given connection. The default value is 1MB
|
// given connection. The default value is 1MB
|
||||||
void http_server_set_input_limit(HTTP_Server *server, uint32_t limit);
|
void chttp_server_set_input_limit(CHTTP_Server *server, uint32_t limit);
|
||||||
void http_server_set_output_limit(HTTP_Server *server, uint32_t limit);
|
void chttp_server_set_output_limit(CHTTP_Server *server, uint32_t limit);
|
||||||
|
|
||||||
// TODO: Comment
|
// TODO: Comment
|
||||||
void http_server_set_trace_bytes(HTTP_Server *server, bool value);
|
void chttp_server_set_trace_bytes(CHTTP_Server *server, bool value);
|
||||||
|
|
||||||
// TODO: Comment
|
// TODO: Comment
|
||||||
void http_server_set_reuse_addr(HTTP_Server *server, bool reuse);
|
void chttp_server_set_reuse_addr(CHTTP_Server *server, bool reuse);
|
||||||
|
|
||||||
// TODO: comment
|
// TODO: comment
|
||||||
void http_server_set_backlog(HTTP_Server *server, int backlog);
|
void chttp_server_set_backlog(CHTTP_Server *server, int backlog);
|
||||||
|
|
||||||
// Enable listening for plain HTTP requests at the
|
// Enable listening for plain HTTP requests at the
|
||||||
// specified interface.
|
// specified interface.
|
||||||
int http_server_listen_tcp(HTTP_Server *server,
|
int chttp_server_listen_tcp(CHTTP_Server *server,
|
||||||
HTTP_String addr, Port port);
|
CHTTP_String addr, Port port);
|
||||||
|
|
||||||
// Enable listening for HTTPS requests at the specified
|
// Enable listening for HTTPS requests at the specified
|
||||||
// interfact, using the specified certificate and key
|
// interfact, using the specified certificate and key
|
||||||
// to verify the connection.
|
// to verify the connection.
|
||||||
int http_server_listen_tls(HTTP_Server *server, HTTP_String addr, Port port,
|
int chttp_server_listen_tls(CHTTP_Server *server, CHTTP_String addr, Port port,
|
||||||
HTTP_String cert_file_name, HTTP_String key_file_name);
|
CHTTP_String cert_file_name, CHTTP_String key_file_name);
|
||||||
|
|
||||||
// Add the certificate for an additional domain when
|
// Add the certificate for an additional domain when
|
||||||
// the server is listening for HTTPS requests.
|
// the server is listening for HTTPS requests.
|
||||||
int http_server_add_certificate(HTTP_Server *server,
|
int chttp_server_add_certificate(CHTTP_Server *server,
|
||||||
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
|
CHTTP_String domain, CHTTP_String cert_file, CHTTP_String key_file);
|
||||||
|
|
||||||
// When a thread is blocked waiting for server events,
|
// When a thread is blocked waiting for server events,
|
||||||
// other threads can call this function to wake it up.
|
// other threads can call this function to wake it up.
|
||||||
int http_server_wakeup(HTTP_Server *server);
|
int chttp_server_wakeup(CHTTP_Server *server);
|
||||||
|
|
||||||
// Resets the event register with the list of descriptors
|
// Resets the event register with the list of descriptors
|
||||||
// the server wants monitored.
|
// the server wants monitored.
|
||||||
void http_server_register_events(HTTP_Server *server,
|
void chttp_server_register_events(CHTTP_Server *server,
|
||||||
EventRegister *reg);
|
EventRegister *reg);
|
||||||
|
|
||||||
// The caller has waited for poll() to return and some
|
// The caller has waited for poll() to return and some
|
||||||
// I/O events to be triggered, so now the HTTP server
|
// I/O events to be triggered, so now the HTTP server
|
||||||
// can continue its buffering and flushing operations.
|
// can continue its buffering and flushing operations.
|
||||||
void http_server_process_events(HTTP_Server *server,
|
void chttp_server_process_events(CHTTP_Server *server,
|
||||||
EventRegister reg);
|
EventRegister reg);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HTTP_Server *server;
|
CHTTP_Server *server;
|
||||||
uint16_t index;
|
uint16_t index;
|
||||||
uint16_t gen;
|
uint16_t gen;
|
||||||
} HTTP_ResponseBuilder;
|
} CHTTP_ResponseBuilder;
|
||||||
|
|
||||||
// After some I/O events were processes, some requests
|
// After some I/O events were processes, some requests
|
||||||
// may be availabe. This function returns one of the
|
// may be availabe. This function returns one of the
|
||||||
@@ -202,32 +202,32 @@ typedef struct {
|
|||||||
// respond in batches.
|
// respond in batches.
|
||||||
// For each request returned by this function, the user
|
// For each request returned by this function, the user
|
||||||
// must build a response using the response builder API.
|
// must build a response using the response builder API.
|
||||||
bool http_server_next_request(HTTP_Server *server,
|
bool chttp_server_next_request(CHTTP_Server *server,
|
||||||
HTTP_Request **request, HTTP_ResponseBuilder *builder);
|
CHTTP_Request **request, CHTTP_ResponseBuilder *builder);
|
||||||
|
|
||||||
// TODO: comment
|
// TODO: comment
|
||||||
void http_server_wait_request(HTTP_Server *server,
|
void chttp_server_wait_request(CHTTP_Server *server,
|
||||||
HTTP_Request **request, HTTP_ResponseBuilder *builder);
|
CHTTP_Request **request, CHTTP_ResponseBuilder *builder);
|
||||||
|
|
||||||
// This function is called to set the status code of
|
// This function is called to set the status code of
|
||||||
// a request's response. If this function is called
|
// a request's response. If this function is called
|
||||||
// after the other response builder functions, it will
|
// after the other response builder functions, it will
|
||||||
// reset the response and set a new status.
|
// reset the response and set a new status.
|
||||||
void http_response_builder_status(HTTP_ResponseBuilder builder, int status);
|
void chttp_response_builder_status(CHTTP_ResponseBuilder builder, int status);
|
||||||
|
|
||||||
// Append a header to the response. This can only be
|
// Append a header to the response. This can only be
|
||||||
// used after having set the status and before appending
|
// used after having set the status and before appending
|
||||||
// to the body.
|
// to the body.
|
||||||
void http_response_builder_header(HTTP_ResponseBuilder builder, HTTP_String str);
|
void chttp_response_builder_header(CHTTP_ResponseBuilder builder, CHTTP_String str);
|
||||||
|
|
||||||
// Append some bytes to the response's body
|
// Append some bytes to the response's body
|
||||||
void http_response_builder_body(HTTP_ResponseBuilder builder, HTTP_String str);
|
void chttp_response_builder_body(CHTTP_ResponseBuilder builder, CHTTP_String str);
|
||||||
|
|
||||||
// TODO: comment
|
// TODO: comment
|
||||||
void http_response_builder_body_cap(HTTP_ResponseBuilder builder, int cap);
|
void chttp_response_builder_body_cap(CHTTP_ResponseBuilder builder, int cap);
|
||||||
char *http_response_builder_body_buf(HTTP_ResponseBuilder builder, int *cap);
|
char *chttp_response_builder_body_buf(CHTTP_ResponseBuilder builder, int *cap);
|
||||||
void http_response_builder_body_ack(HTTP_ResponseBuilder builder, int num);
|
void chttp_response_builder_body_ack(CHTTP_ResponseBuilder builder, int num);
|
||||||
|
|
||||||
// Mark the response as complete. This will invalidate
|
// Mark the response as complete. This will invalidate
|
||||||
// the response builder handle.
|
// the response builder handle.
|
||||||
void http_response_builder_send(HTTP_ResponseBuilder builder);
|
void chttp_response_builder_send(CHTTP_ResponseBuilder builder);
|
||||||
|
|||||||
+187
-64
@@ -39,7 +39,7 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
|
|||||||
WSADATA wsaData;
|
WSADATA wsaData;
|
||||||
WORD wVersionRequested = MAKEWORD(2, 2);
|
WORD wVersionRequested = MAKEWORD(2, 2);
|
||||||
if (WSAStartup(wVersionRequested, &wsaData))
|
if (WSAStartup(wVersionRequested, &wsaData))
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
|
|
||||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
if (sock == INVALID_SOCKET && *global_cleanup)
|
if (sock == INVALID_SOCKET && *global_cleanup)
|
||||||
@@ -49,7 +49,7 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
|
|||||||
if (sock == INVALID_SOCKET) {
|
if (sock == INVALID_SOCKET) {
|
||||||
if (*global_cleanup)
|
if (*global_cleanup)
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind to loopback address with port 0 (dynamic port assignment)
|
// Bind to loopback address with port 0 (dynamic port assignment)
|
||||||
@@ -64,21 +64,21 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
|
|||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
if (*global_cleanup)
|
if (*global_cleanup)
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) {
|
if (getsockname(sock, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) {
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
if (*global_cleanup)
|
if (*global_cleanup)
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
if (*global_cleanup)
|
if (*global_cleanup)
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional: Set socket to non-blocking mode
|
// Optional: Set socket to non-blocking mode
|
||||||
@@ -88,20 +88,20 @@ static int create_socket_pair(NATIVE_SOCKET *a, NATIVE_SOCKET *b, bool *global_c
|
|||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
if (*global_cleanup)
|
if (*global_cleanup)
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
*a = sock;
|
*a = sock;
|
||||||
*b = sock;
|
*b = sock;
|
||||||
return HTTP_OK;
|
return CHTTP_OK;
|
||||||
#else
|
#else
|
||||||
*global_cleanup = false;
|
*global_cleanup = false;
|
||||||
int fds[2];
|
int fds[2];
|
||||||
if (pipe(fds) < 0)
|
if (pipe(fds) < 0)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
*a = fds[0];
|
*a = fds[0];
|
||||||
*b = fds[1];
|
*b = fds[1];
|
||||||
return HTTP_OK;
|
return CHTTP_OK;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,23 +110,23 @@ static int set_socket_blocking(NATIVE_SOCKET sock, bool value)
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
u_long mode = !value;
|
u_long mode = !value;
|
||||||
if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR)
|
if (ioctlsocket(sock, FIONBIO, &mode) == SOCKET_ERROR)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
return HTTP_OK;
|
return CHTTP_OK;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
int flags = fcntl(sock, F_GETFL, 0);
|
int flags = fcntl(sock, F_GETFL, 0);
|
||||||
if (flags < 0)
|
if (flags < 0)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
if (value) flags &= ~O_NONBLOCK;
|
if (value) flags &= ~O_NONBLOCK;
|
||||||
else flags |= O_NONBLOCK;
|
else flags |= O_NONBLOCK;
|
||||||
if (fcntl(sock, F_SETFL, flags) < 0)
|
if (fcntl(sock, F_SETFL, flags) < 0)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
return HTTP_OK;
|
return CHTTP_OK;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static NATIVE_SOCKET create_listen_socket(HTTP_String addr,
|
static NATIVE_SOCKET create_listen_socket(CHTTP_String addr,
|
||||||
Port port, bool reuse_addr, int backlog)
|
Port port, bool reuse_addr, int backlog)
|
||||||
{
|
{
|
||||||
NATIVE_SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
|
NATIVE_SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
@@ -193,6 +193,9 @@ static void close_socket_pair(NATIVE_SOCKET a, NATIVE_SOCKET b)
|
|||||||
int socket_manager_init(SocketManager *sm, Socket *socks,
|
int socket_manager_init(SocketManager *sm, Socket *socks,
|
||||||
int num_socks)
|
int num_socks)
|
||||||
{
|
{
|
||||||
|
sm->creation_timeout = 60000;
|
||||||
|
sm->recv_timeout = 3000;
|
||||||
|
|
||||||
sm->plain_sock = NATIVE_SOCKET_INVALID;
|
sm->plain_sock = NATIVE_SOCKET_INVALID;
|
||||||
sm->secure_sock = NATIVE_SOCKET_INVALID;
|
sm->secure_sock = NATIVE_SOCKET_INVALID;
|
||||||
|
|
||||||
@@ -212,7 +215,7 @@ int socket_manager_init(SocketManager *sm, Socket *socks,
|
|||||||
socks[i].state = SOCKET_STATE_FREE;
|
socks[i].state = SOCKET_STATE_FREE;
|
||||||
socks[i].gen = 1;
|
socks[i].gen = 1;
|
||||||
}
|
}
|
||||||
return HTTP_OK;
|
return CHTTP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void socket_manager_free(SocketManager *sm)
|
void socket_manager_free(SocketManager *sm)
|
||||||
@@ -237,58 +240,68 @@ void socket_manager_free(SocketManager *sm)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void socket_manager_set_creation_timeout(SocketManager *sm, int timeout)
|
||||||
|
{
|
||||||
|
sm->creation_timeout = (timeout < 0) ? INVALID_TIME : (Time) timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket_manager_set_recv_timeout(SocketManager *sm, int timeout)
|
||||||
|
{
|
||||||
|
sm->recv_timeout = (timeout < 0) ? INVALID_TIME : (Time) timeout;
|
||||||
|
}
|
||||||
|
|
||||||
int socket_manager_listen_tcp(SocketManager *sm,
|
int socket_manager_listen_tcp(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, int backlog,
|
CHTTP_String addr, Port port, int backlog,
|
||||||
bool reuse_addr)
|
bool reuse_addr)
|
||||||
{
|
{
|
||||||
if (sm->plain_sock != NATIVE_SOCKET_INVALID)
|
if (sm->plain_sock != NATIVE_SOCKET_INVALID)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
|
|
||||||
sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
sm->plain_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
||||||
if (sm->plain_sock == NATIVE_SOCKET_INVALID)
|
if (sm->plain_sock == NATIVE_SOCKET_INVALID)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
|
|
||||||
return HTTP_OK;
|
return CHTTP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int socket_manager_listen_tls(SocketManager *sm,
|
int socket_manager_listen_tls(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, int backlog,
|
CHTTP_String addr, Port port, int backlog,
|
||||||
bool reuse_addr, HTTP_String cert_file,
|
bool reuse_addr, CHTTP_String cert_file,
|
||||||
HTTP_String key_file)
|
CHTTP_String key_file)
|
||||||
{
|
{
|
||||||
#ifndef HTTPS_ENABLED
|
#ifndef HTTPS_ENABLED
|
||||||
return HTTP_ERROR_NOTLS;
|
return CHTTP_ERROR_NOTLS;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock != NATIVE_SOCKET_INVALID)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
|
|
||||||
sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
sm->secure_sock = create_listen_socket(addr, port, reuse_addr, backlog);
|
||||||
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
|
|
||||||
if (server_secure_context_init(&sm->server_secure_context,
|
if (server_secure_context_init(&sm->server_secure_context,
|
||||||
cert_file, key_file) < 0) {
|
cert_file, key_file) < 0) {
|
||||||
CLOSE_NATIVE_SOCKET(sm->secure_sock);
|
CLOSE_NATIVE_SOCKET(sm->secure_sock);
|
||||||
sm->secure_sock = NATIVE_SOCKET_INVALID;
|
sm->secure_sock = NATIVE_SOCKET_INVALID;
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return HTTP_OK;
|
return CHTTP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int socket_manager_add_certificate(SocketManager *sm,
|
int socket_manager_add_certificate(SocketManager *sm,
|
||||||
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file)
|
CHTTP_String domain, CHTTP_String cert_file, CHTTP_String key_file)
|
||||||
{
|
{
|
||||||
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
if (sm->secure_sock == NATIVE_SOCKET_INVALID)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
|
|
||||||
int ret = server_secure_context_add_certificate(
|
int ret = server_secure_context_add_certificate(
|
||||||
&sm->server_secure_context, domain, cert_file, key_file);
|
&sm->server_secure_context, domain, cert_file, key_file);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return HTTP_OK;
|
return CHTTP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_secure(Socket *s)
|
static bool is_secure(Socket *s)
|
||||||
@@ -297,6 +310,7 @@ static bool is_secure(Socket *s)
|
|||||||
return s->server_secure_context != NULL
|
return s->server_secure_context != NULL
|
||||||
|| s->client_secure_context != NULL;
|
|| s->client_secure_context != NULL;
|
||||||
#else
|
#else
|
||||||
|
(void) s;
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -427,13 +441,13 @@ static void socket_update(Socket *s)
|
|||||||
struct sockaddr_in buf;
|
struct sockaddr_in buf;
|
||||||
buf.sin_family = AF_INET;
|
buf.sin_family = AF_INET;
|
||||||
buf.sin_port = htons(addr.port);
|
buf.sin_port = htons(addr.port);
|
||||||
memcpy(&buf.sin_addr, &addr.ipv4, sizeof(HTTP_IPv4));
|
memcpy(&buf.sin_addr, &addr.ipv4, sizeof(CHTTP_IPv4));
|
||||||
ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf));
|
ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf));
|
||||||
} else {
|
} else {
|
||||||
struct sockaddr_in6 buf;
|
struct sockaddr_in6 buf;
|
||||||
buf.sin6_family = AF_INET6;
|
buf.sin6_family = AF_INET6;
|
||||||
buf.sin6_port = htons(addr.port);
|
buf.sin6_port = htons(addr.port);
|
||||||
memcpy(&buf.sin6_addr, &addr.ipv6, sizeof(HTTP_IPv6));
|
memcpy(&buf.sin6_addr, &addr.ipv6, sizeof(CHTTP_IPv6));
|
||||||
ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf));
|
ret = connect(sock, (struct sockaddr*) &buf, sizeof(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,6 +538,9 @@ static void socket_update(Socket *s)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SSL_set_verify(s->ssl, s->dont_verify_cert
|
||||||
|
? SSL_VERIFY_NONE : SSL_VERIFY_PEER, NULL);
|
||||||
|
|
||||||
AddressAndPort addr;
|
AddressAndPort addr;
|
||||||
if (s->num_addr > 1)
|
if (s->num_addr > 1)
|
||||||
addr = s->addrs[s->next_addr];
|
addr = s->addrs[s->next_addr];
|
||||||
@@ -687,15 +704,14 @@ int socket_manager_wakeup(SocketManager *sm)
|
|||||||
// NOTE: It's assumed send/write operate atomically
|
// NOTE: It's assumed send/write operate atomically
|
||||||
// on The descriptor.
|
// on The descriptor.
|
||||||
char byte = 1;
|
char byte = 1;
|
||||||
int ret = 0;
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (send(sm->signal_sock, &byte, 1, 0) < 0)
|
if (send(sm->signal_sock, &byte, 1, 0) < 0)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
#else
|
#else
|
||||||
if (write(sm->signal_sock, &byte, 1) < 0)
|
if (write(sm->signal_sock, &byte, 1) < 0)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
#endif
|
#endif
|
||||||
return HTTP_OK;
|
return CHTTP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void socket_manager_register_events(
|
void socket_manager_register_events(
|
||||||
@@ -735,17 +751,34 @@ void socket_manager_register_events(
|
|||||||
// is ready to be processed exists, return an empty
|
// is ready to be processed exists, return an empty
|
||||||
// event registration list so that those entries can
|
// event registration list so that those entries can
|
||||||
// be processed immediately.
|
// be processed immediately.
|
||||||
|
// TODO: comment about deadline
|
||||||
|
Time deadline = INVALID_TIME;
|
||||||
for (int i = 0, j = 0; j < sm->num_used; i++) {
|
for (int i = 0, j = 0; j < sm->num_used; i++) {
|
||||||
Socket *s = &sm->sockets[i];
|
Socket *s = &sm->sockets[i];
|
||||||
if (s->state == SOCKET_STATE_FREE)
|
if (s->state == SOCKET_STATE_FREE)
|
||||||
continue;
|
continue;
|
||||||
j++;
|
j++;
|
||||||
|
|
||||||
|
if (s->silent)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (s->creation_timeout != INVALID_TIME) {
|
||||||
|
Time creation_deadline = s->creation_time + s->creation_timeout;
|
||||||
|
if (deadline == INVALID_TIME || creation_deadline < deadline)
|
||||||
|
deadline = creation_deadline;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->recv_timeout != INVALID_TIME) {
|
||||||
|
Time recv_deadline = s->last_recv_time + s->recv_timeout;
|
||||||
|
if (deadline == INVALID_TIME || recv_deadline < deadline)
|
||||||
|
deadline = recv_deadline;
|
||||||
|
}
|
||||||
|
|
||||||
// If at least one socket can be processed, return an
|
// If at least one socket can be processed, return an
|
||||||
// empty list.
|
// empty list.
|
||||||
if (s->state == SOCKET_STATE_DIED || s->state == SOCKET_STATE_ESTABLISHED_READY) {
|
if (s->state == SOCKET_STATE_DIED ||
|
||||||
reg->num_polled = 0;
|
s->state == SOCKET_STATE_ESTABLISHED_READY) {
|
||||||
return;
|
deadline = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->events) {
|
if (s->events) {
|
||||||
@@ -756,6 +789,20 @@ void socket_manager_register_events(
|
|||||||
reg->num_polled++;
|
reg->num_polled++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (deadline == INVALID_TIME) {
|
||||||
|
reg->timeout = -1;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Time current_time = get_current_time();
|
||||||
|
if (current_time == INVALID_TIME) {
|
||||||
|
reg->timeout = 1000;
|
||||||
|
} else if (deadline < current_time) {
|
||||||
|
reg->timeout = 0;
|
||||||
|
} else {
|
||||||
|
reg->timeout = deadline - current_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static SocketHandle
|
static SocketHandle
|
||||||
@@ -779,6 +826,8 @@ int socket_manager_translate_events(
|
|||||||
SocketManager *sm, SocketEvent *events,
|
SocketManager *sm, SocketEvent *events,
|
||||||
EventRegister reg)
|
EventRegister reg)
|
||||||
{
|
{
|
||||||
|
Time current_time = get_current_time();
|
||||||
|
|
||||||
int num_events = 0;
|
int num_events = 0;
|
||||||
for (int i = 0; i < reg.num_polled; i++) {
|
for (int i = 0; i < reg.num_polled; i++) {
|
||||||
|
|
||||||
@@ -800,10 +849,6 @@ int socket_manager_translate_events(
|
|||||||
if (sm->num_used == sm->max_used)
|
if (sm->num_used == sm->max_used)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Determine whether the event came from
|
|
||||||
// the encrypted listener or not.
|
|
||||||
bool secure = (reg.polled[i].fd == sm->secure_sock);
|
|
||||||
|
|
||||||
Socket *s = sm->sockets;
|
Socket *s = sm->sockets;
|
||||||
while (s->state != SOCKET_STATE_FREE) {
|
while (s->state != SOCKET_STATE_FREE) {
|
||||||
s++;
|
s++;
|
||||||
@@ -823,7 +868,16 @@ int socket_manager_translate_events(
|
|||||||
s->sock = sock;
|
s->sock = sock;
|
||||||
s->events = 0;
|
s->events = 0;
|
||||||
s->user = NULL;
|
s->user = NULL;
|
||||||
|
s->silent = false;
|
||||||
|
s->creation_time = current_time;
|
||||||
|
s->last_recv_time = current_time;
|
||||||
|
s->creation_timeout = sm->creation_timeout;
|
||||||
|
s->recv_timeout = sm->recv_timeout;
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
|
// Determine whether the event came from
|
||||||
|
// the encrypted listener or not.
|
||||||
|
bool secure = (reg.polled[i].fd == sm->secure_sock);
|
||||||
|
|
||||||
s->ssl = NULL;
|
s->ssl = NULL;
|
||||||
s->server_secure_context = NULL;
|
s->server_secure_context = NULL;
|
||||||
s->client_secure_context = NULL;
|
s->client_secure_context = NULL;
|
||||||
@@ -854,7 +908,10 @@ int socket_manager_translate_events(
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
Socket *s = reg.ptrs[i];
|
Socket *s = reg.ptrs[i];
|
||||||
|
assert(!s->silent);
|
||||||
|
|
||||||
socket_update(s);
|
socket_update(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -865,7 +922,34 @@ int socket_manager_translate_events(
|
|||||||
continue;
|
continue;
|
||||||
j++;
|
j++;
|
||||||
|
|
||||||
if (s->state == SOCKET_STATE_DIED) {
|
if (s->silent)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (s->creation_timeout != INVALID_TIME
|
||||||
|
&& current_time != INVALID_TIME
|
||||||
|
&& current_time > s->creation_time + s->creation_timeout) {
|
||||||
|
|
||||||
|
s->creation_time = INVALID_TIME;
|
||||||
|
|
||||||
|
events[num_events++] = (SocketEvent) {
|
||||||
|
SOCKET_EVENT_CREATION_TIMEOUT,
|
||||||
|
socket_to_handle(sm, s),
|
||||||
|
s->user
|
||||||
|
};
|
||||||
|
|
||||||
|
} else if (s->recv_timeout != INVALID_TIME
|
||||||
|
&& current_time != INVALID_TIME
|
||||||
|
&& current_time > s->last_recv_time + s->recv_timeout) {
|
||||||
|
|
||||||
|
s->recv_timeout = INVALID_TIME;
|
||||||
|
|
||||||
|
events[num_events++] = (SocketEvent) {
|
||||||
|
SOCKET_EVENT_RECV_TIMEOUT,
|
||||||
|
socket_to_handle(sm, s),
|
||||||
|
s->user
|
||||||
|
};
|
||||||
|
|
||||||
|
} else if (s->state == SOCKET_STATE_DIED) {
|
||||||
|
|
||||||
events[num_events++] = (SocketEvent) {
|
events[num_events++] = (SocketEvent) {
|
||||||
SOCKET_EVENT_DISCONNECT,
|
SOCKET_EVENT_DISCONNECT,
|
||||||
@@ -882,9 +966,14 @@ int socket_manager_translate_events(
|
|||||||
if (s->num_addr > 1)
|
if (s->num_addr > 1)
|
||||||
free(s->addrs);
|
free(s->addrs);
|
||||||
}
|
}
|
||||||
|
#ifdef HTTPS_ENABLED
|
||||||
|
if (s->ssl)
|
||||||
|
SSL_free(s->ssl);
|
||||||
|
#endif // HTTPS_ENABLED
|
||||||
sm->num_used--;
|
sm->num_used--;
|
||||||
|
|
||||||
} else if (s->state == SOCKET_STATE_ESTABLISHED_READY) {
|
} else if (s->state == SOCKET_STATE_ESTABLISHED_READY) {
|
||||||
|
|
||||||
events[num_events++] = (SocketEvent) {
|
events[num_events++] = (SocketEvent) {
|
||||||
SOCKET_EVENT_READY,
|
SOCKET_EVENT_READY,
|
||||||
socket_to_handle(sm, s),
|
socket_to_handle(sm, s),
|
||||||
@@ -916,7 +1005,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1);
|
RegisteredName *name = malloc(sizeof(RegisteredName) + targets[i].name.len + 1);
|
||||||
if (name == NULL) {
|
if (name == NULL) {
|
||||||
free_addr_list(resolved, num_resolved);
|
free_addr_list(resolved, num_resolved);
|
||||||
return HTTP_ERROR_OOM;
|
return CHTTP_ERROR_OOM;
|
||||||
}
|
}
|
||||||
name->refs = 0;
|
name->refs = 0;
|
||||||
memcpy(name->data, targets[i].name.ptr, targets[i].name.len);
|
memcpy(name->data, targets[i].name.ptr, targets[i].name.len);
|
||||||
@@ -926,7 +1015,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
// 512 bytes is more than enough for a DNS hostname (max 253 chars)
|
// 512 bytes is more than enough for a DNS hostname (max 253 chars)
|
||||||
char hostname[1<<9];
|
char hostname[1<<9];
|
||||||
if (targets[i].name.len >= (int) sizeof(hostname))
|
if (targets[i].name.len >= (int) sizeof(hostname))
|
||||||
return HTTP_ERROR_OOM;
|
return CHTTP_ERROR_OOM;
|
||||||
memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
|
memcpy(hostname, targets[i].name.ptr, targets[i].name.len);
|
||||||
hostname[targets[i].name.len] = '\0';
|
hostname[targets[i].name.len] = '\0';
|
||||||
#endif
|
#endif
|
||||||
@@ -938,12 +1027,12 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
free(name);
|
free(name);
|
||||||
#endif
|
#endif
|
||||||
free_addr_list(resolved, num_resolved);
|
free_addr_list(resolved, num_resolved);
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
|
for (struct addrinfo *rp = res; rp; rp = rp->ai_next) {
|
||||||
if (rp->ai_family == AF_INET) {
|
if (rp->ai_family == AF_INET) {
|
||||||
HTTP_IPv4 ipv4 = *(HTTP_IPv4*) &((struct sockaddr_in*)rp->ai_addr)->sin_addr;
|
CHTTP_IPv4 ipv4 = *(CHTTP_IPv4*) &((struct sockaddr_in*)rp->ai_addr)->sin_addr;
|
||||||
if (num_resolved < max_resolved) {
|
if (num_resolved < max_resolved) {
|
||||||
resolved[num_resolved].is_ipv4 = true;
|
resolved[num_resolved].is_ipv4 = true;
|
||||||
resolved[num_resolved].ipv4 = ipv4;
|
resolved[num_resolved].ipv4 = ipv4;
|
||||||
@@ -955,7 +1044,7 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
num_resolved++;
|
num_resolved++;
|
||||||
}
|
}
|
||||||
} else if (rp->ai_family == AF_INET6) {
|
} else if (rp->ai_family == AF_INET6) {
|
||||||
HTTP_IPv6 ipv6 = *(HTTP_IPv6*) &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr;
|
CHTTP_IPv6 ipv6 = *(CHTTP_IPv6*) &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr;
|
||||||
if (num_resolved < max_resolved) {
|
if (num_resolved < max_resolved) {
|
||||||
resolved[num_resolved].is_ipv4 = false;
|
resolved[num_resolved].is_ipv4 = false;
|
||||||
resolved[num_resolved].ipv6 = ipv6;
|
resolved[num_resolved].ipv6 = ipv6;
|
||||||
@@ -1007,20 +1096,25 @@ static int resolve_connect_targets(ConnectTarget *targets,
|
|||||||
#define MAX_CONNECT_TARGETS 16
|
#define MAX_CONNECT_TARGETS 16
|
||||||
|
|
||||||
int socket_connect(SocketManager *sm, int num_targets,
|
int socket_connect(SocketManager *sm, int num_targets,
|
||||||
ConnectTarget *targets, bool secure, void *user)
|
ConnectTarget *targets, bool secure, bool dont_verify_cert,
|
||||||
|
void *user)
|
||||||
{
|
{
|
||||||
|
Time current_time = get_current_time();
|
||||||
|
if (current_time == INVALID_TIME)
|
||||||
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
|
|
||||||
if (sm->num_used == sm->max_used)
|
if (sm->num_used == sm->max_used)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
|
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
if (!sm->at_least_one_secure_connect) {
|
if (!sm->at_least_one_secure_connect) {
|
||||||
if (client_secure_context_init(&sm->client_secure_context) < 0)
|
if (client_secure_context_init(&sm->client_secure_context) < 0)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
sm->at_least_one_secure_connect = true;
|
sm->at_least_one_secure_connect = true;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (secure)
|
if (secure)
|
||||||
return HTTP_ERROR_NOTLS;
|
return CHTTP_ERROR_NOTLS;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AddressAndPort resolved[MAX_CONNECT_TARGETS];
|
AddressAndPort resolved[MAX_CONNECT_TARGETS];
|
||||||
@@ -1028,7 +1122,7 @@ int socket_connect(SocketManager *sm, int num_targets,
|
|||||||
targets, num_targets, resolved, MAX_CONNECT_TARGETS);
|
targets, num_targets, resolved, MAX_CONNECT_TARGETS);
|
||||||
|
|
||||||
if (num_resolved <= 0)
|
if (num_resolved <= 0)
|
||||||
return HTTP_ERROR_UNSPECIFIED;
|
return CHTTP_ERROR_UNSPECIFIED;
|
||||||
|
|
||||||
Socket *s = sm->sockets;
|
Socket *s = sm->sockets;
|
||||||
while (s->state != SOCKET_STATE_FREE) {
|
while (s->state != SOCKET_STATE_FREE) {
|
||||||
@@ -1045,7 +1139,7 @@ int socket_connect(SocketManager *sm, int num_targets,
|
|||||||
s->next_addr = 0;
|
s->next_addr = 0;
|
||||||
s->addrs = malloc(num_resolved * sizeof(AddressAndPort));
|
s->addrs = malloc(num_resolved * sizeof(AddressAndPort));
|
||||||
if (s->addrs == NULL)
|
if (s->addrs == NULL)
|
||||||
return HTTP_ERROR_OOM;
|
return CHTTP_ERROR_OOM;
|
||||||
for (int i = 0; i < num_resolved; i++)
|
for (int i = 0; i < num_resolved; i++)
|
||||||
s->addrs[i] = resolved[i];
|
s->addrs[i] = resolved[i];
|
||||||
}
|
}
|
||||||
@@ -1053,17 +1147,27 @@ int socket_connect(SocketManager *sm, int num_targets,
|
|||||||
UPDATE_STATE(s->state, SOCKET_STATE_PENDING);
|
UPDATE_STATE(s->state, SOCKET_STATE_PENDING);
|
||||||
s->sock = NATIVE_SOCKET_INVALID;
|
s->sock = NATIVE_SOCKET_INVALID;
|
||||||
s->user = user;
|
s->user = user;
|
||||||
|
s->silent = false;
|
||||||
|
s->creation_time = current_time;
|
||||||
|
s->last_recv_time = current_time;
|
||||||
|
s->creation_timeout = sm->creation_timeout;
|
||||||
|
s->recv_timeout = sm->recv_timeout;
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
s->server_secure_context = NULL;
|
s->server_secure_context = NULL;
|
||||||
s->client_secure_context = NULL;
|
s->client_secure_context = NULL;
|
||||||
s->ssl = NULL;
|
s->ssl = NULL;
|
||||||
if (secure)
|
s->dont_verify_cert = false;
|
||||||
|
if (secure) {
|
||||||
s->client_secure_context = &sm->client_secure_context;
|
s->client_secure_context = &sm->client_secure_context;
|
||||||
|
s->dont_verify_cert = dont_verify_cert;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void) dont_verify_cert;
|
||||||
#endif
|
#endif
|
||||||
sm->num_used++;
|
sm->num_used++;
|
||||||
|
|
||||||
socket_update(s);
|
socket_update(s);
|
||||||
return HTTP_OK;
|
return CHTTP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool would_block(void)
|
static bool would_block(void)
|
||||||
@@ -1098,8 +1202,9 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ret;
|
||||||
if (!is_secure(s)) {
|
if (!is_secure(s)) {
|
||||||
int ret = recv(s->sock, dst, max, 0);
|
ret = recv(s->sock, dst, max, 0);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
UPDATE_STATE(s->state, SOCKET_STATE_DIED);
|
UPDATE_STATE(s->state, SOCKET_STATE_DIED);
|
||||||
s->events = 0;
|
s->events = 0;
|
||||||
@@ -1113,10 +1218,9 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
|
|||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
} else {
|
} else {
|
||||||
#ifdef HTTPS_ENABLED
|
#ifdef HTTPS_ENABLED
|
||||||
int ret = SSL_read(s->ssl, dst, max);
|
ret = SSL_read(s->ssl, dst, max);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
int err = SSL_get_error(s->ssl, ret);
|
int err = SSL_get_error(s->ssl, ret);
|
||||||
if (err == SSL_ERROR_WANT_READ) {
|
if (err == SSL_ERROR_WANT_READ) {
|
||||||
@@ -1131,9 +1235,18 @@ int socket_recv(SocketManager *sm, SocketHandle handle,
|
|||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
return ret;
|
#else
|
||||||
|
// Unreachable
|
||||||
|
ret = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret > 0 && s->recv_timeout != INVALID_TIME) {
|
||||||
|
Time current_time = get_current_time();
|
||||||
|
if (current_time != INVALID_TIME)
|
||||||
|
s->last_recv_time = current_time;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int socket_send(SocketManager *sm, SocketHandle handle,
|
int socket_send(SocketManager *sm, SocketHandle handle,
|
||||||
@@ -1180,13 +1293,15 @@ int socket_send(SocketManager *sm, SocketHandle handle,
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
#else
|
||||||
|
// Unreachable
|
||||||
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void socket_close(SocketManager *sm, SocketHandle handle)
|
void socket_close(SocketManager *sm, SocketHandle handle)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
Socket *s = handle_to_socket(sm, handle);
|
Socket *s = handle_to_socket(sm, handle);
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
return;
|
return;
|
||||||
@@ -1217,7 +1332,6 @@ void socket_set_user(SocketManager *sm, SocketHandle handle, void *user)
|
|||||||
|
|
||||||
bool socket_ready(SocketManager *sm, SocketHandle handle)
|
bool socket_ready(SocketManager *sm, SocketHandle handle)
|
||||||
{
|
{
|
||||||
bool ready = false;
|
|
||||||
Socket *s = handle_to_socket(sm, handle);
|
Socket *s = handle_to_socket(sm, handle);
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
return false;
|
return false;
|
||||||
@@ -1227,3 +1341,12 @@ bool socket_ready(SocketManager *sm, SocketHandle handle)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void socket_silent(SocketManager *sm, SocketHandle handle, bool value)
|
||||||
|
{
|
||||||
|
Socket *s = handle_to_socket(sm, handle);
|
||||||
|
if (s == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
s->silent = value;
|
||||||
|
}
|
||||||
|
|||||||
+36
-12
@@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define NATIVE_SOCKET SOCKET
|
#define NATIVE_SOCKET SOCKET
|
||||||
#define NATIVE_SOCKET_INVALID SOCKET_ERROR
|
#define NATIVE_SOCKET_INVALID INVALID_SOCKET
|
||||||
#define CLOSE_NATIVE_SOCKET closesocket
|
#define CLOSE_NATIVE_SOCKET closesocket
|
||||||
#else
|
#else
|
||||||
#define NATIVE_SOCKET int
|
#define NATIVE_SOCKET int
|
||||||
@@ -65,6 +65,8 @@ typedef uint16_t Port;
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SOCKET_EVENT_READY,
|
SOCKET_EVENT_READY,
|
||||||
|
SOCKET_EVENT_CREATION_TIMEOUT,
|
||||||
|
SOCKET_EVENT_RECV_TIMEOUT,
|
||||||
SOCKET_EVENT_DISCONNECT,
|
SOCKET_EVENT_DISCONNECT,
|
||||||
} SocketEventType;
|
} SocketEventType;
|
||||||
|
|
||||||
@@ -123,8 +125,8 @@ typedef struct {
|
|||||||
// Internal use only
|
// Internal use only
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
HTTP_IPv4 ipv4;
|
CHTTP_IPv4 ipv4;
|
||||||
HTTP_IPv6 ipv6;
|
CHTTP_IPv6 ipv6;
|
||||||
};
|
};
|
||||||
bool is_ipv4;
|
bool is_ipv4;
|
||||||
Port port;
|
Port port;
|
||||||
@@ -149,6 +151,9 @@ typedef struct {
|
|||||||
// Native socket events that need to be monitored
|
// Native socket events that need to be monitored
|
||||||
int events;
|
int events;
|
||||||
|
|
||||||
|
// If this is set, the raw socket handle shouldn't be monitored
|
||||||
|
bool silent;
|
||||||
|
|
||||||
// Generation counter to invalidate any SocketHandle
|
// Generation counter to invalidate any SocketHandle
|
||||||
// referring to this socket when it is freed.
|
// referring to this socket when it is freed.
|
||||||
// Note that this counter may wrap but always skips
|
// Note that this counter may wrap but always skips
|
||||||
@@ -159,6 +164,12 @@ typedef struct {
|
|||||||
// User-provided context pointer
|
// User-provided context pointer
|
||||||
void *user;
|
void *user;
|
||||||
|
|
||||||
|
Time creation_time;
|
||||||
|
Time last_recv_time;
|
||||||
|
|
||||||
|
Time recv_timeout;
|
||||||
|
Time creation_timeout;
|
||||||
|
|
||||||
// A single connect operation may involve
|
// A single connect operation may involve
|
||||||
// trying to establish a connection towards
|
// trying to establish a connection towards
|
||||||
// one of a set of addresses.
|
// one of a set of addresses.
|
||||||
@@ -173,6 +184,7 @@ typedef struct {
|
|||||||
ClientSecureContext *client_secure_context;
|
ClientSecureContext *client_secure_context;
|
||||||
ServerSecureContext *server_secure_context;
|
ServerSecureContext *server_secure_context;
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
|
bool dont_verify_cert;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} Socket;
|
} Socket;
|
||||||
@@ -182,6 +194,10 @@ typedef struct {
|
|||||||
// header.
|
// header.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
|
// TODO: comment
|
||||||
|
Time recv_timeout;
|
||||||
|
Time creation_timeout;
|
||||||
|
|
||||||
// TCP listener sockets. The first is intended
|
// TCP listener sockets. The first is intended
|
||||||
// for plaintext, while the second is for TLS.
|
// for plaintext, while the second is for TLS.
|
||||||
// The socket manager will accept and add new
|
// The socket manager will accept and add new
|
||||||
@@ -230,6 +246,9 @@ int socket_manager_init(SocketManager *sm, Socket *socks,
|
|||||||
// Deinitialize a socket manager
|
// Deinitialize a socket manager
|
||||||
void socket_manager_free(SocketManager *sm);
|
void socket_manager_free(SocketManager *sm);
|
||||||
|
|
||||||
|
void socket_manager_set_creation_timeout(SocketManager *sm, int timeout);
|
||||||
|
void socket_manager_set_recv_timeout(SocketManager *sm, int timeout);
|
||||||
|
|
||||||
// Configure the socket manager to listen on
|
// Configure the socket manager to listen on
|
||||||
// the specified interface for TCP connections.
|
// the specified interface for TCP connections.
|
||||||
// Incoming connections will be automatically
|
// Incoming connections will be automatically
|
||||||
@@ -237,7 +256,7 @@ void socket_manager_free(SocketManager *sm);
|
|||||||
// can only be used once per manager.
|
// can only be used once per manager.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int socket_manager_listen_tcp(SocketManager *sm,
|
int socket_manager_listen_tcp(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, int backlog,
|
CHTTP_String addr, Port port, int backlog,
|
||||||
bool reuse_addr);
|
bool reuse_addr);
|
||||||
|
|
||||||
// Same as the previous function, but incoming
|
// Same as the previous function, but incoming
|
||||||
@@ -248,9 +267,9 @@ int socket_manager_listen_tcp(SocketManager *sm,
|
|||||||
// and secure connections.
|
// and secure connections.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int socket_manager_listen_tls(SocketManager *sm,
|
int socket_manager_listen_tls(SocketManager *sm,
|
||||||
HTTP_String addr, Port port, int backlog,
|
CHTTP_String addr, Port port, int backlog,
|
||||||
bool reuse_addr, HTTP_String cert_file,
|
bool reuse_addr, CHTTP_String cert_file,
|
||||||
HTTP_String key_file);
|
CHTTP_String key_file);
|
||||||
|
|
||||||
// If the socket manager was configures to accept
|
// If the socket manager was configures to accept
|
||||||
// TLS connections, this adds additional certificates
|
// TLS connections, this adds additional certificates
|
||||||
@@ -258,7 +277,7 @@ int socket_manager_listen_tls(SocketManager *sm,
|
|||||||
// authenticity.
|
// authenticity.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int socket_manager_add_certificate(SocketManager *sm,
|
int socket_manager_add_certificate(SocketManager *sm,
|
||||||
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
|
CHTTP_String domain, CHTTP_String cert_file, CHTTP_String key_file);
|
||||||
|
|
||||||
// When a thread is blocked on a poll() call for
|
// When a thread is blocked on a poll() call for
|
||||||
// descriptors associated to this socket manager,
|
// descriptors associated to this socket manager,
|
||||||
@@ -271,6 +290,7 @@ typedef struct {
|
|||||||
void **ptrs;
|
void **ptrs;
|
||||||
struct pollfd *polled;
|
struct pollfd *polled;
|
||||||
int num_polled;
|
int num_polled;
|
||||||
|
int timeout;
|
||||||
} EventRegister;
|
} EventRegister;
|
||||||
|
|
||||||
// Resets the event register with the list of descriptors
|
// Resets the event register with the list of descriptors
|
||||||
@@ -301,9 +321,9 @@ typedef struct {
|
|||||||
ConnectTargetType type;
|
ConnectTargetType type;
|
||||||
Port port;
|
Port port;
|
||||||
union {
|
union {
|
||||||
HTTP_IPv4 ipv4;
|
CHTTP_IPv4 ipv4;
|
||||||
HTTP_IPv6 ipv6;
|
CHTTP_IPv6 ipv6;
|
||||||
HTTP_String name;
|
CHTTP_String name;
|
||||||
};
|
};
|
||||||
} ConnectTarget;
|
} ConnectTarget;
|
||||||
|
|
||||||
@@ -312,7 +332,8 @@ typedef struct {
|
|||||||
// one succedes. If secure=true, the socket uses TLS.
|
// one succedes. If secure=true, the socket uses TLS.
|
||||||
// Returns 0 on success, -1 on error.
|
// Returns 0 on success, -1 on error.
|
||||||
int socket_connect(SocketManager *sm, int num_targets,
|
int socket_connect(SocketManager *sm, int num_targets,
|
||||||
ConnectTarget *targets, bool secure, void *user);
|
ConnectTarget *targets, bool secure, bool dont_verify_cert,
|
||||||
|
void *user);
|
||||||
|
|
||||||
int socket_recv(SocketManager *sm, SocketHandle handle,
|
int socket_recv(SocketManager *sm, SocketHandle handle,
|
||||||
char *dst, int max);
|
char *dst, int max);
|
||||||
@@ -333,3 +354,6 @@ void socket_set_user(SocketManager *sm, SocketHandle handle, void *user);
|
|||||||
// Returns true iff the socket is ready for reading or
|
// Returns true iff the socket is ready for reading or
|
||||||
// writing.
|
// writing.
|
||||||
bool socket_ready(SocketManager *sm, SocketHandle handle);
|
bool socket_ready(SocketManager *sm, SocketHandle handle);
|
||||||
|
|
||||||
|
// When a socket is marked as silent it will not generate events
|
||||||
|
void socket_silent(SocketManager *sm, SocketHandle handle, bool value);
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
Time get_current_time(void)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
{
|
||||||
|
int64_t count;
|
||||||
|
int64_t freq;
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
ok = QueryPerformanceCounter((LARGE_INTEGER*) &count);
|
||||||
|
if (!ok) return INVALID_TIME;
|
||||||
|
|
||||||
|
ok = QueryPerformanceFrequency((LARGE_INTEGER*) &freq);
|
||||||
|
if (!ok) return INVALID_TIME;
|
||||||
|
|
||||||
|
uint64_t res = 1000 * (double) count / freq;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
struct timespec time;
|
||||||
|
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &time))
|
||||||
|
return INVALID_TIME;
|
||||||
|
|
||||||
|
uint64_t res;
|
||||||
|
|
||||||
|
uint64_t sec = time.tv_sec;
|
||||||
|
if (sec > UINT64_MAX / 1000)
|
||||||
|
return INVALID_TIME;
|
||||||
|
res = sec * 1000;
|
||||||
|
|
||||||
|
uint64_t nsec = time.tv_nsec;
|
||||||
|
if (res > UINT64_MAX - nsec / 1000000)
|
||||||
|
return INVALID_TIME;
|
||||||
|
res += nsec / 1000000;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
typedef uint64_t Time;
|
||||||
|
|
||||||
|
#define INVALID_TIME ((Time) UINT64_MAX-1)
|
||||||
|
|
||||||
|
Time get_current_time(void);
|
||||||
Reference in New Issue
Block a user