添加智能灯固件代码

This commit is contained in:
kerwincui
2021-07-13 17:14:51 +08:00
parent 332f74dd17
commit ecc0b91b8b
2568 changed files with 229441 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(http2-request)

View File

@@ -0,0 +1,11 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := http2-request
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,6 @@
# HTTP/2 Request Example
Established HTTP/2 connection with https://http2.golang.org
- Performs a GET on /clockstream
- Performs a PUT on /ECHO

View File

@@ -0,0 +1,4 @@
idf_component_register(SRCS "sh2lib.c"
INCLUDE_DIRS .
REQUIRES nghttp
PRIV_REQUIRES lwip esp-tls)

View File

@@ -0,0 +1 @@
COMPONENT_ADD_INCLUDEDIRS := .

View File

@@ -0,0 +1,369 @@
// 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.
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <netdb.h>
#include <esp_log.h>
#include <http_parser.h>
#include "sh2lib.h"
static const char *TAG = "sh2lib";
#define DBG_FRAME_SEND 1
/*
* The implementation of nghttp2_send_callback type. Here we write
* |data| with size |length| to the network and return the number of
* bytes actually written. See the documentation of
* nghttp2_send_callback for the details.
*/
static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data,
size_t length)
{
int rv = esp_tls_conn_write(hd->http2_tls, data, length);
if (rv <= 0) {
if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) {
rv = NGHTTP2_ERR_WOULDBLOCK;
} else {
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
return rv;
}
static ssize_t callback_send(nghttp2_session *session, const uint8_t *data,
size_t length, int flags, void *user_data)
{
int rv = 0;
struct sh2lib_handle *hd = user_data;
int copy_offset = 0;
int pending_data = length;
/* Send data in 1000 byte chunks */
while (copy_offset != length) {
int chunk_len = pending_data > 1000 ? 1000 : pending_data;
int subrv = callback_send_inner(hd, data + copy_offset, chunk_len);
if (subrv <= 0) {
if (copy_offset == 0) {
/* If no data is transferred, send the error code */
rv = subrv;
}
break;
}
copy_offset += subrv;
pending_data -= subrv;
rv += subrv;
}
return rv;
}
/*
* The implementation of nghttp2_recv_callback type. Here we read data
* from the network and write them in |buf|. The capacity of |buf| is
* |length| bytes. Returns the number of bytes stored in |buf|. See
* the documentation of nghttp2_recv_callback for the details.
*/
static ssize_t callback_recv(nghttp2_session *session, uint8_t *buf,
size_t length, int flags, void *user_data)
{
struct sh2lib_handle *hd = user_data;
int rv;
rv = esp_tls_conn_read(hd->http2_tls, (char *)buf, (int)length);
if (rv < 0) {
if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) {
rv = NGHTTP2_ERR_WOULDBLOCK;
} else {
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
}
} else if (rv == 0) {
rv = NGHTTP2_ERR_EOF;
}
return rv;
}
const char *sh2lib_frame_type_str(int type)
{
switch (type) {
case NGHTTP2_HEADERS:
return "HEADERS";
break;
case NGHTTP2_RST_STREAM:
return "RST_STREAM";
break;
case NGHTTP2_GOAWAY:
return "GOAWAY";
break;
case NGHTTP2_DATA:
return "DATA";
break;
case NGHTTP2_SETTINGS:
return "SETTINGS";
break;
case NGHTTP2_PUSH_PROMISE:
return "PUSH_PROMISE";
break;
case NGHTTP2_PING:
return "PING";
break;
default:
return "other";
break;
}
}
static int callback_on_frame_send(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data)
{
ESP_LOGD(TAG, "[frame-send] frame type %s", sh2lib_frame_type_str(frame->hd.type));
switch (frame->hd.type) {
case NGHTTP2_HEADERS:
if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
ESP_LOGD(TAG, "[frame-send] C ----------------------------> S (HEADERS)");
#if DBG_FRAME_SEND
ESP_LOGD(TAG, "[frame-send] headers nv-len = %d", frame->headers.nvlen);
const nghttp2_nv *nva = frame->headers.nva;
size_t i;
for (i = 0; i < frame->headers.nvlen; ++i) {
ESP_LOGD(TAG, "[frame-send] %s : %s", nva[i].name, nva[i].value);
}
#endif
}
break;
}
return 0;
}
static int callback_on_frame_recv(nghttp2_session *session,
const nghttp2_frame *frame, void *user_data)
{
ESP_LOGD(TAG, "[frame-recv][sid: %d] frame type %s", frame->hd.stream_id, sh2lib_frame_type_str(frame->hd.type));
if (frame->hd.type != NGHTTP2_DATA) {
return 0;
}
/* Subsequent processing only for data frame */
sh2lib_frame_data_recv_cb_t data_recv_cb = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
if (data_recv_cb) {
struct sh2lib_handle *h2 = user_data;
(*data_recv_cb)(h2, NULL, 0, DATA_RECV_FRAME_COMPLETE);
}
return 0;
}
static int callback_on_stream_close(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *user_data)
{
ESP_LOGD(TAG, "[stream-close][sid %d]", stream_id);
sh2lib_frame_data_recv_cb_t data_recv_cb = nghttp2_session_get_stream_user_data(session, stream_id);
if (data_recv_cb) {
struct sh2lib_handle *h2 = user_data;
(*data_recv_cb)(h2, NULL, 0, DATA_RECV_RST_STREAM);
}
return 0;
}
static int callback_on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
int32_t stream_id, const uint8_t *data,
size_t len, void *user_data)
{
sh2lib_frame_data_recv_cb_t data_recv_cb;
ESP_LOGD(TAG, "[data-chunk][sid:%d]", stream_id);
data_recv_cb = nghttp2_session_get_stream_user_data(session, stream_id);
if (data_recv_cb) {
ESP_LOGD(TAG, "[data-chunk] C <---------------------------- S (DATA chunk)"
"%lu bytes",
(unsigned long int)len);
struct sh2lib_handle *h2 = user_data;
(*data_recv_cb)(h2, (char *)data, len, 0);
/* TODO: What to do with the return value: look for pause/abort */
}
return 0;
}
static int callback_on_header(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen, const uint8_t *value,
size_t valuelen, uint8_t flags, void *user_data)
{
ESP_LOGD(TAG, "[hdr-recv][sid:%d] %s : %s", frame->hd.stream_id, name, value);
return 0;
}
static int do_http2_connect(struct sh2lib_handle *hd)
{
int ret;
nghttp2_session_callbacks *callbacks;
nghttp2_session_callbacks_new(&callbacks);
nghttp2_session_callbacks_set_send_callback(callbacks, callback_send);
nghttp2_session_callbacks_set_recv_callback(callbacks, callback_recv);
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, callback_on_frame_send);
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, callback_on_frame_recv);
nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, callback_on_stream_close);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, callback_on_data_chunk_recv);
nghttp2_session_callbacks_set_on_header_callback(callbacks, callback_on_header);
ret = nghttp2_session_client_new(&hd->http2_sess, callbacks, hd);
if (ret != 0) {
ESP_LOGE(TAG, "[sh2-connect] New http2 session failed");
nghttp2_session_callbacks_del(callbacks);
return -1;
}
nghttp2_session_callbacks_del(callbacks);
/* Create the SETTINGS frame */
ret = nghttp2_submit_settings(hd->http2_sess, NGHTTP2_FLAG_NONE, NULL, 0);
if (ret != 0) {
ESP_LOGE(TAG, "[sh2-connect] Submit settings failed");
return -1;
}
return 0;
}
int sh2lib_connect(struct sh2lib_handle *hd, const char *uri)
{
memset(hd, 0, sizeof(*hd));
const char *proto[] = {"h2", NULL};
esp_tls_cfg_t tls_cfg = {
.alpn_protos = proto,
.non_block = true,
.timeout_ms = 10 * 1000,
};
if ((hd->http2_tls = esp_tls_conn_http_new(uri, &tls_cfg)) == NULL) {
ESP_LOGE(TAG, "[sh2-connect] esp-tls connection failed");
goto error;
}
struct http_parser_url u;
http_parser_url_init(&u);
http_parser_parse_url(uri, strlen(uri), 0, &u);
hd->hostname = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len);
/* HTTP/2 Connection */
if (do_http2_connect(hd) != 0) {
ESP_LOGE(TAG, "[sh2-connect] HTTP2 Connection failed with %s", uri);
goto error;
}
return 0;
error:
sh2lib_free(hd);
return -1;
}
void sh2lib_free(struct sh2lib_handle *hd)
{
if (hd->http2_sess) {
nghttp2_session_del(hd->http2_sess);
hd->http2_sess = NULL;
}
if (hd->http2_tls) {
esp_tls_conn_delete(hd->http2_tls);
hd->http2_tls = NULL;
}
if (hd->hostname) {
free(hd->hostname);
hd->hostname = NULL;
}
}
int sh2lib_execute(struct sh2lib_handle *hd)
{
int ret;
ret = nghttp2_session_send(hd->http2_sess);
if (ret != 0) {
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;
}
int sh2lib_do_get_with_nv(struct sh2lib_handle *hd, const nghttp2_nv *nva, size_t nvlen, sh2lib_frame_data_recv_cb_t recv_cb)
{
int ret = nghttp2_submit_request(hd->http2_sess, NULL, nva, nvlen, NULL, recv_cb);
if (ret < 0) {
ESP_LOGE(TAG, "[sh2-do-get] HEADERS call failed");
return -1;
}
return ret;
}
int sh2lib_do_get(struct sh2lib_handle *hd, const char *path, sh2lib_frame_data_recv_cb_t recv_cb)
{
const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "GET"),
SH2LIB_MAKE_NV(":scheme", "https"),
SH2LIB_MAKE_NV(":authority", hd->hostname),
SH2LIB_MAKE_NV(":path", path),
};
return sh2lib_do_get_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), recv_cb);
}
ssize_t sh2lib_data_provider_cb(nghttp2_session *session, int32_t stream_id, uint8_t *buf,
size_t length, uint32_t *data_flags,
nghttp2_data_source *source, void *user_data)
{
struct sh2lib_handle *h2 = user_data;
sh2lib_putpost_data_cb_t data_cb = source->ptr;
return (*data_cb)(h2, (char *)buf, length, data_flags);
}
int sh2lib_do_putpost_with_nv(struct sh2lib_handle *hd, const nghttp2_nv *nva, size_t nvlen,
sh2lib_putpost_data_cb_t send_cb,
sh2lib_frame_data_recv_cb_t recv_cb)
{
nghttp2_data_provider sh2lib_data_provider;
sh2lib_data_provider.read_callback = sh2lib_data_provider_cb;
sh2lib_data_provider.source.ptr = send_cb;
int ret = nghttp2_submit_request(hd->http2_sess, NULL, nva, nvlen, &sh2lib_data_provider, recv_cb);
if (ret < 0) {
ESP_LOGE(TAG, "[sh2-do-putpost] HEADERS call failed");
return -1;
}
return ret;
}
int sh2lib_do_post(struct sh2lib_handle *hd, const char *path,
sh2lib_putpost_data_cb_t send_cb,
sh2lib_frame_data_recv_cb_t recv_cb)
{
const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "POST"),
SH2LIB_MAKE_NV(":scheme", "https"),
SH2LIB_MAKE_NV(":authority", hd->hostname),
SH2LIB_MAKE_NV(":path", path),
};
return sh2lib_do_putpost_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), send_cb, recv_cb);
}
int sh2lib_do_put(struct sh2lib_handle *hd, const char *path,
sh2lib_putpost_data_cb_t send_cb,
sh2lib_frame_data_recv_cb_t recv_cb)
{
const nghttp2_nv nva[] = { SH2LIB_MAKE_NV(":method", "PUT"),
SH2LIB_MAKE_NV(":scheme", "https"),
SH2LIB_MAKE_NV(":authority", hd->hostname),
SH2LIB_MAKE_NV(":path", path),
};
return sh2lib_do_putpost_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), send_cb, recv_cb);
}

