diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..df30947 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +.idea/ diff --git a/README.md b/README.md index 364f475..848c734 100644 --- a/README.md +++ b/README.md @@ -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")); ``` +### 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 Some responses are implemented, but you should not use them, because they do not conform to HTTP. diff --git a/src/ESPAsyncWebServer.h b/src/ESPAsyncWebServer.h index 94d8f89..0f9b646 100644 --- a/src/ESPAsyncWebServer.h +++ b/src/ESPAsyncWebServer.h @@ -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, 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 onFileUpload(ArUploadHandlerFunction fn); //handle file uploads diff --git a/src/WebHandlerImpl.h b/src/WebHandlerImpl.h index dc0992e..5a7dc36 100644 --- a/src/WebHandlerImpl.h +++ b/src/WebHandlerImpl.h @@ -24,6 +24,18 @@ #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 { private: String _getPath(AsyncWebServerRequest *request); @@ -32,10 +44,11 @@ class AsyncStaticWebHandler: public AsyncWebHandler { String _uri; String _path; String _cache_header; + String _modified_header; bool _isFile; public: - AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header) - : _fs(fs), _uri(uri), _path(path), _cache_header(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), _modified_header(modified_header) { _isFile = _fs.exists(path) || _fs.exists((String(path)+".gz").c_str()); if (_uri != "/" && _uri.endsWith("/")) { diff --git a/src/WebHandlers.cpp b/src/WebHandlers.cpp index e55269f..af5d7ff 100644 --- a/src/WebHandlers.cpp +++ b/src/WebHandlers.cpp @@ -21,6 +21,25 @@ #include "ESPAsyncWebServer.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) { if (request->method() != HTTP_GET) { @@ -33,7 +52,10 @@ bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) if (request->url().startsWith(_uri)) { String path = _getPath(request); 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; } } @@ -82,14 +104,19 @@ void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request) String path = _getPath(request); if (_fs.exists(path) || _fs.exists(path + ".gz")) { - AsyncWebServerResponse * response = request->beginResponse(_fs, path); - if (_cache_header.length() != 0) - response->addHeader("Cache-Control", _cache_header); - request->send(response); + if (_modified_header.length() != 0 && _modified_header == request->header("If-Modified-Since")) { + request->send(304); // Sed not modified + } else { + 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 { request->send(404); } path = String(); - } diff --git a/src/WebServer.cpp b/src/WebServer.cpp index 7f26acc..c5cf8bf 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -104,8 +104,12 @@ void AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){ addHandler(handler); } -void AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header){ - addHandler(new AsyncStaticWebHandler(fs, path, uri, cache_header)); +void AsyncWebServer::redirect(const char* url, const char* location, const uint32_t exclude_ip){ + 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){