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]
|
## [9.5.0.8]
|
||||||
### Added
|
### Added
|
||||||
- Command ``WebGetConfig <url>`` if ``#define USE_WEBGETCONFIG`` is enabled to restore/init configuration from external webserver (#13034)
|
- 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
|
### Fixed
|
||||||
- OpenTherm invalid JSON (#13028)
|
- 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
|
#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) {
|
int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) {
|
||||||
DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port);
|
DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port);
|
||||||
clearLastError();
|
clearLastError();
|
||||||
|
@ -297,7 +308,28 @@ int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) {
|
||||||
}
|
}
|
||||||
return _connectSSL(nullptr);
|
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) {
|
int WiFiClientSecure_light::connect(const char* name, uint16_t port) {
|
||||||
DEBUG_BSSL("connect(%s,%d)\n", name, port);
|
DEBUG_BSSL("connect(%s,%d)\n", name, port);
|
||||||
IPAddress remote_addr;
|
IPAddress remote_addr;
|
||||||
|
@ -316,6 +348,7 @@ int WiFiClientSecure_light::connect(const char* name, uint16_t port) {
|
||||||
LOG_HEAP_SIZE("Before calling _connectSSL");
|
LOG_HEAP_SIZE("Before calling _connectSSL");
|
||||||
return _connectSSL(name);
|
return _connectSSL(name);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void WiFiClientSecure_light::_freeSSL() {
|
void WiFiClientSecure_light::_freeSSL() {
|
||||||
_ctx_present = false;
|
_ctx_present = false;
|
||||||
|
|
|
@ -38,8 +38,13 @@ class WiFiClientSecure_light : public WiFiClient {
|
||||||
|
|
||||||
void allocateBuffers(void);
|
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(IPAddress ip, uint16_t port) override;
|
||||||
int connect(const char* name, uint16_t port) override;
|
int connect(const char* name, uint16_t port) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
uint8_t connected() override;
|
uint8_t connected() override;
|
||||||
size_t write(const uint8_t *buf, size_t size) 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_Timer_class(bvm *vm);
|
||||||
extern void be_load_driver_i2c_lib(bvm *vm);
|
extern void be_load_driver_i2c_lib(bvm *vm);
|
||||||
extern void be_load_md5_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);
|
extern void be_load_crypto_lib(bvm *vm);
|
||||||
|
|
||||||
#ifdef USE_I2S_AUDIO_BERRY
|
#ifdef USE_I2S_AUDIO_BERRY
|
||||||
|
@ -148,6 +149,9 @@ BERRY_API void be_load_custom_libs(bvm *vm)
|
||||||
be_load_wirelib(vm);
|
be_load_wirelib(vm);
|
||||||
be_load_driver_i2c_lib(vm);
|
be_load_driver_i2c_lib(vm);
|
||||||
#endif // USE_I2C
|
#endif // USE_I2C
|
||||||
|
#ifdef USE_WEBCLIENT
|
||||||
|
be_load_webclient_lib(vm);
|
||||||
|
#endif // USE_WEBCLIENT
|
||||||
#if defined(USE_ONEWIRE) || defined(USE_DS18x20)
|
#if defined(USE_ONEWIRE) || defined(USE_DS18x20)
|
||||||
be_load_onewirelib(vm);
|
be_load_onewirelib(vm);
|
||||||
#endif
|
#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 // 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_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
|
#define USE_CSE7761 // Add support for CSE7761 Energy monitor as used in Sonoff Dual R3
|
||||||
|
|
||||||
// -- LVGL Graphics Library ---------------------------------
|
// -- LVGL Graphics Library ---------------------------------
|
||||||
|
@ -1075,10 +1079,10 @@
|
||||||
* Post-process compile options for TLS
|
* 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
|
#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
|
#define USE_MQTT_TLS_FORCE_EC_CIPHER // AWS IoT and TELEGRAM require EC Cipher
|
||||||
#endif
|
#endif
|
||||||
#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