Skip to content
This repository was archived by the owner on Feb 4, 2023. It is now read-only.

Commit c8bf8fe

Browse files
authored
Merge pull request #22 from gagulik/serveStatic_dev2
streamFile and serveStatic for ESP8266/ESP32 boards
2 parents aedfd65 + 7d4d3f4 commit c8bf8fe

10 files changed

+433
-8
lines changed

examples/serveStatic/defines.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242

4343
// For ESP8266
4444
#include <FS.h>
45-
#include <LittleFS.h>
46-
45+
#include <LittleFS.h
46+
4747
#warning Use ESP8266 architecture
4848
#include <ESP8266mDNS.h>
4949
#define ETHERNET_USE_ESP8266

examples/serveStatic/serveStatic.ino

+3-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
https://www.qrz.com/db/ac8l
77
88
Prior to the usage - use ESP8266/ESP32 Sketch data upoad tool to save the content of web-site to the SPIFFS flash memory.
9-
109
This example sketch is based on HTML templates from Robert Ulbricht: https://www.arduinoslovakia.eu
1110
https://github.com./RoboUlbricht/arduinoslovakia/tree/master/esp8266/simple_http_server_multiplepages_responsive_spiffs
1211
@@ -20,7 +19,7 @@
2019
Based on and modified from ESP8266 https://github.com./esp8266/Arduino/releases
2120
Built by Khoi Hoang https://github.com./khoih-prog/EthernetWebServer
2221
Licensed under MIT license
23-
*****************************************************************************************************************************/
22+
*****************************************************************************************************************************
2423
/*****************************************************************************************************************************
2524
The Arduino board communicates with the shield using the SPI bus. This is on digital pins 11, 12, and 13 on the Uno
2625
and pins 50, 51, and 52 on the Mega. On both boards, pin 10 is used as SS. On the Mega, the hardware SS pin, 53,
@@ -52,7 +51,7 @@ void setup(void)
5251
EthernetInit();
5352

5453
#else
55-
54+
5655
#if USE_NATIVE_ETHERNET
5756
ET_LOGWARN(F("======== USE_NATIVE_ETHERNET ========"));
5857
#elif USE_ETHERNET
@@ -77,7 +76,7 @@ void setup(void)
7776
ET_LOGWARN1(F("SCK:"), SCK);
7877
ET_LOGWARN1(F("SS:"), SS);
7978
ET_LOGWARN(F("========================="));
80-
79+
8180
#if defined(ESP8266)
8281
// For ESP8266, change for other boards if necessary
8382
#ifndef USE_THIS_SS_PIN

src/EthernetWebServer-impl.h

+18
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,24 @@ void EthernetWebServer::sendContent_P(PGM_P content, size_t size)
664664
}
665665
}
666666
//////
667+
#if (ESP32 || ESP8266)
668+
#include "FS.h"
669+
void EthernetWebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
670+
_addRequestHandler(new StaticFileRequestHandler(fs, path, uri, cache_header));
671+
}
672+
673+
void EthernetWebServer::_streamFileCore(const size_t fileSize, const String &fileName, const String &contentType)
674+
{
675+
using namespace mime;
676+
setContentLength(fileSize);
677+
if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) &&
678+
contentType != String(FPSTR(mimeTable[gz].mimeType)) &&
679+
contentType != String(FPSTR(mimeTable[none].mimeType))) {
680+
sendHeader(F("Content-Encoding"), F("gzip"));
681+
}
682+
send(200, contentType, emptyString);
683+
}
684+
#endif
667685

668686
String EthernetWebServer::arg(String name)
669687
{

src/EthernetWebServer.h

+46-1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ typedef struct
186186
} HTTPUpload;
187187

188188
#include "detail/RequestHandler.h"
189+
#if (ESP32 || ESP8266)
190+
#include "FS.h"
191+
#endif
189192

190193
class EthernetWebServer
191194
{
@@ -279,6 +282,7 @@ class EthernetWebServer
279282

280283
static String urlDecode(const String& text);
281284

285+
#if !(ESP32 || ESP8266)
282286
template<typename T> size_t streamFile(T &file, const String& contentType)
283287
{
284288
using namespace mime;
@@ -293,6 +297,27 @@ class EthernetWebServer
293297

294298
return _currentClient.write(file);
295299
}
300+
#else
301+
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); // serve static pages from file system
302+
303+
// Handle a GET request by sending a response header and stream file content to response body
304+
template<typename T>
305+
size_t streamFile(T &file, const String& contentType) {
306+
return streamFile(file, contentType, HTTP_GET);
307+
}
308+
309+
// Implement GET and HEAD requests for files.
310+
// Stream body on HTTP_GET but not on HTTP_HEAD requests.
311+
template<typename T>
312+
size_t streamFile(T &file, const String& contentType, HTTPMethod requestMethod) {
313+
size_t contentLength = 0;
314+
_streamFileCore(file.size(), file.name(), contentType);
315+
if (requestMethod == HTTP_GET) {
316+
contentLength = _customClientWrite(file);
317+
}
318+
return contentLength;
319+
}
320+
#endif
296321

297322
protected:
298323
void _addRequestHandler(RequestHandler* handler);
@@ -316,7 +341,27 @@ class EthernetWebServer
316341
uint8_t _uploadReadByte(EthernetClient& client);
317342
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
318343
bool _collectHeader(const char* headerName, const char* headerValue);
319-
344+
345+
#if (ESP32 || ESP8266)
346+
void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType);
347+
348+
template<typename T>
349+
size_t _customClientWrite(T &file) {
350+
char buffer[256];
351+
size_t contentLength = 0;
352+
size_t bytesRead = 0;
353+
354+
// read up to sizeof(buffer) bytes
355+
while ((bytesRead = file.readBytes(buffer, sizeof(buffer))) > 0)
356+
{
357+
_currentClient.write(buffer, bytesRead);
358+
contentLength += bytesRead;
359+
}
360+
361+
return contentLength;
362+
}
363+
#endif
364+
320365
struct RequestArgument {
321366

322367
String key;

src/detail/ESP_RequestHandlersImpl.h

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/****************************************************************************************************************************
2+
ESP_RequestHandlersImpl.h - Dead simple web-server.
3+
For Ethernet shields on ESP8266 and ESP32
4+
For EthernetWebServer library by Khoi Hoang
5+
6+
EthernetWebServer is a library for the Ethernet shields to run WebServer
7+
8+
Based on and modified from ESP8266 https://github.com./esp8266/Arduino/releases
9+
Built by Khoi Hoang https://github.com./khoih-prog/EthernetWebServer
10+
Licensed under MIT license
11+
Version: 1.0.0
12+
13+
Original author:
14+
@file RequestHandlersImpl.h
15+
@author Khoi Hoang
16+
17+
Version Modified By Date Comments
18+
------- ----------- ---------- -----------
19+
1.0.0 S Azari 25/01/2021 Initial coding, inheriting EthernetWebServer class and porting ESP8266 code
20+
*****************************************************************************************************************************/
21+
22+
#pragma once
23+
24+
#include "RequestHandler.h"
25+
#include "esp_detail/mimetable.h"
26+
#include "FS.h"
27+
#include "WString.h"
28+
#include <MD5Builder.h>
29+
#include <base64.h>
30+
31+
class FunctionRequestHandler : public RequestHandler
32+
{
33+
public:
34+
35+
FunctionRequestHandler(EthernetWebServer::THandlerFunction fn, EthernetWebServer::THandlerFunction ufn, const String &uri, HTTPMethod method)
36+
: _fn(fn)
37+
, _ufn(ufn)
38+
, _uri(uri)
39+
, _method(method)
40+
{
41+
}
42+
43+
bool canHandle(HTTPMethod requestMethod, String requestUri) override
44+
{
45+
if (_method != HTTP_ANY && _method != requestMethod)
46+
return false;
47+
48+
if (requestUri == _uri)
49+
return true;
50+
51+
if (_uri.endsWith("/*"))
52+
{
53+
String _uristart = _uri;
54+
_uristart.replace("/*", "");
55+
56+
if (requestUri.startsWith(_uristart))
57+
return true;
58+
}
59+
60+
return false;
61+
}
62+
63+
bool canUpload(String requestUri) override
64+
{
65+
if (!_ufn || !canHandle(HTTP_POST, requestUri))
66+
return false;
67+
68+
return true;
69+
}
70+
71+
bool handle(EthernetWebServer& server, HTTPMethod requestMethod, String requestUri) override
72+
{
73+
ETW_UNUSED(server);
74+
75+
if (!canHandle(requestMethod, requestUri))
76+
return false;
77+
78+
_fn();
79+
return true;
80+
}
81+
82+
void upload(EthernetWebServer& server, String requestUri, HTTPUpload& upload) override
83+
{
84+
ETW_UNUSED(server);
85+
ETW_UNUSED(upload);
86+
87+
if (canUpload(requestUri))
88+
_ufn();
89+
}
90+
91+
protected:
92+
EthernetWebServer::THandlerFunction _fn;
93+
EthernetWebServer::THandlerFunction _ufn;
94+
String _uri;
95+
HTTPMethod _method;
96+
};
97+
98+
class StaticRequestHandler : public RequestHandler {
99+
using WebServerType = EthernetWebServer;
100+
public:
101+
StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
102+
: _fs(fs)
103+
, _uri(uri)
104+
, _path(path)
105+
, _cache_header(cache_header)
106+
{
107+
//DEBUGV("StaticRequestHandler: path=%s uri=%s, cache_header=%s\r\n", path, uri, cache_header == __null ? "" : cache_header);
108+
_isFile = fs.exists(path);
109+
_baseUriLength = _uri.length();
110+
}
111+
112+
bool validMethod(HTTPMethod requestMethod){
113+
return (requestMethod == HTTP_GET) || (requestMethod == HTTP_HEAD);
114+
}
115+
116+
/* Deprecated version. Please use mime::getContentType instead */
117+
static String getContentType(const String& path) __attribute__((deprecated)) {
118+
return mime_esp::getContentType(path);
119+
}
120+
121+
protected:
122+
FS _fs;
123+
bool _isFile;
124+
String _uri;
125+
String _path;
126+
String _cache_header;
127+
size_t _baseUriLength;
128+
};
129+
130+
131+
class StaticFileRequestHandler
132+
:
133+
public StaticRequestHandler {
134+
135+
using SRH = StaticRequestHandler;
136+
using WebServerType = EthernetWebServer;
137+
138+
public:
139+
StaticFileRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
140+
:
141+
StaticRequestHandler{fs, path, uri, cache_header}
142+
{
143+
File f = SRH::_fs.open(path, "r");
144+
MD5Builder calcMD5;
145+
calcMD5.begin();
146+
calcMD5.addStream(f, f.size());
147+
calcMD5.calculate();
148+
calcMD5.getBytes(_ETag_md5);
149+
f.close();
150+
}
151+
152+
bool canHandle(HTTPMethod requestMethod, const String requestUri) override {
153+
return SRH::validMethod(requestMethod) && requestUri == SRH::_uri;
154+
}
155+
156+
bool handle(EthernetWebServer& server, HTTPMethod requestMethod, const String requestUri) {
157+
158+
if (!canHandle(requestMethod, requestUri))
159+
return false;
160+
161+
162+
const String etag = "\"" + base64::encode(_ETag_md5, 16) + "\"";
163+
164+
if(server.header("If-None-Match") == etag){
165+
server.send(304);
166+
return true;
167+
}
168+
169+
File f = SRH::_fs.open(SRH::_path, "r");
170+
171+
if (!f)
172+
return false;
173+
174+
if (!_isFile) {
175+
f.close();
176+
return false;
177+
}
178+
179+
if (SRH::_cache_header.length() != 0)
180+
server.sendHeader("Cache-Control", SRH::_cache_header);
181+
182+
server.sendHeader("ETag", etag);
183+
184+
server.streamFile(f, mime_esp::getContentType(SRH::_path), requestMethod);
185+
return true;
186+
}
187+
188+
protected:
189+
uint8_t _ETag_md5[16];
190+
};
191+

src/detail/RequestHandlersImpl.h

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
#pragma once
4040

41+
#if !(ESP32 || ESP8266)
4142
#include "RequestHandler.h"
4243
#include "mimetable.h"
4344

@@ -187,3 +188,6 @@ class StaticRequestHandler : public RequestHandler
187188
size_t _baseUriLength;
188189
};
189190

191+
#else
192+
#include "ESP_RequestHandlersImpl.h"
193+
#endif

0 commit comments

Comments
 (0)