pub struct App { /* private fields */ }Expand description
An assembled, immutable, cheaply-cloneable application.
Produced by AppBuilder::build. Internally reference-counted, so cloning
is cheap and clones share the same router, middleware, state, and config.
Serve it with start (or
start_with_shutdown), drive a single request
in-process with process, or hand it to a
TestClient for testing.
use churust_core::{Churust, Call, TestClient};
let app = Churust::server()
.routing(|r| { r.get("/", |_c: Call| async { "ok" }); })
.build();
let clone = app.clone(); // cheap; shares the same inner app
let res = TestClient::new(clone).get("/").send().await;
assert_eq!(res.status().as_u16(), 200);Implementations§
Source§impl App
impl App
Sourcepub fn config(&self) -> &ServerConfig
pub fn config(&self) -> &ServerConfig
The resolved ServerConfig this app was built with.
Sourcepub async fn process(
&self,
method: Method,
uri: Uri,
headers: HeaderMap,
body: Bytes,
) -> Response
pub async fn process( &self, method: Method, uri: Uri, headers: HeaderMap, body: Bytes, ) -> Response
The single request entry point: run one request through the full
pipeline (middleware then routed handler) and return the Response.
Both the hyper engine and the TestClient call
this. It is panic-isolated — a panicking handler is caught and turned
into 500 Internal Server Error rather than crashing the task.
use churust_core::{Churust, Call};
use http::{HeaderMap, Method};
use bytes::Bytes;
let app = Churust::server()
.routing(|r| { r.get("/", |_c: Call| async { "ok" }); })
.build();
let res = app.process(Method::GET, "/".parse().unwrap(), HeaderMap::new(), Bytes::new()).await;
assert_eq!(res.status.as_u16(), 200);THE single request entry point used by the engine and TestClient.
Panic-isolated: a panicking handler yields 500.
Sourcepub async fn process_with_extensions(
&self,
method: Method,
uri: Uri,
headers: HeaderMap,
body: Bytes,
extensions: Extensions,
) -> Response
pub async fn process_with_extensions( &self, method: Method, uri: Uri, headers: HeaderMap, body: Bytes, extensions: Extensions, ) -> Response
Like App::process, but seeds the Call with pre-built extensions
(e.g. a captured WebSocket upgrade handle). Advanced/engine use.
Sourcepub async fn start(self) -> Result<()>
pub async fn start(self) -> Result<()>
Bind the configured address — plus anything AppBuilder::bind added —
and serve until Ctrl-C (SIGINT), then drain in-flight connections
gracefully. Does not return until shutdown.
§Errors
Returns an std::io::Error if any configured address is invalid or a
socket cannot be bound (e.g. the port is in use).
use churust_core::{Churust, Call};
let app = Churust::server()
.routing(|r| { r.get("/", |_c: Call| async { "hi" }); })
.build();
app.start().awaitSourcepub async fn start_with_shutdown<F>(self, shutdown: F) -> Result<()>
pub async fn start_with_shutdown<F>(self, shutdown: F) -> Result<()>
Bind and serve until the provided shutdown future resolves, then drain
gracefully. Use this to wire a custom shutdown signal (e.g. in tests, or
to combine SIGTERM with SIGINT).
Serves every address AppBuilder::bind registered as well as the
configured host:port; a bind failure on any one of them aborts the
whole start rather than leaving a half-up server.
§Errors
Returns an std::io::Error if any configured address is invalid or a
socket cannot be bound.
use churust_core::{Churust, Call};
let app = Churust::server()
.routing(|r| { r.get("/", |_c: Call| async { "hi" }); })
.build();
let (tx, rx) = tokio::sync::oneshot::channel::<()>();
// ... later: tx.send(()) to trigger shutdown ...
app.start_with_shutdown(async { let _ = rx.await; }).awaitSourcepub async fn start_on<F>(self, listener: TcpListener, shutdown: F) -> Result<()>
pub async fn start_on<F>(self, listener: TcpListener, shutdown: F) -> Result<()>
Serve on an already-bound listener until shutdown resolves.
The counterpart to start_with_shutdown for
callers that must know the port before the server runs — a supervisor
passing a socket in, or a test that needs the address.
It also closes a race the address-based entry points cannot: finding a free port by binding, dropping, and letting the server bind again leaves a window for something else to claim it. Handing over the listener removes the window.
use churust_core::{Churust, Call};
let app = Churust::server()
.routing(|r| { r.get("/", |_c: Call| async { "hi" }); })
.build();
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await?;
println!("listening on {}", listener.local_addr()?);
app.start_on(listener, async { let _ = tokio::signal::ctrl_c().await; }).await