29
29
# # await req.respond(Http200, "Hello World")
30
30
# #
31
31
# # 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)
86
32
87
33
import tables, asyncnet, asyncdispatch, parseutils, uri, strutils
88
34
import httpcore
89
35
90
36
export httpcore except parseHeader
91
37
38
+ const
39
+ maxLine = 8 * 1024
40
+
92
41
# TODO : If it turns out that the decisions that asynchttpserver makes
93
42
# explicitly, about whether to close the client sockets or upgrade them are
94
43
# wrong, then add a return value which determines what to do for the callback.
95
44
# Also, maybe move `client` out of `Request` object and into the args for
96
45
# 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 ,
149
63
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
155
69
156
70
proc addHeaders (msg: var string , headers: HttpHeaders ) =
157
71
for k, v in headers:
@@ -214,30 +128,13 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
214
128
proc sendStatus (client: AsyncSocket , status: string ): Future [void ] =
215
129
client.send (" HTTP/1.1 " & status & " \c\L\c\L " )
216
130
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
-
234
131
proc processRequest (
235
132
server: AsyncHttpServer ,
236
133
req: FutureVar [Request ],
237
134
client: AsyncSocket ,
238
135
address: string ,
239
136
lineFut: FutureVar [string ],
240
- callback: proc (request: Request ): Future [void ] {.closure , gcsafe .}
137
+ callback: proc (request: Request ): Future [void ] {.closure , gcsafe .},
241
138
): Future [bool ] {.async .} =
242
139
243
140
# Alias `request` to `req.mget()` so we don't have to write `mget` everywhere.
@@ -248,12 +145,10 @@ proc processRequest(
248
145
# Header: val
249
146
# \n
250
147
request.headers.clear ()
148
+ request.body = " "
251
149
request.hostname.shallowCopy (address)
252
150
assert client != nil
253
151
request.client = client
254
- request.body = " "
255
- when (NimMajor , NimMinor ) >= (1 , 1 ):
256
- request.contentLength = 0
257
152
258
153
# We should skip at least one empty line before the request
259
154
# https://tools.ietf.org/html/rfc7230#section-3.5
@@ -348,19 +243,10 @@ proc processRequest(
348
243
if contentLength > server.maxBody:
349
244
await request.respondError (Http413 )
350
245
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
364
250
elif request.reqMethod == HttpPost :
365
251
await request.respond (Http411 , " Content-Length required." )
366
252
return true
0 commit comments