http_client
IHTTPClient is simple HTTP client.
File information
Functions
Helper functions
inline const char * HTTPShutdownReasonToStr(http_shutdown_reason_t reason)
Helper function to convert a http_shutdown_reason_t to a string. Helpful for debugging and logging purposes.
HTTPShutdownReasonToStr
Parameters
http_shutdown_reason_t reason | The shutdown reason to get the string for. |
Return value
The name of the shutdown reason. Will be the same as the name of the num itself.
Classes
IHTTPClient
class IHTTPClient {
public:
virtual ~IHTTPClient() {}
static IHTTPClient * Create(class IIoMux * const iomux,
class ISocketProvider * const tcpSocketProvider,
class ISocketProvider * const tlsSocketProvider,
class UHTTPClient * const user,
class IInstanceLog * const log,
class IDns * const dns = nullptr,
class ISocketContext * const socketContext = nullptr);
virtual void Connect(const char * address,
const char * user = NULL,
const char * pwd = NULL,
int authMethods = HTTP_AUTH_ALL) = 0;
virtual void Recv(byte * buffer, size_t size, bool recvPartial = false);
virtual void Send(const byte * data = NULL, size_t size = 0, bool last = false);
virtual void Shutdown();
virtual void Reconnect();
virtual void PassErrorToUser(http_error_t err);
virtual void SendContentForAuthentication(bool doSend);
virtual void SetRequestType(http_request_type_t reqType, const char * resourceName, size_t contentLength = 0, const char * contentType = "text/xml; charset=utf-8");
virtual void SetCustomHeaderField(const char * field, const char * value);
virtual void SetTimeout(dword timeoutMs);
virtual http_result_t GetHTTPResult();
virtual int GetResponseCode();
virtual const char * GetResponseCodeDescription();
virtual size_t GetContentLength(bool & chunked);
virtual size_t GetHeaderFieldValueCount(const char * headerField);
virtual const char * GetHeaderFieldValue(const char * headerField, size_t index = 0);
virtual bool Connected();
virtual void SetUser(class UHTTPClient * const user);
};
This is the main class of the HTTP client functionality. Please note that IHTTPClient is not a fully-fledged HTTP client. For now only GET, PUT and POST requests can be used.
However, the generally used authentication variants are supported by the Client (basic, digest and NTLM). Connections can be established by a TCP or a TLS socket. This depends
on the address given to connect (HTTP for normal TCP connections, HTTPS for a TLS connection).
Public functions
Create (static function)
Creates an IHTTPClient instance.
Parameters
IIoMux * const iomux | The iomux instance needed for socket communication. |
ISocketProvider * const tcpSocketProvider | A socket provider to create a TCP socket. |
ISocketProvider * const tlsSocketProvider | A socket provider to create a TLS socket. |
UHTTPClient * const user | An UHTTPClient instance to receive the callbacks from IHTTPClient. |
IInstanceLog * const log | The log instance for logging. |
IDns * const dns | The optional dns instance for dns requests. If specified, the same DNS instance is used for DNS requests which saves performance. |
ISocketContext * const socketContext | An optional socketContext which can be used for the socket which is created by the httpclient. You can disable sending of a client certificate with this socketContext or send a specific own client certificate. |
Return Value
The IHTTPClient instance created. Must be deleted if no longer used.
Connect
Connects to the given address (which can be an IPv4 or IPv6 address, as well as an URI). If the address is a URI and that URI starts with HTTPS,
an TLS connection will be established instead of the default TCP connection.
Parameters
const char * address | The address to connect to. Can be an IPv4 / IPv6 address or an valid URI. |
const char * user | (Default: nullptr) The user for authentication if requested by the server. |
const char * pwd | (Default: nullptr) The password for authentication if requested by the server. |
int authMethods | (Default: HTTP_AUTH_ALL) The authentications to accept as bit field Can be a combination of HTTP_AUTH_BASIC, HTTP_AUTH_DIGEST and HTTP_AUTH_NTLM. Or HTTP_AUTH_ALL to accept all of them. |
Callbacks
On success, UTHHPClient::HTTPClientConnectComplete() will be called. If an error occurs, the connection will be closed, which leads to a call to UHTTPClient::HTTPClientShutdown().
Remarks
If using an URI to connect, an username and password for authentication can also be given with the URI itself ("http://user::pwd@mysite.com").
Recv
Let the HTTP Client receive data. Must be called after sending a GET request and receiving the callback, that the request is completed (which means, the response header
had been read and interpreted). Recv() also should only be called after checking, if the request result actually delivers data.
Parameters
byte * buffer | The buffer to write the received data to. |
size_t size | The size of the buffer. |
bool recvPartial |
(Default: false) If true, the function will call the callback as soon as a couple of bytes had been received. If false,
the HTTP client waits until received the number of bytes given in size.
|
Callbacks
UHTTPClient::HTTPClientRecvResult() will be called after the data had been read or UHTTPClient::HTTPClientRecvCanceled(), if the Recv() call had been canceled for whatever reason.
Remarks
Be careful with calling Recv() and recvPartial set to false. Because if the data received will be less than size, Recv() will never lead to a callback.
Send
Sends the request by building a correct header and sending that it. So Send() must be called even if there is no need to send additional data, too. If there is additional data,
it can be given to Send() and will be send after the header. If the data to send is chunked encoded, Send() can be called multiple times until all of the data had been send.
To end a chunk encoded transfer, set the last flag to true.
Parameters
const byte * data | (Default: nullptr) The buffer holding additional data to send or nullptr to send nothing else. |
size_t size | (Default: 0) The number of bytes to send. |
bool last | (Default: false) If true, that send call indicates the end of a chunk encoded transfer. |
Callbacks
UHTTPClient::HTTPClientSendResult() will be called after the data had been send. After the request had been completed, UTHHPClient::HTTPClientRequestComplete() will be called.
Remarks
To respect the flow control necessary for the asynchronous kind of communication used by the IHTTPClient, continuous send calls should be made after receiving a
UHTTPClient::HTTPClientSendResult() callback.
Shutdown
Closes the HTTP connection. If there is data left inside the IHTTPClient that still must be send, it will be send first before closing the connection.
Callbacks
After the connection had been closed, UHTTPClient::HTTPClientShutdown() will be called.
Reconnect
After the connection had been closed (because of a call to IHTTPConnection::Shutdown() or because of a close initiated by the server), it can be reestablished by calling
Reconnect() using the same options (request type, server address, ...) as given for the first request.
Callbacks
On success, UTHHPClient::HTTPClientConnectComplete() will be called. If an error occurs, the connection will be closed, which leads to a call to UHTTPClient::HTTPClientShutdown().
PassErrorToUser
Generally an error leads to a shutdown of the IHTTPClient. But sometimes it is useful to not close the error and pass it to the app instead. This can be activated with
PassErrorToUser(). In case of an error that should be passed to the user, the error case will be redirected to a normal UHTTPClient::HTTPClientRequestComplete() callback.
The UHTTPClient must than check the HTTP result by calling IHTTPClient::GetHTTPResult().
Parameters
http_error_t err | The HTTP Error to pass to the user. Can be a bit field. |
Callbacks
In case of one of the given errors occurs, UHTTPClient::HTTPClientReqeustComplete() will be called anyway.
Remarks
Even if PassErrorToUser() can theoretically called multiple times, the better way will be to call it once giving a bit filed of flags, combined using or.
Valid flags are HTTP_CL_ERR_NOT_FOUND, HTTP_CL_ERR_BAD_REQUEST, HTTP_CL_ERR_INTERNAL_SERVER and HTTP_CL_ERR_REDIRECT. See the data type section below for details.
SendContentForAuthentication
When connecting to a server that needs authentication, the IHTTPClient at first sends a request without the content (if a POST request need to be send). This
helps to prevent sending data that won't be needed. But it can be, that the server won't accept such a request. An example for that is the Microsoft Exchange
365 server, which will respond with an bad request. For that case, SendContentFoprAuthentication() can be used, so that the data for the POST request will
be send even if the HTTP client needs to authenticate first.
Parameters
bool doSend | True, to send the request content for authentication, too. |
Remarks
The default is, that no content will be send for authentication.
SetRequestType
Before sending the request, the application first needs to set the type of the request as well as the resourceName it wants to access. If the request is not a GET
request, data can be or need to send. In that case, the content type (aka mime-type) must be defined as well as the content length. If the content length is unknown,
HTTP_CLIENT_CHUNKED_TRANSFER can be used to enabled chunked transfer of the data.
Parameters
http_request_type_t reqType | The request type. |
const char * resourceName | The resource name for the request. |
size_t contentLength | (Default: 0) The content length of additional data to send (HTTP_POST & HTTP_PUT only). |
const char * contentType | (Default: "text/xml; charset=utf-8") The mime-type of the additional data to send (HTTP_POST & HTTP_PUT only). |
SetTimeout
SetTimeout can be called to enable timeout handling. If timeoutMs is greater zero, a HTTPClientShutdown with HTTP_SHUTDOWN_TIMEOUT is triggered if HTTPClientConnectComplete, HTTPClientRequestComplete or HTTPClientRecvResult cannot be called within timeoutMs.
You may call SetTimeout before Connect to enable timeout handling for every state.
Parameters
dword timeoutMs | Timeout in milliseconds. Use 0 to disable timeouts (60s are the default value). |
Remarks
The given timeout is not a timeout for the complete request from Connect to Shutdown but for every asynchronously triggered operation, e.g. from Connect to HTTPClientConnectComplete and from Send(..., last=true) until HTTPClientRequestComplete.
SetCustomHeaderField
Adds the given header field with the given value to the HTTP header to send. Because of this, SetCustomHeaderField() must be called before calling Send().
Parameters
const char * field | The name of the field to set. |
const char * value | The value to set for that field. |
GetHTTPResult
Return Value
The result of the last completed request. See the data types section of this document for details on http_result_t.
GetResponseCode
Return Value
The response status code of the last completed request. Returns 0, if the request has not been completed, yet.
GetResponseCodeDescription
Return Value
The response status code description of the last completed request. Returns NULL, if the request has not been completed, yet.
GetContentLength
Returns the size of the content sent with the requests response. It can be that the content is chunk encoded. In that case, the given bool
variable will set to true.
Parameters
bool & chunked | If the received data is chunk encoded, true will be stored to the chunked variable. |
Return Value
The size of the content sent with the response or 0, if no data is available or if the received data is chunk encoded.
Remarks
The return value must be interpreted as follows:
- If the returned value is > 0, the app must read that amount of data using Recv().
- If the returned value is 0 and the flag chunked is set, the app must read data until the end of the stream had been reached.
- If the returned value is 0 and the flag chunked is not set, there is not data sent with the response.
GetHeaderFieldValueCount
A header field can be set more then once in the HTTP header. To get the number of occurrences, GetHeaderFieldValueCount() must be called.
Parameters
const char * headerField | The name of the requested header field. |
Return Value
The number of occurrences of that field or 0, if that field won't exist.
GetHeaderFieldValue
Can be used to access the values of the HTTP header fields send with the last response.
Parameters
const char * headerField | The name of the requested header field. |
size_t index | (Default: 0) The index of the header field, must be between 0 and GetHeaderFieldValueCount(headerField) - 1. |
Return Value
The value of the header field.
Remarks
Don't save the pointer itself for later use, because after sending an new request, the returned pointer becomes invalid. If the value of that header field needs to
be used later, it must be copied.
Connected
Return Value
True, if the HTTP client is connected, else false. This just reflects the socket connection state and does not mean, that there is not a socket connect already running!
You can use this function for debugging purposes but not for a state machine!
SetUser
Changes the user class for receiving callbacks.
Parameters
UHTTPClient * const user | An UHTTPClient instance to receive the callbacks from IHTTPClient. |
UHTTPClient
class UHTTPClient {
public:
~UHTTPClient() {}
virtual void HTTPClientConnectComplete(IHTTPClient * const httpClient);
virtual void HTTPClientShutdown(IHTTPClient * const httpClient, http_shutdown_reason_t reason);
virtual void HTTPClientSendResult(IHTTPClient * const httpClient);
virtual void HTTPClientRecvResult(IHTTPClient * const httpClient, byte * buffer, size_t len, bool transferComplete);
virtual void HTTPClientRecvCanceled(IHTTPClient * const httpClient, byte * buffer);
virtual void HTTPClientRequestComplete(IHTTPClient * const httpClient);
};
This class is for receiving callbacks from the IHTTPClient. You must pass as subclass of UHTTPClient to an IHTTPClient as user.
HTTPClientConnectComplete
Will be called after the connection to the server had been successfully established. Here is the right place to setup up the request type
(by calling ITTHClient::SetRequestType()) and send the request.
Parameters
IHTTPClient * const httpClient | The calling IHTTPClient instance. |
HTTPClientShutdown
Will be called after the IHTTPClient connection had been shutdown because of Shutdown() had been called or because of the connection had been closed by the server.
Parameters
IHTTPClient * const httpClient | The calling IHTTPClient instance. |
http_shutdown_reason_t reason | The reason for the shutdown. See the data types section below for details. |
Remarks
You can reuse the same IHTTPClient instance by calling Connect again after HTTPClientShutdown callback.
HTTPClientSendResult
Will be called after the Send() had been completed, which means that the header and any additional data to send had been send. If there is more data to send,
this will be the right place for sending the next part to respect the asynchronous flow control.
Parameters
IHTTPClient * const httpClient | The calling IHTTPClient instance. |
HTTPClientRequestComplete
Will be called if the HTTP request has been successfully completed. A completed request means, that everything for the request had been send and the result header had been
received and interpreted. So this callback function will be the right place to check for data that maybe is available with the response and call IHTTPClient::Recv() to receive it.
Parameters
IHTTPClient * const httpClient | The calling IHTTPClient instance. |
HTTPClientRecvResult
Will be called after the application called IHTTPClient::Recv() and data had been received. The flag transferComplete indicates, if the data of the response
had been received completely or not (which counts for both, streamed and chunk encoded transfer). If false, additional Recv() calls must follow until the flag
is set to true.
Parameters
IHTTPClient * const httpClient | The calling IHTTPClient instance. |
byte * buffer | The buffer holding the received data. |
size_t len | The number of bytes received. |
bool transferComplete | true, if the transfer is completed (all data received), else false. |
Remarks
On transferComplete==true, you may call SetRequestType again to make a further request to the same server.
HTTPClientRecvCanceled
Will be called before UHTTPClient::HTTPClientShutdown(), if a receive data buffer had been passed through IHTTPClient::Recv() and not returned back via
UHTTPClient::HTTPClientRecvResult(). If the buffer was allocated individually this gives the opportunity to free it.
Parameters
IHTTPClient * const httpClient | The calling IHTTPClient instance. |
byte * buffer | The buffer given to Recv() to free it (if needed). |
Data types
Defines
#define HTTP_AUTH_NONE 0
#define HTTP_AUTH_NTLM 1
#define HTTP_AUTH_DIGEST 2
#define HTTP_AUTH_BASIC 4
#define HTTP_AUTH_ALL HTTP_AUTH_NTLM | HTTP_AUTH_DIGEST | HTTP_AUTH_BASIC
#define HTTP_CLIENT_CHUNKED_TRANSFER 0xffffffff
HTTP_AUTH_NONE | (Use with IHTTPClient::Connect()) Don't support authentication. |
HTTP_AUTH_NTLM | (Use with IHTTPClient::Connect()) Accept NTLM authentication. |
HTTP_AUTH_DIGEST | (Use with IHTTPClient::Connect()) Accept digest authentication. |
HTTP_AUTH_BASIC | (Use with IHTTPClient::Connect()) Accept basic authentication. |
HTTP_AUTH_ALL | (Use with IHTTPClient::Connect()) Accept all supported authentications. This is the default. |
HTTP_CLIENT_CHUNKED_TRANSFER | The end of line, used inside the HTTP header. The Header ends with on empty line, which is a line that only holds CLRF. |
http_request_type_t
typedef enum {
HTTP_GET,
HTTP_POST,
HTTP_PUT,
HTTP_DELETE
} http_request_type_t;
This enum defines the request types supported by the IHTTPClient. One of them must be given to IHTTP::SetRequestType().
http_shutdown_reason_t
typedef enum {
HTTP_SHUTDOWN_NORMAL,
HTTP_SOCKET_LOST,
HTTP_ADDRESS_INVALID,
HTTP_SOCKET_ERROR,
HTTP_CONNECT_FAILED,
HTTP_BYTE_STREAM_BROKEN,
HTTP_UNHANDLED_HTTP_RESULT,
HTTP_FAILURE,
HTTP_AUTHENTICATION_FAILED,
HTTP_NOT_FOUND,
HTTP_BAD_REQUEST,
HTTP_INTERNAL_SERVER_ERROR,
HTTP_CONNECTION_REFUSED,
HTTP_SHUTDOWN_TIMEOUT
} http_shutdown_reason_t;
This enum defines the reasons for a shutdown. The following reason can lead to a IHTTPClient shutdown:
HTTP_SHUTDOWN_NORMAL | A normal shutdown initiated by the application itself by calling IHTTPClient::Shutdown(). |
HTTP_SOCKET_LOST | The socket had been lost. |
HTTP_ADDRESS_INVALID | The address given to IHTTPClient::Connect() was invalid. |
HTTP_SOCKET_ERROR | An unexpected socket error occurred. |
HTTP_CONNECT_FAILED | Creating the connection failed. |
HTTP_BYTE_STREAM_BROKEN | There was an error inside the byte stream while authenticating or receiving data. |
HTTP_UNHANDLED_HTTP_RESULT | An not expected HTTP result had been returned by the server. |
HTTP_FAILURE | En error had been found in the response http header, sent by the server. |
HTTP_AUTHENTICATION_FAILED | The authentication failed (e. G. because of wrong username / password or an unsupported authentication method). |
HTTP_NOT_FOUND | The requested resource for the request could not be found on the server. |
HTTP_BAD_REQUEST | The request was bad or malformed (e. g. a custom header field was wrong). |
HTTP_INTERNAL_SERVER_ERROR | An internal error occurred on the server side. |
HTTP_CONNECTION_REFUSED | The server refused the connection. |
HTTP_SHUTDOWN_TIMEOUT | The timeout of SetTimeout triggered. |
http_result_t
typedef enum {
HTTP_RESULT_OK,
HTTP_RESULT_NOT_FOUND,
HTTP_RESULT_BAD_REQUEST,
HTTP_RESULT_REDIRECT,
HTTP_RESULT_INTERNAL_SERVER_ERROR,
HTTP_RESULT_AUTHENTICATION_ERROR,
HTTP_RESULT_SERVICE_UNAVAILABLE,
HTTP_RESULT_UNKNOWN
} http_result_t;
This enum defines the results of a HTTP request. That results can be get by calling IHTTPClient::GetHTTPResult(). The following results are supported
by that function:
HTTP_RESULT_OK | (HTTP 200) The request was correct and an appropriate response had been send. |
HTTP_RESULT_NOT_FOUND | (HTTP 404) The requested resource could not be found on the server. |
HTTP_RESULT_BAD_REQUEST | (HTTP 400) The request was malformed (e. g. a custom header field was used incorrectly). |
HTTP_RESULT_REDIRECT | (HTTP 30x) The request was redirected and flag HTTP_CL_ERR_REDIRECT set. |
HTTP_RESULT_INTERNAL_SERVER_ERROR | (HTTP 500) An internal error occurred on the server. |
HTTP_RESULT_AUTHENTICATION_ERROR | (HTTP 401) Authentication failed and flag HTTP_CL_ERR_AUTHENTICATION_FAILED set. |
HTTP_RESULT_SERVICE_UNAVAILABLE | (HTTP 503) service unavailable and flag HTTP_CL_ERR_SERVICE_UNAVAILABLE set. |
HTTP_RESULT_UNKNOWN | Another result code. |
http_error_t
typedef enum {
HTTP_CL_ERR_NOT_FOUND = 0x0001,
HTTP_CL_ERR_BAD_REQUEST = 0x0002,
HTTP_CL_ERR_INTERNAL_SERVER = 0x0004,
HTTP_CL_ERR_REDIRECT = 0x0008,
HTTP_CL_ERR_AUTHENTICATION_FAILED = 0x0010,
HTTP_CL_ERR_SERVICE_UNAVAILABLE = 0x0020
HTTP_CL_ERR_NOT_ACCAPTABLE = 0x0040,
HTTP_CL_ERR_FORBIDDEN = 0x0080,
HTTP_CL_ERR_ALL = (0xffff & HTTP_CL_ERR_AUTHENTICATION_FAILED & HTTP_CL_ERR_REDIRECT)
} http_error_t;
This enum defines the errors that should be passed to the UHTTPClient user. Instead of shutting down the IHTTPClient, the request will be accepted
and UHTTPClient::HTTPClientRequestComplete() will be called. The user must check the error code (by calling IHTTPClient::GetHTTPResult()) and
handle it properly. The flags of http_error_t can be combined to a bit field by a binary or operator. The following flags are supported:
HTTP_CL_ERR_NOT_FOUND | (HTTP 404) requested resource could not be found on the server. |
HTTP_CL_ERR_BAD_REQUEST | (HTTP 400) The request was malformed (e. g. a custom header field was used incorrectly). |
HTTP_CL_ERR_INTERNAL_SERVER | (HTTP 30x) The request was redirected. |
HTTP_CL_ERR_REDIRECT | (HTTP 500) An internal error occurred on the server. |
HTTP_CL_ERR_AUTHENTICATION_FAILED | (HTTP 401) Authentication failed. |
HTTP_CL_ERR_SERVICE_UNAVAILABLE | (HTTP 503) service unavailable. |
HTTP_CL_ERR_NOT_ACCAPTABLE | (HTTP 406) Not Acceptable. |
HTTP_CL_ERR_FORBIDDEN | (HTTP 403) forbidden. |
HTTP_CL_ERR_ALL | All other codes but 301,302,307,308 and 401 |
Code Example
app::app(IIoMux * iomux)
: iomux(iomux)
{
// you can optionally create a socketContext, if you e.g. want to disable sending of a client certificate
class ISocketContext * socketContext = aTlsSocketProvider->CreateSocketContext(this);
socketContext->DisableClientCertificate();
// create the httpClient instance
this->httpClient = IHTTPClient::Create(iomux, aTcpSocketProvider, aTlsSocketProvider, this, this, nullptr, socketContext);
this->httpClient->Connect("https://google.de");
}
void app::HTTPClientConnectComplete(IHTTPClient * const httpClient)
{
this->httpClient->SetRequestType(HTTP_GET, "/index.html");
this->httpClient->Send();
}
void app::HTTPClientSendResult(IHTTPClient * const httpClient)
{
// for GET we can simply ignore the send result and wait for HTTPClientRequestComplete
}
void app::HTTPClientRequestComplete(IHTTPClient * const httpClient)
{
this->httpClient->Recv((byte *)malloc(1024), 1024);
}
void app::HTTPClientRecvResult(IHTTPClient * const httpClient, byte * buffer, size_t len, bool transferComplete)
{
printf("%.*s", len, (char *)buffer);
if (transferComplete) {
free(buffer);
this->httpClient->Shutdown();
}
else
this->httpClient->Recv(buffer, 1024);
}
void app::HTTPClientShutdown(IHTTPClient * const httpClient, http_shutdown_reason_t reason)
{
delete this->httpClient;
}