ec0bdb8
use hyper::{Method, StatusCode, header};
use super::polling::{polling_open, polling_poll, polling_post};
use super::response::{HttpRequest, HttpResponse, cors, empty_response, engine_error};
use super::{SocketHub, transport};
pub(super) async fn handle(request: HttpRequest, hub: SocketHub) -> HttpResponse {
let headers = request.headers().clone();
let response = route(request, &hub).await;
cors(&headers, response)
}
async fn route(request: HttpRequest, hub: &SocketHub) -> HttpResponse {
if !matches!(request.uri().path(), "/socket.io/" | "/socket.io") {
return empty_response(StatusCode::NOT_FOUND);
}
if request.method() == Method::OPTIONS {
return empty_response(StatusCode::NO_CONTENT);
}
let query = Query::parse(request.uri().query().unwrap_or_default());
if query.eio.as_deref() != Some("4") {
let (code, message) = if query.eio.is_some() {
(5, "Unsupported protocol version")
} else {
(3, "Bad request")
};
return engine_error(code, message);
}
if is_websocket(&request) {
if query.transport.as_deref() != Some("websocket") {
return engine_error(0, "Transport unknown");
}
return transport::handle_websocket(request, hub.clone(), query.sid).await;
}
match *request.method() {
Method::GET => {
if query.transport.as_deref() != Some("polling") {
return engine_error(0, "Transport unknown");
}
match query.sid {
Some(sid) => polling_poll(hub, &sid).await,
None => polling_open(&request, hub).await,
}
}
Method::POST => {
if query.transport.as_deref() != Some("polling") {
return engine_error(0, "Transport unknown");
}
polling_post(request.into_body(), hub, query.sid.as_deref()).await
}
_ => empty_response(StatusCode::METHOD_NOT_ALLOWED),
}
}
fn is_websocket(request: &HttpRequest) -> bool {
request
.headers()
.get(header::UPGRADE)
.and_then(|value| value.to_str().ok())
.is_some_and(|value| value.eq_ignore_ascii_case("websocket"))
}
struct Query {
eio: Option<String>,
transport: Option<String>,
sid: Option<String>,
}
impl Query {
fn parse(query: &str) -> Self {
let mut parsed = Self {
eio: None,
transport: None,
sid: None,
};
for pair in query.split('&') {
let (name, value) = pair.split_once('=').unwrap_or((pair, ""));
match name {
"EIO" => parsed.eio = Some(value.to_string()),
"transport" => parsed.transport = Some(value.to_string()),
"sid" => parsed.sid = Some(value.to_string()),
_ => {}
}
}
parsed
}
}