mirror of https://github.com/arendst/Tasmota.git
Berry class ``webclient`` for HTTP/HTTPS requests
This commit is contained in:
parent
86205d0c29
commit
3d5c68b850
|
@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
|
|||
## [9.5.0.8]
|
||||
### Added
|
||||
- Command ``WebGetConfig <url>`` if ``#define USE_WEBGETCONFIG`` is enabled to restore/init configuration from external webserver (#13034)
|
||||
- Berry class ``webclient`` for HTTP/HTTPS requests
|
||||
|
||||
### Fixed
|
||||
- OpenTherm invalid JSON (#13028)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,270 @@
|
|||
/**
|
||||
* HTTPClientLight.h
|
||||
*
|
||||
* Created on: 02.11.2015
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
* This file is part of the HTTPClient for Arduino.
|
||||
* Port to ESP32 by Evandro Luis Copercini (2017),
|
||||
* changed fingerprints to CA verification.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HTTPClient_Light_H_
|
||||
#define HTTPClient_Light_H_
|
||||
|
||||
#define HTTPCLIENT_1_1_COMPATIBLE
|
||||
|
||||
#include <memory>
|
||||
#include <Arduino.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
#include <HTTPClient.h> // import definitions from the original code
|
||||
|
||||
#include "tasmota_options.h"
|
||||
|
||||
#define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000)
|
||||
|
||||
/// HTTP client errors
|
||||
#define HTTPC_ERROR_CONNECTION_REFUSED (-1)
|
||||
#define HTTPC_ERROR_SEND_HEADER_FAILED (-2)
|
||||
#define HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3)
|
||||
#define HTTPC_ERROR_NOT_CONNECTED (-4)
|
||||
#define HTTPC_ERROR_CONNECTION_LOST (-5)
|
||||
#define HTTPC_ERROR_NO_STREAM (-6)
|
||||
#define HTTPC_ERROR_NO_HTTP_SERVER (-7)
|
||||
#define HTTPC_ERROR_TOO_LESS_RAM (-8)
|
||||
#define HTTPC_ERROR_ENCODING (-9)
|
||||
#define HTTPC_ERROR_STREAM_WRITE (-10)
|
||||
#define HTTPC_ERROR_READ_TIMEOUT (-11)
|
||||
|
||||
/// size for the stream handling
|
||||
#define HTTP_TCP_BUFFER_SIZE (1460)
|
||||
|
||||
/// HTTP codes see RFC7231
|
||||
// typedef enum {
|
||||
// HTTP_CODE_CONTINUE = 100,
|
||||
// HTTP_CODE_SWITCHING_PROTOCOLS = 101,
|
||||
// HTTP_CODE_PROCESSING = 102,
|
||||
// HTTP_CODE_OK = 200,
|
||||
// HTTP_CODE_CREATED = 201,
|
||||
// HTTP_CODE_ACCEPTED = 202,
|
||||
// HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
// HTTP_CODE_NO_CONTENT = 204,
|
||||
// HTTP_CODE_RESET_CONTENT = 205,
|
||||
// HTTP_CODE_PARTIAL_CONTENT = 206,
|
||||
// HTTP_CODE_MULTI_STATUS = 207,
|
||||
// HTTP_CODE_ALREADY_REPORTED = 208,
|
||||
// HTTP_CODE_IM_USED = 226,
|
||||
// HTTP_CODE_MULTIPLE_CHOICES = 300,
|
||||
// HTTP_CODE_MOVED_PERMANENTLY = 301,
|
||||
// HTTP_CODE_FOUND = 302,
|
||||
// HTTP_CODE_SEE_OTHER = 303,
|
||||
// HTTP_CODE_NOT_MODIFIED = 304,
|
||||
// HTTP_CODE_USE_PROXY = 305,
|
||||
// HTTP_CODE_TEMPORARY_REDIRECT = 307,
|
||||
// HTTP_CODE_PERMANENT_REDIRECT = 308,
|
||||
// HTTP_CODE_BAD_REQUEST = 400,
|
||||
// HTTP_CODE_UNAUTHORIZED = 401,
|
||||
// HTTP_CODE_PAYMENT_REQUIRED = 402,
|
||||
// HTTP_CODE_FORBIDDEN = 403,
|
||||
// HTTP_CODE_NOT_FOUND = 404,
|
||||
// HTTP_CODE_METHOD_NOT_ALLOWED = 405,
|
||||
// HTTP_CODE_NOT_ACCEPTABLE = 406,
|
||||
// HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
// HTTP_CODE_REQUEST_TIMEOUT = 408,
|
||||
// HTTP_CODE_CONFLICT = 409,
|
||||
// HTTP_CODE_GONE = 410,
|
||||
// HTTP_CODE_LENGTH_REQUIRED = 411,
|
||||
// HTTP_CODE_PRECONDITION_FAILED = 412,
|
||||
// HTTP_CODE_PAYLOAD_TOO_LARGE = 413,
|
||||
// HTTP_CODE_URI_TOO_LONG = 414,
|
||||
// HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
// HTTP_CODE_RANGE_NOT_SATISFIABLE = 416,
|
||||
// HTTP_CODE_EXPECTATION_FAILED = 417,
|
||||
// HTTP_CODE_MISDIRECTED_REQUEST = 421,
|
||||
// HTTP_CODE_UNPROCESSABLE_ENTITY = 422,
|
||||
// HTTP_CODE_LOCKED = 423,
|
||||
// HTTP_CODE_FAILED_DEPENDENCY = 424,
|
||||
// HTTP_CODE_UPGRADE_REQUIRED = 426,
|
||||
// HTTP_CODE_PRECONDITION_REQUIRED = 428,
|
||||
// HTTP_CODE_TOO_MANY_REQUESTS = 429,
|
||||
// HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||
// HTTP_CODE_INTERNAL_SERVER_ERROR = 500,
|
||||
// HTTP_CODE_NOT_IMPLEMENTED = 501,
|
||||
// HTTP_CODE_BAD_GATEWAY = 502,
|
||||
// HTTP_CODE_SERVICE_UNAVAILABLE = 503,
|
||||
// HTTP_CODE_GATEWAY_TIMEOUT = 504,
|
||||
// HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||
// HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506,
|
||||
// HTTP_CODE_INSUFFICIENT_STORAGE = 507,
|
||||
// HTTP_CODE_LOOP_DETECTED = 508,
|
||||
// HTTP_CODE_NOT_EXTENDED = 510,
|
||||
// HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
// } t_http_codes;
|
||||
|
||||
// typedef enum {
|
||||
// HTTPC_TE_IDENTITY,
|
||||
// HTTPC_TE_CHUNKED
|
||||
// } transferEncoding_t;
|
||||
|
||||
/**
|
||||
* redirection follow mode.
|
||||
* + `HTTPC_DISABLE_FOLLOW_REDIRECTS` - no redirection will be followed.
|
||||
* + `HTTPC_STRICT_FOLLOW_REDIRECTS` - strict RFC2616, only requests using
|
||||
* GET or HEAD methods will be redirected (using the same method),
|
||||
* since the RFC requires end-user confirmation in other cases.
|
||||
* + `HTTPC_FORCE_FOLLOW_REDIRECTS` - all redirections will be followed,
|
||||
* regardless of a used method. New request will use the same method,
|
||||
* and they will include the same body data and the same headers.
|
||||
* In the sense of the RFC, it's just like every redirection is confirmed.
|
||||
*/
|
||||
// typedef enum {
|
||||
// HTTPC_DISABLE_FOLLOW_REDIRECTS,
|
||||
// HTTPC_STRICT_FOLLOW_REDIRECTS,
|
||||
// HTTPC_FORCE_FOLLOW_REDIRECTS
|
||||
// } followRedirects_t;
|
||||
|
||||
|
||||
|
||||
class HTTPClientLight
|
||||
{
|
||||
public:
|
||||
HTTPClientLight();
|
||||
~HTTPClientLight();
|
||||
|
||||
/*
|
||||
* Since both begin() functions take a reference to client as a parameter, you need to
|
||||
* ensure the client object lives the entire time of the HTTPClientLight
|
||||
*/
|
||||
// bool begin(WiFiClient &client, String url);
|
||||
// bool begin(WiFiClient &client, String host, uint16_t port, String uri = "/", bool https = false);
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
bool begin(String url);
|
||||
bool begin(String url, const char* CAcert);
|
||||
// bool begin(String host, uint16_t port, String uri = "/");
|
||||
// bool begin(String host, uint16_t port, String uri, const char* CAcert);
|
||||
// bool begin(String host, uint16_t port, String uri, const char* CAcert, const char* cli_cert, const char* cli_key);
|
||||
#endif
|
||||
|
||||
void end(void);
|
||||
|
||||
bool connected(void);
|
||||
|
||||
void setReuse(bool reuse); /// keep-alive
|
||||
void setUserAgent(const String& userAgent);
|
||||
void setAuthorization(const char * user, const char * password);
|
||||
void setAuthorization(const char * auth);
|
||||
void setConnectTimeout(int32_t connectTimeout);
|
||||
void setTimeout(uint16_t timeout);
|
||||
|
||||
// Redirections
|
||||
void setFollowRedirects(followRedirects_t follow);
|
||||
void setRedirectLimit(uint16_t limit); // max redirects to follow for a single request
|
||||
|
||||
bool setURL(const String &url);
|
||||
void useHTTP10(bool usehttp10 = true);
|
||||
|
||||
/// request handling
|
||||
int GET();
|
||||
int PATCH(uint8_t * payload, size_t size);
|
||||
int PATCH(String payload);
|
||||
int POST(uint8_t * payload, size_t size);
|
||||
int POST(String payload);
|
||||
int PUT(uint8_t * payload, size_t size);
|
||||
int PUT(String payload);
|
||||
int sendRequest(const char * type, String payload);
|
||||
int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0);
|
||||
int sendRequest(const char * type, Stream * stream, size_t size = 0);
|
||||
|
||||
void addHeader(const String& name, const String& value, bool first = false, bool replace = true);
|
||||
|
||||
/// Response handling
|
||||
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount);
|
||||
String header(const char* name); // get request header value by name
|
||||
String header(size_t i); // get request header value by number
|
||||
String headerName(size_t i); // get request header name by number
|
||||
int headers(); // get header count
|
||||
bool hasHeader(const char* name); // check if header exists
|
||||
|
||||
|
||||
int getSize(void);
|
||||
const String &getLocation(void);
|
||||
|
||||
WiFiClient& getStream(void);
|
||||
WiFiClient* getStreamPtr(void);
|
||||
int writeToStream(Stream* stream);
|
||||
String getString(void);
|
||||
|
||||
static String errorToString(int error);
|
||||
|
||||
protected:
|
||||
struct RequestArgument {
|
||||
String key;
|
||||
String value;
|
||||
};
|
||||
|
||||
bool beginInternal(String url, const char* expectedProtocol);
|
||||
void disconnect(bool preserveClient = false);
|
||||
void clear();
|
||||
int returnError(int error);
|
||||
bool connect(void);
|
||||
bool sendHeader(const char * type);
|
||||
int handleHeaderResponse();
|
||||
int writeToStreamDataBlock(Stream * stream, int len);
|
||||
|
||||
|
||||
#ifdef HTTPCLIENT_1_1_COMPATIBLE
|
||||
TransportTraitsPtr _transportTraits;
|
||||
std::unique_ptr<WiFiClient> _tcpDeprecated;
|
||||
#endif
|
||||
|
||||
WiFiClient* _client = nullptr;
|
||||
|
||||
/// request handling
|
||||
String _host;
|
||||
uint16_t _port = 0;
|
||||
int32_t _connectTimeout = -1;
|
||||
bool _reuse = true;
|
||||
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
|
||||
bool _useHTTP10 = false;
|
||||
bool _secure = false;
|
||||
|
||||
String _uri;
|
||||
String _protocol;
|
||||
String _headers;
|
||||
String _userAgent = "ESP32HTTPClient";
|
||||
String _base64Authorization;
|
||||
|
||||
/// Response handling
|
||||
RequestArgument* _currentHeaders = nullptr;
|
||||
size_t _headerKeysCount = 0;
|
||||
|
||||
int _returnCode = 0;
|
||||
int _size = -1;
|
||||
bool _canReuse = false;
|
||||
followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS;
|
||||
uint16_t _redirectLimit = 10;
|
||||
String _location;
|
||||
transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* HTTPClient_Light_H_ */
|
|
@ -288,6 +288,17 @@ void WiFiClientSecure_light::flush(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port, int32_t timeout) {
|
||||
DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port);
|
||||
clearLastError();
|
||||
if (!WiFiClient::connect(ip, port, timeout)) {
|
||||
setLastError(ERR_TCP_CONNECT);
|
||||
return 0;
|
||||
}
|
||||
return _connectSSL(nullptr);
|
||||
}
|
||||
#else // ESP32
|
||||
int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) {
|
||||
DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port);
|
||||
clearLastError();
|
||||
|
@ -297,7 +308,28 @@ int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) {
|
|||
}
|
||||
return _connectSSL(nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
int WiFiClientSecure_light::connect(const char* name, uint16_t port, int32_t timeout) {
|
||||
DEBUG_BSSL("connect(%s,%d)\n", name, port);
|
||||
IPAddress remote_addr;
|
||||
clearLastError();
|
||||
if (!WiFi.hostByName(name, remote_addr)) {
|
||||
DEBUG_BSSL("connect: Name loopup failure\n");
|
||||
setLastError(ERR_CANT_RESOLVE_IP);
|
||||
return 0;
|
||||
}
|
||||
DEBUG_BSSL("connect(%s,%d)\n", remote_addr.toString().c_str(), port);
|
||||
if (!WiFiClient::connect(remote_addr, port, timeout)) {
|
||||
DEBUG_BSSL("connect: Unable to connect TCP socket\n");
|
||||
_last_error = ERR_TCP_CONNECT;
|
||||
return 0;
|
||||
}
|
||||
LOG_HEAP_SIZE("Before calling _connectSSL");
|
||||
return _connectSSL(name);
|
||||
}
|
||||
#else // ESP32
|
||||
int WiFiClientSecure_light::connect(const char* name, uint16_t port) {
|
||||
DEBUG_BSSL("connect(%s,%d)\n", name, port);
|
||||
IPAddress remote_addr;
|
||||
|
@ -316,6 +348,7 @@ int WiFiClientSecure_light::connect(const char* name, uint16_t port) {
|
|||
LOG_HEAP_SIZE("Before calling _connectSSL");
|
||||
return _connectSSL(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
void WiFiClientSecure_light::_freeSSL() {
|
||||
_ctx_present = false;
|
||||
|
|
|
@ -38,8 +38,13 @@ class WiFiClientSecure_light : public WiFiClient {
|
|||
|
||||
void allocateBuffers(void);
|
||||
|
||||
#ifdef ESP32 // the method to override in ESP32 has timeout argument
|
||||
int connect(IPAddress ip, uint16_t port, int32_t timeout) override;
|
||||
int connect(const char* name, uint16_t port, int32_t timeout) override;
|
||||
#else
|
||||
int connect(IPAddress ip, uint16_t port) override;
|
||||
int connect(const char* name, uint16_t port) override;
|
||||
#endif
|
||||
|
||||
uint8_t connected() override;
|
||||
size_t write(const uint8_t *buf, size_t size) override;
|
||||
|
|
|
@ -109,6 +109,7 @@ extern void be_load_Driver_class(bvm *vm);
|
|||
extern void be_load_Timer_class(bvm *vm);
|
||||
extern void be_load_driver_i2c_lib(bvm *vm);
|
||||
extern void be_load_md5_lib(bvm *vm);
|
||||
extern void be_load_webclient_lib(bvm *vm);
|
||||
extern void be_load_crypto_lib(bvm *vm);
|
||||
|
||||
#ifdef USE_I2S_AUDIO_BERRY
|
||||
|
@ -148,6 +149,9 @@ BERRY_API void be_load_custom_libs(bvm *vm)
|
|||
be_load_wirelib(vm);
|
||||
be_load_driver_i2c_lib(vm);
|
||||
#endif // USE_I2C
|
||||
#ifdef USE_WEBCLIENT
|
||||
be_load_webclient_lib(vm);
|
||||
#endif // USE_WEBCLIENT
|
||||
#if defined(USE_ONEWIRE) || defined(USE_DS18x20)
|
||||
be_load_onewirelib(vm);
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/********************************************************************
|
||||
* Webclient mapped to Arduino framework
|
||||
*
|
||||
* To use: `d = webclient()`
|
||||
*
|
||||
*******************************************************************/
|
||||
#include "be_constobj.h"
|
||||
|
||||
#ifdef USE_WEBCLIENT
|
||||
|
||||
extern int wc_init(bvm *vm);
|
||||
extern int wc_deinit(bvm *vm);
|
||||
extern int wc_urlencode(bvm *vm);
|
||||
extern int wc_begin(bvm *vm);
|
||||
extern int wc_set_timeouts(bvm *vm);
|
||||
extern int wc_set_useragent(bvm *vm);
|
||||
extern int wc_set_auth(bvm *vm);
|
||||
extern int wc_connected(bvm *vm);
|
||||
extern int wc_close(bvm *vm);
|
||||
extern int wc_addheader(bvm *vm);
|
||||
extern int wc_GET(bvm *vm);
|
||||
extern int wc_POST(bvm *vm);
|
||||
extern int wc_getstring(bvm *vm);
|
||||
extern int wc_getsize(bvm *vm);
|
||||
|
||||
#include "../generate/be_fixed_be_class_webclient.h"
|
||||
|
||||
void be_load_webclient_lib(bvm *vm) {
|
||||
be_pushntvclass(vm, &be_class_webclient);
|
||||
be_setglobal(vm, "webclient");
|
||||
be_pop(vm, 1);
|
||||
}
|
||||
/* @const_object_info_begin
|
||||
|
||||
class be_class_webclient (scope: global, name: webclient) {
|
||||
.p, var
|
||||
.w, var
|
||||
init, func(wc_init)
|
||||
deinit, func(wc_deinit)
|
||||
url_encode, func(wc_urlencode)
|
||||
|
||||
begin, func(wc_begin)
|
||||
set_timeouts, func(wc_set_timeouts)
|
||||
set_useragent, func(wc_set_useragent)
|
||||
set_auth, func(wc_set_auth)
|
||||
close, func(wc_close)
|
||||
add_header, func(wc_addheader)
|
||||
GET, func(wc_GET)
|
||||
POST, func(wc_POST)
|
||||
get_string, func(wc_getstring)
|
||||
get_size, func(wc_getsize)
|
||||
}
|
||||
@const_object_info_end */
|
||||
|
||||
#endif // USE_WEBCLIENT
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
|||
#include "be_constobj.h"
|
||||
|
||||
static be_define_const_map_slots(be_class_webclient_map) {
|
||||
{ be_const_key(url_encode, 3), be_const_func(wc_urlencode) },
|
||||
{ be_const_key(POST, -1), be_const_func(wc_POST) },
|
||||
{ be_const_key(dot_p, -1), be_const_var(0) },
|
||||
{ be_const_key(begin, 5), be_const_func(wc_begin) },
|
||||
{ be_const_key(set_useragent, 2), be_const_func(wc_set_useragent) },
|
||||
{ be_const_key(set_auth, 12), be_const_func(wc_set_auth) },
|
||||
{ be_const_key(close, -1), be_const_func(wc_close) },
|
||||
{ be_const_key(add_header, 9), be_const_func(wc_addheader) },
|
||||
{ be_const_key(get_size, -1), be_const_func(wc_getsize) },
|
||||
{ be_const_key(deinit, -1), be_const_func(wc_deinit) },
|
||||
{ be_const_key(set_timeouts, -1), be_const_func(wc_set_timeouts) },
|
||||
{ be_const_key(GET, 13), be_const_func(wc_GET) },
|
||||
{ be_const_key(init, -1), be_const_func(wc_init) },
|
||||
{ be_const_key(dot_w, -1), be_const_var(1) },
|
||||
{ be_const_key(get_string, 11), be_const_func(wc_getstring) },
|
||||
};
|
||||
|
||||
static be_define_const_map(
|
||||
be_class_webclient_map,
|
||||
15
|
||||
);
|
||||
|
||||
BE_EXPORT_VARIABLE be_define_const_class(
|
||||
be_class_webclient,
|
||||
2,
|
||||
NULL,
|
||||
webclient
|
||||
);
|
|
@ -980,7 +980,11 @@
|
|||
|
||||
#define USE_BERRY // Enable Berry scripting language
|
||||
//#define USE_BERRY_PSRAM // Allocate Berry memory in PSRAM if PSRAM is connected - this might be slightly slower but leaves main memory intact
|
||||
|
||||
#define USE_WEBCLIENT // Enable `webclient` to make HTTP/HTTPS requests. Can be disabled for security reasons.
|
||||
#define USE_WEBCLIENT_HTTPS // Enable HTTPS outgoing requests based on BearSSL (much ligher then mbedTLS, 42KB vs 150KB) in insecure mode (no verification of server's certificate)
|
||||
// Note that only one cipher is enabled: ECDHE_RSA_WITH_AES_128_GCM_SHA256 which is very commonly used and highly secure
|
||||
#define USE_BERRY_WEBCLIENT_USERAGENT "TasmotaClient" // default user-agent used, can be changed with `wc.set_useragent()`
|
||||
#define USE_BERRY_WEBCLIENT_TIMEOUT 5000 // Default timeout in milliseconds
|
||||
#define USE_CSE7761 // Add support for CSE7761 Energy monitor as used in Sonoff Dual R3
|
||||
|
||||
// -- LVGL Graphics Library ---------------------------------
|
||||
|
@ -1075,10 +1079,10 @@
|
|||
* Post-process compile options for TLS
|
||||
\*********************************************************************************************/
|
||||
|
||||
#if defined(USE_MQTT_TLS) || defined(USE_SENDMAIL) || defined(USE_TELEGRAM)
|
||||
#if defined(USE_MQTT_TLS) || defined(USE_SENDMAIL) || defined(USE_TELEGRAM) || defined(USE_WEBCLIENT_HTTPS) || defined(USE_ALEXA_AVS)
|
||||
#define USE_TLS // flag indicates we need to include TLS code
|
||||
|
||||
#if defined(USE_MQTT_AWS_IOT) || defined(USE_TELEGRAM)
|
||||
#if defined(USE_MQTT_AWS_IOT) || defined(USE_TELEGRAM) || defined(USE_WEBCLIENT_HTTPS)
|
||||
#define USE_MQTT_TLS_FORCE_EC_CIPHER // AWS IoT and TELEGRAM require EC Cipher
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
xdrv_52_3_berry_webserver.ino - Berry scripting language, webserver module
|
||||
|
||||
Copyright (C) 2021 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef USE_BERRY
|
||||
|
||||
#ifdef USE_WEBCLIENT
|
||||
|
||||
#include <berry.h>
|
||||
#include <HTTPClientLight.h>
|
||||
|
||||
String wc_UrlEncode(const String& text) {
|
||||
const char hex[] = "0123456789ABCDEF";
|
||||
|
||||
String encoded = "";
|
||||
int len = text.length();
|
||||
int i = 0;
|
||||
while (i < len) {
|
||||
char decodedChar = text.charAt(i++);
|
||||
|
||||
if (('a' <= decodedChar && decodedChar <= 'z') ||
|
||||
('A' <= decodedChar && decodedChar <= 'Z') ||
|
||||
('0' <= decodedChar && decodedChar <= '9') ||
|
||||
('=' == decodedChar)) {
|
||||
encoded += decodedChar;
|
||||
} else {
|
||||
encoded += '%';
|
||||
encoded += hex[decodedChar >> 4];
|
||||
encoded += hex[decodedChar & 0xF];
|
||||
}
|
||||
|
||||
}
|
||||
return encoded;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Int constants
|
||||
*********************************************************************************************/
|
||||
// const be_constint_t webserver_constants[] = {
|
||||
// { "BUTTON_CONFIGURATION", BUTTON_CONFIGURATION },
|
||||
// { "BUTTON_INFORMATION", BUTTON_INFORMATION },
|
||||
// { "BUTTON_MAIN", BUTTON_MAIN },
|
||||
// { "BUTTON_MANAGEMENT", BUTTON_MANAGEMENT },
|
||||
// { "BUTTON_MODULE", BUTTON_MODULE },
|
||||
// { "HTTP_ADMIN", HTTP_ADMIN },
|
||||
// { "HTTP_ANY", HTTP_ANY },
|
||||
// { "HTTP_GET", HTTP_GET },
|
||||
// { "HTTP_MANAGER", HTTP_MANAGER },
|
||||
// { "HTTP_MANAGER_RESET_ONLY", HTTP_MANAGER_RESET_ONLY },
|
||||
// { "HTTP_OFF", HTTP_OFF },
|
||||
// { "HTTP_OPTIONS", HTTP_OPTIONS },
|
||||
// { "HTTP_POST", HTTP_POST },
|
||||
// { "HTTP_USER", HTTP_USER },
|
||||
// };
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Native functions mapped to Berry functions
|
||||
*
|
||||
* import webclient
|
||||
*
|
||||
\*********************************************************************************************/
|
||||
extern "C" {
|
||||
// Berry: ``
|
||||
//
|
||||
int32_t wc_init(struct bvm *vm);
|
||||
int32_t wc_init(struct bvm *vm) {
|
||||
// int32_t argc = be_top(vm); // Get the number of arguments
|
||||
WiFiClient * wcl = new WiFiClient();
|
||||
be_pushcomptr(vm, (void*) wcl);
|
||||
be_setmember(vm, 1, ".w");
|
||||
HTTPClientLight * cl = new HTTPClientLight();
|
||||
cl->setUserAgent(USE_BERRY_WEBCLIENT_USERAGENT);
|
||||
cl->setConnectTimeout(USE_BERRY_WEBCLIENT_TIMEOUT); // set default timeout
|
||||
be_pushcomptr(vm, (void*) cl);
|
||||
be_setmember(vm, 1, ".p");
|
||||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
HTTPClientLight * wc_getclient(struct bvm *vm) {
|
||||
be_getmember(vm, 1, ".p");
|
||||
void *p = be_tocomptr(vm, -1);
|
||||
be_pop(vm, 1);
|
||||
return (HTTPClientLight*) p;
|
||||
}
|
||||
|
||||
WiFiClient * wc_getwificlient(struct bvm *vm) {
|
||||
be_getmember(vm, 1, ".w");
|
||||
void *p = be_tocomptr(vm, -1);
|
||||
be_pop(vm, 1);
|
||||
return (WiFiClient*) p;
|
||||
}
|
||||
|
||||
int32_t wc_deinit(struct bvm *vm);
|
||||
int32_t wc_deinit(struct bvm *vm) {
|
||||
// int32_t argc = be_top(vm); // Get the number of arguments
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
if (cl != nullptr) { delete cl; }
|
||||
be_pushnil(vm);
|
||||
be_setmember(vm, 1, ".p");
|
||||
WiFiClient * wcl = wc_getwificlient(vm);
|
||||
if (wcl != nullptr) { delete wcl; }
|
||||
be_setmember(vm, 1, ".w");
|
||||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
// wc.url_encode(string) -> string
|
||||
int32_t wc_urlencode(struct bvm *vm);
|
||||
int32_t wc_urlencode(struct bvm *vm) {
|
||||
int32_t argc = be_top(vm);
|
||||
if (argc >= 2 && be_isstring(vm, 2)) {
|
||||
const char * s = be_tostring(vm, 2);
|
||||
String url = wc_UrlEncode(String(s));
|
||||
be_pushstring(vm, url.c_str());
|
||||
be_return(vm); /* return self */
|
||||
}
|
||||
be_raise(vm, kTypeError, nullptr);
|
||||
}
|
||||
|
||||
// wc.begin(url:string) -> self
|
||||
int32_t wc_begin(struct bvm *vm);
|
||||
int32_t wc_begin(struct bvm *vm) {
|
||||
int32_t argc = be_top(vm);
|
||||
if (argc == 1 || !be_tostring(vm, 2)) { be_raise(vm, "attribute_error", "missing URL as string"); }
|
||||
const char * url = be_tostring(vm, 2);
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
// open connection
|
||||
if (!cl->begin(url)) {
|
||||
be_raise(vm, "value_error", "unsupported protocol");
|
||||
}
|
||||
be_pushvalue(vm, 1);
|
||||
be_return(vm); /* return self */
|
||||
}
|
||||
|
||||
// wc.close(void) -> nil
|
||||
int32_t wc_close(struct bvm *vm);
|
||||
int32_t wc_close(struct bvm *vm) {
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
cl->end();
|
||||
be_return_nil(vm);
|
||||
}
|
||||
|
||||
// wc.wc_set_timeouts([http_timeout_ms:int, tcp_timeout_ms:int]) -> self
|
||||
int32_t wc_set_timeouts(struct bvm *vm);
|
||||
int32_t wc_set_timeouts(struct bvm *vm) {
|
||||
int32_t argc = be_top(vm);
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
if (argc >= 2) {
|
||||
cl->setTimeout(be_toint(vm, 2));
|
||||
}
|
||||
if (argc >= 3) {
|
||||
cl->setConnectTimeout(be_toint(vm, 3));
|
||||
}
|
||||
be_pushvalue(vm, 1);
|
||||
be_return(vm); /* return self */
|
||||
}
|
||||
|
||||
// wc.set_useragent(user_agent:string) -> self
|
||||
int32_t wc_set_useragent(struct bvm *vm);
|
||||
int32_t wc_set_useragent(struct bvm *vm) {
|
||||
int32_t argc = be_top(vm);
|
||||
if (argc >= 2 && be_isstring(vm, 2)) {
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
const char * useragent = be_tostring(vm, 2);
|
||||
cl->setUserAgent(String(useragent));
|
||||
be_pushvalue(vm, 1);
|
||||
be_return(vm); /* return self */
|
||||
}
|
||||
be_raise(vm, kTypeError, nullptr);
|
||||
}
|
||||
|
||||
// wc.wc_set_auth(auth:string | (user:string, password:string)) -> self
|
||||
int32_t wc_set_auth(struct bvm *vm);
|
||||
int32_t wc_set_auth(struct bvm *vm) {
|
||||
int32_t argc = be_top(vm);
|
||||
if (argc >= 2 && be_isstring(vm, 2) && (argc < 3 || be_isstring(vm, 3))) {
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
const char * user = be_tostring(vm, 2);
|
||||
if (argc == 2) {
|
||||
cl->setAuthorization(user);
|
||||
} else {
|
||||
const char * password = be_tostring(vm, 3);
|
||||
cl->setAuthorization(user, password);
|
||||
}
|
||||
be_pushvalue(vm, 1);
|
||||
be_return(vm); /* return self */
|
||||
}
|
||||
be_raise(vm, kTypeError, nullptr);
|
||||
}
|
||||
|
||||
// wc.addheader(name:string, value:string [, first:bool=false [, replace:bool=true]]) -> nil
|
||||
int32_t wc_addheader(struct bvm *vm);
|
||||
int32_t wc_addheader(struct bvm *vm) {
|
||||
int32_t argc = be_top(vm);
|
||||
if (argc >= 3 && (be_isstring(vm, 2) || be_isstring(vm, 2))) {
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
|
||||
const char * name = be_tostring(vm, 2);
|
||||
const char * value = be_tostring(vm, 3);
|
||||
bool first = false;
|
||||
bool replace = true;
|
||||
if (argc >= 4) {
|
||||
first = be_tobool(vm, 4);
|
||||
}
|
||||
if (argc >= 5) {
|
||||
replace = be_tobool(vm, 5);
|
||||
}
|
||||
// do the call
|
||||
cl->addHeader(String(name), String(value), first, replace);
|
||||
be_return_nil(vm);
|
||||
}
|
||||
be_raise(vm, kTypeError, nullptr);
|
||||
}
|
||||
|
||||
// cw.connected(void) -> bool
|
||||
int32_t wc_connected(struct bvm *vm);
|
||||
int32_t wc_connected(struct bvm *vm) {
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
be_pushbool(vm, cl->connected());
|
||||
be_return(vm); /* return code */
|
||||
}
|
||||
|
||||
// cw.GET(void) -> httpCode:int
|
||||
int32_t wc_GET(struct bvm *vm);
|
||||
int32_t wc_GET(struct bvm *vm) {
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
uint32_t http_connect_time = millis();
|
||||
int32_t httpCode = cl->GET();
|
||||
if (httpCode <= -1000) {
|
||||
AddLog(LOG_LEVEL_INFO, D_LOG_HTTP "TLS connection error: %d", -httpCode - 1000);
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "TLS connected in %d ms, stack low mark %d"),
|
||||
millis() - http_connect_time, uxTaskGetStackHighWaterMark(nullptr));
|
||||
}
|
||||
be_pushint(vm, httpCode);
|
||||
be_return(vm); /* return code */
|
||||
}
|
||||
|
||||
// wc.POST(string | bytes) -> httpCode:int
|
||||
int32_t wc_POST(struct bvm *vm);
|
||||
int32_t wc_POST(struct bvm *vm) {
|
||||
int32_t argc = be_top(vm);
|
||||
if (argc >= 2 && (be_isstring(vm, 2) || be_isbytes(vm, 2))) {
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
const char * buf = nullptr;
|
||||
size_t buf_len = 0;
|
||||
if (be_isstring(vm, 2)) { // string
|
||||
buf = be_tostring(vm, 2);
|
||||
buf_len = strlen(buf);
|
||||
} else { // bytes
|
||||
buf = (const char*) be_tobytes(vm, 2, &buf_len);
|
||||
}
|
||||
uint32_t http_connect_time = millis();
|
||||
int32_t httpCode = cl->POST((uint8_t*)buf, buf_len);
|
||||
if (httpCode <= -1000) {
|
||||
AddLog(LOG_LEVEL_INFO, D_LOG_HTTP "TLS connection error: %d", -httpCode - 1000);
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "TLS connected in %d ms, stack low mark %d"),
|
||||
millis() - http_connect_time, uxTaskGetStackHighWaterMark(nullptr));
|
||||
}
|
||||
be_pushint(vm, httpCode);
|
||||
be_return(vm); /* return code */
|
||||
}
|
||||
be_raise(vm, kTypeError, nullptr);
|
||||
}
|
||||
|
||||
int32_t wc_getstring(struct bvm *vm);
|
||||
int32_t wc_getstring(struct bvm *vm) {
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
int32_t sz = cl->getSize();
|
||||
// abort if we exceed 32KB size, things will not go well otherwise
|
||||
if (sz >= 32767) {
|
||||
be_raise(vm, "value_error", "response size too big (>32KB)");
|
||||
}
|
||||
String payload = cl->getString();
|
||||
be_pushstring(vm, payload.c_str());
|
||||
cl->end(); // free allocated memory ~16KB
|
||||
be_return(vm); /* return code */
|
||||
}
|
||||
|
||||
int32_t wc_getsize(struct bvm *vm);
|
||||
int32_t wc_getsize(struct bvm *vm) {
|
||||
HTTPClientLight * cl = wc_getclient(vm);
|
||||
be_pushint(vm, cl->getSize());
|
||||
be_return(vm); /* return code */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // USE_WEBCLIENT
|
||||
#endif // USE_BERRY
|
Loading…
Reference in New Issue