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, transport: Option, sid: Option, } 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 } }