diff options
Diffstat (limited to 'lib/pure/httpclient.nim')
-rw-r--r-- | lib/pure/httpclient.nim | 74 |
1 files changed, 55 insertions, 19 deletions
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 4404a9426..ba967b14f 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -18,14 +18,14 @@ ## ## .. code-block:: Nim ## var client = newHttpClient() -## echo(getContent("http://google.com")) +## echo client.getContent("http://google.com") ## ## The same action can also be performed asynchronously, simply use the ## ``AsyncHttpClient``: ## ## .. code-block:: Nim ## var client = newAsyncHttpClient() -## echo(await getContent("http://google.com")) +## echo await client.getContent("http://google.com") ## ## The functionality implemented by ``HttpClient`` and ``AsyncHttpClient`` ## is the same, so you can use whichever one suits you best in the examples @@ -50,6 +50,20 @@ ## ## echo client.postContent("http://validator.w3.org/check", multipart=data) ## +## You can also make post requests with custom headers. +## This example sets ``Content-Type`` to ``application/json`` +## and uses a json object for the body +## +## .. code-block:: Nim +## import httpclient, json +## +## let client = newHttpClient() +## client.headers = newHttpHeaders({ "Content-Type": "application/json" }) +## let body = %*{ +## "data": "some text" +## } +## echo client.request("http://some.api", httpMethod = HttpPost, body = $body) +## ## Progress reporting ## ================== ## @@ -103,7 +117,7 @@ ## only basic authentication is supported at the moment. import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, - math, random, httpcore, times + math, random, httpcore, times, tables import asyncnet, asyncdispatch import nativesockets @@ -432,7 +446,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "", # get the socket ready. If we are connecting through a proxy to SSL, - # send the appropiate CONNECT header. If not, simply connect to the proper + # send the appropriate CONNECT header. If not, simply connect to the proper # host (which may still be the proxy, for normal HTTP) if proxy != nil and hostUrl.scheme == "https": when defined(ssl): @@ -736,7 +750,7 @@ proc newHttpClient*(userAgent = defUserAgent, ## ``proxy`` specifies an HTTP proxy to use for this HTTP client's ## connections. ## - ## ``timeout`` specifies the number of miliseconds to allow before a + ## ``timeout`` specifies the number of milliseconds to allow before a ## ``TimeoutError`` is raised. new result result.headers = newHttpHeaders() @@ -944,8 +958,10 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, proc newConnection(client: HttpClient | AsyncHttpClient, url: Uri) {.multisync.} = if client.currentURL.hostname != url.hostname or - client.currentURL.scheme != url.scheme: - if client.connected: client.close() + client.currentURL.scheme != url.scheme or + client.currentURL.port != url.port: + if client.connected: + client.close() when client is HttpClient: client.socket = newSocket() @@ -973,8 +989,20 @@ proc newConnection(client: HttpClient | AsyncHttpClient, client.currentURL = url 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, - 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 ## using the custom method string specified by ``httpMethod``. ## @@ -1007,13 +1035,15 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, else: await newConnection(client, connectionUrl) - if not client.headers.hasKey("user-agent") and client.userAgent != "": - client.headers["User-Agent"] = client.userAgent + let effectiveHeaders = client.headers.override(headers) + + if not effectiveHeaders.hasKey("user-agent") and client.userAgent != "": + effectiveHeaders["User-Agent"] = client.userAgent - var headers = generateHeaders(requestUrl, httpMethod, - client.headers, body, client.proxy) + var headersString = generateHeaders(requestUrl, httpMethod, + effectiveHeaders, body, client.proxy) - await client.socket.send(headers) + await client.socket.send(headersString) if body != "": await client.socket.send(body) @@ -1024,7 +1054,8 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, client.proxy = savedProxy 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 ## using the method specified. ## @@ -1034,7 +1065,8 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, ## ## When a request is made to a different hostname, the current connection will ## be closed. - result = await request(client, url, $httpMethod, body) + result = await request(client, url, $httpMethod, body, + headers = headers) proc get*(client: HttpClient | AsyncHttpClient, url: string): Future[Response] {.multisync.} = @@ -1081,18 +1113,22 @@ proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", else: x var xb = mpBody.withNewLine() & body + + var headers = newHttpHeaders() if multipart != nil: - client.headers["Content-Type"] = mpHeader.split(": ")[1] - client.headers["Content-Length"] = $len(xb) + headers["Content-Type"] = mpHeader.split(": ")[1] + headers["Content-Length"] = $len(xb) - result = await client.request(url, HttpPOST, xb) + result = await client.request(url, HttpPOST, xb, + headers = headers) # Handle redirects. var lastURL = url for i in 1..client.maxRedirects: if result.status.redirection(): let redirectTo = getNewLocation(lastURL, result.headers) 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 proc postContent*(client: HttpClient | AsyncHttpClient, url: string, |