feat(client,server): remove tcp feature and code (#2929)

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>
This commit is contained in:
Sean McArthur
2022-07-29 10:07:09 -07:00
committed by GitHub
parent d4b5bd4ee6
commit 0c8ee93d7f
40 changed files with 1565 additions and 2676 deletions

View File

@@ -2,8 +2,9 @@
#![warn(rust_2018_idioms)]
use std::env;
use hyper::{body::HttpBody as _, Client};
use hyper::{body::HttpBody as _, Body, Request};
use tokio::io::{self, AsyncWriteExt as _};
use tokio::net::TcpStream;
// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
@@ -33,9 +34,20 @@ async fn main() -> Result<()> {
}
async fn fetch_url(url: hyper::Uri) -> Result<()> {
let client = Client::new();
let host = url.host().expect("uri has no host");
let port = url.port_u16().unwrap_or(80);
let addr = format!("{}:{}", host, port);
let stream = TcpStream::connect(addr).await?;
let mut res = client.get(url).await?;
let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});
let req = Request::builder().uri(url).body(Body::empty()).unwrap();
let mut res = sender.send_request(req).await?;
println!("Response: {}", res.status());
println!("Headers: {:#?}\n", res.headers());

View File

@@ -1,9 +1,10 @@
#![deny(warnings)]
#![warn(rust_2018_idioms)]
use hyper::body::Buf;
use hyper::Client;
use hyper::Body;
use hyper::{body::Buf, Request};
use serde::Deserialize;
use tokio::net::TcpStream;
// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
@@ -22,10 +23,22 @@ async fn main() -> Result<()> {
}
async fn fetch_json(url: hyper::Uri) -> Result<Vec<User>> {
let client = Client::new();
let host = url.host().expect("uri has no host");
let port = url.port_u16().unwrap_or(80);
let addr = format!("{}:{}", host, port);
let stream = TcpStream::connect(addr).await?;
let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});
// Fetch the url...
let res = client.get(url).await?;
let req = Request::builder().uri(url).body(Body::empty()).unwrap();
let res = sender.send_request(req).await?;
// asynchronously aggregate the chunks of the body
let body = hyper::body::aggregate(res).await?;

View File

@@ -1,7 +1,11 @@
#![deny(warnings)]
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use std::net::SocketAddr;
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{Body, Method, Request, Response, StatusCode};
use tokio::net::TcpListener;
/// This is our service handler. It receives a Request, routes on its
/// path, and returns a Future of a Response.
@@ -51,15 +55,17 @@ async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = ([127, 0, 0, 1], 3000).into();
let service = make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(echo)) });
let server = Server::bind(&addr).serve(service);
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
server.await?;
Ok(())
tokio::task::spawn(async move {
if let Err(err) = Http::new().serve_connection(stream, service_fn(echo)).await {
println!("Error serving connection: {:?}", err);
}
});
}
}

View File

@@ -1,51 +1,63 @@
#![deny(warnings)]
use hyper::service::{make_service_fn, service_fn};
use hyper::{Client, Error, Server};
use hyper::{server::conn::Http, service::service_fn};
use std::net::SocketAddr;
use tokio::net::{TcpListener, TcpStream};
#[tokio::main]
async fn main() {
async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
let in_addr = ([127, 0, 0, 1], 3001).into();
let in_addr: SocketAddr = ([127, 0, 0, 1], 3001).into();
let out_addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
let client_main = Client::new();
let out_addr_clone = out_addr.clone();
// The closure inside `make_service_fn` is run for each connection,
// creating a 'service' to handle requests for that specific connection.
let make_service = make_service_fn(move |_| {
let client = client_main.clone();
async move {
// This is the `Service` that will handle the connection.
// `service_fn` is a helper to convert a function that
// returns a Response into a `Service`.
Ok::<_, Error>(service_fn(move |mut req| {
let uri_string = format!(
"http://{}{}",
out_addr_clone,
req.uri()
.path_and_query()
.map(|x| x.as_str())
.unwrap_or("/")
);
let uri = uri_string.parse().unwrap();
*req.uri_mut() = uri;
client.request(req)
}))
}
});
let server = Server::bind(&in_addr).serve(make_service);
let listener = TcpListener::bind(in_addr).await?;
println!("Listening on http://{}", in_addr);
println!("Proxying on http://{}", out_addr);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
loop {
let (stream, _) = listener.accept().await?;
// This is the `Service` that will handle the connection.
// `service_fn` is a helper to convert a function that
// returns a Response into a `Service`.
let service = service_fn(move |mut req| {
let uri_string = format!(
"http://{}{}",
out_addr_clone,
req.uri()
.path_and_query()
.map(|x| x.as_str())
.unwrap_or("/")
);
let uri = uri_string.parse().unwrap();
*req.uri_mut() = uri;
let host = req.uri().host().expect("uri has no host");
let port = req.uri().port_u16().unwrap_or(80);
let addr = format!("{}:{}", host, port);
async move {
let client_stream = TcpStream::connect(addr).await.unwrap();
let (mut sender, conn) = hyper::client::conn::handshake(client_stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});
sender.send_request(req).await
}
});
tokio::task::spawn(async move {
if let Err(err) = Http::new().serve_connection(stream, service).await {
println!("Failed to servce connection: {:?}", err);
}
});
}
}

View File

@@ -1,9 +1,12 @@
#![deny(warnings)]
use std::convert::Infallible;
use std::net::SocketAddr;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("Hello World!")))
@@ -13,22 +16,20 @@ async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> {
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pretty_env_logger::init();
// For every connection, we must make a `Service` to handle all
// incoming HTTP requests on said connection.
let make_svc = make_service_fn(|_conn| {
// This is the `Service` that will handle the connection.
// `service_fn` is a helper to convert a function that
// returns a Response into a `Service`.
async { Ok::<_, Infallible>(service_fn(hello)) }
});
let addr = ([127, 0, 0, 1], 3000).into();
let server = Server::bind(&addr).serve(make_svc);
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
server.await?;
Ok(())
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, service_fn(hello))
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
}

View File

@@ -1,15 +1,14 @@
#![deny(warnings)]
use std::convert::Infallible;
use std::net::SocketAddr;
use hyper::service::{make_service_fn, service_fn};
use hyper::client::conn::Builder;
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::upgrade::Upgraded;
use hyper::{Body, Client, Method, Request, Response, Server};
use hyper::{Body, Method, Request, Response};
use tokio::net::TcpStream;
type HttpClient = Client<hyper::client::HttpConnector>;
use tokio::net::{TcpListener, TcpStream};
// To try this example:
// 1. cargo run --example http_proxy
@@ -19,32 +18,29 @@ type HttpClient = Client<hyper::client::HttpConnector>;
// 3. send requests
// $ curl -i https://www.some_domain.com/
#[tokio::main]
async fn main() {
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 8100));
let client = Client::builder()
.http1_title_case_headers(true)
.http1_preserve_header_case(true)
.build_http();
let make_service = make_service_fn(move |_| {
let client = client.clone();
async move { Ok::<_, Infallible>(service_fn(move |req| proxy(client.clone(), req))) }
});
let server = Server::bind(&addr)
.http1_preserve_header_case(true)
.http1_title_case_headers(true)
.serve(make_service);
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
loop {
let (stream, _) = listener.accept().await?;
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.http1_preserve_header_case(true)
.http1_title_case_headers(true)
.serve_connection(stream, service_fn(proxy))
.await
{
println!("Failed to serve connection: {:?}", err);
}
});
}
}
async fn proxy(client: HttpClient, req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
async fn proxy(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
println!("req: {:?}", req);
if Method::CONNECT == req.method() {
@@ -82,7 +78,24 @@ async fn proxy(client: HttpClient, req: Request<Body>) -> Result<Response<Body>,
Ok(resp)
}
} else {
client.request(req).await
let host = req.uri().host().expect("uri has no host");
let port = req.uri().port_u16().unwrap_or(80);
let addr = format!("{}:{}", host, port);
let stream = TcpStream::connect(addr).await.unwrap();
let (mut sender, conn) = Builder::new()
.http1_preserve_header_case(true)
.http1_title_case_headers(true)
.handshake(stream)
.await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});
sender.send_request(req).await
}
}

View File

@@ -1,9 +1,13 @@
#![deny(warnings)]
#![warn(rust_2018_idioms)]
use std::net::SocketAddr;
use futures_util::future::join;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
static INDEX1: &[u8] = b"The 1st service!";
static INDEX2: &[u8] = b"The 2nd service!";
@@ -20,16 +24,40 @@ async fn index2(_: Request<Body>) -> Result<Response<Body>, hyper::Error> {
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pretty_env_logger::init();
let addr1 = ([127, 0, 0, 1], 1337).into();
let addr2 = ([127, 0, 0, 1], 1338).into();
let addr1: SocketAddr = ([127, 0, 0, 1], 1337).into();
let addr2: SocketAddr = ([127, 0, 0, 1], 1338).into();
let srv1 = Server::bind(&addr1).serve(make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(index1))
}));
let srv1 = async move {
let listener = TcpListener::bind(addr1).await.unwrap();
loop {
let (stream, _) = listener.accept().await.unwrap();
let srv2 = Server::bind(&addr2).serve(make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(index2))
}));
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, service_fn(index1))
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
};
let srv2 = async move {
let listener = TcpListener::bind(addr2).await.unwrap();
loop {
let (stream, _) = listener.accept().await.unwrap();
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, service_fn(index2))
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
};
println!("Listening on http://{} and http://{}", addr1, addr2);

View File

@@ -1,10 +1,13 @@
// #![deny(warnings)] // FIXME: https://github.com/rust-lang/rust/issues/62411
#![warn(rust_2018_idioms)]
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{Body, Method, Request, Response, StatusCode};
use tokio::net::TcpListener;
use std::collections::HashMap;
use std::net::SocketAddr;
use url::form_urlencoded;
static INDEX: &[u8] = b"<html><body><form action=\"post\" method=\"post\">Name: <input type=\"text\" name=\"name\"><br>Number: <input type=\"text\" name=\"number\"><br><input type=\"submit\"></body></html>";
@@ -102,15 +105,20 @@ async fn param_example(req: Request<Body>) -> Result<Response<Body>, hyper::Erro
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pretty_env_logger::init();
let addr = ([127, 0, 0, 1], 1337).into();
let server = Server::bind(&addr).serve(make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(param_example))
}));
let addr: SocketAddr = ([127, 0, 0, 1], 1337).into();
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
server.await?;
Ok(())
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, service_fn(param_example))
.await
{
println!("Error serving connection: {:?}", err);
}
});
}
}

View File

@@ -1,26 +1,36 @@
#![deny(warnings)]
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Result, Server, StatusCode};
use std::net::SocketAddr;
use hyper::server::conn::Http;
use tokio::net::TcpListener;
use hyper::service::service_fn;
use hyper::{Body, Method, Request, Response, Result, StatusCode};
static INDEX: &str = "examples/send_file_index.html";
static NOTFOUND: &[u8] = b"Not Found";
#[tokio::main]
async fn main() {
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap();
let make_service =
make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(response_examples)) });
let server = Server::bind(&addr).serve(make_service);
let addr: SocketAddr = "127.0.0.1:1337".parse().unwrap();
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
loop {
let (stream, _) = listener.accept().await?;
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, service_fn(response_examples))
.await
{
println!("Failed to serve connection: {:?}", err);
}
});
}
}

View File

@@ -1,7 +1,10 @@
use hyper::server::conn::Http;
use hyper::service::Service;
use hyper::{Body, Request, Response, Server};
use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -9,13 +12,23 @@ type Counter = i32;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = ([127, 0, 0, 1], 3000).into();
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
let server = Server::bind(&addr).serve(MakeSvc { counter: 81818 });
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
server.await?;
Ok(())
loop {
let (stream, _) = listener.accept().await?;
tokio::task::spawn(async move {
if let Err(err) = Http::new()
.serve_connection(stream, Svc { counter: 81818 })
.await
{
println!("Failed to serve connection: {:?}", err);
}
});
}
}
struct Svc {
@@ -54,23 +67,3 @@ impl Service<Request<Body>> for Svc {
Box::pin(async { res })
}
}
struct MakeSvc {
counter: Counter,
}
impl<T> Service<T> for MakeSvc {
type Response = Svc;
type Error = hyper::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: T) -> Self::Future {
let counter = self.counter.clone();
let fut = async move { Ok(Svc { counter }) };
Box::pin(fut)
}
}

