Add esp_http_client

Add error handling for http client

set ssid password correct with Example_WIFI test, and clear password before free

Fixed the CI failure due to HTTP errror names
This commit is contained in:
Tuan PM 2017-11-14 10:16:20 +07:00
parent 9c53b599b2
commit ff528d13c7
30 changed files with 3915 additions and 0 deletions

View File

@ -10,6 +10,9 @@
#if __has_include("esp_err.h")
#include "esp_err.h"
#endif
#if __has_include("esp_http_client.h")
#include "esp_http_client.h"
#endif
#if __has_include("esp_image_format.h")
#include "esp_image_format.h"
#endif
@ -397,6 +400,26 @@ static const esp_err_msg_t esp_err_msg_table[] = {
# endif
# ifdef ESP_ERR_PING_NO_MEM
ERR_TBL_IT(ESP_ERR_PING_NO_MEM), /* 24578 0x6002 */
# endif
// components/esp_http_client/include/esp_http_client.h
# ifdef ESP_ERR_HTTP_BASE
ERR_TBL_IT(ESP_ERR_HTTP_BASE), /* 28672 0x7000 Starting number of HTTP error codes */
# endif
# ifdef ESP_ERR_HTTP_MAX_REDIRECT
ERR_TBL_IT(ESP_ERR_HTTP_MAX_REDIRECT), /* 28673 0x7001 The error exceeds the number of HTTP redirects */
# endif
# ifdef ESP_ERR_HTTP_CONNECT
ERR_TBL_IT(ESP_ERR_HTTP_CONNECT), /* 28674 0x7002 Error open the HTTP connection */
# endif
# ifdef ESP_ERR_HTTP_WRITE_DATA
ERR_TBL_IT(ESP_ERR_HTTP_WRITE_DATA), /* 28675 0x7003 Error write HTTP data */
# endif
# ifdef ESP_ERR_HTTP_FETCH_HEADER
ERR_TBL_IT(ESP_ERR_HTTP_FETCH_HEADER), /* 28676 0x7004 Error read HTTP header from server */
# endif
# ifdef ESP_ERR_HTTP_INVALID_TRANSPORT
ERR_TBL_IT(ESP_ERR_HTTP_INVALID_TRANSPORT), /* 28677 0x7005 There are no transport support for the input
scheme */
# endif
// components/spi_flash/include/esp_spi_flash.h
# ifdef ESP_ERR_FLASH_BASE

View File

@ -0,0 +1,10 @@
menu "ESP HTTP client"
config ESP_HTTP_CLIENT_ENABLE_HTTPS
bool "Enable https"
default y
help
This option will enable https protocol by linking mbedtls library and initializing SSL transport
endmenu

View File

@ -0,0 +1,6 @@
#
# Component Makefile
#
COMPONENT_SRCDIRS := . lib
COMPONENT_PRIV_INCLUDEDIRS := lib/include

View File

@ -0,0 +1,972 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include "esp_system.h"
#include "esp_log.h"
#include "http_header.h"
#include "transport.h"
#include "transport_tcp.h"
#include "http_utils.h"
#include "http_auth.h"
#include "sdkconfig.h"
#include "transport.h"
#include "esp_http_client.h"
#ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS
#include "transport_ssl.h"
#endif
static const char *TAG = "HTTP_CLIENT";
typedef struct {
char *data;
int len;
char *raw_data;
int raw_len;
} esp_http_buffer_t;
/**
* private HTTP Data structure
*/
typedef struct {
http_header_handle_t headers; /*!< http header */
esp_http_buffer_t *buffer; /*!< data buffer as linked list */
int status_code; /*!< status code (integer) */
int content_length; /*!< data length */
int data_offset; /*!< offset to http data (Skip header) */
int data_process; /*!< data processed */
int method; /*!< http method */
bool is_chunked;
} esp_http_data_t;
typedef struct {
char *url;
char *scheme;
char *host;
int port;
char *username;
char *password;
char *path;
char *query;
char *cert_pem;
esp_http_client_method_t method;
esp_http_client_auth_type_t auth_type;
esp_http_client_transport_t transport_type;
int max_store_header_size;
} connection_info_t;
typedef enum {
HTTP_STATE_UNINIT = 0,
HTTP_STATE_INIT,
HTTP_STATE_CONNECTED,
HTTP_STATE_REQ_COMPLETE_HEADER,
HTTP_STATE_REQ_COMPLETE_DATA,
HTTP_STATE_RES_COMPLETE_HEADER,
HTTP_STATE_RES_COMPLETE_DATA,
HTTP_STATE_CLOSE
} esp_http_state_t;
/**
* HTTP client class
*/
struct esp_http_client {
int redirect_counter;
int max_redirection_count;
int process_again;
struct http_parser *parser;
struct http_parser_settings *parser_settings;
transport_list_handle_t transport_list;
transport_handle_t transport;
esp_http_data_t *request;
esp_http_data_t *response;
void *user_data;
esp_http_auth_data_t *auth_data;
char *post_data;
char *location;
char *auth_header;
char *current_header_key;
char *current_header_value;
int post_len;
connection_info_t connection_info;
bool is_chunk_complete;
esp_http_state_t state;
http_event_handle_cb event_handler;
int timeout_ms;
int buffer_size;
bool disable_auto_redirect;
esp_http_client_event_t event;
};
typedef struct esp_http_client esp_http_client_t;
static esp_err_t _clear_connection_info(esp_http_client_handle_t client);
/**
* Default settings
*/
#define DEFAULT_HTTP_PORT (80)
#define DEFAULT_HTTPS_PORT (443)
static const char *DEFAULT_HTTP_USER_AGENT = "ESP32 HTTP Client/1.0";
static const char *DEFAULT_HTTP_PROTOCOL = "HTTP/1.1";
static const char *DEFAULT_HTTP_PATH = "/";
static int DEFAULT_MAX_REDIRECT = 10;
static int DEFAULT_TIMEOUT_MS = 5000;
static const char *HTTP_METHOD_MAPPING[] = {
"GET",
"POST",
"PUT",
"PATCH",
"DELETE"
};
static esp_err_t http_dispatch_event(esp_http_client_t *client, esp_http_client_event_id_t event_id, void *data, int len)
{
esp_http_client_event_t *event = &client->event;
if (client->event_handler) {
event->event_id = event_id;
event->user_data = client->user_data;
event->data = data;
event->data_len = len;
return client->event_handler(event);
}
return ESP_OK;
}
static int http_on_message_begin(http_parser *parser)
{
esp_http_client_t *client = parser->data;
ESP_LOGD(TAG, "on_message_begin");
client->response->is_chunked = false;
client->is_chunk_complete = false;
return 0;
}
static int http_on_url(http_parser *parser, const char *at, size_t length)
{
ESP_LOGD(TAG, "http_on_url");
return 0;
}
static int http_on_status(http_parser *parser, const char *at, size_t length)
{
return 0;
}
static int http_on_header_field(http_parser *parser, const char *at, size_t length)
{
esp_http_client_t *client = parser->data;
http_utils_assign_string(&client->current_header_key, at, length);
return 0;
}
static int http_on_header_value(http_parser *parser, const char *at, size_t length)
{
esp_http_client_handle_t client = parser->data;
if (client->current_header_key == NULL) {
return 0;
}
if (strcasecmp(client->current_header_key, "Location") == 0) {
http_utils_assign_string(&client->location, at, length);
} else if (strcasecmp(client->current_header_key, "Transfer-Encoding") == 0
&& memcmp(at, "chunked", length) == 0) {
client->response->is_chunked = true;
} else if (strcasecmp(client->current_header_key, "WWW-Authenticate") == 0) {
http_utils_assign_string(&client->auth_header, at, length);
}
http_utils_assign_string(&client->current_header_value, at, length);
ESP_LOGD(TAG, "HEADER=%s:%s", client->current_header_key, client->current_header_value);
client->event.header_key = client->current_header_key;
client->event.header_value = client->current_header_value;
http_dispatch_event(client, HTTP_EVENT_ON_HEADER, NULL, 0);
free(client->current_header_key);
free(client->current_header_value);
client->current_header_key = NULL;
client->current_header_value = NULL;
return 0;
}
static int http_on_headers_complete(http_parser *parser)
{
esp_http_client_handle_t client = parser->data;
client->response->status_code = parser->status_code;
client->response->data_offset = parser->nread;
client->response->content_length = parser->content_length;
client->response->data_process = 0;
ESP_LOGD(TAG, "http_on_headers_complete, status=%d, offset=%d, nread=%d", parser->status_code, client->response->data_offset, parser->nread);
client->state = HTTP_STATE_RES_COMPLETE_HEADER;
return 0;
}
static int http_on_body(http_parser *parser, const char *at, size_t length)
{
esp_http_client_t *client = parser->data;
ESP_LOGD(TAG, "http_on_body %d", length);
client->response->buffer->raw_data = (char*)at;
client->response->buffer->raw_len = length;
client->response->data_process += length;
http_dispatch_event(client, HTTP_EVENT_ON_DATA, (void *)at, length);
return 0;
}
static int http_on_message_complete(http_parser *parser)
{
ESP_LOGD(TAG, "http_on_message_complete, parser=%x", (int)parser);
esp_http_client_handle_t client = parser->data;
client->is_chunk_complete = true;
return 0;
}
static int http_on_chunk_complete(http_parser *parser)
{
ESP_LOGD(TAG, "http_on_chunk_complete");
return 0;
}
esp_err_t esp_http_client_set_header(esp_http_client_handle_t client, const char *key, const char *value)
{
return http_header_set(client->request->headers, key, value);
}
esp_err_t esp_http_client_delete_header(esp_http_client_handle_t client, const char *key)
{
return http_header_delete(client->request->headers, key);
}
static esp_err_t _set_config(esp_http_client_handle_t client, esp_http_client_config_t *config)
{
client->connection_info.method = config->method;
client->connection_info.port = config->port;
client->connection_info.auth_type = config->auth_type;
client->event_handler = config->event_handler;
client->timeout_ms = config->timeout_ms;
client->max_redirection_count = config->max_redirection_count;
client->user_data = config->user_data;
client->buffer_size = config->buffer_size;
client->disable_auto_redirect = config->disable_auto_redirect;
if (config->buffer_size == 0) {
client->buffer_size = DEFAULT_HTTP_BUF_SIZE;
}
if (client->max_redirection_count == 0) {
client->max_redirection_count = DEFAULT_MAX_REDIRECT;
}
if (config->path) {
client->connection_info.path = strdup(config->path);
} else {
client->connection_info.path = strdup(DEFAULT_HTTP_PATH);
}
HTTP_MEM_CHECK(TAG, client->connection_info.path, {
return ESP_ERR_NO_MEM;
});
if (config->host) {
client->connection_info.host = strdup(config->host);
HTTP_MEM_CHECK(TAG, client->connection_info.host, {
_clear_connection_info(client);
return ESP_ERR_NO_MEM;
});
}
if (config->query) {
client->connection_info.query = strdup(config->query);
HTTP_MEM_CHECK(TAG, client->connection_info.query, {
_clear_connection_info(client);
return ESP_ERR_NO_MEM;
});
}
if (config->username) {
client->connection_info.username = strdup(config->username);
HTTP_MEM_CHECK(TAG, client->connection_info.username, {
_clear_connection_info(client);
return ESP_ERR_NO_MEM;
});
}
if (config->password) {
client->connection_info.password = strdup(config->password);
HTTP_MEM_CHECK(TAG, client->connection_info.password, {
_clear_connection_info(client);
return ESP_ERR_NO_MEM;
});
}
if (config->transport_type == HTTP_TRANSPORT_OVER_SSL) {
http_utils_assign_string(&client->connection_info.scheme, "https", 0);
if (client->connection_info.port == 0) {
client->connection_info.port = DEFAULT_HTTPS_PORT;
}
} else {
http_utils_assign_string(&client->connection_info.scheme, "http", 0);
if (client->connection_info.port == 0) {
client->connection_info.port = DEFAULT_HTTP_PORT;
}
}
if (client->timeout_ms == 0) {
client->timeout_ms = DEFAULT_TIMEOUT_MS;
}
return ESP_OK;
}
static esp_err_t _clear_connection_info(esp_http_client_handle_t client)
{
free(client->connection_info.path);
free(client->connection_info.host);
free(client->connection_info.query);
free(client->connection_info.username);
if (client->connection_info.password) {
memset(client->connection_info.password, 0, strlen(client->connection_info.password));
free(client->connection_info.password);
}
free(client->connection_info.scheme);
free(client->connection_info.url);
memset(&client->connection_info, 0, sizeof(connection_info_t));
return ESP_OK;
}
static esp_err_t _clear_auth_data(esp_http_client_handle_t client)
{
if (client->auth_data == NULL) {
return ESP_FAIL;
}
free(client->auth_data->method);
free(client->auth_data->realm);
free(client->auth_data->algorithm);
free(client->auth_data->qop);
free(client->auth_data->nonce);
free(client->auth_data->opaque);
memset(client->auth_data, 0, sizeof(esp_http_auth_data_t));
return ESP_OK;
}
static esp_err_t esp_http_client_prepare(esp_http_client_handle_t client)
{
client->process_again = 0;
client->response->data_process = 0;
http_parser_init(client->parser, HTTP_RESPONSE);
if (client->connection_info.username) {
char *auth_response = NULL;
if (client->connection_info.auth_type == HTTP_AUTH_TYPE_BASIC) {
auth_response = http_auth_basic(client->connection_info.username, client->connection_info.password);
} else if (client->connection_info.auth_type == HTTP_AUTH_TYPE_DIGEST && client->auth_data) {
client->auth_data->uri = client->connection_info.path;
client->auth_data->cnonce = ((uint64_t)esp_random() << 32) + esp_random();
auth_response = http_auth_digest(client->connection_info.username, client->connection_info.password, client->auth_data);
client->auth_data->nc ++;
}
if (auth_response) {
ESP_LOGD(TAG, "auth_response=%s", auth_response);
esp_http_client_set_header(client, "Authorization", auth_response);
free(auth_response);
}
}
return ESP_OK;
}
esp_http_client_handle_t esp_http_client_init(esp_http_client_config_t *config)
{
esp_http_client_handle_t client;
transport_handle_t tcp;
bool _success;
_success = (
(client = calloc(1, sizeof(esp_http_client_t))) &&
(client->parser = calloc(1, sizeof(struct http_parser))) &&
(client->parser_settings = calloc(1, sizeof(struct http_parser_settings))) &&
(client->auth_data = calloc(1, sizeof(esp_http_auth_data_t))) &&
(client->request = calloc(1, sizeof(esp_http_data_t))) &&
(client->request->headers = http_header_init()) &&
(client->request->buffer = calloc(1, sizeof(esp_http_buffer_t))) &&
(client->response = calloc(1, sizeof(esp_http_data_t))) &&
(client->response->headers = http_header_init()) &&
(client->response->buffer = calloc(1, sizeof(esp_http_buffer_t)))
);
if (!_success) {
ESP_LOGE(TAG, "Error allocate memory");
esp_http_client_cleanup(client);
return NULL;
}
_success = (
(client->transport_list = transport_list_init()) &&
(tcp = transport_tcp_init()) &&
(transport_set_default_port(tcp, DEFAULT_HTTP_PORT) == ESP_OK) &&
(transport_list_add(client->transport_list, tcp, "http") == ESP_OK)
);
if (!_success) {
ESP_LOGE(TAG, "Error initialize transport");
esp_http_client_cleanup(client);
return NULL;
}
#ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS
transport_handle_t ssl;
_success = (
(ssl = transport_ssl_init()) &&
(transport_set_default_port(ssl, DEFAULT_HTTPS_PORT) == ESP_OK) &&
(transport_list_add(client->transport_list, ssl, "https") == ESP_OK)
);
if (!_success) {
ESP_LOGE(TAG, "Error initialize SSL Transport");
esp_http_client_cleanup(client);
return NULL;
}
if (config->cert_pem) {
transport_ssl_set_cert_data(ssl, config->cert_pem, strlen(config->cert_pem));
}
#endif
if (_set_config(client, config) != ESP_OK) {
ESP_LOGE(TAG, "Error set configurations");
esp_http_client_cleanup(client);
return NULL;
}
_success = (
(client->request->buffer->data = malloc(client->buffer_size)) &&
(client->response->buffer->data = malloc(client->buffer_size))
);
if (!_success) {
ESP_LOGE(TAG, "Allocation failed");
esp_http_client_cleanup(client);
return NULL;
}
_success = (
(esp_http_client_set_url(client, config->url) == ESP_OK) &&
(esp_http_client_set_header(client, "User-Agent", DEFAULT_HTTP_USER_AGENT) == ESP_OK) &&
(esp_http_client_set_header(client, "Host", client->connection_info.host) == ESP_OK)
);
if (!_success) {
ESP_LOGE(TAG, "Error set default configurations");
esp_http_client_cleanup(client);
return NULL;
}
client->parser_settings->on_message_begin = http_on_message_begin;
client->parser_settings->on_url = http_on_url;
client->parser_settings->on_status = http_on_status;
client->parser_settings->on_header_field = http_on_header_field;
client->parser_settings->on_header_value = http_on_header_value;
client->parser_settings->on_headers_complete = http_on_headers_complete;
client->parser_settings->on_body = http_on_body;
client->parser_settings->on_message_complete = http_on_message_complete;
client->parser_settings->on_chunk_complete = http_on_chunk_complete;
client->parser->data = client;
client->event.client = client;
client->state = HTTP_STATE_INIT;
return client;
}
esp_err_t esp_http_client_cleanup(esp_http_client_handle_t client)
{
if (client == NULL) {
return ESP_FAIL;
}
esp_http_client_close(client);
transport_list_destroy(client->transport_list);
http_header_destroy(client->request->headers);
free(client->request->buffer->data);
free(client->request->buffer);
free(client->request);
http_header_destroy(client->response->headers);
free(client->response->buffer->data);
free(client->response->buffer);
free(client->response);
free(client->parser);
free(client->parser_settings);
_clear_connection_info(client);
_clear_auth_data(client);
free(client->auth_data);
free(client->current_header_key);
free(client->location);
free(client->auth_header);
free(client);
return ESP_OK;
}
static esp_err_t esp_http_check_response(esp_http_client_handle_t client)
{
char *auth_header = NULL;
if (client->redirect_counter >= client->max_redirection_count || client->disable_auto_redirect) {
ESP_LOGE(TAG, "Error, reach max_redirection_count count=%d", client->redirect_counter);
return ESP_ERR_HTTP_MAX_REDIRECT;
}
switch (client->response->status_code) {
case 301:
case 302:
ESP_LOGI(TAG, "Redirect to %s", client->location);
esp_http_client_set_url(client, client->location);
client->redirect_counter ++;
client->process_again = 1;
break;
case 401:
auth_header = client->auth_header;
http_utils_trim_whitespace(&auth_header);
ESP_LOGI(TAG, "UNAUTHORIZED: %s", auth_header);
client->redirect_counter ++;
if (auth_header) {
if (http_utils_str_starts_with(auth_header, "Digest") == 0) {
ESP_LOGD(TAG, "type = Digest");
client->connection_info.auth_type = HTTP_AUTH_TYPE_DIGEST;
} else if (http_utils_str_starts_with(auth_header, "Basic") == 0) {
ESP_LOGD(TAG, "type = Basic");
client->connection_info.auth_type = HTTP_AUTH_TYPE_BASIC;
} else {
client->connection_info.auth_type = HTTP_AUTH_TYPE_NONE;
ESP_LOGE(TAG, "Unsupport Auth Type");
break;
}
_clear_auth_data(client);
client->auth_data->method = strdup(HTTP_METHOD_MAPPING[client->connection_info.method]);
client->auth_data->nc = 1;
client->auth_data->realm = http_utils_get_string_between(auth_header, "realm=\"", "\"");
client->auth_data->algorithm = http_utils_get_string_between(auth_header, "algorithm=", ",");
client->auth_data->qop = http_utils_get_string_between(auth_header, "qop=\"", "\"");
client->auth_data->nonce = http_utils_get_string_between(auth_header, "nonce=\"", "\"");
client->auth_data->opaque = http_utils_get_string_between(auth_header, "opaque=\"", "\"");
client->process_again = 1;
}
}
return ESP_OK;
}
esp_err_t esp_http_client_set_url(esp_http_client_handle_t client, const char *url)
{
char *old_host = NULL;
struct http_parser_url purl;
int old_port;
if (client == NULL || url == NULL) {
ESP_LOGE(TAG, "client or url must not NULL");
return ESP_ERR_INVALID_ARG;
}
http_parser_url_init(&purl);
int parser_status = http_parser_parse_url(url, strlen(url), 0, &purl);
if (parser_status != 0) {
ESP_LOGE(TAG, "Error parse url %s", url);
return ESP_ERR_INVALID_ARG;
}
old_host = client->connection_info.host;
old_port = client->connection_info.port;
if (purl.field_data[UF_HOST].len) {
http_utils_assign_string(&client->connection_info.host, url + purl.field_data[UF_HOST].off, purl.field_data[UF_HOST].len);
HTTP_MEM_CHECK(TAG, client->connection_info.host, return ESP_ERR_NO_MEM);
}
// Close the connection if host was changed
if (old_host && client->connection_info.host
&& strcasecmp(old_host, (const void *)client->connection_info.host) != 0) {
ESP_LOGD(TAG, "New host assign = %s", client->connection_info.host);
if (esp_http_client_set_header(client, "Host", client->connection_info.host) != ESP_OK) {
return ESP_ERR_NO_MEM;
}
esp_http_client_close(client);
}
if (purl.field_data[UF_SCHEMA].len) {
http_utils_assign_string(&client->connection_info.scheme, url + purl.field_data[UF_SCHEMA].off, purl.field_data[UF_SCHEMA].len);
HTTP_MEM_CHECK(TAG, client->connection_info.scheme, return ESP_ERR_NO_MEM);
if (strcasecmp(client->connection_info.scheme, "http") == 0) {
client->connection_info.port = DEFAULT_HTTP_PORT;
} else if (strcasecmp(client->connection_info.scheme, "https") == 0) {
client->connection_info.port = DEFAULT_HTTPS_PORT;
}
}
if (purl.field_data[UF_PORT].len) {
client->connection_info.port = strtol((const char*)(url + purl.field_data[UF_PORT].off), NULL, 10);
}
if (old_port != client->connection_info.port) {
esp_http_client_close(client);
}
if (purl.field_data[UF_USERINFO].len) {
char *user_info = NULL;
http_utils_assign_string(&user_info, url + purl.field_data[UF_USERINFO].off, purl.field_data[UF_USERINFO].len);
if (user_info) {
char *username = user_info;
char *password = strchr(user_info, ':');
if (password) {
*password = 0;
password ++;
http_utils_assign_string(&client->connection_info.password, password, 0);
HTTP_MEM_CHECK(TAG, client->connection_info.password, return ESP_ERR_NO_MEM);
}
http_utils_assign_string(&client->connection_info.username, username, 0);
HTTP_MEM_CHECK(TAG, client->connection_info.username, return ESP_ERR_NO_MEM);
free(user_info);
} else {
return ESP_ERR_NO_MEM;
}
} else {
free(client->connection_info.username);
free(client->connection_info.password);
client->connection_info.username = NULL;
client->connection_info.password = NULL;
}
//Reset path and query if there are no information
if (purl.field_data[UF_PATH].len) {
http_utils_assign_string(&client->connection_info.path, url + purl.field_data[UF_PATH].off, purl.field_data[UF_PATH].len);
} else {
http_utils_assign_string(&client->connection_info.path, "/", 0);
}
HTTP_MEM_CHECK(TAG, client->connection_info.path, return ESP_ERR_NO_MEM);
if (purl.field_data[UF_QUERY].len) {
http_utils_assign_string(&client->connection_info.query, url + purl.field_data[UF_QUERY].off, purl.field_data[UF_QUERY].len);
HTTP_MEM_CHECK(TAG, client->connection_info.query, return ESP_ERR_NO_MEM);
} else if (client->connection_info.query) {
free(client->connection_info.query);
client->connection_info.query = NULL;
}
return ESP_OK;
}
esp_err_t esp_http_client_set_method(esp_http_client_handle_t client, esp_http_client_method_t method)
{
client->connection_info.method = method;
return ESP_OK;
}
static int esp_http_client_get_data(esp_http_client_handle_t client)
{
if (client->state < HTTP_STATE_RES_COMPLETE_HEADER) {
return -1;
}
esp_http_buffer_t *res_buffer = client->response->buffer;
ESP_LOGD(TAG, "data_process=%d, content_length=%d", client->response->data_process, client->response->content_length);
int rlen = transport_read(client->transport, res_buffer->data, client->buffer_size, client->timeout_ms);
if (rlen >= 0) {
http_parser_execute(client->parser, client->parser_settings, res_buffer->data, rlen);
}
return rlen;
}
int esp_http_client_read(esp_http_client_handle_t client, char *buffer, int len)
{
esp_http_buffer_t *res_buffer = client->response->buffer;
int rlen = -1, ridx = 0;
if (res_buffer->raw_len) {
int remain_len = client->response->buffer->raw_len;
if (remain_len > len) {
remain_len = len;
}
memcpy(buffer, res_buffer->raw_data, remain_len);
res_buffer->raw_len -= remain_len;
res_buffer->raw_data += remain_len;
ridx = remain_len;
}
int need_read = len - ridx;
bool is_data_remain = true;
while (need_read > 0 && is_data_remain) {
if (client->response->is_chunked) {
is_data_remain = !client->is_chunk_complete;
} else {
is_data_remain = client->response->data_process < client->response->content_length;
}
ESP_LOGD(TAG, "is_data_remain=%d, is_chunked=%d", is_data_remain, client->response->is_chunked);
if (!is_data_remain) {
break;
}
int byte_to_read = need_read;
if (byte_to_read > client->buffer_size) {
byte_to_read = client->buffer_size;
}
rlen = transport_read(client->transport, res_buffer->data, byte_to_read, client->timeout_ms);
ESP_LOGD(TAG, "need_read=%d, byte_to_read=%d, rlen=%d, ridx=%d", need_read, byte_to_read, rlen, ridx);
if (rlen <= 0) {
return ridx;
}
http_parser_execute(client->parser, client->parser_settings, res_buffer->data, rlen);
if (res_buffer->raw_len) {
memcpy(buffer + ridx, res_buffer->raw_data, res_buffer->raw_len);
ridx += res_buffer->raw_len;
need_read -= res_buffer->raw_len;
}
res_buffer->raw_len = 0; //clear
}
return ridx;
}
esp_err_t esp_http_client_perform(esp_http_client_handle_t client)
{
esp_err_t err;
do {
if ((err = esp_http_client_open(client, client->post_len)) != ESP_OK) {
return err;
}
if (client->post_data && client->post_len) {
if (esp_http_client_write(client, client->post_data, client->post_len) <= 0) {
ESP_LOGE(TAG, "Error upload data");
return ESP_ERR_HTTP_WRITE_DATA;
}
}
if (esp_http_client_fetch_headers(client) < 0) {
return ESP_ERR_HTTP_FETCH_HEADER;
}
if ((err = esp_http_check_response(client)) != ESP_OK) {
ESP_LOGE(TAG, "Error response");
return err;
}
while (client->response->is_chunked && !client->is_chunk_complete) {
if (esp_http_client_get_data(client) <= 0) {
ESP_LOGD(TAG, "Read finish or server requests close");
break;
}
}
while (client->response->data_process < client->response->content_length) {
if (esp_http_client_get_data(client) <= 0) {
ESP_LOGD(TAG, "Read finish or server requests close");
break;
}
}
http_dispatch_event(client, HTTP_EVENT_ON_FINISH, NULL, 0);
if (!http_should_keep_alive(client->parser)) {
ESP_LOGD(TAG, "Close connection");
esp_http_client_close(client);
} else {
if (client->state > HTTP_STATE_CONNECTED) {
client->state = HTTP_STATE_CONNECTED;
}
}
} while (client->process_again);
return ESP_OK;
}
int esp_http_client_fetch_headers(esp_http_client_handle_t client)
{
if (client->state < HTTP_STATE_REQ_COMPLETE_HEADER) {
return -1;
}
client->state = HTTP_STATE_REQ_COMPLETE_DATA;
esp_http_buffer_t *buffer = client->response->buffer;
client->response->status_code = -1;
while (client->state < HTTP_STATE_RES_COMPLETE_HEADER) {
buffer->len = transport_read(client->transport, buffer->data, client->buffer_size, client->timeout_ms);
if (buffer->len <= 0) {
return -1;
}
http_parser_execute(client->parser, client->parser_settings, buffer->data, buffer->len);
}
ESP_LOGD(TAG, "content_length = %d", client->response->content_length);
if (client->response->content_length <= 0) {
client->response->is_chunked = true;
return 0;
}
return client->response->content_length;
}
esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len)
{
esp_err_t err;
if (client->state == HTTP_STATE_UNINIT) {
ESP_LOGE(TAG, "Client has not been initialized");
return ESP_ERR_INVALID_STATE;
}
if ((err = esp_http_client_prepare(client)) != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize request data");
esp_http_client_close(client);
return err;
}
if (client->state < HTTP_STATE_CONNECTED) {
ESP_LOGD(TAG, "Begin connect to: %s://%s:%d", client->connection_info.scheme, client->connection_info.host, client->connection_info.port);
client->transport = transport_list_get_transport(client->transport_list, client->connection_info.scheme);
if (client->transport == NULL) {
ESP_LOGE(TAG, "No transport found");
return ESP_ERR_HTTP_INVALID_TRANSPORT;
}
if (transport_connect(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms) < 0) {
ESP_LOGE(TAG, "Connection failed, sock < 0");
return ESP_ERR_HTTP_CONNECT;
}
http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0);
client->state = HTTP_STATE_CONNECTED;
}
if (write_len >= 0) {
http_header_set_format(client->request->headers, "Content-Length", "%d", write_len);
} else if (write_len < 0) {
esp_http_client_set_header(client, "Transfer-Encoding", "chunked");
esp_http_client_set_method(client, HTTP_METHOD_POST);
}
int header_index = 0;
int wlen = client->buffer_size;
const char *method = HTTP_METHOD_MAPPING[client->connection_info.method];
int first_line = snprintf(client->request->buffer->data,
client->buffer_size, "%s %s",
method,
client->connection_info.path);
if (first_line > client->buffer_size) {
ESP_LOGE(TAG, "Out of buffer");
return ESP_ERR_HTTP_CONNECT;
}
if (client->connection_info.query) {
first_line += snprintf(client->request->buffer->data + first_line,
client->buffer_size - first_line, "?%s", client->connection_info.query);
if (first_line > client->buffer_size) {
ESP_LOGE(TAG, "Out of buffer");
return ESP_ERR_HTTP_CONNECT;
}
}
first_line += snprintf(client->request->buffer->data + first_line,
client->buffer_size - first_line, " %s\r\n", DEFAULT_HTTP_PROTOCOL);
if (first_line > client->buffer_size) {
ESP_LOGE(TAG, "Out of buffer");
return ESP_ERR_HTTP_CONNECT;
}
wlen -= first_line;
while ((header_index = http_header_generate_string(client->request->headers, header_index, client->request->buffer->data + first_line, &wlen))) {
if (wlen <= 0) {
break;
}
if (first_line) {
wlen += first_line;
first_line = 0;
}
client->request->buffer->data[wlen] = 0;
ESP_LOGD(TAG, "Write header[%d]: %s", header_index, client->request->buffer->data);
if (transport_write(client->transport, client->request->buffer->data, wlen, client->timeout_ms) <= 0) {
ESP_LOGE(TAG, "Error write request");
esp_http_client_close(client);
return ESP_ERR_HTTP_WRITE_DATA;
}
wlen = client->buffer_size;
}
client->state = HTTP_STATE_REQ_COMPLETE_HEADER;
return ESP_OK;
}
int esp_http_client_write(esp_http_client_handle_t client, const char *buffer, int len)
{
if (client->state < HTTP_STATE_REQ_COMPLETE_HEADER) {
return -1;
}
int need_write;
int wlen = 0, widx = 0;
while (len > 0) {
need_write = len;
if (need_write > client->buffer_size) {
need_write = client->buffer_size;
}
wlen = transport_write(client->transport, buffer + widx, need_write, client->timeout_ms);
if (wlen <= 0) {
return wlen;
}
widx += wlen;
len -= wlen;
}
return widx;
}
esp_err_t esp_http_client_close(esp_http_client_handle_t client)
{
if (client->state >= HTTP_STATE_INIT) {
http_dispatch_event(client, HTTP_EVENT_DISCONNECTED, NULL, 0);
client->state = HTTP_STATE_INIT;
return transport_close(client->transport);
}
return ESP_OK;
}
esp_err_t esp_http_client_set_post_field(esp_http_client_handle_t client, const char *data, int len)
{
esp_err_t err = ESP_OK;
client->post_data = (char *)data;
client->post_len = len;
ESP_LOGD(TAG, "set post file length = %d", len);
if (client->post_data) {
err = esp_http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");
} else {
client->post_len = 0;
err = esp_http_client_set_header(client, "Content-Type", NULL);
}
return err;
}
int esp_http_client_get_post_field(esp_http_client_handle_t client, char **data)
{
if (client->post_data) {
*data = client->post_data;
return client->post_len;
}
return 0;
}
int esp_http_client_get_status_code(esp_http_client_handle_t client)
{
return client->response->status_code;
}
int esp_http_client_get_content_length(esp_http_client_handle_t client)
{
return client->response->content_length;
}
bool esp_http_client_is_chunked_response(esp_http_client_handle_t client)
{
return client->response->is_chunked;
}

View File

@ -0,0 +1,343 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _ESP_HTTP_CLIENT_H
#define _ESP_HTTP_CLIENT_H
#include "freertos/FreeRTOS.h"
#include "http_parser.h"
#include "sdkconfig.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DEFAULT_HTTP_BUF_SIZE (512)
typedef struct esp_http_client *esp_http_client_handle_t;
typedef struct esp_http_client_event *esp_http_client_event_handle_t;
/**
* @brief HTTP Client events id
*/
typedef enum {
HTTP_EVENT_ERROR = 0, /*!< This event occurs when there are any errors during execution */
HTTP_EVENT_ON_CONNECTED, /*!< Once the HTTP has been connected to the server, no data exchange has been performed */
HTTP_EVENT_HEADER_SENT, /*!< After sending all the headers to the server */
HTTP_EVENT_ON_HEADER, /*!< Occurs when receiving each header sent from the server */
HTTP_EVENT_ON_DATA, /*!< Occurs when receiving data from the server, possibly multiple portions of the packet */
HTTP_EVENT_ON_FINISH, /*!< Occurs when finish a HTTP session */
HTTP_EVENT_DISCONNECTED, /*!< The connection has been disconnected */
} esp_http_client_event_id_t;
/**
* @brief HTTP Client events data
*/
typedef struct esp_http_client_event {
esp_http_client_event_id_t event_id; /*!< event_id, to know the cause of the event */
esp_http_client_handle_t client; /*!< esp_http_client_handle_t context */
void *data; /*!< data of the event */
int data_len; /*!< data length of data */
void *user_data; /*!< user_data context, from esp_http_client_config_t user_data */
char *header_key; /*!< For HTTP_EVENT_ON_HEADER event_id, it's store current http header key */
char *header_value; /*!< For HTTP_EVENT_ON_HEADER event_id, it's store current http header value */
} esp_http_client_event_t;
/**
* @brief HTTP Client transport
*/
typedef enum {
HTTP_TRANSPORT_UNKNOWN = 0x0, /*!< Unknown */
HTTP_TRANSPORT_OVER_TCP, /*!< Transport over tcp */
HTTP_TRANSPORT_OVER_SSL, /*!< Transport over ssl */
} esp_http_client_transport_t;
typedef esp_err_t (*http_event_handle_cb)(esp_http_client_event_t *evt);
/**
* @brief HTTP method
*/
typedef enum {
HTTP_METHOD_GET = 0, /*!< HTTP GET Method */
HTTP_METHOD_POST, /*!< HTTP POST Method */
HTTP_METHOD_PUT, /*!< HTTP PUT Method */
HTTP_METHOD_PATCH, /*!< HTTP PATCH Method */
HTTP_METHOD_DELETE, /*!< HTTP DELETE Method */
HTTP_METHOD_MAX,
} esp_http_client_method_t;
/**
* @brief HTTP Authentication type
*/
typedef enum {
HTTP_AUTH_TYPE_NONE = 0, /*!< No authention */
HTTP_AUTH_TYPE_BASIC, /*!< HTTP Basic authentication */
HTTP_AUTH_TYPE_DIGEST, /*!< HTTP Disgest authentication */
} esp_http_client_auth_type_t;
/**
* @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 esp_http_client_transport_t (80 or 443) */
const char *username; /*!< Using for Http authentication */
const char *password; /*!< Using for Http authentication */
esp_http_client_auth_type_t auth_type; /*!< Http authentication type, see `esp_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 Certification, PEM format as string, if the client requires to verify server */
esp_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 */
esp_http_client_transport_t transport_type; /*!< HTTP transport type, see `esp_http_client_transport_t` */
int buffer_size; /*!< HTTP buffer size (both send and receive) */
void *user_data; /*!< HTTP user_data context */
} esp_http_client_config_t;
#define ESP_ERR_HTTP_BASE (0x7000) /*!< Starting number of HTTP error codes */
#define ESP_ERR_HTTP_MAX_REDIRECT (ESP_ERR_HTTP_BASE + 1) /*!< The error exceeds the number of HTTP redirects */
#define ESP_ERR_HTTP_CONNECT (ESP_ERR_HTTP_BASE + 2) /*!< Error open the HTTP connection */
#define ESP_ERR_HTTP_WRITE_DATA (ESP_ERR_HTTP_BASE + 3) /*!< Error write HTTP data */
#define ESP_ERR_HTTP_FETCH_HEADER (ESP_ERR_HTTP_BASE + 4) /*!< Error read HTTP header from server */
#define ESP_ERR_HTTP_INVALID_TRANSPORT (ESP_ERR_HTTP_BASE + 5) /*!< There are no transport support for the input scheme */
/**
* @brief Start a HTTP session
* This function must be the first function to call,
* and it returns a esp_http_client_handle_t that you must use as input to other functions in the interface.
* This call MUST have a corresponding call to esp_http_client_cleanup when the operation is complete.
*
* @param[in] config The configurations, see `http_client_config_t`
*
* @return
* - `esp_http_client_handle_t`
* - NULL if any errors
*/
esp_http_client_handle_t esp_http_client_init(esp_http_client_config_t *config);
/**
* @brief Invoke this function after `esp_http_client_init` and all the options calls are made, and will perform the
* transfer as described in the options. It must be called with the same esp_http_client_handle_t as input as the esp_http_client_init call returned.
* esp_http_client_perform performs the entire request in a blocking manner and returns when done, or if it failed.
* You can do any amount of calls to esp_http_client_perform while using the same esp_http_client_handle_t. The underlying connection may be kept open if the server allows it.
* If you intend to transfer more than one file, you are even encouraged to do so.
* esp_http_client will then attempt to re-use the same connection for the following transfers, thus making the operations faster, less CPU intense and using less network resources.
* Just note that you will have to use `esp_http_client_set_**` between the invokes to set options for the following esp_http_client_perform.
*
* @note You must never call this function simultaneously from two places using the same client handle.
* Let the function return first before invoking it another time.
* If you want parallel transfers, you must use several esp_http_client_handle_t.
* This function include `esp_http_client_open` -> `esp_http_client_write` -> `esp_http_client_fetch_headers` -> `esp_http_client_read` (and option) `esp_http_client_close`.
*
* @param client The esp_http_client handle
*
* @return
* - ESP_OK on successful
* - ESP_FAIL on error
*/
esp_err_t esp_http_client_perform(esp_http_client_handle_t client);
/**
* @brief Set URL for client, when performing this behavior, the options in the URL will replace the old ones
*
* @param[in] client The esp_http_client handle
* @param[in] url The url
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t esp_http_client_set_url(esp_http_client_handle_t client, const char *url);
/**
* @brief Set post data, this function must be called before `esp_http_client_finalize_open` or perform
* Note: The data parameter passed to this function is a pointer and this function will not copy the data
*
* @param[in] client The esp_http_client handle
* @param[in] data post data pointer
* @param[in] len post length
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t esp_http_client_set_post_field(esp_http_client_handle_t client, const char *data, int len);
/**
* @brief Get current post field information
*
* @param[in] client The client
* @param[out] data Point to post data pointer
*
* @return Size of post data
*/
int esp_http_client_get_post_field(esp_http_client_handle_t client, char **data);
/**
* @brief Set http request header, this function must be called after esp_http_client_init and before any
* perform function
*
* @param[in] client The esp_http_client handle
* @param[in] key The header key
* @param[in] value The header value
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t esp_http_client_set_header(esp_http_client_handle_t client, const char *key, const char *value);
/**
* @brief Set http request method
*
* @param[in] client The esp_http_client handle
* @param[in] method The method
*
* @return ESP_OK
*/
esp_err_t esp_http_client_set_method(esp_http_client_handle_t client, esp_http_client_method_t method);
/**
* @brief Delete http request header
*
* @param[in] client The esp_http_client handle
* @param[in] key The key
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t esp_http_client_delete_header(esp_http_client_handle_t client, const char *key);
/**
* @brief This function will be open the connection, write all header strings and return
*
* @param[in] client The esp_http_client handle
* @param[in] write_len HTTP Content length need to write to the server
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len);
/**
* @brief This function will write data to the HTTP connection previously opened by esp_http_client_open()
*
* @param[in] client The esp_http_client handle
* @param buffer The buffer
* @param[in] len This value must not be larger than the write_len parameter provided to esp_http_client_open()
*
* @return
* - (-1) if any errors
* - Length of data written
*/
int esp_http_client_write(esp_http_client_handle_t client, const char *buffer, int len);
/**
* @brief This function need to call after esp_http_client_open, it will read from http stream, process all receive headers
*
* @param[in] client The esp_http_client handle
*
* @return
* - (-1) if stream doesn't contain content-length header, or chunked encoding (checked by `esp_http_client_is_chunked` response)
* - Download data length defined by content-length header
*/
int esp_http_client_fetch_headers(esp_http_client_handle_t client);
/**
* @brief Check response data is chunked, must call after `esp_http_client_finalize_open`
*
* @param[in] client The esp_http_client handle
*
* @return true or false
*/
bool esp_http_client_is_chunked_response(esp_http_client_handle_t client);
/**
* @brief Read data from http stream
*
* @param[in] client The esp_http_client handle
* @param buffer The buffer
* @param[in] len The length
*
* @return
* - (-1) if any errors
* - Length of data was read
*/
int esp_http_client_read(esp_http_client_handle_t client, char *buffer, int len);
/**
* @brief Get http response status code, the valid value if this function invoke after `esp_http_client_perform` or `esp_http_client_finalize_open`
*
* @param[in] client The esp_http_client handle
*
* @return Status code
*/
int esp_http_client_get_status_code(esp_http_client_handle_t client);
/**
* @brief Get http response content length (from header Content-Length)
* the valid value if this function invoke after `esp_http_client_perform` or `esp_http_client_finalize_open`
*
* @param[in] client The esp_http_client handle
*
* @return
* - (-1) Chunked transfer
* - Content-Length value as bytes
*/
int esp_http_client_get_content_length(esp_http_client_handle_t client);
/**
* @brief Close http connection, still kept all http request resources
*
* @param[in] client The esp_http_client handle
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t esp_http_client_close(esp_http_client_handle_t client);
/**
* @brief This function must be the last function to call for an session.
* It is the opposite of the esp_http_client_init function and must be called with the same handle as input that a esp_http_client_init call returned.
* This might close all connections this handle has used and possibly has kept open until now.
* Don't call this function if you intend to transfer more files, re-using handles is a key to good performance with esp_http_client.
*
* @param[in] client The esp_http_client handle
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t esp_http_client_cleanup(esp_http_client_handle_t client);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,151 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "tcpip_adapter.h"
#include "lwip/sockets.h"
#include "rom/md5_hash.h"
#include "mbedtls/base64.h"
#include "esp_system.h"
#include "esp_log.h"
#include "http_utils.h"
#include "http_auth.h"
#define MD5_MAX_LEN (33)
#define HTTP_AUTH_BUF_LEN (1024)
static const char *TAG = "HTTP_AUTH";
/**
* @brief This function hash a formatted string with MD5 and format the result as ascii characters
*
* @param md The buffer will hold the ascii result
* @param[in] fmt The format
*
* @return Length of the result
*/
static int md5_printf(char *md, const char *fmt, ...)
{
unsigned char *buf;
unsigned char digest[MD5_MAX_LEN];
int len, i;
struct MD5Context md5_ctx;
va_list ap;
va_start(ap, fmt);
len = vasprintf((char **)&buf, fmt, ap);
if (buf == NULL) {
return -1;
}
MD5Init(&md5_ctx);
MD5Update(&md5_ctx, buf, len);
MD5Final(digest, &md5_ctx);
for (i = 0; i < 16; ++i) {
sprintf(&md[i * 2], "%02x", (unsigned int)digest[i]);
}
va_end(ap);
free(buf);
return MD5_MAX_LEN;
}
char *http_auth_digest(const char *username, const char *password, esp_http_auth_data_t *auth_data)
{
char *ha1, *ha2 = NULL;
char *digest = NULL;
char *auth_str = NULL;
if (username == NULL ||
password == NULL ||
auth_data->nonce == NULL ||
auth_data->uri == NULL ||
auth_data->realm == NULL) {
return NULL;
}
ha1 = calloc(1, MD5_MAX_LEN);
HTTP_MEM_CHECK(TAG, ha1, goto _digest_exit);
ha2 = calloc(1, MD5_MAX_LEN);
HTTP_MEM_CHECK(TAG, ha2, goto _digest_exit);
digest = calloc(1, MD5_MAX_LEN);
HTTP_MEM_CHECK(TAG, digest, goto _digest_exit);
if (md5_printf(ha1, "%s:%s:%s", username, auth_data->realm, password) <= 0) {
goto _digest_exit;
}
ESP_LOGD(TAG, "%s %s %s %s\r\n", "Digest", username, auth_data->realm, password);
if (strcasecmp(auth_data->algorithm, "md5-sess") == 0) {
if (md5_printf(ha1, "%s:%s:%016llx", ha1, auth_data->nonce, auth_data->cnonce) <= 0) {
goto _digest_exit;
}
}
if (md5_printf(ha2, "%s:%s", auth_data->method, auth_data->uri) <= 0) {
goto _digest_exit;
}
//support qop = auth
if (auth_data->qop && strcasecmp(auth_data->qop, "auth-int") == 0) {
if (md5_printf(ha2, "%s:%s", ha2, "entity") <= 0) {
goto _digest_exit;
}
}
if (auth_data->qop) {
// response=MD5(HA1:nonce:nonceCount:cnonce:qop:HA2)
if (md5_printf(digest, "%s:%s:%08x:%016llx:%s:%s", ha1, auth_data->nonce, auth_data->nc, auth_data->cnonce, auth_data->qop, ha2) <= 0) {
goto _digest_exit;
}
} else {
// response=MD5(HA1:nonce:HA2)
if (md5_printf(digest, "%s:%s:%s", ha1, auth_data->nonce, ha2) <= 0) {
goto _digest_exit;
}
}
asprintf(&auth_str, "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", algorithm=\"MD5\", "
"response=\"%s\", opaque=\"%s\", qop=%s, nc=%08x, cnonce=\"%016llx\"",
username, auth_data->realm, auth_data->nonce, auth_data->uri, digest, auth_data->opaque, auth_data->qop, auth_data->nc, auth_data->cnonce);
_digest_exit:
free(ha1);
free(ha2);
free(digest);
return auth_str;
}
char *http_auth_basic(const char *username, const char *password)
{
int out;
char *user_info = NULL;
char *digest = calloc(1, MD5_MAX_LEN + 7);
HTTP_MEM_CHECK(TAG, digest, goto _basic_exit);
asprintf(&user_info, "%s:%s", username, password);
HTTP_MEM_CHECK(TAG, user_info, goto _basic_exit);
if (user_info == NULL) {
goto _basic_exit;
}
strcpy(digest, "Basic ");
mbedtls_base64_encode((unsigned char *)digest + 6, MD5_MAX_LEN, (size_t *)&out, (const unsigned char *)user_info, strlen(user_info));
_basic_exit:
free(user_info);
return digest;
}

View File

@ -0,0 +1,239 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include "esp_log.h"
#include "http_header.h"
#include "http_utils.h"
static const char *TAG = "HTTP_HEADER";
#define HEADER_BUFFER (1024)
/**
* dictionary item struct, with key-value pair
*/
typedef struct http_header_item {
char *key; /*!< key */
char *value; /*!< value */
STAILQ_ENTRY(http_header_item) next; /*!< Point to next entry */
} http_header_item_t;
STAILQ_HEAD(http_header, http_header_item);
http_header_handle_t http_header_init()
{
http_header_handle_t header = calloc(1, sizeof(struct http_header));
HTTP_MEM_CHECK(TAG, header, return NULL);
STAILQ_INIT(header);
return header;
}
esp_err_t http_header_destroy(http_header_handle_t header)
{
esp_err_t err = http_header_clean(header);
free(header);
return err;
}
http_header_item_handle_t http_header_get_item(http_header_handle_t header, const char *key)
{
http_header_item_handle_t item;
if (header == NULL || key == NULL) {
return NULL;
}
STAILQ_FOREACH(item, header, next) {
if (strcasecmp(item->key, key) == 0) {
return item;
}
}
return NULL;
}
esp_err_t http_header_get(http_header_handle_t header, const char *key, char **value)
{
http_header_item_handle_t item;
item = http_header_get_item(header, key);
if (item) {
*value = item->value;
} else {
*value = NULL;
}
return ESP_OK;
}
static esp_err_t http_header_new_item(http_header_handle_t header, const char *key, const char *value)
{
http_header_item_handle_t item;
item = calloc(1, sizeof(http_header_item_t));
HTTP_MEM_CHECK(TAG, item, return ESP_ERR_NO_MEM);
http_utils_assign_string(&item->key, key, 0);
HTTP_MEM_CHECK(TAG, item->key, goto _header_new_item_exit);
http_utils_trim_whitespace(&item->key);
http_utils_assign_string(&item->value, value, 0);
HTTP_MEM_CHECK(TAG, item->value, goto _header_new_item_exit);
http_utils_trim_whitespace(&item->value);
STAILQ_INSERT_TAIL(header, item, next);
return ESP_OK;
_header_new_item_exit:
free(item->key);
free(item->value);
return ESP_ERR_NO_MEM;
}
esp_err_t http_header_set(http_header_handle_t header, const char *key, const char *value)
{
http_header_item_handle_t item;
if (value == NULL) {
return http_header_delete(header, key);
}
item = http_header_get_item(header, key);
if (item) {
free(item->value);
item->value = strdup(value);
http_utils_trim_whitespace(&item->value);
return ESP_OK;
}
return http_header_new_item(header, key, value);
}
esp_err_t http_header_set_from_string(http_header_handle_t header, const char *key_value_data)
{
char *eq_ch;
char *p_str;
p_str = strdup(key_value_data);
HTTP_MEM_CHECK(TAG, p_str, return ESP_ERR_NO_MEM);
eq_ch = strchr(p_str, ':');
if (eq_ch == NULL) {
free(p_str);
return ESP_ERR_INVALID_ARG;
}
*eq_ch = 0;
http_header_set(header, p_str, eq_ch + 1);
free(p_str);
return ESP_OK;
}
esp_err_t http_header_delete(http_header_handle_t header, const char *key)
{
http_header_item_handle_t item = http_header_get_item(header, key);
if (item) {
STAILQ_REMOVE(header, item, http_header_item, next);
free(item->key);
free(item->value);
free(item);
} else {
return ESP_ERR_NOT_FOUND;
}
return ESP_OK;
}
int http_header_set_format(http_header_handle_t header, const char *key, const char *format, ...)
{
va_list argptr;
int len = 0;
char *buf = NULL;
va_start(argptr, format);
len = vasprintf(&buf, format, argptr);
HTTP_MEM_CHECK(TAG, buf, return 0);
va_end(argptr);
if (buf == NULL) {
return 0;
}
http_header_set(header, key, buf);
free(buf);
return len;
}
int http_header_generate_string(http_header_handle_t header, int index, char *buffer, int *buffer_len)
{
http_header_item_handle_t item;
int siz = 0;
int idx = 0;
int ret_idx = -1;
bool is_end = false;
STAILQ_FOREACH(item, header, next) {
if (item->value && idx >= index) {
siz += strlen(item->key);
siz += strlen(item->value);
siz += 4; //': ' and '\r\n'
}
idx ++;
if (siz + 1 > *buffer_len - 2) {
ret_idx = idx - 1;
}
}
if (siz == 0) {
return 0;
}
if (ret_idx < 0) {
ret_idx = idx;
is_end = true;
}
int str_len = 0;
idx = 0;
STAILQ_FOREACH(item, header, next) {
if (item->value && idx >= index && idx < ret_idx) {
str_len += snprintf(buffer + str_len, *buffer_len - str_len, "%s: %s\r\n", item->key, item->value);
}
idx ++;
}
if (is_end) {
str_len += snprintf(buffer + str_len, *buffer_len - str_len, "\r\n");
}
*buffer_len = str_len;
return ret_idx;
}
esp_err_t http_header_clean(http_header_handle_t header)
{
http_header_item_handle_t item = STAILQ_FIRST(header), tmp;
while (item != NULL) {
tmp = STAILQ_NEXT(item, next);
free(item->key);
free(item->value);
free(item);
item = tmp;
}
STAILQ_INIT(header);
return ESP_OK;
}
int http_header_count(http_header_handle_t header)
{
http_header_item_handle_t item;
int count = 0;
STAILQ_FOREACH(item, header, next) {
count ++;
}
return count;
}

View File

@ -0,0 +1,125 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "http_utils.h"
#ifndef mem_check
#define mem_check(x) assert(x)
#endif
char *http_utils_join_string(const char *first_str, int len_first, const char *second_str, int len_second)
{
int first_str_len = len_first > 0 ? len_first : strlen(first_str);
int second_str_len = len_second > 0 ? len_second : strlen(second_str);
char *ret = NULL;
if (first_str_len + second_str_len > 0) {
ret = calloc(1, first_str_len + second_str_len + 1);
mem_check(ret);
memcpy(ret, first_str, first_str_len);
memcpy(ret + first_str_len, second_str, second_str_len);
}
return ret;
}
char *http_utils_assign_string(char **str, const char *new_str, int len)
{
int l = len;
if (new_str == NULL) {
return NULL;
}
char *old_str = *str;
if (l <= 0) {
l = strlen(new_str);
}
if (old_str) {
old_str = realloc(old_str, l + 1);
mem_check(old_str);
old_str[l] = 0;
} else {
old_str = calloc(1, l + 1);
mem_check(old_str);
}
memcpy(old_str, new_str, l);
*str = old_str;
return old_str;
}
void http_utils_trim_whitespace(char **str)
{
char *end;
char *start = *str;
// Trim leading space
while (isspace((unsigned char)*start)) start ++;
if (*start == 0) { // All spaces?
**str = 0;
return;
}
// Trim trailing space
end = (char *)(start + strlen(start) - 1);
while (end > start && isspace((unsigned char)*end)) {
end--;
}
// Write new null terminator
*(end + 1) = 0;
memmove(*str, start, strlen(start) + 1);
}
char *http_utils_get_string_between(const char *str, const char *begin, const char *end)
{
char *found = strstr(str, begin);
char *ret = NULL;
if (found) {
found += strlen(begin);
char *found_end = strstr(found, end);
if (found_end) {
ret = calloc(1, found_end - found + 1);
mem_check(ret);
memcpy(ret, found, found_end - found);
return ret;
}
}
return NULL;
}
int http_utils_str_starts_with(const char *str, const char *start)
{
int i;
int match_str_len = strlen(str);
int start_len = strlen(start);
if (start_len > match_str_len) {
return -1;
}
for (i = 0; i < start_len; i++) {
if (str[i] != start[i]) {
return 1;
}
}
return 0;
}
void http_utils_ms_to_timeval(int timeout_ms, struct timeval *tv)
{
tv->tv_sec = timeout_ms / 1000;
tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000;
}

View File

@ -0,0 +1,60 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _HTTP_BASIC_AUTH_H_
#define _HTTP_BASIC_AUTH_H_
/**
* HTTP Digest authentication data
*/
typedef struct {
char *method; /*!< Request method, example: GET */
char *algorithm; /*!< Authentication algorithm */
char *uri; /*!< URI of request example: /path/to/file.html */
char *realm; /*!< Authentication realm */
char *nonce; /*!< Authentication nonce */
char *qop; /*!< Authentication qop */
char *opaque; /*!< Authentication opaque */
uint64_t cnonce; /*!< Authentication cnonce */
int nc; /*!< Authentication nc */
} esp_http_auth_data_t;
/**
* @brief This use for Http digest authentication method, create http header for digest authentication.
* The returned string need to free after use
*
* @param[in] username The username
* @param[in] password The password
* @param auth_data The auth data
*
* @return
* - HTTP Header value of Authorization, valid for digest authentication, must be freed after usage
* - NULL
*/
char *http_auth_digest(const char *username, const char *password, esp_http_auth_data_t *auth_data);
/**
* @brief This use for Http basic authentication method, create header value for basic http authentication
* The returned string need to free after use
*
* @param[in] username The username
* @param[in] password The password
*
* @return
* - HTTP Header value of Authorization, valid for basic authentication, must be free after use
* - NULL
*/
char *http_auth_basic(const char *username, const char *password);
#endif

View File

@ -0,0 +1,128 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _HTTP_HEADER_H_
#define _HTTP_HEADER_H_
#include "rom/queue.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct http_header *http_header_handle_t;
typedef struct http_header_item *http_header_item_handle_t;
/**
* @brief initialize and allocate the memory for the header object
*
* @return
* - http_header_handle_t
* - NULL if any errors
*/
http_header_handle_t http_header_init();
/**
* @brief Cleanup and free all http header pairs
*
* @param[in] header The header
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t http_header_clean(http_header_handle_t header);
/**
* @brief Cleanup with http_header_clean and destroy http header handle object
*
* @param[in] header The header
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t http_header_destroy(http_header_handle_t header);
/**
* @brief Add a key-value pair of http header to the list,
* note that with value = NULL, this function will remove the header with `key` already exists in the list.
*
* @param[in] header The header
* @param[in] key The key
* @param[in] value The value
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t http_header_set(http_header_handle_t header, const char *key, const char *value);
/**
* @brief Sample as `http_header_set` but the value can be formated
*
* @param[in] header The header
* @param[in] key The key
* @param[in] format The format
* @param[in] ... format parameters
*
* @return Total length of value
*/
int http_header_set_format(http_header_handle_t header, const char *key, const char *format, ...);
/**
* @brief Get a value of header in header list
* The address of the value will be assign set to `value` parameter or NULL if no header with the key exists in the list
*
* @param[in] header The header
* @param[in] key The key
* @param[out] value The value
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t http_header_get(http_header_handle_t header, const char *key, char **value);
/**
* @brief Create HTTP header string from the header with index, output string to buffer with buffer_len
* Also return the last index of header was generated
*
* @param[in] header The header
* @param[in] index The index
* @param buffer The buffer
* @param buffer_len The buffer length
*
* @return The last index of header was generated
*/
int http_header_generate_string(http_header_handle_t header, int index, char *buffer, int *buffer_len);
/**
* @brief Remove the header with key from the headers list
*
* @param[in] header The header
* @param[in] key The key
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t http_header_delete(http_header_handle_t header, const char *key);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,95 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _HTTP_UTILS_H_
#define _HTTP_UTILS_H_
#include <sys/time.h>
/**
* @brief Assign new_str to *str pointer, and realloc *str if it not NULL
*
* @param str pointer to string pointer
* @param new_str assign this tring to str
* @param len length of string, 0 if new_str is zero terminated
*
* @return
* - new_str pointer
* - NULL
*/
char *http_utils_assign_string(char **str, const char *new_str, int len);
/**
* @brief Remove white space at begin and end of string
*
* @param[in] str The string
*
* @return New strings have been trimmed
*/
void http_utils_trim_whitespace(char **str);
/**
* @brief Gets the string between 2 string.
* It will allocate a new memory space for this string, so you need to free it when no longer use
*
* @param[in] str The source string
* @param[in] begin The begin string
* @param[in] end The end string
*
* @return The string between begin and end
*/
char *http_utils_get_string_between(const char *str, const char *begin, const char *end);
/**
* @brief Join 2 strings to one
* It will allocate a new memory space for this string, so you need to free it when no longer use
*
* @param[in] first_str The first string
* @param[in] len_first The length first
* @param[in] second_str The second string
* @param[in] len_second The length second
*
* @return
* - New string pointer
* - NULL: Invalid input
*/
char *http_utils_join_string(const char *first_str, int len_first, const char *second_str, int len_second);
/**
* @brief Check if ``str`` is start with ``start``
*
* @param[in] str The string
* @param[in] start The start
*
* @return
* - (-1) if length of ``start`` larger than length of ``str``
* - (1) if ``start`` NOT starts with ``start``
* - (0) if ``str`` starts with ``start``
*/
int http_utils_str_starts_with(const char *str, const char *start);
/**
* @brief Convert milliseconds to timeval struct
*
* @param[in] timeout_ms The timeout milliseconds
* @param[out] tv Timeval struct
*/
void http_utils_ms_to_timeval(int timeout_ms, struct timeval *tv);
#define HTTP_MEM_CHECK(TAG, a, action) if (!(a)) { \
ESP_LOGE(TAG,"%s:%d (%s): %s", __FILE__, __LINE__, __FUNCTION__, "Memory exhausted"); \
action; \
}
#endif

View File

@ -0,0 +1,251 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _TRANSPORT_H_
#define _TRANSPORT_H_
#include <esp_err.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct transport_list_t* transport_list_handle_t;
typedef struct transport_item_t* transport_handle_t;
typedef int (*connect_func)(transport_handle_t t, const char *host, int port, int timeout_ms);
typedef int (*io_func)(transport_handle_t t, const char *buffer, int len, int timeout_ms);
typedef int (*io_read_func)(transport_handle_t t, char *buffer, int len, int timeout_ms);
typedef int (*trans_func)(transport_handle_t t);
typedef int (*poll_func)(transport_handle_t t, int timeout_ms);
/**
* @brief Create transport list
*
* @return A handle can hold all transports
*/
transport_list_handle_t transport_list_init();
/**
* @brief Cleanup and free all transports, include itself,
* this function will invoke transport_destroy of every transport have added this the list
*
* @param[in] list The list
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t transport_list_destroy(transport_list_handle_t list);
/**
* @brief Add a transport to the list, and define a scheme to indentify this transport in the list
*
* @param[in] list The list
* @param[in] t The Transport
* @param[in] scheme The scheme
*
* @return
* - ESP_OK
*/
esp_err_t transport_list_add(transport_list_handle_t list, transport_handle_t t, const char *scheme);
/**
* @brief This function will remove all transport from the list,
* invoke transport_destroy of every transport have added this the list
*
* @param[in] list The list
*
* @return
* - ESP_OK
* - ESP_ERR_INVALID_ARG
*/
esp_err_t transport_list_clean(transport_list_handle_t list);
/**
* @brief Get the transport by scheme, which has been defined when calling function `transport_list_add`
*
* @param[in] list The list
* @param[in] tag The tag
*
* @return The transport handle
*/
transport_handle_t transport_list_get_transport(transport_list_handle_t list, const char *scheme);
/**
* @brief Initialize a transport handle object
*
* @return The transport handle
*/
transport_handle_t transport_init();
/**
* @brief Cleanup and free memory the transport
*
* @param[in] t The transport handle
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t transport_destroy(transport_handle_t t);
/**
* @brief Get default port number used by this transport
*
* @param[in] t The transport handle
*
* @return the port number
*/
int transport_get_default_port(transport_handle_t t);
/**
* @brief Set default port number that can be used by this transport
*
* @param[in] t The transport handle
* @param[in] port The port number
*
* @return
* - ESP_OK
* - ESP_FAIL
*/
esp_err_t transport_set_default_port(transport_handle_t t, int port);
/**
* @brief Transport connection function, to make a connection to server
*
* @param t The transport handle
* @param[in] host Hostname
* @param[in] port Port
* @param[in] timeout_ms The timeout milliseconds
*
* @return
* - socket for will use by this transport
* - (-1) if there are any errors, should check errno
*/
int transport_connect(transport_handle_t t, const char *host, int port, int timeout_ms);
/**
* @brief Transport read function
*
* @param t The transport handle
* @param buffer The buffer
* @param[in] len The length
* @param[in] timeout_ms The timeout milliseconds
*
* @return
* - Number of bytes was read
* - (-1) if there are any errors, should check errno
*/
int transport_read(transport_handle_t t, char *buffer, int len, int timeout_ms);
/**
* @brief Poll the transport until readable or timeout
*
* @param[in] t The transport handle
* @param[in] timeout_ms The timeout milliseconds
*
* @return
* - 0 Timeout
* - (-1) If there are any errors, should check errno
* - other The transport can read
*/
int transport_poll_read(transport_handle_t t, int timeout_ms);
/**
* @brief Transport write function
*
* @param t The transport handle
* @param buffer The buffer
* @param[in] len The length
* @param[in] timeout_ms The timeout milliseconds
*
* @return
* - Number of bytes was written
* - (-1) if there are any errors, should check errno
*/
int transport_write(transport_handle_t t, const char *buffer, int len, int timeout_ms);
/**
* @brief Poll the transport until writeable or timeout
*
* @param[in] t The transport handle
* @param[in] timeout_ms The timeout milliseconds
*
* @return
* - 0 Timeout
* - (-1) If there are any errors, should check errno
* - other The transport can write
*/
int transport_poll_write(transport_handle_t t, int timeout_ms);
/**
* @brief Transport close
*
* @param t The transport handle
*
* @return
* - 0 if ok
* - (-1) if there are any errors, should check errno
*/
int transport_close(transport_handle_t t);
/**
* @brief Get user data context of this transport
*
* @param[in] t The transport handle
*
* @return The user data context
*/
void *transport_get_context_data(transport_handle_t t);
/**
* @brief Set the user context data for this transport
*
* @param[in] t The transport handle
* @param data The user data context
*
* @return
* - ESP_OK
*/
esp_err_t transport_set_context_data(transport_handle_t t, void *data);
/**
* @brief Set transport functions for the transport handle
*
* @param[in] t The transport handle
* @param[in] _connect The connect function pointer
* @param[in] _read The read function pointer
* @param[in] _write The write function pointer
* @param[in] _close The close function pointer
* @param[in] _poll_read The poll read function pointer
* @param[in] _poll_write The poll write function pointer
* @param[in] _destroy The destroy function pointer
*
* @return
* - ESP_OK
*/
esp_err_t transport_set_func(transport_handle_t t,
connect_func _connect,
io_read_func _read,
io_func _write,
trans_func _close,
poll_func _poll_read,
poll_func _poll_write,
trans_func _destroy);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,48 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _TRANSPORT_SSL_H_
#define _TRANSPORT_SSL_H_
#include "transport.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Create new SSL transport, the transport handle must be release transport_destroy callback
*
* @return the allocated transport_handle_t, or NULL if the handle can not be allocated
*/
transport_handle_t transport_ssl_init();
/**
* @brief Set SSL certificate data (as PEM format).
* Note that, this function stores the pointer to data, rather than making a copy.
* So we need to make sure to keep the data lifetime before cleanup the connection
*
* @param t ssl transport
* @param[in] data The pem data
* @param[in] len The length
*/
void transport_ssl_set_cert_data(transport_handle_t t, const char *data, int len);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,36 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _TRANSPORT_TCP_H_
#define _TRANSPORT_TCP_H_
#include "transport.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Create TCP transport, the transport handle must be release transport_destroy callback
*
* @return the allocated transport_handle_t, or NULL if the handle can not be allocated
*/
transport_handle_t transport_tcp_init();
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,232 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include "rom/queue.h"
#include "esp_log.h"
#include "transport.h"
#include "http_utils.h"
static const char *TAG = "TRANSPORT";
/**
* Transport layer structure, which will provide functions, basic properties for transport types
*/
struct transport_item_t {
int port;
int socket; /*!< Socket to use in this transport */
char *scheme; /*!< Tag name */
void *context; /*!< Context data */
void *data; /*!< Additional transport data */
connect_func _connect; /*!< Connect function of this transport */
io_read_func _read; /*!< Read */
io_func _write; /*!< Write */
trans_func _close; /*!< Close */
poll_func _poll_read; /*!< Poll and read */
poll_func _poll_write; /*!< Poll and write */
trans_func _destroy; /*!< Destroy and free transport */
STAILQ_ENTRY(transport_item_t) next;
};
/**
* This list will hold all transport available
*/
STAILQ_HEAD(transport_list_t, transport_item_t);
transport_list_handle_t transport_list_init()
{
transport_list_handle_t list = calloc(1, sizeof(struct transport_list_t));
HTTP_MEM_CHECK(TAG, list, return NULL);
STAILQ_INIT(list);
return list;
}
esp_err_t transport_list_add(transport_list_handle_t list, transport_handle_t t, const char *scheme)
{
if (list == NULL || t == NULL) {
return ESP_ERR_INVALID_ARG;
}
t->scheme = calloc(1, strlen(scheme) + 1);
HTTP_MEM_CHECK(TAG, t->scheme, return ESP_ERR_NO_MEM);
strcpy(t->scheme, scheme);
STAILQ_INSERT_TAIL(list, t, next);
return ESP_OK;
}
transport_handle_t transport_list_get_transport(transport_list_handle_t list, const char *scheme)
{
if (!list) {
return NULL;
}
if (scheme == NULL) {
return STAILQ_FIRST(list);
}
transport_handle_t item;
STAILQ_FOREACH(item, list, next) {
if (strcasecmp(item->scheme, scheme) == 0) {
return item;
}
}
return NULL;
}
esp_err_t transport_list_destroy(transport_list_handle_t list)
{
transport_list_clean(list);
free(list);
return ESP_OK;
}
esp_err_t transport_list_clean(transport_list_handle_t list)
{
transport_handle_t item = STAILQ_FIRST(list);
transport_handle_t tmp;
while (item != NULL) {
tmp = STAILQ_NEXT(item, next);
if (item->_destroy) {
item->_destroy(item);
}
transport_destroy(item);
item = tmp;
}
STAILQ_INIT(list);
return ESP_OK;
}
transport_handle_t transport_init()
{
transport_handle_t t = calloc(1, sizeof(struct transport_item_t));
HTTP_MEM_CHECK(TAG, t, return NULL);
return t;
}
esp_err_t transport_destroy(transport_handle_t t)
{
if (t->scheme) {
free(t->scheme);
}
free(t);
return ESP_OK;
}
int transport_connect(transport_handle_t t, const char *host, int port, int timeout_ms)
{
int ret = -1;
if (t && t->_connect) {
return t->_connect(t, host, port, timeout_ms);
}
return ret;
}
int transport_read(transport_handle_t t, char *buffer, int len, int timeout_ms)
{
if (t && t->_read) {
return t->_read(t, buffer, len, timeout_ms);
}
return -1;
}
int transport_write(transport_handle_t t, const char *buffer, int len, int timeout_ms)
{
if (t && t->_write) {
return t->_write(t, buffer, len, timeout_ms);
}
return -1;
}
int transport_poll_read(transport_handle_t t, int timeout_ms)
{
if (t && t->_poll_read) {
return t->_poll_read(t, timeout_ms);
}
return -1;
}
int transport_poll_write(transport_handle_t t, int timeout_ms)
{
if (t && t->_poll_write) {
return t->_poll_write(t, timeout_ms);
}
return -1;
}
int transport_close(transport_handle_t t)
{
if (t && t->_close) {
return t->_close(t);
}
return 0;
}
void *transport_get_context_data(transport_handle_t t)
{
if (t) {
return t->data;
}
return NULL;
}
esp_err_t transport_set_context_data(transport_handle_t t, void *data)
{
if (t) {
t->data = data;
return ESP_OK;
}
return ESP_FAIL;
}
esp_err_t transport_set_func(transport_handle_t t,
connect_func _connect,
io_read_func _read,
io_func _write,
trans_func _close,
poll_func _poll_read,
poll_func _poll_write,
trans_func _destroy)
{
if (t == NULL) {
return ESP_FAIL;
}
t->_connect = _connect;
t->_read = _read;
t->_write = _write;
t->_close = _close;
t->_poll_read = _poll_read;
t->_poll_write = _poll_write;
t->_destroy = _destroy;
return ESP_OK;
}
int transport_get_default_port(transport_handle_t t)
{
if (t == NULL) {
return -1;
}
return t->port;
}
esp_err_t transport_set_default_port(transport_handle_t t, int port)
{
if (t == NULL) {
return ESP_FAIL;
}
t->port = port;
return ESP_OK;
}

View File

@ -0,0 +1,267 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "mbedtls/platform.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/esp_debug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "mbedtls/certs.h"
#include "esp_log.h"
#include "esp_system.h"
#include "transport.h"
#include "transport_ssl.h"
#include "http_utils.h"
static const char *TAG = "TRANS_SSL";
/**
* mbedtls specific transport data
*/
typedef struct {
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ctx;
mbedtls_x509_crt cacert;
mbedtls_ssl_config conf;
mbedtls_net_context client_fd;
void *cert_pem_data;
int cert_pem_len;
bool ssl_initialized;
bool verify_server;
} transport_ssl_t;
static int ssl_close(transport_handle_t t);
static int ssl_connect(transport_handle_t t, const char *host, int port, int timeout_ms)
{
int ret = -1, flags;
struct timeval tv;
transport_ssl_t *ssl = transport_get_context_data(t);
if (!ssl) {
return -1;
}
ssl->ssl_initialized = true;
mbedtls_ssl_init(&ssl->ctx);
mbedtls_ctr_drbg_init(&ssl->ctr_drbg);
mbedtls_ssl_config_init(&ssl->conf);
mbedtls_entropy_init(&ssl->entropy);
if ((ret = mbedtls_ssl_config_defaults(&ssl->conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret);
goto exit;
}
if ((ret = mbedtls_ctr_drbg_seed(&ssl->ctr_drbg, mbedtls_entropy_func, &ssl->entropy, NULL, 0)) != 0) {
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret);
goto exit;
}
if (ssl->cert_pem_data) {
mbedtls_x509_crt_init(&ssl->cacert);
ssl->verify_server = true;
if ((ret = mbedtls_x509_crt_parse(&ssl->cacert, ssl->cert_pem_data, ssl->cert_pem_len + 1)) < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x\n\nDATA=%s,len=%d", -ret, (char*)ssl->cert_pem_data, ssl->cert_pem_len);
goto exit;
}
mbedtls_ssl_conf_ca_chain(&ssl->conf, &ssl->cacert, NULL);
mbedtls_ssl_conf_authmode(&ssl->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
if ((ret = mbedtls_ssl_set_hostname(&ssl->ctx, host)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret);
goto exit;
}
} else {
mbedtls_ssl_conf_authmode(&ssl->conf, MBEDTLS_SSL_VERIFY_NONE);
}
mbedtls_ssl_conf_rng(&ssl->conf, mbedtls_ctr_drbg_random, &ssl->ctr_drbg);
#ifdef CONFIG_MBEDTLS_DEBUG
mbedtls_esp_enable_debug_log(&ssl->conf, 4);
#endif
if ((ret = mbedtls_ssl_setup(&ssl->ctx, &ssl->conf)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x\n\n", -ret);
goto exit;
}
mbedtls_net_init(&ssl->client_fd);
http_utils_ms_to_timeval(timeout_ms, &tv);
setsockopt(ssl->client_fd.fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
ESP_LOGD(TAG, "Connect to %s:%d", host, port);
char port_str[8] = {0};
sprintf(port_str, "%d", port);
if ((ret = mbedtls_net_connect(&ssl->client_fd, host, port_str, MBEDTLS_NET_PROTO_TCP)) != 0) {
ESP_LOGE(TAG, "mbedtls_net_connect returned -%x", -ret);
goto exit;
}
mbedtls_ssl_set_bio(&ssl->ctx, &ssl->client_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
if((ret = mbedtls_ssl_set_hostname(&ssl->ctx, host)) != 0) {
ESP_LOGE(TAG, " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret);
goto exit;
}
ESP_LOGD(TAG, "Performing the SSL/TLS handshake...");
while ((ret = mbedtls_ssl_handshake(&ssl->ctx)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret);
goto exit;
}
}
ESP_LOGD(TAG, "Verifying peer X.509 certificate...");
if ((flags = mbedtls_ssl_get_verify_result(&ssl->ctx)) != 0) {
/* In real life, we probably want to close connection if ret != 0 */
ESP_LOGW(TAG, "Failed to verify peer certificate!");
if (ssl->cert_pem_data) {
goto exit;
}
} else {
ESP_LOGD(TAG, "Certificate verified.");
}
ESP_LOGD(TAG, "Cipher suite is %s", mbedtls_ssl_get_ciphersuite(&ssl->ctx));
return ret;
exit:
ssl_close(t);
return ret;
}
static int ssl_poll_read(transport_handle_t t, int timeout_ms)
{
transport_ssl_t *ssl = transport_get_context_data(t);
fd_set readset;
FD_ZERO(&readset);
FD_SET(ssl->client_fd.fd, &readset);
struct timeval timeout;
http_utils_ms_to_timeval(timeout_ms, &timeout);
return select(ssl->client_fd.fd + 1, &readset, NULL, NULL, &timeout);
}
static int ssl_poll_write(transport_handle_t t, int timeout_ms)
{
transport_ssl_t *ssl = transport_get_context_data(t);
fd_set writeset;
FD_ZERO(&writeset);
FD_SET(ssl->client_fd.fd, &writeset);
struct timeval timeout;
http_utils_ms_to_timeval(timeout_ms, &timeout);
return select(ssl->client_fd.fd + 1, NULL, &writeset, NULL, &timeout);
}
static int ssl_write(transport_handle_t t, const char *buffer, int len, int timeout_ms)
{
int poll, ret;
transport_ssl_t *ssl = transport_get_context_data(t);
if ((poll = transport_poll_write(t, timeout_ms)) <= 0) {
ESP_LOGW(TAG, "Poll timeout or error, errno=%s, fd=%d, timeout_ms=%d", strerror(errno), ssl->client_fd.fd, timeout_ms);
return poll;
}
ret = mbedtls_ssl_write(&ssl->ctx, (const unsigned char *) buffer, len);
if (ret <= 0) {
ESP_LOGE(TAG, "mbedtls_ssl_write error, errno=%s", strerror(errno));
}
return ret;
}
static int ssl_read(transport_handle_t t, char *buffer, int len, int timeout_ms)
{
int ret;
transport_ssl_t *ssl = transport_get_context_data(t);
ret = mbedtls_ssl_read(&ssl->ctx, (unsigned char *)buffer, len);
if (ret == 0) {
return -1;
}
return ret;
}
static int ssl_close(transport_handle_t t)
{
int ret = -1;
transport_ssl_t *ssl = transport_get_context_data(t);
if (ssl->ssl_initialized) {
ESP_LOGD(TAG, "Cleanup mbedtls");
mbedtls_ssl_close_notify(&ssl->ctx);
mbedtls_ssl_session_reset(&ssl->ctx);
mbedtls_net_free(&ssl->client_fd);
mbedtls_ssl_config_free(&ssl->conf);
if (ssl->verify_server) {
mbedtls_x509_crt_free(&ssl->cacert);
}
mbedtls_ctr_drbg_free(&ssl->ctr_drbg);
mbedtls_entropy_free(&ssl->entropy);
mbedtls_ssl_free(&ssl->ctx);
ssl->ssl_initialized = false;
ssl->verify_server = false;
}
return ret;
}
static int ssl_destroy(transport_handle_t t)
{
transport_ssl_t *ssl = transport_get_context_data(t);
transport_close(t);
free(ssl);
return 0;
}
void transport_ssl_set_cert_data(transport_handle_t t, const char *data, int len)
{
transport_ssl_t *ssl = transport_get_context_data(t);
if (t && ssl) {
ssl->cert_pem_data = (void *)data;
ssl->cert_pem_len = len;
}
}
transport_handle_t transport_ssl_init()
{
transport_handle_t t = transport_init();
transport_ssl_t *ssl = calloc(1, sizeof(transport_ssl_t));
HTTP_MEM_CHECK(TAG, ssl, return NULL);
mbedtls_net_init(&ssl->client_fd);
transport_set_context_data(t, ssl);
transport_set_func(t, ssl_connect, ssl_read, ssl_write, ssl_close, ssl_poll_read, ssl_poll_write, ssl_destroy);
return t;
}

View File

@ -0,0 +1,166 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_err.h"
#include "http_utils.h"
#include "transport.h"
static const char *TAG = "TRANS_TCP";
typedef struct {
int sock;
} transport_tcp_t;
static int resolve_dns(const char *host, struct sockaddr_in *ip) {
struct hostent *he;
struct in_addr **addr_list;
he = gethostbyname(host);
if (he == NULL) {
return ESP_FAIL;
}
addr_list = (struct in_addr **)he->h_addr_list;
if (addr_list[0] == NULL) {
return ESP_FAIL;
}
ip->sin_family = AF_INET;
memcpy(&ip->sin_addr, addr_list[0], sizeof(ip->sin_addr));
return ESP_OK;
}
static int tcp_connect(transport_handle_t t, const char *host, int port, int timeout_ms)
{
struct sockaddr_in remote_ip;
struct timeval tv;
transport_tcp_t *tcp = transport_get_context_data(t);
bzero(&remote_ip, sizeof(struct sockaddr_in));
//if stream_host is not ip address, resolve it AF_INET,servername,&serveraddr.sin_addr
if (inet_pton(AF_INET, host, &remote_ip.sin_addr) != 1) {
if (resolve_dns(host, &remote_ip) < 0) {
return -1;
}
}
tcp->sock = socket(PF_INET, SOCK_STREAM, 0);
if (tcp->sock < 0) {
ESP_LOGE(TAG, "Error create socket");
return -1;
}
remote_ip.sin_family = AF_INET;
remote_ip.sin_port = htons(port);
http_utils_ms_to_timeval(timeout_ms, &tv);
setsockopt(tcp->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
ESP_LOGD(TAG, "[sock=%d],connecting to server IP:%s,Port:%d...",
tcp->sock, ipaddr_ntoa((const ip_addr_t*)&remote_ip.sin_addr.s_addr), port);
if (connect(tcp->sock, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr)) != 0) {
close(tcp->sock);
tcp->sock = -1;
return -1;
}
return tcp->sock;
}
static int tcp_write(transport_handle_t t, const char *buffer, int len, int timeout_ms)
{
int poll;
transport_tcp_t *tcp = transport_get_context_data(t);
if ((poll = transport_poll_write(t, timeout_ms)) <= 0) {
return poll;
}
return write(tcp->sock, buffer, len);
}
static int tcp_read(transport_handle_t t, char *buffer, int len, int timeout_ms)
{
transport_tcp_t *tcp = transport_get_context_data(t);
int poll = -1;
if ((poll = transport_poll_read(t, timeout_ms)) <= 0) {
return poll;
}
int read_len = read(tcp->sock, buffer, len);
if (read_len == 0) {
return -1;
}
return read_len;
}
static int tcp_poll_read(transport_handle_t t, int timeout_ms)
{
transport_tcp_t *tcp = transport_get_context_data(t);
fd_set readset;
FD_ZERO(&readset);
FD_SET(tcp->sock, &readset);
struct timeval timeout;
http_utils_ms_to_timeval(timeout_ms, &timeout);
return select(tcp->sock + 1, &readset, NULL, NULL, &timeout);
}
static int tcp_poll_write(transport_handle_t t, int timeout_ms)
{
transport_tcp_t *tcp = transport_get_context_data(t);
fd_set writeset;
FD_ZERO(&writeset);
FD_SET(tcp->sock, &writeset);
struct timeval timeout;
http_utils_ms_to_timeval(timeout_ms, &timeout);
return select(tcp->sock + 1, NULL, &writeset, NULL, &timeout);
}
static int tcp_close(transport_handle_t t)
{
transport_tcp_t *tcp = transport_get_context_data(t);
int ret = -1;
if (tcp->sock >= 0) {
ret = close(tcp->sock);
tcp->sock = -1;
}
return ret;
}
static esp_err_t tcp_destroy(transport_handle_t t)
{
transport_tcp_t *tcp = transport_get_context_data(t);
transport_close(t);
free(tcp);
return 0;
}
transport_handle_t transport_tcp_init()
{
transport_handle_t t = transport_init();
transport_tcp_t *tcp = calloc(1, sizeof(transport_tcp_t));
HTTP_MEM_CHECK(TAG, tcp, return NULL);
tcp->sock = -1;
transport_set_func(t, tcp_connect, tcp_read, tcp_write, tcp_close, tcp_poll_read, tcp_poll_write, tcp_destroy);
transport_set_context_data(t, tcp);
return t;
}

View File

@ -91,6 +91,7 @@ INPUT = \
../../components/esp-tls/esp_tls.h \
## mDNS
../../components/mdns/include/mdns.h \
../../components/esp_http_client/include/esp_http_client.h \
##
## Storage - API Reference
##

View File

@ -0,0 +1,191 @@
ESP HTTP Client
===============
Overview
--------
``esp_http_client`` provides an API for making HTTP/S requests from ESP-IDF programs. The steps to use this API for an HTTP request are:
* :cpp:func:`esp_http_client_init`: To use the HTTP client, the first thing we must do is create an :cpp:class:`esp_http_client` by pass into this function with the :cpp:class:`esp_http_client_config_t` configurations. Which configuration values we do not define, the library will use default.
* :cpp:func:`esp_http_client_perform`: The :cpp:class:`esp_http_client` argument created from the init function is needed. This function performs all operations of the esp_http_client, from opening the connection, sending data, downloading data and closing the connection if necessary. All related events will be invoked in the event_handle (defined by :cpp:class:`esp_http_client_config_t`). This function performs its job and blocks the current task until it's done
* :cpp:func:`esp_http_client_cleanup`: After completing our **esp_http_client's** task, this is the last function to be called. It will close the connection (if any) and free up all the memory allocated to the HTTP client
Application Example
-------------------
.. highlight:: c
::
esp_err_t _http_event_handle(esp_http_client_event_t *evt)
{
switch(evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER");
printf("%.*s", evt->data_len, (char*)evt->data);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
if (!esp_http_client_is_chunked_response(evt->client)) {
printf("%.*s", evt->data_len, (char*)evt->data);
}
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
esp_http_client_config_t config = {
.url = "http://httpbin.org/redirect/2",
.event_handle = _http_event_handle,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
}
esp_http_client_cleanup(client);
Persistent Connections
----------------------
Persistent connections means that the HTTP client can re-use the same connection for several transfers. If the server does not request to close the connection with the ``Connection: close`` header, the new transfer with sample ip address, port, and protocol.
To allow the HTTP client to take full advantage of persistent connections, you should do as many of your file transfers as possible using the same handle.
Persistent Connections example
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. highlight:: c
::
esp_err_t err;
esp_http_client_config_t config = {
.url = "http://httpbin.org/get",
};
esp_http_client_handle_t client = esp_http_client_init(&config);
// first request
err = esp_http_client_perform(client);
// second request
esp_http_client_set_url(client, "http://httpbin.org/anything")
esp_http_client_set_method(client, HTTP_METHOD_DELETE);
esp_http_client_set_header(client, "HeaderKey", "HeaderValue");
err = esp_http_client_perform(client);
esp_http_client_cleanup(client);
HTTPS
-----
The HTTP client supports SSL connections using **mbedtls**, with the **url** configuration starting with ``https`` scheme (or ``transport_type = HTTP_TRANSPORT_OVER_SSL``). HTTPS support can be configured via :ref:CONFIG_ENABLE_HTTPS (enabled by default)..
.. note:: By providing information using HTTPS, the library will use the SSL transport type to connect to the server. If you want to verify server, then need to provide additional certificate in PEM format, and provide to ``cert_pem`` in ``esp_http_client_config_t``
HTTPS example
^^^^^^^^^^^^^
.. highlight:: c
::
static void https()
{
esp_http_client_config_t config = {
.url = "https://www.howsmyssl.com",
.cert_pem = howsmyssl_com_root_cert_pem_start,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
}
esp_http_client_cleanup(client);
}
HTTP Stream
-----------
Some applications need to open the connection and control the reading of the data in an active manner. the HTTP client supports some functions to make this easier, of course, once you use these functions you should not use the :cpp:func:`esp_http_client_perform` function with that handle, and :cpp:func:`esp_http_client_init` alway to called first to get the handle. Perform that functions in the order below:
* :cpp:func:`esp_http_client_init`: to create and handle
* ``esp_http_client_set_*`` or ``esp_http_client_delete_*``: to modify the http connection information (optional)
* :cpp:func:`esp_http_client_open`: Open the http connection with ``write_len`` parameter, ``write_len=0`` if we only need read
* :cpp:func:`esp_http_client_write`: Upload data, max length equal to ``write_len`` of :cpp:func:`esp_http_client_open` function. We may not need to call it if ``write_len=0``
* :cpp:func:`esp_http_client_fetch_headers`: After sending the headers and write data (if any) to the server, this function will read the HTTP Server response headers. Calling this function will return the ``content-length`` from the Server, and we can call :cpp:func:`esp_http_client_get_status_code` for the HTTP status of the connection.
* :cpp:func:`esp_http_client_read`: Now, we can read the HTTP stream by this function.
* :cpp:func:`esp_http_client_close`: We should the connection after finish
* :cpp:func:`esp_http_client_cleanup`: And release the resources
Perform HTTP request as Stream reader
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Check the example function ``http_perform_as_stream_reader`` at :example:`protocols/esp_http_client`.
HTTP Authentication
-------------------
The HTTP client supports both **Basic** and **Digest** Authentication. By providing usernames and passwords in ``url`` or in the ``username``, ``password`` of config entry. And with ``auth_type = HTTP_AUTH_TYPE_BASIC``, the HTTP client takes only 1 perform to pass the authentication process. If ``auth_type = HTTP_AUTH_TYPE_NONE``, but there are ``username`` and ``password`` in the configuration, the HTTP client takes 2 performs. The first time it connects to the server and receives the UNAUTHORIZED header. Based on this information, it will know which authentication method to choose, and perform it on the second.
Config authentication example with URI
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. highlight:: c
::
esp_http_client_config_t config = {
.url = "http://user:passwd@httpbin.org/basic-auth/user/passwd",
.auth_type = HTTP_AUTH_TYPE_BASIC,
};
Config authentication example with username, password entry
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. highlight:: c
::
esp_http_client_config_t config = {
.url = "http://httpbin.org/basic-auth/user/passwd",
.username = "user",
.password = "passwd",
.auth_type = HTTP_AUTH_TYPE_BASIC,
};
HTTP Client example: :example:`protocols/esp_http_client`.
API Reference
-------------
.. include:: /_build/inc/esp_http_client.inc

View File

@ -6,5 +6,6 @@ Protocols API
mDNS <mdns>
ESP-TLS <esp_tls>
HTTP Client <esp_http_client>
Example code for this API section is provided in :example:`protocols` directory of ESP-IDF examples.

View File

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/protocols/esp_http_client.rst

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := esp-http-client-example
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,3 @@
# ESP HTTP Client Example
See the README.md file in the upper level 'examples' directory for more information about examples.

View File

@ -0,0 +1,51 @@
import re
import os
import sys
# this is a test case write with tiny-test-fw.
# to run test cases outside tiny-test-fw,
# we need to set environment variable `TEST_FW_PATH`,
# then get and insert `TEST_FW_PATH` to sys path before import FW module
test_fw_path = os.getenv("TEST_FW_PATH")
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import TinyFW
import IDF
@IDF.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_esp_http_client(env, extra_data):
"""
steps: |
1. join AP
2. Send HTTP request to httpbin.org
"""
dut1 = env.get_dut("esp_http_client", "examples/protocols/esp_http_client")
# check and log bin size
binary_file = os.path.join(dut1.app.binary_path, "esp-http-client-example.bin")
bin_size = os.path.getsize(binary_file)
IDF.log_performance("esp_http_client_bin_size", "{}KB".format(bin_size//1024))
IDF.check_performance("esp_http_client_bin_size", bin_size//1024)
# start test
dut1.start_app()
dut1.expect("Connected to AP, begin http example", timeout=30)
dut1.expect(re.compile(r"HTTP GET Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP POST Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP PUT Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP PATCH Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP DELETE Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP Basic Auth Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP Basic Auth redirect Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP Digest Auth Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP Relative path redirect Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP Absolute path redirect Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTPS Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP redirect to HTTPS Status = 200, content_length = (\d)"))
dut1.expect(re.compile(r"HTTP chunk encoding Status = 200, content_length = -1"))
dut1.expect(re.compile(r"HTTP Stream reader Status = 200, content_length = (\d)"))
dut1.expect("Finish http example")
if __name__ == '__main__':
test_examples_protocol_esp_http_client()

View File

@ -0,0 +1,17 @@
menu "Example Configuration"
config WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
Can be left blank if the network has no security set.
endmenu

View File

@ -0,0 +1,75 @@
/* ESP HTTP Client Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "app_wifi.h"
static const char *TAG = "WIFI";
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
but we only care about one event - are we connected
to the AP with an IP? */
const int CONNECTED_BIT = BIT0;
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
void app_wifi_initialise(void)
{
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
wifi_config_t wifi_config = {
.sta = {
.ssid = CONFIG_WIFI_SSID,
.password = CONFIG_WIFI_PASSWORD,
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
}
void app_wifi_wait_connected()
{
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);
}

View File

@ -0,0 +1,17 @@
/* ESP HTTP Client Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef _APP_WIFI_H_
#define _APP_WIFI_H_
void app_wifi_initialise(void);
void app_wifi_wait_connected();
#endif

View File

@ -0,0 +1,8 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
# embed files from the "certs" directory as binary data symbols
# in the app
COMPONENT_EMBED_TXTFILES := howsmyssl_com_root_cert.pem

View File

@ -0,0 +1,362 @@
/* ESP HTTP Client Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "app_wifi.h"
#include "esp_http_client.h"
#define MAX_HTTP_RECV_BUFFER 512
static const char *TAG = "HTTP_CLIENT";
/* Root cert for howsmyssl.com, taken from howsmyssl_com_root_cert.pem
The PEM file was extracted from the output of this command:
openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
The CA root cert is the last cert given in the chain of certs.
To embed it in the app binary, the PEM file is named
in the component.mk COMPONENT_EMBED_TXTFILES variable.
*/
extern const char howsmyssl_com_root_cert_pem_start[] asm("_binary_howsmyssl_com_root_cert_pem_start");
extern const char howsmyssl_com_root_cert_pem_end[] asm("_binary_howsmyssl_com_root_cert_pem_end");
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
switch(evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
if (!esp_http_client_is_chunked_response(evt->client)) {
// Write out data
// printf("%.*s", evt->data_len, (char*)evt->data);
}
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
static void http_rest()
{
esp_http_client_config_t config = {
.url = "http://httpbin.org/get",
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
// GET
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
}
// POST
const char *post_data = "field1=value1&field2=value2";
esp_http_client_set_url(client, "http://httpbin.org/post");
esp_http_client_set_method(client, HTTP_METHOD_POST);
esp_http_client_set_post_field(client, post_data, strlen(post_data));
err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP POST Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err));
}
//PUT
esp_http_client_set_url(client, "http://httpbin.org/put");
esp_http_client_set_method(client, HTTP_METHOD_PUT);
err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP PUT Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "HTTP PUT request failed: %s", esp_err_to_name(err));
}
//PATCH
esp_http_client_set_url(client, "http://httpbin.org/patch");
esp_http_client_set_method(client, HTTP_METHOD_PATCH);
esp_http_client_set_post_field(client, NULL, 0);
err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP PATCH Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "HTTP PATCH request failed: %s", esp_err_to_name(err));
}
//DELETE
esp_http_client_set_url(client, "http://httpbin.org/delete");
esp_http_client_set_method(client, HTTP_METHOD_DELETE);
err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP DELETE Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "HTTP DELETE request failed: %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
static void http_auth_basic()
{
esp_http_client_config_t config = {
.url = "http://user:passwd@httpbin.org/basic-auth/user/passwd",
.event_handler = _http_event_handler,
.auth_type = HTTP_AUTH_TYPE_BASIC,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP Basic Auth Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
static void http_auth_basic_redirect()
{
esp_http_client_config_t config = {
.url = "http://user:passwd@httpbin.org/basic-auth/user/passwd",
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP Basic Auth redirect Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
static void http_auth_digest()
{
esp_http_client_config_t config = {
.url = "http://user:passwd@httpbin.org/digest-auth/auth/user/passwd/MD5/never",
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP Digest Auth Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
static void https()
{
esp_http_client_config_t config = {
.url = "https://www.howsmyssl.com",
.event_handler = _http_event_handler,
.cert_pem = howsmyssl_com_root_cert_pem_start,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
static void http_relative_redirect()
{
esp_http_client_config_t config = {
.url = "http://httpbin.org/relative-redirect/3",
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP Relative path redirect Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
static void http_absolute_redirect()
{
esp_http_client_config_t config = {
.url = "http://httpbin.org/absolute-redirect/3",
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP Absolute path redirect Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
static void http_redirect_to_https()
{
esp_http_client_config_t config = {
.url = "http://httpbin.org/redirect-to?url=https%3A%2F%2Fwww.howsmyssl.com",
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP redirect to HTTPS Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
static void http_download_chunk()
{
esp_http_client_config_t config = {
.url = "http://httpbin.org/stream-bytes/8912",
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP chunk encoding Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
static void http_perform_as_stream_reader()
{
char *buffer = malloc(MAX_HTTP_RECV_BUFFER);
if (buffer == NULL) {
ESP_LOGE(TAG, "Cannot malloc http receive buffer");
return;
}
esp_http_client_config_t config = {
.url = "http://httpbin.org/get",
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err;
if ((err = esp_http_client_open(client, 0)) != ESP_OK) {
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
free(buffer);
return;
}
int content_length = esp_http_client_fetch_headers(client);
int total_read_len = 0, read_len;
if (total_read_len < content_length && content_length <= MAX_HTTP_RECV_BUFFER) {
read_len = esp_http_client_read(client, buffer, content_length);
if (read_len <= 0) {
ESP_LOGE(TAG, "Error read data");
}
buffer[read_len] = 0;
ESP_LOGD(TAG, "read_len = %d", read_len);
}
ESP_LOGI(TAG, "HTTP Stream reader Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
esp_http_client_close(client);
esp_http_client_cleanup(client);
free(buffer);
}
static void http_test_task(void *pvParameters)
{
app_wifi_wait_connected();
ESP_LOGI(TAG, "Connected to AP, begin http example");
http_rest();
http_auth_basic();
http_auth_basic_redirect();
http_auth_digest();
http_relative_redirect();
http_absolute_redirect();
https();
http_redirect_to_https();
http_download_chunk();
http_perform_as_stream_reader();
ESP_LOGI(TAG, "Finish http example");
vTaskDelete(NULL);
}
void app_main()
{
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
app_wifi_initialise();
xTaskCreate(&http_test_task, "http_test_task", 8192, NULL, 5, NULL);
}

View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----