From e8b27912d724aa4bc5a78bcb535d08c7bed0a998 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 5 Aug 2015 07:44:00 -0400 Subject: [PATCH] ESP8266WebServer: serve static files from FS --- .../ESP8266WebServer/src/ESP8266WebServer.cpp | 59 +++++------- .../ESP8266WebServer/src/ESP8266WebServer.h | 18 ++-- .../src/detail/RequestHandler.h | 96 +++++++++++++++++++ 3 files changed, 129 insertions(+), 44 deletions(-) create mode 100644 libraries/ESP8266WebServer/src/detail/RequestHandler.h diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index cf0121388..bc0073774 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -1,9 +1,9 @@ -/* +/* ESP8266WebServer.cpp - Dead simple web-server. Supports only one simultaneous client, knows how to handle GET and POST. Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -25,25 +25,10 @@ #include "WiFiServer.h" #include "WiFiClient.h" #include "ESP8266WebServer.h" - +#include "detail/RequestHandler.h" // #define DEBUG #define DEBUG_OUTPUT Serial -struct ESP8266WebServer::RequestHandler { - RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) - : fn(fn) - , uri(uri) - , method(method) - , next(NULL) - { - } - - ESP8266WebServer::THandlerFunction fn; - String uri; - HTTPMethod method; - RequestHandler* next; - -}; ESP8266WebServer::ESP8266WebServer(int port) : _server(port) @@ -78,15 +63,22 @@ void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction ha void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) { - RequestHandler* handler = new RequestHandler(fn, uri, method); - if (!_lastHandler) { - _firstHandler = handler; - _lastHandler = handler; - } - else { - _lastHandler->next = handler; - _lastHandler = handler; - } + _addRequestHandler(new FunctionRequestHandler(fn, uri, method)); +} + +void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) { + if (!_lastHandler) { + _firstHandler = handler; + _lastHandler = handler; + } + else { + _lastHandler->next = handler; + _lastHandler = handler; + } +} + +void ESP8266WebServer::serveStatic(const char* uri, FS fs, const char* path) { + _addRequestHandler(new StaticRequestHandler(fs, uri)); } void ESP8266WebServer::handleClient() @@ -269,16 +261,9 @@ void ESP8266WebServer::onNotFound(THandlerFunction fn) { void ESP8266WebServer::_handleRequest() { RequestHandler* handler; - for (handler = _firstHandler; handler; handler = handler->next) - { - if (handler->method != HTTP_ANY && handler->method != _currentMethod) - continue; - - if (handler->uri != _currentUri) - continue; - - handler->fn(); - break; + for (handler = _firstHandler; handler; handler = handler->next) { + if (handler->handle(*this, _currentMethod, _currentUri)) + break; } if (!handler){ diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index bf538c3e0..49c610294 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -1,9 +1,9 @@ -/* +/* ESP8266WebServer.h - Dead simple web-server. Supports only one simultaneous client, knows how to handle GET and POST. Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -25,6 +25,7 @@ #define ESP8266WEBSERVER_H #include +#include enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE }; enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END }; @@ -37,6 +38,8 @@ enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END }; #define CONTENT_LENGTH_UNKNOWN ((size_t) -1) #define CONTENT_LENGTH_NOT_SET ((size_t) -2) +class RequestHandler; + typedef struct { HTTPUploadStatus status; String filename; @@ -59,6 +62,7 @@ public: typedef std::function THandlerFunction; void on(const char* uri, THandlerFunction handler); void on(const char* uri, HTTPMethod method, THandlerFunction fn); + void serveStatic(const char* uri, FS fs, const char* path); void onNotFound(THandlerFunction fn); //called when handler is not assigned void onFileUpload(THandlerFunction fn); //handle file uploads @@ -66,13 +70,13 @@ public: HTTPMethod method() { return _currentMethod; } WiFiClient client() { return _currentClient; } HTTPUpload& upload() { return _currentUpload; } - + String arg(const char* name); // get request argument value by name String arg(int i); // get request argument value by number String argName(int i); // get request argument name by number int args(); // get arguments count bool hasArg(const char* name); // check if argument exists - + // send response to the client // code - HTTP response code, can be 200 or 404 // content_type - HTTP content type, like "text/plain" or "image/png" @@ -89,7 +93,7 @@ public: template size_t streamFile(T &file, const String& contentType){ setContentLength(file.size()); - if (String(file.name()).endsWith(".gz") && + if (String(file.name()).endsWith(".gz") && contentType != "application/x-gzip" && contentType != "application/octet-stream"){ sendHeader("Content-Encoding", "gzip"); @@ -97,8 +101,9 @@ template size_t streamFile(T &file, const String& contentType){ send(200, contentType, ""); return _currentClient.write(file, HTTP_DOWNLOAD_UNIT_SIZE); } - + protected: + void _addRequestHandler(RequestHandler* handler); void _handleRequest(); bool _parseRequest(WiFiClient& client); void _parseArguments(String data); @@ -108,7 +113,6 @@ protected: uint8_t _uploadReadByte(WiFiClient& client); void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); - struct RequestHandler; struct RequestArgument { String key; String value; diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandler.h b/libraries/ESP8266WebServer/src/detail/RequestHandler.h new file mode 100644 index 000000000..702e24e0b --- /dev/null +++ b/libraries/ESP8266WebServer/src/detail/RequestHandler.h @@ -0,0 +1,96 @@ +#ifndef REQUESTHANDLER_H +#define REQUESTHANDLER_H + +class RequestHandler { +public: + RequestHandler(const char* uri, HTTPMethod method) + : uri(uri) + , method(method) + , next(NULL) + { + } + + virtual bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) = 0; + + RequestHandler* next; + +protected: + String uri; + HTTPMethod method; +}; + + +class FunctionRequestHandler : public RequestHandler { + typedef RequestHandler base; + +public: + FunctionRequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) + : fn(fn) + , base(uri, method) + { + } + + bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { + if (method != HTTP_ANY && method != requestMethod) + return false; + + if (requestUri != uri) + return false; + + fn(); + return true; + } + +protected: + ESP8266WebServer::THandlerFunction fn; +}; + +class StaticRequestHandler : public RequestHandler { + typedef RequestHandler base; + +public: + StaticRequestHandler(FS& fs, const char* uri) + : fs(fs) + , base(uri, HTTP_GET) + { + } + + bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { + if (requestMethod != method) + return false; + DEBUGV("StaticRequestHandler::handle: %s\r\n", requestUri.c_str()); + if (!requestUri.startsWith(uri)) + return false; + + auto prefixLength = uri.length() - 1; + String path = requestUri.substring(prefixLength); + DEBUGV("StaticRequestHandler::handle: %d %s\r\n", prefixLength, path.c_str()); + File f = fs.open(path, "r"); + if (!f) + return false; + + server.streamFile(f, getContentType(path)); + return true; + } + + static String getContentType(const String& path) { + if (path.endsWith(".html")) return "text/html"; + else if (path.endsWith(".htm")) return "text/html"; + else if (path.endsWith(".css")) return "text/css"; + else if (path.endsWith(".txt")) return "text/plain"; + else if (path.endsWith(".js")) return "application/javascript"; + else if (path.endsWith(".png")) return "image/png"; + else if (path.endsWith(".gif")) return "image/gif"; + else if (path.endsWith(".jpg")) return "image/jpeg"; + else if (path.endsWith(".ico")) return "image/x-icon"; + else if (path.endsWith(".xml")) return "text/xml"; + else if (path.endsWith(".pdf")) return "application/pdf"; + else if (path.endsWith(".zip")) return "application/zip"; + return "text/plain"; + } + +protected: + FS fs; +}; + +#endif //REQUESTHANDLER_H