|
|
|
@@ -81,13 +81,8 @@ static _Thread_local ElArena _tl_arena = {NULL, 0, 0};
|
|
|
|
|
static _Thread_local int _tl_arena_active = 0;
|
|
|
|
|
|
|
|
|
|
/* Binary-safe fs_read length — set by fs_read, consumed by http_send_response.
|
|
|
|
|
* Allows serving PNGs and other binary files without strlen truncation.
|
|
|
|
|
* _tl_fs_read_ptr records WHICH buffer that length belongs to: the length is only
|
|
|
|
|
* valid when the handler returns that exact buffer (a raw file). A handler that
|
|
|
|
|
* fs_reads then builds a different/longer response (e.g. wraps a file in JSON) must
|
|
|
|
|
* be measured by strlen — otherwise the reply is truncated to the file's size. */
|
|
|
|
|
* Allows serving PNGs and other binary files without strlen truncation. */
|
|
|
|
|
static _Thread_local size_t _tl_fs_read_len = 0;
|
|
|
|
|
static _Thread_local const char* _tl_fs_read_ptr = NULL;
|
|
|
|
|
|
|
|
|
|
static void el_arena_track(char* p) {
|
|
|
|
|
if (!_tl_arena_active || !p) return;
|
|
|
|
@@ -1066,7 +1061,6 @@ el_val_t http_post_to_file(el_val_t url, el_val_t body, el_val_t headers_map, el
|
|
|
|
|
|
|
|
|
|
#define HTTP_MAX_CONNS 64
|
|
|
|
|
|
|
|
|
|
typedef el_val_t (*http_handler_fn)(el_val_t method, el_val_t path, el_val_t body);
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
char* name;
|
|
|
|
@@ -1541,12 +1535,20 @@ static void http_send_response(int fd, const char* body) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
SOCKET fd;
|
|
|
|
|
#else
|
|
|
|
|
int fd;
|
|
|
|
|
#endif
|
|
|
|
|
} HttpWorkerArg;
|
|
|
|
|
|
|
|
|
|
static void* http_worker(void* arg) {
|
|
|
|
|
HttpWorkerArg* a = (HttpWorkerArg*)arg;
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
SOCKET fd = a->fd;
|
|
|
|
|
#else
|
|
|
|
|
int fd = a->fd;
|
|
|
|
|
#endif
|
|
|
|
|
free(a);
|
|
|
|
|
char *method = NULL, *path = NULL, *body = NULL;
|
|
|
|
|
if (http_read_request(fd, &method, &path, &body, NULL) == 0) {
|
|
|
|
@@ -1563,15 +1565,11 @@ static void* http_worker(void* arg) {
|
|
|
|
|
const char* rs = EL_CSTR(r);
|
|
|
|
|
/* Copy response out BEFORE arena teardown.
|
|
|
|
|
* For binary files, _tl_fs_read_len holds the real byte count —
|
|
|
|
|
* use memcpy instead of strdup so null bytes are preserved. But only
|
|
|
|
|
* trust that count when the handler returned the fs_read buffer itself;
|
|
|
|
|
* a wrapped/concatenated response must be measured by strlen. */
|
|
|
|
|
int is_fs_body = (_tl_fs_read_len > 0 && rs == _tl_fs_read_ptr);
|
|
|
|
|
size_t rlen = is_fs_body ? _tl_fs_read_len : (rs ? strlen(rs) : 0);
|
|
|
|
|
* use memcpy instead of strdup so null bytes are preserved. */
|
|
|
|
|
size_t rlen = _tl_fs_read_len > 0 ? _tl_fs_read_len : (rs ? strlen(rs) : 0);
|
|
|
|
|
response = malloc(rlen + 1);
|
|
|
|
|
if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; }
|
|
|
|
|
else if (response) { response[0] = '\0'; }
|
|
|
|
|
_tl_fs_read_len = is_fs_body ? rlen : 0; /* length http_send_response should use */
|
|
|
|
|
} else {
|
|
|
|
|
response = el_strdup_persist("el-runtime: no http handler registered");
|
|
|
|
|
}
|
|
|
|
@@ -1582,7 +1580,7 @@ static void* http_worker(void* arg) {
|
|
|
|
|
free(response);
|
|
|
|
|
}
|
|
|
|
|
free(method); free(path); free(body);
|
|
|
|
|
close(fd);
|
|
|
|
|
el_closesocket(fd);
|
|
|
|
|
/* release a slot */
|
|
|
|
|
pthread_mutex_lock(&_http_conn_mu);
|
|
|
|
|
_http_conn_active--;
|
|
|
|
@@ -1619,7 +1617,11 @@ el_val_t http_serve(el_val_t port, el_val_t handler) {
|
|
|
|
|
while (1) {
|
|
|
|
|
struct sockaddr_in6 cli;
|
|
|
|
|
socklen_t clen = sizeof(cli);
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
SOCKET cfd = accept(sock, (struct sockaddr*)&cli, &clen);
|
|
|
|
|
#else
|
|
|
|
|
int cfd = accept(sock, (struct sockaddr*)&cli, &clen);
|
|
|
|
|
#endif
|
|
|
|
|
if (cfd < 0) {
|
|
|
|
|
if (errno == EINTR) continue;
|
|
|
|
|
perror("accept"); break;
|
|
|
|
@@ -1665,8 +1667,6 @@ el_val_t http_serve(el_val_t port, el_val_t handler) {
|
|
|
|
|
* separate active-handler slot, separate dlsym fallback. Mixing v1 and v2
|
|
|
|
|
* handlers in the same process is fine — they don't share the active slot. */
|
|
|
|
|
|
|
|
|
|
typedef el_val_t (*http_handler4_fn)(el_val_t method, el_val_t path,
|
|
|
|
|
el_val_t headers_map, el_val_t body);
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
char* name;
|
|
|
|
@@ -1802,7 +1802,11 @@ static el_val_t http_build_headers_map(const char* hdr_block) {
|
|
|
|
|
|
|
|
|
|
static void* http_worker_v2(void* arg) {
|
|
|
|
|
HttpWorkerArg* a = (HttpWorkerArg*)arg;
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
SOCKET fd = a->fd;
|
|
|
|
|
#else
|
|
|
|
|
int fd = a->fd;
|
|
|
|
|
#endif
|
|
|
|
|
free(a);
|
|
|
|
|
char *method = NULL, *path = NULL, *body = NULL, *hdr_block = NULL;
|
|
|
|
|
if (http_read_request(fd, &method, &path, &body, &hdr_block) == 0) {
|
|
|
|
@@ -1815,14 +1819,10 @@ static void* http_worker_v2(void* arg) {
|
|
|
|
|
el_val_t hmap = http_build_headers_map(hdr_block ? hdr_block : "");
|
|
|
|
|
el_val_t r = h(EL_STR(dispatch_method), EL_STR(path), hmap, EL_STR(body));
|
|
|
|
|
const char* rs = EL_CSTR(r);
|
|
|
|
|
/* Only trust fs_read's byte count when the handler returned that exact
|
|
|
|
|
* buffer (raw binary file); otherwise measure the built response by strlen. */
|
|
|
|
|
int is_fs_body = (_tl_fs_read_len > 0 && rs == _tl_fs_read_ptr);
|
|
|
|
|
size_t rlen = is_fs_body ? _tl_fs_read_len : (rs ? strlen(rs) : 0);
|
|
|
|
|
size_t rlen = _tl_fs_read_len > 0 ? _tl_fs_read_len : (rs ? strlen(rs) : 0);
|
|
|
|
|
response = malloc(rlen + 1);
|
|
|
|
|
if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; }
|
|
|
|
|
else if (response) { response[0] = '\0'; }
|
|
|
|
|
_tl_fs_read_len = is_fs_body ? rlen : 0; /* length http_send_response should use */
|
|
|
|
|
el_release(hmap);
|
|
|
|
|
} else {
|
|
|
|
|
response = el_strdup_persist(
|
|
|
|
@@ -1836,7 +1836,7 @@ static void* http_worker_v2(void* arg) {
|
|
|
|
|
free(response);
|
|
|
|
|
}
|
|
|
|
|
free(method); free(path); free(body); free(hdr_block);
|
|
|
|
|
close(fd);
|
|
|
|
|
el_closesocket(fd);
|
|
|
|
|
pthread_mutex_lock(&_http_conn_mu);
|
|
|
|
|
_http_conn_active--;
|
|
|
|
|
pthread_cond_signal(&_http_conn_cv);
|
|
|
|
@@ -1873,7 +1873,11 @@ el_val_t http_serve_v2(el_val_t port, el_val_t handler) {
|
|
|
|
|
while (1) {
|
|
|
|
|
struct sockaddr_in6 cli;
|
|
|
|
|
socklen_t clen = sizeof(cli);
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
SOCKET cfd = accept(sock, (struct sockaddr*)&cli, &clen);
|
|
|
|
|
#else
|
|
|
|
|
int cfd = accept(sock, (struct sockaddr*)&cli, &clen);
|
|
|
|
|
#endif
|
|
|
|
|
if (cfd < 0) {
|
|
|
|
|
if (errno == EINTR) continue;
|
|
|
|
|
perror("accept"); break;
|
|
|
|
@@ -1939,7 +1943,6 @@ el_val_t http_response(el_val_t status, el_val_t headers_json, el_val_t body) {
|
|
|
|
|
el_val_t fs_read(el_val_t pathv) {
|
|
|
|
|
const char* path = EL_CSTR(pathv);
|
|
|
|
|
_tl_fs_read_len = 0;
|
|
|
|
|
_tl_fs_read_ptr = NULL;
|
|
|
|
|
if (!path) return el_wrap_str(el_strdup(""));
|
|
|
|
|
FILE* f = fopen(path, "rb");
|
|
|
|
|
if (!f) return el_wrap_str(el_strdup(""));
|
|
|
|
@@ -1951,7 +1954,6 @@ el_val_t fs_read(el_val_t pathv) {
|
|
|
|
|
size_t got = fread(buf, 1, (size_t)sz, f);
|
|
|
|
|
buf[got] = '\0';
|
|
|
|
|
_tl_fs_read_len = got; /* store real byte count for binary-safe send */
|
|
|
|
|
_tl_fs_read_ptr = buf; /* ...valid only if THIS buffer is the response body */
|
|
|
|
|
fclose(f);
|
|
|
|
|
return el_wrap_str(buf);
|
|
|
|
|
}
|
|
|
|
|