http_client

IHTTPClient is simple HTTP client.

File information

Filecommon/interface/http_client.h

Public functions HTTPShutdownReasonToStr

Classes IHTTPClient
UHTTPClient

Data types HTTP_AUTH_NONE
HTTP_AUTH_NTLM
HTTP_AUTH_DIGEST
HTTP_AUTH_BASIC
HTTP_AUTH_ALL
HTTP_CLIENT_CHUNKED_TRANSFER
http_request_type_t
http_shutdown_reason_t
http_result_t
http_error_t

Examples Code Example

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 reasonThe 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 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 iomuxThe iomux instance needed for socket communication.
ISocketProvider * const tcpSocketProviderA socket provider to create a TCP socket.
ISocketProvider * const tlsSocketProviderA socket provider to create a TLS socket.
UHTTPClient * const userAn UHTTPClient instance to receive the callbacks from IHTTPClient.
IInstanceLog * const logThe log instance for logging.
IDns * const dnsThe optional dns instance for dns requests. If specified, the same DNS instance is used for DNS requests which saves performance.
ISocketContext * const socketContextAn 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 * addressThe 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 * bufferThe buffer to write the received data to.
size_t sizeThe 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 errThe 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 doSendTrue, 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 reqTypeThe request type.
const char * resourceNameThe 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 timeoutMsTimeout 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 * fieldThe name of the field to set.
const char * valueThe 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.
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 & chunkedIf 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:
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 * headerFieldThe 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 * headerFieldThe 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 userAn 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 httpClientThe 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 httpClientThe calling IHTTPClient instance.
http_shutdown_reason_t reasonThe 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 httpClientThe 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 httpClientThe 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 httpClientThe calling IHTTPClient instance.
byte * bufferThe buffer holding the received data.
size_t lenThe number of bytes received.
bool transferCompletetrue, 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 httpClientThe calling IHTTPClient instance.
byte * bufferThe 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_TRANSFERThe 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_NORMALA normal shutdown initiated by the application itself by calling IHTTPClient::Shutdown().
HTTP_SOCKET_LOSTThe socket had been lost.
HTTP_ADDRESS_INVALIDThe address given to IHTTPClient::Connect() was invalid.
HTTP_SOCKET_ERRORAn unexpected socket error occurred.
HTTP_CONNECT_FAILEDCreating the connection failed.
HTTP_BYTE_STREAM_BROKENThere was an error inside the byte stream while authenticating or receiving data.
HTTP_UNHANDLED_HTTP_RESULTAn not expected HTTP result had been returned by the server.
HTTP_FAILUREEn error had been found in the response http header, sent by the server.
HTTP_AUTHENTICATION_FAILEDThe authentication failed (e. G. because of wrong username / password or an unsupported authentication method).
HTTP_NOT_FOUNDThe requested resource for the request could not be found on the server.
HTTP_BAD_REQUESTThe request was bad or malformed (e. g. a custom header field was wrong).
HTTP_INTERNAL_SERVER_ERRORAn internal error occurred on the server side.
HTTP_CONNECTION_REFUSEDThe server refused the connection.
HTTP_SHUTDOWN_TIMEOUTThe 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_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_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_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.

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;
}