mirror of
https://github.com/m5stack/M5Stack_MicroPython.git
synced 2026-05-20 10:14:44 -07:00
1049 lines
37 KiB
C
1049 lines
37 KiB
C
/*
|
|
* This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo
|
|
*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2018 LoBo (https://github.com/loboris)
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
|
|
#include "sdkconfig.h"
|
|
|
|
#ifdef CONFIG_MICROPY_USE_REQUESTS
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_log.h"
|
|
#include "esp_system.h"
|
|
#include "nvs_flash.h"
|
|
#include "mbedtls/base64.h"
|
|
|
|
#include "esp_http_client.h"
|
|
#include "transport.h"
|
|
#include "esp_wifi_types.h"
|
|
#include "tcpip_adapter.h"
|
|
|
|
#include "py/obj.h"
|
|
#include "py/runtime.h"
|
|
#include "modmachine.h"
|
|
#include "extmod/vfs_native.h"
|
|
#include "modnetwork.h"
|
|
|
|
#define MAX_HTTP_RECV_BUFFER 512
|
|
static const char *TAG = "[REQUESTS]";
|
|
|
|
static char *rqheader = NULL;
|
|
static char *rqbody = NULL;
|
|
static FILE* rqbody_file = NULL;
|
|
static int rqheader_len = 0;
|
|
static int rqheader_ptr = 0;
|
|
static int rqbody_len = 0;
|
|
static int rqbody_ptr = 0;
|
|
static bool rqbody_ok = true;
|
|
static bool rq_debug = false;
|
|
static bool rq_base64 = false;
|
|
static char *cert_pem = NULL;
|
|
|
|
|
|
/*
|
|
* You can get the Root cert for the server using openssl
|
|
|
|
The PEM file can be extracted from the output of this command:
|
|
openssl s_client -showcerts -connect <the_server>:443
|
|
|
|
The CA root cert is the last cert given in the chain of certs.
|
|
*/
|
|
|
|
//----------------------------------------------------------------
|
|
static esp_err_t _http_event_handler(esp_http_client_event_t *evt)
|
|
{
|
|
switch(evt->event_id) {
|
|
case HTTP_EVENT_ERROR:
|
|
if (rq_debug) ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
|
|
break;
|
|
case HTTP_EVENT_ON_CONNECTED:
|
|
if (rq_debug) ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
|
|
break;
|
|
case HTTP_EVENT_HEADER_SENT:
|
|
if (rq_debug) {
|
|
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
|
|
ESP_LOGD(TAG, " KEY: [%s]", evt->header_key);
|
|
ESP_LOGD(TAG, "VALUE: [%s]", evt->header_value);
|
|
}
|
|
break;
|
|
case HTTP_EVENT_ON_HEADER:
|
|
if (rq_debug) ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
|
|
if (rqheader == NULL) {
|
|
rqheader = malloc(256);
|
|
if (rqheader) {
|
|
rqheader_len = 256;
|
|
rqheader_ptr = 0;
|
|
rqheader[0] = '\0';
|
|
}
|
|
}
|
|
if (rqheader) {
|
|
bool f = true;
|
|
int len = strlen(evt->header_key) + strlen(evt->header_value) + rqheader_ptr + 5;
|
|
if (len > rqheader_len) {
|
|
char *tmphdr = realloc(rqheader, len + 128);
|
|
if (tmphdr) {
|
|
rqheader = tmphdr;
|
|
rqheader_len = len + 128;
|
|
}
|
|
else f = false;
|
|
}
|
|
if (f) {
|
|
strcat(rqheader, evt->header_key);
|
|
strcat(rqheader, ": ");
|
|
strcat(rqheader, evt->header_value);
|
|
strcat(rqheader, "\r\n");
|
|
rqheader_ptr = strlen(rqheader);
|
|
}
|
|
}
|
|
break;
|
|
case HTTP_EVENT_ON_DATA:
|
|
if (rq_debug) ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d, rqptr=%d [%d]", evt->data_len, rqbody_ptr, rqbody_len);
|
|
if (rqbody_ok) {
|
|
if (rqbody_file) {
|
|
int nwrite = fwrite(evt->data, 1, evt->data_len, rqbody_file);
|
|
if (nwrite <= 0) {
|
|
rqbody_ok = false;
|
|
ESP_LOGE(TAG, "Download: Error writing to file %d", nwrite);
|
|
}
|
|
}
|
|
else {
|
|
if (rqbody == NULL) {
|
|
rqbody = malloc(4096);
|
|
if (rqbody) {
|
|
rqbody_len = 4096;
|
|
rqbody_ptr = 0;
|
|
}
|
|
}
|
|
if (rqbody) {
|
|
int len = evt->data_len + rqbody_ptr;
|
|
if (len > rqbody_len) {
|
|
char *tmpbody = realloc(rqbody, len + 512);
|
|
if (tmpbody) {
|
|
rqbody = tmpbody;
|
|
rqbody_len = len + 512;
|
|
}
|
|
else {
|
|
rqbody_ok = false;
|
|
ESP_LOGE(TAG, "Error reallocating body buffer");
|
|
}
|
|
}
|
|
|
|
if (rqbody_ok) {
|
|
memcpy(rqbody + rqbody_ptr, evt->data, evt->data_len);
|
|
rqbody_ptr += evt->data_len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case HTTP_EVENT_ON_FINISH:
|
|
if (rq_debug) ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
|
|
break;
|
|
case HTTP_EVENT_DISCONNECTED:
|
|
if (rq_debug) ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
|
|
break;
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
/*
|
|
//---------------------------
|
|
static void http_auth_basic()
|
|
{
|
|
esp_http_client_config_t config = {
|
|
.url = "http://user:passwd@httpbin.org/basic-auth/user/passwd",
|
|
.event_handler = _http_event_handler,
|
|
.auth_type = HTTP_AUTH_TYPE_BASIC,
|
|
};
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
esp_err_t err = esp_http_client_perform(client);
|
|
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "HTTP Basic Auth Status = %d, content_length = %d",
|
|
esp_http_client_get_status_code(client),
|
|
esp_http_client_get_content_length(client));
|
|
} else {
|
|
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
|
|
}
|
|
esp_http_client_cleanup(client);
|
|
}
|
|
|
|
//------------------------------------
|
|
static void http_auth_basic_redirect()
|
|
{
|
|
esp_http_client_config_t config = {
|
|
.url = "http://user:passwd@httpbin.org/basic-auth/user/passwd",
|
|
.event_handler = _http_event_handler,
|
|
};
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
esp_err_t err = esp_http_client_perform(client);
|
|
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "HTTP Basic Auth redirect Status = %d, content_length = %d",
|
|
esp_http_client_get_status_code(client),
|
|
esp_http_client_get_content_length(client));
|
|
} else {
|
|
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
|
|
}
|
|
esp_http_client_cleanup(client);
|
|
}
|
|
|
|
//----------------------------
|
|
static void http_auth_digest()
|
|
{
|
|
esp_http_client_config_t config = {
|
|
.url = "http://user:passwd@httpbin.org/digest-auth/auth/user/passwd/MD5/never",
|
|
.event_handler = _http_event_handler,
|
|
};
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
esp_err_t err = esp_http_client_perform(client);
|
|
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "HTTP Digest Auth Status = %d, content_length = %d",
|
|
esp_http_client_get_status_code(client),
|
|
esp_http_client_get_content_length(client));
|
|
} else {
|
|
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
|
|
}
|
|
esp_http_client_cleanup(client);
|
|
}
|
|
|
|
//-----------------
|
|
static void https()
|
|
{
|
|
esp_http_client_config_t config = {
|
|
.url = "https://www.howsmyssl.com",
|
|
.event_handler = _http_event_handler,
|
|
//.cert_pem = howsmyssl_com_root_cert_pem_start,
|
|
};
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
esp_err_t err = esp_http_client_perform(client);
|
|
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %d",
|
|
esp_http_client_get_status_code(client),
|
|
esp_http_client_get_content_length(client));
|
|
} else {
|
|
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
|
|
}
|
|
esp_http_client_cleanup(client);
|
|
}
|
|
|
|
static void http_relative_redirect()
|
|
{
|
|
esp_http_client_config_t config = {
|
|
.url = "http://httpbin.org/relative-redirect/3",
|
|
.event_handler = _http_event_handler,
|
|
};
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
esp_err_t err = esp_http_client_perform(client);
|
|
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "HTTP Relative path redirect Status = %d, content_length = %d",
|
|
esp_http_client_get_status_code(client),
|
|
esp_http_client_get_content_length(client));
|
|
} else {
|
|
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
|
|
}
|
|
esp_http_client_cleanup(client);
|
|
}
|
|
|
|
static void http_absolute_redirect()
|
|
{
|
|
esp_http_client_config_t config = {
|
|
.url = "http://httpbin.org/absolute-redirect/3",
|
|
.event_handler = _http_event_handler,
|
|
};
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
esp_err_t err = esp_http_client_perform(client);
|
|
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "HTTP Absolute path redirect Status = %d, content_length = %d",
|
|
esp_http_client_get_status_code(client),
|
|
esp_http_client_get_content_length(client));
|
|
} else {
|
|
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
|
|
}
|
|
esp_http_client_cleanup(client);
|
|
}
|
|
|
|
static void http_redirect_to_https()
|
|
{
|
|
esp_http_client_config_t config = {
|
|
.url = "http://httpbin.org/redirect-to?url=https%3A%2F%2Fwww.howsmyssl.com",
|
|
.event_handler = _http_event_handler,
|
|
};
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
esp_err_t err = esp_http_client_perform(client);
|
|
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "HTTP redirect to HTTPS Status = %d, content_length = %d",
|
|
esp_http_client_get_status_code(client),
|
|
esp_http_client_get_content_length(client));
|
|
} else {
|
|
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
|
|
}
|
|
esp_http_client_cleanup(client);
|
|
}
|
|
|
|
|
|
static void http_download_chunk()
|
|
{
|
|
esp_http_client_config_t config = {
|
|
.url = "http://httpbin.org/stream-bytes/8912",
|
|
.event_handler = _http_event_handler,
|
|
};
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
esp_err_t err = esp_http_client_perform(client);
|
|
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "HTTP chunk encoding Status = %d, content_length = %d",
|
|
esp_http_client_get_status_code(client),
|
|
esp_http_client_get_content_length(client));
|
|
} else {
|
|
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
|
|
}
|
|
esp_http_client_cleanup(client);
|
|
}
|
|
|
|
static void http_perform_as_stream_reader()
|
|
{
|
|
char *buffer = malloc(MAX_HTTP_RECV_BUFFER);
|
|
if (buffer == NULL) {
|
|
ESP_LOGE(TAG, "Cannot malloc http receive buffer");
|
|
return;
|
|
}
|
|
esp_http_client_config_t config = {
|
|
.url = "http://httpbin.org/get",
|
|
.event_handler = _http_event_handler,
|
|
};
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
esp_err_t err;
|
|
if ((err = esp_http_client_open(client, 0)) != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
|
|
free(buffer);
|
|
return;
|
|
}
|
|
int content_length = esp_http_client_fetch_headers(client);
|
|
int total_read_len = 0, read_len;
|
|
if (total_read_len < content_length && content_length <= MAX_HTTP_RECV_BUFFER) {
|
|
read_len = esp_http_client_read(client, buffer, content_length);
|
|
if (read_len <= 0) {
|
|
ESP_LOGE(TAG, "Error read data");
|
|
}
|
|
buffer[read_len] = 0;
|
|
ESP_LOGD(TAG, "read_len = %d", read_len);
|
|
}
|
|
ESP_LOGI(TAG, "HTTP Stream reader Status = %d, content_length = %d",
|
|
esp_http_client_get_status_code(client),
|
|
esp_http_client_get_content_length(client));
|
|
esp_http_client_close(client);
|
|
esp_http_client_cleanup(client);
|
|
free(buffer);
|
|
}
|
|
*/
|
|
|
|
//-----------------------------------------------
|
|
static char *url_post_fields(mp_obj_dict_t *dict)
|
|
{
|
|
char *params = calloc(256, sizeof(char));
|
|
if (params == NULL) return 0;
|
|
|
|
int nparam = 0;
|
|
const char *key;
|
|
const char *value;
|
|
char sval[64];
|
|
size_t max = dict->map.alloc;
|
|
mp_map_t *map = &dict->map;
|
|
mp_map_elem_t *next;
|
|
size_t cur = 0;
|
|
while (1) {
|
|
next = NULL;
|
|
for (size_t i = cur; i < max; i++) {
|
|
if (MP_MAP_SLOT_IS_FILLED(map, i)) {
|
|
cur = i + 1;
|
|
next = &(map->table[i]);
|
|
break;
|
|
}
|
|
}
|
|
if (next == NULL) break;
|
|
|
|
value = NULL;
|
|
key = mp_obj_str_get_str(next->key);
|
|
if (MP_OBJ_IS_STR(next->value)) {
|
|
value = mp_obj_str_get_str(next->value);
|
|
for (int i=0; i<strlen(value); i++) {
|
|
if ((value[i] < 0x20) || (value[i] > 0x7F)) {
|
|
value = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (MP_OBJ_IS_INT(next->value)) {
|
|
int ival = mp_obj_get_int(next->value);
|
|
sprintf(sval,"%d", ival);
|
|
value = sval;
|
|
}
|
|
else if (MP_OBJ_IS_TYPE(next->value, &mp_type_float)) {
|
|
double fval = mp_obj_get_float(next->value);
|
|
sprintf(sval,"%f", fval);
|
|
value = sval;
|
|
}
|
|
if ((value) && ((strlen(params) + strlen(key) + strlen(value) + 2) < 256)) {
|
|
if (strlen(params) > 0) strcat(params, "&");
|
|
strcat(params, key);
|
|
strcat(params, "=");
|
|
strcat(params, value);
|
|
nparam++;
|
|
}
|
|
}
|
|
if (nparam == 0) {
|
|
free(params);
|
|
params = NULL;
|
|
}
|
|
return params;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------------------------------------
|
|
static int post_field(esp_http_client_handle_t client, char *bndry, char *key, char *cont_type, char *value, int vlen, bool send)
|
|
{
|
|
int len = vlen;
|
|
int res;
|
|
char buff[256];
|
|
sprintf(buff, "--%s\r\nContent-Disposition: form-data; name=%s\r\n%s\r\n\r\n", bndry, key, cont_type);
|
|
len += strlen(buff);
|
|
if (send) {
|
|
res = esp_http_client_write(client, buff, strlen(buff));
|
|
if (rq_debug) printf("%s", buff);
|
|
if (res <= 0) return res;
|
|
res = esp_http_client_write(client, value, vlen);
|
|
if (rq_debug) printf("%s", value);
|
|
if (res <= 0) return res;
|
|
}
|
|
|
|
sprintf(buff, "\r\n--%s\r\n\r\n", bndry);
|
|
len += strlen(buff);
|
|
if (send) {
|
|
res = esp_http_client_write(client, buff, strlen(buff));
|
|
if (rq_debug) printf("%s", buff);
|
|
if (res <= 0) return res;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------
|
|
static int handle_file(esp_http_client_handle_t client, char *bndry, char *key, char *fname, bool send)
|
|
{
|
|
int flen = 0;
|
|
if (strlen(fname) < 128) {
|
|
FILE* file = NULL;
|
|
char fullname[128] = {'\0'};
|
|
int res = physicalPath(fname, fullname);
|
|
if ((res == 0) && (strlen(fullname) > 0)) {
|
|
file = fopen(fullname, "rb");
|
|
if (file == NULL) return flen;
|
|
|
|
char *buff = malloc(1024);
|
|
if (buff == NULL) return flen;
|
|
|
|
if (key) {
|
|
sprintf(buff, "--%s\r\nContent-Disposition: form-data; name=\"file_%s\"; filename=\"%s\"\r\nContent-Type: application/octet-stream\r\n\r\n", bndry, key, fname);
|
|
flen += strlen(buff);
|
|
if (send) {
|
|
res = esp_http_client_write(client, buff, strlen(buff));
|
|
if (rq_debug) printf("%s", buff);
|
|
if (res <= 0) {
|
|
free(buff);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
res = 1;
|
|
while (res > 0) {
|
|
res = fread(buff, 1, 1023, file);
|
|
if (send) {
|
|
if (res > 0) buff[res] = 0;
|
|
if ((rq_debug) && (res > 0)) printf("%s", buff);
|
|
res = esp_http_client_write(client, buff, res);
|
|
}
|
|
flen += res;
|
|
}
|
|
fclose(file);
|
|
if (key) {
|
|
sprintf(buff, "\r\n--%s\r\n\r\n", bndry);
|
|
flen += strlen(buff);
|
|
if (send) {
|
|
res = esp_http_client_write(client, buff, strlen(buff));
|
|
if (rq_debug) printf("%s", buff);
|
|
if (res <= 0) {
|
|
free(buff);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
free(buff);
|
|
}
|
|
}
|
|
return flen;
|
|
}
|
|
|
|
// Parse MicroPython dictionary, calculate the content length
|
|
// or send the body to the server
|
|
//------------------------------------------------------------------------------------------------------------
|
|
static int multipart_post_fields(mp_obj_dict_t *dict, char *bndry, esp_http_client_handle_t client, bool send)
|
|
{
|
|
int data_len = 0;
|
|
int res;
|
|
int nparam = 0;
|
|
char *key;
|
|
char *value;
|
|
int vlen;
|
|
bool value_free;
|
|
char sval[64];
|
|
char cont_type[64];
|
|
|
|
sprintf(cont_type, "%s", "Content-Type: text/plain");
|
|
|
|
// Parse dictionary
|
|
size_t max = dict->map.alloc;
|
|
mp_map_t *map = &dict->map;
|
|
mp_map_elem_t *next;
|
|
size_t cur = 0;
|
|
if ((rq_debug) && (send)) printf(">>> SEND FIELDS [\n");
|
|
while (1) {
|
|
next = NULL;
|
|
for (size_t i = cur; i < max; i++) {
|
|
if (MP_MAP_SLOT_IS_FILLED(map, i)) {
|
|
cur = i + 1;
|
|
next = &(map->table[i]);
|
|
break;
|
|
}
|
|
}
|
|
if (next == NULL) break;
|
|
|
|
value = NULL;
|
|
value_free = false;
|
|
key = (char *)mp_obj_str_get_str(next->key);
|
|
|
|
if (MP_OBJ_IS_STR(next->value)) {
|
|
// String, it can be string to send or file name
|
|
value = (char *)mp_obj_str_get_str(next->value);
|
|
vlen = strlen(value);
|
|
res = handle_file(client, bndry, key, value, send);
|
|
if (res == 0) {
|
|
for (int i=0; i<strlen(value); i++) {
|
|
if ((value[i] < 0x20) || (value[i] > 0x7F)) {
|
|
value = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
data_len += res;
|
|
nparam++;
|
|
value = NULL;
|
|
}
|
|
}
|
|
else if (MP_OBJ_IS_INT(next->value)) {
|
|
int ival = mp_obj_get_int(next->value);
|
|
sprintf(sval,"%d", ival);
|
|
value = sval;
|
|
vlen = strlen(sval);
|
|
}
|
|
else if (MP_OBJ_IS_TYPE(next->value, &mp_type_float)) {
|
|
double fval = mp_obj_get_float(next->value);
|
|
sprintf(sval,"%f", fval);
|
|
value = sval;
|
|
vlen = strlen(sval);
|
|
}
|
|
else {
|
|
mp_obj_type_t *type = mp_obj_get_type(next->value);
|
|
mp_buffer_info_t value_buff;
|
|
if (type->buffer_p.get_buffer != NULL) {
|
|
int ret = type->buffer_p.get_buffer(next->value, &value_buff, MP_BUFFER_READ);
|
|
if ((ret == 0) && (value_buff.len > 0)) {
|
|
if (rq_base64) {
|
|
// Send base64 encoded
|
|
value = malloc(value_buff.len * 4 / 3 + 64);
|
|
if (value) {
|
|
ret = mbedtls_base64_encode((unsigned char *)value, value_buff.len * 4 / 3 + 64, (size_t *)&vlen, (const unsigned char *)value_buff.buf, value_buff.len);
|
|
if (ret == 0) {
|
|
value[vlen] = '\0';
|
|
sprintf(cont_type, "%s", "Content-Type: binary\r\nContent-Encoding: base64");
|
|
value_free = true;
|
|
}
|
|
else {
|
|
free((void *)value);
|
|
value = NULL;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
value = value_buff.buf;
|
|
vlen = value_buff.len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (value) {
|
|
res = post_field(client, bndry, key, cont_type, value, vlen, send);
|
|
if (value_free) free((void *)value);
|
|
if (res <= 0) return res;
|
|
data_len += res;
|
|
nparam++;
|
|
}
|
|
}
|
|
if (rq_debug) {
|
|
if (send) {
|
|
printf("] LENGTH=%d\n", data_len);
|
|
}
|
|
else printf(">>> CONTENT LENGTH = %d\n", data_len);
|
|
}
|
|
return data_len;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
static mp_obj_t request(int method, bool multipart, mp_obj_t post_data_in, char * url, char *tofile)
|
|
{
|
|
int status;
|
|
char fullname[128] = {'\0'};
|
|
char err_msg[128] = {'\0'};
|
|
esp_err_t err;
|
|
bool perform_handled = false;
|
|
bool free_post_data = false;
|
|
|
|
// Disable logging
|
|
if (!rq_debug) {
|
|
esp_log_level_set("HTTP_CLIENT", ESP_LOG_WARN);
|
|
esp_log_level_set("TRANSPORT", ESP_LOG_WARN);
|
|
esp_log_level_set("TRANS_SSL", ESP_LOG_WARN);
|
|
}
|
|
|
|
// Check if the response is redirected to file
|
|
rqbody_file = NULL;
|
|
if (tofile) {
|
|
// GET to file
|
|
int res = physicalPath(tofile, fullname);
|
|
if ((res != 0) || (strlen(fullname) == 0)) {
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error resolving file name"));
|
|
}
|
|
rqbody_file = fopen(fullname, "wb");
|
|
if (rqbody_file == NULL) {
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error opening file"));
|
|
}
|
|
}
|
|
|
|
char* post_data = NULL;
|
|
char bndry[32];
|
|
|
|
esp_http_client_config_t config = {0};
|
|
config.url = url;
|
|
config.event_handler = _http_event_handler;
|
|
config.buffer_size = 1024;
|
|
|
|
// Initialize the http_client and set the method
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
if (client == NULL) {
|
|
if (rqbody_file) fclose(rqbody_file);
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error initializing http client"));
|
|
}
|
|
esp_http_client_set_method(client, method);
|
|
|
|
if (rqheader) free(rqheader);
|
|
if (rqbody) free(rqbody);
|
|
rqheader = NULL;
|
|
rqheader_len = 0;
|
|
rqbody = NULL;
|
|
rqbody_len = 0;
|
|
rqbody_ptr = 0;
|
|
rqbody_ok = true;
|
|
|
|
if (method == HTTP_METHOD_POST) {
|
|
mp_obj_dict_t *dict;
|
|
if (!multipart) {
|
|
if (MP_OBJ_IS_TYPE(post_data_in, &mp_type_dict)) {
|
|
dict = MP_OBJ_TO_PTR(post_data_in);
|
|
post_data = url_post_fields(dict);
|
|
err = esp_http_client_set_post_field(client, post_data, strlen(post_data));
|
|
if (err != ESP_OK) {
|
|
if (rqbody_file) fclose(rqbody_file);
|
|
free(post_data);
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error setting post fields"));
|
|
}
|
|
free_post_data = true;
|
|
}
|
|
else if (MP_OBJ_IS_STR(post_data_in)) {
|
|
post_data = (char *)mp_obj_str_get_str(post_data_in);
|
|
err = esp_http_client_set_post_field(client, post_data, strlen(post_data));
|
|
if (err != ESP_OK) {
|
|
if (rqbody_file) fclose(rqbody_file);
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error setting post fields"));
|
|
}
|
|
}
|
|
else {
|
|
if (rqbody_file) fclose(rqbody_file);
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Expected Dict or String type argument"));
|
|
}
|
|
}
|
|
else {
|
|
// === multipart POST ===
|
|
if (MP_OBJ_IS_TYPE(post_data_in, &mp_type_dict)) {
|
|
dict = MP_OBJ_TO_PTR(post_data_in);
|
|
}
|
|
else {
|
|
if (rqbody_file) fclose(rqbody_file);
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Expected Dict type argument"));
|
|
}
|
|
|
|
// Prepare multipart boundary
|
|
memset(bndry,0x00,20);
|
|
int randn = rand();
|
|
sprintf(bndry, "_____%d_____", randn);
|
|
// Get body length
|
|
int cont_len = multipart_post_fields(dict, bndry, client, false);
|
|
if (cont_len <= 0) {
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Nothing to send"));
|
|
}
|
|
char temp_buf[128];
|
|
sprintf(temp_buf, "multipart/form-data; boundary=%s", bndry);
|
|
esp_http_client_set_header(client, "Content-Type", temp_buf);
|
|
|
|
// Perform actions
|
|
MP_THREAD_GIL_EXIT();
|
|
err = ESP_OK;
|
|
do {
|
|
if ((err = esp_http_client_open(client, cont_len)) != ESP_OK) {
|
|
sprintf(err_msg, "Http client error: open");
|
|
break;
|
|
}
|
|
|
|
// Send content
|
|
cont_len = multipart_post_fields(dict, bndry, client, true);
|
|
|
|
// Check response
|
|
if ((esp_http_client_perform_response(client)) != ESP_OK) {
|
|
sprintf(err_msg, "Http client error: response");
|
|
break;
|
|
}
|
|
} while (esp_http_client_process_again(client));
|
|
esp_http_client_cleanup(client);
|
|
MP_THREAD_GIL_ENTER();
|
|
perform_handled = true;
|
|
}
|
|
}
|
|
else if ((method == HTTP_METHOD_PUT) || (method == HTTP_METHOD_PATCH)) {
|
|
// String, it can be string to send or file name
|
|
if (MP_OBJ_IS_STR(post_data_in)) {
|
|
post_data = (char *)mp_obj_str_get_str(post_data_in);
|
|
int cont_len = handle_file(client, NULL, NULL, post_data, false);
|
|
if (cont_len > 0) {
|
|
// Perform actions
|
|
MP_THREAD_GIL_EXIT();
|
|
err = ESP_OK;
|
|
do {
|
|
if ((err = esp_http_client_open(client, cont_len)) != ESP_OK) {
|
|
sprintf(err_msg, "Http client error: open");
|
|
break;
|
|
}
|
|
|
|
// Send content
|
|
cont_len = handle_file(client, NULL, NULL, post_data, true);
|
|
|
|
// Check response
|
|
if ((esp_http_client_perform_response(client)) != ESP_OK) {
|
|
sprintf(err_msg, "Http client error: response");
|
|
break;
|
|
}
|
|
} while (esp_http_client_process_again(client));
|
|
esp_http_client_cleanup(client);
|
|
MP_THREAD_GIL_ENTER();
|
|
perform_handled = true;
|
|
}
|
|
else {
|
|
err = esp_http_client_set_post_field(client, post_data, strlen(post_data));
|
|
if (err != ESP_OK) {
|
|
if (rqbody_file) fclose(rqbody_file);
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error setting post fields"));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (rqbody_file) fclose(rqbody_file);
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Expected String type argument"));
|
|
}
|
|
}
|
|
|
|
if (!perform_handled) {
|
|
MP_THREAD_GIL_EXIT();
|
|
err = esp_http_client_perform(client);
|
|
esp_http_client_cleanup(client);
|
|
if ((free_post_data) && (post_data)) free(post_data);
|
|
MP_THREAD_GIL_ENTER();
|
|
}
|
|
|
|
if (err != ESP_OK) {
|
|
if (rqbody_file) fclose(rqbody_file);
|
|
if (rqheader) free(rqheader);
|
|
if (rqbody) free(rqbody);
|
|
rqbody_file = NULL;
|
|
rqheader = NULL;
|
|
rqbody = NULL;
|
|
ESP_LOGE(TAG, "HTTP Request failed: %s [%s]", esp_err_to_name(err), err_msg);
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "HTTP Request failed"));
|
|
}
|
|
|
|
status = esp_http_client_get_status_code(client);
|
|
|
|
mp_obj_t tuple[3];
|
|
|
|
tuple[0] = mp_obj_new_int(status);
|
|
if ((rqheader) && (rqheader_ptr)) tuple[1] = mp_obj_new_str(rqheader, rqheader_ptr);
|
|
else tuple[1] = mp_const_none;
|
|
|
|
if (rqbody_file) tuple[2] = mp_obj_new_str(tofile, strlen(tofile));
|
|
else if ((rqbody) && (rqbody_ptr)) tuple[2] = mp_obj_new_str(rqbody, rqbody_ptr);
|
|
else tuple[2] = mp_const_none;
|
|
|
|
if (rqbody_file) fclose(rqbody_file);
|
|
if (rqheader) free(rqheader);
|
|
if (rqbody) free(rqbody);
|
|
rqbody_file = NULL;
|
|
rqheader = NULL;
|
|
rqbody = NULL;
|
|
|
|
return mp_obj_new_tuple(3, tuple);
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
void get_certificate(mp_obj_t cert, char *cert_pem_buf)
|
|
{
|
|
if (MP_OBJ_IS_STR(cert)) {
|
|
struct stat sb;
|
|
char *fname = NULL;
|
|
char certname[128] = {'\0'};
|
|
|
|
fname = (char *)mp_obj_str_get_str(cert);
|
|
esp_err_t res = physicalPath(fname, certname);
|
|
if ((res != 0) || (strlen(certname) == 0)) {
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error resolving file name"));
|
|
}
|
|
if (stat(certname, &sb) != 0) {
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error opening certificate file"));
|
|
}
|
|
FILE *fhndl = fopen(certname, "rb");
|
|
if (!fhndl) {
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error opening certificate file"));
|
|
}
|
|
|
|
if (cert_pem_buf) free(cert_pem_buf);
|
|
cert_pem_buf = NULL;
|
|
cert_pem_buf = calloc(sizeof(char), sb.st_size+16);
|
|
if (cert_pem_buf == NULL) {
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error allocating certificate buffer"));
|
|
}
|
|
int rd_len = fread(cert_pem_buf, 1, sb.st_size, fhndl);
|
|
fclose(fhndl);
|
|
if (rd_len != sb.st_size) {
|
|
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Error reading from certificate file"));
|
|
}
|
|
}
|
|
else if (cert == mp_const_false) {
|
|
if (cert_pem_buf) free(cert_pem_buf);
|
|
cert_pem_buf = NULL;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
STATIC mp_obj_t requests_GET(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
|
{
|
|
network_checkConnection();
|
|
enum { ARG_url, ARG_file };
|
|
const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_url, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = mp_const_none } },
|
|
{ MP_QSTR_file, MP_ARG_OBJ, { .u_obj = mp_const_none } },
|
|
};
|
|
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
char *url = NULL;
|
|
char *fname = NULL;
|
|
|
|
url = (char *)mp_obj_str_get_str(args[ARG_url].u_obj);
|
|
|
|
if (MP_OBJ_IS_STR(args[ARG_file].u_obj)) {
|
|
// GET to file
|
|
fname = (char *)mp_obj_str_get_str(args[ARG_file].u_obj);
|
|
}
|
|
|
|
mp_obj_t res = request(HTTP_METHOD_GET, false, NULL, url, fname);
|
|
|
|
return res;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(requests_GET_obj, 1, requests_GET);
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
STATIC mp_obj_t requests_HEAD(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
|
{
|
|
network_checkConnection();
|
|
enum { ARG_url };
|
|
const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_url, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = mp_const_none } },
|
|
};
|
|
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
char *url = NULL;
|
|
|
|
url = (char *)mp_obj_str_get_str(args[ARG_url].u_obj);
|
|
|
|
mp_obj_t res = request(HTTP_METHOD_HEAD, false, NULL, url, NULL);
|
|
|
|
return res;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(requests_HEAD_obj, 1, requests_HEAD);
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
STATIC mp_obj_t requests_POST(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
|
{
|
|
network_checkConnection();
|
|
enum { ARG_url, ARG_params, ARG_file, ARG_multipart };
|
|
const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_url, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = mp_const_none } },
|
|
{ MP_QSTR_params, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = mp_const_none } },
|
|
{ MP_QSTR_file, MP_ARG_OBJ, { .u_obj = mp_const_none } },
|
|
{ MP_QSTR_multipart, MP_ARG_BOOL, { .u_bool = false } },
|
|
};
|
|
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
char *url = NULL;
|
|
char *fname = NULL;
|
|
|
|
url = (char *)mp_obj_str_get_str(args[ARG_url].u_obj);
|
|
|
|
if (MP_OBJ_IS_STR(args[ARG_file].u_obj)) {
|
|
// GET to file
|
|
fname = (char *)mp_obj_str_get_str(args[ARG_file].u_obj);
|
|
}
|
|
|
|
mp_obj_t res = request(HTTP_METHOD_POST, args[ARG_multipart].u_bool, args[ARG_params].u_obj, url, fname);
|
|
|
|
return res;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(requests_POST_obj, 1, requests_POST);
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
STATIC mp_obj_t requests_PUT(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
|
{
|
|
network_checkConnection();
|
|
enum { ARG_url, ARG_data };
|
|
const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_url, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = mp_const_none } },
|
|
{ MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = mp_const_none } },
|
|
};
|
|
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
char *url = NULL;
|
|
|
|
url = (char *)mp_obj_str_get_str(args[ARG_url].u_obj);
|
|
|
|
mp_obj_t res = request(HTTP_METHOD_PUT, false, args[ARG_data].u_obj, url, NULL);
|
|
|
|
return res;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(requests_PUT_obj, 1, requests_PUT);
|
|
|
|
//----------------------------------------------------------------------------------------
|
|
STATIC mp_obj_t requests_PATCH(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
|
{
|
|
network_checkConnection();
|
|
enum { ARG_url, ARG_data };
|
|
const mp_arg_t allowed_args[] = {
|
|
{ MP_QSTR_url, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = mp_const_none } },
|
|
{ MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = mp_const_none } },
|
|
};
|
|
|
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
|
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
|
|
|
char *url = NULL;
|
|
|
|
url = (char *)mp_obj_str_get_str(args[ARG_url].u_obj);
|
|
|
|
mp_obj_t res = request(HTTP_METHOD_PATCH, false, args[ARG_data].u_obj, url, NULL);
|
|
|
|
return res;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(requests_PATCH_obj, 1, requests_PATCH);
|
|
|
|
//---------------------------------------------
|
|
STATIC mp_obj_t requests_debug(mp_obj_t dbg_in)
|
|
{
|
|
rq_debug = mp_obj_is_true(dbg_in);
|
|
|
|
return mp_const_none;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(requests_debug_obj, requests_debug);
|
|
|
|
//----------------------------------------------------
|
|
STATIC mp_obj_t requests_certificate(mp_obj_t cert_in)
|
|
{
|
|
if (cert_in != mp_const_none) get_certificate(cert_in, cert_pem);
|
|
|
|
return mp_const_none;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(requests_certificate_obj, requests_certificate);
|
|
|
|
|
|
//================================================================
|
|
STATIC const mp_rom_map_elem_t requests_module_globals_table[] = {
|
|
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_requests) },
|
|
|
|
{ MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&requests_GET_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_head), MP_ROM_PTR(&requests_HEAD_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_post), MP_ROM_PTR(&requests_POST_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_put), MP_ROM_PTR(&requests_PUT_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_patch), MP_ROM_PTR(&requests_PATCH_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_debug), MP_ROM_PTR(&requests_debug_obj) },
|
|
{ MP_ROM_QSTR(MP_QSTR_certificate), MP_ROM_PTR(&requests_certificate_obj) },
|
|
};
|
|
STATIC MP_DEFINE_CONST_DICT(requests_module_globals, requests_module_globals_table);
|
|
|
|
//==========================================
|
|
const mp_obj_module_t mp_module_requests = {
|
|
.base = { &mp_type_module },
|
|
.globals = (mp_obj_dict_t*)&requests_module_globals,
|
|
};
|
|
|
|
#endif
|