mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Use esp-tls in the http2 example
This commit is contained in:
parent
070884fc2e
commit
8211a16207
@ -7,6 +7,7 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <http_parser.h>
|
||||
#include "esp-tls.h"
|
||||
|
||||
|
||||
@ -209,3 +210,28 @@ struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, st
|
||||
return tls;
|
||||
}
|
||||
|
||||
static int get_port(const char *url, struct http_parser_url *u)
|
||||
{
|
||||
if (u->field_data[UF_PORT].len) {
|
||||
return strtol(&url[u->field_data[UF_PORT].off], NULL, 10);
|
||||
} else {
|
||||
if (strncmp(&url[u->field_data[UF_SCHEMA].off], "http", u->field_data[UF_SCHEMA].len) == 0) {
|
||||
return 80;
|
||||
} else if (strncmp(&url[u->field_data[UF_SCHEMA].off], "https", u->field_data[UF_SCHEMA].len) == 0) {
|
||||
return 443;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct esp_tls *esp_tls_conn_http_new(const char *url, struct esp_tls_cfg *cfg)
|
||||
{
|
||||
/* Parse URI */
|
||||
struct http_parser_url u;
|
||||
http_parser_url_init(&u);
|
||||
http_parser_parse_url(url, strlen(url), 0, &u);
|
||||
|
||||
/* Connect to host */
|
||||
return esp_tls_conn_new(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
|
||||
get_port(url, &u), cfg);
|
||||
}
|
||||
|
@ -37,6 +37,9 @@ struct esp_tls {
|
||||
*/
|
||||
struct esp_tls *esp_tls_conn_new(const char *hostname, int hostlen, int port, struct esp_tls_cfg *cfg);
|
||||
|
||||
/* Convenience API for HTTP URIs */
|
||||
struct esp_tls *esp_tls_conn_http_new(const char *url, struct esp_tls_cfg *cfg);
|
||||
|
||||
static inline ssize_t esp_tls_conn_write(struct esp_tls *tls, const char *data, size_t datalen)
|
||||
{
|
||||
return tls->write(tls, data, datalen);
|
||||
|
@ -1,164 +0,0 @@
|
||||
/* With adaptations by Espressif Systems
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "connectlib.h"
|
||||
|
||||
/* Basic parser from nghttp2/examples/client.c */
|
||||
int parse_uri(struct uri *res, const char *uri)
|
||||
{
|
||||
/* We only interested in https */
|
||||
size_t len, i, offset;
|
||||
int ipv6addr = 0;
|
||||
memset(res, 0, sizeof(struct uri));
|
||||
len = strlen(uri);
|
||||
if (len < 9 || memcmp("https://", uri, 8) != 0) {
|
||||
return -1;
|
||||
}
|
||||
offset = 8;
|
||||
res->host = res->hostport = &uri[offset];
|
||||
res->hostlen = 0;
|
||||
if (uri[offset] == '[') {
|
||||
/* IPv6 literal address */
|
||||
++offset;
|
||||
++res->host;
|
||||
ipv6addr = 1;
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (uri[i] == ']') {
|
||||
res->hostlen = i - offset;
|
||||
offset = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char delims[] = ":/?#";
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (strchr(delims, uri[i]) != NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
res->hostlen = i - offset;
|
||||
offset = i;
|
||||
}
|
||||
if (res->hostlen == 0) {
|
||||
return -1;
|
||||
}
|
||||
/* Assuming https */
|
||||
res->port = 443;
|
||||
if (offset < len) {
|
||||
if (uri[offset] == ':') {
|
||||
/* port */
|
||||
const char delims[] = "/?#";
|
||||
int port = 0;
|
||||
++offset;
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (strchr(delims, uri[i]) != NULL) {
|
||||
break;
|
||||
}
|
||||
if ('0' <= uri[i] && uri[i] <= '9') {
|
||||
port *= 10;
|
||||
port += uri[i] - '0';
|
||||
if (port > 65535) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (port == 0) {
|
||||
return -1;
|
||||
}
|
||||
offset = i;
|
||||
res->port = (uint16_t)port;
|
||||
}
|
||||
}
|
||||
res->hostportlen = (size_t)(uri + offset + ipv6addr - res->host);
|
||||
for (i = offset; i < len; ++i) {
|
||||
if (uri[i] == '#') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i - offset == 0) {
|
||||
res->path = "/";
|
||||
res->pathlen = 1;
|
||||
} else {
|
||||
res->path = &uri[offset];
|
||||
res->pathlen = i - offset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int connect_to_host(const char *host, size_t hostlen, uint16_t port)
|
||||
{
|
||||
int ret;
|
||||
struct addrinfo hints = {
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
};
|
||||
|
||||
char service[6];
|
||||
snprintf(service, sizeof(service), "%u", port);
|
||||
|
||||
char *use_host = calloc(1, hostlen + 1);
|
||||
if (!use_host) {
|
||||
return -1;
|
||||
}
|
||||
strncpy(use_host, host, hostlen);
|
||||
|
||||
struct addrinfo *res;
|
||||
ret = getaddrinfo(use_host, service, &hints, &res);
|
||||
if (ret) {
|
||||
free(use_host);
|
||||
return -1;
|
||||
}
|
||||
free(use_host);
|
||||
|
||||
ret = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (ret < 0) {
|
||||
goto err_freeaddr;
|
||||
}
|
||||
|
||||
int fd = ret;
|
||||
ret = connect(fd, res->ai_addr, res->ai_addrlen);
|
||||
if (ret < 0) {
|
||||
goto err_freesocket;
|
||||
}
|
||||
|
||||
return fd;
|
||||
|
||||
err_freesocket:
|
||||
close(fd);
|
||||
err_freeaddr:
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
// Copyright 2017 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_EXAMPLE_CONNECT_LIB_H_
|
||||
#define __ESP_EXAMPLE_CONNECT_LIB_H_
|
||||
|
||||
struct uri {
|
||||
const char *host;
|
||||
/* In this program, path contains query component as well. */
|
||||
const char *path;
|
||||
size_t pathlen;
|
||||
const char *hostport;
|
||||
size_t hostlen;
|
||||
size_t hostportlen;
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
/* connect() to a TCP host, return socket descriptor */
|
||||
int connect_to_host(const char *host, size_t hostlen, uint16_t port);
|
||||
|
||||
/* Parse a URI into its components */
|
||||
int parse_uri(struct uri *res, const char *uri);
|
||||
|
||||
#endif /* ! __ESP_EXAMPLE_CONNECT_LIB_H_ */
|
@ -21,8 +21,8 @@
|
||||
#include <ctype.h>
|
||||
#include <netdb.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp-tls.h>
|
||||
|
||||
#include "connectlib.h"
|
||||
#include "sh2lib.h"
|
||||
|
||||
static const char *TAG = "sh2lib";
|
||||
@ -75,10 +75,9 @@ static int do_ssl_connect(struct sh2lib_handle *hd, int sockfd, const char *host
|
||||
static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data,
|
||||
size_t length)
|
||||
{
|
||||
int rv = SSL_write(hd->ssl, data, (int)length);
|
||||
int rv = esp_tls_conn_write(hd->http2_tls, (const char *)data, (int)length);
|
||||
if (rv <= 0) {
|
||||
int err = SSL_get_error(hd->ssl, rv);
|
||||
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
if (rv == -SSL_ERROR_WANT_WRITE || rv == -SSL_ERROR_WANT_READ) {
|
||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||
} else {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
@ -128,10 +127,9 @@ static ssize_t callback_recv(nghttp2_session *session, uint8_t *buf,
|
||||
{
|
||||
struct sh2lib_handle *hd = user_data;
|
||||
int rv;
|
||||
rv = SSL_read(hd->ssl, buf, (int)length);
|
||||
rv = esp_tls_conn_read(hd->http2_tls, (char *)buf, (int)length);
|
||||
if (rv < 0) {
|
||||
int err = SSL_get_error(hd->ssl, rv);
|
||||
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
|
||||
if (rv == -SSL_ERROR_WANT_WRITE || rv == -SSL_ERROR_WANT_READ) {
|
||||
rv = NGHTTP2_ERR_WOULDBLOCK;
|
||||
} else {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
@ -281,24 +279,10 @@ static int do_http2_connect(struct sh2lib_handle *hd)
|
||||
int sh2lib_connect(struct sh2lib_handle *hd, const char *uri)
|
||||
{
|
||||
memset(hd, 0, sizeof(*hd));
|
||||
|
||||
struct uri res;
|
||||
/* Parse the URI */
|
||||
if (parse_uri(&res, uri) != 0) {
|
||||
ESP_LOGE(TAG, "[sh2-connect] Failed to parse URI");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TCP connection with the server */
|
||||
int sockfd = connect_to_host(res.host, res.hostlen, res.port);
|
||||
if (sockfd < 0) {
|
||||
ESP_LOGE(TAG, "[sh2-connect] Failed to connect to %s", uri);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* SSL Connection on the socket */
|
||||
if (do_ssl_connect(hd, sockfd, res.host) != 0) {
|
||||
ESP_LOGE(TAG, "[sh2-connect] SSL Handshake failed with %s", uri);
|
||||
struct esp_tls_cfg tls_cfg;
|
||||
tls_cfg.alpn_protos = (unsigned char *) "\x02h2";
|
||||
if ((hd->http2_tls = esp_tls_conn_http_new(uri, &tls_cfg)) == NULL) {
|
||||
ESP_LOGE(TAG, "[sh2-connect] esp-tls connection failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -308,6 +292,9 @@ int sh2lib_connect(struct sh2lib_handle *hd, const char *uri)
|
||||
goto error;
|
||||
}
|
||||
|
||||
int flags = fcntl(hd->http2_tls->sockfd, F_GETFL, 0);
|
||||
fcntl(hd->http2_tls->sockfd, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
sh2lib_free(hd);
|
||||
@ -320,17 +307,9 @@ void sh2lib_free(struct sh2lib_handle *hd)
|
||||
nghttp2_session_del(hd->http2_sess);
|
||||
hd->http2_sess = NULL;
|
||||
}
|
||||
if (hd->ssl) {
|
||||
SSL_free(hd->ssl);
|
||||
hd->ssl = NULL;
|
||||
}
|
||||
if (hd->ssl_ctx) {
|
||||
SSL_CTX_free(hd->ssl_ctx);
|
||||
hd->ssl_ctx = NULL;
|
||||
}
|
||||
if (hd->sockfd) {
|
||||
close(hd->sockfd);
|
||||
hd->ssl_ctx = 0;
|
||||
if (hd->http2_tls) {
|
||||
esp_tls_conn_delete(hd->http2_tls);
|
||||
hd->http2_tls = NULL;
|
||||
}
|
||||
if (hd->hostname) {
|
||||
free(hd->hostname);
|
||||
@ -346,11 +325,13 @@ int sh2lib_execute(struct sh2lib_handle *hd)
|
||||
ESP_LOGE(TAG, "[sh2-execute] HTTP2 session send failed %d", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = nghttp2_session_recv(hd->http2_sess);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "[sh2-execute] HTTP2 session recv failed %d", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -33,14 +33,10 @@
|
||||
* @brief Handle for working with sh2lib APIs
|
||||
*/
|
||||
struct sh2lib_handle {
|
||||
/* Ideally, CTX is per-program, so we could potentially take it out of this
|
||||
* per-connection structure
|
||||
*/
|
||||
SSL_CTX *ssl_ctx; /*!< Pointer to the SSL context */
|
||||
SSL *ssl; /*!< Pointer to the SSL handle */
|
||||
nghttp2_session *http2_sess; /*!< Pointer to the HTTP2 session handle */
|
||||
int sockfd; /*!< Socket file descriptor */
|
||||
char *hostname; /*!< The hostname we are connected to */
|
||||
struct esp_tls *http2_tls; /*!< Pointer to the TLS session handle */
|
||||
};
|
||||
|
||||
/** Flag indicating receive stream is reset */
|
||||
|
Loading…
x
Reference in New Issue
Block a user