mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-21 10:26:06 +03:00
Merge pull request #1091 from Links2004/httpClient
rework ESP8266HTTPUpdate to use httpClient and allow https updates
This commit is contained in:
commit
14949f50d6
@ -340,6 +340,7 @@ function sendFile($path) {
|
||||
header('Content-Type: application/octet-stream', true);
|
||||
header('Content-Disposition: attachment; filename='.basename($path));
|
||||
header('Content-Length: '.filesize($path), true);
|
||||
header('x-MD5: '.md5_file($path), true);
|
||||
readfile($path);
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,8 @@ httpClient::httpClient() {
|
||||
_reuse = false;
|
||||
_https = false;
|
||||
|
||||
_userAgent = "ESP8266httpClient";
|
||||
|
||||
_headerKeysCount = 0;
|
||||
_currentHeaders = NULL;
|
||||
|
||||
@ -200,7 +202,6 @@ bool httpClient::connected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* try to reuse the connection to the server
|
||||
* keep-alive
|
||||
@ -210,6 +211,14 @@ void httpClient::setReuse(bool reuse) {
|
||||
_reuse = reuse;
|
||||
}
|
||||
|
||||
/**
|
||||
* set User Agent
|
||||
* @param userAgent const char *
|
||||
*/
|
||||
void httpClient::setUserAgent(const char * userAgent) {
|
||||
_userAgent = userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* send a GET request
|
||||
* @return http code
|
||||
@ -240,7 +249,7 @@ int httpClient::POST(String payload) {
|
||||
* @return -1 if no info or > 0 when Content-Length is set by server
|
||||
*/
|
||||
int httpClient::sendRequest(const char * type, uint8_t * payload, size_t size) {
|
||||
// connect ro server
|
||||
// connect to server
|
||||
if(!connect()) {
|
||||
return HTTPC_ERROR_CONNECTION_REFUSED;
|
||||
}
|
||||
@ -265,6 +274,77 @@ int httpClient::sendRequest(const char * type, uint8_t * payload, size_t size) {
|
||||
return handleHeaderResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* sendRequest
|
||||
* @param type const char * "GET", "POST", ....
|
||||
* @param stream Stream * data stream for the message body
|
||||
* @param size size_t size for the message body if 0 not Content-Length is send
|
||||
* @return -1 if no info or > 0 when Content-Length is set by server
|
||||
*/
|
||||
int httpClient::sendRequest(const char * type, Stream * stream, size_t size) {
|
||||
|
||||
if(!stream) {
|
||||
return HTTPC_ERROR_NO_STREAM;
|
||||
}
|
||||
|
||||
// connect to server
|
||||
if(!connect()) {
|
||||
return HTTPC_ERROR_CONNECTION_REFUSED;
|
||||
}
|
||||
|
||||
if(size > 0) {
|
||||
addHeader("Content-Length", String(size));
|
||||
}
|
||||
|
||||
// send Header
|
||||
if(!sendHeader(type)) {
|
||||
return HTTPC_ERROR_SEND_HEADER_FAILED;
|
||||
}
|
||||
|
||||
// create buffer for read
|
||||
uint8_t buff[1460] = { 0 };
|
||||
|
||||
int len = size;
|
||||
int bytesWritten = 0;
|
||||
|
||||
if(len == 0) {
|
||||
len = -1;
|
||||
}
|
||||
|
||||
// read all data from stream and send it to server
|
||||
while(connected() && stream->available() && (len > 0 || len == -1)) {
|
||||
|
||||
// get available data size
|
||||
size_t s = stream->available();
|
||||
|
||||
if(s) {
|
||||
int c = stream->readBytes(buff, ((s > sizeof(buff)) ? sizeof(buff) : s));
|
||||
|
||||
// write it to Stream
|
||||
bytesWritten += _tcp->write((const uint8_t *)buff, c);
|
||||
|
||||
if(len > 0) {
|
||||
len -= c;
|
||||
}
|
||||
|
||||
delay(0);
|
||||
} else {
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(size && (int)size != bytesWritten) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload bytesWritten %d and size %d mismatch!.\n", bytesWritten, _size);
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] ERROR SEND PAYLOAD FAILED!");
|
||||
return HTTPC_ERROR_SEND_PAYLOAD_FAILED;
|
||||
} else {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload written: %d\n", bytesWritten);
|
||||
}
|
||||
|
||||
// handle Server Response (Header)
|
||||
return handleHeaderResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* size of message body / payload
|
||||
* @return -1 if no info or > 0 when Content-Length is set by server
|
||||
@ -374,7 +454,6 @@ String httpClient::getString(void) {
|
||||
return sstring;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* adds Header to the request
|
||||
* @param name
|
||||
@ -383,6 +462,8 @@ String httpClient::getString(void) {
|
||||
*/
|
||||
void httpClient::addHeader(const String& name, const String& value, bool first) {
|
||||
|
||||
// not allow set of Header handled by code
|
||||
if(!name.equalsIgnoreCase("Connection") && !name.equalsIgnoreCase("User-Agent") && !name.equalsIgnoreCase("Host")) {
|
||||
String headerLine = name;
|
||||
headerLine += ": ";
|
||||
headerLine += value;
|
||||
@ -393,6 +474,8 @@ void httpClient::addHeader(const String& name, const String& value, bool first)
|
||||
} else {
|
||||
_Headers += headerLine;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void httpClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) {
|
||||
@ -448,7 +531,6 @@ bool httpClient::connect(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if(_https) {
|
||||
DEBUG_HTTPCLIENT("[HTTP-Client] connect https...\n");
|
||||
if(_tcps) {
|
||||
@ -502,9 +584,10 @@ bool httpClient::sendHeader(const char * type) {
|
||||
if(!connected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String header = String(type) + " " + _url + " HTTP/1.1\r\n"
|
||||
"Host: " + _host + "\r\n"
|
||||
"User-Agent: ESP8266httpClient\r\n"
|
||||
"User-Agent: " + _userAgent + "\r\n"
|
||||
"Connection: ";
|
||||
|
||||
if(_reuse) {
|
||||
|
@ -59,12 +59,14 @@ class httpClient {
|
||||
bool connected(void);
|
||||
|
||||
void setReuse(bool reuse); /// keep-alive
|
||||
void setUserAgent(const char * userAgent);
|
||||
|
||||
/// request handling
|
||||
int GET();
|
||||
int POST(uint8_t * payload, size_t size);
|
||||
int POST(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);
|
||||
|
||||
@ -106,6 +108,7 @@ class httpClient {
|
||||
String _httpsFingerprint;
|
||||
|
||||
String _Headers;
|
||||
String _userAgent;
|
||||
|
||||
/// Response handling
|
||||
RequestArgument* _currentHeaders;
|
||||
|
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* httpUpdate.ino
|
||||
*
|
||||
* Created on: 27.11.2015
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WiFiMulti.h>
|
||||
|
||||
#include <ESP8266httpClient.h>
|
||||
#include <ESP8266httpUpdate.h>
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
|
||||
void setup() {
|
||||
|
||||
USE_SERIAL.begin(115200);
|
||||
// USE_SERIAL.setDebugOutput(true);
|
||||
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
|
||||
for(uint8_t t = 4; t > 0; t--) {
|
||||
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
|
||||
USE_SERIAL.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFiMulti.addAP("SSID", "PASSWORD");
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// wait for WiFi connection
|
||||
if((WiFiMulti.run() == WL_CONNECTED)) {
|
||||
|
||||
t_httpUpdate_return ret = ESPhttpUpdate.update("http://server/file.bin");
|
||||
//t_httpUpdate_return ret = ESPhttpUpdate.update("https://server/file.bin");
|
||||
|
||||
switch(ret) {
|
||||
case HTTP_UPDATE_FAILED:
|
||||
USE_SERIAL.println("HTTP_UPDATE_FAILD");
|
||||
break;
|
||||
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
USE_SERIAL.println("HTTP_UPDATE_NO_UPDATES");
|
||||
break;
|
||||
|
||||
case HTTP_UPDATE_OK:
|
||||
USE_SERIAL.println("HTTP_UPDATE_OK");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,103 +23,96 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "ESP8266httpUpdate.h"
|
||||
|
||||
ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) {
|
||||
|
||||
ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) {
|
||||
}
|
||||
|
||||
ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void) {
|
||||
|
||||
}
|
||||
|
||||
t_httpUpdate_return ESP8266HTTPUpdate::update(const char * host, uint16_t port, const char * url, const char * current_version) {
|
||||
WiFiClient tcp;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] connected to %s:%u %s .... ", host, port, url);
|
||||
|
||||
if(!tcp.connect(host, port)) {
|
||||
DEBUG_HTTP_UPDATE("failed.\n");
|
||||
return HTTP_UPDATE_FAILED;
|
||||
}
|
||||
DEBUG_HTTP_UPDATE("ok.\n");
|
||||
return update(tcp, host, url, current_version);
|
||||
/**
|
||||
*
|
||||
* @param url const char *
|
||||
* @param current_version const char *
|
||||
* @param httpsFingerprint const char *
|
||||
* @return t_httpUpdate_return
|
||||
*/
|
||||
t_httpUpdate_return ESP8266HTTPUpdate::update(const char * url, const char * current_version, const char * httpsFingerprint) {
|
||||
httpClient http;
|
||||
http.begin(url, httpsFingerprint);
|
||||
return handleUpdate(&http, current_version);
|
||||
}
|
||||
|
||||
t_httpUpdate_return ESP8266HTTPUpdate::update(WiFiClient& tcp, const char* host, const char* url, const char * current_version) {
|
||||
/**
|
||||
*
|
||||
* @param host const char *
|
||||
* @param port uint16_t
|
||||
* @param url const char *
|
||||
* @param current_version const char *
|
||||
* @param httpsFingerprint const char *
|
||||
* @return
|
||||
*/
|
||||
t_httpUpdate_return ESP8266HTTPUpdate::update(const char * host, uint16_t port, const char * url, const char * current_version, bool https, const char * httpsFingerprint) {
|
||||
httpClient http;
|
||||
http.begin(host, port, url, https, httpsFingerprint);
|
||||
return handleUpdate(&http, current_version);
|
||||
}
|
||||
|
||||
t_httpUpdate_return ESP8266HTTPUpdate::update(String host, uint16_t port, String url, String current_version, bool https, String httpsFingerprint) {
|
||||
httpClient http;
|
||||
http.begin(host, port, url, https, httpsFingerprint);
|
||||
return handleUpdate(&http, current_version.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param http httpClient *
|
||||
* @param current_version const char *
|
||||
* @return t_httpUpdate_return
|
||||
*/
|
||||
t_httpUpdate_return ESP8266HTTPUpdate::handleUpdate(httpClient * http, const char * current_version) {
|
||||
|
||||
t_httpUpdate_return ret = HTTP_UPDATE_FAILED;
|
||||
// set Timeout for readBytesUntil and readStringUntil
|
||||
tcp.setTimeout(2000);
|
||||
tcp.setNoDelay(true);
|
||||
|
||||
String req = "GET ";
|
||||
http->setUserAgent("ESP8266-http-Update");
|
||||
http->addHeader("x-ESP8266-STA-MAC", WiFi.macAddress());
|
||||
http->addHeader("x-ESP8266-AP-MAC", WiFi.softAPmacAddress());
|
||||
http->addHeader("x-ESP8266-free-space", String(ESP.getFreeSketchSpace()));
|
||||
http->addHeader("x-ESP8266-sketch-size", String(ESP.getSketchSize()));
|
||||
http->addHeader("x-ESP8266-chip-size", String(ESP.getFlashChipRealSize()));
|
||||
http->addHeader("x-ESP8266-sdk-version", ESP.getSdkVersion());
|
||||
|
||||
req += url;
|
||||
req += " HTTP/1.1\r\n"
|
||||
"Host: ";
|
||||
req += host;
|
||||
req += "\r\n"
|
||||
"Connection: close\r\n"
|
||||
"User-Agent: ESP8266-http-Update\r\n"
|
||||
"x-ESP8266-STA-MAC: ";
|
||||
req += WiFi.macAddress();
|
||||
req += "\r\n"
|
||||
"x-ESP8266-AP-MAC: ";
|
||||
req += WiFi.softAPmacAddress();
|
||||
req += "\r\n"
|
||||
"x-ESP8266-free-space: ";
|
||||
req += ESP.getFreeSketchSpace();
|
||||
req += "\r\n"
|
||||
"x-ESP8266-sketch-size: ";
|
||||
req += ESP.getSketchSize();
|
||||
req += "\r\n"
|
||||
"x-ESP8266-chip-size: ";
|
||||
req += ESP.getFlashChipRealSize();
|
||||
req += "\r\n"
|
||||
"x-ESP8266-sdk-version: ";
|
||||
req += ESP.getSdkVersion();
|
||||
|
||||
if(current_version[0] != 0x00) {
|
||||
req += "\r\n"
|
||||
"x-ESP8266-version: ";
|
||||
req += current_version;
|
||||
if(current_version && current_version[0] != 0x00) {
|
||||
http->addHeader("x-ESP8266-version", current_version);
|
||||
}
|
||||
|
||||
req += "\r\n"
|
||||
"\r\n";
|
||||
const char * headerkeys[] = { "x-MD5" };
|
||||
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
|
||||
|
||||
tcp.write(req.c_str(), req.length());
|
||||
// track these headers
|
||||
http->collectHeaders(headerkeys, headerkeyssize);
|
||||
|
||||
uint32_t code = 0;
|
||||
size_t len = 0;
|
||||
|
||||
while(true) {
|
||||
String headerLine = tcp.readStringUntil('\n');
|
||||
headerLine.trim(); // remove \r
|
||||
|
||||
if(headerLine.length() > 0) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate][Header] RX: %s\n", headerLine.c_str());
|
||||
if(headerLine.startsWith("HTTP/1.")) {
|
||||
// 9 = lenght of "HTTP/1.x "
|
||||
code = headerLine.substring(9, headerLine.indexOf(' ', 9)).toInt();
|
||||
} else if(headerLine.startsWith("Content-Length: ")) {
|
||||
// 16 = lenght of "Content-Length: "
|
||||
len = headerLine.substring(16).toInt();
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int code = http->GET();
|
||||
int len = http->getSize();
|
||||
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n");
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n");
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code);
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len);
|
||||
|
||||
if(http->hasHeader("x-MD5")) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", http->header("x-MD5").c_str());
|
||||
}
|
||||
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n");
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace());
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize());
|
||||
|
||||
if(current_version[0] != 0x00) {
|
||||
if(current_version && current_version[0] != 0x00) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", current_version);
|
||||
}
|
||||
|
||||
@ -131,15 +124,17 @@ t_httpUpdate_return ESP8266HTTPUpdate::update(WiFiClient& tcp, const char* host,
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len);
|
||||
} else {
|
||||
|
||||
WiFiClient * tcp = http->getStreamPtr();
|
||||
|
||||
WiFiUDP::stopAll();
|
||||
WiFiClient::stopAllExcept(&tcp);
|
||||
WiFiClient::stopAllExcept(tcp);
|
||||
|
||||
delay(100);
|
||||
|
||||
if(ESP.updateSketch(tcp, len, false, false)) {
|
||||
if(runUpdate(*tcp, len, http->header("x-MD5"))) {
|
||||
ret = HTTP_UPDATE_OK;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n");
|
||||
tcp.stop();
|
||||
http->end();
|
||||
ESP.restart();
|
||||
} else {
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
@ -148,7 +143,7 @@ t_httpUpdate_return ESP8266HTTPUpdate::update(WiFiClient& tcp, const char* host,
|
||||
}
|
||||
} else {
|
||||
ret = HTTP_UPDATE_FAILED;
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0?!\n");
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0 or not set by Server?!\n");
|
||||
}
|
||||
break;
|
||||
case 304:
|
||||
@ -164,11 +159,42 @@ t_httpUpdate_return ESP8266HTTPUpdate::update(WiFiClient& tcp, const char* host,
|
||||
break;
|
||||
}
|
||||
|
||||
http->end();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
t_httpUpdate_return ESP8266HTTPUpdate::update(String host, uint16_t port, String url, String current_version) {
|
||||
return update(host.c_str(), port, url.c_str(), current_version.c_str());
|
||||
/**
|
||||
* write Update to flash
|
||||
* @param in Stream&
|
||||
* @param size uint32_t
|
||||
* @param md5 String
|
||||
* @return true if Update ok
|
||||
*/
|
||||
bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5) {
|
||||
|
||||
if(!Update.begin(size)) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.begin failed!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(md5.length()) {
|
||||
Update.setMD5(md5.c_str());
|
||||
}
|
||||
|
||||
if(Update.writeStream(in) != size) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.writeStream failed!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Update.end()) {
|
||||
DEBUG_HTTP_UPDATE("[httpUpdate] Update.end failed!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ESP8266HTTPUpdate ESPhttpUpdate;
|
||||
|
@ -28,8 +28,9 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <WiFiClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <ESP8266httpClient.h>
|
||||
|
||||
//#define DEBUG_HTTP_UPDATE(...) Serial1.printf( __VA_ARGS__ )
|
||||
|
||||
@ -48,9 +49,13 @@ class ESP8266HTTPUpdate {
|
||||
ESP8266HTTPUpdate(void);
|
||||
~ESP8266HTTPUpdate(void);
|
||||
|
||||
t_httpUpdate_return update(const char * host, uint16_t port, const char * url = "/", const char * current_version = "");
|
||||
t_httpUpdate_return update(String host, uint16_t port, String url = "/", String current_version = "");
|
||||
t_httpUpdate_return update(WiFiClient& client, const char* host, const char* url = "/", const char * current_version = "");
|
||||
t_httpUpdate_return update(const char * url, const char * current_version = "", const char * httpsFingerprint = "");
|
||||
t_httpUpdate_return update(const char * host, uint16_t port, const char * url = "/", const char * current_version = "", bool https = false, const char * httpsFingerprint = "");
|
||||
t_httpUpdate_return update(String host, uint16_t port, String url = "/", String current_version = "", bool https = false, String httpsFingerprint = "");
|
||||
|
||||
protected:
|
||||
t_httpUpdate_return handleUpdate(httpClient * http, const char * current_version);
|
||||
bool runUpdate(Stream& in, uint32_t size, String md5);
|
||||
};
|
||||
|
||||
extern ESP8266HTTPUpdate ESPhttpUpdate;
|
||||
|
Loading…
x
Reference in New Issue
Block a user