HTTP(s) Client

概述

Http(s) client模块为http/https客户端组件,为用户提供一组简洁的调用接口。

接口定义

主要类型定义

/**
 * @brief HTTP configuration
 */
typedef struct {
    const char                  *url;                       /*!< HTTP URL, the information on the URL is most important, it overrides the other fields below, if any */
    const char                  *host;                      /*!< Domain or IP as string */
    int                         port;                       /*!< Port to connect, default depend on http_client_transport_t (80 or 443) */
    const char                  *username;                  /*!< Using for Http authentication */
    const char                  *password;                  /*!< Using for Http authentication */
    http_client_auth_type_t     auth_type;                  /*!< Http authentication type, see `http_client_auth_type_t` */
    const char                  *path;                      /*!< HTTP Path, if not set, default is `/` */
    const char                  *query;                     /*!< HTTP query */
    const char                  *cert_pem;                  /*!< SSL server certification, PEM format as string, if the client requires to verify server */
    const char                  *client_cert_pem;           /*!< SSL client certification, PEM format as string, if the server requires to verify client */
    const char                  *client_key_pem;            /*!< SSL client key, PEM format as string, if the server requires to verify client */
    http_client_method_t        method;                     /*!< HTTP Method */
    int                         timeout_ms;                 /*!< Network timeout in milliseconds */
    bool                        disable_auto_redirect;      /*!< Disable HTTP automatic redirects */
    int                         max_redirection_count;      /*!< Max redirection number, using default value if zero*/
    http_event_handle_cb        event_handler;              /*!< HTTP Event Handle */
    http_client_transport_t     transport_type;             /*!< HTTP transport type, see `http_client_transport_t` */
    int                         buffer_size;                /*!< HTTP buffer size (both send and receive) */
    void                        *user_data;                 /*!< HTTP user_data context */
    bool                        is_async;                   /*!< Set asynchronous mode, only supported with HTTPS for now */
    bool                        use_global_ca_store;        /*!< Use a global ca_store for all the connections in which this bool is set. */
} http_client_config_t;

HTTP(s) Client初始化及销毁

http_client_handle_t http_client_init(const http_client_config_t *config);
  • 参数
    • config: http配置,详见http_client_config_t定义
  • 返回值:
    • 非空:成功
    • 空:失败
http_errors_t http_client_cleanup(http_client_handle_t client);
  • 参数:
    • client: http_client_init返回的句柄
  • 返回值:
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

设置HTTP(s)请求链接

web_err_t http_client_set_url(http_client_handle_t client, const char *url)
  • 参数:
    • client: http_client_init返回的handler
    • url: HTTP请求链接
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

设置HTTP(s)请求方法

支持的HTTP方法包括 GET/POST/PUT/PATCH/DELETE/HEAD/NOTIFY/SUBSCRIBE/UNSUBSCRIBE/OPTIONS。

http_errors_t http_client_set_method(http_client_handle_t client, http_client_method_t method);
  • 参数:
    • client: http_client_init返回的handler
    • method: HTTP请求方法
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

设置HTTP POST请求数据

http_errors_t http_client_set_post_field(http_client_handle_t client, const char *data, int len);
  • 参数:
    • client: http_client_init返回的handler
    • data: POST数据指针
    • len: POST数据长度
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

获取HTTP POST请求数据

int http_client_get_post_field(http_client_handle_t client, char **data);
  • 参数:
    • client: http_client_init返回的handler
    • data: POST数据指针
  • 返回
    • POST数据长度: 成功
    • 其他值: 失败

设置HTTP(s)请求头

http_errors_t http_client_set_header(http_client_handle_t client, const char *key, const char *value);
  • 参数:
    • client: http_client_init返回的handler
    • key: 请求头的key
    • value: 请求头的值
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

获取HTTP(s)请求头

http_errors_t http_client_get_header(http_client_handle_t client, const char *key, char **value);
  • 参数:
    • client: http_client_init返回的handler
    • key: 请求头的key
    • value: 请求头的值
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

获取HTTP(s)请求的用户名

http_errors_t http_client_get_username(http_client_handle_t client, char **value);
  • 参数:
    • client: http_client_init返回的handler
    • value: 用户名
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

获取HTTP(s)请求的密码

http_errors_t http_client_get_password(http_client_handle_t client, char **value);
  • 参数:
    • client: http_client_init返回的handler
    • value: 密码
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

