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