Movatterモバイル変換


[0]ホーム

URL:


Dream

Tidy Web framework for OCaml and ReasonML
let hello who =<html><body><h1>Hello,<%s who%>!</h1></body></html>let() =Dream.run  @@Dream.logger  @@Dream.router[Dream.get"/" (fun _ ->Dream.html (hello"world"));]

Dream is an easy-to-use, boilerplate-free Web framework, whose entireAPI fits on this page!

It supportsTLS,WebSockets, andGraphQL. HTTP/2 support istransparent. It also includes:

You can integrate Dream into a fully self-contained binary, or run it in large deployments behind proxies. Dream assumes no databases, environment variables, or configuration files, and requires no setup beyond installing the one package,dream.

Dream sticks to base OCaml types as much as possible, introducing only a fewtypes of its own. Dream handlers and middlewares are just bare functions. Dream has a flat namespace and aims for maximal clarity.

This page is the API documentation, but Dream also has:

Types

Dream is built on just five types. The first two are the data types of Dream. Both are abstract, even though they appear to have definitions:

type request =clientmessage

HTTP requests, such asGET /something HTTP/1.1. SeeRequests.

and response =servermessage

HTTP responses, such as200 OK. SeeResponses.

The remaining three types are for building up Web apps.

and handler =request->responsepromise

Handlers are asynchronous functions from requests to responses. Example1-hello shows the simplest handler, an anonymous function which we pass toDream.run. This creates a complete Web server! You can also see the Reason version in exampler-hello.

let () =  Dream.run (fun _ ->    Dream.html "Good morning, world!")
and middleware =handler->handler

Middlewares are functions that take ahandler, and run some code before or after — producing a “bigger” handler. Example2-middleware inserts theDream.logger middleware into a Web app:

let () =  Dream.run  @@ Dream.logger  @@ fun _ -> Dream.html "Good morning, world!"

Examples4-counter and5-promise show user-defined middlewares:

let count_requests inner_handler request =  count := !count + 1;  inner_handler request

In case you are wondering why the example middlewarecount_requests takes two arguments, while the type says it should take only one, it's because:

middleware  = handler -> handler  = handler -> (request -> response promise)  = handler -> request -> response promise
and route

Routes tellDream.router which handler to select for each request. SeeRouting and example3-router. Routes are created by helpers such asDream.get andDream.scope:

Dream.router [  Dream.scope "/admin" [Dream.memory_sessions] [    Dream.get "/" admin_handler;    Dream.get "/logout" admin_logout_handler;  ];]

Algebra

The three handler-related types have a vaguely algebraic interpretation:

Dream.scope implements a left distributive law, making Dream a ring-like structure.

Helpers

and 'a message

'a message, pronounced “any message,” allows some functions to take eitherrequest orresponse as arguments, because both are defined in terms of'a message. For example, inHeaders:

val Dream.header : string -> 'a message -> string option
and client
and server

Type parameters formessage forrequest andresponse, respectively. These are “phantom” types. They have no meaning other than they are different from each other. Dream only ever createsclient message andserver message.client andserver are never mentioned again in the docs.

and 'a promise = 'aLwt.t

Dream usesLwt for promises and asynchronous I/O. See example5-promise.

Useraise to reject promises. If you are writing a library, you may prefer usingLwt.fail in some places, in order to avoid clobbering your user's current exception backtrace — though, in most cases, you should still extend it withraise andlet%lwt, instead.

Methods

type method_ = [  | `GET  | `POST  | `PUT  | `DELETE  | `HEAD  | `CONNECT  | `OPTIONS  | `TRACE  | `PATCH  | `Methodofstring]

HTTP request methods. SeeRFC 7231 §4.2,RFC 5789 §2, andMDN.

val method_to_string : [<method_ ]->string

Evaluates to a string representation of the given method. For example,`GET is converted to"GET".

val string_to_method :string->method_

Evaluates to themethod_ corresponding to the given method string.

val methods_equal : [<method_ ]-> [<method_ ]->bool

Compares two methods, such that equal methods are detected even if one is represented as a string. For example,

Dream.methods_equal `GET (`Method "GET") = true
val normalize_method :[<method_ ]->method_

Converts methods represented as strings to variants. Methods generated by Dream are always normalized.

Dream.normalize_method (`Method "GET") = `GET

Status codes

type informational = [  | `Continue  | `Switching_Protocols]

Informational (1xx) status codes. SeeRFC 7231 §6.2 andMDN.101 Switching Protocols is generated internally byDream.websocket. It is usually not necessary to use it directly.

type successful = [  | `OK  | `Created  | `Accepted  | `Non_Authoritative_Information  | `No_Content  | `Reset_Content  | `Partial_Content]

Successful (2xx) status codes. SeeRFC 7231 §6.3,RFC 7233 §4.1 andMDN. The most common is200 OK.

type redirection = [  | `Multiple_Choices  | `Moved_Permanently  | `Found  | `See_Other  | `Not_Modified  | `Temporary_Redirect  | `Permanent_Redirect]

Redirection (3xx) status codes. SeeRFC 7231 §6.4 andRFC 7538 §3, andMDN. Use303 See Other to direct clients to follow up with aGET request, especially after a form submission. Use301 Moved Permanently for permanent redirections.

type client_error = [  | `Bad_Request  | `Unauthorized  | `Payment_Required  | `Forbidden  | `Not_Found  | `Method_Not_Allowed  | `Not_Acceptable  | `Proxy_Authentication_Required  | `Request_Timeout  | `Conflict  | `Gone  | `Length_Required  | `Precondition_Failed  | `Payload_Too_Large  | `URI_Too_Long  | `Unsupported_Media_Type  | `Range_Not_Satisfiable  | `Expectation_Failed  | `Misdirected_Request  | `Too_Early  | `Upgrade_Required  | `Precondition_Required  | `Too_Many_Requests  | `Request_Header_Fields_Too_Large  | `Unavailable_For_Legal_Reasons]

Client error (4xx) status codes. The most common are400 Bad Request,401 Unauthorized,403 Forbidden, and, of course,404 Not Found.

SeeMDN, and

type server_error = [  | `Internal_Server_Error  | `Not_Implemented  | `Bad_Gateway  | `Service_Unavailable  | `Gateway_Timeout  | `HTTP_Version_Not_Supported]

Server error (5xx) status codes. SeeRFC 7231 §6.6 andMDN. The most common of these is500 Internal Server Error.

type standard_status = [  |informational  |successful  |redirection  |client_error  |server_error]

Sum of all the status codes declared above.

