feat(lib): update Tokio, bytes, http, h2, and http-body

This commit is contained in:
Sean McArthur
2019-12-03 14:36:20 -08:00
parent 131962c86a
commit cb3f39c2dc
51 changed files with 985 additions and 1305 deletions

View File

@@ -4,6 +4,7 @@ use std::env;
use std::io::{self, Write};
use hyper::Client;
use futures_util::StreamExt;
// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
@@ -24,7 +25,7 @@ async fn main() -> Result<()> {
// HTTPS requires picking a TLS implementation, so give a better
// warning if the user tries to request an 'https' URL.
let url = url.parse::<hyper::Uri>().unwrap();
if url.scheme_part().map(|s| s.as_ref()) != Some("http") {
if url.scheme_str() != Some("http") {
println!("This example only works with 'http' URLs.");
return Ok(());
}

View File

@@ -5,7 +5,7 @@
extern crate serde_derive;
use hyper::Client;
use futures_util::TryStreamExt;
use futures_util::StreamExt;
// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
@@ -27,9 +27,12 @@ async fn fetch_json(url: hyper::Uri) -> Result<Vec<User>> {
let client = Client::new();
// Fetch the url...
let res = client.get(url).await?;
let mut res = client.get(url).await?;
// asynchronously concatenate chunks of the body
let body = res.into_body().try_concat().await?;
let mut body = Vec::new();
while let Some(chunk) = res.body_mut().next().await {
body.extend_from_slice(&chunk?);
}
// try to parse as json with serde_json
let users = serde_json::from_slice(&body)?;

View File

@@ -2,12 +2,11 @@
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use hyper::service::{make_service_fn, service_fn};
use futures_util::TryStreamExt;
use futures_util::{StreamExt, TryStreamExt};
/// This is our service handler. It receives a Request, routes on its
/// path, and returns a Future of a Response.
async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
async fn echo(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
match (req.method(), req.uri().path()) {
// Serve some instructions at /
(&Method::GET, "/") => {
@@ -37,13 +36,17 @@ async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
// So here we do `.await` on the future, waiting on concatenating the full body,
// then afterwards the content can be reversed. Only then can we return a `Response`.
(&Method::POST, "/echo/reversed") => {
let whole_chunk = req.into_body().try_concat().await;
let mut whole_body = Vec::new();
while let Some(chunk) = req.body_mut().next().await {
whole_body.extend_from_slice(&chunk?);
}
let reversed_chunk = whole_chunk.map(move |chunk| {
chunk.iter().rev().cloned().collect::<Vec<u8>>()
})?;
Ok(Response::new(Body::from(reversed_chunk)))
let reversed_body = whole_body
.iter()
.rev()
.cloned()
.collect::<Vec<u8>>();
Ok(Response::new(Body::from(reversed_body)))
}
// Return the 404 Not Found for other routes.

View File

@@ -6,20 +6,24 @@ use hyper::service::{service_fn, make_service_fn};
use std::collections::HashMap;
use url::form_urlencoded;
use futures_util::TryStreamExt;
use futures_util::StreamExt;
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>";
static MISSING: &[u8] = b"Missing field";
static NOTNUMERIC: &[u8] = b"Number field is not numeric";
// Using service_fn, we can turn this function into a `Service`.
async fn param_example(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
async fn param_example(mut req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
match (req.method(), req.uri().path()) {
(&Method::GET, "/") | (&Method::GET, "/post") => {
Ok(Response::new(INDEX.into()))
},
(&Method::POST, "/post") => {
let b = req.into_body().try_concat().await?;
// Concatenate the body...
let mut b = Vec::new();
while let Some(chunk) = req.body_mut().next().await {
b.extend_from_slice(&chunk?);
}
// Parse the request body. form_urlencoded::parse
// always succeeds, but in general parsing may
// fail (for example, an invalid post of json), so

View File

@@ -1,7 +1,7 @@
#![deny(warnings)]
use tokio::io::AsyncReadExt;
use tokio_fs::File;
use tokio::fs::File;
use hyper::{Body, Method, Result, Request, Response, Server, StatusCode};
use hyper::service::{make_service_fn, service_fn};

View File

@@ -1,3 +1,5 @@
fn main() {}
/*
#![deny(warnings)]
use std::cell::Cell;
@@ -5,14 +7,24 @@ 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...
#[tokio::main(single_thread)]
async fn main() {
fn main() {
pretty_env_logger::init();
// Configure a runtime that runs everything on the current thread
let mut rt = tokio::runtime::Builder::new()
.enable_all()
.basic_scheduler()
.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(&mut rt, run());
}
async fn run() {
let addr = ([127, 0, 0, 1], 3000).into();
// Using a !Send request counter is fine on 1 thread...
@@ -36,12 +48,8 @@ async fn main() {
}
});
// 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)
.executor(LocalExec)
.serve(make_service);
println!("Listening on http://{}", addr);
@@ -52,3 +60,18 @@ async fn main() {
}
}
// 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);
}
}
*/

View File

@@ -21,7 +21,7 @@ impl Service<Request<Body>> for Svc {
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
let mut rsp = Response::builder();
let rsp = Response::builder();
let uri = req.uri();
if uri.path() != ROOT {

View File

@@ -51,7 +51,7 @@ async fn server_upgrade(req: Request<Body>) -> Result<Response<Body>> {
// 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 {
tokio::task::spawn(async move {
match req.into_body().on_upgrade().await {
Ok(upgraded) => {
if let Err(e) = server_upgraded_io(upgraded).await {
@@ -129,13 +129,13 @@ async fn main() {
// the server should be shutdown.
let (tx, rx) = oneshot::channel::<()>();
let server = server
.with_graceful_shutdown(async {
.with_graceful_shutdown(async move {
rx.await.ok();
});
// Spawn server on the default executor,
// which is usually a thread-pool from tokio default runtime.
hyper::rt::spawn(async {
tokio::task::spawn(async move {
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}

View File

@@ -3,7 +3,7 @@
use hyper::{Body, Chunk, Client, Method, Request, Response, Server, StatusCode, header};
use hyper::client::HttpConnector;
use hyper::service::{make_service_fn, service_fn};
use futures_util::{TryStreamExt};
use futures_util::{StreamExt, TryStreamExt};
type GenericError = Box<dyn std::error::Error + Send + Sync>;
type Result<T> = std::result::Result<T, GenericError>;
@@ -35,13 +35,17 @@ async fn client_request_response(
Ok(Response::new(body))
}
async fn api_post_response(req: Request<Body>) -> Result<Response<Body>> {
// A web api to run against
let entire_body = req.into_body().try_concat().await?;
// TODO: Replace all unwraps with proper error handling
let str = String::from_utf8(entire_body.to_vec())?;
let mut data : serde_json::Value = serde_json::from_str(&str)?;
async fn api_post_response(mut req: Request<Body>) -> Result<Response<Body>> {
// Concatenate the body...
let mut whole_body = Vec::new();
while let Some(chunk) = req.body_mut().next().await {
whole_body.extend_from_slice(&chunk?);
}
// Decode as JSON...
let mut data: serde_json::Value = serde_json::from_slice(&whole_body)?;
// Change the JSON...
data["test"] = serde_json::Value::from("test_value");
// And respond with the new JSON.
let json = serde_json::to_string(&data)?;
let response = Response::builder()
.status(StatusCode::OK)