Skip to content

WebSocket requests ​

WebSocket provides a persistent bidirectional connection between the client and server, allowing real-time data exchange without HTTP overhead. With its API, you can send a message to the server and receive a response without making an HTTP request, and this process is event-driven.

Request context ​

WebSocket requests are handled through the wsctx_t context. It holds pointers to the request and response objects and a slot for arbitrary user data.

c
typedef struct wsctx {
    websocketsrequest_t* request;   // request object
    websocketsresponse_t* response; // response object
    void* user_data;                // arbitrary data (e.g. the authenticated user)
} wsctx_t;

The websocketsrequest_t object stores the frame's bookkeeping fields (type, fragmentation, compression) and a reference to a protocol. The protocol is what parses the HTTP-like message and keeps the method, path, query parameters, and body:

c
typedef struct websocketsrequest {
    request_t base;                  // reset / free
    websockets_datatype_e type;      // frame type: TEXT, BINARY, PING, PONG, CLOSE
    websockets_protocol_t* protocol; // parsing and routing protocol
    int can_reset;
    int fragmented;                  // message spans multiple frames
    int compressed;                  // permessage-deflate compressed
    connection_t* connection;        // connection this request belongs to
} websocketsrequest_t;

Request data is accessed through the protocol. The framework ships two variants: websockets_protocol_resource_t (HTTP-like routing METHOD /path?query DATA) and a simpler websockets_protocol_default_t (frame body only, no address or method). This page covers the resource protocol — the one used by config.json routes.

Basic handler structure ​

c
#include "websockets.h"

void my_handler(wsctx_t* ctx) {
    ctx->response->send_text(ctx->response, "Hello World");
}

The request and response methods are function pointers bound to the object. The first argument is always the object itself: ctx->response->send_text(ctx->response, ...).

Request format ​

Resource-protocol messages have the form [method] [path] [payload]. The method, path, and payload are separated by a single space, for example:

GET /
GET /users?id=100
POST /users {"id": 100, "name":"Alex"}
PATCH /users/100 {"name":"Alen"}
DELETE /users/100

Recognized methods are GET, POST, PATCH, DELETE. A message with any other method is rejected.

HTTP method ​

The current method is available through the protocol->method field after casting the protocol to websockets_protocol_resource_t, and is represented by the values of the route_methods_e enum (core/src/route/route.h):

c
#include "websockets.h"

void handler(wsctx_t* ctx) {
    websockets_protocol_resource_t* protocol = (websockets_protocol_resource_t*)ctx->request->protocol;

    switch (protocol->method) {
        case ROUTE_GET:    // GET
        case ROUTE_POST:   // POST
        case ROUTE_PATCH:  // PATCH
        case ROUTE_DELETE: // DELETE
            break;
        case ROUTE_NONE:   // method not set
            break;
    }
}

The method lives on the protocol

The method field belongs to the resource protocol, not to the request object itself. websockets_protocol_default_t has no method or path — it only deals with the frame body.

Request URL ​

The resource protocol splits the address into components:

FieldTypeDescription
urichar*Raw URI with the query string: /users?id=100
pathchar*URL-decoded path without the query string: /users
uri_lengthsize_tLength of uri
path_lengthsize_tLength of path
query_query_t*Linked list of query parameters and route parameters
c
void handler(wsctx_t* ctx) {
    websockets_protocol_resource_t* protocol = (websockets_protocol_resource_t*)ctx->request->protocol;

    ctx->response->send_text(ctx->response, protocol->path);
}

Ready-made lengths

The uri and path fields are null-terminated, so strlen() works, but uri_length and path_length come straight from the parser and avoid scanning the string.

Query parameters ​

Query-string parameters (after ?) and route parameters land in the shared protocol->query_ list. The shortest way to read a value is the protocol's get_query method, which returns the string and reports success through its ok output parameter (set to 1 when the key is found, 0 otherwise):

c
#include "websockets.h"

void get(wsctx_t* ctx) {
    websockets_protocol_resource_t* protocol = (websockets_protocol_resource_t*)ctx->request->protocol;
    int ok = 0;
    const char* data = protocol->get_query(protocol, "mydata", &ok);

    if (ok) {
        ctx->response->send_text(ctx->response, data);
        return;
    }

    ctx->response->send_text(ctx->response, "Data not found");
}

For type-safe extraction — integers, floating point, arrays — use the functions from query.h directly against the protocol->query_ list, the same way you would in an HTTP handler. See Query parameters.

Request body ​

The body is carried by POST and PATCH methods. Extraction is done with the websocketsrequest_payload* functions or the protocol methods of the same name. A full overview (including file and JSON handling) is in Receiving data from the client.

c
#include "websockets.h"

void post(wsctx_t* ctx) {
    char* payload = websocketsrequest_payload(ctx->request->protocol);

    if (!payload) {
        ctx->response->send_text(ctx->response, "Payload not found");
        return;
    }

    ctx->response->send_text(ctx->response, payload);

    free(payload);
}
ApproachReturns
websocketsrequest_payload / protocol->get_payloadchar* — the whole body as a string
websocketsrequest_payload_file / protocol->get_payload_filefile_content_t — the body as a file descriptor
websocketsrequest_payload_json / protocol->get_payload_jsonjson_doc_t* — the body parsed as JSON

Connection ​

The ctx->request->connection field points to the underlying connection. It is used for broadcast channels — for example, to add a connection to a channel or to send a message to subscribers. See Broadcasting.

Released under the MIT License.