···152 HttpResponse(Body)
153154/// Create an empty response with the given status code.
155-///
156/// # Examples
157-///
158/// ```gleam
159/// response(200)
160/// // -> Response(200, [], Empty)
161/// ```
162-///
163pub fn response(status: Int) -> Response {
164 HttpResponse(status, [], Empty)
165}
166167/// Set the body of a response.
168-///
169/// # Examples
170-///
171/// ```gleam
172/// response(200)
173/// |> set_body(File("/tmp/myfile.txt"))
174/// // -> Response(200, [], File("/tmp/myfile.txt"))
175/// ```
176-///
177pub fn set_body(response: Response, body: Body) -> Response {
178 response
179 |> response.set_body(body)
···193/// `set_body` function with the `File` body variant.
194///
195/// # Examples
196-///
197/// ```gleam
198/// response(200)
199/// |> file_download(named: "myfile.txt", from: "/tmp/myfile.txt")
···229/// as this can result in cross-site scripting vulnerabilities.
230///
231/// # Examples
232-///
233/// ```gleam
234/// response(200)
235/// |> file_download_from_memory(named: "myfile.txt", containing: "Hello, Joe!")
···255}
256257/// Create a HTML response.
258-///
259/// The body is expected to be valid HTML, though this is not validated.
260/// The `content-type` header will be set to `text/html`.
261-///
262/// # Examples
263-///
264/// ```gleam
265/// let body = string_builder.from_string("<h1>Hello, Joe!</h1>")
266/// html_response(body, 200)
267/// // -> Response(200, [#("content-type", "text/html")], Text(body))
268/// ```
269-///
270pub fn html_response(html: StringBuilder, status: Int) -> Response {
271 HttpResponse(status, [#("content-type", "text/html")], Text(html))
272}
273274/// Create a JSON response.
275-///
276/// The body is expected to be valid JSON, though this is not validated.
277/// The `content-type` header will be set to `application/json`.
278-///
279/// # Examples
280-///
281/// ```gleam
282/// let body = string_builder.from_string("{\"name\": \"Joe\"}")
283/// json_response(body, 200)
284/// // -> Response(200, [#("content-type", "application/json")], Text(body))
285/// ```
286-///
287pub fn json_response(json: StringBuilder, status: Int) -> Response {
288 HttpResponse(status, [#("content-type", "application/json")], Text(json))
289}
290291/// Set the body of a response to a given HTML document, and set the
292/// `content-type` header to `text/html`.
293-///
294/// The body is expected to be valid HTML, though this is not validated.
295-///
296/// # Examples
297-///
298/// ```gleam
299/// let body = string_builder.from_string("<h1>Hello, Joe!</h1>")
300/// response(201)
301/// |> html_body(body)
302/// // -> Response(201, [#("content-type", "text/html")], Text(body))
303/// ```
304-///
305pub fn html_body(response: Response, html: StringBuilder) -> Response {
306 response
307 |> response.set_body(Text(html))
···310311/// Set the body of a response to a given JSON document, and set the
312/// `content-type` header to `application/json`.
313-///
314/// The body is expected to be valid JSON, though this is not validated.
315-///
316/// # Examples
317-///
318/// ```gleam
319/// let body = string_builder.from_string("{\"name\": \"Joe\"}")
320/// response(201)
321/// |> json_body(body)
322/// // -> Response(201, [#("content-type", "application/json")], Text(body))
323/// ```
324-///
325pub fn json_body(response: Response, json: StringBuilder) -> Response {
326 response
327 |> response.set_body(Text(json))
···334/// appropriate value for the format of the content.
335///
336/// # Examples
337-///
338/// ```gleam
339/// let body = string_builder.from_string("Hello, Joe!")
340/// response(201)
341/// |> string_builder_body(body)
342/// // -> Response(201, [], Text(body))
343/// ```
344-///
345pub fn string_builder_body(
346 response: Response,
347 content: StringBuilder,
···356/// appropriate value for the format of the content.
357///
358/// # Examples
359-///
360/// ```gleam
361-/// let body =
362/// response(201)
363/// |> string_body("Hello, Joe!")
364/// // -> Response(
···367/// // Text(string_builder.from_string("Hello, Joe"))
368/// // )
369/// ```
370-///
371pub fn string_body(response: Response, content: String) -> Response {
372 response
373 |> response.set_body(Text(string_builder.from_string(content)))
···384/// escape_html("<h1>Hello, Joe!</h1>")
385/// // -> "<h1>Hello, Joe!</h1>"
386/// ```
387-///
388pub fn escape_html(content: String) -> String {
389- do_escape_html("", content)
0000000000000000000000000000000000000000000000000000000000390}
391392-fn do_escape_html(escaped: String, content: String) -> String {
393- case string.pop_grapheme(content) {
394- Ok(#("<", xs)) -> do_escape_html(escaped <> "<", xs)
395- Ok(#(">", xs)) -> do_escape_html(escaped <> ">", xs)
396- Ok(#("&", xs)) -> do_escape_html(escaped <> "&", xs)
397- Ok(#(x, xs)) -> do_escape_html(escaped <> x, xs)
398- Error(_) -> escaped <> content
000000000000000000000000000000000000000000000000000000000399 }
400}
401···593//
594595/// The connection to the client for a HTTP request.
596-///
597/// The body of the request can be read from this connection using functions
598/// such as `require_multipart_body`.
599-///
600pub opaque type Connection {
601 Connection(
602 reader: Reader,
···676}
677678/// Get the maximum permitted size of a request body of the request in bytes.
679-///
680pub fn get_max_body_size(request: Request) -> Int {
681 request.body.max_body_size
682}
683684/// Set the secret key base used to sign cookies and other sensitive data.
685-///
686/// This key must be at least 64 bytes long and should be kept secret. Anyone
687/// with this secret will be able to manipulate signed cookies and other sensitive
688/// data.
689///
690/// # Panics
691-///
692/// This function will panic if the key is less than 64 bytes long.
693///
694pub fn set_secret_key_base(request: Request, key: String) -> Request {
···701}
702703/// Get the secret key base used to sign cookies and other sensitive data.
704-///
705pub fn get_secret_key_base(request: Request) -> String {
706 request.body.secret_key_base
707}
···723724/// Get the maximum permitted total size of a files uploaded by a request in
725/// bytes.
726-///
727pub fn get_max_files_size(request: Request) -> Int {
728 request.body.max_files_size
729}
···743744/// Get the size limit for each chunk of the request body when read from the
745/// client.
746-///
747pub fn get_read_chunk_size(request: Request) -> Int {
748 request.body.read_chunk_size
749}
750751/// A convenient alias for a HTTP request with a Wisp connection as the body.
752-///
753pub type Request =
754 HttpRequest(Connection)
755···758/// if the method is not correct.
759///
760/// # Examples
761-///
762/// ```gleam
763/// fn handle_request(request: Request) -> Response {
764/// use <- wisp.require_method(request, http.Patch)
···779780// TODO: re-export once Gleam has a syntax for that
781/// Return the non-empty segments of a request path.
782-///
783/// # Examples
784///
785/// ```gleam
···793794// TODO: re-export once Gleam has a syntax for that
795/// Set a given header to a given value, replacing any existing value.
796-///
797/// # Examples
798///
799/// ```gleam
···863/// return an incorrect value, depending on the underlying web server. It is the
864/// responsibility of the caller to cache the body if it is needed multiple
865/// times.
866-///
867/// If the body is larger than the `max_body_size` limit then an empty response
868/// with status code 413: Entity too large will be returned to the client.
869-///
870/// If the body is found not to be valid UTF-8 then an empty response with
871/// status code 400: Bad request will be returned to the client.
872-///
873/// # Examples
874///
875/// ```gleam
···899/// return an incorrect value, depending on the underlying web server. It is the
900/// responsibility of the caller to cache the body if it is needed multiple
901/// times.
902-///
903/// If the body is larger than the `max_body_size` limit then an empty response
904/// with status code 413: Entity too large will be returned to the client.
905-///
906/// # Examples
907///
908/// ```gleam
···925// TODO: don't always return entity to large. Other errors are possible, such as
926// network errors.
927/// Read the entire body of the request as a bit string.
928-///
929/// You may instead wish to use the `require_bit_array_body` or the
930/// `require_string_body` middleware functions instead.
931-///
932/// This function does not cache the body in any way, so if you call this
933/// function (or any other body reading function) more than once it may hang or
934/// return an incorrect value, depending on the underlying web server. It is the
935/// responsibility of the caller to cache the body if it is needed multiple
936/// times.
937-///
938/// If the body is larger than the `max_body_size` limit then an empty response
939/// with status code 413: Entity too large will be returned to the client.
940-///
941pub fn read_body_to_bitstring(request: Request) -> Result(BitArray, Nil) {
942 let connection = request.body
943 read_body_loop(
···971/// A middleware which extracts form data from the body of a request that is
972/// encoded as either `application/x-www-form-urlencoded` or
973/// `multipart/form-data`.
974-///
975/// Extracted fields are sorted into alphabetical order by key, so if you wish
976/// to use pattern matching the order can be relied upon.
977-///
978/// ```gleam
979/// fn handle_request(request: Request) -> Response {
980/// use form <- wisp.require_form(request)
···1003///
1004/// If the body cannot be parsed successfully then an empty response with status
1005/// code 400: Bad request will be returned to the client.
1006-///
1007pub fn require_form(
1008 request: Request,
1009 next: fn(FormData) -> Response,
···1030/// Unsupported media type if the header is not the expected value
1031///
1032/// # Examples
1033-///
1034/// ```gleam
1035/// fn handle_request(request: Request) -> Response {
1036/// use <- wisp.require_content_type(request, "application/json")
···1050}
10511052/// A middleware which extracts JSON from the body of a request.
1053-///
1054/// ```gleam
1055/// fn handle_request(request: Request) -> Response {
1056/// use json <- wisp.require_json(request)
···1071///
1072/// If the body cannot be parsed successfully then an empty response with status
1073/// code 400: Bad request will be returned to the client.
1074-///
1075pub fn require_json(request: Request, next: fn(Dynamic) -> Response) -> Response {
1076 use <- require_content_type(request, "application/json")
1077 use body <- require_string_body(request)
···1305}
13061307/// Data parsed from form sent in a request's body.
1308-///
1309pub type FormData {
1310 FormData(
1311 /// String values of the form's fields.
···1429///
1430/// The `under` parameter is the request path prefix that must match for the
1431/// file to be served.
1432-///
1433/// | `under` | `from` | `request.path` | `file` |
1434/// |-----------|---------|--------------------|-------------------------|
1435/// | `/static` | `/data` | `/static/file.txt` | `/data/file.txt` |
···1576/// > erlang.priv_directory("my_app")
1577/// // -> Ok("/some/location/my_app/priv")
1578/// ```
1579-///
1580pub const priv_directory = erlang.priv_directory
15811582//
···15851586/// Configure the Erlang logger, setting the minimum log level to `info`, to be
1587/// called when your application starts.
1588-///
1589/// You may wish to use an alternative for this such as one provided by a more
1590/// sophisticated logging library.
1591-///
1592/// In future this function may be extended to change the output format.
1593-///
1594pub fn configure_logger() -> Nil {
1595 logging.configure()
1596}
15971598/// Log a message to the Erlang logger with the level of `emergency`.
1599-///
1600/// See the [Erlang logger documentation][1] for more information.
1601-///
1602/// [1]: https://www.erlang.org/doc/man/logger
1603-///
1604pub fn log_emergency(message: String) -> Nil {
1605 logging.log(logging.Emergency, message)
1606}
16071608/// Log a message to the Erlang logger with the level of `alert`.
1609-///
1610/// See the [Erlang logger documentation][1] for more information.
1611-///
1612/// [1]: https://www.erlang.org/doc/man/logger
1613-///
1614pub fn log_alert(message: String) -> Nil {
1615 logging.log(logging.Alert, message)
1616}
16171618/// Log a message to the Erlang logger with the level of `critical`.
1619-///
1620/// See the [Erlang logger documentation][1] for more information.
1621-///
1622/// [1]: https://www.erlang.org/doc/man/logger
1623-///
1624pub fn log_critical(message: String) -> Nil {
1625 logging.log(logging.Critical, message)
1626}
16271628/// Log a message to the Erlang logger with the level of `error`.
1629-///
1630/// See the [Erlang logger documentation][1] for more information.
1631-///
1632/// [1]: https://www.erlang.org/doc/man/logger
1633-///
1634pub fn log_error(message: String) -> Nil {
1635 logging.log(logging.Error, message)
1636}
16371638/// Log a message to the Erlang logger with the level of `warning`.
1639-///
1640/// See the [Erlang logger documentation][1] for more information.
1641-///
1642/// [1]: https://www.erlang.org/doc/man/logger
1643-///
1644pub fn log_warning(message: String) -> Nil {
1645 logging.log(logging.Warning, message)
1646}
16471648/// Log a message to the Erlang logger with the level of `notice`.
1649-///
1650/// See the [Erlang logger documentation][1] for more information.
1651-///
1652/// [1]: https://www.erlang.org/doc/man/logger
1653-///
1654pub fn log_notice(message: String) -> Nil {
1655 logging.log(logging.Notice, message)
1656}
16571658/// Log a message to the Erlang logger with the level of `info`.
1659-///
1660/// See the [Erlang logger documentation][1] for more information.
1661-///
1662/// [1]: https://www.erlang.org/doc/man/logger
1663-///
1664pub fn log_info(message: String) -> Nil {
1665 logging.log(logging.Info, message)
1666}
16671668/// Log a message to the Erlang logger with the level of `debug`.
1669-///
1670/// See the [Erlang logger documentation][1] for more information.
1671-///
1672/// [1]: https://www.erlang.org/doc/man/logger
1673-///
1674pub fn log_debug(message: String) -> Nil {
1675 logging.log(logging.Debug, message)
1676}
···16891690/// Sign a message which can later be verified using the `verify_signed_message`
1691/// function to detect if the message has been tampered with.
1692-///
1693/// Signed messages are not encrypted and can be read by anyone. They are not
1694/// suitable for storing sensitive information.
1695-///
1696/// This function uses the secret key base from the request. If the secret
1697/// changes then the signature will no longer be verifiable.
1698-///
1699pub fn sign_message(
1700 request: Request,
1701 message: BitArray,
···1705}
17061707/// Verify a signed message which was signed using the `sign_message` function.
1708-///
1709/// Returns the content of the message if the signature is valid, otherwise
1710/// returns an error.
1711-///
1712/// This function uses the secret key base from the request. If the secret
1713/// changes then the signature will no longer be verifiable.
1714-///
1715pub fn verify_signed_message(
1716 request: Request,
1717 message: String,
···1749/// wisp.ok()
1750/// |> wisp.set_cookie(request, "id", "123", wisp.PlainText, 60 * 60)
1751/// ```
1752-///
1753/// Setting a signed cookie that the client can read but not modify:
1754-///
1755/// ```gleam
1756/// wisp.ok()
1757/// |> wisp.set_cookie(request, "id", value, wisp.Signed, 60 * 60)
···18211822// TODO: chunk the body
1823/// Create a connection which will return the given body when read.
1824-///
1825/// This function is intended for use in tests, though you probably want the
1826/// `wisp/testing` module instead.
1827-///
1828pub fn create_canned_connection(
1829 body: BitArray,
1830 secret_key_base: String,
···152 HttpResponse(Body)
153154/// Create an empty response with the given status code.
155+///
156/// # Examples
157+///
158/// ```gleam
159/// response(200)
160/// // -> Response(200, [], Empty)
161/// ```
162+///
163pub fn response(status: Int) -> Response {
164 HttpResponse(status, [], Empty)
165}
166167/// Set the body of a response.
168+///
169/// # Examples
170+///
171/// ```gleam
172/// response(200)
173/// |> set_body(File("/tmp/myfile.txt"))
174/// // -> Response(200, [], File("/tmp/myfile.txt"))
175/// ```
176+///
177pub fn set_body(response: Response, body: Body) -> Response {
178 response
179 |> response.set_body(body)
···193/// `set_body` function with the `File` body variant.
194///
195/// # Examples
196+///
197/// ```gleam
198/// response(200)
199/// |> file_download(named: "myfile.txt", from: "/tmp/myfile.txt")
···229/// as this can result in cross-site scripting vulnerabilities.
230///
231/// # Examples
232+///
233/// ```gleam
234/// response(200)
235/// |> file_download_from_memory(named: "myfile.txt", containing: "Hello, Joe!")
···255}
256257/// Create a HTML response.
258+///
259/// The body is expected to be valid HTML, though this is not validated.
260/// The `content-type` header will be set to `text/html`.
261+///
262/// # Examples
263+///
264/// ```gleam
265/// let body = string_builder.from_string("<h1>Hello, Joe!</h1>")
266/// html_response(body, 200)
267/// // -> Response(200, [#("content-type", "text/html")], Text(body))
268/// ```
269+///
270pub fn html_response(html: StringBuilder, status: Int) -> Response {
271 HttpResponse(status, [#("content-type", "text/html")], Text(html))
272}
273274/// Create a JSON response.
275+///
276/// The body is expected to be valid JSON, though this is not validated.
277/// The `content-type` header will be set to `application/json`.
278+///
279/// # Examples
280+///
281/// ```gleam
282/// let body = string_builder.from_string("{\"name\": \"Joe\"}")
283/// json_response(body, 200)
284/// // -> Response(200, [#("content-type", "application/json")], Text(body))
285/// ```
286+///
287pub fn json_response(json: StringBuilder, status: Int) -> Response {
288 HttpResponse(status, [#("content-type", "application/json")], Text(json))
289}
290291/// Set the body of a response to a given HTML document, and set the
292/// `content-type` header to `text/html`.
293+///
294/// The body is expected to be valid HTML, though this is not validated.
295+///
296/// # Examples
297+///
298/// ```gleam
299/// let body = string_builder.from_string("<h1>Hello, Joe!</h1>")
300/// response(201)
301/// |> html_body(body)
302/// // -> Response(201, [#("content-type", "text/html")], Text(body))
303/// ```
304+///
305pub fn html_body(response: Response, html: StringBuilder) -> Response {
306 response
307 |> response.set_body(Text(html))
···310311/// Set the body of a response to a given JSON document, and set the
312/// `content-type` header to `application/json`.
313+///
314/// The body is expected to be valid JSON, though this is not validated.
315+///
316/// # Examples
317+///
318/// ```gleam
319/// let body = string_builder.from_string("{\"name\": \"Joe\"}")
320/// response(201)
321/// |> json_body(body)
322/// // -> Response(201, [#("content-type", "application/json")], Text(body))
323/// ```
324+///
325pub fn json_body(response: Response, json: StringBuilder) -> Response {
326 response
327 |> response.set_body(Text(json))
···334/// appropriate value for the format of the content.
335///
336/// # Examples
337+///
338/// ```gleam
339/// let body = string_builder.from_string("Hello, Joe!")
340/// response(201)
341/// |> string_builder_body(body)
342/// // -> Response(201, [], Text(body))
343/// ```
344+///
345pub fn string_builder_body(
346 response: Response,
347 content: StringBuilder,
···356/// appropriate value for the format of the content.
357///
358/// # Examples
359+///
360/// ```gleam
361+/// let body =
362/// response(201)
363/// |> string_body("Hello, Joe!")
364/// // -> Response(
···367/// // Text(string_builder.from_string("Hello, Joe"))
368/// // )
369/// ```
370+///
371pub fn string_body(response: Response, content: String) -> Response {
372 response
373 |> response.set_body(Text(string_builder.from_string(content)))
···384/// escape_html("<h1>Hello, Joe!</h1>")
385/// // -> "<h1>Hello, Joe!</h1>"
386/// ```
387+///
388pub fn escape_html(content: String) -> String {
389+ let bits = <<content:utf8>>
390+ let acc = do_escape_html(bits, 0, bits, [])
391+392+ list.reverse(acc)
393+ |> bit_array.concat
394+ // We know the bit array produced by `do_escape_html` is still a valid utf8
395+ // string so we coerce it without passing through the validation steps of
396+ // `bit_array.to_string`.
397+ |> coerce_bit_array_to_string
398+}
399+400+@external(erlang, "wisp_ffi", "coerce")
401+fn coerce_bit_array_to_string(bit_array: BitArray) -> String
402+403+// A possible way to escape chars would be to split the string into graphemes,
404+// traverse those one by one and accumulate them back into a string escaping
405+// ">", "<", etc. as we see them.
406+//
407+// However, we can be a lot more performant by working directly on the
408+// `BitArray` representing a Gleam UTF-8 String.
409+// This means that, instead of popping a grapheme at a time, we can work
410+// directly on BitArray slices: this has the big advantage of making sure we
411+// share as much as possible with the original string without having to build
412+// a new one from scratch.
413+//
414+@target(erlang)
415+fn do_escape_html(
416+ bin: BitArray,
417+ skip: Int,
418+ original: BitArray,
419+ acc: List(BitArray),
420+) -> List(BitArray) {
421+ case bin {
422+ // If we find a char to escape we just advance the `skip` counter so that
423+ // it will be ignored in the following slice, then we append the escaped
424+ // version to the accumulator.
425+ <<"<":utf8, rest:bits>> -> {
426+ let acc = [<<"<":utf8>>, ..acc]
427+ do_escape_html(rest, skip + 1, original, acc)
428+ }
429+430+ <<">":utf8, rest:bits>> -> {
431+ let acc = [<<">":utf8>>, ..acc]
432+ do_escape_html(rest, skip + 1, original, acc)
433+ }
434+435+ <<"&":utf8, rest:bits>> -> {
436+ let acc = [<<"&":utf8>>, ..acc]
437+ do_escape_html(rest, skip + 1, original, acc)
438+ }
439+440+ // For any other bit that doesn't need to be escaped we go into an inner
441+ // loop, consuming as much "non-escapable" chars as possible.
442+ <<_char, rest:bits>> -> do_escape_html_regular(rest, skip, original, acc, 1)
443+444+ <<>> -> acc
445+446+ _ -> panic as "non byte aligned string, all strings should be byte aligned"
447+ }
448}
449450+@target(erlang)
451+fn do_escape_html_regular(
452+ bin: BitArray,
453+ skip: Int,
454+ original: BitArray,
455+ acc: List(BitArray),
456+ len: Int,
457+) -> List(BitArray) {
458+ // Remember, if we're here it means we've found a char that doesn't need to be
459+ // escaped, so what we want to do is advance the `len` counter until we reach
460+ // a char that _does_ need to be escaped and take the slice going from
461+ // `skip` with size `len`.
462+ //
463+ // Imagine we're escaping this string: "abc<def&ghi" and we've reached 'd':
464+ // ```
465+ // abc<def&ghi
466+ // ^ `skip` points here
467+ // ```
468+ // We're going to be increasing `len` until we reach the '&':
469+ // ```
470+ // abc<def&ghi
471+ // ^^^ len will be 3 when we reach the '&' that needs escaping
472+ // ```
473+ // So we take the slice corresponding to "def".
474+ //
475+ case bin {
476+ // If we reach a char that has to be escaped we append the slice starting
477+ // from `skip` with size `len` and the escaped char.
478+ // This is what allows us to share as much of the original string as
479+ // possible: we only allocate a new BitArray for the escaped chars,
480+ // everything else is just a slice of the original String.
481+ <<"<":utf8, rest:bits>> -> {
482+ let assert Ok(slice) = bit_array.slice(original, skip, len)
483+ let acc = [<<"<":utf8>>, slice, ..acc]
484+ do_escape_html(rest, skip + len + 1, original, acc)
485+ }
486+487+ <<">":utf8, rest:bits>> -> {
488+ let assert Ok(slice) = bit_array.slice(original, skip, len)
489+ let acc = [<<">":utf8>>, slice, ..acc]
490+ do_escape_html(rest, skip + len + 1, original, acc)
491+ }
492+493+ <<"&":utf8, rest:bits>> -> {
494+ let assert Ok(slice) = bit_array.slice(original, skip, len)
495+ let acc = [<<"&":utf8>>, slice, ..acc]
496+ do_escape_html(rest, skip + len + 1, original, acc)
497+ }
498+499+ // If a char doesn't need escaping we keep increasing the length of the
500+ // slice we're going to take.
501+ <<_char, rest:bits>> ->
502+ do_escape_html_regular(rest, skip, original, acc, len + 1)
503+504+ <<>> ->
505+ case skip {
506+ 0 -> [original]
507+ _ -> {
508+ let assert Ok(slice) = bit_array.slice(original, skip, len)
509+ [slice, ..acc]
510+ }
511+ }
512+513+ _ -> panic as "non byte aligned string, all strings should be byte aligned"
514 }
515}
516···708//
709710/// The connection to the client for a HTTP request.
711+///
712/// The body of the request can be read from this connection using functions
713/// such as `require_multipart_body`.
714+///
715pub opaque type Connection {
716 Connection(
717 reader: Reader,
···791}
792793/// Get the maximum permitted size of a request body of the request in bytes.
794+///
795pub fn get_max_body_size(request: Request) -> Int {
796 request.body.max_body_size
797}
798799/// Set the secret key base used to sign cookies and other sensitive data.
800+///
801/// This key must be at least 64 bytes long and should be kept secret. Anyone
802/// with this secret will be able to manipulate signed cookies and other sensitive
803/// data.
804///
805/// # Panics
806+///
807/// This function will panic if the key is less than 64 bytes long.
808///
809pub fn set_secret_key_base(request: Request, key: String) -> Request {
···816}
817818/// Get the secret key base used to sign cookies and other sensitive data.
819+///
820pub fn get_secret_key_base(request: Request) -> String {
821 request.body.secret_key_base
822}
···838839/// Get the maximum permitted total size of a files uploaded by a request in
840/// bytes.
841+///
842pub fn get_max_files_size(request: Request) -> Int {
843 request.body.max_files_size
844}
···858859/// Get the size limit for each chunk of the request body when read from the
860/// client.
861+///
862pub fn get_read_chunk_size(request: Request) -> Int {
863 request.body.read_chunk_size
864}
865866/// A convenient alias for a HTTP request with a Wisp connection as the body.
867+///
868pub type Request =
869 HttpRequest(Connection)
870···873/// if the method is not correct.
874///
875/// # Examples
876+///
877/// ```gleam
878/// fn handle_request(request: Request) -> Response {
879/// use <- wisp.require_method(request, http.Patch)
···894895// TODO: re-export once Gleam has a syntax for that
896/// Return the non-empty segments of a request path.
897+///
898/// # Examples
899///
900/// ```gleam
···908909// TODO: re-export once Gleam has a syntax for that
910/// Set a given header to a given value, replacing any existing value.
911+///
912/// # Examples
913///
914/// ```gleam
···978/// return an incorrect value, depending on the underlying web server. It is the
979/// responsibility of the caller to cache the body if it is needed multiple
980/// times.
981+///
982/// If the body is larger than the `max_body_size` limit then an empty response
983/// with status code 413: Entity too large will be returned to the client.
984+///
985/// If the body is found not to be valid UTF-8 then an empty response with
986/// status code 400: Bad request will be returned to the client.
987+///
988/// # Examples
989///
990/// ```gleam
···1014/// return an incorrect value, depending on the underlying web server. It is the
1015/// responsibility of the caller to cache the body if it is needed multiple
1016/// times.
1017+///
1018/// If the body is larger than the `max_body_size` limit then an empty response
1019/// with status code 413: Entity too large will be returned to the client.
1020+///
1021/// # Examples
1022///
1023/// ```gleam
···1040// TODO: don't always return entity to large. Other errors are possible, such as
1041// network errors.
1042/// Read the entire body of the request as a bit string.
1043+///
1044/// You may instead wish to use the `require_bit_array_body` or the
1045/// `require_string_body` middleware functions instead.
1046+///
1047/// This function does not cache the body in any way, so if you call this
1048/// function (or any other body reading function) more than once it may hang or
1049/// return an incorrect value, depending on the underlying web server. It is the
1050/// responsibility of the caller to cache the body if it is needed multiple
1051/// times.
1052+///
1053/// If the body is larger than the `max_body_size` limit then an empty response
1054/// with status code 413: Entity too large will be returned to the client.
1055+///
1056pub fn read_body_to_bitstring(request: Request) -> Result(BitArray, Nil) {
1057 let connection = request.body
1058 read_body_loop(
···1086/// A middleware which extracts form data from the body of a request that is
1087/// encoded as either `application/x-www-form-urlencoded` or
1088/// `multipart/form-data`.
1089+///
1090/// Extracted fields are sorted into alphabetical order by key, so if you wish
1091/// to use pattern matching the order can be relied upon.
1092+///
1093/// ```gleam
1094/// fn handle_request(request: Request) -> Response {
1095/// use form <- wisp.require_form(request)
···1118///
1119/// If the body cannot be parsed successfully then an empty response with status
1120/// code 400: Bad request will be returned to the client.
1121+///
1122pub fn require_form(
1123 request: Request,
1124 next: fn(FormData) -> Response,
···1145/// Unsupported media type if the header is not the expected value
1146///
1147/// # Examples
1148+///
1149/// ```gleam
1150/// fn handle_request(request: Request) -> Response {
1151/// use <- wisp.require_content_type(request, "application/json")
···1165}
11661167/// A middleware which extracts JSON from the body of a request.
1168+///
1169/// ```gleam
1170/// fn handle_request(request: Request) -> Response {
1171/// use json <- wisp.require_json(request)
···1186///
1187/// If the body cannot be parsed successfully then an empty response with status
1188/// code 400: Bad request will be returned to the client.
1189+///
1190pub fn require_json(request: Request, next: fn(Dynamic) -> Response) -> Response {
1191 use <- require_content_type(request, "application/json")
1192 use body <- require_string_body(request)
···1420}
14211422/// Data parsed from form sent in a request's body.
1423+///
1424pub type FormData {
1425 FormData(
1426 /// String values of the form's fields.
···1544///
1545/// The `under` parameter is the request path prefix that must match for the
1546/// file to be served.
1547+///
1548/// | `under` | `from` | `request.path` | `file` |
1549/// |-----------|---------|--------------------|-------------------------|
1550/// | `/static` | `/data` | `/static/file.txt` | `/data/file.txt` |
···1691/// > erlang.priv_directory("my_app")
1692/// // -> Ok("/some/location/my_app/priv")
1693/// ```
1694+///
1695pub const priv_directory = erlang.priv_directory
16961697//
···17001701/// Configure the Erlang logger, setting the minimum log level to `info`, to be
1702/// called when your application starts.
1703+///
1704/// You may wish to use an alternative for this such as one provided by a more
1705/// sophisticated logging library.
1706+///
1707/// In future this function may be extended to change the output format.
1708+///
1709pub fn configure_logger() -> Nil {
1710 logging.configure()
1711}
17121713/// Log a message to the Erlang logger with the level of `emergency`.
1714+///
1715/// See the [Erlang logger documentation][1] for more information.
1716+///
1717/// [1]: https://www.erlang.org/doc/man/logger
1718+///
1719pub fn log_emergency(message: String) -> Nil {
1720 logging.log(logging.Emergency, message)
1721}
17221723/// Log a message to the Erlang logger with the level of `alert`.
1724+///
1725/// See the [Erlang logger documentation][1] for more information.
1726+///
1727/// [1]: https://www.erlang.org/doc/man/logger
1728+///
1729pub fn log_alert(message: String) -> Nil {
1730 logging.log(logging.Alert, message)
1731}
17321733/// Log a message to the Erlang logger with the level of `critical`.
1734+///
1735/// See the [Erlang logger documentation][1] for more information.
1736+///
1737/// [1]: https://www.erlang.org/doc/man/logger
1738+///
1739pub fn log_critical(message: String) -> Nil {
1740 logging.log(logging.Critical, message)
1741}
17421743/// Log a message to the Erlang logger with the level of `error`.
1744+///
1745/// See the [Erlang logger documentation][1] for more information.
1746+///
1747/// [1]: https://www.erlang.org/doc/man/logger
1748+///
1749pub fn log_error(message: String) -> Nil {
1750 logging.log(logging.Error, message)
1751}
17521753/// Log a message to the Erlang logger with the level of `warning`.
1754+///
1755/// See the [Erlang logger documentation][1] for more information.
1756+///
1757/// [1]: https://www.erlang.org/doc/man/logger
1758+///
1759pub fn log_warning(message: String) -> Nil {
1760 logging.log(logging.Warning, message)
1761}
17621763/// Log a message to the Erlang logger with the level of `notice`.
1764+///
1765/// See the [Erlang logger documentation][1] for more information.
1766+///
1767/// [1]: https://www.erlang.org/doc/man/logger
1768+///
1769pub fn log_notice(message: String) -> Nil {
1770 logging.log(logging.Notice, message)
1771}
17721773/// Log a message to the Erlang logger with the level of `info`.
1774+///
1775/// See the [Erlang logger documentation][1] for more information.
1776+///
1777/// [1]: https://www.erlang.org/doc/man/logger
1778+///
1779pub fn log_info(message: String) -> Nil {
1780 logging.log(logging.Info, message)
1781}
17821783/// Log a message to the Erlang logger with the level of `debug`.
1784+///
1785/// See the [Erlang logger documentation][1] for more information.
1786+///
1787/// [1]: https://www.erlang.org/doc/man/logger
1788+///
1789pub fn log_debug(message: String) -> Nil {
1790 logging.log(logging.Debug, message)
1791}
···18041805/// Sign a message which can later be verified using the `verify_signed_message`
1806/// function to detect if the message has been tampered with.
1807+///
1808/// Signed messages are not encrypted and can be read by anyone. They are not
1809/// suitable for storing sensitive information.
1810+///
1811/// This function uses the secret key base from the request. If the secret
1812/// changes then the signature will no longer be verifiable.
1813+///
1814pub fn sign_message(
1815 request: Request,
1816 message: BitArray,
···1820}
18211822/// Verify a signed message which was signed using the `sign_message` function.
1823+///
1824/// Returns the content of the message if the signature is valid, otherwise
1825/// returns an error.
1826+///
1827/// This function uses the secret key base from the request. If the secret
1828/// changes then the signature will no longer be verifiable.
1829+///
1830pub fn verify_signed_message(
1831 request: Request,
1832 message: String,
···1864/// wisp.ok()
1865/// |> wisp.set_cookie(request, "id", "123", wisp.PlainText, 60 * 60)
1866/// ```
1867+///
1868/// Setting a signed cookie that the client can read but not modify:
1869+///
1870/// ```gleam
1871/// wisp.ok()
1872/// |> wisp.set_cookie(request, "id", value, wisp.Signed, 60 * 60)
···19361937// TODO: chunk the body
1938/// Create a connection which will return the given body when read.
1939+///
1940/// This function is intended for use in tests, though you probably want the
1941/// `wisp/testing` module instead.
1942+///
1943pub fn create_canned_connection(
1944 body: BitArray,
1945 secret_key_base: String,