I'm aware that the title of this issue reads really weird, so I will do my best to explain and start a discussion. My explanation is a little long, so if you're in a hurry here's the TL;DR:
As a server application writer, there's no way I can handle a Request<T>
without knowing what T
is. As a gateway/framework, there's no way I can enforce what T
is without making the application non-interoperable with other gateways/frameworks.
I've been working on a web application interface crate (think Ruby's Rack) for a couple of months, but I heard that this crate was in the works and decided to wait and see what this crate would look like. (I haven't publicly announced said crate, but I guess the cat is out of the bag now...)
I am inspecting it now to see how well this crate will work as an interface for HTTP applications, and I am encountering a problem with Request
. The problem is that the request is generic over T
, which is the body. There is no way for an application to realistically read a body of unknown type.
Consider the following web application (greatly simplified, but works as an example):
use http::{Request, Response};
pub fn my_application<Body>(request: Request<Body>) -> Response<&'static str> {
Response::new("Hello World!".into())
}
That works great, but what if we want to do something more interesting, like respond with the number of bytes in the request body? That should be easy:
use http::{Request, Response};
use std::io::Read;
pub fn my_application<Body: Read>(mut request: Request<Body>) -> Response<String> {
let mut buf = Vec::new();
let len = request.body_mut().read_to_end(&mut buf).unwrap();
Response::new(format!("{}", len))
}
That should work, but now we have a problem: we can no longer handle any type of request; we can only handle requests of a specific type. More specifically, we accept a request of type http::Request<T> where T: std::io::Read
. If we wanted to support Request<String>
, we'd have to re-implement our application function again for this type.
It gets worse. The above examples are stateless, which very few real-world applications are. We'd probably want to instead talk about these applications as traits instead of functions. Something like this comes to mind (more similar to my crate, actually similar to Iron too):
use http::{Request, Response};
pub trait HttpApp: Send + Sync + 'static {
fn handle<Req, Res>(&self, _: Request<Req>) -> Response<Res>;
}
There's an immediate problem with this; no HTTP server implementation can actually support the above trait. If I write a CGI gateway crate, I might provide a Request<Stdin>
, but now the application writers have to explicitly code against Request<Stdin>
, and interoperability is lost.
To put it another way: Request<T>
doesn't provide application writers a unified request type to code against, because Request
is generic over something without meaningful bounds. If application writers do code to a specific body type, then they are no longer coding against a crate-agnostic type. T
here is basically leaking the implementation into the interface.
I don't pretend to have an answer to this problem; maybe this crate is too high-level (or is it low-level?) for what I need. I am very interested in comments, disagreements, or suggestions.