STTNet 0.7.0

Chapter 7: WebSocket

This chapter covers WebSocket handshake validation, callback types, text/binary frames, heartbeat, and close behavior.

1. Lifecycle callbacks

StageAPIResult
Before handshakesetJudgeFunctionbool; false rejects the connection.
After successful handshakesetStartFunctionbool; suitable for a welcome frame.
Ordinary messagesetGlobalSolveFunctionbool; false signals failure and close.
Keyed multi-stage messagesetGetKeyFunction + setFunctionint with 1/0/-1/-2 flow semantics.

2. Complete echo demo

#include <sttnet.h>

#include <iostream>
#include <string_view>

int main()
{
    using namespace stt::network;
    using namespace stt::data;
    using stt::system::ServerSetting;

    if(!ServerSetting::blockTerminationSignals())
    {
        std::cerr << "failed to block termination signals\n";
        return 1;
    }

    WebSocketServer server;

    // Check the HTTP upgrade request before accepting the WebSocket handshake.
    // This demo accepts only /echo and an optional query string.
    server.setJudgeFunction(
        [](WebSocketFDInformation &connection) {
            const bool correctPath =
                connection.locPara == "/echo" ||
                connection.locPara.rfind("/echo?", 0) == 0;

            // A real service may also inspect connection.header here, for
            // example with HttpStringUtil::get_value_header().
            return correctPath;
        });

    // The global callback receives messages that do not use a custom key route.
    // It returns bool: false tells STTNet that message handling failed.
    server.setGlobalSolveFunction(
        [](WebSocketServerFDHandler &client,
           WebSocketFDInformation &message) {
            // RFC 6455 opcode 1 is text and opcode 2 is binary.
            const std::string frameType =
                message.message_type == 2 ? "0010" : "0001";

            // Echo the payload using the same text/binary frame type.
            return client.sendMessage(message.message, frameType);
        });

    // Close idle connections and require a heartbeat response within 30 seconds.
    server.setTimeOutTime(20);   // Idle timeout in minutes.
    server.setHBTimeOutTime(30); // Heartbeat response timeout in seconds.

    if(!server.startListen(5050))
    {
        std::cerr << "failed to listen on port 5050\n";
        return 2;
    }

    std::cout << "WebSocket echo listening on ws://127.0.0.1:5050/echo\n";
    ServerSetting::waitForTerminationSignal();
    return server.close() ? 0 : 3;
}

3. Validate handshake headers

server.setJudgeFunction([](WebSocketFDInformation &info) {
    std::string_view token;
    stt::data::HttpStringUtil::get_value_header(
        std::string_view(info.header), token, "X-Demo-Token");
    return info.locPara == "/echo" && token == "sttnet";
});

4. Frame types

  • Opcode 1 represents text and uses send type "0001".
  • Opcode 2 represents binary data and uses send type "0010".
  • Ping/Pong is protocol-level liveness, not a complete application presence model.
  • Protocol-level close uses a WebSocket close frame such as closeFD(fd, 1000, "bye").

5. Test

./build/examples/sttnet_websocket_echo
websocat ws://127.0.0.1:5050/echo