# std/asynchttpserver

Theme:

- Manual
- Standard library
- Index
- Compiler docs
- Fusion docs
- devel, stable

Search:
Source Edit

This module implements a high performance asynchronous HTTP server.

This HTTP server has not been designed to be used in production, but for testing applications locally. Because of this, when deploying your application in production you should use a reverse proxy (for example nginx) instead of allowing users to connect directly to this server.

Example: cmd: -r:off

    import std/asynchttpserver
    # This example will create an HTTP server on an automatically chosen port.
    # It will respond to all requests with a `200 OK` response code and "Hello World"
    # as the response body.
    import std/asyncdispatch
    proc main {.async.} =
      var server = newAsyncHttpServer()
      proc cb(req: Request) {.async.} =
        echo (req.reqMethod, req.url, req.headers)
        let headers = {"Content-type": "text/plain; charset=utf-8"}
        await req.respond(Http200, "Hello World", headers.newHttpHeaders())

      server.listen(Port(0)) # or Port(8080) to hardcode the standard HTTP port.
      let port = server.getPort
      echo "test this with: curl localhost:" & $port.uint16 & "/"
      while true:
        if server.shouldAcceptRequest():
          await server.acceptRequest(cb)
        else:
          # too many concurrent connections, `maxFDs` exceeded
          # wait 500ms for FDs to be closed
          await sleepAsync(500)

    waitFor main()

# Imports

asyncnet, asyncdispatch, parseutils, uri, strutils, httpcore, nativesockets, since, assertions

# Types

    AsyncHttpServer = ref object

  Source Edit

    Request = object
      client*: AsyncSocket
      reqMethod*: HttpMethod
      headers*: HttpHeaders
      protocol*: tuple[orig: string, major, minor: int]
      url*: Uri
      hostname*: string          ## The hostname of the client that made the request.
      body*: string

  Source Edit

# Consts

    nimMaxDescriptorsFallback {.intdefine.} = 16000

  fallback value for when `maxDescriptors` is not available. This can be set on the command line during compilation via `-d:nimMaxDescriptorsFallback=N` Source Edit

# Procs

    proc acceptRequest(server: AsyncHttpServer; callback: proc (request: Request): Future[
        void] {.closure, ...gcsafe.}): owned(Future[void]) {....stackTrace: false,
        raises: [Exception, ValueError, OSError, SslError, LibraryError, KeyError],
        tags: [RootEffect], forbids: [].}

  Accepts a single request. Write an explicit loop around this proc so that errors can be handled properly. Source Edit

    proc close(server: AsyncHttpServer) {....raises: [LibraryError, Exception, SslError],
                                          tags: [RootEffect], forbids: [].}

  Terminates the async http server instance. Source Edit

    proc getPort(self: AsyncHttpServer): Port {....raises: [OSError, Exception],
        tags: [], forbids: [].}

  Returns the port `self` was bound to.

  Useful for identifying what port `self` is bound to, if it was chosen automatically, for example via `listen(Port(0))`.

  Example:

      from std/nativesockets import Port
      let server = newAsyncHttpServer()
      server.listen(Port(0))
      assert server.getPort.uint16 > 0
      server.close()

  Source Edit

    proc listen(server: AsyncHttpServer; port: Port; address = ""; domain = AF_INET) {.
        ...raises: [OSError, ValueError], tags: [WriteIOEffect, ReadIOEffect],
        forbids: [].}

  Listen to the given port and address. Source Edit

    proc newAsyncHttpServer(reuseAddr = true; reusePort = false; maxBody = 8388608): AsyncHttpServer {.
        ...raises: [], tags: [], forbids: [].}

  Creates a new `AsyncHttpServer` instance. Source Edit

    proc respond(req: Request; code: HttpCode; content: string;
                 headers: HttpHeaders = nil): Future[void] {.
        ...raises: [Exception, SslError, ValueError], tags: [RootEffect], forbids: [].}

  Responds to the request with the specified `HttpCode`, headers and content.

  This procedure will not close the client socket.

  Example:

      import std/json
      proc handler(req: Request) {.async.} =
        if req.url.path == "/hello-world":
          let msg = %* {"message": "Hello World"}
          let headers = newHttpHeaders([("Content-Type","application/json")])
          await req.respond(Http200, $msg, headers)
        else:
          await req.respond(Http404, "Not Found")

  Source Edit

    proc sendHeaders(req: Request; headers: HttpHeaders): Future[void] {.
        ...raises: [Exception, SslError, ValueError], tags: [RootEffect], forbids: [].}

  Sends the specified headers to the requesting client. Source Edit

    proc serve(server: AsyncHttpServer; port: Port;
               callback: proc (request: Request): Future[void] {.closure, ...gcsafe.};
               address = ""; assumedDescriptorsPerRequest = -1; domain = AF_INET): owned(
        Future[void]) {....stackTrace: false, raises: [Exception, OSError, ValueError,
        SslError, LibraryError, KeyError], tags: [RootEffect, WriteIOEffect,
        ReadIOEffect, TimeEffect], forbids: [].}

  Starts the process of listening for incoming HTTP connections on the specified address and port.

  When a request is made by a client the specified callback will be called.

  If `assumedDescriptorsPerRequest` is 0 or greater the server cares about the process's maximum file descriptor limit. It then ensures that the process still has the resources for `assumedDescriptorsPerRequest` file descriptors before accepting a connection.

  You should prefer to call `acceptRequest` instead with a custom server loop so that you're in control over the error handling and logging.

  Source Edit

    proc shouldAcceptRequest(server: AsyncHttpServer;
                             assumedDescriptorsPerRequest = 5): bool {.inline,
        ...raises: [], tags: [], forbids: [].}

  Returns true if the process's current number of opened file descriptors is still within the maximum limit and so it's reasonable to accept yet another request. Source Edit

# Exports

Http305, Http101, HttpConnect, Http502, Http302, ==, Http205, HttpCode, Http307, Http426, Http226, Http400, HttpOptions, HttpPost, HttpVersion, $, Http416, Http300, Http403, Http429, Http405, Http510, Http203, Http413, Http103, Http410, Http421, Http423, headerLimit, Http505, Http415, Http409, Http200, contains, []=, Http425, Http407, Http304, HttpMethod, Http308, Http500, is2xx, Http303, Http402, HttpHeaders, del, Http206, Http428, Http508, clear, Http417, HttpHeaderValues, HttpHead, hasKey, Http100, toCaseInsensitive, Http501, Http204, Http201, add, Http301, pairs, Http503, Http401, HttpPut, is1xx, is4xx, Http202, ==, HttpGet, httpNewLine, Http422, contains, Http511, getOrDefault, Http506, $, Http406, newHttpHeaders, Http424, HttpDelete, HttpTrace, Http102, Http451, toString, Http207, newHttpHeaders, Http507, Http418, Http412, []=, len, is3xx, Http411, Http504, Http208, HttpPatch, Http404, Http431, Http408, is5xx, [], Http414, []