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 recvPartitial = 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 http_result_t GetHTTPResult();
    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();
};

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 callbakcs 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 (wich can be an IPv4 or IPv6 address, as well asn 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 autentications 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 occurres, 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 wiht 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 ben 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 recvPartitial (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 readPartitial 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 chunck 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 nescessary for the asynchronous kind of communication used by the IHTTPClient, continous send calles should be made after receiving a UHTTPClient::HTTPClientSendResult() callback.
Shutdown
Closes the HTTP conenction. 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 restablished by calling Reconnect() using the same optiones (request type, server address, ...) as given for the first request.

Callbacks

On success, UTHHPClient::HTTPClientConnectComplete() will be called. If an error occurres, 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 it useful to not closed the error and pass it the 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 occurres, UHTTPClient::HTTPClientReqeustComplete() will be called anyway.

Remarks

Even if PassErrorToUser() can theoreticaly called multiple times, the better way will be to call it once giving a bit filed of flags, cobined using or. Valid flags ar 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 conent (if a POST request need to be send). This helps to prevend 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 autnehtication, too.

Remarks

The default is, that no content will be send for authentication.
SetRequestType
Befor 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 datatype (aka mime-type) must be defined as well as the content length. If the content length is unkown, HTTP_CLIENT_CHUNKED_TRANSFER can be used to enabled chuncked transfer of the data.

Parameters

http_request_type_t reqTypeThe request type - must be HTTP_GET, HTTP_POST or HTTP_PUT.
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).
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.
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 chuncked 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 intepreted as follows:
GetHeaderFieldValueCount
A headerfield 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.

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.
HTTPClientSendResult
Will be called after the Send() had been completed, which means that the header and an eventuell given 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 thay maybe 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 completley 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.
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_request_type_t;

This enum defines the request types supported by the IHTTPClient. One of them must be given to IHTTP::SetRequestType(). The supported request types by the IHTTPClient are GET, POST and PUT.

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_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 occured.
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 occured on the server side.

http_result_t

typedef enum {
    HTTP_RESULT_OK,
    HTTP_RESULT_NOT_FOUND,
    HTTP_RESULT_BAD_REQUEST,
    HTTP_RESULT_INTERNAL_SERVER_ERROR
} 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_INTERNAL_SERVER_ERROR(HTTP 500) An internal error occurred on the server.

http_error_t

typedef enum {
    HTTP_CL_ERR_NOT_FOUND           = 0x0001,
    HTTP_CL_ERR_BAD_REQUEST         = 0x0002,
    HTTP_CL_ERR_INTERNAL_SERVER     = 0x0004
} 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 bitfield 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 500) An internal error occurred on the server.

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