Compare commits
16 Commits
c7be0adc66
...
dev
Author | SHA1 | Date | |
---|---|---|---|
af62a3065a
|
|||
c36ad8c113
|
|||
7f7a07c4d2
|
|||
4ff22bd0c6
|
|||
83ca2467de
|
|||
0a1fb977d6
|
|||
1405036cf2
|
|||
5f3cd03a6f
|
|||
819b71f285
|
|||
ceaa384fce
|
|||
99c4eb1c8a
|
|||
fb59b0d8c4
|
|||
946adb54d7
|
|||
b369a1116e
|
|||
950bf19331
|
|||
993cb65724
|
4
Makefile
4
Makefile
@@ -63,7 +63,7 @@ bin/sesimos: bin/server.o bin/logger.o bin/cache_handler.o bin/async.o bin/worke
|
||||
bin/lib/http_static.o bin/res/default.o bin/res/proxy.o bin/res/style.o \
|
||||
bin/res/icon_error.o bin/res/icon_info.o bin/res/icon_success.o bin/res/icon_warning.o \
|
||||
bin/res/globe.o \
|
||||
bin/lib/compress.o bin/lib/config.o bin/lib/fastcgi.o bin/lib/geoip.o \
|
||||
bin/lib/compress.o bin/lib/config.o bin/lib/fastcgi.o bin/lib/geoip.o bin/lib/error.o \
|
||||
bin/lib/http.o bin/lib/proxy.o bin/lib/sock.o bin/lib/uri.o \
|
||||
bin/lib/utils.o bin/lib/websocket.o bin/lib/mpmc.o bin/lib/list.o
|
||||
$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)
|
||||
@@ -99,6 +99,8 @@ bin/lib/config.o: src/lib/config.h src/lib/utils.h src/lib/uri.h src/logger.h
|
||||
bin/lib/fastcgi.o: src/lib/fastcgi.h src/server.h src/lib/utils.h src/lib/compress.h src/lib/http.h \
|
||||
src/lib/uri.h src/lib/include/fastcgi.h src/logger.h
|
||||
|
||||
bin/lib/error.o: src/lib/error.h
|
||||
|
||||
bin/lib/geoip.o: src/lib/geoip.h
|
||||
|
||||
bin/lib/http.o: src/lib/http.h src/lib/utils.h src/lib/compress.h src/lib/sock.h src/logger.h
|
||||
|
49
src/async.c
49
src/async.c
@@ -9,6 +9,7 @@
|
||||
#include "async.h"
|
||||
#include "logger.h"
|
||||
#include "lib/list.h"
|
||||
#include "lib/utils.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <sys/epoll.h>
|
||||
@@ -28,6 +29,7 @@ typedef struct {
|
||||
int flags;
|
||||
void *arg;
|
||||
void (*cb)(void *);
|
||||
void (*to_cb)(void *);
|
||||
void (*err_cb)(void *);
|
||||
} evt_listen_t;
|
||||
|
||||
@@ -154,7 +156,7 @@ static int async_add(evt_listen_t *evt) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *), void err_cb(void *)) {
|
||||
int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *)) {
|
||||
evt_listen_t evt = {
|
||||
.fd = fd,
|
||||
.socket = NULL,
|
||||
@@ -162,12 +164,13 @@ int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *),
|
||||
.flags = flags,
|
||||
.arg = arg,
|
||||
.cb = cb,
|
||||
.to_cb = to_cb,
|
||||
.err_cb = err_cb,
|
||||
};
|
||||
return async_add(&evt);
|
||||
}
|
||||
|
||||
int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), void err_cb(void *)) {
|
||||
int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *)) {
|
||||
evt_listen_t evt = {
|
||||
.fd = s->socket,
|
||||
.socket = s,
|
||||
@@ -175,6 +178,7 @@ int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), vo
|
||||
.flags = flags,
|
||||
.arg = arg,
|
||||
.cb = cb,
|
||||
.to_cb = to_cb,
|
||||
.err_cb = err_cb,
|
||||
};
|
||||
return async_add(&evt);
|
||||
@@ -206,6 +210,8 @@ void async_free(void) {
|
||||
void async_thread(void) {
|
||||
struct epoll_event ev, events[ASYNC_MAX_EVENTS];
|
||||
int num_fds;
|
||||
long ts, min_ts, cur_ts;
|
||||
listen_queue_t *l;
|
||||
evt_listen_t **local = list_create(sizeof(evt_listen_t *), 16);
|
||||
if (local == NULL) {
|
||||
critical("Unable to create async local list");
|
||||
@@ -217,7 +223,7 @@ void async_thread(void) {
|
||||
// main event loop
|
||||
while (alive) {
|
||||
// swap listen queue
|
||||
listen_queue_t *l = listen_q;
|
||||
l = listen_q;
|
||||
listen_q = (listen_q == &listen1) ? &listen2 : &listen1;
|
||||
|
||||
// fill local list and epoll instance with previously added queue entries
|
||||
@@ -240,7 +246,17 @@ void async_thread(void) {
|
||||
// reset size of queue
|
||||
l->n = 0;
|
||||
|
||||
if ((num_fds = epoll_wait(epoll_fd, events, ASYNC_MAX_EVENTS, -1)) == -1) {
|
||||
// calculate wait timeout
|
||||
min_ts = -1000, cur_ts = clock_micros();;
|
||||
for (int i = 0; i < list_size(local); i++) {
|
||||
evt_listen_t *evt = local[i];
|
||||
if (!evt->socket) continue;
|
||||
|
||||
ts = evt->socket->ts_last + evt->socket->timeout_us - cur_ts;
|
||||
if (min_ts == -1000 || ts < min_ts) min_ts = ts;
|
||||
}
|
||||
|
||||
if ((num_fds = epoll_wait(epoll_fd, events, ASYNC_MAX_EVENTS, (int) (min_ts / 1000))) == -1) {
|
||||
if (errno == EINTR) {
|
||||
// interrupt
|
||||
errno = 0;
|
||||
@@ -274,6 +290,31 @@ void async_thread(void) {
|
||||
free(evt);
|
||||
}
|
||||
}
|
||||
|
||||
// check, if some socket ran into a timeout
|
||||
cur_ts = clock_micros();
|
||||
for (int i = 0; i < list_size(local); i++) {
|
||||
evt_listen_t *evt = local[i];
|
||||
if (!evt->socket) continue;
|
||||
|
||||
if ((cur_ts - evt->socket->ts_last) >= evt->socket->timeout_us) {
|
||||
evt->to_cb(evt->arg);
|
||||
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, evt->fd, NULL) == -1) {
|
||||
if (errno == EBADF) {
|
||||
// already closed fd, do not die
|
||||
errno = 0;
|
||||
} else {
|
||||
critical("Unable to remove file descriptor from epoll instance");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
local = list_remove(local, i--);
|
||||
}
|
||||
}
|
||||
logger_set_prefix("");
|
||||
errno = 0;
|
||||
}
|
||||
|
||||
// cleanup
|
||||
|
@@ -24,9 +24,9 @@
|
||||
|
||||
typedef unsigned int async_evt_t;
|
||||
|
||||
int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), void err_cb(void *));
|
||||
int async(sock *s, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *));
|
||||
|
||||
int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *), void err_cb(void *));
|
||||
int async_fd(int fd, async_evt_t events, int flags, void *arg, void cb(void *), void to_cb(void *), void err_cb(void *));
|
||||
|
||||
int async_init(void);
|
||||
|
||||
|
134
src/lib/error.c
Normal file
134
src/lib/error.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Error interface
|
||||
* @file src/lib/error.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2023-01-08
|
||||
*/
|
||||
|
||||
#include "error.h"
|
||||
#include "http.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <maxminddb.h>
|
||||
|
||||
static const char *error_ssl_strerror(int err) {
|
||||
switch (err) {
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
return "closed";
|
||||
case SSL_ERROR_WANT_READ:
|
||||
return "want read";
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
return "want write";
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
return "want connect";
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
return "want accept";
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
return "want x509 lookup";
|
||||
case SSL_ERROR_WANT_ASYNC:
|
||||
return "want async";
|
||||
case SSL_ERROR_WANT_ASYNC_JOB:
|
||||
return "want async job";
|
||||
case SSL_ERROR_WANT_CLIENT_HELLO_CB:
|
||||
return "want client hello callback";
|
||||
case SSL_ERROR_WANT_RETRY_VERIFY:
|
||||
return "want retry verify";
|
||||
case SSL_ERROR_SSL:
|
||||
return ERR_reason_error_string(ERR_get_error());
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *error_http_strerror(int err) {
|
||||
switch (err) {
|
||||
case HTTP_ERROR_TOO_MANY_HEADER_FIELDS:
|
||||
return "too many header fields";
|
||||
case HTTP_ERROR_EOH_NOT_FOUND:
|
||||
return "end of http header not found";
|
||||
case HTTP_ERROR_HEADER_MALFORMED:
|
||||
return "http header malformed";
|
||||
case HTTP_ERROR_INVALID_VERSION:
|
||||
return "invalid http version";
|
||||
case HTTP_ERROR_URI_TOO_LONG:
|
||||
return "uri too long";
|
||||
case HTTP_ERROR_GENERAL:
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
const char *error_str(int err_no, char *buf, int buf_len) {
|
||||
buf[0] = 0;
|
||||
unsigned char mode = (unsigned char) (err_no >> 24);
|
||||
int e = err_no & 0x00FFFFFF;
|
||||
if (mode == 0x00) {
|
||||
// normal
|
||||
strerror_r(e, buf, buf_len);
|
||||
return buf;
|
||||
} else if (mode == 0x01) {
|
||||
// ssl
|
||||
return error_ssl_strerror(e);
|
||||
} else if (mode == 0x02) {
|
||||
// mmdb
|
||||
return MMDB_strerror(e);
|
||||
} else if (mode == 0x03) {
|
||||
// http
|
||||
return error_http_strerror(e);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void error_ssl(int err) {
|
||||
if (err == SSL_ERROR_NONE) {
|
||||
errno = 0;
|
||||
} else if (err == SSL_ERROR_SYSCALL) {
|
||||
// errno already set
|
||||
} else {
|
||||
errno = 0x01000000 | err;
|
||||
}
|
||||
}
|
||||
|
||||
void error_mmdb(int err) {
|
||||
if (err == MMDB_SUCCESS) {
|
||||
errno = 0;
|
||||
} else if (err == MMDB_IO_ERROR) {
|
||||
// errno already set
|
||||
} else {
|
||||
errno = 0x02000000 | err;
|
||||
}
|
||||
}
|
||||
|
||||
int error_http(int err) {
|
||||
if (err == 0) {
|
||||
errno = 0;
|
||||
} else if (err == HTTP_ERROR_SYSCALL) {
|
||||
// errno already set
|
||||
} else {
|
||||
errno = 0x03000000 | err;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int error_get(unsigned char prefix) {
|
||||
return (errno >> 24 != prefix) ? 0 : errno & 0x00FFFFFF;
|
||||
}
|
||||
|
||||
int error_get_sys() {
|
||||
return error_get(0x00);
|
||||
}
|
||||
|
||||
int error_get_ssl() {
|
||||
return error_get(0x01);
|
||||
}
|
||||
|
||||
int error_get_mmdb() {
|
||||
return error_get(0x02);
|
||||
}
|
||||
|
||||
int error_get_http() {
|
||||
return error_get(0x03);
|
||||
}
|
28
src/lib/error.h
Normal file
28
src/lib/error.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* sesimos - secure, simple, modern web server
|
||||
* @brief Error interface (header fie)
|
||||
* @file src/lib/error.h
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2023-01-08
|
||||
*/
|
||||
|
||||
#ifndef SESIMOS_ERROR_H
|
||||
#define SESIMOS_ERROR_H
|
||||
|
||||
const char *error_str(int err_no, char *buf, int buf_len);
|
||||
|
||||
void error_ssl(int err);
|
||||
|
||||
void error_mmdb(int err);
|
||||
|
||||
int error_http(int err);
|
||||
|
||||
int error_get_sys();
|
||||
|
||||
int error_get_ssl();
|
||||
|
||||
int error_get_mmdb();
|
||||
|
||||
int error_get_http();
|
||||
|
||||
#endif //SESIMOS_ERROR_H
|
@@ -383,6 +383,7 @@ int fastcgi_header(fastcgi_cnx_t *conn, http_res *res, char *err_msg) {
|
||||
|
||||
ret = http_parse_header_field(&res->hdr, ptr, pos0, 0);
|
||||
if (ret != 0) return (int) ret;
|
||||
|
||||
if (pos0[2] == '\r' && pos0[3] == '\n') {
|
||||
return 0;
|
||||
}
|
||||
@@ -593,7 +594,7 @@ int fastcgi_receive(fastcgi_cnx_t *conn, sock *client, unsigned long len) {
|
||||
while (rcv_len < len) {
|
||||
ret = sock_recv(client, buf, sizeof(buf), 0);
|
||||
if (ret <= 0) {
|
||||
error("Unable to receive: %s", sock_strerror(client));
|
||||
error("Unable to receive");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "geoip.h"
|
||||
#include "../logger.h"
|
||||
#include "error.h"
|
||||
#include <memory.h>
|
||||
#include <dirent.h>
|
||||
|
||||
@@ -101,7 +102,8 @@ int geoip_init(const char *directory) {
|
||||
|
||||
sprintf(buf, "%s/%s", directory, entry->d_name);
|
||||
if ((status = MMDB_open(buf, 0, &mmdbs[i])) != MMDB_SUCCESS) {
|
||||
critical("Unable to initialize geoip: Unable to open .mmdb file: %s", MMDB_strerror(status));
|
||||
error_mmdb(status);
|
||||
critical("Unable to initialize geoip: Unable to open .mmdb file");
|
||||
closedir(geoip_dir);
|
||||
return 1;
|
||||
}
|
||||
@@ -164,7 +166,8 @@ int geoip_lookup_json(struct sockaddr *addr, char *json, long len) {
|
||||
int mmdb_res;
|
||||
MMDB_lookup_result_s result = MMDB_lookup_sockaddr(&mmdbs[i], addr, &mmdb_res);
|
||||
if (mmdb_res != MMDB_SUCCESS) {
|
||||
error("Unable to lookup geoip info: %s", MMDB_strerror(mmdb_res));
|
||||
error_mmdb(mmdb_res);
|
||||
error("Unable to lookup geoip info");
|
||||
continue;
|
||||
} else if (!result.found_entry) {
|
||||
continue;
|
||||
@@ -172,7 +175,8 @@ int geoip_lookup_json(struct sockaddr *addr, char *json, long len) {
|
||||
|
||||
MMDB_entry_data_list_s *list;
|
||||
if ((mmdb_res = MMDB_get_entry_data_list(&result.entry, &list)) != MMDB_SUCCESS) {
|
||||
error("Unable to lookup geoip info: %s", MMDB_strerror(mmdb_res));
|
||||
error_mmdb(mmdb_res);
|
||||
error("Unable to lookup geoip info");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@@ -6,11 +6,11 @@
|
||||
* @date 2020-12-09
|
||||
*/
|
||||
|
||||
#include "../logger.h"
|
||||
#include "http.h"
|
||||
#include "utils.h"
|
||||
#include "compress.h"
|
||||
#include "list.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -90,23 +90,20 @@ void http_free_res(http_res *res) {
|
||||
int http_init_hdr(http_hdr *hdr) {
|
||||
hdr->fields = list_create(sizeof(http_field), HTTP_INIT_HEADER_FIELD_NUM);
|
||||
if (hdr->fields == NULL)
|
||||
return -1;
|
||||
return error_http(HTTP_ERROR_SYSCALL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, int flags) {
|
||||
if (hdr->last_field_num > list_size(hdr->fields)) {
|
||||
error("Unable to parse header: Invalid state");
|
||||
return 3;
|
||||
}
|
||||
if (hdr->last_field_num > list_size(hdr->fields))
|
||||
return error_http(HTTP_ERROR_GENERAL);
|
||||
|
||||
char *pos1 = (char *) buf, *pos2 = (char *) end_ptr;
|
||||
if (buf[0] == ' ' || buf[0] == '\t') {
|
||||
if (hdr->last_field_num == -1) {
|
||||
error("Unable to parse header");
|
||||
return 3;
|
||||
}
|
||||
if (hdr->last_field_num == -1)
|
||||
return error_http(HTTP_ERROR_GENERAL);
|
||||
|
||||
http_field *f = &hdr->fields[(int) hdr->last_field_num];
|
||||
|
||||
str_trim_lws(&pos1, &pos2);
|
||||
@@ -116,10 +113,9 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr,
|
||||
}
|
||||
|
||||
pos1 = memchr(buf, ':', end_ptr - buf);
|
||||
if (pos1 == NULL) {
|
||||
error("Unable to parse header");
|
||||
return 3;
|
||||
}
|
||||
if (pos1 == NULL)
|
||||
return error_http(HTTP_ERROR_GENERAL);
|
||||
|
||||
long len1 = pos1 - buf;
|
||||
|
||||
pos1++;
|
||||
@@ -129,10 +125,8 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr,
|
||||
int field_num = list_size(hdr->fields);
|
||||
int found = http_get_header_field_num_len(hdr, buf, len1);
|
||||
if (!(flags & HTTP_MERGE_FIELDS) || found == -1) {
|
||||
if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0) {
|
||||
error("Unable to parse header: Too many header fields");
|
||||
return 3;
|
||||
}
|
||||
if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0)
|
||||
return error_http(HTTP_ERROR_TOO_MANY_HEADER_FIELDS);
|
||||
} else {
|
||||
field_num = found;
|
||||
http_append_to_header_field(&hdr->fields[found], ", ", 2);
|
||||
@@ -148,62 +142,52 @@ int http_parse_request(char *buf, http_req *req) {
|
||||
long len;
|
||||
|
||||
unsigned long header_len = strstr(buf, "\r\n\r\n") - buf + 4;
|
||||
if (header_len <= 0) {
|
||||
error("Unable to parse http header: End of header not found");
|
||||
return -5;
|
||||
}
|
||||
if (header_len <= 0)
|
||||
return error_http(HTTP_ERROR_EOH_NOT_FOUND);
|
||||
|
||||
for (int i = 0; i < header_len; i++) {
|
||||
if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) {
|
||||
error("Unable to parse http header: Header contains illegal characters");
|
||||
return -4;
|
||||
}
|
||||
if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F)
|
||||
return error_http(HTTP_ERROR_HEADER_MALFORMED);
|
||||
}
|
||||
|
||||
ptr = buf;
|
||||
while (header_len > (ptr - buf + 2)) {
|
||||
pos0 = strstr(ptr, "\r\n");
|
||||
if (pos0 == NULL) {
|
||||
error("Unable to parse http header: Invalid header format");
|
||||
return -1;
|
||||
}
|
||||
if (pos0 == NULL)
|
||||
return error_http(HTTP_ERROR_HEADER_MALFORMED);
|
||||
|
||||
if (req->version[0] == 0) {
|
||||
pos1 = (char *) strchr(ptr, ' ') + 1;
|
||||
if (pos1 == NULL) goto err_hdr_fmt;
|
||||
|
||||
if (pos1 - ptr - 1 >= sizeof(req->method)) {
|
||||
error("Unable to parse http header: Method name too long");
|
||||
return -2;
|
||||
}
|
||||
if (pos1 - ptr - 1 >= sizeof(req->method))
|
||||
return error_http(HTTP_ERROR_HEADER_MALFORMED);
|
||||
|
||||
for (int i = 0; i < (pos1 - ptr - 1); i++) {
|
||||
if (ptr[i] < 'A' || ptr[i] > 'Z') {
|
||||
error("Unable to parse http header: Invalid method");
|
||||
return -2;
|
||||
}
|
||||
if (ptr[i] < 'A' || ptr[i] > 'Z')
|
||||
return error_http(HTTP_ERROR_HEADER_MALFORMED);
|
||||
}
|
||||
snprintf(req->method, sizeof(req->method), "%.*s", (int) (pos1 - ptr - 1), ptr);
|
||||
|
||||
pos2 = (char *) strchr(pos1, ' ') + 1;
|
||||
if (pos2 == NULL) {
|
||||
err_hdr_fmt:
|
||||
error("Unable to parse http header: Invalid header format");
|
||||
return -1;
|
||||
return error_http(HTTP_ERROR_HEADER_MALFORMED);
|
||||
}
|
||||
|
||||
if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0) {
|
||||
error("Unable to parse http header: Invalid version");
|
||||
return -3;
|
||||
}
|
||||
if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0)
|
||||
return error_http(HTTP_ERROR_INVALID_VERSION);
|
||||
|
||||
len = pos2 - pos1 - 1;
|
||||
if (len >= 2048)
|
||||
return error_http(HTTP_ERROR_URI_TOO_LONG);
|
||||
|
||||
req->uri = malloc(len + 1);
|
||||
sprintf(req->uri, "%.*s", (int) len, pos1);
|
||||
sprintf(req->version, "%.3s", pos2 + 5);
|
||||
} else {
|
||||
int ret = http_parse_header_field(&req->hdr, ptr, pos0, HTTP_MERGE_FIELDS);
|
||||
if (ret != 0) return -ret;
|
||||
if (http_parse_header_field(&req->hdr, ptr, pos0, HTTP_MERGE_FIELDS) != 0)
|
||||
return -1;
|
||||
}
|
||||
ptr = pos0 + 2;
|
||||
}
|
||||
@@ -212,7 +196,7 @@ int http_parse_request(char *buf, http_req *req) {
|
||||
return (int) header_len;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return error_http(HTTP_ERROR_GENERAL);
|
||||
}
|
||||
|
||||
int http_receive_request(sock *client, http_req *req) {
|
||||
@@ -226,10 +210,9 @@ int http_receive_request(sock *client, http_req *req) {
|
||||
http_init_hdr(&req->hdr);
|
||||
|
||||
rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE - 1, MSG_PEEK);
|
||||
if (rcv_len <= 0) {
|
||||
error("Unable to receive http header: %s", sock_strerror(client));
|
||||
if (rcv_len <= 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf[rcv_len] = 0;
|
||||
|
||||
long header_len = http_parse_request(buf, req);
|
||||
@@ -351,9 +334,9 @@ int http_send_response(sock *client, http_res *res) {
|
||||
off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f));
|
||||
}
|
||||
off += sprintf(buf + off, "\r\n");
|
||||
if (sock_send(client, buf, off, 0) < 0) {
|
||||
if (sock_send(client, buf, off, 0) != off)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -365,10 +348,9 @@ int http_send_request(sock *server, http_req *req) {
|
||||
off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f));
|
||||
}
|
||||
off += sprintf(buf + off, "\r\n");
|
||||
long ret = sock_send(server, buf, off, 0);
|
||||
if (ret <= 0) {
|
||||
if (sock_send(server, buf, off, 0) != off)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -39,6 +39,21 @@
|
||||
#define CLIENT_MAX_HEADER_SIZE 8192
|
||||
#define HTTP_INIT_HEADER_FIELD_NUM 16
|
||||
|
||||
#define HTTP_TYPE_INFORMATIONAL 1
|
||||
#define HTTP_TYPE_SUCCESS 2
|
||||
#define HTTP_TYPE_REDIRECTION 3
|
||||
#define HTTP_TYPE_CLIENT_ERROR 4
|
||||
#define HTTP_TYPE_SERVER_ERROR 5
|
||||
|
||||
#define HTTP_ERROR_GENERAL 1
|
||||
#define HTTP_ERROR_SYSCALL 2
|
||||
#define HTTP_ERROR_TOO_MANY_HEADER_FIELDS 3
|
||||
#define HTTP_ERROR_EOH_NOT_FOUND 4
|
||||
#define HTTP_ERROR_HEADER_MALFORMED 5
|
||||
#define HTTP_ERROR_INVALID_VERSION 6
|
||||
#define HTTP_ERROR_URI_TOO_LONG 7
|
||||
#define HTTP_ERROR_
|
||||
|
||||
#ifndef SERVER_STR
|
||||
# define SERVER_STR "sesimos"
|
||||
#endif
|
||||
@@ -48,13 +63,13 @@
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
unsigned short code;
|
||||
char type[16];
|
||||
unsigned short code:10;
|
||||
unsigned char type:3;
|
||||
char msg[64];
|
||||
} http_status;
|
||||
|
||||
typedef struct {
|
||||
unsigned short code;
|
||||
unsigned short code:10;
|
||||
const char *msg;
|
||||
} http_status_msg;
|
||||
|
||||
|
@@ -4,103 +4,184 @@
|
||||
* @file src/lib/http_static.c
|
||||
* @author Lorenz Stechauner
|
||||
* @date 2021-05-03
|
||||
* @details https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||
*/
|
||||
|
||||
#include "http.h"
|
||||
|
||||
const http_status http_statuses[] = {
|
||||
{100, "Informational", "Continue"},
|
||||
{101, "Informational", "Switching Protocols"},
|
||||
{100, HTTP_TYPE_INFORMATIONAL, "Continue"},
|
||||
{101, HTTP_TYPE_INFORMATIONAL, "Switching Protocols"},
|
||||
{102, HTTP_TYPE_INFORMATIONAL, "Processing"},
|
||||
{103, HTTP_TYPE_INFORMATIONAL, "Early Hints"},
|
||||
|
||||
{200, "Success", "OK"},
|
||||
{201, "Success", "Created"},
|
||||
{202, "Success", "Accepted"},
|
||||
{203, "Success", "Non-Authoritative Information"},
|
||||
{204, "Success", "No Content"},
|
||||
{205, "Success", "Reset Content"},
|
||||
{206, "Success", "Partial Content"},
|
||||
{200, HTTP_TYPE_SUCCESS, "OK"},
|
||||
{201, HTTP_TYPE_SUCCESS, "Created"},
|
||||
{202, HTTP_TYPE_SUCCESS, "Accepted"},
|
||||
{203, HTTP_TYPE_SUCCESS, "Non-Authoritative Information"},
|
||||
{204, HTTP_TYPE_SUCCESS, "No Content"},
|
||||
{205, HTTP_TYPE_SUCCESS, "Reset Content"},
|
||||
{206, HTTP_TYPE_SUCCESS, "Partial Content"},
|
||||
{207, HTTP_TYPE_SUCCESS, "Multi-Status"},
|
||||
{208, HTTP_TYPE_SUCCESS, "Already Reported"},
|
||||
{226, HTTP_TYPE_SUCCESS, "Instance Manipulation Used"},
|
||||
|
||||
{300, "Redirection", "Multiple Choices"},
|
||||
{301, "Redirection", "Moved Permanently"},
|
||||
{302, "Redirection", "Found"},
|
||||
{303, "Redirection", "See Other"},
|
||||
{304, "Success", "Not Modified"},
|
||||
{305, "Redirection", "Use Proxy"},
|
||||
{307, "Redirection", "Temporary Redirect"},
|
||||
{308, "Redirection", "Permanent Redirect"},
|
||||
{300, HTTP_TYPE_REDIRECTION, "Multiple Choices"},
|
||||
{301, HTTP_TYPE_REDIRECTION, "Moved Permanently"},
|
||||
{302, HTTP_TYPE_REDIRECTION, "Found"},
|
||||
{303, HTTP_TYPE_REDIRECTION, "See Other"},
|
||||
{304, HTTP_TYPE_SUCCESS, "Not Modified"},
|
||||
{305, HTTP_TYPE_REDIRECTION, "Use Proxy"},
|
||||
{307, HTTP_TYPE_REDIRECTION, "Temporary Redirect"},
|
||||
{308, HTTP_TYPE_REDIRECTION, "Permanent Redirect"},
|
||||
|
||||
{400, "Client Error", "Bad Request"},
|
||||
{401, "Client Error", "Unauthorized"},
|
||||
{402, "Client Error", "Payment Required"},
|
||||
{403, "Client Error", "Forbidden"},
|
||||
{404, "Client Error", "Not Found"},
|
||||
{405, "Client Error", "Method Not Allowed"},
|
||||
{406, "Client Error", "Not Acceptable"},
|
||||
{407, "Client Error", "Proxy Authentication Required"},
|
||||
{408, "Client Error", "Request Timeout"},
|
||||
{409, "Client Error", "Conflict"},
|
||||
{410, "Client Error", "Gone"},
|
||||
{411, "Client Error", "Length Required"},
|
||||
{412, "Client Error", "Precondition Failed"},
|
||||
{413, "Client Error", "Request Entity Too Large"},
|
||||
{414, "Client Error", "Request-URI Too Long"},
|
||||
{415, "Client Error", "Unsupported Media Type"},
|
||||
{416, "Client Error", "Range Not Satisfiable"},
|
||||
{417, "Client Error", "Expectation Failed"},
|
||||
{400, HTTP_TYPE_CLIENT_ERROR, "Bad Request"},
|
||||
{401, HTTP_TYPE_CLIENT_ERROR, "Unauthorized"},
|
||||
{402, HTTP_TYPE_CLIENT_ERROR, "Payment Required"},
|
||||
{403, HTTP_TYPE_CLIENT_ERROR, "Forbidden"},
|
||||
{404, HTTP_TYPE_CLIENT_ERROR, "Not Found"},
|
||||
{405, HTTP_TYPE_CLIENT_ERROR, "Method Not Allowed"},
|
||||
{406, HTTP_TYPE_CLIENT_ERROR, "Not Acceptable"},
|
||||
{407, HTTP_TYPE_CLIENT_ERROR, "Proxy Authentication Required"},
|
||||
{408, HTTP_TYPE_CLIENT_ERROR, "Request Timeout"},
|
||||
{409, HTTP_TYPE_CLIENT_ERROR, "Conflict"},
|
||||
{410, HTTP_TYPE_CLIENT_ERROR, "Gone"},
|
||||
{411, HTTP_TYPE_CLIENT_ERROR, "Length Required"},
|
||||
{412, HTTP_TYPE_CLIENT_ERROR, "Precondition Failed"},
|
||||
{413, HTTP_TYPE_CLIENT_ERROR, "Request Entity Too Large"},
|
||||
{414, HTTP_TYPE_CLIENT_ERROR, "Request-URI Too Long"},
|
||||
{415, HTTP_TYPE_CLIENT_ERROR, "Unsupported Media Type"},
|
||||
{416, HTTP_TYPE_CLIENT_ERROR, "Range Not Satisfiable"},
|
||||
{417, HTTP_TYPE_CLIENT_ERROR, "Expectation Failed"},
|
||||
{421, HTTP_TYPE_CLIENT_ERROR, "Misdirected Request"},
|
||||
{422, HTTP_TYPE_CLIENT_ERROR, "Unprocessable Content"},
|
||||
{423, HTTP_TYPE_CLIENT_ERROR, "Locked"},
|
||||
{424, HTTP_TYPE_CLIENT_ERROR, "Failed Dependency"},
|
||||
{425, HTTP_TYPE_CLIENT_ERROR, "Too Early"},
|
||||
{426, HTTP_TYPE_CLIENT_ERROR, "Upgrade Required"},
|
||||
{428, HTTP_TYPE_CLIENT_ERROR, "Precondition Required"},
|
||||
{429, HTTP_TYPE_CLIENT_ERROR, "Too Many Requests"},
|
||||
{431, HTTP_TYPE_CLIENT_ERROR, "Request Header Fields Too Large"},
|
||||
{451, HTTP_TYPE_CLIENT_ERROR, "Unavailable For Legal Reasons"},
|
||||
|
||||
{500, "Server Error", "Internal Server Error"},
|
||||
{501, "Server Error", "Not Implemented"},
|
||||
{502, "Server Error", "Bad Gateway"},
|
||||
{503, "Server Error", "Service Unavailable"},
|
||||
{504, "Server Error", "Gateway Timeout"},
|
||||
{505, "Server Error", "HTTP Version Not Supported"},
|
||||
{500, HTTP_TYPE_SERVER_ERROR, "Internal Server Error"},
|
||||
{501, HTTP_TYPE_SERVER_ERROR, "Not Implemented"},
|
||||
{502, HTTP_TYPE_SERVER_ERROR, "Bad Gateway"},
|
||||
{503, HTTP_TYPE_SERVER_ERROR, "Service Unavailable"},
|
||||
{504, HTTP_TYPE_SERVER_ERROR, "Gateway Timeout"},
|
||||
{505, HTTP_TYPE_SERVER_ERROR, "HTTP Version Not Supported"},
|
||||
{506, HTTP_TYPE_SERVER_ERROR, "Variant Also Negotiates"},
|
||||
{507, HTTP_TYPE_SERVER_ERROR, "Insufficient Storage"},
|
||||
{508, HTTP_TYPE_SERVER_ERROR, "Loop Detected"},
|
||||
{511, HTTP_TYPE_SERVER_ERROR, "Network Authentication Required"},
|
||||
};
|
||||
|
||||
const http_status_msg http_status_messages[] = {
|
||||
{100, "The client SHOULD continue with its request."},
|
||||
{101, "The server understands and is willing to comply with the clients request, via the Upgrade message header field, for a change in the application protocol being used on this connection."},
|
||||
{100, "The client SHOULD continue with its request. The server MUST send a final response after the request "
|
||||
"has been completed."},
|
||||
{101, "The server understands and is willing to comply with the clients request, via the Upgrade message "
|
||||
"header field, for a change in the application protocol being used on this connection."},
|
||||
{102, "The server has a reasonable expectation that the request will take significant time to complete. The "
|
||||
"server MUST send a final response after the request has been completed."},
|
||||
{103, "The client can speculatively evaluate the header fields included in the response while waiting for the "
|
||||
"final response. The server MUST send a final response after the request has been completed."},
|
||||
|
||||
{200, "The request has succeeded."},
|
||||
{201, "The request has been fulfilled and resulted in a new resource being created."},
|
||||
{202, "The request has been accepted for processing, but the processing has not been completed."},
|
||||
{203, "The returned meta information in the entity-header is not the definitive set as available from the origin server, but is gathered from a local or a third-party copy."},
|
||||
{204, "The server has fulfilled the request but does not need to return an entity-body, and might want to return updated meta information."},
|
||||
{205, "The server has fulfilled the request and the user agent SHOULD reset the document view which caused the request to be sent."},
|
||||
{203, "The returned meta information in the entity-header is not the definitive set as available from the "
|
||||
"origin server, but is gathered from a local or a third-party copy."},
|
||||
{204, "The server has fulfilled the request but does not need to return an entity-body, and might want to "
|
||||
"return updated meta information."},
|
||||
{205, "The server has fulfilled the request and the user agent SHOULD reset the document view which caused the "
|
||||
"request to be sent."},
|
||||
{206, "The server has fulfilled the partial GET request for the resource."},
|
||||
{207, "The response provides status for multiple independent operations."},
|
||||
{208, "The response is used to avoid enumerating the internal members of multiple bindings to the same "
|
||||
"collection repeatedly."},
|
||||
{226, "The server has fulfilled a GET request for the resource, and the response is a representation of the "
|
||||
"result of one or more instance-manipulations applied to the current instance."},
|
||||
|
||||
{300, "The requested resource corresponds to any one of a set of representations, each with its own specific location, and agent-driven negotiation information is being provided so that the user (or user agent) can select a preferred representation and redirect its request to that location."},
|
||||
{301, "The requested resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs."},
|
||||
{300, "The requested resource corresponds to any one of a set of representations, each with its own specific "
|
||||
"location, and agent-driven negotiation information is being provided so that the user (or user agent) "
|
||||
"can select a preferred representation and redirect its request to that location."},
|
||||
{301, "The requested resource has been assigned a new permanent URI and any future references to this resource "
|
||||
"SHOULD use one of the returned URIs."},
|
||||
{302, "The requested resource resides temporarily under a different URI."},
|
||||
{303, "The response to the request can be found under a different URI and SHOULD be retrieved using a GET method on that resource."},
|
||||
{303, "The response to the request can be found under a different URI and SHOULD be retrieved using a GET "
|
||||
"method on that resource."},
|
||||
{304, "The request has been fulfilled and the requested resource has not been modified."},
|
||||
{305, "The requested resource MUST be accessed through the proxy given by the Location field."},
|
||||
{307, "The requested resource resides temporarily under a different URI."},
|
||||
{308, "The requested resource has been assigned a new permanent URI and any future references to this resource ought to use one of the enclosed URIs."},
|
||||
{308, "The requested resource has been assigned a new permanent URI and any future references to this resource "
|
||||
"ought to use one of the enclosed URIs."},
|
||||
|
||||
{400, "The request could not be understood by the server due to malformed syntax."},
|
||||
{401, "The request requires user authentication."},
|
||||
{403, "The server understood the request, but is refusing to fulfill it."},
|
||||
{404, "The server has not found anything matching the Request-URI."},
|
||||
{405, "The method specified in the Request-Line is not allowed for the resource identified by the Request-URI."},
|
||||
{406, "The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request."},
|
||||
{405, "The method specified in the Request-Line is not allowed for the resource identified by the "
|
||||
"Request-URI."},
|
||||
{406, "The resource identified by the request is only capable of generating response entities which have "
|
||||
"content characteristics not acceptable according to the accept headers sent in the request."},
|
||||
{407, "The request requires user authentication on the proxy."},
|
||||
{408, "The client did not produce a request within the time that the server was prepared to wait."},
|
||||
{409, "The request could not be completed due to a conflict with the current state of the resource."},
|
||||
{410, "The requested resource is no longer available at the server and no forwarding address is known."},
|
||||
{411, "The server refuses to accept the request without a defined Content-Length."},
|
||||
{412, "The precondition given in one or more of the request-header fields evaluated to false when it was tested on the server."},
|
||||
{413, "The server is refusing to process a request because the request entity is larger than the server is willing or able to process."},
|
||||
{414, "The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret."},
|
||||
{415, "The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method."},
|
||||
{416, "None of the ranges in the requests Range header field overlap the current extent of the selected resource or that the set of ranges requested has been rejected due to invalid ranges or an excessive request of small or overlapping ranges."},
|
||||
{417, "The expectation given in an Expect request-header field could not be met by this server, or, if the server is a proxy, the server has unambiguous evidence that the request could not be met by the next-hop server."},
|
||||
{412, "The precondition given in one or more of the request-header fields evaluated to false when it was "
|
||||
"tested on the server."},
|
||||
{413, "The server is refusing to process a request because the request entity is larger than the server is "
|
||||
"willing or able to process."},
|
||||
{414, "The server is refusing to service the request because the Request-URI is longer than the server is "
|
||||
"willing to interpret."},
|
||||
{415, "The server is refusing to service the request because the entity of the request is in a format not "
|
||||
"supported by the requested resource for the requested method."},
|
||||
{416, "None of the ranges in the requests Range header field overlap the current extent of the selected "
|
||||
"resource or that the set of ranges requested has been rejected due to invalid ranges or an excessive "
|
||||
"request of small or overlapping ranges."},
|
||||
{417, "The expectation given in an Expect request-header field could not be met by this server, or, if the "
|
||||
"server is a proxy, the server has unambiguous evidence that the request could not be met by the "
|
||||
"next-hop server."},
|
||||
{421, "The server is not able to produce a response. The client MAY retry the request over a different "
|
||||
"connection."},
|
||||
{422, "The server understands the content type of the request content, and the syntax of the request content "
|
||||
"is correct, but the server was unable to process the contained information."},
|
||||
{423, "The source or destination resource of a method is locked."},
|
||||
{424, "The method could not be performed on the resource because the requested action depended on another "
|
||||
"action and that action failed."},
|
||||
{425, "The server is unwilling to risk processing a request that might be replayed."},
|
||||
{426, "The server refuses to perform the request using the current protocol but might be willing to do so "
|
||||
"after the client upgrades to a different protocol. The server MUST send an Upgrade header field to"
|
||||
"indicate the required protocol(s)."},
|
||||
{428, "The origin server requires the request to be conditional. By requiring requests to be conditional, the "
|
||||
"server can assure that clients are working with the correct copies and thus avoiding a lost update."},
|
||||
{429, "The client has sent too many requests in a given amount of time."},
|
||||
{431, "The server is unwilling to process the request because its header fields are too large. The request MAY "
|
||||
"be resubmitted after reducing the size of the request header fields."},
|
||||
{451, "The server is denying access to the resource as a consequence of a legal demand."},
|
||||
|
||||
{500, "The server encountered an unexpected condition which prevented it from fulfilling the request."},
|
||||
{501, "The server does not support the functionality required to fulfill the request."},
|
||||
{502, "The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request."},
|
||||
{503, "The server is currently unable to handle the request due to a temporary overloading or maintenance of the server."},
|
||||
{504, "The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI or some other auxiliary server it needed to access in attempting to complete the request."},
|
||||
{505, "The server does not support, or refuses to support, the HTTP protocol version that was used in the request message."}
|
||||
{502, "The server, while acting as a gateway or proxy, received an invalid response from the upstream server "
|
||||
"it accessed in attempting to fulfill the request."},
|
||||
{503, "The server is currently unable to handle the request due to a temporary overloading or maintenance of "
|
||||
"the server."},
|
||||
{504, "The server, while acting as a gateway or proxy, did not receive a timely response from the upstream "
|
||||
"server specified by the URI or some other auxiliary server it needed to access in attempting to "
|
||||
"complete the request."},
|
||||
{505, "The server does not support, or refuses to support, the HTTP protocol version that was used in the "
|
||||
"request message."},
|
||||
{506, "The server has an internal configuration error: the chosen variant resource is configured to engage in "
|
||||
"transparent content negotiation itself, and is therefore not a proper end point in the negotiation "
|
||||
"process."},
|
||||
{507, "The method could not be performed on the resource because the server is unable to store the "
|
||||
"representation needed to successfully complete the request. This condition is considered to be "
|
||||
"temporary."},
|
||||
{508, "The server terminated an operation because it encountered an infinite loop while processing the "
|
||||
"request."},
|
||||
{511, "The client needs to authenticate to gain network access. The response representation SHOULD contain a "
|
||||
"link to a resource that allows the user to submit credentials."},
|
||||
};
|
||||
|
||||
const int http_statuses_size = sizeof(http_statuses) / sizeof(http_status);
|
||||
|
118
src/lib/proxy.c
118
src/lib/proxy.c
@@ -13,6 +13,7 @@
|
||||
#include "utils.h"
|
||||
#include "compress.h"
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <string.h>
|
||||
@@ -24,7 +25,9 @@
|
||||
|
||||
static SSL_CTX *proxy_ctx = NULL;
|
||||
static proxy_ctx_t *proxies = NULL;
|
||||
static sem_t *available = NULL;
|
||||
static sem_t lock;
|
||||
static int num_proxy_hosts = -1;
|
||||
|
||||
int proxy_preload(void) {
|
||||
int n = 0;
|
||||
@@ -45,42 +48,53 @@ int proxy_preload(void) {
|
||||
proxy_unload();
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(proxies, 0, n * MAX_PROXY_CNX_PER_HOST * sizeof(proxy_ctx_t));
|
||||
|
||||
available = malloc(n * sizeof(*available));
|
||||
if (available == NULL) {
|
||||
proxy_unload();
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (sem_init(&available[i], 0, MAX_PROXY_CNX_PER_HOST) != 0) {
|
||||
proxy_unload();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (sem_init(&lock, 0, 1) != 0) {
|
||||
proxy_unload();
|
||||
return -1;
|
||||
}
|
||||
|
||||
num_proxy_hosts = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void proxy_unload(void) {
|
||||
int e = errno;
|
||||
sem_destroy(&lock);
|
||||
SSL_CTX_free(proxy_ctx);
|
||||
sem_destroy(&lock);
|
||||
if (num_proxy_hosts != -1) {
|
||||
for (int i = 0; i < num_proxy_hosts; i++) {
|
||||
sem_destroy(&available[i]);
|
||||
}
|
||||
}
|
||||
free(available);
|
||||
free(proxies);
|
||||
errno = e;
|
||||
}
|
||||
|
||||
void proxy_close_all(void) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) {
|
||||
host_config_t *hc = &config.hosts[i];
|
||||
if (hc->type == CONFIG_TYPE_UNSET) break;
|
||||
if (hc->type != CONFIG_TYPE_REVERSE_PROXY) continue;
|
||||
n++;
|
||||
}
|
||||
|
||||
proxy_ctx_t *ptr = proxies;
|
||||
for (int i = 0; i < MAX_PROXY_CNX_PER_HOST * n; i++, ptr++) {
|
||||
for (int i = 0; i < MAX_PROXY_CNX_PER_HOST * num_proxy_hosts; i++, ptr++) {
|
||||
if (ptr->initialized)
|
||||
proxy_close(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static proxy_ctx_t *proxy_get_by_conf(host_config_t *conf) {
|
||||
proxy_ctx_t *proxy_get_by_conf(host_config_t *conf) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) {
|
||||
host_config_t *hc = &config.hosts[i];
|
||||
@@ -90,11 +104,21 @@ static proxy_ctx_t *proxy_get_by_conf(host_config_t *conf) {
|
||||
n++;
|
||||
}
|
||||
|
||||
try_again:
|
||||
try_again_1:
|
||||
if (sem_wait(&available[n]) != 0) {
|
||||
if (errno == EINTR) {
|
||||
goto try_again_1;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
try_again_2:
|
||||
if (sem_wait(&lock) != 0) {
|
||||
if (errno == EINTR) {
|
||||
goto try_again;
|
||||
goto try_again_2;
|
||||
} else {
|
||||
sem_post(&available[n]);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -109,9 +133,16 @@ static proxy_ctx_t *proxy_get_by_conf(host_config_t *conf) {
|
||||
}
|
||||
|
||||
sem_post(&lock);
|
||||
sem_post(&available[n]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void proxy_unlock_ctx(proxy_ctx_t *ctx) {
|
||||
int n = (int) ((ctx - proxies) / MAX_PROXY_CNX_PER_HOST);
|
||||
ctx->in_use = 0;
|
||||
sem_post(&available[n]);
|
||||
}
|
||||
|
||||
int proxy_request_header(http_req *req, sock *sock) {
|
||||
char buf1[256], buf2[256];
|
||||
int p_len;
|
||||
@@ -263,7 +294,7 @@ int proxy_response_header(http_req *req, http_res *res, host_config_t *conf) {
|
||||
}
|
||||
|
||||
int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_status_ctx *ctx, host_config_t *conf, sock *client, http_status *custom_status, char *err_msg) {
|
||||
char buffer[CHUNK_SIZE];
|
||||
char buffer[CHUNK_SIZE], err_buf[256];
|
||||
const char *connection, *upgrade, *ws_version;
|
||||
long ret;
|
||||
int tries = 0, retry = 0;
|
||||
@@ -299,7 +330,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
||||
if (host_ent == NULL) {
|
||||
host_ent = gethostbyname2(conf->proxy.hostname, AF_INET);
|
||||
if (host_ent == NULL) {
|
||||
res->status = http_get_status(503);
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_REQ;
|
||||
error("Unable to connect to server: Name or service not known");
|
||||
sprintf(err_msg, "Unable to connect to server: Name or service not known.");
|
||||
@@ -324,14 +355,14 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
||||
res->status = http_get_status(504);
|
||||
ctx->origin = SERVER_REQ;
|
||||
} else if (errno == ECONNREFUSED) {
|
||||
res->status = http_get_status(503);
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_REQ;
|
||||
} else {
|
||||
res->status = http_get_status(500);
|
||||
ctx->origin = INTERNAL;
|
||||
}
|
||||
error("Unable to connect to [%s]:%i: %s", buffer, conf->proxy.port, strerror(errno));
|
||||
sprintf(err_msg, "Unable to connect to server: %s.", strerror(errno));
|
||||
error("Unable to connect to [%s]:%i", buffer, conf->proxy.port);
|
||||
sprintf(err_msg, "Unable to connect to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
goto proxy_err;
|
||||
}
|
||||
|
||||
@@ -340,7 +371,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
||||
res->status = http_get_status(500);
|
||||
ctx->origin = INTERNAL;
|
||||
error("Unable to set timeout for reverse proxy socket");
|
||||
sprintf(err_msg, "Unable to set timeout for reverse proxy socket: %s", strerror(errno));
|
||||
sprintf(err_msg, "Unable to set timeout for reverse proxy socket: %s", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
goto proxy_err;
|
||||
}
|
||||
|
||||
@@ -350,17 +381,15 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
||||
SSL_set_connect_state(proxy->proxy.ssl);
|
||||
|
||||
ret = SSL_do_handshake(proxy->proxy.ssl);
|
||||
proxy->proxy._last_ret = ret;
|
||||
proxy->proxy._errno = errno;
|
||||
proxy->proxy._ssl_error = ERR_get_error();
|
||||
proxy->proxy.enc = 1;
|
||||
if (ret < 0) {
|
||||
if (ret != 1) {
|
||||
error_ssl(SSL_get_error(proxy->proxy.ssl, (int) ret));
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_REQ;
|
||||
error("Unable to perform handshake: %s", sock_strerror(&proxy->proxy));
|
||||
sprintf(err_msg, "Unable to perform handshake: %s.", sock_strerror(&proxy->proxy));
|
||||
error("Unable to perform handshake");
|
||||
sprintf(err_msg, "Unable to perform handshake: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
goto proxy_err;
|
||||
}
|
||||
proxy->proxy.enc = 1;
|
||||
}
|
||||
|
||||
proxy->initialized = 1;
|
||||
@@ -395,8 +424,8 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
||||
if (ret < 0) {
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_REQ;
|
||||
error("Unable to send request to server (1): %s", sock_strerror(&proxy->proxy));
|
||||
sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&proxy->proxy));
|
||||
error("Unable to send request to server (1)");
|
||||
sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
retry = tries < 4;
|
||||
goto proxy_err;
|
||||
}
|
||||
@@ -416,15 +445,15 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
||||
if (ret == -1) {
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_REQ;
|
||||
error("Unable to send request to server (2): %s", sock_strerror(&proxy->proxy));
|
||||
sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&proxy->proxy));
|
||||
error("Unable to send request to server (2)");
|
||||
sprintf(err_msg, "Unable to send request to server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
retry = tries < 4;
|
||||
goto proxy_err;
|
||||
} else if (ret == -2) {
|
||||
res->status = http_get_status(400);
|
||||
ctx->origin = CLIENT_REQ;
|
||||
error("Unable to receive request from client: %s", sock_strerror(client));
|
||||
sprintf(err_msg, "Unable to receive request from client: %s.", sock_strerror(client));
|
||||
error("Unable to receive request from client");
|
||||
sprintf(err_msg, "Unable to receive request from client: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
return -1;
|
||||
}
|
||||
res->status = http_get_status(500);
|
||||
@@ -435,18 +464,16 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
||||
|
||||
ret = sock_recv(&proxy->proxy, buffer, sizeof(buffer), MSG_PEEK);
|
||||
if (ret <= 0) {
|
||||
int enc_err = sock_enc_error(&proxy->proxy);
|
||||
if (errno == EAGAIN || errno == EINPROGRESS || enc_err == SSL_ERROR_WANT_READ ||
|
||||
enc_err == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
int e_sys = error_get_sys(), e_ssl = error_get_ssl();
|
||||
if (e_sys == EAGAIN || e_sys == EINPROGRESS || e_ssl == SSL_ERROR_WANT_READ || e_ssl == SSL_ERROR_WANT_WRITE) {
|
||||
res->status = http_get_status(504);
|
||||
ctx->origin = SERVER_RES;
|
||||
} else {
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_RES;
|
||||
}
|
||||
error("Unable to receive response from server: %s", sock_strerror(&proxy->proxy));
|
||||
sprintf(err_msg, "Unable to receive response from server: %s.", sock_strerror(&proxy->proxy));
|
||||
error("Unable to receive response from server");
|
||||
sprintf(err_msg, "Unable to receive response from server: %s.", error_str(errno, err_buf, sizeof(err_buf)));
|
||||
retry = tries < 4;
|
||||
goto proxy_err;
|
||||
}
|
||||
@@ -494,7 +521,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
||||
res->status = http_get_status(status_code);
|
||||
if (res->status == NULL && status_code >= 100 && status_code <= 999) {
|
||||
custom_status->code = status_code;
|
||||
strcpy(custom_status->type, "");
|
||||
custom_status->type = 0;
|
||||
snprintf(custom_status->msg, sizeof(custom_status->msg), "%.*s",
|
||||
(int) (strchr(ptr, '\r') - ptr - 13), ptr + 13);
|
||||
res->status = custom_status;
|
||||
@@ -506,8 +533,7 @@ int proxy_init(proxy_ctx_t **proxy_ptr, http_req *req, http_res *res, http_statu
|
||||
goto proxy_err;
|
||||
}
|
||||
} else {
|
||||
ret = http_parse_header_field(&res->hdr, ptr, pos0, 0);
|
||||
if (ret != 0) {
|
||||
if (http_parse_header_field(&res->hdr, ptr, pos0, 0) != 0) {
|
||||
res->status = http_get_status(502);
|
||||
ctx->origin = SERVER_RES;
|
||||
error("Unable to parse header");
|
||||
@@ -565,7 +591,7 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int
|
||||
if (ret == -1) {
|
||||
error("Unable to receive from server: Malformed chunk header");
|
||||
} else {
|
||||
error("Unable to receive from server: %s", sock_strerror(&proxy->proxy));
|
||||
error("Unable to receive from server");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -585,7 +611,7 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int
|
||||
unsigned long avail_in, avail_out;
|
||||
ret = sock_recv(&proxy->proxy, buffer, CHUNK_SIZE < (len_to_send - snd_len) ? CHUNK_SIZE : len_to_send - snd_len, 0);
|
||||
if (ret <= 0) {
|
||||
error("Unable to receive from server: %s", sock_strerror(&proxy->proxy));
|
||||
error("Unable to receive from server");
|
||||
break;
|
||||
}
|
||||
len = ret;
|
||||
@@ -616,7 +642,7 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int
|
||||
if (flags & PROXY_CHUNKED) ret = sock_send(client, "\r\n", 2, 0);
|
||||
if (ret <= 0) {
|
||||
err:
|
||||
error("Unable to send: %s", sock_strerror(client));
|
||||
error("Unable to send");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -633,7 +659,7 @@ int proxy_send(proxy_ctx_t *proxy, sock *client, unsigned long len_to_send, int
|
||||
if (flags & PROXY_CHUNKED) {
|
||||
ret = sock_send(client, "0\r\n\r\n", 5, 0);
|
||||
if (ret <= 0) {
|
||||
error("Unable to send: %s", sock_strerror(client));
|
||||
error("Unable to send");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@@ -34,6 +34,10 @@ void proxy_unload(void);
|
||||
|
||||
void proxy_close_all(void);
|
||||
|
||||
proxy_ctx_t *proxy_get_by_conf(host_config_t *conf);
|
||||
|
||||
void proxy_unlock_ctx(proxy_ctx_t *ctx);
|
||||
|
||||
int proxy_request_header(http_req *req, sock *sock);
|
||||
|
||||
int proxy_response_header(http_req *req, http_res *res, host_config_t *conf);
|
||||
|
@@ -8,58 +8,15 @@
|
||||
|
||||
#include "sock.h"
|
||||
#include "utils.h"
|
||||
#include "error.h"
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <errno.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
int sock_enc_error(sock *s) {
|
||||
return (int) s->enc ? SSL_get_error(s->ssl, (int) s->_last_ret) : 0;
|
||||
}
|
||||
|
||||
const char *sock_strerror(sock *s) {
|
||||
// FIXME sock_strerror not Thread Safe!
|
||||
// (and ugly)
|
||||
errno = 0;
|
||||
if (s->_last_ret == 0) {
|
||||
return "closed";
|
||||
} else if (s->enc) {
|
||||
if (s->_last_ret > 0) {
|
||||
return NULL;
|
||||
}
|
||||
const char *err1 = ERR_reason_error_string(s->_ssl_error);
|
||||
const char *err2 = strerror(s->_errno);
|
||||
switch (sock_enc_error(s)) {
|
||||
case SSL_ERROR_NONE:
|
||||
return NULL;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
return "closed";
|
||||
case SSL_ERROR_WANT_READ:
|
||||
return "want read";
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
return "want write";
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
return "want connect";
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
return "want accept";
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
return "want x509 lookup";
|
||||
case SSL_ERROR_SYSCALL:
|
||||
return ((s->_ssl_error == 0) ? ((s->_last_ret == 0) ? "protocol violation" : err2) : err1);
|
||||
case SSL_ERROR_SSL:
|
||||
return err1;
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
} else {
|
||||
return strerror(s->_errno);
|
||||
}
|
||||
}
|
||||
|
||||
int sock_set_timeout_micros(sock *s, long recv_micros, long send_micros) {
|
||||
int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros) {
|
||||
struct timeval recv_to = {.tv_sec = recv_micros / 1000000, .tv_usec = recv_micros % 1000000},
|
||||
send_to = {.tv_sec = send_micros / 1000000, .tv_usec = send_micros % 1000000};
|
||||
|
||||
@@ -72,21 +29,37 @@ int sock_set_timeout_micros(sock *s, long recv_micros, long send_micros) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sock_set_timeout(sock *s, int sec) {
|
||||
return sock_set_timeout_micros(s, sec * 1000000L, sec * 1000000L);
|
||||
int sock_set_socket_timeout(sock *s, double sec) {
|
||||
return sock_set_socket_timeout_micros(s, (long) (sec * 1000000L), (long) (sec * 1000000L));
|
||||
}
|
||||
|
||||
int sock_set_timeout_micros(sock *s, long micros) {
|
||||
if (micros < 0)
|
||||
return -1;
|
||||
|
||||
s->timeout_us = micros;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sock_set_timeout(sock *s, double sec) {
|
||||
return sock_set_timeout_micros(s, (long) (sec * 1000000));
|
||||
}
|
||||
|
||||
long sock_send(sock *s, void *buf, unsigned long len, int flags) {
|
||||
long ret;
|
||||
if (s->enc) {
|
||||
ret = SSL_write(s->ssl, buf, (int) len);
|
||||
s->_ssl_error = ERR_get_error();
|
||||
if (ret <= 0) error_ssl(SSL_get_error(s->ssl, (int) ret));
|
||||
} else {
|
||||
ret = send(s->socket, buf, len, flags);
|
||||
}
|
||||
s->_last_ret = ret;
|
||||
s->_errno = errno;
|
||||
return ret >= 0 ? ret : -1;
|
||||
|
||||
if (ret >= 0) {
|
||||
s->ts_last = clock_micros();
|
||||
return ret;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
long sock_recv(sock *s, void *buf, unsigned long len, int flags) {
|
||||
@@ -94,13 +67,17 @@ long sock_recv(sock *s, void *buf, unsigned long len, int flags) {
|
||||
if (s->enc) {
|
||||
int (*func)(SSL*, void*, int) = (flags & MSG_PEEK) ? SSL_peek : SSL_read;
|
||||
ret = func(s->ssl, buf, (int) len);
|
||||
s->_ssl_error = ERR_get_error();
|
||||
if (ret <= 0) error_ssl(SSL_get_error(s->ssl, (int) ret));
|
||||
} else {
|
||||
ret = recv(s->socket, buf, len, flags);
|
||||
}
|
||||
s->_last_ret = ret;
|
||||
s->_errno = errno;
|
||||
return ret >= 0 ? ret : -1;
|
||||
|
||||
if (ret >= 0) {
|
||||
s->ts_last = clock_micros();
|
||||
return ret;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigned long len) {
|
||||
@@ -142,7 +119,7 @@ long sock_splice_chunked(sock *dst, sock *src, void *buf, unsigned long buf_len)
|
||||
int sock_close(sock *s) {
|
||||
int e = errno;
|
||||
if (s->enc && s->ssl != NULL) {
|
||||
if (s->_last_ret >= 0) SSL_shutdown(s->ssl);
|
||||
SSL_shutdown(s->ssl);
|
||||
SSL_free(s->ssl);
|
||||
s->ssl = NULL;
|
||||
}
|
||||
|
@@ -23,18 +23,16 @@ typedef struct {
|
||||
char *addr, *s_addr;
|
||||
SSL_CTX *ctx;
|
||||
SSL *ssl;
|
||||
long _last_ret;
|
||||
int _errno;
|
||||
unsigned long _ssl_error;
|
||||
long ts_start, ts_last, timeout_us;
|
||||
} sock;
|
||||
|
||||
int sock_enc_error(sock *s);
|
||||
int sock_set_socket_timeout_micros(sock *s, long recv_micros, long send_micros);
|
||||
|
||||
const char *sock_strerror(sock *s);
|
||||
int sock_set_socket_timeout(sock *s, double sec);
|
||||
|
||||
int sock_set_timeout_micros(sock *s, long recv_micros, long send_micros);
|
||||
int sock_set_timeout_micros(sock *s, long micros);
|
||||
|
||||
int sock_set_timeout(sock *s, int sec);
|
||||
int sock_set_timeout(sock *s, double sec);
|
||||
|
||||
long sock_send(sock *s, void *buf, unsigned long len, int flags);
|
||||
|
||||
|
@@ -211,7 +211,6 @@ long clock_micros(void) {
|
||||
return time.tv_sec * 1000000 + time.tv_nsec / 1000;
|
||||
}
|
||||
|
||||
|
||||
long clock_cpu(void) {
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time);
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "logger.h"
|
||||
#include "lib/utils.h"
|
||||
#include "lib/error.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
@@ -59,8 +60,8 @@ static const char *level_keywords[] = {
|
||||
|
||||
static void err(const char *restrict msg) {
|
||||
char err_buf[64];
|
||||
strerror_r(errno, err_buf, sizeof(err_buf));
|
||||
fprintf(stderr, ERR_STR LOG_PREFIX " %s: %s" CLR_STR "\n", "logger", level_keywords[LOG_CRITICAL], msg, err_buf);
|
||||
fprintf(stderr, ERR_STR LOG_PREFIX " %s: %s" CLR_STR "\n", "logger",
|
||||
level_keywords[LOG_CRITICAL], msg, error_str(errno, err_buf, sizeof(err_buf)));
|
||||
}
|
||||
|
||||
void logmsgf(log_lvl_t level, const char *restrict format, ...) {
|
||||
@@ -71,8 +72,7 @@ void logmsgf(log_lvl_t level, const char *restrict format, ...) {
|
||||
|
||||
const char *color = (level <= LOG_ERROR) ? ERR_STR : ((level <= LOG_WARNING) ? WRN_STR : "");
|
||||
if (errno != 0) {
|
||||
strerror_r(errno, err_buf, sizeof(err_buf));
|
||||
snprintf(buf, sizeof(buf), "%s%s: %s" CLR_STR, color, format, err_buf);
|
||||
snprintf(buf, sizeof(buf), "%s%s: %s" CLR_STR, color, format, error_str(errno, err_buf, sizeof(err_buf)));
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s%s" CLR_STR, color, format);
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include "workers.h"
|
||||
#include "worker/func.h"
|
||||
#include "lib/list.h"
|
||||
#include "lib/utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
@@ -98,6 +99,10 @@ static void accept_cb(void *arg) {
|
||||
|
||||
client->socket = client_fd;
|
||||
client->enc = (i == 1);
|
||||
client->ts_start = clock_micros();
|
||||
client->ts_last = client->ts_start;
|
||||
client_ctx->cnx_s = client->ts_start;
|
||||
client_ctx->cnx_e = -1, client_ctx->req_s = -1, client_ctx->req_e = -1, client_ctx->res_ts = -1;
|
||||
|
||||
tcp_accept(client_ctx);
|
||||
}
|
||||
@@ -301,7 +306,7 @@ int main(int argc, char *const argv[]) {
|
||||
workers_init();
|
||||
|
||||
for (int i = 0; i < NUM_SOCKETS; i++) {
|
||||
async_fd(sockets[i], ASYNC_WAIT_READ, ASYNC_KEEP, &sockets[i], accept_cb, accept_err_cb);
|
||||
async_fd(sockets[i], ASYNC_WAIT_READ, ASYNC_KEEP, &sockets[i], accept_cb, accept_err_cb, accept_err_cb);
|
||||
}
|
||||
|
||||
notice("Ready to accept connections");
|
||||
|
@@ -101,7 +101,7 @@ static int fastcgi_handler_1(client_ctx_t *ctx, fastcgi_cnx_t *fcgi_cnx) {
|
||||
http_remove_header_field(&res->hdr, "Status", HTTP_REMOVE_ALL);
|
||||
if (res->status == NULL && status_code >= 100 && status_code <= 999) {
|
||||
ctx->custom_status.code = status_code;
|
||||
strcpy(ctx->custom_status.type, "");
|
||||
ctx->custom_status.type = 0;
|
||||
strcpy(ctx->custom_status.msg, status_hdr + 4);
|
||||
res->status = &ctx->custom_status;
|
||||
} else if (res->status == NULL) {
|
||||
|
@@ -58,6 +58,8 @@ int respond(client_ctx_t *ctx);
|
||||
|
||||
void request_complete(client_ctx_t *ctx);
|
||||
|
||||
void timeout_request(client_ctx_t *ctx);
|
||||
|
||||
void tcp_close(client_ctx_t *ctx);
|
||||
|
||||
void proxy_close(proxy_ctx_t *ctx);
|
||||
|
@@ -22,15 +22,20 @@ static int proxy_handler_2(client_ctx_t *ctx);
|
||||
void proxy_handler_func(client_ctx_t *ctx) {
|
||||
logger_set_prefix("[%s%*s%s]%s", BLD_STR, INET6_ADDRSTRLEN, ctx->req_host, CLR_STR, ctx->log_prefix);
|
||||
|
||||
// TODO handle 1xx responses
|
||||
|
||||
int ret = proxy_handler_1(ctx);
|
||||
respond(ctx);
|
||||
|
||||
if (ret == 1) {
|
||||
|
||||
proxy_unlock_ctx(ctx->proxy);
|
||||
ctx->proxy = NULL;
|
||||
} else if (ctx->use_proxy == 0) {
|
||||
proxy_close(ctx->proxy);
|
||||
} else if (ctx->use_proxy == 1) {
|
||||
proxy_handler_2(ctx);
|
||||
proxy_unlock_ctx(ctx->proxy);
|
||||
ctx->proxy = NULL;
|
||||
} else if (ctx->use_proxy == 2) {
|
||||
// WebSocket
|
||||
ws_handle_connection(ctx);
|
||||
@@ -78,11 +83,11 @@ static int proxy_handler_1(client_ctx_t *ctx) {
|
||||
const char *content_length_f = http_get_header_field(&res->hdr, "Content-Length");
|
||||
const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding");
|
||||
if (content_encoding == NULL && (
|
||||
strcmp(ctx->req.method, "HEAD") == 0 ||
|
||||
content_length_f == NULL ||
|
||||
(content_length_f != NULL && strcmp(content_length_f, "0") == 0) ||
|
||||
(content_type != NULL && content_length_f != NULL && strncmp(content_type, "text/html", 9) == 0)))
|
||||
{
|
||||
long content_len = (content_length_f != NULL) ? strtol(content_length_f, NULL, 10) : 0;
|
||||
long content_len = (strcmp(ctx->req.method, "HEAD") != 0 && content_length_f != NULL) ? strtol(content_length_f, NULL, 10) : 0;
|
||||
if (content_len <= sizeof(ctx->msg_content) - 1) {
|
||||
if (status->status != 101) {
|
||||
status->status = res->status->code;
|
||||
@@ -98,6 +103,10 @@ static int proxy_handler_1(client_ctx_t *ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(ctx->req.method, "HEAD") == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding");
|
||||
if (use_proxy && content_encoding == NULL) {
|
||||
@@ -137,8 +146,6 @@ static int proxy_handler_2(client_ctx_t *ctx) {
|
||||
|
||||
int flags = (chunked ? PROXY_CHUNKED : 0) | (ctx->use_proxy & PROXY_COMPRESS);
|
||||
int ret = proxy_send(ctx->proxy, &ctx->socket, len_to_send, flags);
|
||||
ctx->proxy->in_use = 0;
|
||||
ctx->proxy = NULL;
|
||||
|
||||
if (ret < 0) {
|
||||
ctx->c_keep_alive = 0;
|
||||
|
@@ -14,9 +14,11 @@
|
||||
#include "../lib/utils.h"
|
||||
#include "../server.h"
|
||||
#include "../lib/res.h"
|
||||
#include "../lib/error.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
|
||||
static int request_handler(client_ctx_t *ctx);
|
||||
|
||||
@@ -41,15 +43,7 @@ void request_handler_func(client_ctx_t *ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
static int request_handler(client_ctx_t *ctx) {
|
||||
sock *client = &ctx->socket;
|
||||
char *err_msg = ctx->err_msg;
|
||||
|
||||
long ret;
|
||||
char buf0[1024], buf1[1024];
|
||||
|
||||
err_msg[0] = 0;
|
||||
|
||||
static void init_ctx(client_ctx_t *ctx) {
|
||||
ctx->conf = NULL;
|
||||
ctx->file = NULL;
|
||||
ctx->proxy = NULL;
|
||||
@@ -62,24 +56,35 @@ static int request_handler(client_ctx_t *ctx) {
|
||||
ctx->msg_buf_ptr = NULL;
|
||||
ctx->req_host[0] = 0;
|
||||
ctx->err_msg[0] = 0;
|
||||
ctx->req_s = ctx->socket.ts_last;
|
||||
|
||||
memset(&ctx->uri, 0, sizeof(ctx->uri));
|
||||
memset(&ctx->req, 0, sizeof(ctx->req));
|
||||
memset(&ctx->res, 0, sizeof(ctx->res));
|
||||
|
||||
http_res *res = &ctx->res;
|
||||
res->status = http_get_status(501);
|
||||
http_init_hdr(&res->hdr);
|
||||
res->hdr.last_field_num = -1;
|
||||
sprintf(res->version, "1.1");
|
||||
|
||||
http_status_ctx *status = &ctx->status;
|
||||
status->status = 0;
|
||||
status->origin = NONE;
|
||||
status->ws_key = NULL;
|
||||
ctx->res.status = http_get_status(501);
|
||||
http_init_hdr(&ctx->res.hdr);
|
||||
ctx->res.hdr.last_field_num = -1;
|
||||
sprintf(ctx->res.version, "1.1");
|
||||
|
||||
ctx->status.status = 0;
|
||||
ctx->status.origin = NONE;
|
||||
ctx->status.ws_key = NULL;
|
||||
}
|
||||
|
||||
static int request_handler(client_ctx_t *ctx) {
|
||||
sock *client = &ctx->socket;
|
||||
char *err_msg = ctx->err_msg;
|
||||
http_res *res = &ctx->res;
|
||||
|
||||
long ret;
|
||||
char buf0[1024], buf1[1024];
|
||||
|
||||
ctx->req_s = clock_micros();
|
||||
|
||||
init_ctx(ctx);
|
||||
|
||||
http_add_header_field(&res->hdr, "Date", http_get_date(buf0, sizeof(buf0)));
|
||||
http_add_header_field(&res->hdr, "Server", SERVER_STR);
|
||||
/*if (ret <= 0) {
|
||||
@@ -94,20 +99,11 @@ static int request_handler(client_ctx_t *ctx) {
|
||||
ret = http_receive_request(client, req);
|
||||
if (ret != 0) {
|
||||
ctx->c_keep_alive = 0;
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
} else if (ret == 1) {
|
||||
sprintf(err_msg, "Unable to parse http header: Invalid header format.");
|
||||
} else if (ret == 2) {
|
||||
sprintf(err_msg, "Unable to parse http header: Invalid method.");
|
||||
} else if (ret == 3) {
|
||||
sprintf(err_msg, "Unable to parse http header: Invalid version.");
|
||||
} else if (ret == 4) {
|
||||
sprintf(err_msg, "Unable to parse http header: Header contains illegal characters.");
|
||||
} else if (ret == 5) {
|
||||
sprintf(err_msg, "Unable to parse http header: End of header not found.");
|
||||
}
|
||||
res->status = http_get_status(400);
|
||||
error("Unable to receive http header");
|
||||
sprintf(err_msg, "Unable to receive http header: %s.", error_str(errno, buf0, sizeof(buf0)));
|
||||
int err = error_get_http();
|
||||
res->status = http_get_status(err == HTTP_ERROR_URI_TOO_LONG ? 414 : (err == HTTP_ERROR_TOO_MANY_HEADER_FIELDS ? 431 : 400));
|
||||
errno = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -155,7 +151,7 @@ static int request_handler(client_ctx_t *ctx) {
|
||||
for (int i = 0; i < sizeof(resources) / sizeof(res_t); i++) {
|
||||
const res_t *r = &resources[i];
|
||||
if (strcmp(req->uri + 14, r->name) == 0) {
|
||||
res->status = http_get_status(200);
|
||||
res->status = http_get_status(203);
|
||||
http_add_header_field(&res->hdr, "Content-Type", r->type);
|
||||
http_add_header_field(&res->hdr, "Cache-Control", "public, max-age=86400");
|
||||
ctx->msg_buf = (char *) r->content;
|
||||
@@ -169,10 +165,8 @@ static int request_handler(client_ctx_t *ctx) {
|
||||
|
||||
ctx->conf = get_host_config(ctx->req_host);
|
||||
if (ctx->conf == NULL) {
|
||||
info("Unknown host, redirecting to default");
|
||||
res->status = http_get_status(307);
|
||||
sprintf(buf0, "https://%s%s", DEFAULT_HOST, req->uri);
|
||||
http_add_header_field(&res->hdr, "Location", buf0);
|
||||
res->status = http_get_status(421);
|
||||
strcpy(ctx->err_msg, "The requested host name is not configured on the server.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -351,7 +345,7 @@ int respond(client_ctx_t *ctx) {
|
||||
if (ctx->msg_buf != NULL) {
|
||||
ret = sock_send(client, ctx->msg_buf, ctx->content_length, 0);
|
||||
if (ret <= 0) {
|
||||
error("Unable to send: %s", sock_strerror(client));
|
||||
error("Unable to send");
|
||||
}
|
||||
} else if (ctx->file != NULL) {
|
||||
unsigned long len, snd_len = 0;
|
||||
@@ -362,7 +356,7 @@ int respond(client_ctx_t *ctx) {
|
||||
}
|
||||
ret = sock_send(client, buffer, len, feof(ctx->file) ? 0 : MSG_MORE);
|
||||
if (ret <= 0) {
|
||||
error("Unable to send: %s", sock_strerror(client));
|
||||
error("Unable to send");
|
||||
break;
|
||||
}
|
||||
snd_len += ret;
|
||||
@@ -389,3 +383,14 @@ void request_complete(client_ctx_t *ctx) {
|
||||
http_free_req(&ctx->req);
|
||||
http_free_res(&ctx->res);
|
||||
}
|
||||
|
||||
void timeout_request(client_ctx_t *ctx) {
|
||||
init_ctx(ctx);
|
||||
logger_set_prefix("[%*s]%s", INET6_ADDRSTRLEN, ctx->socket.s_addr, ctx->log_prefix);
|
||||
|
||||
ctx->s_keep_alive = 0;
|
||||
ctx->res.status = http_get_status(408);
|
||||
|
||||
respond(ctx);
|
||||
tcp_close(ctx);
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include "../lib/geoip.h"
|
||||
#include "../workers.h"
|
||||
#include "../server.h"
|
||||
#include "../lib/error.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
@@ -63,7 +64,7 @@ static int tcp_acceptor(client_ctx_t *ctx) {
|
||||
sprintf(buf, "dig @%s +short +time=1 -x %s", config.dns_server, ctx->socket.addr);
|
||||
FILE *dig = popen(buf, "r");
|
||||
if (dig == NULL) {
|
||||
error("Unable to start dig: %s", strerror(errno));
|
||||
error("Unable to start dig: %s");
|
||||
goto dig_err;
|
||||
}
|
||||
unsigned long read = fread(buf, 1, sizeof(buf), dig);
|
||||
@@ -90,7 +91,7 @@ static int tcp_acceptor(client_ctx_t *ctx) {
|
||||
ctx->host[0] != 0 ? ctx->host : "", ctx->host[0] != 0 ? ") " : "",
|
||||
ctx->cc[0] != 0 ? ctx->cc : "N/A");
|
||||
|
||||
if (sock_set_timeout(client, CLIENT_TIMEOUT)) {
|
||||
if (sock_set_socket_timeout(client, 1) != 0 || sock_set_timeout(client, CLIENT_TIMEOUT) != 0) {
|
||||
error("Unable to set timeout for socket");
|
||||
return -1;
|
||||
}
|
||||
@@ -101,13 +102,12 @@ static int tcp_acceptor(client_ctx_t *ctx) {
|
||||
SSL_set_accept_state(client->ssl);
|
||||
|
||||
ret = SSL_accept(client->ssl);
|
||||
client->_last_ret = ret;
|
||||
client->_errno = errno;
|
||||
client->_ssl_error = ERR_get_error();
|
||||
if (ret <= 0) {
|
||||
info("Unable to perform handshake: %s", sock_strerror(client));
|
||||
if (ret != 1) {
|
||||
error_ssl(SSL_get_error(client->ssl, ret));
|
||||
info("Unable to perform handshake");
|
||||
return - 1;
|
||||
}
|
||||
client->ts_last = clock_micros();
|
||||
}
|
||||
|
||||
ctx->req_num = 0;
|
||||
|
@@ -53,7 +53,10 @@ static int handle_request_cb(client_ctx_t *ctx) {
|
||||
|
||||
int handle_request(client_ctx_t *ctx) {
|
||||
if (ctx->c_keep_alive && ctx->s_keep_alive) {
|
||||
return async(&ctx->socket, ASYNC_WAIT_READ, 0, ctx, (void (*)(void *)) handle_request_cb, (void (*)(void *)) tcp_close);
|
||||
return async(&ctx->socket, ASYNC_WAIT_READ, 0, ctx,
|
||||
(void (*)(void *)) handle_request_cb,
|
||||
(void (*)(void *)) timeout_request,
|
||||
(void (*)(void *)) tcp_close);
|
||||
} else {
|
||||
tcp_close(ctx);
|
||||
return 0;
|
||||
@@ -77,5 +80,8 @@ static int ws_handle_frame_cb(ws_ctx_t *ctx) {
|
||||
}
|
||||
|
||||
int ws_handle_frame(ws_ctx_t *ctx) {
|
||||
return async(ctx->socket, ASYNC_WAIT_READ, 0, ctx, (void (*)(void *)) ws_handle_frame_cb, (void (*)(void *)) ws_close);
|
||||
return async(ctx->socket, ASYNC_WAIT_READ, 0, ctx,
|
||||
(void (*)(void *)) ws_handle_frame_cb,
|
||||
(void (*)(void *)) ws_close,
|
||||
(void (*)(void *)) ws_close);
|
||||
}
|
||||
|
Reference in New Issue
Block a user