删除HTTP(s)请求头

http_errors_t http_client_delete_header(http_client_handle_t client, const char *key);
  • 参数:
    • client: http_client_init返回的handler
    • key: 请求头的key
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

获取HTTP(s)传输类型

http_client_transport_t http_client_get_transport_type(http_client_handle_t client);
  • 参数:
    • client: http_client_init返回的handler
  • 返回
    • HTTP_TRANSPORT_UNKNOWN: 失败
    • HTTP_TRANSPORT_OVER_TCP: 成功
    • HTTP_TRANSPORT_OVER_SSL: 成功

获取HTTP(s)重置请求链接

http_errors_t http_client_set_redirection(http_client_handle_t client);
  • 参数:
    • client: http_client_init返回的handler
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

检查HTTP(s)响应的数据

http_errors_t http_client_set_redirection(http_client_handle_t client);

检查响应中的全部数据是否已被读取且没有任何错误

  • 参数:
    • client: http_client_init返回的handler
  • 返回
    • true: 成功
    • false: 失败

HTTP(s)读取更多的数据块

int http_client_read_response(http_client_handle_t client, char *buffer, int len);

这是一个辅助 API,它在内部多次调用 http_client_read,直到到达数据末尾或缓冲区已满

  • 参数:
    • client: http_client_init返回的handler
    • buffer: buffer 缓冲区
    • len: 缓冲区长度
  • 返回
    • 读取数据的长度: 成功
    • 其它值: 失败

执行HTTP(s)交互

在调用http_client_init之后,即可使用http_client_perform进行一套完整的HTTP交互,交互过程中会依次执行open->write->fetch headers->read->close的操作。用户可通过http_client_config_t结构体中的event_handler回调获取执行状态及数据。

http_errors_t http_client_perform(http_client_handle_t client);
  • 参数:
    • client: http_client_init返回的handler
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

打开及关闭HTTP(s)连接

http_errors_t http_client_open(http_client_handle_t client, int write_len);
  • 参数:
    • client: http_client_init返回的handler
    • write_len: HTTP发送长度
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败
http_errors_t http_client_close(http_client_handle_t client);
  • 参数:
    • client: http_client_init返回的handler
  • 返回
    • HTTP_CLI_OK: 成功
    • 非HTTP_CLI_OK: 失败

写HTTP(s)数据

int http_client_write(http_client_handle_t client, const char *buffer, int len);
  • 参数:
    • client: http_client_init返回的handler
    • buffer: 写数据指针
    • len: 写数据长度,此长度不应超过http_client_open中给定的长度
  • 返回
    • 实际写长度
    • -1: 失败

获取HTTP(s)头

int http_client_fetch_headers(http_client_handle_t client);
  • 参数:
    • client: http_client_init返回的handler
  • 返回
    • 0: 无content-length字段,或者为分块应答(通过http_client_is_chunked检查)
    • -1: 失败

检查HTTP(s)应答是否分块

bool http_client_is_chunked_response(http_client_handle_t client);
  • 参数:
    • client: http_client_init返回的handler
  • 返回
    • true: 分块
    • false: 不分块

读取HTTP(s)数据

int http_client_read(http_client_handle_t client, char *buffer, int len);
  • 参数:
    • client: http_client_init返回的handler
    • buffer: 数据buffer指针
    • len: 数据buffer长度
  • 返回
    • 实际度长度
    • -1: 失败

获取HTTP(s)应答状态码

int http_client_get_status_code(http_client_handle_t client);
  • 参数:
    • client: http_client_init返回的handler
  • 返回
    • 应答状态码

获取HTTP(s)应答数据长度

int http_client_get_content_length(http_client_handle_t client);
  • 参数:
    • client: http_client_init返回的handler
  • 返回
    • 应答数据长度
    • -1: 分块应答

HTTPS配置

HTTP(s) client组件同时支持HTTP及HTTPS请求,两者接口调用方式及流程完全一样。模块会通过请求的URL协议头自动匹配HTTP/HTTPS请求操作。

但为了节省硬件资源,该模块没有内置所有的CA机构根证书,需要由用户在http_client_init配置参数中设置,如下:

static char ca_crt_rsa[] = {
"-----BEGIN CERTIFICATE-----\r\n"
"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n"
"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n"
"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n"
"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n"
"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n"
"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n"
"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n"
"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n"
"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n"
"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n"
"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n"
"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n"
"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n"
"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n"
"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n"
"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n"
"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n"
"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n"
"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n"
"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n"
"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n"
"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n"
"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n"
"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n"
"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n"
"-----END CERTIFICATE-----\r\n"
" "
};

http_client_config_t config = {
    .url = "https://www.howsmyssl.com",
    .event_handler = _http_event_handler,
    .cert_pem = ca_crt_rsa,
};
http_client_handle_t client = http_client_init(&config);

根证书获取方式

每个网站的CA认证机构不同,所以需要事先取得其对应的根证书。可以通过Linux中的openssl程序获取,以百度为例,获取命令如下:

openssl s_client -showcerts -connect www.baidu.com:443 -servername www.baidu.com

其执行结果如下,其中第二个GlobalSign Root CA证书为我们所需要的CA根证书。将其完整拷贝下来(包括“-----BEGIN CERTIFICATE-----”及“-----END CERTIFICATE-----”),放入程序中的证书字符串中即可。

yohn@L:/mnt/$ openssl s_client -showcerts -connect www.baidu.com:443 -servername www.baidu.com
CONNECTED(00000003)
depth=2 C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
verify return:1
depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign Organization Validation CA - SHA256 - G2
verify return:1
depth=0 C = CN, ST = beijing, L = beijing, OU = service operation department, O = "Beijing Baidu Netcom Science Technology Co., Ltd", CN = baidu.com
verify return:1
---
Certificate chain
 0 s:/C=CN/ST=beijing/L=beijing/OU=service operation department/O=Beijing Baidu Netcom Science Technology Co., Ltd/CN=baidu.com
   i:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
-----BEGIN CERTIFICATE-----
MIIJrzCCCJegAwIBAgIMLO4ZPBiCeOo+Q3VzMA0GCSqGSIb3DQEBCwUAMGYxCzAJ
BgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTwwOgYDVQQDEzNH
....
....
XVdqyUHEJSsyGKpiqB5JgXMcgV9e+uSUMsNQbY6qzGxMUwz6j040eZ+lYMD4UHW4
oZ0B5qslIww7JAJAWCT/NAKLlGEQaC+2gOPQX0oKpwLSwJg+HegCyCdxJrKoh7bb
nRBHS8ITYjTG0Dw5CTklj/6i9PP735snPfzQKOht3N0X0x8=
-----END CERTIFICATE-----
 1 s:/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Organization Validation CA - SHA256 - G2
   i:/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA
-----BEGIN CERTIFICATE-----
MIIEaTCCA1GgAwIBAgILBAAAAAABRE7wQkcwDQYJKoZIhvcNAQELBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
....
....
30JAZGSGvip2CTFvHST0mdCF/vIhCPnG9vHQWe3WVjwIKANnuvD58ZAWR65n5ryA
SOlCdjSXVWkkDoPWoC209fN5ikkodBpBocLTJIg1MGCUF7ThBCIxPTsvFwayuJ2G
K1pp74P1S8SqtCr4fKGxhZSM9AyHDPSsQPhZSZg=
-----END CERTIFICATE-----
....
....

使用示例

头文件:

#include <http_client.h>

使用http_client_perform进行HTTPS交互:

void https_with_url()
{
    http_client_config_t config = {
        .url = "https://www.howsmyssl.com",
        .event_handler = _http_event_handler,
        .cert_pem = ca_crt_rsa,
    };
    http_client_handle_t client = http_client_init(&config);
    http_errors_t err = http_client_perform(client);

    if (err == HTTP_CLI_OK) {
        LOGI(TAG, "HTTPS Status = %d, content_length = %d \r\n",
                http_client_get_status_code(client),
                http_client_get_content_length(client));
    } else {
        LOGE(TAG, "Error perform http request 0x%x @#@@@@@@", (err));
        e_count ++;
    }
    http_client_cleanup(client);
}

用户通过event_handler获取事件及HTTP应答数据:

int _http_event_handler(http_client_event_t *evt)
{
    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:
            LOGD(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            if (!http_client_is_chunked_response(evt->client)) {
                // Write out data
                // printf("%.*s", evt->data_len, (char*)evt->data);
            }

            break;
        case HTTP_EVENT_ON_FINISH:
            LOGD(TAG, "HTTP_EVENT_ON_FINISH");
            break;
        case HTTP_EVENT_DISCONNECTED:
            LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
            break;
    }
    return 0;
}

results matching ""

    No results matching ""