View File

@@ -1,13 +1,15 @@
#![deny(warnings)]
use hyper::server::conn::Http;
use std::cell::Cell;
use std::net::SocketAddr;
use std::rc::Rc;
use tokio::sync::oneshot;
use tokio::net::TcpListener;
use hyper::body::{Bytes, HttpBody};
use hyper::header::{HeaderMap, HeaderValue};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Error, Response, Server};
use hyper::service::service_fn;
use hyper::{Error, Response};
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
@@ -46,7 +48,7 @@ impl HttpBody for Body {
}
}
fn main() {
fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
// Configure a runtime that runs everything on the current thread
@@ -57,43 +59,39 @@ fn main() {
// Combine it with a `LocalSet, which means it can spawn !Send futures...
let local = tokio::task::LocalSet::new();
local.block_on(&rt, run());
local.block_on(&rt, run())
}
async fn run() {
let addr = ([127, 0, 0, 1], 3000).into();
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 make_service = make_service_fn(move |_| {
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();
async move {
Ok::<_, Error>(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)))) }
}))
}
});
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)))) }
});
let server = Server::bind(&addr).executor(LocalExec).serve(make_service);
// Just shows that with_graceful_shutdown compiles with !Send,
// !Sync HttpBody.
let (_tx, rx) = oneshot::channel::<()>();
let server = server.with_graceful_shutdown(async move {
rx.await.ok();
});
println!("Listening on http://{}", addr);
// The server would block on current thread to await !Send futures.
if let Err(e) = server.await {
eprintln!("server error: {}", e);
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);
}
});
}
}

View File

@@ -1,52 +1,46 @@
#![deny(warnings)]
use std::net::SocketAddr;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Error, Response, Server};
use hyper::{server::conn::Http, service::service_fn};
use hyper::{Body, Error, Response};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() {
async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
let addr = ([127, 0, 0, 1], 3000).into();
let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();
// For the most basic of state, we just share a counter, that increments
// with each request, and we send its value back in the response.
let counter = Arc::new(AtomicUsize::new(0));
// The closure inside `make_service_fn` is run for each connection,
// creating a 'service' to handle requests for that specific connection.
let make_service = make_service_fn(move |_| {
// While the state was moved into the make_service closure,
// we need to clone it here because this closure is called
// once for every connection.
//
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
// Each connection could send multiple requests, so
// the `Service` needs a clone to handle later requests.
let counter = counter.clone();
async move {
// This is the `Service` that will handle the connection.
// `service_fn` is a helper to convert a function that
// returns a Response into a `Service`.
Ok::<_, Error>(service_fn(move |_req| {
// Get the current count, and also increment by 1, in a single
// atomic operation.
let count = counter.fetch_add(1, Ordering::AcqRel);
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", count)))) }
}))
// This is the `Service` that will handle the connection.
// `service_fn` is a helper to convert a function that
// returns a Response into a `Service`.
let service = service_fn(move |_req| {
// Get the current count, and also increment by 1, in a single
// atomic operation.
let count = counter.fetch_add(1, Ordering::AcqRel);
async move { Ok::<_, Error>(Response::new(Body::from(format!("Request #{}", count)))) }
});
if let Err(err) = Http::new().serve_connection(stream, service).await {
println!("Error serving connection: {:?}", err);
}
});
let server = Server::bind(&addr).serve(make_service);
println!("Listening on http://{}", addr);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
}

View File

@@ -1,20 +1,20 @@
#![deny(warnings)]
use hyper::client::conn::Builder;
use hyper::client::connect::HttpConnector;
use hyper::client::service::Connect;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use hyper::service::Service;
use hyper::{Body, Request};
use hyper::{Body, Request, Response};
use tokio::net::TcpStream;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
pretty_env_logger::init();
let mut mk_svc = Connect::new(HttpConnector::new(), Builder::new());
let uri = "http://127.0.0.1:8080".parse::<http::Uri>()?;
let mut svc = mk_svc.call(uri.clone()).await?;
let mut svc = Connector;
let body = Body::empty();
@@ -25,3 +25,35 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
struct Connector;
impl Service<Request<Body>> for Connector {
type Response = Response<Body>;
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
Box::pin(async move {
let host = req.uri().host().expect("no host in uri");
let port = req.uri().port_u16().expect("no port in uri");
let stream = TcpStream::connect(format!("{}:{}", host, port)).await?;
let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection error: {:?}", err);
}
});
let res = sender.send_request(req).await?;
Ok(res)
})
}
}