type status = [  |standard_status  | `Statusofint]

Status codes, including codes directly represented as integers. See the types above for the full list and references.

val status_to_string : [<status ]->string

Evaluates to a string representation of the given status. For example,`Not_Found and`Status 404 are both converted to"Not Found". Numbers are used for unknown status codes. For example,`Status 567 is converted to"567".

val status_to_reason : [<status ]->stringoption

Converts known status codes to their string representations. Evaluates toNone for unknown status codes.

val status_to_int : [<status ]->int

Evaluates to the numeric value of the given status code.

val int_to_status :int->status

Evaluates to the symbolic representation of the status code with the given number.

val is_informational : [<status ]->bool

Evaluates totrue if the given status is either from typeDream.informational, or is in the range`Status 100`Status 199.

val is_successful : [<status ]->bool

LikeDream.is_informational, but for typeDream.successful and numeric codes2xx.

val is_redirection : [<status ]->bool

LikeDream.is_informational, but for typeDream.redirection and numeric codes3xx.

val is_client_error : [<status ]->bool

LikeDream.is_informational, but for typeDream.client_error and numeric codes4xx.

val is_server_error : [<status ]->bool

LikeDream.is_informational, but for typeDream.server_error and numeric codes5xx.

val status_codes_equal : [<status ]-> [<status ]->bool

Compares two status codes, such that equal codes are detected even if one is represented as a number. For example,

Dream.status_codes_equal `Not_Found (`Status 404) = true
val normalize_status :[<status ]->status

Converts status codes represented as numbers to variants. Status codes generated by Dream are always normalized.

Dream.normalize_status (`Status 404) = `Not_Found

Requests

val client :request->string

Client sending the request. For example,"127.0.0.1:56001".

val tls :request->bool

Whether the request was sent over a TLS connection.

val method_ :request->method_

Request method. For example,`GET.

val target :request->string

Request target. For example,"/foo/bar".

val set_client :request->string->unit

Replaces the client. SeeDream.client.

val set_method_ :request->[<method_ ]->unit

Replaces the method. SeeDream.method_.

val query :request->string->stringoption

First query parameter with the given name. SeeRFC 3986 §3.4 and examplew-query.

val queries :request->string->stringlist

All query parameters with the given name.

val all_queries :request->(string *string)list

Entire query string as a name-value list.

Responses

val response :?status:[<status ] ->  ?code:int ->  ?headers:(string *string)list ->string ->response

Creates a newresponse with the given string as body.~code and~status are two ways to specify thestatus code, which is200 OK by default. The headers are empty by default.

Note that browsers may interpret lack of aContent-Type: header as if its value wereapplication/octet-stream ortext/html; charset=us-ascii, which will prevent correct interpretation of UTF-8 strings. Either add aContent-Type: header using~headers orDream.add_header, or use a wrapper likeDream.html. The modernContent-Type: for HTML istext/html; charset=utf-8. SeeDream.text_html.

val respond :?status:[<status ] ->  ?code:int ->  ?headers:(string *string)list ->string ->responsepromise

Same asDream.response, but the newresponse is wrapped in apromise.

val html :?status:[<status ] ->  ?code:int ->  ?headers:(string *string)list ->string ->responsepromise

Same asDream.respond, but addsContent-Type: text/html; charset=utf-8. SeeDream.text_html.

As your Web app develops, consider addingContent-Security-Policy headers, as described in examplew-content-security-policy. These headers are completely optional, but they can provide an extra layer of defense for a mature app.

val json :?status:[<status ] ->  ?code:int ->  ?headers:(string *string)list ->string ->responsepromise

Same asDream.respond, but addsContent-Type: application/json. SeeDream.application_json.

val redirect :?status:[<redirection ] ->  ?code:int ->  ?headers:(string *string)list ->request ->string ->responsepromise

Creates a newresponse. Adds aLocation: header with the given string. The default status code is303 See Other, for a temporary redirection. Use~status:`Moved_Permanently or~code:301 for a permanent redirection.

If you use~code, be sure the number follows the pattern3xx, or most browsers and other clients won't actually perform a redirect.

Therequest is used for retrieving the site prefix, if the string is an absolute path. Most applications don't have a site prefix.

val empty :  ?headers:(string *string)list ->status ->responsepromise

Same asDream.response with the empty string for a body.

val status :response->status

Responsestatus. For example,`OK.

val set_status :response->status->unit

Sets the response status.

Headers

val header :'amessage->string->stringoption

First header with the given name. Header names are case-insensitive. SeeRFC 7230 §3.2 andMDN.

val headers :'amessage->string->stringlist

All headers with the given name.

val all_headers :'amessage->(string *string)list

Entire header set as name-value list.

val has_header :'amessage->string->bool

Whether the message has a header with the given name.

val add_header :  'amessage ->string ->string ->unit

Appends a header with the given name and value. Does not remove any existing headers with the same name.

val drop_header :'amessage->string->unit

Removes all headers with the given name.

val set_header :  'amessage ->string ->string ->unit

Equivalent toDream.drop_header followed byDream.add_header.

Cookies

Dream.set_cookie andDream.cookie are designed for round-tripping secure cookies. The most secure settings applicable to the current server are inferred automatically. See examplec-cookie.

Dream.set_cookie response request "my.cookie" "foo"Dream.cookie request "my.cookie"

TheDream.cookie call evaluates toSome "foo", but the actual cookie that is exchanged may look like:

__Host-my.cookie=AL7NLA8-so3e47uy0R5E2MpEQ0TtTWztdhq5pTEUT7KSFg; \  Path=/; Secure; HttpOnly; SameSite=Strict

Dream.set_cookie has a large number of optional arguments for tweaking the inferred security settings. If you use them, pass the same arguments toDream.cookie to automatically undo the result.

val set_cookie :?prefix:[< `Host | `Secure ]option ->  ?encrypt:bool ->  ?expires:float ->  ?max_age:float ->  ?domain:string ->  ?path:stringoption ->  ?secure:bool ->  ?http_only:bool ->  ?same_site:[< `Strict | `Lax | `None ]option ->response ->request ->string ->string ->unit

Appends aSet-Cookie: header to theresponse. Infers the most secure defaults from therequest.

Dream.set_cookie request response "my.cookie" "value"

Use theDream.set_secret middleware, or the Web app will not be able to decrypt cookies from prior starts.

See examplec-cookie.

Most of the optional arguments are for overriding inferred defaults.~expires and~max_age are independently useful. In particular, to delete a cookie, use~expires:0.

Dream.to_set_cookie is a “raw” version of this function that does not do any inference.

