EV-Websockets

 view release on metacpan or  search on metacpan

Websockets.xs  view on Meta::CPAN

        }
        pending_adoption = RETVAL;
        if (initial_data_sv && SvOK(initial_data_sv)) {
            STRLEN rdlen;
            const char *rdbuf = SvPV(initial_data_sv, rdlen);
            RETVAL->wsi = lws_adopt_socket_vhost_readbuf(vh,
                (lws_sockfd_type)fd, rdbuf, rdlen);
        } else {
            RETVAL->wsi = lws_adopt_socket_vhost(vh, (lws_sockfd_type)fd);
        }
        pending_adoption = NULL;
    }

    if (RETVAL->wsi == NULL) {
        unlink_conn(RETVAL);
        if (RETVAL->perl_self == NULL) {
            free_conn_resources(RETVAL);
            RETVAL->magic = EV_WS_CONN_FREED;
            Safefree(RETVAL);
        } else {
            conn_unref(RETVAL);
        }
        croak("Failed to adopt socket");
    }
    conn_unref(RETVAL); /* drop sentinel */

    /* Kick lws to process the adopted socket's readbuf (needed for lws 4.5+).
     * Use the same non-blocking forced-service call as do_lws_service: the
     * readbuf is pending work, so lws_service_tsi(ctx, -1, 0) drains it without
     * blocking. A plain lws_service(ctx, 0) would block the EV loop here when a
     * socket is adopted with no immediately-pending data.
     * Guard with extra refs: the service call may synchronously fire
     * error/destroy callbacks that would free RETVAL or ctx. */
    {
        int rejected, alive = 1;
        int* prev_flag = self->alive_flag;
        conn_ref(RETVAL);
        ctx_ref(self);
        self->alive_flag = &alive;
        lws_service_tsi(self->lws_ctx, -1, 0);
        if (alive) {
            self->alive_flag = prev_flag;
            schedule_timeout(self);
        } else if (prev_flag) {
            /* Context destroyed during inner lws_service.
               Propagate destruction up the alive_flag chain. */
            *prev_flag = 0;
        }
        rejected = (RETVAL->wsi == NULL);
        conn_unref(RETVAL);
        ctx_unref(self);
        if (rejected)
            croak("Failed to adopt socket");
    }
}
OUTPUT:
    RETVAL

void
connections(EV::Websockets::Context self);
PPCODE:
{
    ev_ws_conn_t* conn;
    if (self->magic != EV_WS_CTX_MAGIC) XSRETURN_EMPTY;
    for (conn = self->connections; conn != NULL; conn = conn->next) {
        /* "connected" stays set during the "closing" drain (close() never
           clears it), and is cleared together with wsi on teardown — so this
           single predicate covers both the "connected" and "closing" states. */
        if (conn->magic == EV_WS_CONN_MAGIC && conn->connected) {
            XPUSHs(sv_2mortal(get_conn_sv(conn)));
        }
    }
}

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

void
DESTROY(EV::Websockets::Connection self);
CODE:
{
    if (self->magic != EV_WS_CONN_MAGIC) return;

    DEBUG_LOG("Perl object DESTROY: self=%p wsi=%p", self, self->wsi);

    /* Clear the cached Perl object pointer in the C struct */
    self->perl_self = NULL;

    conn_unref(self); /* drop Perl ref */
}

void
send(EV::Websockets::Connection self, SV* data);
CODE:
{
    STRLEN len;
    const char* buf;

    CHECK_CONN_OPEN(self);
    CHECK_NOT_FRAGMENTING(self);

    buf = SvPV(data, len);
    queue_send(self, buf, len, LWS_WRITE_TEXT);
}

void
send_binary(EV::Websockets::Connection self, SV* data);
CODE:
{
    STRLEN len;
    const char* buf;

    CHECK_CONN_OPEN(self);
    CHECK_NOT_FRAGMENTING(self);

    buf = SvPV(data, len);
    queue_send(self, buf, len, LWS_WRITE_BINARY);
}

void
send_ping(EV::Websockets::Connection self, SV* data = NULL);
CODE:



( run in 1.908 second using v1.01-cache-2.11-cpan-5511b514fd6 )