runtime: native SSE streaming — http_sse_open/send/close
Add Server-Sent Events support to the El runtime. El v2 handlers can now
hold HTTP connections open and push events in real time.
New builtins in el_seed.c:
__http_conn_fd() — retrieve raw fd from thread-local set by worker
__http_sse_open(fd) — send SSE headers (text/event-stream), keep-alive
__http_sse_send(fd, data) — write "data: <data>\n\n" frame
__http_sse_close(fd) — close the connection fd
http_worker_v2 in legacy/el_runtime.c now:
- stashes the fd via el_seed_set_http_conn_fd() before calling the handler
- detects the "__sse__" sentinel return value to skip http_send_response
and skip close(fd) — SSE handler took ownership of the fd
- clears the thread-local after the handler returns
El wrappers added to runtime/http.el:
http_conn_fd() http_sse_open(fd) http_sse_send(fd, data)
http_sse_close(fd) http_sse_sentinel()
This commit is contained in:
@@ -1673,6 +1673,7 @@ static void* http_worker_v2(void* arg) {
|
||||
HttpWorkerArg* a = (HttpWorkerArg*)arg;
|
||||
int fd = a->fd;
|
||||
free(a);
|
||||
int is_sse = 0;
|
||||
char *method = NULL, *path = NULL, *body = NULL, *hdr_block = NULL;
|
||||
if (http_read_request(fd, &method, &path, &body, &hdr_block) == 0) {
|
||||
http_handler4_fn h = http_lookup_active_v2();
|
||||
@@ -1680,28 +1681,39 @@ static void* http_worker_v2(void* arg) {
|
||||
int head_only = (method && strcmp(method, "HEAD") == 0);
|
||||
const char* dispatch_method = head_only ? "GET" : method;
|
||||
el_request_start(); /* begin per-request arena */
|
||||
/* Expose the raw fd to El SSE builtins (__http_conn_fd etc.). */
|
||||
el_seed_set_http_conn_fd(fd);
|
||||
if (h) {
|
||||
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);
|
||||
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'; }
|
||||
/* Detect SSE sentinel — handler took ownership of the fd. */
|
||||
if (rs && strcmp(rs, "__sse__") == 0) {
|
||||
is_sse = 1;
|
||||
} else {
|
||||
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'; }
|
||||
}
|
||||
el_release(hmap);
|
||||
} else {
|
||||
response = el_strdup_persist(
|
||||
"el-runtime: no v2 http handler registered "
|
||||
"(call http_set_handler_v2)");
|
||||
}
|
||||
el_seed_set_http_conn_fd(-1); /* clear before arena teardown */
|
||||
el_request_end(); /* free all intermediate strings */
|
||||
_tl_http_head_only = head_only;
|
||||
http_send_response(fd, response);
|
||||
_tl_http_head_only = 0;
|
||||
free(response);
|
||||
if (!is_sse) {
|
||||
_tl_http_head_only = head_only;
|
||||
http_send_response(fd, response);
|
||||
_tl_http_head_only = 0;
|
||||
free(response);
|
||||
}
|
||||
}
|
||||
free(method); free(path); free(body); free(hdr_block);
|
||||
close(fd);
|
||||
/* SSE handlers close the fd themselves via __http_sse_close. */
|
||||
if (!is_sse) close(fd);
|
||||
pthread_mutex_lock(&_http_conn_mu);
|
||||
_http_conn_active--;
|
||||
pthread_cond_signal(&_http_conn_cv);
|
||||
|
||||
@@ -176,6 +176,11 @@ void http_set_handler_v2(el_val_t name);
|
||||
* auto-content-type contract for legacy handlers that return plain bodies. */
|
||||
el_val_t http_response(el_val_t status, el_val_t headers_json, el_val_t body);
|
||||
|
||||
/* SSE connection fd — set by http_worker_v2 before calling the El handler,
|
||||
* cleared afterwards. Defined in el_seed.c; called from el_runtime.c.
|
||||
* The getter is exposed as __http_conn_fd() to El programs. */
|
||||
void el_seed_set_http_conn_fd(int fd);
|
||||
|
||||
/* HTTP timeout — every libcurl request honors EL_HTTP_TIMEOUT_MS (default
|
||||
* 60000ms). Read lazily on first use, so setting the env var any time before
|
||||
* the first http_* call is sufficient. */
|
||||
|
||||
Reference in New Issue
Block a user