Use esp-tls in the http2 example

This commit is contained in:
Kedar Sovani 2018-02-04 10:02:31 +05:30 committed by Jitin George
parent 070884fc2e
commit 8211a16207
6 changed files with 47 additions and 239 deletions

View File

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

View File

@ -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);

View File

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

View File

@ -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_ */

View File

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

View File

@ -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 */