use base64::Engine; use sha1::{Digest, Sha1}; use super::{ RpcServerConfig, http_surface::{ HttpRequest, contains_ascii_case_insensitive, header_value, http_response, http_response_with_headers, normalized_rpc_path, }, }; /// Computes the `Sec-WebSocket-Accept` value for a client-provided handshake key. fn websocket_accept_value(key: &str) -> String { let mut sha1 = Sha1::new(); sha1.update(key.as_bytes()); sha1.update(b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); let digest = sha1.finalize(); base64::engine::general_purpose::STANDARD.encode(digest) } /// Builds the HTTP upgrade response for a successful RPC WebSocket handshake. pub(super) fn websocket_upgrade_response( config: &RpcServerConfig, request: &HttpRequest, ) -> Vec { if !config.enable_websocket_rpc { return http_response("404 Not Found", "text/plain", b"websocket-rpc disabled"); } if normalized_rpc_path(&request.path) != "/jsonrpc" { return http_response("404 Not Found", "text/plain", b"unknown websocket path"); } if !request.method.eq_ignore_ascii_case("GET") { return http_response( "405 Method Not Allowed", "text/plain", b"websocket upgrade requires GET", ); } let Some(key) = header_value(&request.headers, "sec-websocket-key") else { return http_response( "400 Bad Request", "text/plain", b"missing sec-websocket-key", ); }; if !header_value(&request.headers, "upgrade") .is_some_and(|value| value.eq_ignore_ascii_case("websocket")) { return http_response( "400 Bad Request", "text/plain", b"missing websocket upgrade header", ); } if !header_value(&request.headers, "connection") .is_some_and(|value| contains_ascii_case_insensitive(value, "upgrade")) { return http_response( "400 Bad Request", "text/plain", b"missing connection upgrade header", ); } if header_value(&request.headers, "sec-websocket-version") .is_none_or(|value| value.trim() != "13") { return http_response_with_headers( "426 Upgrade Required", Some("text/plain"), &[("Sec-WebSocket-Version", "13".to_owned())], b"unsupported websocket version", ); } let mut headers = vec![ ("Upgrade", "websocket".to_owned()), ("Connection", "Upgrade".to_owned()), ("Sec-WebSocket-Accept", websocket_accept_value(key)), ]; if let Some(origin) = &config.allow_origin { headers.push(("Access-Control-Allow-Origin", origin.clone())); } http_response_with_headers("101 Switching Protocols", None, &headers, b"") }