val drop_cookie :?prefix:[< `Host | `Secure ]option ->  ?domain:string ->  ?path:stringoption ->  ?secure:bool ->  ?http_only:bool ->  ?same_site:[< `Strict | `Lax | `None ]option ->response ->request ->string ->unit

Deletes the given cookie.

This function works by callingDream.set_cookie, and setting the cookie to expire in the past. Pass all the same optional values that you would pass toDream.set_cookie to make sure that the same cookie is deleted.

val cookie :  ?prefix:[< `Host | `Secure ]option ->  ?decrypt:bool ->  ?domain:string ->  ?path:stringoption ->  ?secure:bool ->request ->string ->stringoption

First cookie with the given name. See examplec-cookie.

Dream.cookie request "my.cookie"

Pass the same optional arguments as toDream.set_cookie for the same cookie. This will allowDream.cookie to infer the cookie name prefix, implementing a transparent cookie round trip with the most secure attributes applicable.

val all_cookies :request->(string *string)list

All cookies, with raw names and values.

Bodies

val body :'amessage->stringpromise

Retrieves the entire body. See example6-echo.

val set_body :'amessage->string->unit

Replaces the body.

Streams

type stream

Gradual reading of request bodies or gradual writing of response bodies.

val body_stream :request->stream

A stream that can be used to gradually read the request's body.

val stream :  ?status:[<status ] ->  ?code:int ->  ?headers:(string *string)list ->  ?close:bool ->    (stream ->unitpromise) ->responsepromise

Creates a response with astream open for writing, and passes the stream to the callback when it is ready. See examplej-stream.

fun request ->  Dream.stream (fun stream ->    Dream.write stream "foo")

Dream.stream automatically closes the stream when the callback returns or raises an exception. Pass~close:false to suppress this behavior.

val read :stream->stringoptionpromise

Retrieves a body chunk. See examplej-stream.

val write :stream->string->unitpromise

Streams out the string. The promise is fulfilled when the response can accept more writes.

val flush :stream->unitpromise

Flushes the stream's write buffer. Data is sent to the client.

val close :stream->unitpromise

Closes the stream.

Low-level streaming

Note: this part of the API is still a work in progress.

type buffer =  (char,Bigarray.int8_unsigned_elt,Bigarray.c_layout)Bigarray.Array1.t

Byte arrays in the C heap. SeeBigarray.Array1. This type is also found in several libraries installed by Dream, so their functions can be used withDream.buffer:

val read_stream :stream ->  data:(buffer ->int ->int ->bool ->bool ->unit) ->  flush:(unit ->unit) ->  ping:(buffer ->int ->int ->unit) ->  pong:(buffer ->int ->int ->unit) ->  close:(int ->unit) ->  exn:(exn ->unit) ->unit

Waits for the next stream event, and calls:

  • ~data with an offset and length, if abuffer is received, ~~flush if a flush request is received,
  • ~ping if a ping is received (WebSockets only),
  • ~pong if a pong is received (WebSockets only),
  • ~close if the stream is closed, and
  • ~exn to report an exception.
val write_stream :stream ->buffer ->int ->int ->bool ->bool ->  close:(int ->unit) ->  exn:(exn ->unit) ->  (unit ->unit) ->unit

Writes abuffer into the stream:

write_stream stream buffer offset length binary fin ~close ~exn callback

write_stream calls one of its three callback functions, depending on what happens with the write:

  • ~close if the stream is closed before the write completes,
  • ~exn to report an exception during or before the write,
  • callback to report that the write has succeeded and the stream can accept another write.

binary andfin are for WebSockets only.binary marks the stream as containing binary (non-text) data, andfin sets theFIN bit, indicating the end of a message. These two parameters are ignored by non-WebSocket streams.

val flush_stream :stream ->  close:(int ->unit) ->  exn:(exn ->unit) ->  (unit ->unit) ->unit

Requests the stream be flushed. The callbacks have the same meaning as inwrite_stream.

val ping_stream :stream ->buffer ->int ->int ->  close:(int ->unit) ->  exn:(exn ->unit) ->  (unit ->unit) ->unit

Sends a ping frame on the WebSocket stream. The buffer is typically empty, but may contain up to 125 bytes of data.

val pong_stream :stream ->buffer ->int ->int ->  close:(int ->unit) ->  exn:(exn ->unit) ->  (unit ->unit) ->unit

Likeping_stream, but sends a pong event.

val close_stream :stream->int->unit

Closes the stream. The integer parameter is a WebSocket close code, and is ignored by non-WebSocket streams.

val abort_stream :stream->exn->unit

Aborts the stream, causing all readers and writers to receive the given exception.

WebSockets

type websocket

A WebSocket connection. SeeRFC 6455 andMDN.

val websocket :  ?headers:(string *string)list ->  ?close:bool ->    (websocket ->unitpromise) ->responsepromise

Creates a fresh101 Switching Protocols response. Once this response is returned to Dream's HTTP layer, the callback is passed a newwebsocket, and the application can begin using it. See examplek-websocket.

let my_handler = fun request ->  Dream.websocket (fun websocket ->    let%lwt () = Dream.send websocket "Hello, world!");

Dream.websocket automatically closes the WebSocket when the callback returns or raises an exception. Pass~close:false to suppress this behavior.

type text_or_binary = [ `Text | `Binary ]

Seesend andreceive_fragment.

type end_of_message = [ `End_of_message | `Continues ]

Seesend andreceive_fragment.

val send :  ?text_or_binary:[<text_or_binary ] ->  ?end_of_message:[<end_of_message ] ->websocket ->string ->unitpromise

Sends a single WebSocket message. The WebSocket is ready for another message when the promise resolves.

With~text_or_binary:`Text, the default, the message is interpreted as a UTF-8 string. The client will receive it transcoded to JavaScript's UTF-16 representation.

With~text_or_binary:`Binary, the message will be received unmodified, as either aBlob or anArrayBuffer. SeeMDN,WebSocket.binaryType.

~end_of_message is ignored for now, as the WebSocket library underlying Dream does not support sending message fragments yet.

val receive :websocket->stringoptionpromise

Receives a message. If the WebSocket is closed before a complete message arrives, the result isNone.

val receive_fragment :websocket ->    (string *text_or_binary *end_of_message)optionpromise

Receives a single fragment of a message, streaming it.

val close_websocket :  ?code:int ->websocket ->unitpromise

Closes the WebSocket.~code is usually not necessary, but is needed for some protocols based on WebSockets. SeeRFC 6455 §7.4.

JSON

Dream presently recommends usingYojson. See alsoppx_yojson_conv for generating JSON parsers and serializers for OCaml data types.

See examplee-json.

val origin_referrer_check :middleware

CSRF protection for AJAX requests. Either the method must be`GET or`HEAD, or:

  • Origin: orReferer: must be present, and
  • their value must matchHost:

Responds with400 Bad Request if the check fails. See examplee-json.

Implements theOWASPVerifying Origin With Standard Headers CSRF defense-in-depth technique, which is good enough for basic usage. Do not allow`GET or`HEAD requests to trigger important side effects if relying only onDream.origin_referrer_check.

Future extensions to this function may useX-Forwarded-Host or host whitelists.

For more thorough protection, generate CSRF tokens withDream.csrf_token, send them to the client (for instance, in<meta> tags of a single-page application), and require their presence in anX-CSRF-Token: header.

Forms

Dream.csrf_tag andDream.form round-trip secure forms.Dream.csrf_tag is used inside a form template to generate a hidden field with a CSRF token:

<form method="POST" action="/">  <%s! Dream.csrf_tag request %>  <input name="my.field"></form>

Dream.form recieves the form and checks the CSRF token:

match%lwt Dream.form request with| `Ok ["my.field", value] -> (* ... *)| _ -> Dream.empty `Bad_Request

See exampled-form.

type 'a form_result = [  | `Okof 'a  | `Expiredof 'a *float  | `Wrong_sessionof 'a  | `Invalid_tokenof 'a  | `Missing_tokenof 'a  | `Many_tokensof 'a  | `Wrong_content_type]

Form CSRF checking results, in order from least to most severe. SeeDream.form and exampled-form.

The first three constructors,`Ok,`Expired, and`Wrong_session can occur in regular usage.

The remaining constructors,`Invalid_token,`Missing_token,`Many_tokens,`Wrong_content_type correspond to bugs, suspicious activity, or tokens so old that decryption keys have since been rotated on the server.

val form :  ?csrf:bool ->request -> (string *string)listform_resultpromise

Parses the request body as a form. Performs CSRF checks. UseDream.csrf_tag in a form template to transparently generate forms that will pass these checks. SeeTemplates and exampled-form.

The call must be done under a session middleware, since each CSRF token is scoped to a session. SeeSessions.

CSRF token checking can be bypassed by passing~csrf:false.

The returned form fields are sorted in alphabetical order for reliable pattern matching. This is because browsers can transmit the form fields in a different order from how they appear in the HTML:

match%lwt Dream.form request with| `Ok ["email", email; "name", name] -> (* ... *)| _ -> Dream.empty `Bad_Request

To recover from conditions like expired forms, add extra cases:

match%lwt Dream.form request with| `Ok      ["email", email; "name", name] -> (* ... *)| `Expired (["email", email; "name", name], _) -> (* ... *)| _ -> Dream.empty `Bad_Request

It is recommended not to mutate state or send back sensitive data in the`Expired and`Wrong_session cases, as theymay indicate an attack against a client.

The remaining cases, including unexpected field sets and the remaining constructors ofDream.form_result, usually indicate either bugs or attacks. It's usually fine to respond to all of them with400 Bad Request.

Upload

type multipart_form =  (string * ((stringoption *string)list))list

Submitted file upload forms,<form enctype="multipart/form-data">. For example, if a form

<input name="files" type="file" multiple><input name="text">

is submitted with two files and a text value, it will be received byDream.multipart as

[  "files", [    Some "file1.ext", "file1-content";    Some "file2.ext", "file2-content";  ];  "text", [    None, "text-value"  ];]

See exampleg-upload andRFC 7578.

Note that clients such as curl can send files with no filename (None), though most browsers seem to insert at least an empty filename (Some ""). Don't use use the presence of a filename to determine if the field value is a file. Use the field name and knowledge about the form instead.

If a file field has zero files when submitted, browsers send"field-name", [Some ""; ""].Dream.multipart replaces this with"field-name", []. Use the advanced interface,Dream.upload, for the raw behavior.

Non-file fields always have one value, which might be the empty string.

SeeOWASPFile Upload Cheat Sheet for security precautions for upload forms.

val multipart :  ?csrf:bool ->request ->multipart_formform_resultpromise

LikeDream.form, but also reads files, andContent-Type: must bemultipart/form-data. The CSRF token can be generated in a template with

<form method="POST" action="/" enctype="multipart/form-data">  <%s! Dream.csrf_tag request %>

See sectionTemplates, and exampleg-upload.

Note that, likeDream.form, this function sorts form fields by field name.

Dream.multipart reads entire files into memory, so it is only suitable for prototyping, or with yet-to-be-added file size and count limits. SeeDream.upload below for a streaming version.

Streaming uploads

type part =stringoption *stringoption * ((string *string)list)

Upload form parts.

A valueSome (name, filename, headers) received byDream.upload begins apart in the stream. A part represents either a form field, or a single, complete file.

Note that, in the general case,filename andheaders are not reliable.name is the form field name.

val upload :request->partoptionpromise

Retrieves the next upload part.

Upon gettingSome (name, filename, headers) from this function, the user should callDream.upload_part to stream chunks of the part's data, until that function returnsNone. The user should then callDream.upload again.None fromDream.upload indicates that all parts have been received.

Dream.upload does not verify a CSRF token. There are several ways to add CSRF protection for an upload stream, including:

val upload_part :request->stringoptionpromise

Retrieves a part chunk.

CSRF tokens

It's usually not necessary to handle CSRF tokens directly.

CSRF functions are exposed for creating custom schemes, and for defense-in-depth purposes. SeeOWASPCross-Site Request Forgery Prevention Cheat Sheet.

type csrf_result = [  | `Ok  | `Expiredoffloat  | `Wrong_session  | `Invalid]

CSRF token verification outcomes.

`Expired and`Wrong_session can occur in normal usage, when a user's form or session expire, respectively. However, they can also indicate attacks, including stolen tokens, stolen tokens from other sessions, or attempts to use a token from an invalidated pre-session after login.

