# http_client

IHTTPClient is simple HTTP client.

## File information

File | common/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 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;
    }