# `http_static_files`

The `http_static_files` library provides a router-agnostic helper for serving existing files from a configured document root using normalized `http_core` request and response terms.

This library resolves a relative request path against a document root, canonicalizes the joined target path using `os::absolute_file_name/2`, rejects canonicalized escapes with the same outward `404 Not Found` response used for missing files, falls back to configurable index files for directory targets, guesses the response media type using `mime_types`, emits `ETag`, `Last-Modified`, and `Accept-Ranges` metadata, and returns file-backed response bodies that `http_server_core` can stream efficiently.

## API documentation

Open the ../../apis/library_index.html#http_static_files link in a web browser.

## Loading

To load the library, load the `loader.lgt` file:

    | ?- logtalk_load(http_static_files(loader)).

## Testing

To test this library, load the `tester.lgt` file:

    | ?- logtalk_load(http_static_files(tester)).

## Current scope

The current library provides one public object with two public predicates:

- `serve/4`

- `serve/5`

Supported options are:

- `index_files(IndexFiles)`

- `mime_types_strict(Boolean)`

- `mime_type_overrides(Overrides)`

- `fallback_file(Fallback)`

- `directory_listing(BooleanOrOptions)`

- `cors(Policy)`

- `cache_control(Directives)`

- `expires(Expires)`

- `content_disposition(Disposition)`

Default options are:

- `index_files(['index.html', 'index.htm'])`

- `mime_types_strict(false)`

- `mime_type_overrides([])`

- `fallback_file(none)`

- `directory_listing(false)`

- `cors([])`

- `cache_control([])`

- `expires(none)`

- `content_disposition(none)`

Supported features:

- `GET`, `HEAD`, and `OPTIONS`

- `200 OK` file responses

- `204 No Content` `OPTIONS` responses with `Allow: GET, HEAD, OPTIONS`

- canonical docroot-prefix checks after path normalization

- weak `ETag` and `Last-Modified` validators derived from file size and modification time

- `412 Precondition Failed` responses for failing `If-Match` and `If-Unmodified-Since` request preconditions

- `304 Not Modified` responses for matching `If-None-Match` and `If-Modified-Since` requests, with `If-None-Match` taking precedence

- single `bytes=` ranges with `206 Partial Content`

- `416 Range Not Satisfiable` for malformed, unsupported multi-range, and unsatisfiable range requests

- `If-Range` fallback to full `200 OK` responses when the validator does not match or is only weakly equal

- `406 Not Acceptable` when `Accept-Encoding` rejects the identity representation and no acceptable precompressed variant is available

- `404 Not Found` for missing or unsafe paths

- custom fallback file responses for missing paths that resolve safely inside the document root

- `405 Method Not Allowed` for other methods with `Allow: GET, HEAD, OPTIONS`

- `301 Moved Permanently` trailing-slash redirects for directory targets

- index-file lookup for directory targets

- optional directory-listing fallback for directory targets without an index file, using the `http_directory_listing` library

- precompressed `.zst`, `.br`, and `.gz` asset negotiation driven by `Accept-Encoding`

- `Vary: Accept-Encoding` responses when negotiated precompressed variants exist

- MIME type and content-encoding guessing

- per-extension and per-path MIME type overrides on top of `mime_types`

- optional CORS response decoration using the `http_cors` library

- configurable `Cache-Control` and `Expires` response headers, also shared by `OPTIONS` responses when configured

- configurable `Content-Disposition` response headers for inline or attachment file delivery

For directory listing, see the `http_directory_listing` library.

Cache-policy configuration uses these option values:

- `cache_control(Directives)` where `Directives` is a list containing any of `public`, `private`, `no_cache`, `no_store`, `no_transform`, `must_revalidate`, `proxy_revalidate`, `immutable`, `max_age(Seconds)`, `s_maxage(Seconds)`, `stale_while_revalidate(Seconds)`, `stale_if_error(Seconds)`, or `extension(Directive)`

- `expires(none)` to omit the header, `expires(Seconds)` for a relative expiry from the current system time, or `expires(date_time(Year,Month,Day,Hour,Minute,Second))` for an absolute expiry time

- `mime_type_overrides(Overrides)` where `Overrides` is a list containing `extension(Extension, MediaType)` or `path(Path, MediaType)` terms; path overrides match the served document-root relative file path and take precedence over extension overrides

- `fallback_file(none)` to omit custom fallback handling, `fallback_file(not_found(Path))` to serve a document-root relative file with `404 Not Found` status for missing targets, or `fallback_file(spa(Path))` to serve a document-root relative file with `200 OK` status for SPA client-side routes; fallback files are only considered after the requested path has resolved safely inside the document root

- `directory_listing(false)` to omit directory-listing fallback, `directory_listing(true)` to serve listings using the `http_directory_listing` defaults, or `directory_listing(Options)` to pass options to `http_directory_listing::serve/5`; listing fallback is only used for existing directory targets after trailing-slash redirect checks and index-file lookup, and before generic `fallback_file/1` handling

- `cors([])` to omit CORS response decoration, or `cors(Policy)` to decorate generated responses using `http_cors::add_response_headers/4`; when the policy omits `allowed_methods/1`, static files provide the default CORS method policy `allowed_methods([get, head])`, and `allowed_methods(any)` expands from the effective methods `[get, head, options]`

- `content_disposition(none)` to omit the header, `content_disposition(inline)` or `content_disposition(attachment)` for disposition-only values, and `content_disposition(inline(Filename))` or `content_disposition(attachment(Filename))` to include a quoted `filename` parameter

Current validator and date handling is intentionally conservative:

- `ETag` values are weak validators of the form `W/"Size-ModifiedTime"`

- HTTP-date formatting, date validation, and Unix-time conversion delegate to the `dates` library; request parsing remains limited to the IMF-fixdate form generated for `Last-Modified`

- `If-Match` entity-tag evaluation uses strong comparison semantics, so the generated weak `ETag` values only satisfy the wildcard `*` precondition

- `If-Range` entity-tag evaluation uses strong comparison semantics, so the generated weak `ETag` values never authorize partial responses; matching `Last-Modified` dates can still do so

- `If-Range` mismatches fall back to the full `200 OK` response before range validation, so malformed or unsupported range syntax is ignored in that case

- precompressed-asset negotiation currently recognizes `.zst`, `.br`, and `.gz` sibling files and selects between them using `Accept-Encoding` quality values, otherwise falling back to the identity representation when it remains acceptable

- path sandboxing is based on canonicalized absolute-path prefix checks; no separate cross-backend symlink-policy layer has been added yet