use std::fmt;
use std::error::Error;
use http_types::{
Request,
Response,
StatusCode,
};
use sha1::{Digest, Sha1};
use base64;
pub fn convert_key(input: &[u8]) -> String {
const WS_GUID: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
let mut sha1 = Sha1::default();
sha1.input(input);
sha1.input(WS_GUID);
base64::encode(&sha1.result())
}
#[derive(Debug)]
pub enum HandshakeError {
MissingHeader(&'static str),
InvalidHeaderValue {
header: &'static str,
expected: Option<&'static str>,
found: String,
},
}
impl fmt::Display for HandshakeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::MissingHeader(header) => {
write!(f, "Missing required header `{}`", header)
}
Self::InvalidHeaderValue { header, expected, found } => {
write!(f, "Invalid value `{}` for header `{}`", found, header)?;
if let Some(expected) = expected {
write!(f, ", expected `{}`", expected)?;
}
Ok(())
}
}
}
}
impl Error for HandshakeError {}
#[allow(dead_code)]
pub struct HandshakeInfo<'a> {
key: &'a str,
extensions: Vec<&'a str>,
protocols: Vec<&'a str>,
}
impl HandshakeInfo<'_> {
pub fn make_response(&self) -> Response {
let mut res = Response::new(StatusCode::SwitchingProtocols);
let accept = convert_key(self.key.as_bytes());
res.insert_header("Upgrade", "websocket");
res.insert_header("Connection", "Upgrade");
res.insert_header("Sec-WebSocket-Accept", accept);
res
}
}
fn assert_header<'a>(req: &'a Request, header: &'static str, expected: &'static str) -> Result<(), HandshakeError> {
let value = req.header(header)
.ok_or_else(|| HandshakeError::MissingHeader(header))?
.last()
.as_str()
;
if value != expected {
return Err(HandshakeError::InvalidHeaderValue {
header,
expected: Some(expected),
found: value.to_string()
})
}
Ok(())
}
pub fn check_request_headers(request: &Request) -> Result<HandshakeInfo, HandshakeError> {
assert_header(request, "Connection", "Upgrade")?;
assert_header(request, "Upgrade", "websocket")?;
assert_header(request, "Sec-WebSocket-Version", "13")?;
let key = request
.header("Sec-WebSocket-Key")
.ok_or_else(|| HandshakeError::MissingHeader("Sec-WebSocket-Key"))?
.last()
.as_str()
;
let mut extensions = Vec::new();
if let Some(values) = request.header("Sec-WebSocket-Extensions") {
let mut i = 0;
while let Some(value) = values.get(i) {
let value = value.as_str();
for ext_string in value.split(",") {
if ext_string.contains(";") {
continue
}
let ext_string = ext_string.trim();
extensions.push(ext_string);
}
i += 1;
}
}
let mut protocols = Vec::new();
if let Some(values) = request.header("Sec-WebSocket-Protocol") {
let mut i = 0;
while let Some(value) = values.get(i) {
let value = value.as_str();
for proto_string in value.split(",") {
protocols.push(proto_string.trim());
}
i += 1;
}
}
Ok(HandshakeInfo {
key,
extensions,
protocols,
})
}