EV-Websockets
view release on metacpan or search on metacpan
Websockets.xs view on Meta::CPAN
int closing;
};
#define EV_WS_PROTOCOL_NAME "ev-websockets"
/* Extensions support (compression) */
#ifdef LWS_HAS_EXTENSIONS
static const struct lws_extension extensions[] = {
{
"permessage-deflate",
lws_extension_callback_pm_deflate,
"permessage-deflate; client_no_context_takeover; client_max_window_bits"
},
{ NULL, NULL, NULL }
};
#endif
static int ev_ws_debug = 0;
/* Bridges userdata into ws_callback() before lws_adopt returns. */
static ev_ws_conn_t* pending_adoption = NULL;
static HV* handshake_headers_map = NULL; /* wsi-ptr â per-conn response headers HV */
static struct lws_context* ssl_keepalive_ctx = NULL; /* see ensure_ssl_keepalive() */
/* Copy an lws header token into a fresh SV, or return NULL if absent/empty */
static SV* hdr_to_sv(struct lws *wsi, enum lws_token_indexes tok) {
int total = lws_hdr_total_length(wsi, tok);
if (total > 0) {
char *buf;
int n;
Newx(buf, total + 1, char);
n = lws_hdr_copy(wsi, buf, total + 1, tok);
if (n > 0) {
SV *val = newSVpvn(buf, n);
Safefree(buf);
return val;
}
Safefree(buf);
}
return NULL;
}
/* Capture a header value into an HV under the given key */
static void capture_header(struct lws *wsi, HV *hv, enum lws_token_indexes tok,
const char *name, STRLEN nlen) {
SV *val = hdr_to_sv(wsi, tok);
if (val && !hv_store(hv, name, nlen, val, 0))
SvREFCNT_dec(val);
}
typedef struct { enum lws_token_indexes tok; const char *name; STRLEN nlen; } header_def_t;
static const header_def_t request_hdrs[] = {
{ WSI_TOKEN_GET_URI, "Path", 4 },
{ WSI_TOKEN_HOST, "Host", 4 },
{ WSI_TOKEN_ORIGIN, "Origin", 6 },
{ WSI_TOKEN_HTTP_COOKIE, "Cookie", 6 },
{ WSI_TOKEN_HTTP_AUTHORIZATION, "Authorization", 13 },
{ WSI_TOKEN_PROTOCOL, "Sec-WebSocket-Protocol", 22 },
{ WSI_TOKEN_HTTP_USER_AGENT, "User-Agent", 10 },
{ WSI_TOKEN_X_FORWARDED_FOR, "X-Forwarded-For", 15 },
};
#define N_REQUEST_HDRS (int)(sizeof(request_hdrs)/sizeof(request_hdrs[0]))
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) --
( run in 0.677 second using v1.01-cache-2.11-cpan-13bb782fe5a )