feat(lib): update Tokio, bytes, http, h2, and http-body
This commit is contained in:
@@ -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(());
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user