Read the content of the uploaded file and follow t...
Criado em: 12 de julho de 2025
Criado em: 12 de julho de 2025
Below is a practical, step-by-step blueprint you can follow to wire full SIP-over-WebSocket (RFC 7118) support into your existing C++20 SIP server that currently runs over UDP/TCP/TLS on eXosip2 + osip2.
Where I reference existing source or config, I cite the relevant lines so you can see exactly where each change “plugs in”.
textsrc/ └─ sip/ ├─ SipWebSocketHandler.{hpp,cpp} // already stub-created in SipServer ├─ SipWebSocketConnection.hpp // 1 per WS client, does text-frame ↔ SIP parse ├─ SipWebSocketTransport.hpp // bridges SIP msg ⇆ eXosip2 └─ …
Piece | Responsibility | Key API |
---|---|---|
SipWebSocketHandler | Owns a uWS::App , listens on <listen_addr>:<listen_port> (or WSS on 8443). Internally keeps a std::unordered_map<ConnectionId, SipWebSocketConnection> and tears them down cleanly. | bool start(ip,port,bool secure) / void stop() / void wait() (already invoked from SipServer L44-51) |
SipWebSocketConnection | Per-browser-tab object that converts WS text frames (each containing exactly one SIP message, terminated by CRLF CRLF) into osip_message_t* , and vice-versa. It also tracks the remote Contact URI for routing. | void onText(std::string_view) / void send(osip_message_t*) |
SipWebSocketTransport | Very thin shim that allows eXosip2 to “send” through WebSocket: when eXosip builds an outgoing message whose Via / Contact transport is ws or wss , SipWebSocketTransport finds the correct SipWebSocketConnection and calls send() . Provides a static bool injectIncoming(osip_message_t*) so SipWebSocketConnection can push parsed messages straight into eXosip’s core. |
Why a custom transport instead of patching eXosip2 sockets?
eXosip2’s network I/O is hard-wired to classic sockets. Treating WS as a virtual transport lets you keep the upstream library untouched while still benefiting from its transaction/statemachine logic.
Your config.ini
already carries a [sip_websocket]
section with enable
, listen_addr
, listen_port
, etc. .
ServerConfig
is already parsing those fields (see lines 6-17) , so no extra parsing work is required.
You’ve introduced an ENABLE_WEBSOCKET_SERVER
option in CMakeLists.txt
(L61-63) .
Add the new sources conditionally:
cmakeif(ENABLE_WEBSOCKET_SERVER) target_sources(SipServer PRIVATE src/sip/SipWebSocketHandler.cpp src/sip/SipWebSocketConnection.cpp src/sip/SipWebSocketTransport.cpp) target_link_libraries(SipServer PRIVATE uWS::uWS) endif()
\r\n\r\n
.SipWebSocketConnection::onText
cppif (auto* msg = osip_message_init(); osip_message_parse(msg, frame.data(), frame.size()) == 0) { SipWebSocketTransport::injectIncoming(msg); // ownership passes inside } else { LOGW("Malformed SIP over WS message"); // drop / 400 }
injectIncoming
simply calls the internal function eXosip_event_add()
(not public) or, if you prefer a stable API, opens a loopback UDP socket to 127.0.0.1:15060
(your normal SIP port) and sends the raw bytes — eXosip will happily consume them.
Add a send hook inside SipProxy / Registrar when they create responses or forwards:
cppif (osip_uri_param_get_byname(msg->via->transport, "ws") == 0) { SipWebSocketTransport::send(msg); return; // already sent } // else fall back to existing eXosip send path (UDP/TCP/TLS)
SipWebSocketTransport::send
locates the client via its Contact URI → ConnectionId map and does:
cppstd::string data; osip_message_to_str(msg, &data_ptr, &len); connection->send({data_ptr, len}); osip_free(data_ptr);
Your existing SipSimulatedClient
already builds INVITEs; you just need to:
Contact
header.
;transport=ws
or URI scheme sip:wss:
, route the INVITE through SipWebSocketTransport::send
.Contact
so it preserves transport=ws
; your Registrar already stores bindings, so the Proxy can later detect them.SipServer
already starts the handler and joins it in waitForShutdown()
(L8-13) , so keep:
cppm_webSocketHandler = std::make_unique<SipWebSocketHandler>(); m_webSocketHandler->initialize(cfg, m_excontext, m_registrar.get(), m_proxy.get());
The handler itself launches a dedicated uWS::App
thread and exposes stop()
/wait()
so shutdown stays graceful.
Concern | Recommendation |
---|---|
WSS | Enable wss_enable=true , load wss_cert_file + wss_key_file , and use uWS::SSLApp for the secure listener. |
Origin checking | Validate "Origin" header in the WS handshake to mitigate CSWSH. |
Keep-alive | Send SIP CRLF keep-alives every 15 s per RFC 7118 § 8. |
Test | Expectation |
---|---|
SIP.js REGISTER → server | 200 OK via WS; binding visible in /listClients API |
Browser A INVITE → Browser B | SDPs exchanged, BYE tears down, call state correct |
HTTP /simulateCall → Browser | SipSimulatedClient builds INVITE, delivered over WS |
Legacy UDP client ↔ WS client | Proxy routes automatically across transports |
TLS break-test | Invalid cert rejected; valid cert success |
cmake -DENABLE_WEBSOCKET_SERVER=ON …
wss_enable
.jsconst transportOptions = { server: "wss://your-host:8443", traceSip: true };
sip over websocket
functionality” , .SipServer
for a SipWebSocketHandler
.Follow the roadmap module-by-module and you’ll have fully working RFC 7118 WebSocket transport without invasive changes to eXosip2. Happy coding!