`Invalid indicates a token with a bad signature, a payload that was not generated by Dream, or other serious errors that cannot usually be triggered by normal users.`Invalid usually corresponds to bugs or attacks.`Invalid can also occur for very old tokens after old keys are no longer in use on the server.

val csrf_token :?valid_for:float->request->string

Returns a fresh CSRF token bound to the given request's and signed with the secret given toDream.set_secret.~valid_for is the token's lifetime, in seconds. The default value is one hour (3600.). Dream uses signed tokens that are not stored server-side.

val verify_csrf_token :request ->string ->csrf_resultpromise

Checks that the CSRF token is valid for therequest's session.

Templates

Dream includes a template preprocessor that allows interleaving OCaml and HTML in the same file:

let render message =  <html>    <body>      <p>The message is <b><%s message %></b>!</p>    </body>  </html>

See examples7-template andr-template.

There is also a typed alternative, provided by an external library,TyXML. It is shown in examplew-tyxml. If you are using Reason syntax, TyXML can be used withserver-side JSX. See exampler-tyxml.

To use the built-in templates, add this todune:

(rule (targets template.ml) (deps template.eml.ml) (action (run dream_eml %{deps} --workspace %{workspace_root})))

A template begins...

A%% line can also be used to set template options. The only option supported presently is%% response for streaming the template usingDream.write, to aresponse that is in scope. This is shown in examplesw-template-stream andr-template-stream.

A template ends...

Everything outside a template is ordinary OCaml code.

OCaml code can also be inserted into a template:

Thes in<%s code %> is actually aPrintf-style format specification. So, for example, one can print two hex digits using<%02X code %>.

<%s code %> automatically escapes the result ofcode usingDream.html_escape. This can be suppressed with!.<%s! code %> prints the result ofcode literally.Dream.html_escape is only safe for use in HTML text and quoted attribute values. It does not offer XSS protection in unquoted attribute values, CSS in<style> tags, or literal JavaScript in<script> tags.

val csrf_tag :request->string

Generates an<input> tag with a CSRF token, suitable for use withDream.form andDream.multipart. For example, in a template,

<form method="POST" action="/">  <%s! Dream.csrf_tag request %>  <input name="my.field"></form>

expands to

<form method="POST" action="/">  <input name="dream.csrf" type="hidden" value="j8vjZ6...">  <input name="my.field"></form>

It isrecommended to put the CSRF tag immediately after the starting<form> tag, to prevent certain kinds of DOM manipulation-based attacks.

Middleware

Interesting built-in middlewares are scattered throughout the various sections of these docs, according to where they are relevant. This section contains only generic middleware combinators.

val no_middleware :middleware

Does nothing but call its inner handler. Useful for disabling middleware conditionally during application startup:

if development then  my_middlewareelse  Dream.no_middleware
val livereload :middleware

Adds live reloading to your Dream application.

It works by injecting a script in the HTML pages sent to clients that will initiate a WebSocket.

When the server restarts, the WebSocket connection is lost, at which point, the client will try to reconnect every 500ms for 5s. If within these 5s the client is able to reconnect to the server, it will trigger a reload of the page.

val pipeline :middlewarelist->middleware

Combines a sequence of middlewares into one, such that these two lines are equivalent:

Dream.pipeline [middleware_1; middleware_2] @@ handler
               middleware_1 @@ middleware_2 @@ handler

Stream transformers

When writing a middleware that transforms a request body stream, useserver_stream to retrieve the server's view of the body stream. Create a new transformed stream (note: a function for doing this is not yet exposed), and replace the request's server stream by your transformed stream withset_server_stream.

When transforming a response stream, replace the client stream instead.

val client_stream :'amessage->stream

The stream that clients interact with.

val server_stream :'amessage->stream

The stream that servers interact with.

val set_client_stream :response->stream->unit

Replaces the stream that the client will use when it receives the response.

val set_server_stream :request->stream->unit

Replaces the stream that the server will use when it receives the request.

Routing

val router :routelist->handler

Creates a router. If none of the routes match the request, the router responds with404 Not Found. Route components starting with: are parameters, which can be retrieved withDream.param. See example3-router.

let () =  Dream.run  @@ Dream.router [    Dream.get "/echo/:word" @@ fun request ->      Dream.html (Dream.param request "word");  ]

Dream.scope is the main form of site composition. However, Dream also supports full subsites with**:

let () =  Dream.run  @@ Dream.router [    Dream.get "/static/**" @@ Dream.static "www/static";  ]

** causes the request's path to be trimmed by the route prefix, and the request's prefix to be extended by it. It is mainly useful for “mounting”Dream.static as a subsite.

It can also be used as an escape hatch to convert a handler, which may include its own router, into a subsite. However, it is better to compose sites with routes andDream.scope rather than opaque handlers and**, because, in the future, it may be possible to query routes for site structure metadata.

Note: routes that end with/ and routes that don't end with/ aredifferent.

val get     :string->handler->route

Forwards`GET requests for the given path to the handler.

Dream.get "/home" home_template
val post    :string->handler->route
val put     :string->handler->route
val delete  :string->handler->route
val head    :string->handler->route
val connect :string->handler->route
val options :string->handler->route
val trace   :string->handler->route
val patch   :string->handler->route

LikeDream.get, but for each of the othermethods.

val any     :string->handler->route

LikeDream.get, but does not check the method.

val param :request->string->string

Retrieves the path parameter. If it is missing,Dream.param raises an exception — the program is buggy.

val scope :string ->middlewarelist ->routelist ->route

Groups routes under a common path prefix and middlewares. Middlewares are run only if a route matches.

Dream.scope "/api" [Dream.origin_referrer_check] [  Dream.get  "/widget" get_widget_handler;  Dream.post "/widget" set_widget_handler;]

To prefix routes without applying any more middleware, use the empty list:

Dream.scope "/api" [] [  (* ...routes... *)]

To apply middleware without prefixing the routes, use"/":

Dream.scope "/" [Dream.origin_referrer_check] [  (* ...routes... *)]

Scopes can be nested.

val no_route :route

A dummy value of typeroute that is completely ignored by the router. Useful for disabling routes conditionally during application start:

Dream.router [  if development then    Dream.get "/graphiql" (Dream.graphiql "/graphql")  else    Dream.no_route;]

Static files

val static :  ?loader:(string ->string ->handler) ->string ->handler

Serves static files from a local directory. See examplef-static.

let () =  Dream.run  @@ Dream.router {    Dream.get "/static/**" @@ Dream.static "www/static";  }

Dream.static local_directory validates the path substituted for** by checking that it is (1) relative, (2) does not contain parent directory references (..), and (3) does not contain separators (/) within components. If these checks fail,Dream.static responds with404 Not Found.

If the checks succeed,Dream.static calls~loader local_directory path request, where

  • local_directory is the same directory that was passed toDream.static.
  • path is what was substituted for**.

The default loader isDream.from_filesystem. See examplew-one-binary for a loader that serves files from memory instead.

val from_filesystem :string->string->handler

Dream.from_filesystem local_directory path request responds with a file from the file system found atlocal_directory ^ "/" ^ path. If such a file does not exist, it responds with404 Not Found.

To serve single files likesitemap.xml from the file system, use routes like

Dream.get "/sitemap.xml" (Dream.from_filesystem "assets" "sitemap.xml")

Dream.from_filesystem callsDream.mime_lookup to guess aContent-Type: based on the file's extension.

val mime_lookup :string->(string *string)list

Returns aContent-Type: header based on the given filename. This is mostly a wrapper aroundmagic-mime. However, if the result istext/html,Dream.mime_lookup replaces it withtext/html; charset=utf-8, so as to matchDream.html.

Sessions

Dream's default sessions contain string-to-string dictionaries for application data. For example, a logged-in session might have

[  "user", "someone";  "lang", "ut-OP";]

Sessions also have three pieces of metadata:

There are several back ends, which decide where the sessions are stored:

All requests passing through session middleware are assigned a session, either an existing one, or a new empty session, known as apre-session.

When a session is at least half-expired, it is automatically refreshed by the next request that it is assigned to.

See exampleb-session.

val session_field :request->string->stringoption

Value from the request's session.

val set_session_field :request ->string ->string ->unitpromise

Mutates a value in the request's session. The back end may commit the value to storage immediately, so this function returns a promise.

val drop_session_field :request->string->unitpromise

Drops a field from the request's session.

val all_session_fields :request->(string *string)list

Full session dictionary.

val invalidate_session :request->unitpromise

Invalidates the request's session, replacing it with a fresh, empty pre-session.

Back ends

val memory_sessions :?lifetime:float->middleware

Stores sessions in server memory. Passes session IDs to clients in cookies. Session data is lost when the server process exits.

val cookie_sessions :?lifetime:float->middleware

Stores sessions in encrypted cookies. UseDream.set_secret to be able to decrypt cookies from previous server runs.

val sql_sessions :?lifetime:float->middleware

Stores sessions in an SQL database. Passes session IDs to clients in cookies. Must be used underDream.sql_pool. Expects a table

CREATE TABLE dream_session (  id TEXT PRIMARY KEY,  label TEXT NOT NULL,  expires_at REAL NOT NULL,  payload TEXT NOT NULL)

Metadata

val session_id :request->string

Secret value used to identify a client.

val session_label :request->string

Tracing label suitable for printing to logs.

val session_expires_at :request->float

