Skip to content

Update examples to async/await #1871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions examples/proxy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#![feature(async_await)]
#![deny(warnings)]

use hyper::{Client, Error, Server};
use hyper::service::{make_service_fn, service_fn};
use std::net::SocketAddr;

#[hyper::rt::main]
async fn main() {
pretty_env_logger::init();

let in_addr = ([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);

println!("Listening on http://{}", in_addr);
println!("Proxying on http://{}", out_addr);

if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
}
55 changes: 55 additions & 0 deletions examples/single_threaded.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#![feature(async_await)]
#![deny(warnings)]

use std::cell::Cell;
use std::rc::Rc;

use hyper::{Body, Error, Response, Server};
use hyper::service::{make_service_fn, service_fn};
use tokio::runtime::current_thread;

// Configure a runtime that runs everything on the current thread,
// which means it can spawn !Send futures...
#[hyper::rt::main(single_thread)]
async fn main() {
pretty_env_logger::init();

let addr = ([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 |_| {
// 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)
)))
}
}))
}
});

// Since the Server needs to spawn some background tasks, we needed
// to configure an Executor that can spawn !Send futures...
let exec = current_thread::TaskExecutor::current();

let server = Server::bind(&addr)
.executor(exec)
.serve(make_service);

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);
}
}

56 changes: 56 additions & 0 deletions examples/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#![feature(async_await)]
#![deny(warnings)]

use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};

use hyper::{Body, Error, Response, Server};
use hyper::service::{make_service_fn, service_fn};

#[hyper::rt::main]
async fn main() {
pretty_env_logger::init();

let addr = ([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.
//
// 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)))
)
}
}))
}
});

let server = Server::bind(&addr)
.serve(make_service);

println!("Listening on http://{}", addr);

if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
}

154 changes: 154 additions & 0 deletions examples/upgrades.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#![feature(async_await)]
#![deny(warnings)]

// Note: `hyper::upgrade` docs link to this upgrade.
use std::str;

use tokio::sync::oneshot;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

use hyper::{Body, Client, Request, Response, Server, StatusCode};
use hyper::header::{UPGRADE, HeaderValue};
use hyper::service::{make_service_fn, service_fn};
use hyper::upgrade::Upgraded;
use std::net::SocketAddr;

// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

/// Handle server-side I/O after HTTP upgraded.
async fn server_upgraded_io(mut upgraded: Upgraded) -> Result<()> {
// we have an upgraded connection that we can read and
// write on directly.
//
// since we completely control this example, we know exactly
// how many bytes the client will write, so just read exact...
let mut vec = vec![0; 7];
upgraded.read_exact(&mut vec).await?;
println!("server[foobar] recv: {:?}", str::from_utf8(&vec));

// and now write back the server 'foobar' protocol's
// response...
upgraded.write_all(b"barr=foo").await?;
println!("server[foobar] sent");
Ok(())
}

/// Our server HTTP handler to initiate HTTP upgrades.
async fn server_upgrade(req: Request<Body>) -> Result<Response<Body>> {
let mut res = Response::new(Body::empty());

// Send a 400 to any request that doesn't have
// an `Upgrade` header.
if !req.headers().contains_key(UPGRADE) {
*res.status_mut() = StatusCode::BAD_REQUEST;
return Ok(res);
}

// Setup a future that will eventually receive the upgraded
// connection and talk a new protocol, and spawn the future
// into the runtime.
//
// Note: This can't possibly be fulfilled until the 101 response
// is returned below, so it's better to spawn this future instead
// waiting for it to complete to then return a response.
hyper::rt::spawn(async move {
match req.into_body().on_upgrade().await {
Ok(upgraded) => {
if let Err(e) = server_upgraded_io(upgraded).await {
eprintln!("server foobar io error: {}", e)
};
}
Err(e) => eprintln!("upgrade error: {}", e)
}
});

// Now return a 101 Response saying we agree to the upgrade to some
// made-up 'foobar' protocol.
*res.status_mut() = StatusCode::SWITCHING_PROTOCOLS;
res.headers_mut().insert(UPGRADE, HeaderValue::from_static("foobar"));
Ok(res)
}

/// Handle client-side I/O after HTTP upgraded.
async fn client_upgraded_io(mut upgraded: Upgraded) -> Result<()> {
// We've gotten an upgraded connection that we can read
// and write directly on. Let's start out 'foobar' protocol.
upgraded.write_all(b"foo=bar").await?;
println!("client[foobar] sent");

let mut vec = Vec::new();
upgraded.read_to_end(&mut vec).await?;
println!("client[foobar] recv: {:?}", str::from_utf8(&vec));

Ok(())
}

/// Our client HTTP handler to initiate HTTP upgrades.
async fn client_upgrade_request(addr: SocketAddr) -> Result<()> {
let req = Request::builder()
.uri(format!("http://{}/", addr))
.header(UPGRADE, "foobar")
.body(Body::empty())
.unwrap();

let res = Client::new().request(req).await?;
if res.status() != StatusCode::SWITCHING_PROTOCOLS {
panic!("Our server didn't upgrade: {}", res.status());
}

match res.into_body().on_upgrade().await {
Ok(upgraded) => {
if let Err(e) = client_upgraded_io(upgraded).await {
eprintln!("client foobar io error: {}", e)
};
}
Err(e) => eprintln!("upgrade error: {}", e)
}

Ok(())
}

#[hyper::rt::main]
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 make_service = make_service_fn(|_| async {
Ok::<_, hyper::Error>(service_fn(server_upgrade))
});

let server = Server::bind(&addr)
.serve(make_service);

// We need the assigned address for the client to send it messages.
let addr = server.local_addr();

// 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 {
rx.await.ok();
});

// Spawn server on the default executor,
// which is usually a thread-pool from tokio default runtime.
hyper::rt::spawn(async {
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
});

// Client requests a HTTP connection upgrade.
let request = client_upgrade_request(addr.clone());
if let Err(e) = request.await {
eprintln!("client error: {}", e);
}

// Complete the oneshot so that the server stops
// listening and the process can close down.
let _ = tx.send(());
}
44 changes: 0 additions & 44 deletions examples_disabled/proxy.rs

This file was deleted.

Loading