module
Kemal::Utils
Defined in:
kemal/helpers/utils.crConstant Summary
-
COMPRESS_MIN_SIZE =
860 -
Below this size the framing a content coding adds costs more than the coding saves. https://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits
-
CONTENT_CODINGS =
{"gzip", "deflate"} -
The content codings Kemal can apply to a response body, most preferred first. The order breaks ties between codings the client is equally happy with.
send_filehas a writer for each of them; adding one here without adding its writer there leaves the body unencoded. -
ZIP_TYPES =
{".htm", ".html", ".txt", ".css", ".js", ".svg", ".json", ".xml", ".otf", ".ttf", ".woff", ".woff2"}
Class Method Summary
-
.append_vary(headers : HTTP::Headers, field : String) : Nil
Adds field to the
Varyresponse header, keeping the fields already listed there. -
.compressible?(path : String, size : Int) : Bool
Whether
send_filecompresses a file of size bytes stored at path, given thegzipoption ofserve_static. -
.content_coding_for(request_headers : HTTP::Headers, path : String, size : Int) : String | Nil
The content coding
send_fileapplies to a file of size bytes at path for a request carrying request_headers, ornilwhen it sends the stored bytes as they are. -
.content_disposition(disposition : String, filename : String) : String
Builds a
Content-Dispositionfield value for filename (RFC 6266 §4.3). -
.etag_with_coding(etag : String, coding : String) : String
Marks etag as identifying the coding encoded form of a representation, so that the encoded and identity forms of one file cannot share a validator (RFC 9110 §8.8.1).
-
.matches_path_prefix?(prefix : String, path : String) : Bool
Exact prefix, or prefix followed by
/. -
.parse_accept_encoding(value : String) : Hash(String, Float64)
Parses an
Accept-Encodingfield value into its content codings and their qvalues (RFC 9110 §12.5.3). - .path_starts_with_slash?(path : String)
-
.select_content_coding(accept_encoding : String | Nil, available : Enumerable(String) = CONTENT_CODINGS) : String | Nil
Picks the content coding to apply to a response, given the request's accept_encoding field value and the available codings the caller can produce (RFC 9110 §12.5.3).
-
.set_content_coding(headers : HTTP::Headers, coding : String) : Nil
Records coding as the content coding of a response and gives the encoded form its own entity tag, so that a cache holding the identity representation under
W/"..."cannot hand it to a client being served the encoded one (RFC 9110 §8.8.1). -
.valid_method?(method : String) : Bool
Whether method is a well-formed HTTP method, that is a non-empty RFC 9110 token (§9.1).
- .zip_types(path : String)
Class Method Detail
Adds field to the Vary response header, keeping the fields already listed there.
A field that is present, and the * that already covers every field, are left alone.
Whether send_file compresses a file of size bytes stored at path, given the
gzip option of serve_static. This also decides whether the response needs a
Vary: Accept-Encoding, which is why Kemal::StaticFileHandler asks before it knows
whether it is answering with a body at all.
The content coding send_file applies to a file of size bytes at path for a
request carrying request_headers, or nil when it sends the stored bytes as they
are. Kemal::StaticFileHandler has to know the answer before it writes a body, to
name the right entity tag on a 304, so both ask the same question here rather than
each working it out for itself.
Builds a Content-Disposition field value for filename
(RFC 6266 §4.3).
The filename parameter is a quoted-string and has to stay in ASCII, so " and
\\ are escaped as quoted-pairs and every other character outside printable ASCII
becomes _. When that loses something, the original name follows as
filename*=UTF-8''... percent-encoded per
RFC 8187, which user agents that
understand it prefer. A name that is plain ASCII to begin with gets only the
first form, unchanged.
Kemal::Utils.content_disposition("attachment", "report.pdf") # => %(attachment; filename="report.pdf")
Kemal::Utils.content_disposition("attachment", "rapor ü.pdf") # => %(attachment; filename="rapor _.pdf"; filename*=UTF-8''rapor%20%C3%BC.pdf)
Marks etag as identifying the coding encoded form of a representation, so that the encoded and identity forms of one file cannot share a validator (RFC 9110 §8.8.1). The suffix is spelled the way nginx and Apache spell it.
Only the codings in CONTENT_CODINGS are marked. A tag Kemal cannot reproduce from
the file's own tag is one it could never match on revalidation, which would re-send
the whole body every time, so a coding the application applied itself is left alone.
Kemal::Utils.etag_with_coding(%(W/"1700000000"), "gzip") # => %(W/"1700000000-gzip")
Exact prefix, or prefix followed by /. "/", and "" match all paths.
Parses an Accept-Encoding field value into its content codings and their qvalues
(RFC 9110 §12.5.3). Coding
names are case-insensitive (§8.4.1) and come back lowercased, with x-gzip folded
into gzip (§8.4.1.3); * is kept as is.
An element whose qvalue is not a number in 0..1 is dropped rather than taken as
q=1, so a garbled field value cannot be read as a request for compression, and a
coding listed more than once keeps its lowest qvalue, so a later duplicate cannot
undo a refusal.
Kemal::Utils.parse_accept_encoding("gzip;q=0.5, deflate") # => {"gzip" => 0.5, "deflate" => 1.0}
Picks the content coding to apply to a response, given the request's accept_encoding field value and the available codings the caller can produce (RFC 9110 §12.5.3).
Returns "identity" when the body should be sent unencoded, and nil when the field
value rules out every coding, identity included. RFC 9110 has the origin server
send an unencoded response in that case as well, so nil may be treated as
"identity"; it is distinct only so that a caller can tell the two apart.
A missing field is answered with "identity": it carries no preference, and Kemal
does not compress a response the client did not ask to have compressed.
Kemal::Utils.select_content_coding("gzip;q=0, deflate") # => "deflate"
Kemal::Utils.select_content_coding("gzip;q=0") # => "identity"
Kemal::Utils.select_content_coding("identity;q=0") # => nil
Records coding as the content coding of a response and gives the encoded form its
own entity tag, so that a cache holding the identity representation under W/"..."
cannot hand it to a client being served the encoded one (RFC 9110 §8.8.1). The
-gzip style suffix is the one nginx and Apache use.
Only for a coding Kemal applied itself: an entity tag that came with a body the caller encoded already describes that body.
Whether method is a well-formed HTTP method, that is a non-empty RFC 9110 token (§9.1).
Crystal's request parser takes whatever bytes stand before the first space of
the request line as the method, without validating them, so a method can
arrive carrying any character - / included. Kemal keys its routing tree on
"/#{method}#{path}", so a method holding a / moves the boundary between
the two: GET/admin + /secret produces the same key as GET +
/admin/secret and reaches the same route, while every path-scoped guard -
use "/prefix", only/exclude, before_*/after_* - matches on
request.path and sees only /secret (#820).
Unknown but well-formed methods stay valid: a PROPFIND or a custom verb is
a token, and answering it is the router's business, not this check's.