Chiamate in HTTPS e non più in HTTP

Dal mio Wemos chiamo un file ad un indirizzo per vedere se è a ZERO oppure a UNO.

Succede che questo sito è diventato https e il Wemos non riesce più a leggere con la chiamata GET, cosa che funziona benissimo se io faccio www.miosito.it/public/BB/miofile.txt

la chiamata che ha sempre funzionato in http è

if (client.connect("www.miosito.net", 80)) {
    strURL = "GET /public/BB/";
    strURL += serialnumber;
    strURL += ".txt HTTP/1.1";
    client.println(strURL);
    client.println("Host: www.miosito.net");
    client.println("User-Agent: Arduino 1.0\r\n\r\n");

La riprova è che se io forzo il sito in http la chiamata torna a funzionare.
Mentre adesso, con https non funziona più.
Se faccio un Serial.print della risposta mi da

HTTP/1.1 301 Moved Permanently
Date: Thu, 23 Dec 2021 08:41:36 GMT
Server: Apache
Location: https://www.miosito.net/public/BB/5.txt
Content-Length: 251
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="https://www.miosito.net/public/BB/5.txt">here</a>.</p>
</body></html>

Qualche suggerimento?

Hai provato a forzare https nella chiamata connect ?
if (client.connect("https://www.miosito.net", 80))

Qui ho trovato delle info su come connettersi a https da esp8266 (wemos è 8266 giusto?)
"How to connect to an SSL protected server with ESP8266(WiFiClient)"

Si, Wemos è 8266. Adesso provo a seguire le tue dritte.
Grazie

Niente da fare.
Non è così facile, da quanto ho letto: il Wemos con il sito web devono scambiarsi i certificati SSL e la cosa non è per niente semplice

Con ESP8266, se non ti interessa la verifica del certificato, puoi impostare la connessione come "insecure" usando però il client WiFiClientSecure.
Le connessioni HTTPS però sono solo sulla porta 443, mentre tu stai usando la 80 che è solo HTTP

  #include <WiFiClientSecure.h>
  WiFiClientSecure client;
  client.setInsecure(true);

Se invece vuoi usare il certificato, lo devi prima recuperare dal sito web in formato base64 (basta un browser) e poi impostarlo nel client (come un char array)

const char cert_Root_CA [] PROGMEM = R"CERT(
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
......
......
-----END CERTIFICATE-----
)CERT";

  X509List cert(cert_Root_CA );
 client.setTrustAnchors(&cert);

Se ricordo bene, proprio tu avevi indicato QUESTO ottimo sito per il recupero dei certificati da usare con ESP :wink:

Guglielmo

No, non lo conoscevo.
Io uso direttamente il browser e poi esporto il certificato se ho bisogno del formato base 64.

Basta fare click sull'icona a forma di lucchetto vicino all'indirizzo, poi visualizza certificato e quindi esporta.
Di solito conviene, risalire la catena di autorizzazioni ed inserire il certificato della certification authority (Go Daddy, Cloudflare etc et c) perché ha una durata maggiore rispetto a quello del singolo sito (tipicamente decine di anni)
Non sono ancora riuscito a fare quest'operazione direttamente dagli ESP :disappointed_relieved:

Il file del certificato si potrebbe anche caricare direttamente dalla flash volendo, questo potrebbe essere una buon compromesso.

... allora ricordavo male, comunque era un link che era stato dato qui, su un post del forum Italiano ed è piuttosto comodo, ti prepara già tutto lui il pezzo di codice ... devi fare solo copia/incolla :grinning:

Guglielmo

Quindi dovrei togliere #WiFiClient e mettere #WiFiClientSecure ?
poi WiFiClient client; andrà sostituito con WiFiClientSecure client; immagino.

#include <WiFiClientSecure.h>
WiFiClientSecure client;

void setup() {
  
  client.setInsecure(true);
}

void loop() {

}

Siccome mi dava errore, ho provato a mettere solamente le istruzioni , senza il programma, ma mi da sempre errore:

no matching function for call to 'BearSSL::WiFiClientSecure::setInsecure(bool)'

Colpa mia scusa, il metodo setInsecure() è senza parametri.

 client.setInsecure();

Grazie mille cotestatnt.
Sembra che funzioni, anche se è più lento a scrivere sul MySQL.
Vedrò come mai.

E' abbastanza normale perché anche se lato ESP decidi di non verificare il certificato, tutto l'handshake necessario per instaurare una connessione HTTPS rimane.
Inoltre i dati viaggiano crittografati mentre prima era tutto in chiaro ed anche questo richiede un minimo di tempo di elaborazione in più.

Puoi ridurre un pochino i tempi usando le sessioni. Dai uno sguardo all'esempio BearSSL_session.ino

Ho fatto una prova al volo con setInsecure + sessione con un server HTTPS remoto (Telegram) facendo una richiesta ogni 30 secondi e sono passato da circa 1400/1500 ms senza sessione a circa 1000/1100 ms.

Poco, ma meglio di niente!

Un modo molto più efficace per aumentare la velocità di connessione, è quello di non chiudere il socket TCP immediatamente dopo la richiesta con l'istruzione client.stop();
E' necessario anche rimuovere l'eventuale header "Connection: close" perché altrimenti sarà il server una volta elaborata la richiesta a chiudere. Modica o aggiungi l'header in "Connection: Keep-Alive".

