EV-Websockets
view release on metacpan or search on metacpan
Websockets.xs view on Meta::CPAN
static void capture_request_headers(struct lws *wsi, HV *hv) {
int i;
for (i = 0; i < N_REQUEST_HDRS; i++)
capture_header(wsi, hv, request_hdrs[i].tok,
request_hdrs[i].name, request_hdrs[i].nlen);
}
/* Inject all key/value pairs from an HV as HTTP headers via lws.
Returns -1 if lws rejected a header (client path aborts the handshake);
server callers ignore the result and simply stop adding. */
static int inject_headers(struct lws *wsi, HV *hv,
unsigned char **p, unsigned char *end) {
HE *entry;
char kbuf[256];
hv_iterinit(hv);
while ((entry = hv_iternext(hv))) {
I32 klen;
const char *key = hv_iterkey(entry, &klen);
SV *val_sv;
STRLEN vlen;
const char *val;
if (klen >= 254) continue;
val_sv = hv_iterval(hv, entry);
val = SvPV(val_sv, vlen);
memcpy(kbuf, key, klen);
kbuf[klen] = ':';
kbuf[klen + 1] = '\0';
if (lws_add_http_header_by_name(wsi, (unsigned char *)kbuf,
(unsigned char *)val, vlen, p, end))
return -1;
}
return 0;
}
/* Format a wsi pointer as the lookup key for handshake_headers_map.
Callers must pass a buffer of at least 32 bytes; returns the key length. */
static int wsi_key(char *buf, struct lws *wsi) {
return snprintf(buf, 32, "%p", (void*)wsi);
}
#define DEBUG_LOG(fmt, ...) do { if (ev_ws_debug) fprintf(stderr, "[EV::WS] " fmt "\n", ##__VA_ARGS__); } while(0)
static void ctx_ref(ev_ws_ctx_t* ctx) {
ctx->refcnt++;
}
static void ctx_unref(ev_ws_ctx_t* ctx) {
if (--ctx->refcnt == 0) {
Safefree(ctx);
}
}
/* Schedule the next lws housekeeping wake-up.
lws_service_adjust_timeout returns the ms until lws next needs servicing; 0
means "service as soon as possible". This timer only paces lws's time-based
work (connection/handshake timeouts, TLS cert aging, draining buffered rx) --
socket readability/writability is driven by the per-fd io watcher, not here.
We deliberately floor the delay at 1ms rather than arming an ev_idle watcher
on 0: do_lws_service is now non-blocking, so an always-ready idle watcher
would busy-spin at 100% CPU whenever lws keeps asking for immediate service.
A 1ms floor lets the loop block briefly so every other EV watcher still
fires, at negligible latency for this coarse, time-based work. */
static void schedule_timeout(ev_ws_ctx_t* ctx) {
int delay_ms = lws_service_adjust_timeout(ctx->lws_ctx, 1000, 0);
double delay_s;
if (delay_ms < 1) delay_ms = 1;
delay_s = (double)delay_ms / 1000.0;
ev_timer_stop(ctx->loop, &ctx->timer);
ev_timer_set(&ctx->timer, delay_s, 0.);
ev_timer_start(ctx->loop, &ctx->timer);
}
static void flush_recv_messages(ev_ws_ctx_t* ctx);
static void do_lws_service(ev_ws_ctx_t* ctx) {
if (ctx && ctx->magic == EV_WS_CTX_MAGIC && ctx->lws_ctx) {
int alive = 1;
int* prev_flag = ctx->alive_flag;
ctx->alive_flag = &alive;
ctx_ref(ctx);
/* "Forced service": drive connections that need servicing with no
pending socket event -- rx already read into lws's buflist, plus any
due lws_sul timeouts. Counterintuitively a timeout_ms of 0 is NOT
non-blocking here: lws maps it to its maximum internal poll wait, so
the old lws_service(ctx, 0) blocked for seconds on an idle connection
and starved every other EV watcher. A negative timeout_ms clamps the
wait to 0, servicing only ready + forced-service work and returning at
once (see the lws_service_adjust_timeout docs). lws_service_fd(ctx,
NULL) is invalid since lws 3.2 and never drains buflists. Per-fd I/O
is driven by io_cb via lws_service_fd(&pollfd). */
lws_service_tsi(ctx->lws_ctx, -1, 0);
if (alive)
flush_recv_messages(ctx); /* deliver messages reassembled above */
if (alive) {
ctx->alive_flag = prev_flag;
schedule_timeout(ctx);
} else if (prev_flag) {
*prev_flag = 0; /* propagate destruction up the alive_flag chain */
}
ctx_unref(ctx);
}
}
static void timer_cb(EV_P_ ev_timer* w, int revents) {
(void)loop; (void)revents;
do_lws_service((ev_ws_ctx_t*)w->data);
}
/* Forward declarations */
static void io_cb(EV_P_ ev_io* w, int revents);
static void add_fd_watcher(ev_ws_ctx_t* ctx, int fd, int events);
static void change_fd_watcher(ev_ws_ctx_t* ctx, int fd, int events);
static void conn_ref(ev_ws_conn_t* conn);
static void conn_unref(ev_ws_conn_t* conn);
static SV* get_conn_sv(ev_ws_conn_t* conn) {
if (conn->perl_self) {
SV* rv = newRV_inc(conn->perl_self);
( run in 0.383 second using v1.01-cache-2.11-cpan-995e09ba956 )