Merge pull request #4970 from nigredo-tori/fix-4969

Fix problems with persistent HTTP connections
This commit is contained in:
Dominik Picheta 2016-11-02 18:57:00 +01:00 • committed by GitHub
commit e784fdee45
2 changed files with 48 additions and 21 deletions

View file

@ -133,14 +133,21 @@ proc processClient(client: AsyncSocket, address: string,
assert client != nil assert client != nil
request.client = client request.client = client
# First line - GET /path HTTP/1.1 # We should skip at least one empty line before the request
# https://tools.ietf.org/html/rfc7230#section-3.5
for i in 0..1:
lineFut.mget().setLen(0) lineFut.mget().setLen(0)
lineFut.clean() lineFut.clean()
await client.recvLineInto(lineFut) # TODO: Timeouts. await client.recvLineInto(lineFut) # TODO: Timeouts.
if lineFut.mget == "": if lineFut.mget == "":
client.close() client.close()
return return
if lineFut.mget != "\c\L":
break
# First line - GET /path HTTP/1.1
var i = 0 var i = 0
for linePart in lineFut.mget.split(' '): for linePart in lineFut.mget.split(' '):
case i case i

View file

@ -117,7 +117,7 @@
## only basic authentication is supported at the moment. ## only basic authentication is supported at the moment.
import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes,
math, random, httpcore, times math, random, httpcore, times, tables
import asyncnet, asyncdispatch import asyncnet, asyncdispatch
import nativesockets import nativesockets
@ -989,8 +989,20 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
client.currentURL = url client.currentURL = url
client.connected = true client.connected = true
proc override(fallback, override: HttpHeaders): HttpHeaders =
# Right-biased map union for `HttpHeaders`
if override.isNil:
return fallback
result = newHttpHeaders()
# Copy by value
result.table[] = fallback.table[]
for k, vs in override.table:
result[k] = vs
proc request*(client: HttpClient | AsyncHttpClient, url: string, proc request*(client: HttpClient | AsyncHttpClient, url: string,
httpMethod: string, body = ""): Future[Response] {.multisync.} = httpMethod: string, body = "",
headers: HttpHeaders = nil): Future[Response] {.multisync.} =
## Connects to the hostname specified by the URL and performs a request ## Connects to the hostname specified by the URL and performs a request
## using the custom method string specified by ``httpMethod``. ## using the custom method string specified by ``httpMethod``.
## ##
@ -1023,13 +1035,15 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
else: else:
await newConnection(client, connectionUrl) await newConnection(client, connectionUrl)
if not client.headers.hasKey("user-agent") and client.userAgent != "": let effectiveHeaders = client.headers.override(headers)
client.headers["User-Agent"] = client.userAgent
var headers = generateHeaders(requestUrl, httpMethod, if not effectiveHeaders.hasKey("user-agent") and client.userAgent != "":
client.headers, body, client.proxy) effectiveHeaders["User-Agent"] = client.userAgent
await client.socket.send(headers) var headersString = generateHeaders(requestUrl, httpMethod,
effectiveHeaders, body, client.proxy)
await client.socket.send(headersString)
if body != "": if body != "":
await client.socket.send(body) await client.socket.send(body)
@ -1040,7 +1054,8 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
client.proxy = savedProxy client.proxy = savedProxy
proc request*(client: HttpClient | AsyncHttpClient, url: string, proc request*(client: HttpClient | AsyncHttpClient, url: string,
httpMethod = HttpGET, body = ""): Future[Response] {.multisync.} = httpMethod = HttpGET, body = "",
headers: HttpHeaders = nil): Future[Response] {.multisync.} =
## Connects to the hostname specified by the URL and performs a request ## Connects to the hostname specified by the URL and performs a request
## using the method specified. ## using the method specified.
## ##
@ -1050,7 +1065,8 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
## ##
## When a request is made to a different hostname, the current connection will ## When a request is made to a different hostname, the current connection will
## be closed. ## be closed.
result = await request(client, url, $httpMethod, body) result = await request(client, url, $httpMethod, body,
headers = headers)
proc get*(client: HttpClient | AsyncHttpClient, proc get*(client: HttpClient | AsyncHttpClient,
url: string): Future[Response] {.multisync.} = url: string): Future[Response] {.multisync.} =
@ -1097,18 +1113,22 @@ proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "",
else: else:
x x
var xb = mpBody.withNewLine() & body var xb = mpBody.withNewLine() & body
if multipart != nil:
client.headers["Content-Type"] = mpHeader.split(": ")[1]
client.headers["Content-Length"] = $len(xb)
result = await client.request(url, HttpPOST, xb) var headers = newHttpHeaders()
if multipart != nil:
headers["Content-Type"] = mpHeader.split(": ")[1]
headers["Content-Length"] = $len(xb)
result = await client.request(url, HttpPOST, xb,
headers = headers)
# Handle redirects. # Handle redirects.
var lastURL = url var lastURL = url
for i in 1..client.maxRedirects: for i in 1..client.maxRedirects:
if result.status.redirection(): if result.status.redirection():
let redirectTo = getNewLocation(lastURL, result.headers) let redirectTo = getNewLocation(lastURL, result.headers)
var meth = if result.status != "307": HttpGet else: HttpPost var meth = if result.status != "307": HttpGet else: HttpPost
result = await client.request(redirectTo, meth, xb) result = await client.request(redirectTo, meth, xb,
headers = headers)
lastURL = redirectTo lastURL = redirectTo
proc postContent*(client: HttpClient | AsyncHttpClient, url: string, proc postContent*(client: HttpClient | AsyncHttpClient, url: string,