Time at which the session will expire.

Flash messages

Flash messages are short strings which are stored in cookies during one request, to be made available for the next request. The typical use case is to provide form feedback across a redirect. See examplew-flash.

val flash :middleware

Implements storing flash messages in cookies.

val flash_messages :request->(string *string)list

The request's flash messages.

val add_flash_message :request->string->string->unit

Adds a flash message to the request.

GraphQL

Dream integratesocaml-graphql-server. See examples:

If you are alsowriting a client in a flavor of OCaml, considergraphql-ppx for generating GraphQL queries.

SeeOWASPGraphQL Cheat Sheet for an overview of security topics related to GraphQL.

val graphql :  (request -> 'apromise) ->  'aGraphql_lwt.Schema.schema ->handler

Dream.graphql make_context schema serves the GraphQLschema.

let () =  Dream.run  @@ Dream.router [    Dream.any "/graphql"  (Dream.graphql Lwt.return schema);    Dream.get "/graphiql" (Dream.graphiql "/graphql");  ]

make_context is called byDream.graphql on everyrequest to create thecontext, a value that is passed to each resolver from the schema. PassingLwt.return, the same as

fun request -> Lwt.return request

causes therequest itself to be used as the context:

field "name"  ~doc:"User name"  ~typ:(non_null string)  ~args:Arg.[]  ~resolve:(fun info user ->    (* The context is in info.ctx *)    user.name);
val graphiql :?default_query:string->string->handler

ServesGraphiQL, a GraphQL query editor. The string gives the GraphQL endpoint that the editor will work with.

~default_query sets the query that appears upon the first visit to the endpoint. It is empty by default. The string is pasted literally into the content of a JavaScript string, between its quotes, so it must be escaped manually.

Dream's build of GraphiQL is found in thesrc/graphiql directory. If you have the need, you can use it as the starting point for your own customized GraphiQL.

UseDream.no_route to disable GraphiQL conditionally outside of development.

SQL

Dream provides thin convenience functions overCaqti, an SQL interface with several back ends. See exampleh-sql.

Dream installs the corecaqti package, but you should also install at least one of:

They are separated because each has its own system library dependencies. Regardless of which you install, usage on the OCaml level is the same. The differences are in SQL syntax, and in external SQL server or file setup. See

For an introductory overview of database security, seeOWASPDatabase Security Cheat Sheet.

val sql_pool :?size:int->string->middleware

Makes an SQL connection pool available to its inner handler.?size is the maximum number of concurrent connections that the pool will support. The default value is picked by the driver. Note that for SQLite,?size is capped to1.

val sql :request -> (Caqti_lwt.connection -> 'apromise) ->    'apromise

Runs the callback with a connection from the SQL pool. See exampleh-sql.

let () =  Dream.run  @@ Dream.sql_pool "sqlite3:db.sqlite"  @@ fun request ->    Dream.sql request (fun db ->      (* ... *) |> Dream.html)

Logging

Dream uses theLogs library internally, and integrates with all other libraries in your project that are also using it. Dream provides a slightly simplified interface to Logs.

All log output is written tostderr.

SeeOWASPLogging Cheat Sheet for a survey of security topics related to logging.

val logger :middleware

Logs and times requests. Time spent logging is included. See example2-middleware.

val log :('a, Format.formatter, unit, unit) format4->'a

Formats a message and logs it. Disregard the obfuscated type: the first argument is a format string as described in the standard library modulesPrintf andFormat. The rest of the arguments are determined by the format string. See examplea-log.

Dream.log "Counter is now: %i" counter;Dream.log "Client: %s" (Dream.client request);
type ('a, 'b) conditional_log =  ((?request:request ->('a, Format.formatter, unit, 'b) format4 -> 'a) -> 'b) ->unit

Loggers. This type is difficult to read — instead, seeDream.error for usage.

type log_level = [ `Error | `Warning | `Info | `Debug ]

Log levels, in order from most urgent to least.

val error      :('aunit)conditional_log

Formats a message and writes it to the log at level`Error. The inner formatting function is called only if thecurrent log level is`Error or higher. See examplea-log.

Dream.error (fun log ->  log ~request "My message, details: %s" details);

Pass the optional argument~request toDream.error to associate the message with a specific request. If not passed,Dream.error will try to guess the request. This usually works, but not always.

val warning    :('aunit)conditional_log
val info       :('aunit)conditional_log
val debug      :('aunit)conditional_log

LikeDream.error, but for each of the otherlog levels.

type sub_log = {  error: 'a. ('a,unit)conditional_log;  warning: 'a. ('a,unit)conditional_log;  info: 'a. ('a,unit)conditional_log;  debug: 'a. ('a,unit)conditional_log;}

Sub-logs. SeeDream.sub_log right below.

val sub_log :?level:[<log_level ]->string->sub_log

Creates a new sub-log with the given name. For example,

let log = Dream.sub_log "myapp.ajax"

...creates a logger that can be used likeDream.error and the other default loggers, but prefixes"myapp.ajax" to each log message.

log.error (fun log -> log ~request "Validation failed")

?level sets the log level threshold for this sub-log only. If not provided, falls back to the global log level set byDream.initialize_log, unlessDream.set_log_level is used.

SeeREADME of examplea-log.

val initialize_log :?backtraces:bool ->  ?async_exception_hook:bool ->  ?level:[<log_level ] ->  ?enable:bool ->unit ->unit

Initializes Dream's log with the given settings.

Dream initializes its logging back end lazily. This is so that if a Dream Web app is linked into a larger binary, it does not affect that binary's runtime unless the Web app runs.

This also allows the Web app to give logging settings explicitly by callingDream.initialize_log early in program execution.

  • ~async_exception_hook:true, the default, causes Dream to setLwt.async_exception_hook so as to forward all asynchronous exceptions to the logger, and not terminate the process.
  • ~level sets the log level threshold for the entire binary. The default is`Info.
  • ~enable:false disables Dream logging completely. This can help sanitize output during testing.
val set_log_level :string->[<log_level ]->unit

Set the log level threshold of the given sub-log.

Errors

Dream passes all errors to a single error handler, including...

This allows customizing error handling in one place. Including low-level errors prevents leakage of strings in automatic responses not under the application's control, for full internationalization.

UseDream.error_template and pass the result toDream.run~error_handler to customize the error template.

The default error handler logs errors and its template generates completely empty responses, to avoid internationalization issues. In addition, this conforms to the recommendations inOWASPError Handling Cheat Sheet.

For full control over error handling, including logging, you can define anerror_handler directly.

val error_template :  (error ->string ->response ->responsepromise) ->error_handler

Builds anerror_handler from a template. See example9-error.

let my_error_handler =  Dream.error_template (fun _error debug_dump suggested_response ->    let body =      match debug_dump with      | Some string -> Dream.html_escape string      | None -> Dream.status_to_string (Dream.status suggested_response)    in    suggested_response    |> Dream.with_body body    |> Lwt.return)

The error's context suggests a response. Usually, its only valid field isDream.status.

  • If the error is an exception or rejection from the application, the status is usually500 Internal Server Error.
  • In case of a4xx or5xx response from the application, that response itself is passed to the template.
  • For low-level errors, the status is typically either400 Bad Request if the error was likely caused by the client, and500 Internal Server Error if the error was likely caused by the server.

~debug_dump is a multi-line string containing an error description, stack trace, request state, and other information.

When an error occurs in a context where a response is not possible, the template is not called. In some contexts where the template is called, the status code is hardcoded, but the headers and body from the template's response will still be used.

If the template itself raises an exception or rejects, an empty500 Internal Server Error will be sent in contexts that require a response.

type error = {  condition: [    | `Response ofresponse    | `String ofstring    | `Exn ofexn  ];  layer: [ `App | `HTTP | `HTTP2 | `TLS | `WebSocket ];  caused_by: [ `Server | `Client ];  request:requestoption;  response:responseoption;  client:stringoption;  severity:log_level;  will_send_response:bool;}

Detailed errors. Ignore this type if only usingDream.error_template.

  • condition describes the error itself.

    • `Response is a4xx or5xx response.
    • `String is an error that has only an English-language description.
    • `Exn is a caught exception.

    The default error handler logs`Exn and`Strings, but not`Response.`Response is assumed to be deliberate, and already logged byDream.logger.

  • layer is which part of the Dream stack detected the error.

    • `App is for application exceptions, rejections, and4xx,5xx responses.
    • `HTTP and`HTTP2 are for low-level HTTP protocol errors.
    • `TLS is for low-level TLS errors.
    • `WebSocket is for WebSocket errors.

    The default error handler uses this to just prepend a prefix to its log messages.

  • caused_by is the party likely to have caused the error.

    • `Server errors suggest bugs, and correspond to5xx responses.
    • `Client errors suggest user errors, network failure, buggy clients, and sometimes attacks. They correspond to4xx responses.
  • request is arequest associated with the error, if there is one.

    As examples, a request might not be available if the error is a failure to parse an HTTP/1.1 request at all, or failure to perform a TLS handshake.

    In case of a`WebSocket error, the request is the client's original request to establish the WebSocket connection.

  • response is aresponse that was either generated by the application, or suggested by the error context.

    In case of a`WebSocket error, the response is the application's original connection agreement response created byDream.websocket.

    SeeDream.error_template.

  • client is the client's address, if available. For example,127.0.0.1:56001.
  • Suggestedlog level for the error. Usually`Error for`Server errors and`Warning for client errors.
  • will_send_response istrue in error contexts where Dream will still send a response.

    The default handler calls the error template only ifwill_send_response istrue.

type error_handler =error->responseoptionpromise

Error handlers log errors and convert them into responses. Ignore if usingDream.error_template.

If the error haswill_send_response = true, the error handler must return a response. Otherwise, it should returnNone.

If an error handler raises an exception or rejects, Dream logs this secondary failure. If the error context needs a response, Dream responds with an empty500 Internal Server Error.

The behavior of Dream's default error handler is described atDream.error.

val debug_error_handler :error_handler

Anerror_handler for showing extra information about requests and exceptions, for use during development.

val catch :(error->responsepromise)->middleware

Forwards exceptions, rejections, and4xx,5xx responses from the application to the error handler. SeeErrors.

Servers

val run :?interface:string ->  ?port:int ->  ?stop:unitpromise ->  ?socket_path:string ->  ?error_handler:error_handler ->  ?tls:bool ->  ?certificate_file:string ->  ?key_file:string ->  ?builtins:bool ->  ?greeting:bool ->  ?adjust_terminal:bool ->handler ->unit

Runs the Web application represented by thehandler, by default athttp://localhost:8080.

This function callsLwt_main.run internally, so it is intended to be the main loop of a program.Dream.serve is a version that does not callLwt_main.run.

  • ~interface is the network interface to listen on. Defaults to"localhost". Use"0.0.0.0" to listen on all interfaces.
  • ~port is the port to listen on. Defaults to8080.
  • If~socket_path is specified, Dream will listen on a Unix domain socket at the given path, and ignore~interface and~port.
  • ~stop is a promise that causes the server to stop accepting new requests, andDream.run to return. Requests that have already entered the Web application continue to be processed. The default value is a promise that never resolves.
  • ~error_handler handles all errors, both from the application, and low-level errors. SeeErrors and example9-error.Dream.debug_error_handler is a default error handler that can be passed here to help debug Web apps. See example8-debug.
  • ~tls:true enables TLS. You should also specify~certificate_file and~key_file. However, for development, Dream includes an insecure compiled-inlocalhost certificate. Enabling HTTPS also enables transparent upgrading of connections to HTTP/2. See examplel-https.
  • ~certificate_file and~key_file specify the certificate and key file, respectively, when using~tls. They are not required for development, but are required for production. Dream will write a warning to the log if you are using~tls, don't provide~certificate_file and~key_file, and~interface is not"localhost".
  • ~builtins:false disablesBuilt-in middleware.

The remaining arguments can be used to gradually disable convenience features ofDream.run. Once both are disabled, you may want to switch to usingDream.serve.

  • ~greeting:false disables the start-up log message that prints a link to the Web application.
  • ~adjust_terminal:false disables adjusting the terminal to disable echo and line wrapping.
val serve :?interface:string ->  ?port:int ->  ?socket_path:string ->  ?stop:unitpromise ->  ?error_handler:error_handler ->  ?tls:bool ->  ?certificate_file:string ->  ?key_string:string ->  ?builtins:bool ->handler ->unitpromise

LikeDream.run, but returns a promise that does not resolve until the server stops listening, instead of callingLwt_main.run.

This function is meant for integrating Dream applications into larger programs that have their own procedures for starting and stopping the Web server.

All arguments have the same meanings as they have inDream.run.

Built-in middleware

Built-in middleware is Dream functionality that is implemented as middleware for maintainability reasons. It is necessary for Dream to work correctly. However, because it is middleware, Dream allows replacing it withDream.run~builtins:false. The middleware is applied in documented order, so

Dream.run my_app

is the same as

Dream.run ~builtins:false@@ Dream.catch ~error_handler@@ my_app

The middleware can be replaced with work-alikes, or omitted to use Dream as a fairly raw abstraction layer over low-level HTTP libraries.

val with_site_prefix :string->middleware

Removes the given prefix from the path in each request, and adds it to the request prefix. Responds with502 Bad Gateway if the path does not have the expected prefix.

This is for applications that are not running at the root (/) of their domain. The default is"/", for no prefix. Afterwith_site_prefix, routing is done relative to the prefix, and the prefix is also necessary for emitting secure cookies.

Web formats

val html_escape :string->string

Escapes a string so that it is suitable for use as text inside HTML elements and quoted attribute values. ImplementsOWASPCross-Site Scripting Prevention Cheat Sheet RULE #1.

This function isnot suitable for use with unquoted attributes, inline scripts, or inline CSS. SeeSecurity in example7-template.

val to_base64url :string->string

Converts the given string its base64url encoding, as specified inRFC 4648 §5, using a Web-safe alphabet and no padding. The resulting string can be used without escaping in URLs, form data, cookies, HTML content, attributes, and JavaScript code. For more options, see theBase64 library.

val from_base64url :string->stringoption

Inverse ofDream.to_base64url.

val to_percent_encoded :  ?international:bool ->string ->string

Percent-encodes a string for use inside a URL.

~international istrue by default, and causes non-ASCII bytes to be preserved. This is suitable for display to users, including in<a href=""> attributes, which are displayed in browser status lines. SeeRFC 3987.

Use~international:false for compatibility with legacy systems, or when constructing URL fragments from untrusted input that may not match the interface language(s) the user expects. In the latter case, similar letters from different writing scripts can be used to mislead users about the targets of links.

val from_percent_encoded :string->string

Inverse ofDream.to_percent_encoded.

val to_form_urlencoded :(string *string)list->string

Inverse ofDream.from_form_urlencoded. Percent-encodes names and values.

val from_form_urlencoded :string->(string *string)list

Converts form data or a query string fromapplication/x-www-form-urlencoded format to a list of name-value pairs. SeeRFC 1866 §8.2.1. Reverses the percent-encoding of names and values.

val from_cookie :string->(string *string)list

Converts aCookie: header value to key-value pairs. SeeRFC 6265bis §4.2.1. Does not apply any decoding to names and values.

val to_set_cookie :  ?expires:float ->  ?max_age:float ->  ?domain:string ->  ?path:string ->  ?secure:bool ->  ?http_only:bool ->  ?same_site:[ `Strict | `Lax | `None ] ->string ->string ->string

Dream.to_set_cookie name value formats aSet-Cookie: header value. The optional arguments correspond to the attributes specified inRFC 6265bis §5.3, and are documented atDream.set_cookie.

Does not apply any encoding to names and values. Be sure to encode so that names and values cannot contain `=`, `;`, or newline characters.

val split_target :string->string *string

Splits a request target into a path and a query string.

val from_path :string->stringlist

Splits the string into components on/ and percent-decodes each component. Empty components are dropped, except for the last. This function does not distinguish between absolute and relative paths, and is only meant for routes and request targets. So,

  • Dream.from_path "" becomes[].
  • Dream.from_path "/" becomes[""].
  • Dream.from_path "abc" becomes["abc"].
  • Dream.from_path "/abc" becomes["abc"].
  • Dream.from_path "abc/" becomes["abc"; ""].
  • Dream.from_path "a%2Fb" becomes["a/b"].
  • Dream.from_path "a//b" becomes["a"; "b"].

This function is not for use on full targets, because they may include query strings (?), andDream.from_path does not treat them specially. Split query strings off withDream.split_target first.

val to_path :  ?relative:bool ->  ?international:bool ->stringlist ->string

Percent-encodes a list of path components and joins them with/ into a path. Empty components, except for the last, are removed. The path is absolute by default. Use~relative:true for a relative path.Dream.to_path uses an IRI-friendly percent encoder, which preserves UTF-8 bytes in unencoded form. Use~international:false to percent-encode those bytes as well, for legacy protocols that require ASCII URLs.

val drop_trailing_slash :stringlist->stringlist

Changes the representation of pathabc/ to the representation ofabc by checking if the last element in the list is"", and, if it is, dropping it.

val text_html :string

The string"text/html; charset=utf-8" forContent-Type: headers.

val application_json :string

The string"application/json" forContent-Type: headers.

Cryptography

val set_secret :  ?old_secrets:stringlist ->string ->middleware

Sets a key to be used for cryptographic operations, such as signing CSRF tokens and encrypting cookies.

If this middleware is not used, a random secret is generated the first time a secret is needed. The random secret persists for the lifetime of the process. This is useful for quick testing and prototyping, but it means that restarts of the server will not be able to verify tokens or decrypt cookies generated by earlier runs, and multiple servers in a load-balancing arrangement will not accept each others' tokens and cookies.

For production, generate a 256-bit key with

Dream.to_base64url (Dream.random 32)

~old_secrets is a list of previous secrets that will not be used for encryption or signing, but will still be tried for decryption and verification. This is intended for key rotation. A medium-sized Web app serving 1000 fresh encrypted cookies per second should rotate keys about once a year.

val random :int->string

Generates the requested number of bytes using acryptographically secure random number generator.

val encrypt :  ?associated_data:string ->request ->string ->string

Signs and encrypts the string using the secret set byDream.set_secret.

~associated_data is included when computing the signature, but not included in the ciphertext. It can be used like a “salt,” to force ciphertexts from different contexts to be distinct, and dependent on the context.

For example, whenDream.set_cookie encrypts cookie values, it internally passes the cookie names in the associated data. This makes it impossible (or impractical) to use the ciphertext from one cookie as the value of another. The associated data will not match, and the value will be recognized as invalid.

The cipher presently used by Dream isAEAD_AES_256_GCM. It will be replaced byAEAD_AES_256_GCM_SIV as soon as the latter isavailable. The upgrade will be transparent, because Dream includes a cipher rotation scheme.

The cipher is suitable for encrypted transmissions and storing data other than credentials. For password or other credential storage, see packageargon2. SeeOWASPCryptographic Storage Cheat Sheet andOWASPPassword Storage Cheat Sheet.

val decrypt :  ?associated_data:string ->request ->string ->stringoption

ReversesDream.encrypt.

To support secret rotation, this function first tries to decrypt the string using the main secret set byDream.set_secret, and then each of the old secrets passed toDream.set_secret in~old_secrets.

Variables

Dream supports user-defined per-message variables for use by middlewares.

type'a field

Per-message variable.

val new_field :  ?name:string ->  ?show_value:('a ->string) ->unit -> 'afield

Declares a variable of type'a in all messages. The variable is initially unset in each message. The optional~name and~show_value are used byDream.run~debug to show the variable in debug dumps.

val field :'bmessage->'afield->'aoption

Retrieves the value of the per-message variable.

val set_field :'bmessage->'afield->'a->unit

Sets the per-message variable to the value.

Testing

val request :?method_:[<method_ ] ->  ?target:string ->  ?headers:(string *string)list ->string ->request

Dream.request body creates a fresh request with the given body for testing. The optional arguments set the correspondingrequest fields.

val test :?prefix:string->handler->request->response

Dream.test handler runs a handler the same way the HTTP server (Dream.run) would — assigning it a request id and noting the site root prefix, which is used by routers.Dream.test callsLwt_main.run internally to await the response, which is why the response returned from the test is not wrapped in a promise. If you don't need these facilities, you can testhandler by calling it directly with a request.

val sort_headers :  (string *string)list -> (string *string)list

Sorts headers by name. Headers with the same name are not sorted by value or otherwise reordered, because order is significant for some headers. SeeRFC 7230 §3.2.2 on header order. This function can help sanitize output before comparison.

val echo :handler

Responds with the request body.


[8]ページ先頭

©2009-2025 Movatter.jp