2929# # await req.respond(Http200, "Hello World")
3030# #
3131# # waitFor server.serve(Port(8080), cb)
32- # #
33- # # Basic Post request handle
34- # # =========================
35- # #
36- # # This example will create an HTTP server on port 8080. The server will
37- # # respond with a page with the actual and expected body length after
38- # # submitting a file.
39- # #
40- # # .. code-block::nim
41- # # import asynchttpserver, asyncdispatch
42- # # import strutils, strformat
43- # #
44- # # const stream = true # for test purposes switch from true to false
45- # #
46- # # proc htmlpage(contentLength, bodyLength: int): string =
47- # # return &"""
48- # # <!Doctype html>
49- # # <html lang="en">
50- # # <head><meta charset="utf-8"/></head>
51- # # <body>
52- # # <form action="/" method="post" enctype="multipart/form-data">
53- # # File: <input type="file" name="testfile" accept="text/*"><br />
54- # # <input style="margin:10px 0;" type="submit">
55- # # </form><br />
56- # # Expected Body Length: {contentLength} bytes<br />
57- # # Actual Body Length: {bodyLength} bytes
58- # # </body>
59- # # </html>
60- # # """
61- # #
62- # # proc cb(req: Request) {.async.} =
63- # # var
64- # # contentLength = 0
65- # # bodyLength = 0
66- # # if req.reqMethod == HttpPost:
67- # # contentLength = req.headers["Content-length"].parseInt
68- # # if stream:
69- # # # Read 8*1024 bytes at a time
70- # # # optional chunkSize parameter. The default is 8*1024
71- # # for length, data in req.bodyStream(8*1024):
72- # # let content = await data
73- # # if length == content.len:
74- # # bodyLength += content.len
75- # # else:
76- # # # Handle exception
77- # # await req.respond(Http400,
78- # # "Bad Request. Data read has a different length than the expected.")
79- # # return
80- # # else:
81- # # bodyLength += req.body.len
82- # # await req.respond(Http200, htmlpage(contentLength, bodyLength))
83- # #
84- # # let server = newAsyncHttpServer(maxBody = 10485760, stream = stream) # 10 MB
85- # # waitFor server.serve(Port(8080), cb)
8632
8733import tables, asyncnet, asyncdispatch, parseutils, uri, strutils
8834import httpcore
8935
9036export httpcore except parseHeader
9137
38+ const
39+ maxLine = 8 * 1024
40+
9241# TODO : If it turns out that the decisions that asynchttpserver makes
9342# explicitly, about whether to close the client sockets or upgrade them are
9443# wrong, then add a return value which determines what to do for the callback.
9544# Also, maybe move `client` out of `Request` object and into the args for
9645# the proc.
97-
98- const
99- maxLine = 8 * 1024
100-
101- when (NimMajor , NimMinor ) >= (1 , 1 ):
102- type
103- Request * = object
104- client* : AsyncSocket # TODO : Separate this into a Response object?
105- reqMethod* : HttpMethod
106- headers* : HttpHeaders
107- protocol* : tuple [orig: string , major, minor: int ]
108- url* : Uri
109- hostname* : string # # The hostname of the client that made the request.
110- body* : string
111- contentLength* : int
112-
113- type
114- AsyncHttpServer * = ref object
115- socket: AsyncSocket
116- reuseAddr: bool
117- reusePort: bool
118- maxBody: int # # The maximum content-length that will be read for the body.
119- stream: bool # # By default (stream = false), the body of the request is read immediately
120- else :
121- type
122- Request * = object
123- client* : AsyncSocket # TODO : Separate this into a Response object?
124- reqMethod* : HttpMethod
125- headers* : HttpHeaders
126- protocol* : tuple [orig: string , major, minor: int ]
127- url* : Uri
128- hostname* : string # # The hostname of the client that made the request.
129- body* : string
130-
131- type
132- AsyncHttpServer * = ref object
133- socket: AsyncSocket
134- reuseAddr: bool
135- reusePort: bool
136- maxBody: int # # The maximum content-length that will be read for the body.
137-
138- when (NimMajor , NimMinor ) >= (1 , 1 ):
139- proc newAsyncHttpServer * (reuseAddr = true , reusePort = false ,
140- maxBody = 8388608 , stream = false ): AsyncHttpServer =
141- # # Creates a new ``AsyncHttpServer`` instance.
142- new result
143- result .reuseAddr = reuseAddr
144- result .reusePort = reusePort
145- result .maxBody = maxBody
146- result .stream = stream
147- else :
148- proc newAsyncHttpServer * (reuseAddr = true , reusePort = false ,
46+ type
47+ Request * = object
48+ client* : AsyncSocket # TODO : Separate this into a Response object?
49+ reqMethod* : HttpMethod
50+ headers* : HttpHeaders
51+ protocol* : tuple [orig: string , major, minor: int ]
52+ url* : Uri
53+ hostname* : string # # The hostname of the client that made the request.
54+ body* : string
55+
56+ AsyncHttpServer * = ref object
57+ socket: AsyncSocket
58+ reuseAddr: bool
59+ reusePort: bool
60+ maxBody: int # # The maximum content-length that will be read for the body.
61+
62+ proc newAsyncHttpServer * (reuseAddr = true , reusePort = false ,
14963 maxBody = 8388608 ): AsyncHttpServer =
150- # # Creates a new ``AsyncHttpServer`` instance.
151- new result
152- result .reuseAddr = reuseAddr
153- result .reusePort = reusePort
154- result .maxBody = maxBody
64+ # # Creates a new ``AsyncHttpServer`` instance.
65+ new result
66+ result .reuseAddr = reuseAddr
67+ result .reusePort = reusePort
68+ result .maxBody = maxBody
15569
15670proc addHeaders (msg: var string , headers: HttpHeaders ) =
15771 for k, v in headers:
@@ -214,30 +128,13 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
214128proc sendStatus (client: AsyncSocket , status: string ): Future [void ] =
215129 client.send (" HTTP/1.1 " & status & " \c\L\c\L " )
216130
217- when (NimMajor , NimMinor ) >= (1 , 1 ):
218- iterator bodyStream * (
219- request: Request ,
220- chunkSize: int = 8 * 1024 ): (int , Future [string ]) =
221- # # The chunkSize parameter is optional and default value is 8*1024 bytes.
222- # #
223- # # This iterator return a tuple with the length of the data that was read
224- # # and a future.
225- var remainder = request.contentLength
226- while remainder > 0 :
227- let readSize = min (remainder, chunkSize)
228- let data = request.client.recv (readSize)
229- if data.failed:
230- raise newException (ValueError , " Error reading POST data from client." )
231- yield (readSize, data)
232- remainder -= readSize
233-
234131proc processRequest (
235132 server: AsyncHttpServer ,
236133 req: FutureVar [Request ],
237134 client: AsyncSocket ,
238135 address: string ,
239136 lineFut: FutureVar [string ],
240- callback: proc (request: Request ): Future [void ] {.closure , gcsafe .}
137+ callback: proc (request: Request ): Future [void ] {.closure , gcsafe .},
241138): Future [bool ] {.async .} =
242139
243140 # Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
@@ -248,12 +145,10 @@ proc processRequest(
248145 # Header: val
249146 # \n
250147 request.headers.clear ()
148+ request.body = " "
251149 request.hostname.shallowCopy (address)
252150 assert client != nil
253151 request.client = client
254- request.body = " "
255- when (NimMajor , NimMinor ) >= (1 , 1 ):
256- request.contentLength = 0
257152
258153 # We should skip at least one empty line before the request
259154 # https://tools.ietf.org/html/rfc7230#section-3.5
@@ -348,19 +243,10 @@ proc processRequest(
348243 if contentLength > server.maxBody:
349244 await request.respondError (Http413 )
350245 return false
351-
352- when (NimMajor , NimMinor ) >= (1 , 1 ):
353- request.contentLength = contentLength
354- if not server.stream:
355- request.body = await client.recv (contentLength)
356- if request.body.len != contentLength:
357- await request.respond (Http400 , " Bad Request. Content-Length does not match actual." )
358- return true
359- else :
360- request.body = await client.recv (contentLength)
361- if request.body.len != contentLength:
362- await request.respond (Http400 , " Bad Request. Content-Length does not match actual." )
363- return true
246+ request.body = await client.recv (contentLength)
247+ if request.body.len != contentLength:
248+ await request.respond (Http400 , " Bad Request. Content-Length does not match actual." )
249+ return true
364250 elif request.reqMethod == HttpPost :
365251 await request.respond (Http411 , " Content-Length required." )
366252 return true
0 commit comments