On this page
httpclient
This module implements a simple HTTP client that can be used to retrieve webpages and other data.
Retrieving a website
This example uses HTTP GET to retrieve http://google.com
:
import httpclient
var client = newHttpClient()
echo client.getContent("http://google.com")
The same action can also be performed asynchronously, simply use the AsyncHttpClient
:
import asyncdispatch, httpclient
proc asyncProc(): Future[string] {.async.} =
var client = newAsyncHttpClient()
return await client.getContent("http://example.com")
echo waitFor asyncProc()
The functionality implemented by HttpClient
and AsyncHttpClient
is the same, so you can use whichever one suits you best in the examples shown here.
Note: You need to run asynchronous examples in an async proc otherwise you will get an Undeclared identifier: 'await'
error.
Using HTTP POST
This example demonstrates the usage of the W3 HTML Validator, it uses multipart/form-data
as the Content-Type
to send the HTML to be validated to the server.
var client = newHttpClient()
var data = newMultipartData()
data["output"] = "soap12"
data["uploaded_file"] = ("test.html", "text/html",
"<html><head></head><body><p>test</p></body></html>")
echo client.postContent("http://validator.w3.org/check", multipart=data)
To stream files from disk when performing the request, use addFiles
.
Note: This will allocate a new Mimetypes
database every time you call it, you can pass your own via the mimeDb
parameter to avoid this.
let mimes = newMimetypes()
var client = newHttpClient()
var data = newMultipartData()
data.addFiles({"uploaded_file": "test.html"}, mimeDb = mimes)
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
import httpclient, json
let client = newHttpClient()
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
let body = %*{
"data": "some text"
}
let response = client.request("http://some.api", httpMethod = HttpPost, body = $body)
echo response.status
Progress reporting
You may specify a callback procedure to be called during an HTTP request. This callback will be executed every second with information about the progress of the HTTP request.
import asyncdispatch, httpclient
proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
echo("Downloaded ", progress, " of ", total)
echo("Current rate: ", speed div 1000, "kb/s")
proc asyncProc() {.async.} =
var client = newAsyncHttpClient()
client.onProgressChanged = onProgressChanged
discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
waitFor asyncProc()
If you would like to remove the callback simply set it to nil
.
client.onProgressChanged = nil
Warning: The total
reported by httpclient may be 0 in some cases.
SSL/TLS support
This requires the OpenSSL library, fortunately it's widely used and installed on many operating systems. httpclient will use SSL automatically if you give any of the functions a url with the https
schema, for example: https://github.com/
.
You will also have to compile with ssl
defined like so: nim c -d:ssl ...
.
Certificate validation is NOT performed by default. This will change in future.
A set of directories and files from the ssl_certs module are scanned to locate CA certificates.
See newContext to tweak or disable certificate validation.
Timeouts
Currently only the synchronous functions support a timeout. The timeout is measured in milliseconds, once it is set any call on a socket which may block will be susceptible to this timeout.
It may be surprising but the function as a whole can take longer than the specified timeout, only individual internal calls on the socket are affected. In practice this means that as long as the server is sending data an exception will not be raised, if however data does not reach the client within the specified timeout a TimeoutError
exception will be raised.
Here is how to set a timeout when creating an HttpClient
instance:
import httpclient
let client = newHttpClient(timeout = 42)
Proxy
A proxy can be specified as a param to any of the procedures defined in this module. To do this, use the newProxy
constructor. Unfortunately, only basic authentication is supported at the moment.
Some examples on how to configure a Proxy for HttpClient
:
import httpclient
let myProxy = newProxy("http://myproxy.network")
let client = newHttpClient(proxy = myProxy)
Get Proxy URL from environment variables:
import httpclient
var url = ""
try:
if existsEnv("http_proxy"):
url = getEnv("http_proxy")
elif existsEnv("https_proxy"):
url = getEnv("https_proxy")
except ValueError:
echo "Unable to parse proxy from environment variables."
let myProxy = newProxy(url = url)
let client = newHttpClient(proxy = myProxy)
Redirects
The maximum redirects can be set with the maxRedirects
of int
type, it specifies the maximum amount of redirects to follow, it defaults to 5
, you can set it to 0
to disable redirects.
Here you can see an example about how to set the maxRedirects
of HttpClient
:
import httpclient
let client = newHttpClient(maxRedirects = 0)
Imports
- since, net, strutils, uri, parseutils, base64, os, mimetypes, streams, math, random, httpcore, times, tables, streams, monotimes, asyncnet, asyncdispatch, asyncfile, nativesockets
Types
-
Response = ref object version*: string status*: string headers*: HttpHeaders body: string bodyStream*: Stream
- Source Edit
-
AsyncResponse = ref object version*: string status*: string headers*: HttpHeaders body: string bodyStream*: FutureStream[string]
- Source Edit
-
Proxy = ref object url*: Uri auth*: string
- Source Edit
-
MultipartEntries = openArray[tuple[name, content: string]]
- Source Edit
-
MultipartData = ref object content: seq[MultipartEntry]
- Source Edit
-
ProtocolError = object of IOError
- exception that is raised when server does not conform to the implemented protocol Source Edit
-
HttpRequestError = object of IOError
-
Thrown in the
getContent
proc andpostContent
proc, when the server returns an error Source Edit -
ProgressChangedProc[ReturnType] = proc (total, progress, speed: BiggestInt): ReturnType {...}{. closure, gcsafe.}
- Source Edit
-
HttpClientBase[SocketType] = ref object socket: SocketType connected: bool currentURL: Uri ## Where we are currently connected. headers*: HttpHeaders ## Headers to send in requests. maxRedirects: Natural ## Maximum redirects, set to ``0`` to disable. userAgent: string timeout*: int ## Only used for blocking HttpClient for now. proxy: Proxy ## ``nil`` or the callback to call when request progress changes. when SocketType is Socket: onProgressChanged else: onProgressChanged when false: sslContext contentTotal: BiggestInt contentProgress: BiggestInt oneSecondProgress: BiggestInt lastProgressReport: MonoTime when SocketType is AsyncSocket: bodyStream parseBodyFut else: bodyStream getBody: bool ## When `false`, the body is never read in requestAux.
- Source Edit
-
HttpClient = HttpClientBase[Socket]
- Source Edit
-
AsyncHttpClient = HttpClientBase[AsyncSocket]
- Source Edit
Consts
Procs
-
proc code(response: Response | AsyncResponse): HttpCode {...}{. raises: [ValueError, OverflowDefect].}
-
Retrieves the specified response's
HttpCode
.Raises a
Source EditValueError
if the response'sstatus
does not have a correspondingHttpCode
. -
proc contentType(response: Response | AsyncResponse): string {...}{.inline.}
-
Retrieves the specified response's content type.
This is effectively the value of the "Content-Type" header.
Source Edit -
proc contentLength(response: Response | AsyncResponse): int
-
Retrieves the specified response's content length.
This is effectively the value of the "Content-Length" header.
A
Source EditValueError
exception will be raised if the value is not an integer. -
proc lastModified(response: Response | AsyncResponse): DateTime
-
Retrieves the specified response's last modified time.
This is effectively the value of the "Last-Modified" header.
Raises a
Source EditValueError
if the parsing fails or the value is not a correctly formatted time. -
proc body(response: Response): string {...}{.raises: [IOError, OSError], tags: [ReadIOEffect].}
-
Retrieves the specified response's body.
The response's body stream is read synchronously.
Source Edit -
proc body(response: AsyncResponse): Future[string] {...}{. raises: [Exception, ValueError], tags: [RootEffect].}
- Reads the response's body and caches it. The read is performed only once. Source Edit
-
proc newProxy(url: string; auth = ""): Proxy {...}{.raises: [], tags: [].}
-
Constructs a new
TProxy
object. Source Edit -
proc newMultipartData(): MultipartData {...}{.inline, raises: [], tags: [].}
-
Constructs a new
MultipartData
object. Source Edit -
proc `$`(data: MultipartData): string {...}{.raises: [], tags: [].}
- convert MultipartData to string so it's human readable when echo see https://github.com/nim-lang/Nim/issues/11863 Source Edit
-
proc add(p: MultipartData; name, content: string; filename: string = ""; contentType: string = ""; useStream = true) {...}{.raises: [ValueError], tags: [].}
-
Add a value to the multipart data.
When
useStream
isfalse
, the file will be read into memory.Raises a
Source EditValueError
exception ifname
,filename
orcontentType
contain newline characters. -
proc add(p: MultipartData; xs: MultipartEntries): MultipartData {...}{.discardable, raises: [ValueError], tags: [].}
-
Add a list of multipart entries to the multipart data
p
. All values are added without a filename and without a content type.
Source Editdata.add({"action": "login", "format": "json"})
-
proc newMultipartData(xs: MultipartEntries): MultipartData {...}{. raises: [ValueError], tags: [].}
-
Create a new multipart data object and fill it with the entries
xs
directly.
Source Editvar data = newMultipartData({"action": "login", "format": "json"})
-
proc addFiles(p: MultipartData; xs: openArray[tuple[name, file: string]]; mimeDb = newMimetypes(); useStream = true): MultipartData {...}{. discardable, raises: [IOError, ValueError], tags: [ReadIOEffect].}
-
Add files to a multipart data object. The files will be streamed from disk when the request is being made. When
stream
isfalse
, the files are instead read into memory, but beware this is very memory ineffecient even for small files. The MIME types will automatically be determined. Raises anIOError
if the file cannot be opened or reading fails. To manually specify file content, filename and MIME type, use[]=
instead.
Source Editdata.addFiles({"uploaded_file": "public/test.html"})
-
proc `[]=`(p: MultipartData; name, content: string) {...}{.inline, raises: [ValueError], tags: [].}
-
Add a multipart entry to the multipart data
p
. The value is added without a filename and without a content type.
Source Editdata["username"] = "NimUser"
-
proc `[]=`(p: MultipartData; name: string; file: tuple[name, contentType, content: string]) {...}{.inline, raises: [ValueError], tags: [].}
-
Add a file to the multipart data
p
, specifying filename, contentType and content manually.
Source Editdata["uploaded_file"] = ("test.html", "text/html", "<html><head></head><body><p>test</p></body></html>")
-
proc newHttpClient(userAgent = defUserAgent; maxRedirects = 5; sslContext = getDefaultSSL(); proxy: Proxy = nil; timeout = -1; headers = newHttpHeaders()): HttpClient {...}{. raises: [], tags: [].}
-
Creates a new HttpClient instance.
userAgent
specifies the user agent that will be used when making requests.maxRedirects
specifies the maximum amount of redirects to follow, default is 5.sslContext
specifies the SSL context to use for HTTPS requests. See SSL/TLS supportproxy
specifies an HTTP proxy to use for this HTTP client's connections.timeout
specifies the number of milliseconds to allow before aTimeoutError
is raised.headers
specifies the HTTP Headers.Example:
Source Editimport asyncdispatch, httpclient, strutils proc asyncProc(): Future[string] {.async.} = var client = newAsyncHttpClient() return await client.getContent("http://example.com") let exampleHtml = waitFor asyncProc() assert "Example Domain" in exampleHtml assert not ("Pizza" in exampleHtml)
-
proc newAsyncHttpClient(userAgent = defUserAgent; maxRedirects = 5; sslContext = getDefaultSSL(); proxy: Proxy = nil; headers = newHttpHeaders()): AsyncHttpClient {...}{. raises: [], tags: [].}
-
Creates a new AsyncHttpClient instance.
userAgent
specifies the user agent that will be used when making requests.maxRedirects
specifies the maximum amount of redirects to follow, default is 5.sslContext
specifies the SSL context to use for HTTPS requests.proxy
specifies an HTTP proxy to use for this HTTP client's connections.
Source Editheaders
specifies the HTTP Headers. -
proc close(client: HttpClient | AsyncHttpClient)
- Closes any connections held by the HTTP client. Source Edit
-
proc getSocket(client: HttpClient): Socket {...}{.inline, raises: [], tags: [].}
-
Get network socket, useful if you want to find out more details about the connection
this example shows info about local and remote endpoints
Source Editif client.connected: echo client.getSocket.getLocalAddr echo client.getSocket.getPeerAddr
-
proc getSocket(client: AsyncHttpClient): AsyncSocket {...}{.inline, raises: [], tags: [].}
- Source Edit
-
proc request(client: AsyncHttpClient; url: string; httpMethod: string; body = ""; headers: HttpHeaders = nil; multipart: MultipartData = nil): Future[AsyncResponse] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
-
Connects to the hostname specified by the URL and performs a request using the custom method string specified by
httpMethod
.Connection will be kept alive. Further requests on the same
client
to the same hostname will not require a new connection to be made. The connection can be closed by using theclose
procedure.This procedure will follow redirects up to a maximum number of redirects specified in
client.maxRedirects
.You need to make sure that the
Source Editurl
doesn't contain any newline characters. Failing to do so will raiseAssertionDefect
. -
proc request(client: HttpClient; url: string; httpMethod: string; body = ""; headers: HttpHeaders = nil; multipart: MultipartData = nil): Response {...}{.raises: [ ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc request(client: AsyncHttpClient; url: string; httpMethod = HttpGet; body = ""; headers: HttpHeaders = nil; multipart: MultipartData = nil): Future[AsyncResponse] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
-
Connects to the hostname specified by the URL and performs a request using the method specified.
Connection will be kept alive. Further requests on the same
client
to the same hostname will not require a new connection to be made. The connection can be closed by using theclose
procedure.When a request is made to a different hostname, the current connection will be closed.
Source Edit -
proc request(client: HttpClient; url: string; httpMethod = HttpGet; body = ""; headers: HttpHeaders = nil; multipart: MultipartData = nil): Response {...}{.raises: [ ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc head(client: AsyncHttpClient; url: string): Future[AsyncResponse] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
-
Connects to the hostname specified by the URL and performs a HEAD request.
This procedure uses httpClient values such as
Source Editclient.maxRedirects
. -
proc head(client: HttpClient; url: string): Response {...}{.raises: [ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc get(client: AsyncHttpClient; url: string): Future[AsyncResponse] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
-
Connects to the hostname specified by the URL and performs a GET request.
This procedure uses httpClient values such as
Source Editclient.maxRedirects
. -
proc get(client: HttpClient; url: string): Response {...}{.raises: [ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc getContent(client: AsyncHttpClient; url: string): Future[string] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
- Connects to the hostname specified by the URL and returns the content of a GET request. Source Edit
-
proc getContent(client: HttpClient; url: string): string {...}{.raises: [ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc delete(client: AsyncHttpClient; url: string): Future[AsyncResponse] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
-
Connects to the hostname specified by the URL and performs a DELETE request. This procedure uses httpClient values such as
client.maxRedirects
. Source Edit -
proc delete(client: HttpClient; url: string): Response {...}{.raises: [ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc deleteContent(client: AsyncHttpClient; url: string): Future[string] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
- Connects to the hostname specified by the URL and returns the content of a DELETE request. Source Edit
-
proc deleteContent(client: HttpClient; url: string): string {...}{.raises: [ ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc post(client: AsyncHttpClient; url: string; body = ""; multipart: MultipartData = nil): Future[AsyncResponse] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
-
Connects to the hostname specified by the URL and performs a POST request. This procedure uses httpClient values such as
client.maxRedirects
. Source Edit -
proc post(client: HttpClient; url: string; body = ""; multipart: MultipartData = nil): Response {...}{.raises: [ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc postContent(client: AsyncHttpClient; url: string; body = ""; multipart: MultipartData = nil): Future[string] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
- Connects to the hostname specified by the URL and returns the content of a POST request. Source Edit
-
proc postContent(client: HttpClient; url: string; body = ""; multipart: MultipartData = nil): string {...}{.raises: [ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc put(client: AsyncHttpClient; url: string; body = ""; multipart: MultipartData = nil): Future[AsyncResponse] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
-
Connects to the hostname specified by the URL and performs a PUT request. This procedure uses httpClient values such as
client.maxRedirects
. Source Edit -
proc put(client: HttpClient; url: string; body = ""; multipart: MultipartData = nil): Response {...}{.raises: [ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc putContent(client: AsyncHttpClient; url: string; body = ""; multipart: MultipartData = nil): Future[string] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
- Connects to the hostname specified by the URL andreturns the content of a PUT request. Source Edit
-
proc putContent(client: HttpClient; url: string; body = ""; multipart: MultipartData = nil): string {...}{.raises: [ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc patch(client: AsyncHttpClient; url: string; body = ""; multipart: MultipartData = nil): Future[AsyncResponse] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
-
Connects to the hostname specified by the URL and performs a PATCH request. This procedure uses httpClient values such as
client.maxRedirects
. Source Edit -
proc patch(client: HttpClient; url: string; body = ""; multipart: MultipartData = nil): Response {...}{.raises: [ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc patchContent(client: AsyncHttpClient; url: string; body = ""; multipart: MultipartData = nil): Future[string] {...}{. raises: [Exception, ValueError], tags: [ReadIOEffect, RootEffect, TimeEffect].}
- Connects to the hostname specified by the URL and returns the content of a PATCH request. Source Edit
-
proc patchContent(client: HttpClient; url: string; body = ""; multipart: MultipartData = nil): string {...}{.raises: [ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
- Source Edit
-
proc downloadFile(client: HttpClient; url: string; filename: string) {...}{.raises: [ ValueError, IOError, OSError, HttpRequestError, Exception, LibraryError, SslError, TimeoutError, ProtocolError, KeyError], tags: [ReadIOEffect, RootEffect, WriteIOEffect, TimeEffect].}
-
Downloads
url
and saves it tofilename
. Source Edit -
proc downloadFile(client: AsyncHttpClient; url: string; filename: string): Future[ void] {...}{.raises: [Exception], tags: [ReadIOEffect, RootEffect, TimeEffect].}
- Source Edit
Exports
- Http417, Http503, Http431, ==, contains, Http304, Http406, ==, $, clear, Http408, $, Http411, is3xx, Http418, Http206, HttpMethod, Http101, httpNewLine, Http505, Http413, newHttpHeaders, Http200, []=, Http414, add, Http401, Http205, ==, Http407, Http500, Http404, Http416, Http308, Http302, HttpHeaders, Http300, Http428, Http410, is2xx, Http202, Http502, headerLimit, HttpHeaderValues, contains, newHttpHeaders, $, [], Http305, Http451, Http409, Http504, Http426, hasKey, del, pairs, Http429, HttpVersion, []=, Http421, Http307, Http301, is4xx, Http203, getOrDefault, Http100, Http501, len, Http400, Http403, is5xx, Http415, toString, Http412, Http405, Http303, Http204, Http201, HttpCode, Http422, []
© 2006–2021 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/httpclient.html