This removes the `tcp` feature from hyper's `Cargo.toml`, and the code it enabled: - `HttpConnector` - `GaiResolver` - `AddrStream` And parts of `Client` and `Server` that used those types. Alternatives will be available in the `hyper-util` crate. Closes #2856 Co-authored-by: MrGunflame <mrgunflame@protonmail.com>
112 lines
3.0 KiB
Rust
112 lines
3.0 KiB
Rust
#![deny(warnings)]
|
|
|
|
use hyper::server::conn::Http;
|
|
use std::cell::Cell;
|
|
use std::net::SocketAddr;
|
|
use std::rc::Rc;
|
|
use tokio::net::TcpListener;
|
|
|
|
use hyper::body::{Bytes, HttpBody};
|
|
use hyper::header::{HeaderMap, HeaderValue};
|
|
use hyper::service::service_fn;
|
|
use hyper::{Error, Response};
|
|
use std::marker::PhantomData;
|
|
use std::pin::Pin;
|
|
use std::task::{Context, Poll};
|
|
|
|
struct Body {
|
|
// Our Body type is !Send and !Sync:
|
|
_marker: PhantomData<*const ()>,
|
|
data: Option<Bytes>,
|
|
}
|
|
|
|
impl From<String> for Body {
|
|
fn from(a: String) -> Self {
|
|
Body {
|
|
_marker: PhantomData,
|
|
data: Some(a.into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl HttpBody for Body {
|
|
type Data = Bytes;
|
|
type Error = Error;
|
|
|
|
fn poll_data(
|
|
self: Pin<&mut Self>,
|
|
_: &mut Context<'_>,
|
|
) -> Poll<Option<Result<Self::Data, Self::Error>>> {
|
|
Poll::Ready(self.get_mut().data.take().map(Ok))
|
|
}
|
|
|
|
fn poll_trailers(
|
|
self: Pin<&mut Self>,
|
|
_: &mut Context<'_>,
|
|
) -> Poll<Result<Option<HeaderMap<HeaderValue>>, Self::Error>> {
|
|
Poll::Ready(Ok(None))
|
|
}
|
|
}
|
|
|
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
pretty_env_logger::init();
|
|
|
|
// Configure a runtime that runs everything on the current thread
|
|
let rt = tokio::runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()
|
|
.expect("build runtime");
|
|
|
|
// Combine it with a `LocalSet, which means it can spawn !Send futures...
|
|
let local = tokio::task::LocalSet::new();
|
|
local.block_on(&rt, run())
|
|
}
|
|
|
|
async fn run() -> Result<(), Box<dyn std::error::Error>> {
|
|
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
|
|
|
|
// Using a !Send request counter is fine on 1 thread...
|
|
let counter = Rc::new(Cell::new(0));
|
|
|
|
let listener = TcpListener::bind(addr).await?;
|
|
println!("Listening on http://{}", addr);
|
|
loop {
|
|
let (stream, _) = listener.accept().await?;
|
|
|
|
// For each connection, clone the counter to use in our service...
|
|
let cnt = counter.clone();
|
|
|
|
let service = service_fn(move |_| {
|
|
let prev = cnt.get();
|
|
cnt.set(prev + 1);
|
|
let value = cnt.get();
|
|
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", value)))) }
|
|
});
|
|
|
|
tokio::task::spawn_local(async move {
|
|
if let Err(err) = Http::new()
|
|
.with_executor(LocalExec)
|
|
.serve_connection(stream, service)
|
|
.await
|
|
{
|
|
println!("Error serving connection: {:?}", err);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Since the Server needs to spawn some background tasks, we needed
|
|
// to configure an Executor that can spawn !Send futures...
|
|
#[derive(Clone, Copy, Debug)]
|
|
struct LocalExec;
|
|
|
|
impl<F> hyper::rt::Executor<F> for LocalExec
|
|
where
|
|
F: std::future::Future + 'static, // not requiring `Send`
|
|
{
|
|
fn execute(&self, fut: F) {
|
|
// This will spawn into the currently running `LocalSet`.
|
|
tokio::task::spawn_local(fut);
|
|
}
|
|
}
|