Skip to main content

Server

Struct Server 

Source
pub struct Server { /* private fields */ }
Expand description

A static file server for serving files securely from a root directory.

Server canonicalizes the root directory once at creation time and uses the canonical form for all subsequent requests, avoiding repeated filesystem calls.

§Security

The server protects against:

  • Path traversal attacks (e.g., ../../etc/passwd)
  • Accessing files outside the root via symlinks
  • Disclosing filesystem structure (traversal and missing files both return 404)

§Cloning

Server is cheap to clone (a PathBuf and a usize). Multiple clones can be used concurrently in async tasks without synchronization overhead.

§Example

use mini_static::Server;
use std::path::Path;
use std::time::Duration;

let server = Server::new(Path::new("./public"))?;
let (port, _handle) = server.run(Duration::from_secs(30)).await?;
println!("Server running on port {}", port);

Implementations§

Source§

impl Server

Source

pub fn new(root: &Path) -> Result<Self, StaticError>

Create a new server with the given root directory.

Canonicalizes the root once at startup. All subsequent requests use the canonical root without re-canonicalizing it, making this suitable for long-lived servers.

§Arguments
  • root - The root directory to serve files from.
§Errors

Returns Err(StaticError::Io) if the root cannot be canonicalized (e.g., doesn’t exist, no read permissions).

Source

pub fn with_max_connections(self, max: usize) -> Self

Set the maximum number of connections served concurrently (default 1024).

Once this many connections are in flight, run()’s accept loop stops accepting new ones — without pausing the accept loop, a client that opens a connection and sends nothing (see the header-read timeout docs on run()) could otherwise be used, in enough parallel copies, to exhaust the process’s file descriptors or memory with no bound at all.

Source

pub fn resolve(&self, request_path: &str) -> Result<PathBuf, StaticError>

Resolve a request path under the server’s root.

This is a lower-level API for resolving paths without generating HTTP responses. For most use cases, prefer handle_request_with_method() or the run() methods.

§Arguments
  • request_path - The HTTP request path (e.g., /path/to/file.html).
§Returns
  • Ok(PathBuf) if the path resolves to a file within root.
  • Err(StaticError) if the path is invalid, missing, or attempts traversal.
Source

pub fn handle_request(&self, request_path: &str) -> Response<ResponseBody>

Handle an HTTP GET request for a resource path.

Convenience method equivalent to handle_request_with_method(&Method::GET, request_path).

§Arguments
  • request_path - The HTTP request path (e.g., /index.html).
Source

pub fn handle_request_with_method( &self, method: &Method, request_path: &str, ) -> Response<ResponseBody>

Handle an HTTP request with an explicit method.

Only GET and HEAD methods are allowed. Other methods return 405 Method Not Allowed with an Allow header listing the permitted methods.

§Arguments
  • method - The HTTP method (GET and HEAD are allowed; others return 405).
  • request_path - The HTTP request path (e.g., /index.html).
Source

pub async fn run( &self, header_timeout: Duration, ) -> Result<(u16, ServerHandle), StaticError>

Run the server on loopback (127.0.0.1) with a configurable header-read timeout.

Binds to an ephemeral port and spawns the server in a background Tokio task. Returns immediately with the assigned port number and a ServerHandle. Dropping the handle without calling shutdown() leaves the server running in the background for the life of the process — the same behavior run() always had. Call handle.shutdown().await to stop accepting new connections and wait for in-flight connections to finish.

§Header-Read Timeout

Connections that don’t send complete HTTP headers within header_timeout are closed. This prevents slowloris attacks and resource exhaustion from incomplete requests.

§Arguments
  • header_timeout - Maximum time to wait for complete HTTP headers on each connection.
§Returns
  • Ok((u16, ServerHandle)) with the ephemeral port number assigned by the OS and a handle for graceful shutdown.
  • Err(StaticError::Io) if binding to the socket fails.
§Example
use mini_static::Server;
use std::path::Path;
use std::time::Duration;

let server = Server::new(Path::new("./public"))?;
let (port, handle) = server.run(Duration::from_secs(30)).await?;
println!("Server running on http://127.0.0.1:{}", port);
// ... later, to stop it gracefully:
handle.shutdown().await;
Source

pub async fn run_ephemeral(&self) -> Result<(u16, ServerHandle), StaticError>

Run the server on loopback (127.0.0.1) with a default header-read timeout.

Convenience wrapper around run() that uses a default 30-second header-read timeout. Returns immediately with the ephemeral port number and a ServerHandle; the server continues in a background Tokio task until the handle’s shutdown() is awaited or the Tokio runtime shuts down.

This is the recommended method for tests and lightweight services that don’t require custom timeout configuration.

§Returns
  • Ok((u16, ServerHandle)) with the ephemeral port number assigned by the OS and a handle for graceful shutdown.
  • Err(StaticError::Io) if binding to the socket fails.
§Example
use mini_static::Server;
use std::path::Path;

let server = Server::new(Path::new("./public"))?;
let (port, handle) = server.run_ephemeral().await?;
println!("Server ready on http://127.0.0.1:{}", port);
handle.shutdown().await;
Source

pub fn handle_request_with_headers( &self, method: &Method, request_path: &str, _range_header: Option<&str>, _if_range_header: Option<&str>, ) -> Response<ResponseBody>

Handle an HTTP request with method and optional Range/If-Range headers (synchronous API).

This is the synchronous version of request handling used internally by the async server loop. For most use cases, prefer using run() or run_ephemeral() which handle the full async lifecycle.

Only GET and HEAD methods are allowed; other methods return 405 Method Not Allowed. All errors (missing files, traversal attempts, I/O failures) are returned as 404 to avoid leaking filesystem structure information.

§Range Request Handling

mini-static does not yet serve 206 Partial Content — every request, ranged or not, gets the full body with 200. This is RFC 9110-correct behavior (as opposed to incorrectly answering 416), but partial-content serving is deferred to a later phase.

§Arguments
  • method - The HTTP method (GET and HEAD only).
  • request_path - The HTTP request path (e.g., /index.html).
  • _range_header - Optional Range header (currently unused).
  • _if_range_header - Optional If-Range header (currently unused).

Trait Implementations§

Source§

impl Clone for Server

Source§

fn clone(&self) -> Server

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more