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:

  1. It empties LastError and raises error 500 if the URL is empty.
  2. If RequestDataQuery has items, it adds them to the URL.
  3. If RequestHeaders has no Content-Type item, it adds one with RequestContentType, which is set to application/x-www-form-urlencoded when it is empty. Every item of RequestHeaders whose text is not empty is then sent.
  4. It sets the options of the request: all certificate errors are ignored, and redirects are followed or not as EnableRedirects says.
  5. 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 exactly application/x-www-form-urlencoded. For any other content type, a body is sent only when the call gives one.
  6. It encodes the body as UTF-8 when RequestChartSet contains utf, and in the ANSI code page otherwise, sets the extra header Content-Lenght (see Consts), and sends it.
  7. 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.
  8. 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

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.

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.

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.

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.

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’s ResponseText, which it decodes with the character set of the Content-Type header, 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