View File

@@ -0,0 +1,262 @@
// 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_SH2_LIB_H_
#define __ESP_EXAMPLE_SH2_LIB_H_
#include "esp_tls.h"
#include <nghttp2/nghttp2.h>
/*
* This is a thin API wrapper over nghttp2 that offers simplified APIs for usage
* in the application. The intention of this wrapper is to act as a stepping
* stone to quickly get started with using the HTTP/2 client. Since the focus is
* on simplicity, not all the features of HTTP/2 are supported through this
* wrapper. Once you are fairly comfortable with nghttp2, feel free to directly
* use nghttp2 APIs or make changes to sh2lib.c for realising your objectives.
*
* TODO:
* - Allowing to query response code, content-type etc in the receive callback
* - A simple function for per-stream header callback
*/
/**
* @brief Handle for working with sh2lib APIs
*/
struct sh2lib_handle {
nghttp2_session *http2_sess; /*!< Pointer to the HTTP2 session handle */
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 */
#define DATA_RECV_RST_STREAM 1
/** Flag indicating frame is completely received */
#define DATA_RECV_FRAME_COMPLETE 2
/**
* @brief Function Prototype for data receive callback
*
* This function gets called whenever data is received on any stream. The
* function is also called for indicating events like frame receive complete, or
* end of stream. The function may get called multiple times as long as data is
* received on the stream.
*
* @param[in] handle Pointer to the sh2lib handle.
* @param[in] data Pointer to a buffer that contains the data received.
* @param[in] len The length of valid data stored at the 'data' pointer.
* @param[in] flags Flags indicating whether the stream is reset (DATA_RECV_RST_STREAM) or
* this particularly frame is completely received
* DATA_RECV_FRAME_COMPLETE).
*
* @return The function should return 0
*/
typedef int (*sh2lib_frame_data_recv_cb_t)(struct sh2lib_handle *handle, const char *data, size_t len, int flags);
/**
* @brief Function Prototype for callback to send data in PUT/POST
*
* This function gets called whenever nghttp2 wishes to send data, like for
* PUT/POST requests to the server. The function keeps getting called until this
* function sets the flag NGHTTP2_DATA_FLAG_EOF to indicate end of data.
*
* @param[in] handle Pointer to the sh2lib handle.
* @param[out] data Pointer to a buffer that should contain the data to send.
* @param[in] len The maximum length of data that can be sent out by this function.
* @param[out] data_flags Pointer to the data flags. The NGHTTP2_DATA_FLAG_EOF
* should be set in the data flags to indicate end of new data.
*
* @return The function should return the number of valid bytes stored in the
* data pointer
*/
typedef int (*sh2lib_putpost_data_cb_t)(struct sh2lib_handle *handle, char *data, size_t len, uint32_t *data_flags);
/**
* @brief Connect to a URI using HTTP/2
*
* This API opens an HTTP/2 connection with the provided URI. If successful, the
* hd pointer is populated with a valid handle for subsequent communication.
*
* Only 'https' URIs are supported.
*
* @param[out] hd Pointer to a variable of the type 'struct sh2lib_handle'.
* @param[in] uri Pointer to the URI that should be connected to.
*
* @return
* - ESP_OK if the connection was successful
* - ESP_FAIL if the connection fails
*/
int sh2lib_connect(struct sh2lib_handle *hd, const char *uri);
/**
* @brief Free a sh2lib handle
*
* This API frees-up an sh2lib handle, thus closing any open connections that
* may be associated with this handle, and freeing up any resources.
*
* @param[in] hd Pointer to a variable of the type 'struct sh2lib_handle'.
*
*/
void sh2lib_free(struct sh2lib_handle *hd);
/**
* @brief Setup an HTTP GET request stream
*
* This API sets up an HTTP GET request to be sent out to the server. A new
* stream is created for handling the request. Once the request is setup, the
* API sh2lib_execute() must be called to actually perform the socket I/O with
* the server.
*
* @param[in] hd Pointer to a variable of the type 'struct sh2lib_handle'.
* @param[in] path Pointer to the string that contains the resource to
* perform the HTTP GET operation on (for example, /users).
* @param[in] recv_cb The callback function that should be called for
* processing the request's response
*
* @return
* - ESP_OK if request setup is successful
* - ESP_FAIL if the request setup fails
*/
int sh2lib_do_get(struct sh2lib_handle *hd, const char *path, sh2lib_frame_data_recv_cb_t recv_cb);
/**
* @brief Setup an HTTP POST request stream
*
* This API sets up an HTTP POST request to be sent out to the server. A new
* stream is created for handling the request. Once the request is setup, the
* API sh2lib_execute() must be called to actually perform the socket I/O with
* the server.
*
* @param[in] hd Pointer to a variable of the type 'struct sh2lib_handle'.
* @param[in] path Pointer to the string that contains the resource to
* perform the HTTP POST operation on (for example, /users).
* @param[in] send_cb The callback function that should be called for
* sending data as part of this request.
* @param[in] recv_cb The callback function that should be called for
* processing the request's response
*
* @return
* - ESP_OK if request setup is successful
* - ESP_FAIL if the request setup fails
*/
int sh2lib_do_post(struct sh2lib_handle *hd, const char *path,
sh2lib_putpost_data_cb_t send_cb,
sh2lib_frame_data_recv_cb_t recv_cb);
/**
* @brief Setup an HTTP PUT request stream
*
* This API sets up an HTTP PUT request to be sent out to the server. A new
* stream is created for handling the request. Once the request is setup, the
* API sh2lib_execute() must be called to actually perform the socket I/O with
* the server.
*
* @param[in] hd Pointer to a variable of the type 'struct sh2lib_handle'.
* @param[in] path Pointer to the string that contains the resource to
* perform the HTTP PUT operation on (for example, /users).
* @param[in] send_cb The callback function that should be called for
* sending data as part of this request.
* @param[in] recv_cb The callback function that should be called for
* processing the request's response
*
* @return
* - ESP_OK if request setup is successful
* - ESP_FAIL if the request setup fails
*/
int sh2lib_do_put(struct sh2lib_handle *hd, const char *path,
sh2lib_putpost_data_cb_t send_cb,
sh2lib_frame_data_recv_cb_t recv_cb);
/**
* @brief Execute send/receive on an HTTP/2 connection
*
* While the API sh2lib_do_get(), sh2lib_do_post() setup the requests to be
* initiated with the server, this API performs the actual data send/receive
* operations on the HTTP/2 connection. The callback functions are accordingly
* called during the processing of these requests.
*
* @param[in] hd Pointer to a variable of the type 'struct sh2lib_handle'
*
* @return
* - ESP_OK if the connection was successful
* - ESP_FAIL if the connection fails
*/
int sh2lib_execute(struct sh2lib_handle *hd);
#define SH2LIB_MAKE_NV(NAME, VALUE) \
{ \
(uint8_t *)NAME, (uint8_t *)VALUE, strlen(NAME), strlen(VALUE), \
NGHTTP2_NV_FLAG_NONE \
}
/**
* @brief Setup an HTTP GET request stream with custom name-value pairs
*
* For a simpler version of the API, please refer to sh2lib_do_get().
*
* This API sets up an HTTP GET request to be sent out to the server. A new
* stream is created for handling the request. Once the request is setup, the
* API sh2lib_execute() must be called to actually perform the socket I/O with
* the server.
*
* Please note that the following name value pairs MUST be a part of the request
* - name:value
* - ":method":"GET"
* - ":scheme":"https"
* - ":path":<the-path-for-the-GET-operation> (for example, /users)
*
* @param[in] hd Pointer to a variable of the type 'struct sh2lib_handle'.
* @param[in] nva An array of name-value pairs that should be part of the request.
* @param[in] nvlen The number of elements in the array pointed to by 'nva'.
* @param[in] recv_cb The callback function that should be called for
* processing the request's response
*
* @return
* - ESP_OK if request setup is successful
* - ESP_FAIL if the request setup fails
*/
int sh2lib_do_get_with_nv(struct sh2lib_handle *hd, const nghttp2_nv *nva, size_t nvlen, sh2lib_frame_data_recv_cb_t recv_cb);
/**
* @brief Setup an HTTP PUT/POST request stream with custom name-value pairs
*
* For a simpler version of the API, please refer to sh2lib_do_put() or
* sh2lib_do_post().
*
* This API sets up an HTTP PUT/POST request to be sent out to the server. A new
* stream is created for handling the request. Once the request is setup, the
* API sh2lib_execute() must be called to actually perform the socket I/O with
* the server.
*
* Please note that the following name value pairs MUST be a part of the request
* - name:value
* - ":method":"PUT" (or POST)
* - ":scheme":"https"
* - ":path":<the-path-for-the-PUT-operation> (for example, /users)
*
* @param[in] hd Pointer to a variable of the type 'struct sh2lib_handle'.
* @param[in] nva An array of name-value pairs that should be part of the request.
* @param[in] nvlen The number of elements in the array pointed to by 'nva'.
* @param[in] send_cb The callback function that should be called for
* sending data as part of this request.
* @param[in] recv_cb The callback function that should be called for
* processing the request's response
*
* @return
* - ESP_OK if request setup is successful
* - ESP_FAIL if the request setup fails
*/
int sh2lib_do_putpost_with_nv(struct sh2lib_handle *hd, const nghttp2_nv *nva, size_t nvlen,
sh2lib_putpost_data_cb_t send_cb,
sh2lib_frame_data_recv_cb_t recv_cb);
#endif /* ! __ESP_EXAMPLE_SH2_LIB_H_ */

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "http2_request_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,144 @@
/* HTTP2 GET Example using nghttp2
Contacts http2.golang.org and executes the GET/PUT requests. A thin API
wrapper on top of nghttp2, to properly demonstrate the interactions.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "lwip/apps/sntp.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "protocol_examples_common.h"
#include "esp_netif.h"
#include "sh2lib.h"
/* The HTTP/2 server to connect to */
#define HTTP2_SERVER_URI "https://http2.golang.org"
/* A GET request that keeps streaming current time every second */
#define HTTP2_STREAMING_GET_PATH "/clockstream"
/* A PUT request that echoes whatever we had sent to it */
#define HTTP2_PUT_PATH "/ECHO"
int handle_get_response(struct sh2lib_handle *handle, const char *data, size_t len, int flags)
{
if (len) {
printf("[get-response] %.*s\n", len, data);
}
if (flags == DATA_RECV_FRAME_COMPLETE) {
printf("[get-response] Frame fully received\n");
}
if (flags == DATA_RECV_RST_STREAM) {
printf("[get-response] Stream Closed\n");
}
return 0;
}
int handle_echo_response(struct sh2lib_handle *handle, const char *data, size_t len, int flags)
{
if (len) {
printf("[echo-response] %.*s\n", len, data);
}
if (flags == DATA_RECV_FRAME_COMPLETE) {
printf("[echo-response] Frame fully received\n");
}
if (flags == DATA_RECV_RST_STREAM) {
printf("[echo-response] Stream Closed\n");
}
return 0;
}
int send_put_data(struct sh2lib_handle *handle, char *buf, size_t length, uint32_t *data_flags)
{
#define DATA_TO_SEND "Hello World"
int copylen = strlen(DATA_TO_SEND);
if (copylen < length) {
printf("[data-prvd] Sending %d bytes\n", copylen);
memcpy(buf, DATA_TO_SEND, copylen);
} else {
copylen = 0;
}
(*data_flags) |= NGHTTP2_DATA_FLAG_EOF;
return copylen;
}
static void set_time(void)
{
struct timeval tv = {
.tv_sec = 1509449941,
};
struct timezone tz = {
0, 0
};
settimeofday(&tv, &tz);
/* Start SNTP service */
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_init();
}
static void http2_task(void *args)
{
/* Set current time: proper system time is required for TLS based
* certificate verification.
*/
set_time();
/* HTTP2: one connection multiple requests. Do the TLS/TCP connection first */
printf("Connecting to server\n");
struct sh2lib_handle hd;
if (sh2lib_connect(&hd, HTTP2_SERVER_URI) != 0) {
printf("Failed to connect\n");
vTaskDelete(NULL);
return;
}
printf("Connection done\n");
/* HTTP GET */
sh2lib_do_get(&hd, HTTP2_STREAMING_GET_PATH, handle_get_response);
/* HTTP PUT */
sh2lib_do_put(&hd, HTTP2_PUT_PATH, send_put_data, handle_echo_response);
while (1) {
/* Process HTTP2 send/receive */
if (sh2lib_execute(&hd) < 0) {
printf("Error in send/receive\n");
break;
}
vTaskDelay(2);
}
sh2lib_free(&hd);
vTaskDelete(NULL);
}
void app_main(void)
{
ESP_ERROR_CHECK( nvs_flash_init() );
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
xTaskCreate(&http2_task, "http2_task", (1024 * 32), NULL, 5, NULL);
}