<commit_before># Rosencrantz

![shakespeare](https://raw.githubusercontent.com/andreaferretti/rosencrantz/master/shakespeare.jpg)

Rosencrantz is a DSL to write web servers, inspired by [Spray](http://spray.io/)
and its successor [Akka HTTP](http://doc.akka.io/docs/akka/2.4.2/scala/http/introduction.html).

It sits on top of [asynchttpserver](http://nim-lang.org/docs/asynchttpserver.html)
and provides a composable way to write HTTP handlers.

Table of contents
-----------------

<!-- TOC depthFrom:1 depthTo:6 withLinks:1 updateOnSave:1 orderedList:0 -->

- [Rosencrantz](#rosencrantz)
	- [Introduction](#introduction)
		- [Composing handlers](#composing-handlers)
		- [Starting a server](#starting-a-server)
	- [An example](#an-example)
	- [Basic handlers](#basic-handlers)
		- [Path handling](#path-handling)
		- [HTTP methods](#http-methods)
		- [Querystring extraction](#querystring-extraction)
		- [Working with headers](#working-with-headers)
		- [Failure containment](#failure-containment)
	- [JSON support](#json-support)
	- [Form handling support](#form-handling-support)
	- [Static file support](#static-file-support)
	- [CORS support](#cors-support)
	- [API stability](#api-stability)

<!-- /TOC -->

## Introduction

The core abstraction in Rosencrantz is the `Handler`, which is just an alias
for a `proc(req: ref Request, ctx: Context): Future[Context]`. Here `Request`
is the HTTP request from `asynchttpserver`, while `Context` is a place where
we accumulate information such as:

* what part of the path has been matched so far;
* what headers to emit with the response;
* whether the request has matched a route so far.

A handler usually does one or more of the following:

* filter the request, by returning `ctx.reject()` if some condition is not
  satisfied;
* accumulate some headers;
* actually respond to the request, by calling the `complete` function or one
  derived from it.

Rosencrantz provides many of those handlers, which are described below.

### Composing handlers

The nice thing about handlers is that they are composable. There are two ways
to compose two headers `h1` and `h2`:

* `h1 -> h2` (read `h1` **and** `h2`) returns a handler that passes the request
  through `h1` to update the context; then, if `h1` does not reject the request,
  it passes it, together with the new context, to `h2`. Think filtering first
  by HTTP method, then by path.
* `h1 ~ h2` (read `h1` **or** `h2`) returns a handler that passes the request
  through `h1`; if it rejects the request, it tries again with `h2`. Think
  matching on two alternative paths.

The combination `h1 -> h2` can also be written `h1[h2]`, which makes it nicer
when composing many handlers one inside each other.

### Starting a server

Once you have a handler, you can serve it using a server from `asynchttpserver`,
like this:

```nim
let server = newAsyncHttpServer()

waitFor server.serve(Port(8080), handler)
```

## An example

The following uses some of the predefined handlers and composes them together.
We write a small piece of a fictionary API to save and retrieve messages, and
we assume we have functions such as `getMessageById` that perform the actual
business logic. This should give a feel of how the DSL looks like:

```nim
let handler = get[
  path("/api/status")[
    ok(getStatus())
  ] ~
  pathChunk("/api/message")[
    accept("application/json")[
      intSegment(proc(id: int): auto =
        let message = getMessageById(id)
        ok(message)
      )
    ]
  ]
] ~ post [
  path("/api/new-message")[
    jsonBody(proc(msg: Message): auto =
      let
        id = generateId()
        saved = saveMessage(id, msg)
      if saved: ok(id)
      else: complete(Http500, "save failed")
    )
  ]
]
```

## Basic handlers

In order to work with Rosencrantz, you can `import rosencrantz`. If you prefer
a more fine-grained control, there are packages `rosencrantz/core` (which
contains the definitions common to all handlers), `rosencrantz/handlers` (for
the handlers we are about to show), and then more specialized handlers under
`rosencrantz/jsonsupport`, `rosencrantz/formsupport` and so on.

The simplest handlers are:

* `complete(code, body, headers)` that actually responds to the request. Here
  `code` is an instance of `HttpCode` from `asynchttpserver`, `body` is a
  `string` and `headers` are an instance of `StringTableRef`.
* `ok(body)`, which is a specialization of `complete` for a response of `200 Ok`
  with a content type of `text/plain`.
* `notFound(body)`, which is a specialization of `complete` for a response of
  `404 Not Found` with a content type of `text/plain`.
* `body(p)` extracts the body of the request. Here `p` is a
  `proc(s: string): Handler` which takes the extracted body as input and
  returns a handler.
* `logging(handler)` takes a handler and returns the same handler, but logs
  some information when a request passes through.

For instance, a simple handler that echoes back the body of the request would
look like

```nim
body(proc(s: string): auto =
  ok(s)
)
```

### Path handling

There are a few handlers to filter by path and extract path parameters:

* `path(s)` filters the requests where the path is equal to `s`.
* `pathChunk(s)` does the same but only for a prefix of the path. This means
  that one can nest more path handlers after it, unlike `path`, that matches
  and consumes the whole path.
* `pathEnd(p)` extracts whatever is not matched yet of the path and passes it
  to `p`. Here `p` is a `proc(s: string): Handler` that takes the final part of
  the path and returns a handler.
* `segment(p)`, that extracts a segment of path among two `/` signs. Here `p`
  is a `proc(s: string): Handler` that takes the matched segment and return a
  handler. This fails if the position is not just before a `/` sign.
* `intSegment(p)`, works the same as `segment`, but extracts and parses an
  integer number. It fails if the segment does not represent an integer. Here
  `p` is a `proc(s: int): Handler`.

For instance, to match and extract parameters out of a route like
`repeat/$msg/$n`, one would nest the above to get

```nim
pathChunk("/repeat")[
  segment(proc(msg: string): auto =
    intSegment(proc(n: int): auto =
      someHandler
    )
  )
]
```

### HTTP methods

To filter by HTTP method, one can use

* `verb(m)`, where `m` is a member of the `HttpMethod` enum defined in
  `rosencrantz/core`. There are corresponding specializations
* `get`, `post`, `put`, `delete`, `head`, `patch`, `options`, `trace` and
  `connect`

### Querystring extraction

TBD

### Working with headers

There are various handlers to read HTTP headers, filter requests by their
values, or accumulate HTTP headers for the response.

* `headers(h1, h2, ...)` adds headers for the response. Here each argument is
  a tuple of two strings, which are a key/value pair.
* `contentType(s)` is a specialization to emit the `Content-Type` header, so
  is is equivalent to `headers(("Content-Type", s))`.
* `readAllHeaders(p)` extract the headers as a string table. Here `p` is a
  `proc(hs: StringTableRef): Handler`.
* `readHeaders(s1, p)` extracts the value of the header with key `s1` and
  passes it to `p`, which is of type `proc(h1: string): Handler`. It rejects
  the request if the header `s1` is not defined. There are overloads
  `readHeaders(s1, s2, p)` and `readHeaders(s1, s2, s3, p)`, where `p` is a
  function of two arguments (resp. three arguments). To extract more than
  three headers, one can use `readAllHeaders` or nest `readHeaders` calls.
* `tryReadHeaders(s1, p)` works the same as `readHeaders`, but it does not
  reject the request if header `s` is missing; instead, `p` receives an empty
  string as default. Again, there are overloads for two and three arguments.
* `checkHeaders(h1, h2, ...)` filters the request for the header value. Here
  `h1` and the other are pairs of strings, representing a key and a value. If
  the request does not have the corresponding headers with these values, it
  will be rejected.
* `accept(mimetype)` is equivalent to `checkHeaders(("Accept", mimetype))`.
* `addDate()` returns a handler that adds the `Date` header, formatted as
  a GMT date in the [HTTP date format](https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html).<commit_msg>Added querystring docs<commit_after># Rosencrantz

![shakespeare](https://raw.githubusercontent.com/andreaferretti/rosencrantz/master/shakespeare.jpg)

Rosencrantz is a DSL to write web servers, inspired by [Spray](http://spray.io/)
and its successor [Akka HTTP](http://doc.akka.io/docs/akka/2.4.2/scala/http/introduction.html).

It sits on top of [asynchttpserver](http://nim-lang.org/docs/asynchttpserver.html)
and provides a composable way to write HTTP handlers.

Table of contents
-----------------

<!-- TOC depthFrom:1 depthTo:6 withLinks:1 updateOnSave:1 orderedList:0 -->

- [Rosencrantz](#rosencrantz)
	- [Introduction](#introduction)
		- [Composing handlers](#composing-handlers)
		- [Starting a server](#starting-a-server)
	- [An example](#an-example)
	- [Basic handlers](#basic-handlers)
		- [Path handling](#path-handling)
		- [HTTP methods](#http-methods)
		- [Querystring extraction](#querystring-extraction)
		- [Working with headers](#working-with-headers)
		- [Failure containment](#failure-containment)
	- [JSON support](#json-support)
	- [Form handling support](#form-handling-support)
	- [Static file support](#static-file-support)
	- [CORS support](#cors-support)
	- [API stability](#api-stability)

<!-- /TOC -->

## Introduction

The core abstraction in Rosencrantz is the `Handler`, which is just an alias
for a `proc(req: ref Request, ctx: Context): Future[Context]`. Here `Request`
is the HTTP request from `asynchttpserver`, while `Context` is a place where
we accumulate information such as:

* what part of the path has been matched so far;
* what headers to emit with the response;
* whether the request has matched a route so far.

A handler usually does one or more of the following:

* filter the request, by returning `ctx.reject()` if some condition is not
  satisfied;
* accumulate some headers;
* actually respond to the request, by calling the `complete` function or one
  derived from it.

Rosencrantz provides many of those handlers, which are described below.

### Composing handlers

The nice thing about handlers is that they are composable. There are two ways
to compose two headers `h1` and `h2`:

* `h1 -> h2` (read `h1` **and** `h2`) returns a handler that passes the request
  through `h1` to update the context; then, if `h1` does not reject the request,
  it passes it, together with the new context, to `h2`. Think filtering first
  by HTTP method, then by path.
* `h1 ~ h2` (read `h1` **or** `h2`) returns a handler that passes the request
  through `h1`; if it rejects the request, it tries again with `h2`. Think
  matching on two alternative paths.

The combination `h1 -> h2` can also be written `h1[h2]`, which makes it nicer
when composing many handlers one inside each other.

### Starting a server

Once you have a handler, you can serve it using a server from `asynchttpserver`,
like this:

```nim
let server = newAsyncHttpServer()

waitFor server.serve(Port(8080), handler)
```

## An example

The following uses some of the predefined handlers and composes them together.
We write a small piece of a fictionary API to save and retrieve messages, and
we assume we have functions such as `getMessageById` that perform the actual
business logic. This should give a feel of how the DSL looks like:

```nim
let handler = get[
  path("/api/status")[
    ok(getStatus())
  ] ~
  pathChunk("/api/message")[
    accept("application/json")[
      intSegment(proc(id: int): auto =
        let message = getMessageById(id)
        ok(message)
      )
    ]
  ]
] ~ post [
  path("/api/new-message")[
    jsonBody(proc(msg: Message): auto =
      let
        id = generateId()
        saved = saveMessage(id, msg)
      if saved: ok(id)
      else: complete(Http500, "save failed")
    )
  ]
]
```

## Basic handlers

In order to work with Rosencrantz, you can `import rosencrantz`. If you prefer
a more fine-grained control, there are packages `rosencrantz/core` (which
contains the definitions common to all handlers), `rosencrantz/handlers` (for
the handlers we are about to show), and then more specialized handlers under
`rosencrantz/jsonsupport`, `rosencrantz/formsupport` and so on.

The simplest handlers are:

* `complete(code, body, headers)` that actually responds to the request. Here
  `code` is an instance of `HttpCode` from `asynchttpserver`, `body` is a
  `string` and `headers` are an instance of `StringTableRef`.
* `ok(body)`, which is a specialization of `complete` for a response of `200 Ok`
  with a content type of `text/plain`.
* `notFound(body)`, which is a specialization of `complete` for a response of
  `404 Not Found` with a content type of `text/plain`.
* `body(p)` extracts the body of the request. Here `p` is a
  `proc(s: string): Handler` which takes the extracted body as input and
  returns a handler.
* `logging(handler)` takes a handler and returns the same handler, but logs
  some information when a request passes through.

For instance, a simple handler that echoes back the body of the request would
look like

```nim
body(proc(s: string): auto =
  ok(s)
)
```

### Path handling

There are a few handlers to filter by path and extract path parameters:

* `path(s)` filters the requests where the path is equal to `s`.
* `pathChunk(s)` does the same but only for a prefix of the path. This means
  that one can nest more path handlers after it, unlike `path`, that matches
  and consumes the whole path.
* `pathEnd(p)` extracts whatever is not matched yet of the path and passes it
  to `p`. Here `p` is a `proc(s: string): Handler` that takes the final part of
  the path and returns a handler.
* `segment(p)`, that extracts a segment of path among two `/` signs. Here `p`
  is a `proc(s: string): Handler` that takes the matched segment and return a
  handler. This fails if the position is not just before a `/` sign.
* `intSegment(p)`, works the same as `segment`, but extracts and parses an
  integer number. It fails if the segment does not represent an integer. Here
  `p` is a `proc(s: int): Handler`.

For instance, to match and extract parameters out of a route like
`repeat/$msg/$n`, one would nest the above to get

```nim
pathChunk("/repeat")[
  segment(proc(msg: string): auto =
    intSegment(proc(n: int): auto =
      someHandler
    )
  )
]
```

### HTTP methods

To filter by HTTP method, one can use

* `verb(m)`, where `m` is a member of the `HttpMethod` enum defined in
  `rosencrantz/core`. There are corresponding specializations
* `get`, `post`, `put`, `delete`, `head`, `patch`, `options`, `trace` and
  `connect`

### Querystring extraction

There are two handlers to extract the querystring from a request:

* `queryString(p)`, where `p` is a `proc(s: string): Handler` allows to generate
  a handler from the raw querystring (not parsed into parameters yet)
* `queryString(p)`, where `p` is a `proc(s: StringTableRef): Handler` allows to
	generate a handler from the querystring parameters, parsed as a string table.

### Working with headers

There are various handlers to read HTTP headers, filter requests by their
values, or accumulate HTTP headers for the response.

* `headers(h1, h2, ...)` adds headers for the response. Here each argument is
  a tuple of two strings, which are a key/value pair.
* `contentType(s)` is a specialization to emit the `Content-Type` header, so
  is is equivalent to `headers(("Content-Type", s))`.
* `readAllHeaders(p)` extract the headers as a string table. Here `p` is a
  `proc(hs: StringTableRef): Handler`.
* `readHeaders(s1, p)` extracts the value of the header with key `s1` and
  passes it to `p`, which is of type `proc(h1: string): Handler`. It rejects
  the request if the header `s1` is not defined. There are overloads
  `readHeaders(s1, s2, p)` and `readHeaders(s1, s2, s3, p)`, where `p` is a
  function of two arguments (resp. three arguments). To extract more than
  three headers, one can use `readAllHeaders` or nest `readHeaders` calls.
* `tryReadHeaders(s1, p)` works the same as `readHeaders`, but it does not
  reject the request if header `s` is missing; instead, `p` receives an empty
  string as default. Again, there are overloads for two and three arguments.
* `checkHeaders(h1, h2, ...)` filters the request for the header value. Here
  `h1` and the other are pairs of strings, representing a key and a value. If
  the request does not have the corresponding headers with these values, it
  will be rejected.
* `accept(mimetype)` is equivalent to `checkHeaders(("Accept", mimetype))`.
* `addDate()` returns a handler that adds the `Date` header, formatted as
  a GMT date in the [HTTP date format](https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html).
{'fraction_non_alphanumeric': 0.10887049083382613, 'fraction_numerical': 0.00768775872264932, 'mean_word_length': 4.096745027124774, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2667243', 'n_tokens_mistral': 5236, 'n_tokens_neox': 5011, 'n_words': 2235}