View File

@@ -1,10 +1,13 @@
#![deny(warnings)]
use std::net::SocketAddr;
use std::task::{Context, Poll};
use futures_util::future;
use hyper::server::conn::Http;
use hyper::service::Service;
use hyper::{Body, Request, Response, Server};
use hyper::{Body, Request, Response};
use tokio::net::TcpListener;
const ROOT: &str = "/";
@@ -36,33 +39,22 @@ impl Service<Request<Body>> for Svc {
}
}
pub struct MakeSvc;
impl<T> Service<T> for MakeSvc {
type Response = Svc;
type Error = std::io::Error;
type Future = future::Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Ok(()).into()
}
fn call(&mut self, _: T) -> Self::Future {
future::ok(Svc)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap();
let server = Server::bind(&addr).serve(MakeSvc);
let addr: SocketAddr = "127.0.0.1:1337".parse().unwrap();
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
server.await?;
loop {
let (stream, _) = listener.accept().await?;
Ok(())
tokio::task::spawn(async move {
if let Err(err) = Http::new().serve_connection(stream, Svc).await {
println!("Failed to serve connection: {:?}", err);
}
});
}
}

View File

@@ -3,13 +3,15 @@
// Note: `hyper::upgrade` docs link to this upgrade.
use std::str;
use hyper::server::conn::Http;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::oneshot;
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::watch;
use hyper::header::{HeaderValue, UPGRADE};
use hyper::service::{make_service_fn, service_fn};
use hyper::service::service_fn;
use hyper::upgrade::Upgraded;
use hyper::{Body, Client, Request, Response, Server, StatusCode};
use hyper::{Body, Request, Response, StatusCode};
use std::net::SocketAddr;
// A simple type alias so as to DRY.
@@ -92,7 +94,17 @@ async fn client_upgrade_request(addr: SocketAddr) -> Result<()> {
.body(Body::empty())
.unwrap();
let res = Client::new().request(req).await?;
let stream = TcpStream::connect(addr).await?;
let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection failed: {:?}", err);
}
});
let res = sender.send_request(req).await?;
if res.status() != StatusCode::SWITCHING_PROTOCOLS {
panic!("Our server didn't upgrade: {}", res.status());
}
@@ -114,28 +126,52 @@ async fn main() {
// For this example, we just make a server and our own client to talk to
// it, so the exact port isn't important. Instead, let the OS give us an
// unused port.
let addr = ([127, 0, 0, 1], 0).into();
let addr: SocketAddr = ([127, 0, 0, 1], 0).into();
let make_service =
make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(server_upgrade)) });
let server = Server::bind(&addr).serve(make_service);
let listener = TcpListener::bind(addr).await.expect("failed to bind");
// We need the assigned address for the client to send it messages.
let addr = server.local_addr();
let addr = listener.local_addr().unwrap();
// For this example, a oneshot is used to signal that after 1 request,
// the server should be shutdown.
let (tx, rx) = oneshot::channel::<()>();
let server = server.with_graceful_shutdown(async move {
rx.await.ok();
});
let (tx, mut rx) = watch::channel(false);
// Spawn server on the default executor,
// which is usually a thread-pool from tokio default runtime.
tokio::task::spawn(async move {
if let Err(e) = server.await {
eprintln!("server error: {}", e);
loop {
tokio::select! {
res = listener.accept() => {
let (stream, _) = res.expect("Failed to accept");
let mut rx = rx.clone();
tokio::task::spawn(async move {
let conn = Http::new().serve_connection(stream, service_fn(server_upgrade));
// Don't forget to enable upgrades on the connection.
let mut conn = conn.with_upgrades();
let mut conn = Pin::new(&mut conn);
tokio::select! {
res = &mut conn => {
if let Err(err) = res {
println!("Error serving connection: {:?}", err);
return;
}
}
// Continue polling the connection after enabling graceful shutdown.
_ = rx.changed() => {
conn.graceful_shutdown();
}
}
});
}
_ = rx.changed() => {
break;
}
}
}
});
@@ -147,5 +183,5 @@ async fn main() {
// Complete the oneshot so that the server stops
// listening and the process can close down.
let _ = tx.send(());
let _ = tx.send(true);
}

View File

@@ -1,9 +1,12 @@
#![deny(warnings)]
use std::net::SocketAddr;
use bytes::Buf;
use hyper::client::HttpConnector;
use hyper::service::{make_service_fn, service_fn};
use hyper::{header, Body, Client, Method, Request, Response, Server, StatusCode};
use hyper::server::conn::Http;
use hyper::service::service_fn;
use hyper::{header, Body, Method, Request, Response, StatusCode};
use tokio::net::{TcpListener, TcpStream};
type GenericError = Box<dyn std::error::Error + Send + Sync>;
type Result<T> = std::result::Result<T, GenericError>;
@@ -14,7 +17,7 @@ static NOTFOUND: &[u8] = b"Not Found";
static POST_DATA: &str = r#"{"original": "data"}"#;
static URL: &str = "http://127.0.0.1:1337/json_api";
async fn client_request_response(client: &Client<HttpConnector>) -> Result<Response<Body>> {
async fn client_request_response() -> Result<Response<Body>> {
let req = Request::builder()
.method(Method::POST)
.uri(URL)
@@ -22,7 +25,19 @@ async fn client_request_response(client: &Client<HttpConnector>) -> Result<Respo
.body(POST_DATA.into())
.unwrap();
let web_res = client.request(req).await?;
let host = req.uri().host().expect("uri has no host");
let port = req.uri().port_u16().expect("uri has no port");
let stream = TcpStream::connect(format!("{}:{}", host, port)).await?;
let (mut sender, conn) = hyper::client::conn::handshake(stream).await?;
tokio::task::spawn(async move {
if let Err(err) = conn.await {
println!("Connection error: {:?}", err);
}
});
let web_res = sender.send_request(req).await?;
let res_body = web_res.into_body();
@@ -60,13 +75,10 @@ async fn api_get_response() -> Result<Response<Body>> {
Ok(res)
}
async fn response_examples(
req: Request<Body>,
client: Client<HttpConnector>,
) -> Result<Response<Body>> {
async fn response_examples(req: Request<Body>) -> Result<Response<Body>> {
match (req.method(), req.uri().path()) {
(&Method::GET, "/") | (&Method::GET, "/index.html") => Ok(Response::new(INDEX.into())),
(&Method::GET, "/test.html") => client_request_response(&client).await,
(&Method::GET, "/test.html") => client_request_response().await,
(&Method::POST, "/json_api") => api_post_response(req).await,
(&Method::GET, "/json_api") => api_get_response().await,
_ => {
@@ -83,27 +95,19 @@ async fn response_examples(
async fn main() -> Result<()> {
pretty_env_logger::init();
let addr = "127.0.0.1:1337".parse().unwrap();
// Share a `Client` with all `Service`s
let client = Client::new();
let new_service = make_service_fn(move |_| {
// Move a clone of `client` into the `service_fn`.
let client = client.clone();
async {
Ok::<_, GenericError>(service_fn(move |req| {
// Clone again to ensure that client outlives this closure.
response_examples(req, client.to_owned())
}))
}
});
let server = Server::bind(&addr).serve(new_service);
let addr: SocketAddr = "127.0.0.1:1337".parse().unwrap();
let listener = TcpListener::bind(&addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
server.await?;
tokio::task::spawn(async move {
let service = service_fn(move |req| response_examples(req));
Ok(())
if let Err(err) = Http::new().serve_connection(stream, service).await {
println!("Failed to serve connection: {:?}", err);
}
});
}
}