cHttpClient class
A cHttpClient sends HTTP and HTTPS requests with the Windows component WinHTTP (WinHttp.WinHttpRequest.5.1) and reads the answer as text, as JSON, as bytes or as a file. A program fills in the settings (headers, form values, a JSON document, query values), calls one of the Send methods, and reads the result with one of the Return methods. The package keeps one ready-made instance, TBMAN.HttpClient.
The settings are fields of the object, and most of them stay after a request: the headers and the content type are kept, and the form values, the JSON document and the query values are emptied. The Send methods return the object itself, so calls can be chained.
Dim Http As New cHttpClient
Dim Reply As cJson
Http.RequestHeaders.Item("X-Api-Key") = "secret"
Http.SetRequestContentType JsonString
Http.RequestDataJson.Item("name") = "Ann"
Http.SendPost "https://example.com/api/users"
Set Reply = Http.ReturnJson()
Debug.Print Reply.Item("id")
How a request runs
Fetch does the work for every Send method. It opens the request, sets the headers, builds the body, sends it and, unless the request is asynchronous, waits for the answer. In order:
- It empties LastError and raises error 500 if the URL is empty.
- If RequestDataQuery has items, it adds them to the URL.
- If RequestHeaders has no
Content-Typeitem, it adds one with RequestContentType, which is set toapplication/x-www-form-urlencodedwhen it is empty. Every item of RequestHeaders whose text is not empty is then sent. - It sets the options of the request: all certificate errors are ignored, and redirects are followed or not as EnableRedirects says.
- If the call gave no body, it builds one: from RequestDataJson when the content type is exactly
application/json, or from RequestDataForm when it is exactlyapplication/x-www-form-urlencoded. For any other content type, a body is sent only when the call gives one. - It encodes the body as UTF-8 when RequestChartSet contains
utf, and in the ANSI code page otherwise, sets the extra headerContent-Lenght(see Consts), and sends it. - Unless the request is asynchronous, it waits RequestTimeOut seconds (at least 5) for the answer, raises the status as an error when it is 400 to 599, and reads the response headers into ResponseHeaders.
- It empties the form values, the JSON document and the query values, clears DebugStart and makes the next request synchronous. The headers are kept.
A failed request does the last step too, sets LastError and raises the error again. The error is raised with the number of the cause: the HTTP status for a 4xx or 5xx answer, 900 for a timeout, or the number that WinHTTP reports (such as -2147012867, a connection with the server could not be established).
Several error texts of the class are written in Chinese. The ones that are not a status or a WinHTTP text are these:
| Number | Text means | Raised by |
|---|---|---|
| 500 | the URL of the request is empty | Fetch |
| 900 | the request timed out, with the time in seconds | Fetch, UploadFile |
| 310 | more redirects than the maximum | FollowRedirect |
| 58 | the file exists | DownloadFile, FinishDownloadFile |
| 53 | the file does not exist | UploadFile |
| 1 | no download path was found, or the file could not be read | FinishDownloadFile, UploadFile |
Warning
The class ignores every certificate error: an expired certificate, one from an unknown authority and one issued for another name are all accepted. An HTTPS request therefore encrypts the traffic, but does not prove who the server is, and a program that sends a password or a key with it can send it to an impostor.
Note
In BETA 1005 the compiler refuses an assignment written through a public dictionary field of an object, such as Http.RequestHeaders("X-Key") = "1", with TB5090. Write the default member out: Http.RequestHeaders.Item("X-Key") = "1". Reading Http.RequestHeaders("X-Key") is accepted.
Request settings
RequestContentType
A String, the media type of the body. It is empty in a new object. SetRequestContentType sets it from a number, and the constants of Consts and ToolsHttp are the usual values. Fetch reads it to choose the body and, when the headers have no Content-Type, to set that header.
Warning
The Content-Type header is put into RequestHeaders by the first request of an object and stays there. A later change of RequestContentType changes the body that is built, but not the header: after a form request, SetRequestContentType JsonString sends the JSON text with the header application/x-www-form-urlencoded. Set the item as well, Http.RequestHeaders.Item("Content-Type") = "application/json", or use a new object.
UploadFile sets this field to its multipart/form-data value, with the boundary, and leaves it, so a request after an upload is sent with that content type unless the program sets it again.
RequestChartSet
A String, the character set of the request body. It is utf-8 in a new object. If the name contains utf, in any letter case, the body is encoded as UTF-8; any other name encodes it in the ANSI code page of the computer, whatever the name is: gbk does not select GBK, and a character that the code page lacks becomes ?. The field does not set a header.
RequestTimeOut
A Long, the seconds that a synchronous request waits for the answer. It is 5 in a new object. A value under 5 is raised to 5 by Fetch, and the field shows 5 afterwards. When the wait ends without an answer, the call raises error 900. UploadFile waits the same time, but a value under 5 becomes 60 there, so a new object, whose value is 5, waits 5 seconds for the answer to an upload.
RequestHeaders
A Scripting.Dictionary of the request headers: the keys are the header names, compared without regard to letter case, and the items are their text. It is created with the object, and it is the only setting that is not emptied after a request, so a header, a cookie or an API key stays on every request of the object. Write Http.RequestHeaders.Item("X-Key") = "1"; an item whose text is the empty string is not sent. To stop sending a header, remove the key or empty the dictionary with RemoveAll.
RequestDataForm
A Scripting.Dictionary of the values of a form: the keys are the field names. When the content type is application/x-www-form-urlencoded and the call gives no body, Fetch sends name=value pairs joined by &, in the order they were added. It is emptied after each request.
Warning
The names and values are written as they are, without URL encoding. A value with a space, &, = or % produces a body that a server reads as other fields: Form.Item("city") = "A B&C" is sent as city=A B&C. Such a value has to be escaped by the program before it is added; ToolsHttp.UrlEncode does that for text in the ANSI code page.
RequestDataJson
A cJson, the document that is sent when the content type is exactly application/json and the call gives no body. It is encoded with the characters themselves and not as \uXXXX escapes (the FromUnicode argument of cJson.Encode is True), so the warning on that argument applies: a text value with a backslash followed by u raises error 13. The document is emptied after each request.
If the document is empty, the request has no body and goes with Content-Length: 0.
RequestDataQuery
A Scripting.Dictionary of query values. When it has items, Fetch adds them to the URL, after a ? or, if the URL has one, a &. It is emptied after each request.
Warning
The pairs are written after the fixed name ver: the URL gets ?ver= followed by the pairs joined by &. With the items a = 1 and b = 2, the request goes to ?ver=a=1&b=2: a server reads a parameter ver whose value is a=1, and a parameter b. The name of the first item is lost, and no item is sent as a parameter of that name. The values are not URL encoded either. Put the query values in the URL text instead, which the call sends as given.
RequestDataBody
A Scripting.Dictionary. No member reads it.
RequestData
An array of Byte. No member reads it.
EnableRedirects
A Boolean, True in a new object. When it is True, WinHTTP follows the redirects of the server (status 301, 302, 307 and so on) and the object holds the final answer. AllowRedirects sets it. The value stays for every later request.
MaxRedirects
A Long, 10 in a new object. Only FollowRedirect reads it. WinHTTP follows redirects by itself, with a limit of its own, whatever this field says.
Response
ResponseHeaders
A Scripting.Dictionary of the headers of the last answer, keys compared without regard to letter case. It is filled by each successful synchronous request and by UploadFile. It is not emptied: the headers of an earlier answer stay, and the headers of a later answer overwrite those of the same name. An answer with an error status, and an asynchronous request, leave it unchanged.
Each header line is split at its first colon and both parts are trimmed, so the value is cut at its own colon:
| Line | Key | Value |
|---|---|---|
Content-Type: text/plain | Content-Type | text/plain |
Date: Tue, 13 Oct 2026 18:00:00 GMT | Date | Tue, 13 Oct 2026 18 |
Location: http://host/path | Location | http |
The text after a blank line at the end becomes an item with the empty key and the empty value. A header that occurs twice (Set-Cookie) keeps its last value.
Cookies
A Scripting.Dictionary. No member fills it. WinHTTP keeps the cookies that the server sets and sends them with the later requests of the same object, in addition to an item Cookie of RequestHeaders.
ResponseRaw
A Variant. No member sets it.
LastError
A String, the text of the last error that Fetch, DownloadFile, FinishDownloadFile, UploadFile or UploadFileSimple caught. Fetch empties it at the start of a request.
DebugStart
A Boolean. When it is set to True before a request, DebugInfo describes that request, and the field is cleared when the request ends.
DebugInfo
A cJson. Fetch replaces it with an empty document at the start of every request. When DebugStart was set, it holds an object Request with IsAsync, Method, Url, Body, Headers (the request headers), TimeOut and ChartSet, and, for a synchronous request, an object Response with Status, StatusText, Headers and Content. A request that failed adds an object Error with Number, Source and Description.
Dim Http As New cHttpClient
Http.DebugStart = True
Http.SendGet "https://example.com/"
Debug.Print Http.DebugInfo.Item("Response")("Status")
Events
OnResponseFinished
An event with no arguments. The class declares it, and nothing raises it: a handler is never called. A program that sends an asynchronous request has to ask for the answer itself.
Methods
AllowRedirects
Sets EnableRedirects.
Syntax: object.AllowRedirects ( Bool ) As cHttpClient
- Bool
- required A Boolean. True lets WinHTTP follow redirects.
Returns the object itself.
Async
Makes the next request asynchronous.
Syntax: object.Async ( Bool ) As cHttpClient
- Bool
- required A Boolean.
Returns the object itself. The setting is for the next request only. An asynchronous Fetch sends the request and returns at once: it does not wait, does not check the status and does not read ResponseHeaders. The answer is read later with a Return method, which raises an error (the data necessary to complete this operation is not yet available) until the answer is there. There is no status property and OnResponseFinished is never raised, so the program finds out that the answer has come by trying.
DownloadFile
Downloads a file with a GET request.
Syntax: object.DownloadFile ( url , savePath [ , Overwrite ] ) As Boolean
- url
- required A String, the address.
- savePath
- required A String, the file to write.
- Overwrite
- optional A Boolean, True by default. With False, a file that exists makes the call raise error 58.
Sends the request with SendGet (so a 4xx or 5xx status is raised as in Fetch), raises an error for a status other than 200, and writes the body to the file with cToolsStream.SaveFileAsBinary. It returns True. The test for Overwrite comes after the request, so the answer has been received when error 58 is raised; the file is not changed.
Warning
The call returns True also when the file could not be written, for example for a folder that does not exist: SaveFileAsBinary returns False there, and DownloadFile does not look at it. Check that the file exists after the call.
DownloadFileAsync
Starts a download without waiting for it.
Syntax: object.DownloadFileAsync ( url , savePath [ , Overwrite ] ) As cHttpClient
The arguments are those of DownloadFile. The method sends an asynchronous GET request, then stores the path and the overwrite choice in RequestHeaders as the items X-Download-Path and X-Download-Overwrite. FinishDownloadFile reads them and writes the file.
The two items stay in RequestHeaders, so every later request of the object sends them to the server, with the local path in them. Remove them when the download is done: Http.RequestHeaders.Remove "X-Download-Path", and the same for X-Download-Overwrite.
Fetch
Sends a request. The Send methods are shorter forms of this one; see How a request runs.
Syntax: object.Fetch ( Method , url [ , Body ] ) As cHttpClient
- Method
- required An EnumRequestMethod (declared in ToolsHttp): ReqPost, ReqGet, ReqPut, ReqDelete or ReqOptions.
- url
- required A String, the address. An empty string raises error 500.
- Body
- optional A String, the body. If it is given, it is sent as it is and the form values and the JSON document are not used (they are still emptied after the request). An empty string counts as not given.
Returns the object itself.
FinishDownloadFile
Writes the file of a download that DownloadFileAsync started.
Syntax: object.FinishDownloadFile ( ) As Boolean
Reads the answer, raises an error for a status other than 200 and, if the file exists and the overwrite choice was False, error 58. It then writes the file and returns True. While the answer has not arrived, the call raises the WinHTTP error the data necessary to complete this operation is not yet available (-2147483638); a program calls it again after a short wait. If the item X-Download-Path is not in RequestHeaders, it raises error 1. Like DownloadFile, it returns True also when the file could not be written.
FollowRedirect
Meant to send the request again to the address of a redirect.
Syntax: object.FollowRedirect ( [ RedirectMethod ] ) As cHttpClient
- RedirectMethod
- optional An EnumRequestMethod, the method for a 307 or 308 answer. If it is left out, a GET request is sent.
Reads the address with GetRedirectUrl and sends a GET request to it (for 307 and 308, the given method). Each call counts, and error 310 is raised when the count exceeds MaxRedirects; ResetRedirectCount sets the count to 0. The method needs EnableRedirects set to False with AllowRedirects, or no redirect answer is left to follow. It returns the object itself, and does nothing if the last answer had no Location header.
Note
The method cannot send a request in BETA 1005. The address comes from ResponseHeaders, which cuts a value at its first colon, so http://host/path is read as http, and WinHTTP raises error -2147012890, the URL does not use a recognized protocol. A Location header with a relative address, such as /path, has no colon and is read whole, but WinHTTP refuses it as well. A program that has to follow a redirect leaves EnableRedirects at True, so that WinHTTP follows it.
GetRedirectUrl
Returns the Location header of the last answer.
Syntax: object.GetRedirectUrl ( ) As String
Returns the item Location of ResponseHeaders, or an empty string when there is none. The value is cut at its first colon (see ResponseHeaders), so an absolute address comes back as http or https.
MapRequestContentType
The same as SetRequestContentType.
Syntax: object.MapRequestContentType ( ReqType [ , ContentType ] ) As String
ResetRedirectCount
Sets the count of FollowRedirect to 0.
Syntax: object.ResetRedirectCount ( ) As cHttpClient
Returns the object itself.
ReturnBody
Returns the body of the last answer as bytes.
Syntax: object.ReturnBody ( ) As Byte()
Raises the WinHTTP error -2147012795, this method cannot be called until the Send method has been called, when the object has sent no request.
ReturnJson
Reads the body of the last answer as a JSON document.
Syntax: object.ReturnJson ( [ IsUtf8 ] [ , IsConvert ] ) As cJson
- IsUtf8
- optional A Boolean, True by default.
- IsConvert
- optional A Boolean.
Takes the text from ReturnText with the same arguments and decodes it with cJson.Decode, in a new cJson. A text that is not JSON raises error 10001 from the parser.
ReturnStream
Returns the answer as a stream.
Syntax: object.ReturnStream ( ) As Variant
Returns the ResponseStream of the WinHTTP object, an IStream.
ReturnText
Returns the body of the last answer as text.
Syntax: object.ReturnText ( [ IsUtf8 ] [ , IsConvert ] ) As String
- IsUtf8
- optional A Boolean, True by default. With True, the bytes of the body are decoded as UTF-8 with ToolsUtf8.Decode. The character set that the answer’s headers name is not looked at.
- IsConvert
- optional A Boolean. Used only when IsUtf8 is False. With True, every byte of the body becomes one character (
StrConv(bytes, vbUnicode)), which is the form to use for a text that is neither UTF-8 nor correctly named. With False, the text is WinHTTP’sResponseText, which it decodes with the character set of theContent-Typeheader, or UTF-8.
Send
The same as Fetch.
Syntax: object.Send ( Method , Url [ , Body ] ) As cHttpClient
SendDelete
Sends a DELETE request.
Syntax: object.SendDelete ( url [ , Body ] ) As cHttpClient
The same as Fetch with ReqDelete.
SendGet
Sends a GET request.
Syntax: object.SendGet ( url [ , Body ] ) As cHttpClient
The same as Fetch with ReqGet.
SendOptions
Sends an OPTIONS request.
Syntax: object.SendOptions ( url [ , Body ] ) As cHttpClient
The same as Fetch with ReqOptions.
SendPost
Sends a POST request.
Syntax: object.SendPost ( url [ , Body ] ) As cHttpClient
The same as Fetch with ReqPost.
Dim Http As New cHttpClient
Http.RequestDataForm.Item("user") = "ann"
Http.RequestDataForm.Item("language") = "en"
Http.SendPost "https://example.com/login"
Debug.Print Http.ReturnText()
SendPut
Sends a PUT request.
Syntax: object.SendPut ( url [ , Body ] ) As cHttpClient
The same as Fetch with ReqPut.
SetCookies
Sets the Cookie header.
Syntax: object.SetCookies ( Value ) As cHttpClient
- Value
- required A String, the text of the header, such as
a=1; b=2. Nothing happens when it is empty.
Stores the text as the item Cookie of RequestHeaders, where it stays for every later request. It returns the object itself. WinHTTP adds the cookies it has received from the server to the header it sends, so a cookie can appear twice.
SetRequestContentType
Sets RequestContentType from a number.
Syntax: object.SetRequestContentType ( ReqType [ , ContentType ] ) As String
- ReqType
- required An EnumRequestContentType (declared in ToolsHttp): JsonString (
application/json), FormEncode (application/x-www-form-urlencoded), FormMultiPart (multipart/form-data), TextPlain (text/plain) or TextHtml (text/html). A value of 0 sets the empty string, and a value above 5 raises error 9. - ContentType
- optional A String. Not used.
Returns the new text. Read the warning at RequestContentType: the call after a first request does not change the header.
ShowPage
Opens an address in the program that Windows associates with it.
Syntax: object.ShowPage ( url )
- url
- required A String, the address.
Calls ShellExecute with the verb open and the address, so a web address opens in the default browser. It sends no request itself.
UploadFile
Sends a file in a POST request of the type multipart/form-data.
Syntax: object.UploadFile ( url , filePath [ , fieldName ] [ , additionalFormData ] ) As cHttpClient
- url
- required A String, the address.
- filePath
- required A String, the file to send. It is read whole into memory. If it does not exist, the call raises error 53.
- fieldName
- optional A String, the name of the form field that holds the file. The default is
file. - additionalFormData
- optional A Scripting.Dictionary of more form fields, as names and text values.
The body has a part with the file and a part for each item of additionalFormData. The file is always sent with the type application/octet-stream and the file name of the path. The boundary is ----WebKitFormBoundary followed by a random number. The call waits RequestTimeOut seconds for the answer and raises the status of a 400 or higher as an error, and error 900 for a timeout. It reads ResponseHeaders and returns the object itself.
The call sends neither the items of RequestHeaders nor the extra header of Fetch, and it follows redirects as EnableRedirects says. It leaves two things behind: RequestContentType keeps the multipart value, and RequestHeaders gets an item Content-Length with the size of the body. A later request therefore has the multipart content type, as the warning at RequestContentType says. The Content-Length item does not reach the server with a later GET request.
Note
A call that fails with a status or a timeout raises the right number but a general text: Application-defined or object-defined error for a status, Automation error for error 900. The real text, such as 上传失败: 404#Not Found (the upload failed), is in LastError.
Dim Http As New cHttpClient
Dim Extra As New Scripting.Dictionary
Extra.Add "owner", "ann"
Http.UploadFile "https://example.com/upload", "C:\data\report.pdf", "document", Extra
Debug.Print Http.ReturnText()
UploadFileSimple
Sends a file and returns whether it worked.
Syntax: object.UploadFileSimple ( url , filePath ) As Boolean
Calls UploadFile with the default field name, with error handling turned off, and returns True when it raised no error. After an error it sets LastError to the text of the error that was raised, which is the general text described at UploadFile for a status, and not the text of the failure.
See Also
- Consts module – the media types and header names
- ToolsHttp module – the enumerations, and URL and form helpers
- ToolsHttpMimeType module – the media type for a file name
- cJson class – the JSON document of requests and answers
- cToolsStream class – writes the downloaded file
- TBMAN module – the shared instance TBMAN.HttpClient
- TBMANLIB package – overview