This module implements a simple HTTP client that can be used to retrieve webpages/other data.
Note: This module is not ideal, connection is not kept alive so sites with many redirects are expensive. As such in the future this module may change, and the current procedures will be deprecated.
Retrieving a website
This example uses HTTP GET to retrieve http://google.com
echo(getContent("http://google.com"))
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 the server.
var data = newMultipartData() data["output"] = "soap12" data["uploaded_file"] = ("test.html", "text/html", "<html><head></head><body><p>test</p></body></html>") echo postContent("http://validator.w3.org/check", multipart=data)
Asynchronous HTTP requests
You simply have to create a new instance of the AsyncHttpClient object. You may then use await on the functions defined for that object. Keep in mind that the following code needs to be inside an asynchronous procedure.
var client = newAsyncHttpClient() var resp = await client.request("http://google.com")
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 also have to compile with ssl defined like so: nim c -d:ssl ....
Timeouts
Currently all functions support an optional timeout, by default the timeout is set to -1 which means that the function will never time out. The timeout is measured in milliseconds, once it is set any call on a socket which may block will be susceptible to this timeout, however please remember that 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 client within the specified timeout an ETimeout exception will then be raised.
Proxy
A proxy can be specified as a param to any of these procedures, the newProxy constructor should be used for this purpose. However, currently only basic authentication is supported.
Imports
-
net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, math, random, httpcore, asyncnet, asyncdispatch, nativesockets
Types
Response = tuple[version: string, status: string, headers: HttpHeaders, body: string]
- Source
Proxy = ref object url*: Uri auth*: string
- Source
MultipartEntries = openArray[tuple[name, content: string]]
- Source
MultipartData = ref object content: seq[string]
- Source
ProtocolError = object of IOError
- exception that is raised when server does not conform to the implemented protocol Source
HttpRequestError = object of IOError
- Thrown in the getContent proc and postContent proc, when the server returns an error Source
HttpMethod = enum httpHEAD, ## Asks for the response identical to the one that would ## correspond to a GET request, but without the response ## body. httpGET, ## Retrieves the specified resource. httpPOST, ## Submits data to be processed to the identified ## resource. The data is included in the body of the ## request. httpPUT, ## Uploads a representation of the specified resource. httpDELETE, ## Deletes the specified resource. httpTRACE, ## Echoes back the received request, so that a client ## can see what intermediate servers are adding or ## changing in the request. httpOPTIONS, ## Returns the HTTP methods that the server supports ## for specified address. httpCONNECT ## Converts the request connection to a transparent ## TCP/IP tunnel, usually used for proxies.
- the requested HttpMethod Source
AsyncHttpClient = ref object socket: AsyncSocket connected: bool currentURL: Uri headers*: StringTableRef maxRedirects: int userAgent: string when defined(ssl): sslContext: net.SslContext
- Where we are currently connected. Source
Procs
proc newProxy(url: string; auth = ""): Proxy {.raises: [ValueError], tags: [].}
- Constructs a new TProxy object. Source
proc newMultipartData(): MultipartData {.raises: [], tags: [].}
- Constructs a new MultipartData object. Source
proc add(p: var MultipartData; name, content: string; filename: string = nil; contentType: string = nil) {.raises: [ValueError], tags: [].}
- Add a value to the multipart data. Raises a ValueError exception if name, filename or contentType contain newline characters. Source
proc add(p: var 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.
data.add({"action": "login", "format": "json"})
Source proc newMultipartData(xs: MultipartEntries): MultipartData {.raises: [ValueError], tags: [].}
-
Create a new multipart data object and fill it with the entries xs directly.
var data = newMultipartData({"action": "login", "format": "json"})
Source proc addFiles(p: var MultipartData; xs: openArray[tuple[name, file: string]]): MultipartData {. discardable, raises: [ValueError, IOError], tags: [ReadIOEffect].}
-
Add files to a multipart data object. The file will be opened from your disk, read and sent with the automatically determined MIME type. Raises an IOError if the file cannot be opened or reading fails. To manually specify file content, filename and MIME type, use []= instead.
data.addFiles({"uploaded_file": "public/test.html"})
Source proc `[]=`(p: var MultipartData; name, content: string) {.raises: [ValueError], tags: [].}
-
Add a multipart entry to the multipart data p. The value is added without a filename and without a content type.
data["username"] = "NimUser"
Source proc `[]=`(p: var MultipartData; name: string; file: tuple[name, contentType, content: string]) {.raises: [ValueError], tags: [].}
-
Add a file to the multipart data p, specifying filename, contentType and content manually.
data["uploaded_file"] = ("test.html", "text/html", "<html><head></head><body><p>test</p></body></html>")
Source proc request(url: string; httpMethod: string; extraHeaders = ""; body = ""; sslContext = defaultSSLContext; timeout = - 1; userAgent = defUserAgent; proxy: Proxy = nil): Response {.raises: [ValueError, OSError, HttpRequestError, OverflowError, SslError, TimeoutError, ProtocolError, KeyError, Exception], tags: [ReadIOEffect, WriteIOEffect, TimeEffect].}
-
Requests url with the custom method string specified by the
httpMethod parameter.
Extra headers can be specified and must be separated by \c\L
An optional timeout can be specified in milliseconds, if reading from theserver takes longer than specified an ETimeout exception will be raised.
Source proc request(url: string; httpMethod = httpGET; extraHeaders = ""; body = ""; sslContext = defaultSSLContext; timeout = - 1; userAgent = defUserAgent; proxy: Proxy = nil): Response {.raises: [ValueError, OSError, HttpRequestError, OverflowError, SslError, TimeoutError, ProtocolError, KeyError, Exception], tags: [ReadIOEffect, WriteIOEffect, TimeEffect].}
-
Requests url with the specified httpMethod.
Extra headers can be specified and must be separated by \c\L
An optional timeout can be specified in milliseconds, if reading from theserver takes longer than specified an ETimeout exception will be raised.
Source proc get(url: string; extraHeaders = ""; maxRedirects = 5; sslContext: SSLContext = defaultSSLContext; timeout = - 1; userAgent = defUserAgent; proxy: Proxy = nil): Response {.raises: [ValueError, OSError, HttpRequestError, OverflowError, SslError, TimeoutError, ProtocolError, KeyError, Exception], tags: [ReadIOEffect, WriteIOEffect, TimeEffect].}
-
GETs the url and returns a Response object
This proc also handles redirection
Extra headers can be specified and must be separated by \c\L.
An optional timeout can be specified in milliseconds, if reading from theserver takes longer than specified an ETimeout exception will be raised.
Source proc getContent(url: string; extraHeaders = ""; maxRedirects = 5; sslContext: SSLContext = defaultSSLContext; timeout = - 1; userAgent = defUserAgent; proxy: Proxy = nil): string {.raises: [ ValueError, OSError, HttpRequestError, OverflowError, SslError, TimeoutError, ProtocolError, KeyError, Exception], tags: [ReadIOEffect, WriteIOEffect, TimeEffect].}
-
GETs the body and returns it as a string.
Raises exceptions for the status codes 4xx and 5xx
Extra headers can be specified and must be separated by \c\L.
An optional timeout can be specified in milliseconds, if reading from theserver takes longer than specified an ETimeout exception will be raised.
Source proc post(url: string; extraHeaders = ""; body = ""; maxRedirects = 5; sslContext: SSLContext = defaultSSLContext; timeout = - 1; userAgent = defUserAgent; proxy: Proxy = nil; multipart: MultipartData = nil): Response {.raises: [ ValueError, OSError, HttpRequestError, OverflowError, SslError, TimeoutError, ProtocolError, KeyError, Exception], tags: [ReadIOEffect, WriteIOEffect, TimeEffect].}
-
POSTs body to the url and returns a Response object.
This proc adds the necessary Content-Length header.
This proc also handles redirection.
Extra headers can be specified and must be separated by \c\L.
An optional timeout can be specified in milliseconds, if reading from theserver takes longer than specified an ETimeout exception will be raised. | The optional multipart parameter can be used to create multipart/form-data POSTs comfortably.
Source proc postContent(url: string; extraHeaders = ""; body = ""; maxRedirects = 5; sslContext: SSLContext = defaultSSLContext; timeout = - 1; userAgent = defUserAgent; proxy: Proxy = nil; multipart: MultipartData = nil): string {.raises: [ValueError, OSError, HttpRequestError, OverflowError, SslError, TimeoutError, ProtocolError, KeyError, Exception], tags: [ReadIOEffect, WriteIOEffect, TimeEffect].}
-
POSTs body to url and returns the response's body as a string
Raises exceptions for the status codes 4xx and 5xx
Extra headers can be specified and must be separated by \c\L.
An optional timeout can be specified in milliseconds, if reading from theserver takes longer than specified an ETimeout exception will be raised. | The optional multipart parameter can be used to create multipart/form-data POSTs comfortably.
Source proc downloadFile(url: string; outputFilename: string; sslContext: SSLContext = defaultSSLContext; timeout = - 1; userAgent = defUserAgent; proxy: Proxy = nil) {.raises: [IOError, ValueError, OSError, HttpRequestError, OverflowError, SslError, TimeoutError, ProtocolError, KeyError, Exception], tags: [WriteIOEffect, ReadIOEffect, TimeEffect].}
-
Downloads url and saves it to outputFilename
An optional timeout can be specified in milliseconds, if reading from theserver takes longer than specified an ETimeout exception will be raised.
Source proc newAsyncHttpClient(userAgent = defUserAgent; maxRedirects = 5; sslContext = defaultSslContext): 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.
Source proc close(client: AsyncHttpClient) {.raises: [SslError, OSError], tags: [].}
- Closes any connections held by the HTTP client. Source
proc request(client: AsyncHttpClient; url: string; httpMethod: string; body = ""): Future[ Response] {.raises: [FutureError], tags: [RootEffect].}
-
Connects to the hostname specified by the URL and performs a request using the custom method string specified by httpMethod.
Connection will 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 the close procedure.
The returned future will complete once the request is completed.
Source proc request(client: AsyncHttpClient; url: string; httpMethod = httpGET; body = ""): Future[ Response] {.raises: [FutureError], tags: [RootEffect].}
-
Connects to the hostname specified by the URL and performs a request using the method specified.
Connection will 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 the close procedure.
The returned future will complete once the request is completed.
Source proc get(client: AsyncHttpClient; url: string): Future[Response] {. raises: [FutureError], tags: [RootEffect].}
-
Connects to the hostname specified by the URL and performs a GET request.
This procedure will follow redirects up to a maximum number of redirects specified in newAsyncHttpClient.
Source proc post(client: AsyncHttpClient; url: string; body = ""; multipart: MultipartData = nil): Future[Response] {.raises: [FutureError], tags: [RootEffect].}
-
Connects to the hostname specified by the URL and performs a POST request.
This procedure will follow redirects up to a maximum number of redirects specified in newAsyncHttpClient.
Source