La connessione ad un certo punto verrà comunque interrotta, ma testando se c'è ancora connessione la puoi ristabilire nuovamente (dipende dal server, con Telegram ad esempio accade tipo ogni 5 minuti).

Sempre con l'esempio di cui sopra, i tempi sono passati a circa 300/400 ms.

N.B.
Negli esempi di solito inseriscono un delay() o un while tra la richiesta HTTP e la successiva lettura dati, io invece ho usato una variabile bool perché odio gli algoritmi bloccanti (anche se questo esempio lo è voltuamente alla fine).

#include <ESP8266WiFi.h>
const char* ssid     = "xxxxxxxxxx";
const char* password = "xxxxxxxxxx";
const char* host = "api.telegram.org";
const uint16_t port = 443;

#define STARS "\n**************************\n"

// Use WiFiClient class to create TCP connections
WiFiClientSecure client;
BearSSL::Session session;

void setup() {
  Serial.begin(115200);
  Serial.printf("Connecting to %s\n", ssid);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.print("\nWiFi connected. IP address: ");
  Serial.println(WiFi.localIP());

  client.setInsecure();
  client.setSession(&session);
}

void loop() {
  static bool doDelay;
  static bool waitData;
  static uint32_t startTime;

  if (!client.connected()) {
    startTime = millis();
    Serial.printf("Connecting to %s:%d\n", host, port);
    if (!client.connect(host, port)) {
      client.stop();
      Serial.println("connection failed");
    }
    waitData = false;
    Serial.printf(STARS"Connection time: %dms"STARS, millis() - startTime);
  }

  if (client.connected() && !waitData) {
    startTime = millis();
    client.println("GET /botxxxxxxxx:AAGhC7lD2_lLWhRlkoQcnrranavca8rPrfM/getMe HTTP/1.0");
    client.println("Host: api.telegram.org");
    client.println("Connection: Keep-Alive");
    client.println();
    waitData = true;  // waiting for data to be available
  }

  if (client.available()) {
    while (client.available()) {
      char ch = static_cast<char>(client.read());
      Serial.print(ch);
    }
    waitData = false;
    Serial.printf(STARS"HTTP GET request time: %dms"STARS, millis() - startTime);
  }

  //client.stop();
  
  doDelay = true;
  // Do nothing only if not waiting for HTTP data
  if (doDelay && !waitData) {
    delay(30000);
    doDelay = false;
  }
}

Ho fatto prove fino ad oggi. Grazie per le dritte. Ho fatto diversi tipi di chiamate ma questo mi pare funzioni meglio:

HTTPClient https;
BearSSL::WiFiClientSecure newSecure;

void setup ();
{
newSecure.setInsecure();
}

void loop ();
{
  strURL = "/importG.php?s=";
  strURL += serialnumber;
  strURL += "_";
  strURL += strG;

  int checkBegin = https.begin(newSecure, "www.miosito.net", 443, strURL , false);
  int code = https.GET();

in questo caso riesco a scrivere una bella stringa di 35 dati da 2 bytes più gli spazi ogni 2,5 secondi.

Le letture, invece, fatte con:

{
  strURL = "/public/BB/PRO_";
  strURL += serialnumber;
  strURL += ".txt";

  int checkBegin = https.begin(newSecure, "www.miosito.net", 443, strURL , false);
  int code = https.GET();
  String payload = https.getString();
  Serial.print(checkBegin); Serial.print (" - "); Serial.print(code); Serial.print (" - "); Serial.println(payload);

sono velocissime !!!
Vado a leggere 2 files nel web in pochi centesimi mentre prima, col sistema classico in HTTP, ogni lettura di file mi portava via 6 secondi.

Ho visto che è meglio non usare, alla fine del loop:

  https.end();
  newSecure.stop();

E' molto più veloce, ma non so se porta degli "effetti collaterali".

Prova a tenere sotto controllo lo stato dell'heap.
Con ESP32 io ci sono diventato matto, perché ogni volta che aprivo una nuova connessione "sparivano" alcuni byte disponibili perché il buffer allocato non veniva recuperato correttamente.

Con ESP8266 invece non ho mai riscontrato questi problemi, ad ogni modo una verifica non guasta.

Io di solito aggiungo questa funzione (buona per tutte e due le piattaforme) nel loop() nella fase di debug:

void printHeapStats() {
  time_t now = time(nullptr);
  struct tm tInfo = *localtime(&now);
  static uint32_t infoTime;
  if (millis() - infoTime > 10000) {
    infoTime = millis();
#ifdef ESP32
    //heap_caps_print_heap_info(MALLOC_CAP_DEFAULT);
    Serial.printf("%02d:%02d:%02d - Total free: %6d - Max block: %6d\n",
                  tInfo.tm_hour, tInfo.tm_min, tInfo.tm_sec, heap_caps_get_free_size(0), heap_caps_get_largest_free_block(0) );
#elif defined(ESP8266)
    uint32_t free;
    uint16_t max;

    ESP.getHeapStats(&free, &max, nullptr);
    Serial.printf("%02d:%02d:%02d - Total free: %5d - Max block: %5d\n",
                  tInfo.tm_hour, tInfo.tm_min, tInfo.tm_sec, free, max);
#endif
  }
}

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.