MCPcopy Create free account
hub / github.com/ClickHouse/ClickHouse / handleWebSocket

Method handleWebSocket

src/Server/WebTerminalRequestHandler.cpp:347–498  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

345
346
347void WebTerminalRequestHandler::handleWebSocket(HTTPServerRequest & request, HTTPServerResponse & response)
348{
349 auto log = getLogger("WebTerminalHandler");
350
351 /// Require GET method for WebSocket upgrade per RFC 6455
352 if (request.getMethod() != HTTPRequest::HTTP_GET)
353 {
354 response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_METHOD_NOT_ALLOWED);
355 *response.send() << "WebSocket upgrade requires GET method.\n";
356 return;
357 }
358
359 /// Validate WebSocket upgrade headers per RFC 6455
360 String ws_key = request.get("Sec-WebSocket-Key", "");
361 if (!isValidWebSocketKey(ws_key))
362 {
363 response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST);
364 *response.send() << "Invalid or missing Sec-WebSocket-Key.\n";
365 return;
366 }
367
368 /// RFC 6455 fixed the WebSocket protocol at version 13; earlier drafts
369 /// (00, 7, 8, etc.) are obsolete and not implemented here.
370 String ws_version = request.get("Sec-WebSocket-Version", "");
371 if (ws_version != "13")
372 {
373 response.set("Sec-WebSocket-Version", "13");
374 response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST);
375 *response.send() << "Unsupported WebSocket version.\n";
376 return;
377 }
378
379 /// Validate Origin header to prevent cross-site WebSocket hijacking.
380 /// Browsers always send the Origin header on WebSocket upgrades, so a
381 /// missing `Origin` is either a non-browser client or a malicious
382 /// upgrade. We refuse the request rather than allow it through: this is
383 /// an interactive PTY, the cost of accepting a forged or unattributed
384 /// origin is too high. Non-browser test clients must send a synthetic
385 /// `Origin` matching the request host (see `tests/integration/test_webterminal`).
386 ///
387 /// By default, we enforce same-origin: the Origin must match the request in
388 /// both scheme (`http`/`https`) and host/port. Comparing only host would
389 /// allow a less-trusted page served over `http` on the same host to open a
390 /// WebSocket against `https`, weakening CSWSH protection.
391 ///
392 /// Behind a TLS-terminating reverse proxy, `request.isSecure()` is `false`
393 /// even when the browser sees `https`, so the strict check would reject
394 /// legitimate same-origin connections. For such deployments, operators can
395 /// set `webterminal_allowed_origins` in the server config to a
396 /// comma-separated list of full origins (e.g.
397 /// `https://example.com,https://app.example.com:8443`); when non-empty,
398 /// this allowlist replaces the same-origin check.
399 String origin = request.get("Origin", "");
400 if (origin.empty())
401 {
402 LOG_WARNING(log, "WebSocket upgrade rejected: missing Origin header");
403 response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_FORBIDDEN);
404 *response.send() << "Origin header is required.\n";

Callers

nothing calls this directly

Calls 15

isValidWebSocketKeyFunction · 0.85
computeWebSocketAcceptFunction · 0.85
sendAllBytesFunction · 0.85
setStatusAndReasonMethod · 0.80
getHostMethod · 0.80
getLoggerFunction · 0.50
sendMethod · 0.45
getMethod · 0.45
setMethod · 0.45
emptyMethod · 0.45
backMethod · 0.45
pop_backMethod · 0.45

Tested by

no test coverage detected