Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transfer-Encoding:chunked tests #16678

Merged
merged 16 commits into from
Jan 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions lib/pure/asynchttpserver.nim
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ runnableExamples:

import asyncnet, asyncdispatch, parseutils, uri, strutils
import httpcore
import std/private/since

export httpcore except parseHeader

Expand Down Expand Up @@ -71,6 +72,22 @@ type
maxBody: int ## The maximum content-length that will be read for the body.
maxFDs: int

func getSocket*(a: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} =
## Returns the ``AsyncHttpServer``s internal ``AsyncSocket`` instance.
##
## Useful for identifying what port the AsyncHttpServer is bound to, if it
## was chosen automatically.
runnableExamples:
from asyncdispatch import Port
from asyncnet import getFd
from nativesockets import getLocalAddr, AF_INET
let server = newAsyncHttpServer()
server.listen(Port(0)) # Socket is not bound until this point
let port = getLocalAddr(server.getSocket.getFd, AF_INET)[1]
doAssert uint16(port) > 0
server.close()
a.socket

proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
maxBody = 8388608): AsyncHttpServer =
## Creates a new ``AsyncHttpServer`` instance.
Expand Down Expand Up @@ -300,9 +317,13 @@ proc processRequest(
break

# Read bytesToRead and add to body
# Note we add +2 because the line must be terminated by \r\n
let chunk = await client.recv(bytesToRead + 2)
request.body = request.body & chunk
let chunk = await client.recv(bytesToRead)
request.body.add(chunk)
# Skip \r\n (chunk terminating bytes per spec)
let separator = await client.recv(2)
if separator != "\r\n":
await request.respond(Http400, "Bad Request. Encoding separator must be \\r\\n")
return true

inc sizeOrData
elif request.reqMethod == HttpPost:
Expand Down
81 changes: 81 additions & 0 deletions tests/stdlib/tasynchttpserver_transferencoding.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import httpclient, asynchttpserver, asyncdispatch, asyncfutures
import net

import std/asyncnet
import std/nativesockets

const postBegin = """
POST / HTTP/1.1
Transfer-Encoding:chunked

"""

template genTest(input, expected) =
var sanity = false
proc handler(request: Request) {.async.} =
doAssert(request.body == expected)
doAssert(request.headers.hasKey("Transfer-Encoding"))
doAssert(not request.headers.hasKey("Content-Length"))
sanity = true
await request.respond(Http200, "Good")

proc runSleepLoop(server: AsyncHttpServer) {.async.} =
server.listen(Port(0))
proc wrapper() =
waitFor server.acceptRequest(handler)
asyncdispatch.callSoon wrapper

let server = newAsyncHttpServer()
waitFor runSleepLoop(server)
let port = getLocalAddr(server.getSocket.getFd, AF_INET)[1]
let data = postBegin & input
var socket = newSocket()
socket.connect("127.0.0.1", port)
socket.send(data)
waitFor sleepAsync(10)
socket.close()
server.close()

# Verify we ran the handler and its asserts
doAssert(sanity)

block:
const expected = "hello=world"
const input = ("b\r\n" &
"hello=world\r\n" &
"0\r\n" &
"\r\n")
genTest(input, expected)
block:
const expected = "hello encoding"
const input = ("e\r\n" &
"hello encoding\r\n" &
"0\r\n" &
"\r\n")
genTest(input, expected)
block:
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding
const expected = "MozillaDeveloperNetwork"
const input = ("7\r\n" &
"Mozilla\r\n" &
"9\r\n" &
"Developer\r\n" &
"7\r\n" &
"Network\r\n" &
"0\r\n" &
"\r\n")
genTest(input, expected)
block:
# https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
const expected = "Wikipedia in \r\n\r\nchunks."
const input = ("4\r\n" &
"Wiki\r\n" &
"6\r\n" &
"pedia \r\n" &
"E\r\n" &
"in \r\n" &
"\r\n" &
"chunks.\r\n" &
"0\r\n" &
"\r\n")
genTest(input, expected)