1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

ESP8266WebServer: serve static files from FS

This commit is contained in:
Ivan Grokhotkov 2015-08-05 07:44:00 -04:00
parent b5d9db91aa
commit e8b27912d7
3 changed files with 129 additions and 44 deletions

View File

@ -1,9 +1,9 @@
/* /*
ESP8266WebServer.cpp - Dead simple web-server. ESP8266WebServer.cpp - Dead simple web-server.
Supports only one simultaneous client, knows how to handle GET and POST. Supports only one simultaneous client, knows how to handle GET and POST.
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either License as published by the Free Software Foundation; either
@ -25,25 +25,10 @@
#include "WiFiServer.h" #include "WiFiServer.h"
#include "WiFiClient.h" #include "WiFiClient.h"
#include "ESP8266WebServer.h" #include "ESP8266WebServer.h"
#include "detail/RequestHandler.h"
// #define DEBUG // #define DEBUG
#define DEBUG_OUTPUT Serial #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) ESP8266WebServer::ESP8266WebServer(int port)
: _server(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) void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn)
{ {
RequestHandler* handler = new RequestHandler(fn, uri, method); _addRequestHandler(new FunctionRequestHandler(fn, uri, method));
if (!_lastHandler) { }
_firstHandler = handler;
_lastHandler = handler; void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) {
} if (!_lastHandler) {
else { _firstHandler = handler;
_lastHandler->next = handler; _lastHandler = 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() void ESP8266WebServer::handleClient()
@ -269,16 +261,9 @@ void ESP8266WebServer::onNotFound(THandlerFunction fn) {
void ESP8266WebServer::_handleRequest() { void ESP8266WebServer::_handleRequest() {
RequestHandler* handler; RequestHandler* handler;
for (handler = _firstHandler; handler; handler = handler->next) for (handler = _firstHandler; handler; handler = handler->next) {
{ if (handler->handle(*this, _currentMethod, _currentUri))
if (handler->method != HTTP_ANY && handler->method != _currentMethod) break;
continue;
if (handler->uri != _currentUri)
continue;
handler->fn();
break;
} }
if (!handler){ if (!handler){

View File

@ -1,9 +1,9 @@
/* /*
ESP8266WebServer.h - Dead simple web-server. ESP8266WebServer.h - Dead simple web-server.
Supports only one simultaneous client, knows how to handle GET and POST. Supports only one simultaneous client, knows how to handle GET and POST.
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
This library is free software; you can redistribute it and/or This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either License as published by the Free Software Foundation; either
@ -25,6 +25,7 @@
#define ESP8266WEBSERVER_H #define ESP8266WEBSERVER_H
#include <functional> #include <functional>
#include <FS.h>
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE }; 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 }; 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_UNKNOWN ((size_t) -1)
#define CONTENT_LENGTH_NOT_SET ((size_t) -2) #define CONTENT_LENGTH_NOT_SET ((size_t) -2)
class RequestHandler;
typedef struct { typedef struct {
HTTPUploadStatus status; HTTPUploadStatus status;
String filename; String filename;
@ -59,6 +62,7 @@ public:
typedef std::function<void(void)> THandlerFunction; typedef std::function<void(void)> THandlerFunction;
void on(const char* uri, THandlerFunction handler); void on(const char* uri, THandlerFunction handler);
void on(const char* uri, HTTPMethod method, THandlerFunction fn); 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 onNotFound(THandlerFunction fn); //called when handler is not assigned
void onFileUpload(THandlerFunction fn); //handle file uploads void onFileUpload(THandlerFunction fn); //handle file uploads
@ -66,13 +70,13 @@ public:
HTTPMethod method() { return _currentMethod; } HTTPMethod method() { return _currentMethod; }
WiFiClient client() { return _currentClient; } WiFiClient client() { return _currentClient; }
HTTPUpload& upload() { return _currentUpload; } HTTPUpload& upload() { return _currentUpload; }
String arg(const char* name); // get request argument value by name String arg(const char* name); // get request argument value by name
String arg(int i); // get request argument value by number String arg(int i); // get request argument value by number
String argName(int i); // get request argument name by number String argName(int i); // get request argument name by number
int args(); // get arguments count int args(); // get arguments count
bool hasArg(const char* name); // check if argument exists bool hasArg(const char* name); // check if argument exists
// send response to the client // send response to the client
// code - HTTP response code, can be 200 or 404 // code - HTTP response code, can be 200 or 404
// content_type - HTTP content type, like "text/plain" or "image/png" // content_type - HTTP content type, like "text/plain" or "image/png"
@ -89,7 +93,7 @@ public:
template<typename T> size_t streamFile(T &file, const String& contentType){ template<typename T> size_t streamFile(T &file, const String& contentType){
setContentLength(file.size()); setContentLength(file.size());
if (String(file.name()).endsWith(".gz") && if (String(file.name()).endsWith(".gz") &&
contentType != "application/x-gzip" && contentType != "application/x-gzip" &&
contentType != "application/octet-stream"){ contentType != "application/octet-stream"){
sendHeader("Content-Encoding", "gzip"); sendHeader("Content-Encoding", "gzip");
@ -97,8 +101,9 @@ template<typename T> size_t streamFile(T &file, const String& contentType){
send(200, contentType, ""); send(200, contentType, "");
return _currentClient.write(file, HTTP_DOWNLOAD_UNIT_SIZE); return _currentClient.write(file, HTTP_DOWNLOAD_UNIT_SIZE);
} }
protected: protected:
void _addRequestHandler(RequestHandler* handler);
void _handleRequest(); void _handleRequest();
bool _parseRequest(WiFiClient& client); bool _parseRequest(WiFiClient& client);
void _parseArguments(String data); void _parseArguments(String data);
@ -108,7 +113,6 @@ protected:
uint8_t _uploadReadByte(WiFiClient& client); uint8_t _uploadReadByte(WiFiClient& client);
void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
struct RequestHandler;
struct RequestArgument { struct RequestArgument {
String key; String key;
String value; String value;

View File

@ -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