HTTP 302 and 304 Support

Add support for http redirection (302) and http not modified (304) to
reduce the load the server.
server.redirect(“url”, “location”, exclude-ip) will respond with 302 to
redirect the browser to a different url, this is useful for backward
compatibility and to redirect call to CDN when not no AP mode.
server.serveStatic has a new optional parameter to get the
Last-Modified date for all files serve for this location, when the
browser request have the same If-Modified-Since header value, the
server respond with 304 code instead of serving the file.
This commit is contained in:
Hagai Shatz
2016-06-12 11:52:38 +01:00
parent ba45a834e9
commit a01972c9e5
6 changed files with 91 additions and 11 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.idea/

View File

@@ -416,6 +416,38 @@ server.addHandler( new FileFallbackHandler(_fs, "/jquery/jq1.11.1.js" , "/jquery
server.addHandler( new FileFallbackHandler(_fs, "/jquery/jqm1.4.5.js" , "/jquery/jqm1.4.5.js" , "http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js", "max-age=86400")); server.addHandler( new FileFallbackHandler(_fs, "/jquery/jqm1.4.5.js" , "/jquery/jqm1.4.5.js" , "http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js", "max-age=86400"));
``` ```
### Serving static files from SPIFFS
Serve a specific file or attach a folder in SPIFFS to a root url.
An optional Cache-Control header can be added to responses.
An optional Last-Modified header can be added, if the request contains If-Modified-Since header with the same value the server will respond with 304 code instead of serving the file.
```cpp
// Serve static file example
server.serveStatic("/url/file.htm", SPIFFS, "/dir/file.htm");
// Serve static folder example
server.serveStatic("/root-url", SPIFFS, "/root-dir");
// Specify cache control
server.serveStatic("/root-url", SPIFFS, "/root-dir", "max-age=86400");
// Specify last modified (should be updated each time files are updated)
server.serveStatic("/root-url", SPIFFS, "/root-dir", NULL, "Wed, 08 Jun 2016 22:00:00 GMT");
```
### HTTP Redirect (302)
Return HTTP 302 response to redirect the client to a different location.
An optional IPAddress can specify redirection exclusions, for example, exclude redirection to requests to the AP address.
```cpp
// Redirect all requests
server.redirect("/root/file.ext", "/other-location/other-file.ext");
// Redirect requests not for the AP address
server.redirect("/root/file.ext", "/other-location/other-file.ext", WiFi.softAPIP());
// Redirect to jquery CDN except when call is done to AP address
server.redirect("/jquery/jq1.12.3.js", "http://code.jquery.com/jquery-1.12.3.min.js", WiFi.softAPIP());
// Serve the jquery from SPIFFS
server.serveStatic("/jquery", SPIFFS, "/jquery");
```
## Bad Responses ## Bad Responses
Some responses are implemented, but you should not use them, because they do not conform to HTTP. Some responses are implemented, but you should not use them, because they do not conform to HTTP.

View File

@@ -297,7 +297,9 @@ class AsyncWebServer {
void on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload); void on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
void on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody); void on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody);
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL); void redirect(const char* url, const char* location, const uint32_t exclude_ip = 0);
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL, const char* modified_header = NULL);
void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned
void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads

View File

@@ -24,6 +24,18 @@
#include "stddef.h" #include "stddef.h"
class RedirectWebHandler: public AsyncWebHandler {
protected:
String _url;
String _location;
uint32_t _exclude_ip;
public:
RedirectWebHandler(const char* url, const char* location, uint32_t exclude_ip)
: _url(url), _location(location) { _exclude_ip = exclude_ip; }
bool canHandle(AsyncWebServerRequest *request);
void handleRequest(AsyncWebServerRequest *request);
};
class AsyncStaticWebHandler: public AsyncWebHandler { class AsyncStaticWebHandler: public AsyncWebHandler {
private: private:
String _getPath(AsyncWebServerRequest *request); String _getPath(AsyncWebServerRequest *request);
@@ -32,10 +44,11 @@ class AsyncStaticWebHandler: public AsyncWebHandler {
String _uri; String _uri;
String _path; String _path;
String _cache_header; String _cache_header;
String _modified_header;
bool _isFile; bool _isFile;
public: public:
AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header) AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header, const char* modified_header)
: _fs(fs), _uri(uri), _path(path), _cache_header(cache_header){ : _fs(fs), _uri(uri), _path(path), _cache_header(cache_header), _modified_header(modified_header) {
_isFile = _fs.exists(path) || _fs.exists((String(path)+".gz").c_str()); _isFile = _fs.exists(path) || _fs.exists((String(path)+".gz").c_str());
if (_uri != "/" && _uri.endsWith("/")) { if (_uri != "/" && _uri.endsWith("/")) {

View File

@@ -21,6 +21,25 @@
#include "ESPAsyncWebServer.h" #include "ESPAsyncWebServer.h"
#include "WebHandlerImpl.h" #include "WebHandlerImpl.h"
bool RedirectWebHandler::canHandle(AsyncWebServerRequest *request)
{
// We can redirect when the request url match and ip doesn't match
if (request->url() == _url && _exclude_ip != request->client()->localIP()) {
DEBUGF("[RedirectWebHandler::canHandle] TRUE\n");
return true;
}
return false;
}
void RedirectWebHandler::handleRequest(AsyncWebServerRequest *request)
{
AsyncWebServerResponse *response = request->beginResponse(302);
response->addHeader("Location", _location);
request->send(response);
}
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request)
{ {
if (request->method() != HTTP_GET) { if (request->method() != HTTP_GET) {
@@ -33,7 +52,10 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request)
if (request->url().startsWith(_uri)) { if (request->url().startsWith(_uri)) {
String path = _getPath(request); String path = _getPath(request);
if (_fs.exists(path) || _fs.exists(path + ".gz")) { if (_fs.exists(path) || _fs.exists(path + ".gz")) {
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n"); if (_modified_header.length() != 0) {
request->addInterestingHeader("If-Modified-Since");
}
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
return true; return true;
} }
} }
@@ -82,14 +104,19 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
String path = _getPath(request); String path = _getPath(request);
if (_fs.exists(path) || _fs.exists(path + ".gz")) { if (_fs.exists(path) || _fs.exists(path + ".gz")) {
AsyncWebServerResponse * response = request->beginResponse(_fs, path); if (_modified_header.length() != 0 && _modified_header == request->header("If-Modified-Since")) {
if (_cache_header.length() != 0) request->send(304); // Sed not modified
response->addHeader("Cache-Control", _cache_header); } else {
request->send(response); AsyncWebServerResponse * response = request->beginResponse(_fs, path);
if (_modified_header.length() !=0)
response->addHeader("Last-Modified", _modified_header);
if (_cache_header.length() != 0)
response->addHeader("Cache-Control", _cache_header);
request->send(response);
}
} else { } else {
request->send(404); request->send(404);
} }
path = String(); path = String();
} }

View File

@@ -104,8 +104,12 @@ void AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){
addHandler(handler); addHandler(handler);
} }
void AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header){ void AsyncWebServer::redirect(const char* url, const char* location, const uint32_t exclude_ip){
addHandler(new AsyncStaticWebHandler(fs, path, uri, cache_header)); addHandler(new RedirectWebHandler(url, location, exclude_ip));
}
void AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header, const char* modified_header){
addHandler(new AsyncStaticWebHandler(fs, path, uri, cache_header, modified_header));
} }
void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){ void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){