wisp

Types

The body of a HTTP response, to be sent to the client.

pub type Body {
  Text(StringBuilder)
  Bytes(BytesBuilder)
  File(path: String)
  Empty
}

Constructors

  • Text(StringBuilder)

    A body of unicode text.

    The body is represented using a StringBuilder. If you have a String you can use the string_builder.from_string function to convert it.

  • Bytes(BytesBuilder)

    A body of binary data.

    The body is represented using a StringBuilder. If you have a String you can use the string_builder.from_string function to convert it.

  • File(path: String)

    A body of the contents of a file.

    This will be sent efficiently using the send_file function of the underlying HTTP server. The file will not be read into memory so it is safe to send large files this way.

  • Empty

    An empty body. This may be returned by the require_* middleware functions in the event of a failure, invalid request, or other situation in which the request cannot be processed.

    Your application may wish to use a middleware to provide default responses in place of any with an empty body.

The connection to the client for a HTTP request.

The body of the request can be read from this connection using functions such as require_multipart_body.

pub opaque type Connection

Data parsed from form sent in a request’s body.

pub type FormData {
  FormData(
    values: List(#(String, String)),
    files: List(#(String, UploadedFile)),
  )
}

Constructors

  • FormData(
      values: List(#(String, String)),
      files: List(#(String, UploadedFile)),
    )

    Arguments

    • values

      String values of the form’s fields.

    • files

      Uploaded files.

A convenient alias for a HTTP request with a Wisp connection as the body.

pub type Request =
  HttpRequest(Connection)

An alias for a HTTP response containing a Body.

pub type Response =
  HttpResponse(Body)
pub type Security {
  PlainText
  Signed
}

Constructors

  • PlainText

    The value is store as plain text without any additional security. The client will be able to read and modify the value, and create new values.

  • Signed

    The value is signed to prevent modification. The client will be able to read the value but not modify it, or create new values.

pub type UploadedFile {
  UploadedFile(file_name: String, path: String)
}

Constructors

  • UploadedFile(file_name: String, path: String)

    Arguments

    • file_name

      The name that was given to the file in the form. This is user input and should not be trusted.

    • path

      The location of the file on the server. This is a temporary file and will be deleted when the request has finished being handled.

Constants

pub const path_segments: fn(Request(a)) -> List(String)

Return the non-empty segments of a request path.

Examples

> request.new()
> |> request.set_path("/one/two/three")
> |> wisp.path_segments
["one", "two", "three"]
pub const priv_directory: fn(String) -> Result(String, Nil)

Returns the path of a package’s priv directory, where extra non-Gleam or Erlang files are typically kept.

Returns an error if no package was found with the given name.

Example

> erlang.priv_directory("my_app")
// -> Ok("/some/location/my_app/priv")
pub const set_header: fn(Response(a), String, String) ->
  Response(a)

Set a given header to a given value, replacing any existing value.

Examples

> wisp.ok()
> |> wisp.set_header("content-type", "application/json")
Request(200, [#("content-type", "application/json")], Empty)

Functions

pub fn accepted() -> Response(Body)

Create an empty response with status code 202: Accepted.

Examples

accepted()
// -> Response(202, [], Empty)
pub fn bad_request() -> Response(Body)

Create an empty response with status code 400: Bad request.

Examples

bad_request()
// -> Response(400, [], Empty)
pub fn configure_logger() -> Nil

Configure the Erlang logger, setting the minimum log level to info, to be called when your application starts.

You may wish to use an alternative for this such as one provided by a more sophisticated logging library.

In future this function may be extended to change the output format.

pub fn create_canned_connection(
  body: BitArray,
  secret_key_base: String,
) -> Connection

Create a connection which will return the given body when read.

This function is intended for use in tests, though you probably want the wisp/testing module instead.

pub fn created() -> Response(Body)

Create an empty response with status code 201: Created.

Examples

created()
// -> Response(201, [], Empty)
pub fn delete_temporary_files(
  request: Request(Connection),
) -> Result(Nil, FileError)

Delete any temporary files created for the given request.

If you are using the mist_handler function or another compliant web server adapter then this file will be deleted for you when the request is complete. Otherwise you will need to call this function yourself.

pub fn entity_too_large() -> Response(Body)

Create an empty response with status code 413: Entity too large.

Examples

entity_too_large()
// -> Response(413, [], Empty)
pub fn escape_html(content: String) -> String

Escape a string so that it can be safely included in a HTML document.

Any content provided by the user should be escaped before being included in a HTML document to prevent cross-site scripting attacks.

Examples

escape_html("<h1>Hello, Joe!</h1>")
// -> "&lt;h1&gt;Hello, Joe!&lt;/h1&gt;"
pub fn file_download(
  response: Response(Body),
  named name: String,
  from path: String,
) -> Response(Body)

Send a file from the disc as a file download.

The operating system send_file function is used to efficiently send the file over the network socket without reading the entire file into memory.

The content-disposition header will be set to attachment; filename="name" to ensure the file is downloaded by the browser. This is especially good for files that the browser would otherwise attempt to open as this can result in cross-site scripting vulnerabilities.

If you wish to not set the content-disposition header you could use the set_body function with the File body variant.

Examples

response(200)
|> file_download(named: "myfile.txt", from: "/tmp/myfile.txt")
// -> Response(
//   200,
//   [#("content-disposition", "attachment; filename=\"myfile.txt\"")],
//   File("/tmp/myfile.txt"),
// )
pub fn file_download_from_memory(
  response: Response(Body),
  named name: String,
  containing data: BytesBuilder,
) -> Response(Body)

Send a file from memory as a file download.

If your file is already on the disc use file_download instead, to avoid having to read the file into memory to send it.

The content-disposition header will be set to attachment; filename="name" to ensure the file is downloaded by the browser. This is especially good for files that the browser would otherwise attempt to open as this can result in cross-site scripting vulnerabilities.

Examples

response(200)
|> file_download_from_memory(named: "myfile.txt", containing: "Hello, Joe!")
// -> Response(
//   200,
//   [#("content-disposition", "attachment; filename=\"myfile.txt\"")],
//   File("/tmp/myfile.txt"),
// )
pub fn get_cookie(
  request: Request(Connection),
  name: String,
  security: Security,
) -> Result(String, Nil)

Get a cookie from the request.

If a cookie is missing, found to be malformed, or the signature is invalid for a signed cookie, then Error(Nil) is returned.

wisp.get_cookie(request, "group")
// -> Ok("A")
pub fn get_max_body_size(request: Request(Connection)) -> Int

Get the maximum permitted size of a request body of the request in bytes.

pub fn get_max_files_size(request: Request(Connection)) -> Int

Get the maximum permitted total size of a files uploaded by a request in bytes.

pub fn get_query(
  request: Request(Connection),
) -> List(#(String, String))

Parse the query parameters of a request into a list of key-value pairs. The key_find function in the gleam/list stdlib module may be useful for finding values in the list.

Query parameter names do not have to be unique and so may appear multiple times in the list.

pub fn get_read_chunk_size(request: Request(Connection)) -> Int

Get the size limit for each chunk of the request body when read from the client.

pub fn get_secret_key_base(
  request: Request(Connection),
) -> String

Get the secret key base used to sign cookies and other sensitive data.

pub fn handle_head(
  req: Request(Connection),
  next handler: fn(Request(Connection)) -> Response(Body),
) -> Response(Body)

A middleware function that converts HEAD requests to GET requests, handles the request, and then discards the response body. This is useful so that your application can handle HEAD requests without having to implement handlers for them.

The x-original-method header is set to "HEAD" for requests that were originally HEAD requests.

Examples

fn handle_request(req: Request) -> Response {
  use req <- wisp.handle_head(req)
  // ...
}
pub fn html_body(
  response: Response(Body),
  html: StringBuilder,
) -> Response(Body)

Set the body of a response to a given HTML document, and set the content-type header to text/html.

The body is expected to be valid HTML, though this is not validated.

Examples

let body = string_builder.from_string("<h1>Hello, Joe!</h1>")
response(201)
|> html_body(body)
// -> Response(201, [#("content-type", "text/html")], Text(body))
pub fn html_response(
  html: StringBuilder,
  status: Int,
) -> Response(Body)

Create a HTML response.

The body is expected to be valid HTML, though this is not validated. The content-type header will be set to text/html.

Examples

let body = string_builder.from_string("<h1>Hello, Joe!</h1>")
html_response(body, 200)
// -> Response(200, [#("content-type", "text/html")], Text(body))
pub fn internal_server_error() -> Response(Body)

Create an empty response with status code 500: Internal server error.

Examples

internal_server_error()
// -> Response(500, [], Empty)
pub fn json_body(
  response: Response(Body),
  json: StringBuilder,
) -> Response(Body)

Set the body of a response to a given JSON document, and set the content-type header to application/json.

The body is expected to be valid JSON, though this is not validated.

Examples

let body = string_builder.from_string("{\"name\": \"Joe\"}")
response(201)
|> json_body(body)
// -> Response(201, [#("content-type", "application/json")], Text(body))
pub fn json_response(
  json: StringBuilder,
  status: Int,
) -> Response(Body)

Create a JSON response.

The body is expected to be valid JSON, though this is not validated. The content-type header will be set to application/json.

Examples

let body = string_builder.from_string("{\"name\": \"Joe\"}")
json_response(body, 200)
// -> Response(200, [#("content-type", "application/json")], Text(body))
pub fn log_alert(message: String) -> Nil

Log a message to the Erlang logger with the level of alert.

See the Erlang logger documentation for more information.

pub fn log_critical(message: String) -> Nil

Log a message to the Erlang logger with the level of critical.

See the Erlang logger documentation for more information.

pub fn log_debug(message: String) -> Nil

Log a message to the Erlang logger with the level of debug.

See the Erlang logger documentation for more information.

pub fn log_emergency(message: String) -> Nil

Log a message to the Erlang logger with the level of emergency.

See the Erlang logger documentation for more information.

pub fn log_error(message: String) -> Nil

Log a message to the Erlang logger with the level of error.

See the Erlang logger documentation for more information.

pub fn log_info(message: String) -> Nil

Log a message to the Erlang logger with the level of info.

See the Erlang logger documentation for more information.

pub fn log_notice(message: String) -> Nil

Log a message to the Erlang logger with the level of notice.

See the Erlang logger documentation for more information.

pub fn log_request(
  req: Request(Connection),
  handler: fn() -> Response(Body),
) -> Response(Body)

A middleware function that logs details about the request and response.

The format used logged by this middleware may change in future versions of Wisp.

Examples

fn handle_request(req: Request) -> Response {
  use <- wisp.log_request(req)
  // ...
}
pub fn log_warning(message: String) -> Nil

Log a message to the Erlang logger with the level of warning.

See the Erlang logger documentation for more information.

pub fn method_not_allowed(
  allowed methods: List(Method),
) -> Response(Body)

Create an empty response with status code 405: Method Not Allowed. Use this when a request does not have an appropriate method to be handled.

The allow header will be set to a comma separated list of the permitted methods.

Examples

method_not_allowed(allowed: [Get, Post])
// -> Response(405, [#("allow", "GET, POST")], Empty)
pub fn method_override(request: Request(a)) -> Request(a)

This function overrides an incoming POST request with a method given in the request’s _method query paramerter. This is useful as web browsers typically only support GET and POST requests, but our application may expect other HTTP methods that are more semantically correct.

The methods PUT, PATCH, and DELETE are accepted for overriding, all others are ignored.

The _method query paramerter can be specified in a HTML form like so:

Examples

fn handle_request(request: Request) -> Response {
  let request = wisp.method_override(request)
  // The method has now been overridden if appropriate
}
pub fn mist_handler(
  handler: fn(Request(Connection)) -> Response(Body),
  secret_key_base: String,
) -> fn(Request(mist/internal/http.Connection)) ->
  Response(ResponseData)

Convert a Wisp request handler into a function that can be run with the Mist web server.

Examples

pub fn main() {
  let secret_key_base = "..."
  let assert Ok(_) =
    handle_request
    |> wisp.mist_handler(secret_key_base)
    |> mist.new
    |> mist.port(8000)
    |> mist.start_http
  process.sleep_forever()
}
pub fn moved_permanently(to url: String) -> Response(Body)

Create an empty response with status code 308: Moved Permanently, and the location header set to the given URL. Used to redirect the client to another page.

This redirect is permanent and the client is expected to cache the new location, using it for future requests.

Examples

moved_permanently(to: "https://example.com")
// -> Response(308, [#("location", "https://example.com")], Empty)
pub fn new_temporary_file(
  request: Request(Connection),
) -> Result(String, FileError)

Create a new temporary directory for the given request.

If you are using the mist_handler function or another compliant web server adapter then this file will be deleted for you when the request is complete. Otherwise you will need to call the delete_temporary_files function yourself.

pub fn no_content() -> Response(Body)

Create an empty response with status code 204: No content.

Examples

no_content()
// -> Response(204, [], Empty)
pub fn not_found() -> Response(Body)

Create an empty response with status code 404: No content.

Examples

not_found()
// -> Response(404, [], Empty)
pub fn ok() -> Response(Body)

Create an empty response with status code 200: OK.

Examples

ok()
// -> Response(200, [], Empty)
pub fn random_string(length: Int) -> String

Generate a random string of the given length.

pub fn read_body_to_bitstring(
  request: Request(Connection),
) -> Result(BitArray, Nil)

Read the entire body of the request as a bit string.

You may instead wish to use the require_bit_array_body or the require_string_body middleware functions instead.

This function does not cache the body in any way, so if you call this function (or any other body reading function) more than once it may hang or return an incorrect value, depending on the underlying web server. It is the responsibility of the caller to cache the body if it is needed multiple times.

If the body is larger than the max_body_size limit then an empty response with status code 413: Entity too large will be returned to the client.

pub fn redirect(to url: String) -> Response(Body)

Create an empty response with status code 303: See Other, and the location header set to the given URL. Used to redirect the client to another page.

Examples

redirect(to: "https://example.com")
// -> Response(303, [#("location", "https://example.com")], Empty)
pub fn require_bit_array_body(
  request: Request(Connection),
  next: fn(BitArray) -> Response(Body),
) -> Response(Body)

A middleware function which reads the entire body of the request as a bit string.

This function does not cache the body in any way, so if you call this function (or any other body reading function) more than once it may hang or return an incorrect value, depending on the underlying web server. It is the responsibility of the caller to cache the body if it is needed multiple times.

If the body is larger than the max_body_size limit then an empty response with status code 413: Entity too large will be returned to the client.

Examples

fn handle_request(request: Request) -> Response {
  use body <- wisp.require_string_body(request)
  // ...
}
pub fn require_content_type(
  request: Request(Connection),
  expected: String,
  next: fn() -> Response(Body),
) -> Response(Body)

This middleware function ensures that the request has a value for the content-type header, returning an empty response with status code 415: Unsupported media type if the header is not the expected value

Examples

fn handle_request(request: Request) -> Response {
  use <- wisp.require_content_type(request, "application/json")
  // ...
}
pub fn require_form(
  request: Request(Connection),
  next: fn(FormData) -> Response(Body),
) -> Response(Body)

A middleware which extracts form data from the body of a request that is encoded as either application/x-www-form-urlencoded or multipart/form-data.

Extracted fields are sorted into alphabetical order by key, so if you wish to use pattern matching the order can be relied upon.

fn handle_request(request: Request) -> Response {
  use form <- wisp.require_form(request)
  case form.values {
    [#("password", pass), #("username", username)] -> // ...
    _ -> // ...
  }
}

The set_max_body_size, set_max_files_size, and set_read_chunk_size can be used to configure the reading of the request body.

Any file uploads will streamed into temporary files on disc. These files are automatically deleted when the request handler returns, so if you wish to use them after the request has completed you will need to move them to a new location.

If the request does not have a recognised content-type header then an empty response with status code 415: Unsupported media type will be returned to the client.

If the request body is larger than the max_body_size or max_files_size limits then an empty response with status code 413: Entity too large will be returned to the client.

If the body cannot be parsed successfully then an empty response with status code 400: Bad request will be returned to the client.

pub fn require_json(
  request: Request(Connection),
  next: fn(Dynamic) -> Response(Body),
) -> Response(Body)

A middleware which extracts JSON from the body of a request.

fn handle_request(request: Request) -> Response {
  use json <- wisp.require_json(request)
  // decode and use JSON here...
}

The set_max_body_size and set_read_chunk_size can be used to configure the reading of the request body.

If the request does not have the content-type set to application/json an empty response with status code 415: Unsupported media type will be returned to the client.

If the request body is larger than the max_body_size or max_files_size limits then an empty response with status code 413: Entity too large will be returned to the client.

If the body cannot be parsed successfully then an empty response with status code 400: Bad request will be returned to the client.

pub fn require_method(
  request: Request(a),
  method: Method,
  next: fn() -> Response(Body),
) -> Response(Body)

This middleware function ensures that the request has a specific HTTP method, returning an empty response with status code 405: Method not allowed if the method is not correct.

Examples

fn handle_request(request: Request) -> Response {
  use <- wisp.require_method(request, http.Patch)
  // ...
}
pub fn require_string_body(
  request: Request(Connection),
  next: fn(String) -> Response(Body),
) -> Response(Body)

A middleware function which reads the entire body of the request as a string.

This function does not cache the body in any way, so if you call this function (or any other body reading function) more than once it may hang or return an incorrect value, depending on the underlying web server. It is the responsibility of the caller to cache the body if it is needed multiple times.

If the body is larger than the max_body_size limit then an empty response with status code 413: Entity too large will be returned to the client.

If the body is found not to be valid UTF-8 then an empty response with status code 400: Bad request will be returned to the client.

Examples

fn handle_request(request: Request) -> Response {
  use body <- wisp.require_string_body(request)
  // ...
}
pub fn rescue_crashes(
  handler: fn() -> Response(Body),
) -> Response(Body)

A middleware function that rescues crashes and returns an empty response with status code 500: Internal server error.

Examples

fn handle_request(req: Request) -> Response {
  use <- wisp.rescue_crashes
  // ...
}
pub fn response(status: Int) -> Response(Body)

Create an empty response with the given status code.

Examples

response(200)
// -> Response(200, [], Empty)
pub fn serve_static(
  req: Request(Connection),
  under prefix: String,
  from directory: String,
  next handler: fn() -> Response(Body),
) -> Response(Body)

A middleware function that serves files from a directory, along with a suitable content-type header for known file extensions.

Files are sent using the File response body type, so they will be sent directly to the client from the disc, without being read into memory.

The under parameter is the request path prefix that must match for the file to be served.

underfromrequest.pathfile
/static/data/static/file.txt/data/file.txt
``/data/static/file.txt/data/static/file.txt
/static``/static/file.txtfile.txt

This middleware will discard any .. path segments in the request path to prevent the client from accessing files outside of the directory. It is advised not to serve a directory that contains your source code, application configuration, database, or other private files.

Examples

fn handle_request(req: Request) -> Response {
  use <- wisp.serve_static(req, under: "/static", from: "/public")
  // ...
}

Typically you static assets may be kept in your project in a directory called priv. The priv_directory function can be used to get a path to this directory.

fn handle_request(req: Request) -> Response {
  let assert Ok(priv) = priv_directory("my_application")
  use <- wisp.serve_static(req, under: "/static", from: priv)
  // ...
}
pub fn set_body(
  response: Response(Body),
  body: Body,
) -> Response(Body)

Set the body of a response.

Examples

response(200)
|> set_body(File("/tmp/myfile.txt"))
// -> Response(200, [], File("/tmp/myfile.txt"))
pub fn set_cookie(
  response response: Response(Body),
  request request: Request(Connection),
  name name: String,
  value value: String,
  security security: Security,
  max_age max_age: Int,
) -> Response(Body)

Set a cookie on the response. After max_age seconds the cookie will be expired by the client.

This function will sign the value if the security parameter is set to Signed, making it so the cookie cannot be tampered with by the client.

Values are base64 encoded so they can contain any characters you want, even if they would not be permitted directly in a cookie.

Cookies are set using gleam_http’s default attributes for HTTPS. If you wish for more control over the cookie attributes then you may want to use the gleam/http/cookie module from the gleam_http package instead of this function. Be sure to sign and escape the cookie value as needed.

Examples

Setting a plain text cookie that the client can read and modify:

wisp.ok()
|> wisp.set_cookie("id", "123", wisp.PlainText, 60 * 60)

Setting a signed cookie that the client can read but not modify:

wisp.ok()
|> wisp.set_cookie("id", value, wisp.Signed, 60 * 60)
pub fn set_max_body_size(
  request: Request(Connection),
  size: Int,
) -> Request(Connection)

Set the maximum permitted size of a request body of the request in bytes.

If a body is larger than this size attempting to read the body will result in a response with status code 413: Entity too large will be returned to the client.

This limit only applies for headers and bodies that get read into memory. Part of a multipart body that contain files and so are streamed to disc instead use the max_files_size limit.

pub fn set_max_files_size(
  request: Request(Connection),
  size: Int,
) -> Request(Connection)

Set the maximum permitted size of all files uploaded by a request, in bytes.

If a request contains fails which are larger in total than this size then attempting to read the body will result in a response with status code 413: Entity too large will be returned to the client.

This limit only applies for files in a multipart body that get streamed to disc. For headers and other content that gets read into memory use the max_files_size limit.

pub fn set_read_chunk_size(
  request: Request(Connection),
  size: Int,
) -> Request(Connection)

The the size limit for each chunk of the request body when read from the client.

This value is passed to the underlying web server when reading the body and the exact size of chunks read depends on the server implementation. It most likely will read chunks smaller than this size if not yet enough data has been received from the client.

pub fn set_secret_key_base(
  request: Request(Connection),
  key: String,
) -> Request(Connection)

Set the secret key base used to sign cookies and other sensitive data.

This key must be at least 64 bytes long and should be kept secret. Anyone with this secret will be able to manipulate signed cookies and other sensitive data.

Panics

This function will panic if the key is less than 64 bytes long.

pub fn sign_message(
  request: Request(Connection),
  message: BitArray,
  algorithm: HashAlgorithm,
) -> String

Sign a message which can later be verified using the verify_signed_message function to detect if the message has been tampered with.

Signed messages are not encrypted and can be read by anyone. They are not suitable for storing sensitive information.

This function uses the secret key base from the request. If the secret changes then the signature will no longer be verifiable.

pub fn string_body(
  response: Response(Body),
  content: String,
) -> Response(Body)

Set the body of a response to a given string builder.

You likely want to also set the request content-type header to an appropriate value for the format of the content.

Examples

let body = 
response(201)
|> string_builder_body("Hello, Joe!")
// -> Response(
//   201,
//   [],
//   Text(string_builder.from_string("Hello, Joe"))
// )
pub fn string_builder_body(
  response: Response(Body),
  content: StringBuilder,
) -> Response(Body)

Set the body of a response to a given string builder.

You likely want to also set the request content-type header to an appropriate value for the format of the content.

Examples

let body = string_builder.from_string("Hello, Joe!")
response(201)
|> string_builder_body(body)
// -> Response(201, [], Text(body))
pub fn unprocessable_entity() -> Response(Body)

Create an empty response with status code 422: Unprocessable entity.

Examples

unprocessable_entity()
// -> Response(422, [], Empty)
pub fn unsupported_media_type(
  accept acceptable: List(String),
) -> Response(Body)

Create an empty response with status code 415: Unsupported media type.

The allow header will be set to a comma separated list of the permitted content-types.

Examples

unsupported_media_type(accept: ["application/json", "text/plain"])
// -> Response(415, [#("allow", "application/json, text/plain")], Empty)
pub fn verify_signed_message(
  request: Request(Connection),
  message: String,
) -> Result(BitArray, Nil)

Verify a signed message which was signed using the sign_message function.

Returns the content of the message if the signature is valid, otherwise returns an error.

This function uses the secret key base from the request. If the secret changes then the signature will no longer be verifiable.

Search Document