-
-
Save snoyberg/cb72a9cbefc608ec15e05ed70ced1a6b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::sync::{atomic::AtomicUsize, Arc}; | |
mod http { | |
use std::collections::HashMap; | |
pub struct Request { | |
pub path_and_query: String, | |
pub headers: HashMap<String, String>, | |
pub body: Vec<u8>, | |
} | |
#[derive(Debug)] | |
pub struct Response { | |
pub status: u32, | |
pub headers: HashMap<String, String>, | |
pub body: Vec<u8>, | |
} | |
} | |
mod fakeserver { | |
use std::collections::HashMap; | |
use tower::{Service, ServiceExt}; | |
pub async fn run<App>(mut app: App) | |
where | |
App: Service<crate::http::Request, Response = crate::http::Response>, | |
App::Error: std::fmt::Debug, | |
App::Future: Send + 'static, | |
{ | |
loop { | |
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; | |
let req = crate::http::Request { | |
path_and_query: "/fake/path?page=1".to_owned(), | |
headers: HashMap::new(), | |
body: Vec::new(), | |
}; | |
let app = match app.ready().await { | |
Err(e) => { | |
eprintln!("Service not able to accept requests: {:?}", e); | |
continue; | |
} | |
Ok(app) => app, | |
}; | |
let future = app.call(req); | |
tokio::spawn(async move { | |
match future.await { | |
Ok(res) => println!("Successful response: {:?}", res), | |
Err(e) => eprintln!("Error occurred: {:?}", e), | |
} | |
}); | |
} | |
} | |
} | |
mod util { | |
use std::{future::Future, task::Poll}; | |
pub struct AppFn<F> { | |
f: F, | |
} | |
pub fn app_fn<F, Ret>(f: F) -> AppFn<F> | |
where | |
F: FnMut(crate::http::Request) -> Ret, | |
Ret: Future<Output = Result<crate::http::Response, anyhow::Error>>, | |
{ | |
AppFn { f } | |
} | |
impl<F, Ret> tower::Service<crate::http::Request> for AppFn<F> | |
where | |
F: FnMut(crate::http::Request) -> Ret, | |
Ret: Future<Output = Result<crate::http::Response, anyhow::Error>>, | |
{ | |
type Response = crate::http::Response; | |
type Error = anyhow::Error; | |
type Future = Ret; | |
fn poll_ready( | |
&mut self, | |
_cx: &mut std::task::Context<'_>, | |
) -> Poll<Result<(), Self::Error>> { | |
Poll::Ready(Ok(())) // always ready to accept a connection | |
} | |
fn call(&mut self, req: crate::http::Request) -> Self::Future { | |
(self.f)(req) | |
} | |
} | |
} | |
#[tokio::main] | |
async fn main() { | |
let counter = Arc::new(AtomicUsize::new(0)); | |
fakeserver::run(util::app_fn(move |mut req| { | |
// need to clone this from the closure before moving it into the async block | |
let counter = counter.clone(); | |
async move { | |
println!("Handling a request for {}", req.path_and_query); | |
let counter = counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst); | |
anyhow::ensure!(counter % 4 != 2, "Failing 25% of the time, just for fun"); | |
req.headers | |
.insert("X-Counter".to_owned(), counter.to_string()); | |
let res = crate::http::Response { | |
status: 200, | |
headers: req.headers, | |
body: req.body, | |
}; | |
Ok::<_, anyhow::Error>(res) | |
} | |
})) | |
.await; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment