HTTP server, with three operating modes
see examples/httptest.nim, examples/streamingposttest.nim, and examples/replychunkedtest.nim for examples.
Types
ContentType = enum NoBody, ## offers slightly faster handling for requests like GET that do not have a body Compact, ## the default mode. Whole request body must fit into the request string (size defined with [bufferlength] parameter), from where it can then be accessed with [getRequest], [isBody] and [getBody] procs Streaming ## read the body yourself with the [receiveStream] iterator
- mode of the server
HttpContext = ref object of SocketContext request*: string requestlen*: int uristart*: int urilen*: int methlen*: int bodystart*: int contentlength*: int64 contentreceived*: int64 contentdelivered*: int64 headers*: StringTableRef
HttpServer = ref object of GuildenServer contenttype*: ContentType maxheaderlength* = 10000 ## Maximum allowed size for http header part. bufferlength* = 100000 ## Every thread will reserve this much memory, for buffering the incoming request. Must be larger than maxheaderlength. sockettimeoutms* = 5000 ## If socket is unresponsive for longer, it will be closed. requestCallback*: proc () {....gcsafe, nimcall, ...raises: [].} parserequestline*: bool ## If you don't need uri or method, but need max perf, set this to false headerfields*: seq[string] ## list of header fields to be parsed
SocketState = enum Fail = -1, TryAgain = 0, Progress = 1, Complete = 2
Lets
longdivider = "\r\n\r\n"
shortdivider = "\r\n"
Consts
MSG_DONTWAIT = 64'i32
MSG_MORE = 32768'i32
Procs
proc checkSocketState(ret: int): SocketState {....raises: [], tags: [RootEffect], forbids: [].}
proc getBody(): string {....raises: [], tags: [RootEffect], forbids: [].}
- Returns the body as a string copy. When --experimental:views compiler switch is used, there is also getBodyview proc that does not take a copy.
proc getBodylen(): int {....raises: [], tags: [], forbids: [].}
proc getContentLength(): bool {....raises: [], tags: [RootEffect], forbids: [].}
proc getMethod(): string {....raises: [], tags: [], forbids: [].}
- Returns the method as a string copy
proc getRequest(): string {....raises: [], tags: [], forbids: [].}
proc getUri(): string {....raises: [], tags: [], forbids: [].}
- Returns the uri as a string copy
proc handleHttpThreadInitialization(gserver: GuildenServer) {....raises: [], tags: [RootEffect], forbids: [].}
proc initHttpServer(s: HttpServer; loglevel: LogLevel; parserequestline: bool; contenttype: ContentType; headerfields: openArray[string]) {. ...raises: [], tags: [], forbids: [].}
proc isBody(body: string): bool {....raises: [], tags: [RootEffect], forbids: [].}
- Compares the body without making a string copy
proc isHeaderreceived(previouslen, currentlen: int): bool {....raises: [], tags: [], forbids: [].}
proc isHttpContext(): bool {....raises: [], tags: [], forbids: [].}
proc isMethod(amethod: string): bool {....raises: [], tags: [], forbids: [].}
- Compares method without making a string copy
proc isUri(uri: string): bool {....raises: [], tags: [], forbids: [].}
- Compares the uri without making a string copy
proc newHttpServer(onrequestcallback: proc () {....gcsafe, nimcall, ...raises: [].}; loglevel = LogLevel.WARN; parserequestline = true; contenttype = Compact; headerfields: openArray[string] = []): HttpServer {. ...raises: [], tags: [RootEffect], forbids: [].}
-
Constructs a new http server. The essential thing here is to set the onrequestcallback proc. When it is triggered, the http thread-local socket context is accessible.
If you want to tinker with maxheaderlength, bufferlength or sockettimeoutms, that is best done after the server is constructed but before it is started.
proc parseMethod(): bool {....raises: [], tags: [RootEffect], forbids: [].}
proc parseRequestLine(): bool {....gcsafe, raises: [], tags: [RootEffect], forbids: [].}
proc prepareHttpContext(socketdata: ptr SocketData) {.inline, ...raises: [], tags: [], forbids: [].}
proc readHeader(): bool {....gcsafe, raises: [], tags: [RootEffect], forbids: [].}
proc replyContinueChunked(chunk: string): bool {....raises: [], tags: [RootEffect], forbids: [].}
proc replyFinish(): SocketState {.discardable, inline, ...gcsafe, raises: [], tags: [RootEffect], forbids: [].}
proc replyFinishChunked(): bool {.discardable, ...raises: [], tags: [RootEffect], forbids: [].}
proc replyMore(bodypart: ptr string; start: int; partlength: int = -1): ( SocketState, int) {.inline, ...gcsafe, raises: [], tags: [RootEffect], forbids: [].}
- Continuation to replyStart.
proc replyStart(code: HttpCode; contentlength: int; headers: openArray[string]): SocketState {. inline, ...gcsafe, raises: [], tags: [RootEffect], forbids: [].}
proc replyStart(code: HttpCode; contentlength: int; headers: ptr string = nil): SocketState {. inline, ...gcsafe, raises: [], tags: [RootEffect], forbids: [].}
- Start replying to a request (continue with replyMore and replyFinish). If you do not know the content-length yet, use replyStartChunked instead.
proc replyStartChunked(code: HttpCode = Http200; headers: openArray[string] = []): bool {. ...raises: [], tags: [RootEffect], forbids: [].}
-
Starts replying http response as Transfer-encoding: chunked. Mainly for sending dynamic data, where Content-length header cannot be set.
Continue response with calls to replyContinueChunked.
End response with replyContinueChunked.
See examples/replychunkedtest.nim for a concrete example.
proc startsUri(uristart: string): bool {....raises: [], tags: [], forbids: [].}
- Compares the beginning of the uri without making a string copy
Iterators
iterator receiveStream(): (SocketState, string) {....gcsafe, raises: [], tags: [RootEffect], forbids: [].}
- Receives a http request in chunks, yielding the state of operation and a possibly received new chuck on every iteration. With this, you can receive data incrementally without worries about main memory usage. See examples/streamingposttest.nim for a concrete working example of how to use this iterator.
Exports
-
SocketData, closeSocket, shutdown, $, CloseSocketCallback, initialize, shutdownevent, closeOtherSocket, ThreadInitializerCallback, initializeThread, handleRead, LogLevel, GuildenSternVersion, OnCloseSocketCallback, CloseOtherSocketCallback, GuildenServer, SocketContext, suspend, log, HandlerCallback, SocketCloseCause, SuspendCallback, LogCallback, shuttingdown, socketcontext