Why
In the previous post we created a minimal graphql server in Rust, with juniper and hyper-rs libraries. The implementation was lacking HTTP/2 and response compression support. Hyper-rs server supports HTTP/2 by default, but most HTTP/2 clients require a TLS connection, that's why the browsers will fallback to HTTP/1.1.
How
Enabling Compression
We'll go with the simple route and use a blocking approach. It's good enough, since juniper execution is blocking, and it returns a concrete response, there isn't any streaming or async processing involved. We can use the flate2 library.
cargo add flate2
And wire it up directly in graphql handler.
use flate2::write::GzEncoder;
use flate2::Compression;
use std::io::Write;
async fn handle(req: Request<Body>) -> Result<Response<Body>, Infallible> {
let mut response = Response::new(Body::empty());
match (req.method(), req.uri().path()) {
(&Method::GET, "/") => {
*response.body_mut() = Body::from("Home");
}
(&Method::GET, "/playground") => {
*response.body_mut() =
Body::from(juniper::http::playground::playground_source("/graphql"));
(*response.headers_mut()).append(
"Content-Type",
HeaderValue::from_static("text/html;charset=utf-8"),
);
}
(&Method::POST, "/graphql") => {
let body = hyper::body::to_bytes(req)
.await
.expect("to be able to read the request body");
let query: juniper::http::GraphQLRequest =
serde_json::from_slice(&body)
.expect("to be able to deserialize the json request");
let graphql_response = query.execute(&SCHEMA, &STATE);
let status = if graphql_response.is_ok() {
StatusCode::OK
} else {
StatusCode::BAD_REQUEST
};
// Create the encoder, write the serialized response as bytes
let mut e = GzEncoder::new(Vec::new(), Compression::default());
e.write_all(
serde_json::to_string(&graphql_response)
.expect("to be able to serialize the response").as_bytes(),
).unwrap();
// set the Content-Encoding header
(*response.headers_mut()).append(
"Content-Encoding",
HeaderValue::from_static("gzip"),
);
let response_body = e.finish()
.expect("to be able to compress the response");
*response.status_mut() = status;
*response.body_mut() = Body::from(response_body);
}
_ => *response.status_mut() = StatusCode::NOT_FOUND,
}
Ok(response)
}
Configuring TLS
To add TLS support we'll want to include native_tls
and tokio_tls
libraries.
cargo add native_tls
cargo add tokio_tls
We'll want to generate a self-signed certificate. This can be done easily with openssl.
openssl req -x509 -newkey rsa:4096 -keyout server.pem -out server.pem -days 365 -nodes
openssl pkcs12 -export -out server_certificate.p12 -inkey server.pem -in server.pem
Conclusions
Configuring TLS Hyper is not as straightforward as in warp-rs, but still doable.