Server for cases when request body must be processed as a stream (for example, when client is uploading a large dataset to server), or response must sent as a stream (for example, when client is downloading a large dataset from server.)
Example
import guildenstern/[dispatcher, streamingserver] proc handleUpload() = let ok = "ok" if not startReceiveMultipart(giveupSecs = 2): (reply(Http400); return) while true: let (state , chunk) = receiveMultipart() case state of Fail: break of TryAgain: continue of Progress: echo chunk of Complete: (reply(Http200, ok); break) shutdown() proc onRequest() = let html = """<!doctype html><title>StreamCtx</title><body> <form action="/upload" method="post" enctype="multipart/form-data" accept-charset="utf-8"> <input type="file" id="file" name="file"> <input type="submit">""" if startsUri("/upload"): handleUpload() else: reply(Http200, html) let server = newStreamingServer(onRequest) server.start(5050) joinThread(server.thread)
Types
StreamingContext = ref object of HttpContext
Procs
proc continueDownload(chunk: string): bool {....raises: [], tags: [], forbids: [].}
proc finishDownload(): bool {.discardable, ...raises: [], tags: [], forbids: [].}
proc hasData(): bool {....raises: [], tags: [], forbids: [].}
- While this is true, there are more multipart chunks to receive
proc isStreamingContext(): bool {....raises: [], tags: [], forbids: [].}
proc newStreamingServer(onrequestcallback: proc () {....gcsafe, nimcall, ...raises: [].}; loglevel = LogLevel.WARN): HttpServer {....raises: [], tags: [], forbids: [].}
proc receiveMultipart(): (SocketState, string) {....gcsafe, raises: [], tags: [], forbids: [].}
- Tries to receive a chunk from a stream started with startReceiveMultipart. Returns both the socket's state and the possibly received chunk.
proc startDownload(code: HttpCode = Http200; headers: openArray[string] = []; waitMs = 100; giveupSecs = 10): bool {....raises: [], tags: [], forbids: [].}
-
Starts replying http response as Transfer-encoding: chunked. Allows sending large datasets in multiple parts so that main memory is not exhausted. Also supports sending dynamic data, where Content-length header cannot be set.
waitMs is time to sleep if continueDownload is waiting for more data to read. giveupSecs is total time for replying before socket is closed. Continue response with calls to continueDownload. End response with finishDownload.
See examples/streamingdownloadtest.nim for a concrete example.
proc startReceiveMultipart(waitMs = 100; giveupSecs = 10): bool {....gcsafe, raises: [], tags: [], forbids: [].}
-
Starts receiving a multipart/form-data http request as chunks. This is how browsers deliver file uploads to server, see example above.
waitMs is time to sleep before receiveMultipart returns TryAgain. giveupSecs is total time for receiving before socket is closed.
Returns false if content-type header is not multipart/form-data.
Iterators
iterator receiveInChunks(): (SocketState, string) {....gcsafe, raises: [], tags: [], 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 POST data without worries about main memory usage. See examples/streamingposttest.nim for a concrete working example of how to use this iterator.
Templates
template stream(): untyped
- Casts the socketcontext thread local variable into a StreamingContext
Exports
-
shortdivider, closeSocket, reply, shutdown, reply, server, isUri, http, reply, replyStart, parseAllHeaders, SocketCloseCause, newHttpServer, longdivider, reply, isHttpContext, reply, getRequest, LogLevel, GuildenSternVersion, SocketState, replyFinish, getMethod, log, OnCloseSocketCallback, replyMore, startsUri, replyStart, reply, parseHeaders, socketcontext, reply, LogCallback, HttpServer, SocketContext, reply, SocketData, getUri, replyMore, getBody, suspend, isMethod, HttpContext, closeOtherSocket, GuildenServer, getBodylen, isBody, shuttingdown