Merge pull request #4970 from nigredo-tori/fix-4969
Fix problems with persistent HTTP connections
This commit is contained in:
commit
e784fdee45
2 changed files with 48 additions and 21 deletions
|
|
@ -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
|
||||||
lineFut.mget().setLen(0)
|
# https://tools.ietf.org/html/rfc7230#section-3.5
|
||||||
lineFut.clean()
|
for i in 0..1:
|
||||||
await client.recvLineInto(lineFut) # TODO: Timeouts.
|
lineFut.mget().setLen(0)
|
||||||
if lineFut.mget == "":
|
lineFut.clean()
|
||||||
client.close()
|
await client.recvLineInto(lineFut) # TODO: Timeouts.
|
||||||
return
|
|
||||||
|
|
||||||
|
if lineFut.mget == "":
|
||||||
|
client.close()
|
||||||
|
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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue