-
-
Save snoyberg/c6c54ed38ec8fac966e362eb212ab421 to your computer and use it in GitHub Desktop.
Learning Tower
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
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 app { | |
use std::{ | |
future::Future, | |
pin::Pin, | |
sync::{atomic::AtomicUsize, Arc}, | |
task::Poll, | |
}; | |
pub struct DemoApp { | |
counter: Arc<AtomicUsize>, | |
} | |
impl Default for DemoApp { | |
fn default() -> Self { | |
DemoApp { | |
counter: Arc::new(AtomicUsize::new(0)), | |
} | |
} | |
} | |
impl tower::Service<crate::http::Request> for DemoApp { | |
type Response = crate::http::Response; | |
type Error = anyhow::Error; | |
#[allow(clippy::type_complexity)] | |
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; | |
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, mut req: crate::http::Request) -> Self::Future { | |
let counter = self.counter.clone(); | |
Box::pin(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) | |
}) | |
} | |
} | |
} | |
#[tokio::main] | |
async fn main() { | |
fakeserver::run(app::DemoApp::default()).await; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment