mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-10 04:43:33 +00:00
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:
10
components/esp_http_client/Kconfig
Normal file
10
components/esp_http_client/Kconfig
Normal 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
|
6
components/esp_http_client/component.mk
Normal file
6
components/esp_http_client/component.mk
Normal file
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
|
||||
COMPONENT_SRCDIRS := . lib
|
||||
COMPONENT_PRIV_INCLUDEDIRS := lib/include
|
972
components/esp_http_client/esp_http_client.c
Normal file
972
components/esp_http_client/esp_http_client.c
Normal 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;
|
||||
}
|
343
components/esp_http_client/include/esp_http_client.h
Normal file
343
components/esp_http_client/include/esp_http_client.h
Normal 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
|
151
components/esp_http_client/lib/http_auth.c
Normal file
151
components/esp_http_client/lib/http_auth.c
Normal 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;
|
||||
}
|
239
components/esp_http_client/lib/http_header.c
Normal file
239
components/esp_http_client/lib/http_header.c
Normal 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;
|
||||
}
|
125
components/esp_http_client/lib/http_utils.c
Normal file
125
components/esp_http_client/lib/http_utils.c
Normal 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;
|
||||
}
|
60
components/esp_http_client/lib/include/http_auth.h
Normal file
60
components/esp_http_client/lib/include/http_auth.h
Normal 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
|
128
components/esp_http_client/lib/include/http_header.h
Normal file
128
components/esp_http_client/lib/include/http_header.h
Normal 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
|
95
components/esp_http_client/lib/include/http_utils.h
Normal file
95
components/esp_http_client/lib/include/http_utils.h
Normal 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
|
251
components/esp_http_client/lib/include/transport.h
Normal file
251
components/esp_http_client/lib/include/transport.h
Normal 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
|
48
components/esp_http_client/lib/include/transport_ssl.h
Normal file
48
components/esp_http_client/lib/include/transport_ssl.h
Normal 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
|
||||
|
36
components/esp_http_client/lib/include/transport_tcp.h
Normal file
36
components/esp_http_client/lib/include/transport_tcp.h
Normal 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
|
232
components/esp_http_client/lib/transport.c
Normal file
232
components/esp_http_client/lib/transport.c
Normal 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;
|
||||
}
|
267
components/esp_http_client/lib/transport_ssl.c
Normal file
267
components/esp_http_client/lib/transport_ssl.c
Normal 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;
|
||||
}
|
||||
|
166
components/esp_http_client/lib/transport_tcp.c
Normal file
166
components/esp_http_client/lib/transport_tcp.c
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user