ServeFiles¶
Serve files from a directory on disk.
Small files (below the chunk threshold) are served as a single response
with Content-Length. Large files are streamed using chunked transfer
encoding. When the client does not support chunked encoding (HTTP/1.0),
small files are still served normally, but large files are rejected
with 505 HTTP Version Not Supported to prevent memory exhaustion.
HEAD requests are optimized: the handler responds with Content-Type and
Content-Length headers without reading the file, regardless of file size.
Responses include caching headers:
- ETag: Weak ETag computed from file metadata (
W/"<inode>-<size>-<mtime>"). On Windows,FileInfo.inodeis always 0, reducing collision resistance to size+mtime only. - Last-Modified: RFC 7231 IMF-fixdate from the file's modification time.
- Cache-Control: Configurable via the
cache_controlconstructor parameter. Defaults to"public, max-age=3600". PassNoneto omit.
Conditional requests are supported per RFC 7232:
If-None-Matchis checked first (ETag comparison using weak matching).If-Modified-Sinceis checked only whenIf-None-Matchis absent.- When either matches, the handler responds with 304 Not Modified (cache headers included, no body).
Custom content types can be added via the content_types parameter:
let types = hobby.ContentTypes
.add("webp", "image/webp")
.add("avif", "image/avif")
hobby.ServeFiles(root where content_types = types)
Routes must use *filepath as the wildcard parameter name:
use "files"
use hobby = "hobby"
use stallion = "stallion"
use lori = "lori"
actor Main
new create(env: Env) =>
let auth = lori.TCPListenAuth(env.root)
let root = FilePath(FileAuth(env.root), "./public")
hobby.Application
.>get("/static/*filepath", hobby.ServeFiles(root))
.serve(auth, stallion.ServerConfig("0.0.0.0", "8080"), env.out)
Path traversal is prevented by Pony's FilePath.from(), which rejects
any resolved path that is not a child of the base directory.
When a request resolves to a directory, ServeFiles looks for an
index.html file inside it. If found, the index file is served with the
correct text/html content type and caching headers. If no index.html
exists, the directory request returns 404.
Implements¶
- Handler val
Constructors¶
create¶
Create a handler that serves files under root.
root must have FileLookup, FileStat, and FileRead capabilities.
chunk_threshold is the file size in kilobytes at or above which
chunked streaming is used instead of a single response. Default: 1024
(1 MB).
cache_control sets the Cache-Control header value. Defaults to
"public, max-age=3600" (1 hour). Pass None to omit the header.
content_types controls the file extension to MIME type mapping.
Defaults to a ContentTypes with 17 common extensions. Chain
.add() calls to add custom mappings.
If the route uses a wildcard name other than *filepath, param lookup
will fail and the handler will return 500. Always use *filepath.
new val create(
root: FilePath val,
chunk_threshold: USize val = 1024,
cache_control: (String val | None val) = "public, max-age=3600",
content_types: ContentTypes val = reference)
: ServeFiles val^
Parameters¶
- root: FilePath val
- chunk_threshold: USize val = 1024
- cache_control: (String val | None val) = "public, max-age=3600"
- content_types: ContentTypes val = reference
Returns¶
- ServeFiles val^
Public Functions¶
apply¶
Parameters¶
- ctx: Context ref
Returns¶
- None val ?