85 lines
2.8 KiB
Rust
85 lines
2.8 KiB
Rust
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<u8> {
|
|
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"")
|
|
}
|