Skip to content

Instantly share code, notes, and snippets.

@tsidea
Created February 1, 2021 19:04
Show Gist options
  • Save tsidea/a9d9de1f74b4d762401baace4ba9c4ae to your computer and use it in GitHub Desktop.
Save tsidea/a9d9de1f74b4d762401baace4ba9c4ae to your computer and use it in GitHub Desktop.
complete request handler for ws and http
use std::convert::Infallible;
use std::net::SocketAddr;
use hyper::{header, upgrade, StatusCode, Body, Request, Response, Server, server::conn::AddrStream};
use hyper::service::{make_service_fn, service_fn};
use tokio_tungstenite::WebSocketStream;
use tungstenite::{handshake, Error};
use futures::stream::StreamExt;
async fn handle_request(mut request: Request<Body>, remote_addr: SocketAddr) -> Result<Response<Body>, Infallible> {
match (request.uri().path(), request.headers().contains_key(header::UPGRADE)) {
//if the request is ws_echo and the request headers contains an Upgrade key
("/ws_echo", true) => {
//assume request is a handshake, so create the handshake response
let response =
match handshake::server::create_response_with_body(&request, || Body::empty()) {
Ok(response) => {
//in case the handshake response creation succeeds,
//spawn a task to handle the websocket connection
tokio::spawn(async move {
//using the hyper feature of upgrading a connection
match upgrade::on(&mut request).await {
//if successfully upgraded
Ok(upgraded) => {
//create a websocket stream from the upgraded object
let ws_stream = WebSocketStream::from_raw_socket(
//pass the upgraded object
//as the base layer stream of the Websocket
upgraded,
tokio_tungstenite::tungstenite::protocol::Role::Server,
None,
).await;
//we can split the stream into a sink and a stream
let (ws_write, ws_read) = ws_stream.split();
//forward the stream to the sink to achieve echo
match ws_read.forward(ws_write).await {
Ok(_) => {},
Err(Error::ConnectionClosed) =>
println!("Connection closed normally"),
Err(e) =>
println!("error creating echo stream on \
connection from address {}. \
Error is {}", remote_addr, e),
};
},
Err(e) =>
println!("error when trying to upgrade connection \
from address {} to websocket connection. \
Error is: {}", remote_addr, e),
}
});
//return the response to the handshake request
response
},
Err(error) => {
//probably the handshake request is not up to spec for websocket
println!("Failed to create websocket response \
to request from address {}. \
Error is: {}", remote_addr, error);
let mut res = Response::new(Body::from(format!("Failed to create websocket: {}", error)));
*res.status_mut() = StatusCode::BAD_REQUEST;
return Ok(res);
}
};
Ok::<_, Infallible>(response)
},
("/ws_echo", false) => {
//handle the case where the url is /ws_echo, but does not have an Upgrade field
Ok(Response::new(Body::from(format!("Getting even warmer, \
try connecting to this url \
using a websocket client.\n"))))
},
(url@_, false) => {
//handle any other url without an Upgrade header field
Ok(Response::new(Body::from(format!("This {} url doesn't do \
much, try accessing the \
/ws_echo url instead.\n", url))))
},
(_, true) => {
//handle any other url with an Upgrade header field
Ok(Response::new(Body::from(format!("Getting warmer, but I'm \
only letting you connect \
via websocket over on \
/ws_echo, try that url.\n"))))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment