EV-Websockets

 view release on metacpan or  search on metacpan

Websockets.xs  view on Meta::CPAN

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

BOOT:
{
    I_EV_API("EV::Websockets");
    lws_set_log_level(LLL_ERR | LLL_WARN, NULL);
}

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;
    if (ssl_init == -1)
        ssl_init = ev_ws_ssl_inited ? 0 : 1;
    info.options = ssl_init ? LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT : 0;
    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");
    }
    if (ssl_init)
        ev_ws_ssl_inited = 1;
    ev_timer_init(&RETVAL->timer, timer_cb, 0.00001, 0.);
    RETVAL->timer.data = (void*)RETVAL;

    ev_idle_init(&RETVAL->idle, idle_cb);
    RETVAL->idle.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);
    ev_idle_stop(self->loop, &self->idle);

    free_all_fd_watchers(self);

    /* 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 */
    }
    self->connections = NULL;

    if (self->lws_ctx) {
        lws_context_destroy(self->lws_ctx);
        self->lws_ctx = NULL;
    }

Websockets.xs  view on Meta::CPAN

            headers_hv = val;
        } else if (strcmp(key, "on_connect") == 0 && SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVCV) {
            on_connect = SvREFCNT_inc(val);
        } else if (strcmp(key, "on_message") == 0 && SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVCV) {
            on_message = SvREFCNT_inc(val);
        } else if (strcmp(key, "on_close") == 0 && SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVCV) {
            on_close = SvREFCNT_inc(val);
        } else if (strcmp(key, "on_error") == 0 && SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVCV) {
            on_error = SvREFCNT_inc(val);
        } else if (strcmp(key, "on_pong") == 0 && SvROK(val) && SvTYPE(SvRV(val)) == SVt_PVCV) {
            on_pong = SvREFCNT_inc(val);
        } 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);
        }
    }

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

    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_key) {
        info.ssl_cert_filepath = ssl_cert;
        info.ssl_private_key_filepath = ssl_key;
        if (ssl_ca)
            info.ssl_ca_filepath = ssl_ca;
        info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
    }

    vh = lws_create_vhost(self->lws_ctx, &info);
    if (vh == NULL) {
        if (on_connect) SvREFCNT_dec(on_connect);
        if (on_message) SvREFCNT_dec(on_message);
        if (on_close) SvREFCNT_dec(on_close);
        if (on_error) SvREFCNT_dec(on_error);
        if (on_pong) SvREFCNT_dec(on_pong);
        if (on_drain) SvREFCNT_dec(on_drain);
        if (on_handshake) SvREFCNT_dec(on_handshake);
        if (srv->response_headers) SvREFCNT_dec((SV*)srv->response_headers);
        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 SV refs now.
           Do NOT Safefree(srv) — the vhost retains the pointer.
           PROTOCOL_DESTROY will Safefree(srv) on context teardown;
           SRV_FREED sentinel tells it to skip SvREFCNT_dec. */
        if (srv->on_connect) { SvREFCNT_dec(srv->on_connect); srv->on_connect = NULL; }
        if (srv->on_message) { SvREFCNT_dec(srv->on_message); srv->on_message = NULL; }
        if (srv->on_close)   { SvREFCNT_dec(srv->on_close);   srv->on_close = NULL; }
        if (srv->on_error)   { SvREFCNT_dec(srv->on_error);   srv->on_error = NULL; }
        if (srv->on_pong)    { SvREFCNT_dec(srv->on_pong);    srv->on_pong = NULL; }
        if (srv->on_drain)   { SvREFCNT_dec(srv->on_drain);   srv->on_drain = NULL; }
        if (srv->on_handshake) { SvREFCNT_dec(srv->on_handshake); srv->on_handshake = NULL; }
        if (srv->response_headers) { SvREFCNT_dec((SV*)srv->response_headers); srv->response_headers = NULL; }
        if (srv->protocol_name) {
            Safefree(srv->protocol_name); srv->protocol_name = NULL;
        }
        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:



( run in 2.295 seconds using v1.01-cache-2.11-cpan-0bb4e1dffa6 )