Berry class ``webclient`` for HTTP/HTTPS requests

This commit is contained in:
Stephan Hadinger 2021-09-02 21:58:08 +02:00
parent 86205d0c29
commit 3d5c68b850
12 changed files with 3879 additions and 1635 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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