EV-Websockets

 view release on metacpan or  search on metacpan

Websockets.xs  view on Meta::CPAN


void
_set_debug(int enable);
CODE:
{
    ev_ws_debug = enable;
    if (enable)
        lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO | LLL_DEBUG, NULL);
    else
        lws_set_log_level(LLL_ERR | LLL_WARN, NULL);
}

MODULE = EV::Websockets  PACKAGE = EV::Websockets::Context

EV::Websockets::Context
_new(char* class, EV::Loop loop, const char* proxy = NULL, int proxy_port = 0, const char* ssl_cert = NULL, const char* ssl_key = NULL, const char* ssl_ca = NULL, int ssl_init = -1);
CODE:
{
    struct lws_context_creation_info info;
    void* foreign_loops[1];

    PERL_UNUSED_VAR(class);

    Newxz(RETVAL, 1, ev_ws_ctx_t);
    RETVAL->magic = EV_WS_CTX_MAGIC;
    RETVAL->refcnt = 1; /* Perl owns the context */
    RETVAL->loop = loop;

    foreign_loops[0] = loop;

    memset(&info, 0, sizeof(info));
    info.port = CONTEXT_PORT_NO_LISTEN;
    info.protocols = protocols;
#ifdef LWS_HAS_EXTENSIONS
    info.extensions = extensions;
#endif
    info.gid = -1;
    info.uid = -1;
    /* ssl_init: -1 = manage OpenSSL init (default); 1 = force; 0 = coexist
       (leave it to another TLS library). When we manage it, flag this context
       so its own TLS works, and also pin the global init in a process-lifetime
       keepalive so destroying this context can't drop lws's TLS refcount to
       zero (which would run OPENSSL_cleanup() and break later TLS use). */
    info.options = 0;
    if (ssl_init != 0) {
        info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
        ensure_ssl_keepalive();
    }
    info.user = RETVAL;
    info.foreign_loops = foreign_loops;
    info.vhost_name = "default";
    
    if (proxy && strlen(proxy) > 0) {
        DEBUG_LOG("Context using proxy: %s:%d", proxy, proxy_port);
        info.http_proxy_address = proxy;
        info.http_proxy_port = proxy_port;
    }

    if (ssl_cert && strlen(ssl_cert) > 0) {
        info.ssl_cert_filepath = ssl_cert;
        info.ssl_private_key_filepath = ssl_key;
        if (ssl_ca && strlen(ssl_ca) > 0)
            info.ssl_ca_filepath = ssl_ca;
    }

    DEBUG_LOG("Creating context (manual integration)");

    RETVAL->lws_ctx = lws_create_context(&info);
    if (RETVAL->lws_ctx == NULL) {
        Safefree(RETVAL);
        croak("Failed to create libwebsockets context");
    }
    ev_timer_init(&RETVAL->timer, timer_cb, 0.00001, 0.);
    RETVAL->timer.data = (void*)RETVAL;

    schedule_timeout(RETVAL);

    DEBUG_LOG("Context created successfully");
}
OUTPUT:
    RETVAL

