Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 14949f5

Browse files
committedNov 27, 2015
Merge pull request #1091 from Links2004/httpClient
rework ESP8266HTTPUpdate to use httpClient and allow https updates
2 parents 04e5623 + ceb8acb commit 14949f5

File tree

6 files changed

+274
-95
lines changed

6 files changed

+274
-95
lines changed
 

‎doc/ota_updates/ota_updates.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ function sendFile($path) {
340340
header('Content-Type: application/octet-stream', true);
341341
header('Content-Disposition: attachment; filename='.basename($path));
342342
header('Content-Length: '.filesize($path), true);
343+
header('x-MD5: '.md5_file($path), true);
343344
readfile($path);
344345
}
345346

‎libraries/ESP8266httpClient/src/ESP8266httpClient.cpp

Lines changed: 98 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ httpClient::httpClient() {
4141
_reuse = false;
4242
_https = false;
4343

44+
_userAgent = "ESP8266httpClient";
45+
4446
_headerKeysCount = 0;
4547
_currentHeaders = NULL;
4648

@@ -77,7 +79,7 @@ httpClient::~httpClient() {
7779
* @param httpsFingerprint const char *
7880
*/
7981
void httpClient::begin(const char *url, const char * httpsFingerprint) {
80-
begin(String(url), String(httpsFingerprint));
82+
begin(String(url), String(httpsFingerprint));
8183
}
8284

8385
/**
@@ -111,7 +113,7 @@ void httpClient::begin(String url, String httpsFingerprint) {
111113
_host = url.substring(0, index); // hostname
112114
url.remove(0, (index + 1)); // remove hostname + :
113115

114-
index = url.indexOf('/');
116+
index = url.indexOf('/');
115117
_port = url.substring(0, index).toInt(); // get port
116118
url.remove(0, index); // remove port
117119
hasPort = true;
@@ -200,7 +202,6 @@ bool httpClient::connected() {
200202
return false;
201203
}
202204

203-
204205
/**
205206
* try to reuse the connection to the server
206207
* keep-alive
@@ -210,6 +211,14 @@ void httpClient::setReuse(bool reuse) {
210211
_reuse = reuse;
211212
}
212213

214+
/**
215+
* set User Agent
216+
* @param userAgent const char *
217+
*/
218+
void httpClient::setUserAgent(const char * userAgent) {
219+
_userAgent = userAgent;
220+
}
221+
213222
/**
214223
* send a GET request
215224
* @return http code
@@ -240,7 +249,7 @@ int httpClient::POST(String payload) {
240249
* @return -1 if no info or > 0 when Content-Length is set by server
241250
*/
242251
int httpClient::sendRequest(const char * type, uint8_t * payload, size_t size) {
243-
// connect ro server
252+
// connect to server
244253
if(!connect()) {
245254
return HTTPC_ERROR_CONNECTION_REFUSED;
246255
}
@@ -265,6 +274,77 @@ int httpClient::sendRequest(const char * type, uint8_t * payload, size_t size) {
265274
return handleHeaderResponse();
266275
}
267276

277+
/**
278+
* sendRequest
279+
* @param type const char * "GET", "POST", ....
280+
* @param stream Stream * data stream for the message body
281+
* @param size size_t size for the message body if 0 not Content-Length is send
282+
* @return -1 if no info or > 0 when Content-Length is set by server
283+
*/
284+
int httpClient::sendRequest(const char * type, Stream * stream, size_t size) {
285+
286+
if(!stream) {
287+
return HTTPC_ERROR_NO_STREAM;
288+
}
289+
290+
// connect to server
291+
if(!connect()) {
292+
return HTTPC_ERROR_CONNECTION_REFUSED;
293+
}
294+
295+
if(size > 0) {
296+
addHeader("Content-Length", String(size));
297+
}
298+
299+
// send Header
300+
if(!sendHeader(type)) {
301+
return HTTPC_ERROR_SEND_HEADER_FAILED;
302+
}
303+
304+
// create buffer for read
305+
uint8_t buff[1460] = { 0 };
306+
307+
int len = size;
308+
int bytesWritten = 0;
309+
310+
if(len == 0) {
311+
len = -1;
312+
}
313+
314+
// read all data from stream and send it to server
315+
while(connected() && stream->available() && (len > 0 || len == -1)) {
316+
317+
// get available data size
318+
size_t s = stream->available();
319+
320+
if(s) {
321+
int c = stream->readBytes(buff, ((s > sizeof(buff)) ? sizeof(buff) : s));
322+
323+
// write it to Stream
324+
bytesWritten += _tcp->write((const uint8_t *)buff, c);
325+
326+
if(len > 0) {
327+
len -= c;
328+
}
329+
330+
delay(0);
331+
} else {
332+
delay(1);
333+
}
334+
}
335+
336+
if(size && (int)size != bytesWritten) {
337+
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload bytesWritten %d and size %d mismatch!.\n", bytesWritten, _size);
338+
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] ERROR SEND PAYLOAD FAILED!");
339+
return HTTPC_ERROR_SEND_PAYLOAD_FAILED;
340+
} else {
341+
DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload written: %d\n", bytesWritten);
342+
}
343+
344+
// handle Server Response (Header)
345+
return handleHeaderResponse();
346+
}
347+
268348
/**
269349
* size of message body / payload
270350
* @return -1 if no info or > 0 when Content-Length is set by server
@@ -374,7 +454,6 @@ String httpClient::getString(void) {
374454
return sstring;
375455
}
376456

377-
378457
/**
379458
* adds Header to the request
380459
* @param name
@@ -383,16 +462,20 @@ String httpClient::getString(void) {
383462
*/
384463
void httpClient::addHeader(const String& name, const String& value, bool first) {
385464

386-
String headerLine = name;
387-
headerLine += ": ";
388-
headerLine += value;
389-
headerLine += "\r\n";
465+
// not allow set of Header handled by code
466+
if(!name.equalsIgnoreCase("Connection") && !name.equalsIgnoreCase("User-Agent") && !name.equalsIgnoreCase("Host")) {
467+
String headerLine = name;
468+
headerLine += ": ";
469+
headerLine += value;
470+
headerLine += "\r\n";
390471

391-
if(first) {
392-
_Headers = headerLine + _Headers;
393-
} else {
394-
_Headers += headerLine;
472+
if(first) {
473+
_Headers = headerLine + _Headers;
474+
} else {
475+
_Headers += headerLine;
476+
}
395477
}
478+
396479
}
397480

398481
void httpClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) {
@@ -448,7 +531,6 @@ bool httpClient::connect(void) {
448531
return true;
449532
}
450533

451-
452534
if(_https) {
453535
DEBUG_HTTPCLIENT("[HTTP-Client] connect https...\n");
454536
if(_tcps) {
@@ -502,9 +584,10 @@ bool httpClient::sendHeader(const char * type) {
502584
if(!connected()) {
503585
return false;
504586
}
587+
505588
String header = String(type) + " " + _url + " HTTP/1.1\r\n"
506589
"Host: " + _host + "\r\n"
507-
"User-Agent: ESP8266httpClient\r\n"
590+
"User-Agent: " + _userAgent + "\r\n"
508591
"Connection: ";
509592

510593
if(_reuse) {

‎libraries/ESP8266httpClient/src/ESP8266httpClient.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@ class httpClient {
5959
bool connected(void);
6060

6161
void setReuse(bool reuse); /// keep-alive
62+
void setUserAgent(const char * userAgent);
6263

6364
/// request handling
6465
int GET();
6566
int POST(uint8_t * payload, size_t size);
6667
int POST(String payload);
6768
int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0);
69+
int sendRequest(const char * type, Stream * stream, size_t size = 0);
6870

6971
void addHeader(const String& name, const String& value, bool first = false);
7072

@@ -105,7 +107,8 @@ class httpClient {
105107
bool _https;
106108
String _httpsFingerprint;
107109

108-
String _Headers;
110+
String _Headers;
111+
String _userAgent;
109112

110113
/// Response handling
111114
RequestArgument* _currentHeaders;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* httpUpdate.ino
3+
*
4+
* Created on: 27.11.2015
5+
*
6+
*/
7+
8+
#include <Arduino.h>
9+
10+
#include <ESP8266WiFi.h>
11+
#include <ESP8266WiFiMulti.h>
12+
13+
#include <ESP8266httpClient.h>
14+
#include <ESP8266httpUpdate.h>
15+
16+
#define USE_SERIAL Serial
17+
18+
ESP8266WiFiMulti WiFiMulti;
19+
20+
void setup() {
21+
22+
USE_SERIAL.begin(115200);
23+
// USE_SERIAL.setDebugOutput(true);
24+
25+
USE_SERIAL.println();
26+
USE_SERIAL.println();
27+
USE_SERIAL.println();
28+
29+
for(uint8_t t = 4; t > 0; t--) {
30+
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
31+
USE_SERIAL.flush();
32+
delay(1000);
33+
}
34+
35+
WiFiMulti.addAP("SSID", "PASSWORD");
36+
37+
}
38+
39+
void loop() {
40+
// wait for WiFi connection
41+
if((WiFiMulti.run() == WL_CONNECTED)) {
42+
43+
t_httpUpdate_return ret = ESPhttpUpdate.update("http://server/file.bin");
44+
//t_httpUpdate_return ret = ESPhttpUpdate.update("https://server/file.bin");
45+
46+
switch(ret) {
47+
case HTTP_UPDATE_FAILED:
48+
USE_SERIAL.println("HTTP_UPDATE_FAILD");
49+
break;
50+
51+
case HTTP_UPDATE_NO_UPDATES:
52+
USE_SERIAL.println("HTTP_UPDATE_NO_UPDATES");
53+
break;
54+
55+
case HTTP_UPDATE_OK:
56+
USE_SERIAL.println("HTTP_UPDATE_OK");
57+
break;
58+
}
59+
}
60+
}
61+

‎libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp

Lines changed: 101 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -23,103 +23,96 @@
2323
*
2424
*/
2525

26+
2627
#include "ESP8266httpUpdate.h"
2728

28-
ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) {
2929

30+
ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) {
3031
}
3132

3233
ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void) {
34+
}
3335

36+
/**
37+
*
38+
* @param url const char *
39+
* @param current_version const char *
40+
* @param httpsFingerprint const char *
41+
* @return t_httpUpdate_return
42+
*/
43+
t_httpUpdate_return ESP8266HTTPUpdate::update(const char * url, const char * current_version, const char * httpsFingerprint) {
44+
httpClient http;
45+
http.begin(url, httpsFingerprint);
46+
return handleUpdate(&http, current_version);
3447
}
3548

36-
t_httpUpdate_return ESP8266HTTPUpdate::update(const char * host, uint16_t port, const char * url, const char * current_version) {
37-
WiFiClient tcp;
38-
DEBUG_HTTP_UPDATE("[httpUpdate] connected to %s:%u %s .... ", host, port, url);
49+
/**
50+
*
51+
* @param host const char *
52+
* @param port uint16_t
53+
* @param url const char *
54+
* @param current_version const char *
55+
* @param httpsFingerprint const char *
56+
* @return
57+
*/
58+
t_httpUpdate_return ESP8266HTTPUpdate::update(const char * host, uint16_t port, const char * url, const char * current_version, bool https, const char * httpsFingerprint) {
59+
httpClient http;
60+
http.begin(host, port, url, https, httpsFingerprint);
61+
return handleUpdate(&http, current_version);
62+
}
3963

40-
if(!tcp.connect(host, port)) {
41-
DEBUG_HTTP_UPDATE("failed.\n");
42-
return HTTP_UPDATE_FAILED;
43-
}
44-
DEBUG_HTTP_UPDATE("ok.\n");
45-
return update(tcp, host, url, current_version);
64+
t_httpUpdate_return ESP8266HTTPUpdate::update(String host, uint16_t port, String url, String current_version, bool https, String httpsFingerprint) {
65+
httpClient http;
66+
http.begin(host, port, url, https, httpsFingerprint);
67+
return handleUpdate(&http, current_version.c_str());
4668
}
4769

48-
t_httpUpdate_return ESP8266HTTPUpdate::update(WiFiClient& tcp, const char* host, const char* url, const char * current_version) {
70+
/**
71+
*
72+
* @param http httpClient *
73+
* @param current_version const char *
74+
* @return t_httpUpdate_return
75+
*/
76+
t_httpUpdate_return ESP8266HTTPUpdate::handleUpdate(httpClient * http, const char * current_version) {
77+
4978
t_httpUpdate_return ret = HTTP_UPDATE_FAILED;
50-
// set Timeout for readBytesUntil and readStringUntil
51-
tcp.setTimeout(2000);
52-
tcp.setNoDelay(true);
53-
54-
String req = "GET ";
55-
56-
req += url;
57-
req += " HTTP/1.1\r\n"
58-
"Host: ";
59-
req += host;
60-
req += "\r\n"
61-
"Connection: close\r\n"
62-
"User-Agent: ESP8266-http-Update\r\n"
63-
"x-ESP8266-STA-MAC: ";
64-
req += WiFi.macAddress();
65-
req += "\r\n"
66-
"x-ESP8266-AP-MAC: ";
67-
req += WiFi.softAPmacAddress();
68-
req += "\r\n"
69-
"x-ESP8266-free-space: ";
70-
req += ESP.getFreeSketchSpace();
71-
req += "\r\n"
72-
"x-ESP8266-sketch-size: ";
73-
req += ESP.getSketchSize();
74-
req += "\r\n"
75-
"x-ESP8266-chip-size: ";
76-
req += ESP.getFlashChipRealSize();
77-
req += "\r\n"
78-
"x-ESP8266-sdk-version: ";
79-
req += ESP.getSdkVersion();
80-
81-
if(current_version[0] != 0x00) {
82-
req += "\r\n"
83-
"x-ESP8266-version: ";
84-
req += current_version;
85-
}
8679

87-
req += "\r\n"
88-
"\r\n";
80+
http->setUserAgent("ESP8266-http-Update");
81+
http->addHeader("x-ESP8266-STA-MAC", WiFi.macAddress());
82+
http->addHeader("x-ESP8266-AP-MAC", WiFi.softAPmacAddress());
83+
http->addHeader("x-ESP8266-free-space", String(ESP.getFreeSketchSpace()));
84+
http->addHeader("x-ESP8266-sketch-size", String(ESP.getSketchSize()));
85+
http->addHeader("x-ESP8266-chip-size", String(ESP.getFlashChipRealSize()));
86+
http->addHeader("x-ESP8266-sdk-version", ESP.getSdkVersion());
8987

90-
tcp.write(req.c_str(), req.length());
88+
if(current_version && current_version[0] != 0x00) {
89+
http->addHeader("x-ESP8266-version", current_version);
90+
}
9191

92-
uint32_t code = 0;
93-
size_t len = 0;
92+
const char * headerkeys[] = { "x-MD5" };
93+
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
9494

95-
while(true) {
96-
String headerLine = tcp.readStringUntil('\n');
97-
headerLine.trim(); // remove \r
95+
// track these headers
96+
http->collectHeaders(headerkeys, headerkeyssize);
9897

99-
if(headerLine.length() > 0) {
100-
DEBUG_HTTP_UPDATE("[httpUpdate][Header] RX: %s\n", headerLine.c_str());
101-
if(headerLine.startsWith("HTTP/1.")) {
102-
// 9 = lenght of "HTTP/1.x "
103-
code = headerLine.substring(9, headerLine.indexOf(' ', 9)).toInt();
104-
} else if(headerLine.startsWith("Content-Length: ")) {
105-
// 16 = lenght of "Content-Length: "
106-
len = headerLine.substring(16).toInt();
107-
}
108-
} else {
109-
break;
110-
}
111-
}
98+
99+
int code = http->GET();
100+
int len = http->getSize();
112101

113102
DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n");
114103
DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n");
115104
DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code);
116105
DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len);
117106

107+
if(http->hasHeader("x-MD5")) {
108+
DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", http->header("x-MD5").c_str());
109+
}
110+
118111
DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n");
119112
DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace());
120113
DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize());
121114

122-
if(current_version[0] != 0x00) {
115+
if(current_version && current_version[0] != 0x00) {
123116
DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", current_version);
124117
}
125118

@@ -131,15 +124,17 @@ t_httpUpdate_return ESP8266HTTPUpdate::update(WiFiClient& tcp, const char* host,
131124
DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len);
132125
} else {
133126

127+
WiFiClient * tcp = http->getStreamPtr();
128+
134129
WiFiUDP::stopAll();
135-
WiFiClient::stopAllExcept(&tcp);
130+
WiFiClient::stopAllExcept(tcp);
136131

137132
delay(100);
138133

139-
if(ESP.updateSketch(tcp, len, false, false)) {
134+
if(runUpdate(*tcp, len, http->header("x-MD5"))) {
140135
ret = HTTP_UPDATE_OK;
141136
DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n");
142-
tcp.stop();
137+
http->end();
143138
ESP.restart();
144139
} else {
145140
ret = HTTP_UPDATE_FAILED;
@@ -148,7 +143,7 @@ t_httpUpdate_return ESP8266HTTPUpdate::update(WiFiClient& tcp, const char* host,
148143
}
149144
} else {
150145
ret = HTTP_UPDATE_FAILED;
151-
DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0?!\n");
146+
DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0 or not set by Server?!\n");
152147
}
153148
break;
154149
case 304:
@@ -164,11 +159,42 @@ t_httpUpdate_return ESP8266HTTPUpdate::update(WiFiClient& tcp, const char* host,
164159
break;
165160
}
166161

162+
http->end();
163+
167164
return ret;
168165
}
169166

170-
t_httpUpdate_return ESP8266HTTPUpdate::update(String host, uint16_t port, String url, String current_version) {
171-
return update(host.c_str(), port, url.c_str(), current_version.c_str());
167+
/**
168+
* write Update to flash
169+
* @param in Stream&
170+
* @param size uint32_t
171+
* @param md5 String
172+
* @return true if Update ok
173+
*/
174+
bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5) {
175+
176+
if(!Update.begin(size)) {
177+
DEBUG_HTTP_UPDATE("[httpUpdate] Update.begin failed!\n");
178+
return false;
179+
}
180+
181+
if(md5.length()) {
182+
Update.setMD5(md5.c_str());
183+
}
184+
185+
if(Update.writeStream(in) != size) {
186+
DEBUG_HTTP_UPDATE("[httpUpdate] Update.writeStream failed!\n");
187+
return false;
188+
}
189+
190+
if(!Update.end()) {
191+
DEBUG_HTTP_UPDATE("[httpUpdate] Update.end failed!\n");
192+
return false;
193+
}
194+
195+
return true;
172196
}
173197

198+
199+
174200
ESP8266HTTPUpdate ESPhttpUpdate;

‎libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828

2929
#include <Arduino.h>
3030
#include <ESP8266WiFi.h>
31-
#include <WiFiUdp.h>
3231
#include <WiFiClient.h>
32+
#include <WiFiUdp.h>
33+
#include <ESP8266httpClient.h>
3334

3435
//#define DEBUG_HTTP_UPDATE(...) Serial1.printf( __VA_ARGS__ )
3536

@@ -48,9 +49,13 @@ class ESP8266HTTPUpdate {
4849
ESP8266HTTPUpdate(void);
4950
~ESP8266HTTPUpdate(void);
5051

51-
t_httpUpdate_return update(const char * host, uint16_t port, const char * url = "/", const char * current_version = "");
52-
t_httpUpdate_return update(String host, uint16_t port, String url = "/", String current_version = "");
53-
t_httpUpdate_return update(WiFiClient& client, const char* host, const char* url = "/", const char * current_version = "");
52+
t_httpUpdate_return update(const char * url, const char * current_version = "", const char * httpsFingerprint = "");
53+
t_httpUpdate_return update(const char * host, uint16_t port, const char * url = "/", const char * current_version = "", bool https = false, const char * httpsFingerprint = "");
54+
t_httpUpdate_return update(String host, uint16_t port, String url = "/", String current_version = "", bool https = false, String httpsFingerprint = "");
55+
56+
protected:
57+
t_httpUpdate_return handleUpdate(httpClient * http, const char * current_version);
58+
bool runUpdate(Stream& in, uint32_t size, String md5);
5459
};
5560

5661
extern ESP8266HTTPUpdate ESPhttpUpdate;

0 commit comments

Comments
 (0)
Please sign in to comment.