void
DESTROY(EV::Websockets::Context self);
CODE:
{
    ev_ws_conn_t* conn;
    ev_ws_conn_t* next;

    if (self->magic != EV_WS_CTX_MAGIC) return;

    self->magic = EV_WS_CTX_FREED;

    ev_timer_stop(self->loop, &self->timer);

    free_all_fd_watchers(self);

    /* Release connections still queued for message delivery (no delivery during
       teardown); each holds a flush-list ref. They remain in self->connections
       and are torn down by the loop below. */
    while (self->flush_head) {
        conn = self->flush_head;
        self->flush_head = conn->flush_next;
        conn->on_flush = 0;
        conn->flush_next = NULL;
        conn_unref(conn); /* release the flush-list ref */
    }
    self->flush_tail = NULL;

    /* Close all connections */
    for (conn = self->connections; conn != NULL; conn = next) {
        next = conn->next;
        conn->ctx = NULL;
        conn->prev = NULL;
        conn->next = NULL;
        if (conn->wsi) {
            lws_set_wsi_user(conn->wsi, NULL);
            conn->wsi = NULL;
        }
        conn_unref(conn); /* drop wsi ref — may free conn */
    }

Websockets.xs  view on Meta::CPAN

        } else if (strcmp(key, "on_drain") == 0 && SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVCV) {
            on_drain = SvREFCNT_inc(val);
        } else if (strcmp(key, "on_handshake") == 0 && SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVCV) {
            on_handshake = SvREFCNT_inc(val);
        }
    }

    /* An undef string option used to stringify to "" (with an "uninitialized
       value" warning), e.g. name => undef silently created a vhost named "". */
    if (undef_opt) {
        free_cb_svs(on_connect, on_message, on_close, on_error, on_pong, on_drain, on_handshake);
        croak("listen: option '%s' must be a defined string", undef_opt);
    }

    if (strcmp(name, "default") == 0) {
        free_cb_svs(on_connect, on_message, on_close, on_error, on_pong, on_drain, on_handshake);
        croak("listen: vhost name 'default' is reserved");
    }

    /* TLS needs both halves. Accepting just one used to silently create a
       PLAINTEXT listener, serving cleartext on a port the caller believes is
       encrypted -- fail loudly instead. */
    if ((ssl_cert && *ssl_cert) != (ssl_key && *ssl_key)) {
        int have_cert = ssl_cert && *ssl_cert;
        free_cb_svs(on_connect, on_message, on_close, on_error, on_pong, on_drain, on_handshake);
        croak("listen: ssl_cert and ssl_key must both be set for TLS (only %s was given)",
              have_cert ? "ssl_cert" : "ssl_key");
    }

    Newxz(srv, 1, ev_ws_server_t);
    srv->magic = EV_WS_SRV_MAGIC;
    srv->on_connect = on_connect;
    srv->on_message = on_message;
    srv->on_close = on_close;
    srv->on_error = on_error;
    srv->on_pong = on_pong;
    srv->on_drain = on_drain;
    srv->on_handshake = on_handshake;
    srv->max_message_size = max_message_size;
    if (headers_hv)
        srv->response_headers = (HV*)SvREFCNT_inc(SvRV(headers_hv));

    if (protocol_name) {
        STRLEN pnlen = strlen(protocol_name);
        Newx(srv->protocol_name, pnlen + 1, char);
        memcpy(srv->protocol_name, protocol_name, pnlen + 1);
        srv->vhost_protocols[0] = protocols[0];
        srv->vhost_protocols[0].name = srv->protocol_name;
        srv->vhost_protocols[1] = protocols[1];
    }

    memset(&info, 0, sizeof(info));
    info.port = port;
    info.protocols = srv->protocol_name ? srv->vhost_protocols : protocols;
    info.vhost_name = name;
    info.user = srv;
    info.options = 0;

    if (ssl_cert && *ssl_cert && ssl_key && *ssl_key) {
        info.ssl_cert_filepath = ssl_cert;
        info.ssl_private_key_filepath = ssl_key;
        if (ssl_ca && *ssl_ca)
            info.ssl_ca_filepath = ssl_ca;
        info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
        ensure_ssl_keepalive(); /* pin global init so vhost/context teardown won't OPENSSL_cleanup */
    }

    vh = lws_create_vhost(self->lws_ctx, &info);
    if (vh == NULL) {
        free_server_svs(srv);
        if (srv->protocol_name) Safefree(srv->protocol_name);
        Safefree(srv);
        croak("Failed to create vhost for listening");
    }
    
    RETVAL = lws_get_vhost_listen_port(vh);
    if (RETVAL <= 0) {
        /* Vhost created but port bind failed. Release the SV refs now, but
           leave protocol_name alive — the live vhost still points at it via
           vhost_protocols[0].name, so freeing it here would dangle. Do NOT
           Safefree(srv): the vhost retains the pointer. PROTOCOL_DESTROY frees
           protocol_name and srv at context teardown; the SRV_FREED sentinel
           tells it to skip the (already-released) SV refs. */
        free_server_svs(srv);
        srv->magic = EV_WS_SRV_FREED;
        croak("listen: failed to bind port");
    }
    DEBUG_LOG("Server listening on port %d", RETVAL);
}
OUTPUT:
    RETVAL

EV::Websockets::Connection
adopt(EV::Websockets::Context self, ...);
PREINIT:
    int fd = -1;
    SV* fh_sv = NULL;
    SV* initial_data_sv = NULL;
    SV* on_connect = NULL;
    SV* on_message = NULL;
    SV* on_close = NULL;
    SV* on_error = NULL;
    SV* on_pong = NULL;
    SV* on_drain = NULL;
    size_t max_message_size = 0;
    int i;
CODE:
{
    if (self->magic != EV_WS_CTX_MAGIC) {
        croak("Context has been destroyed");
    }

    /* Options are key => value pairs. A dangling key used to be silently
       ignored, hiding typos and truncated argument lists. Checked before any
       callback SV is retained, so croaking here leaks nothing. */
    if (((items - 1) % 2) != 0) {
        croak("odd number of options: key '%s' has no value",
              SvPV_nolen(ST(items - 1)));
    }

    for (i = 1; i < items; i += 2) {



( run in 2.736 seconds using v1.01-cache-2.11-cpan-8dfa8b56332 )