mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-19 23:22:16 +03:00
webserver hook: allow to handle external http protocol (#7459)
* webhook api * simplify webserver debug printouts, move text to flash * Hook examples in HelloServer example * print executable code address in example * simplify example per @mcspr suggestion
This commit is contained in:
parent
63b41bcfab
commit
33083861c8
@ -92,7 +92,19 @@ inline uint32_t esp_get_cycle_count() {
|
|||||||
__asm__ __volatile__("rsr %0,ccount":"=a"(ccount));
|
__asm__ __volatile__("rsr %0,ccount":"=a"(ccount));
|
||||||
return ccount;
|
return ccount;
|
||||||
}
|
}
|
||||||
#endif // not CORE_MOCK
|
|
||||||
|
inline uint32_t esp_get_program_counter() __attribute__((always_inline));
|
||||||
|
inline uint32_t esp_get_program_counter() {
|
||||||
|
uint32_t pc;
|
||||||
|
__asm__ __volatile__("movi %0, ." : "=r" (pc) : : ); // ©earlephilhower
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // CORE_MOCK
|
||||||
|
|
||||||
|
inline uint32_t esp_get_program_counter() { return 0; }
|
||||||
|
|
||||||
|
#endif // CORE_MOCK
|
||||||
|
|
||||||
|
|
||||||
// Tools for preloading code into the flash cache
|
// Tools for preloading code into the flash cache
|
||||||
|
@ -17,7 +17,7 @@ const int led = 13;
|
|||||||
|
|
||||||
void handleRoot() {
|
void handleRoot() {
|
||||||
digitalWrite(led, 1);
|
digitalWrite(led, 1);
|
||||||
server.send(200, "text/plain", "hello from esp8266!");
|
server.send(200, "text/plain", "hello from esp8266!\r\n");
|
||||||
digitalWrite(led, 0);
|
digitalWrite(led, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +86,66 @@ void setup(void) {
|
|||||||
|
|
||||||
server.onNotFound(handleNotFound);
|
server.onNotFound(handleNotFound);
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////
|
||||||
|
// Hook examples
|
||||||
|
|
||||||
|
server.addHook([](const String & method, const String & url, WiFiClient * client, ESP8266WebServer::ContentTypeFunction contentType) {
|
||||||
|
(void)method; // GET, PUT, ...
|
||||||
|
(void)url; // example: /root/myfile.html
|
||||||
|
(void)client; // the webserver tcp client connection
|
||||||
|
(void)contentType; // contentType(".html") => "text/html"
|
||||||
|
Serial.printf("A useless web hook has passed\n");
|
||||||
|
Serial.printf("(this hook is in 0x%08x area (401x=IRAM 402x=FLASH))\n", esp_get_program_counter());
|
||||||
|
return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
|
||||||
|
});
|
||||||
|
|
||||||
|
server.addHook([](const String&, const String & url, WiFiClient*, ESP8266WebServer::ContentTypeFunction) {
|
||||||
|
if (url.startsWith("/fail")) {
|
||||||
|
Serial.printf("An always failing web hook has been triggered\n");
|
||||||
|
return ESP8266WebServer::CLIENT_MUST_STOP;
|
||||||
|
}
|
||||||
|
return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
|
||||||
|
});
|
||||||
|
|
||||||
|
server.addHook([](const String&, const String & url, WiFiClient * client, ESP8266WebServer::ContentTypeFunction) {
|
||||||
|
if (url.startsWith("/dump")) {
|
||||||
|
Serial.printf("The dumper web hook is on the run\n");
|
||||||
|
|
||||||
|
// Here the request is not interpreted, so we cannot for sure
|
||||||
|
// swallow the exact amount matching the full request+content,
|
||||||
|
// hence the tcp connection cannot be handled anymore by the
|
||||||
|
// webserver.
|
||||||
|
#ifdef STREAMTO_API
|
||||||
|
// we are lucky
|
||||||
|
client->toWithTimeout(Serial, 500);
|
||||||
|
#else
|
||||||
|
auto last = millis();
|
||||||
|
while ((millis() - last) < 500) {
|
||||||
|
char buf[32];
|
||||||
|
size_t len = client->read((uint8_t*)buf, sizeof(buf));
|
||||||
|
if (len > 0) {
|
||||||
|
Serial.printf("(<%d> chars)", (int)len);
|
||||||
|
Serial.write(buf, len);
|
||||||
|
last = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Two choices: return MUST STOP and webserver will close it
|
||||||
|
// (we already have the example with '/fail' hook)
|
||||||
|
// or IS GIVEN and webserver will forget it
|
||||||
|
// trying with IS GIVEN and storing it on a dumb WiFiClient.
|
||||||
|
// check the client connection: it should not immediately be closed
|
||||||
|
// (make another '/dump' one to close the first)
|
||||||
|
Serial.printf("\nTelling server to forget this connection\n");
|
||||||
|
static WiFiClient forgetme = *client; // stop previous one if present and transfer client refcounter
|
||||||
|
return ESP8266WebServer::CLIENT_IS_GIVEN;
|
||||||
|
}
|
||||||
|
return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hook examples
|
||||||
|
/////////////////////////////////////////////////////////
|
||||||
|
|
||||||
server.begin();
|
server.begin();
|
||||||
Serial.println("HTTP server started");
|
Serial.println("HTTP server started");
|
||||||
}
|
}
|
||||||
|
@ -28,20 +28,12 @@
|
|||||||
#include "FS.h"
|
#include "FS.h"
|
||||||
#include "detail/RequestHandlersImpl.h"
|
#include "detail/RequestHandlersImpl.h"
|
||||||
|
|
||||||
//#define DEBUG_ESP_HTTP_SERVER
|
|
||||||
#ifdef DEBUG_ESP_PORT
|
|
||||||
#define DEBUG_OUTPUT DEBUG_ESP_PORT
|
|
||||||
#else
|
|
||||||
#define DEBUG_OUTPUT Serial
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const char AUTHORIZATION_HEADER[] PROGMEM = "Authorization";
|
static const char AUTHORIZATION_HEADER[] PROGMEM = "Authorization";
|
||||||
static const char qop_auth[] PROGMEM = "qop=auth";
|
static const char qop_auth[] PROGMEM = "qop=auth";
|
||||||
static const char qop_auth_quoted[] PROGMEM = "qop=\"auth\"";
|
static const char qop_auth_quoted[] PROGMEM = "qop=\"auth\"";
|
||||||
static const char WWW_Authenticate[] PROGMEM = "WWW-Authenticate";
|
static const char WWW_Authenticate[] PROGMEM = "WWW-Authenticate";
|
||||||
static const char Content_Length[] PROGMEM = "Content-Length";
|
static const char Content_Length[] PROGMEM = "Content-Length";
|
||||||
|
|
||||||
|
|
||||||
template <typename ServerType>
|
template <typename ServerType>
|
||||||
ESP8266WebServerTemplate<ServerType>::ESP8266WebServerTemplate(IPAddress addr, int port)
|
ESP8266WebServerTemplate<ServerType>::ESP8266WebServerTemplate(IPAddress addr, int port)
|
||||||
: _server(addr, port)
|
: _server(addr, port)
|
||||||
@ -171,9 +163,7 @@ bool ESP8266WebServerTemplate<ServerType>::authenticateDigest(const String& user
|
|||||||
String authReq = header(FPSTR(AUTHORIZATION_HEADER));
|
String authReq = header(FPSTR(AUTHORIZATION_HEADER));
|
||||||
if(authReq.startsWith(F("Digest"))) {
|
if(authReq.startsWith(F("Digest"))) {
|
||||||
authReq = authReq.substring(7);
|
authReq = authReq.substring(7);
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("%s\n", authReq.c_str());
|
||||||
DEBUG_OUTPUT.println(authReq);
|
|
||||||
#endif
|
|
||||||
String _username = _extractParam(authReq,F("username=\""));
|
String _username = _extractParam(authReq,F("username=\""));
|
||||||
if(!_username.length() || _username != String(username)) {
|
if(!_username.length() || _username != String(username)) {
|
||||||
authReq = "";
|
authReq = "";
|
||||||
@ -200,9 +190,7 @@ bool ESP8266WebServerTemplate<ServerType>::authenticateDigest(const String& user
|
|||||||
_nc = _extractParam(authReq, F("nc="), ',');
|
_nc = _extractParam(authReq, F("nc="), ',');
|
||||||
_cnonce = _extractParam(authReq, F("cnonce=\""));
|
_cnonce = _extractParam(authReq, F("cnonce=\""));
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("Hash of user:realm:pass=%s\n", H1.c_str());
|
||||||
DEBUG_OUTPUT.println("Hash of user:realm:pass=" + H1);
|
|
||||||
#endif
|
|
||||||
MD5Builder md5;
|
MD5Builder md5;
|
||||||
md5.begin();
|
md5.begin();
|
||||||
if(_currentMethod == HTTP_GET){
|
if(_currentMethod == HTTP_GET){
|
||||||
@ -218,9 +206,7 @@ bool ESP8266WebServerTemplate<ServerType>::authenticateDigest(const String& user
|
|||||||
}
|
}
|
||||||
md5.calculate();
|
md5.calculate();
|
||||||
String _H2 = md5.toString();
|
String _H2 = md5.toString();
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("Hash of GET:uri=%s\n", _H2.c_str());
|
||||||
DEBUG_OUTPUT.println("Hash of GET:uri=" + _H2);
|
|
||||||
#endif
|
|
||||||
md5.begin();
|
md5.begin();
|
||||||
if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) {
|
||||||
md5.add(H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2);
|
md5.add(H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2);
|
||||||
@ -229,9 +215,7 @@ bool ESP8266WebServerTemplate<ServerType>::authenticateDigest(const String& user
|
|||||||
}
|
}
|
||||||
md5.calculate();
|
md5.calculate();
|
||||||
String _responsecheck = md5.toString();
|
String _responsecheck = md5.toString();
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("The Proper response=%s\n", _responsecheck.c_str());
|
||||||
DEBUG_OUTPUT.println("The Proper response=" +_responsecheck);
|
|
||||||
#endif
|
|
||||||
if(_response == _responsecheck){
|
if(_response == _responsecheck){
|
||||||
authReq = "";
|
authReq = "";
|
||||||
return true;
|
return true;
|
||||||
@ -315,9 +299,7 @@ void ESP8266WebServerTemplate<ServerType>::handleClient() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("New client\n");
|
||||||
DEBUG_OUTPUT.println("New client");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_currentClient = client;
|
_currentClient = client;
|
||||||
_currentStatus = HC_WAIT_READ;
|
_currentStatus = HC_WAIT_READ;
|
||||||
@ -327,6 +309,13 @@ void ESP8266WebServerTemplate<ServerType>::handleClient() {
|
|||||||
bool keepCurrentClient = false;
|
bool keepCurrentClient = false;
|
||||||
bool callYield = false;
|
bool callYield = false;
|
||||||
|
|
||||||
|
DBGWS("http-server loop: conn=%d avail=%d status=%s\n",
|
||||||
|
_currentClient.connected(), _currentClient.available(),
|
||||||
|
_currentStatus==HC_NONE?"none":
|
||||||
|
_currentStatus==HC_WAIT_READ?"wait-read":
|
||||||
|
_currentStatus==HC_WAIT_CLOSE?"wait-close":
|
||||||
|
"??");
|
||||||
|
|
||||||
if (_currentClient.connected() || _currentClient.available()) {
|
if (_currentClient.connected() || _currentClient.available()) {
|
||||||
if (_currentClient.available() && _keepAlive) {
|
if (_currentClient.available() && _keepAlive) {
|
||||||
_currentStatus = HC_WAIT_READ;
|
_currentStatus = HC_WAIT_READ;
|
||||||
@ -339,34 +328,57 @@ void ESP8266WebServerTemplate<ServerType>::handleClient() {
|
|||||||
case HC_WAIT_READ:
|
case HC_WAIT_READ:
|
||||||
// Wait for data from client to become available
|
// Wait for data from client to become available
|
||||||
if (_currentClient.available()) {
|
if (_currentClient.available()) {
|
||||||
if (_parseRequest(_currentClient)) {
|
switch (_parseRequest(_currentClient))
|
||||||
|
{
|
||||||
|
case CLIENT_REQUEST_CAN_CONTINUE:
|
||||||
_currentClient.setTimeout(HTTP_MAX_SEND_WAIT);
|
_currentClient.setTimeout(HTTP_MAX_SEND_WAIT);
|
||||||
_contentLength = CONTENT_LENGTH_NOT_SET;
|
_contentLength = CONTENT_LENGTH_NOT_SET;
|
||||||
_handleRequest();
|
_handleRequest();
|
||||||
|
/* fallthrough */
|
||||||
if (_currentClient.connected()) {
|
case CLIENT_REQUEST_IS_HANDLED:
|
||||||
|
if (_currentClient.connected() || _currentClient.available()) {
|
||||||
_currentStatus = HC_WAIT_CLOSE;
|
_currentStatus = HC_WAIT_CLOSE;
|
||||||
_statusChange = millis();
|
_statusChange = millis();
|
||||||
keepCurrentClient = true;
|
keepCurrentClient = true;
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
} else { // !_currentClient.available()
|
DBGWS("webserver: peer has closed after served\n");
|
||||||
|
break;
|
||||||
|
case CLIENT_MUST_STOP:
|
||||||
|
DBGWS("Close client\n");
|
||||||
|
_currentClient.stop();
|
||||||
|
break;
|
||||||
|
case CLIENT_IS_GIVEN:
|
||||||
|
// client must not be stopped but must not be handled here anymore
|
||||||
|
// (example: tcp connection given to websocket)
|
||||||
|
DBGWS("Give client\n");
|
||||||
|
break;
|
||||||
|
} // switch _parseRequest()
|
||||||
|
} else {
|
||||||
|
// !_currentClient.available(): waiting for more data
|
||||||
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) {
|
if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) {
|
||||||
keepCurrentClient = true;
|
keepCurrentClient = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
DBGWS("webserver: closing after read timeout\n");
|
||||||
callYield = true;
|
callYield = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HC_WAIT_CLOSE:
|
case HC_WAIT_CLOSE:
|
||||||
// Wait for client to close the connection
|
// Wait for client to close the connection
|
||||||
if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) {
|
if (!_server.available() && (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT)) {
|
||||||
keepCurrentClient = true;
|
keepCurrentClient = true;
|
||||||
callYield = true;
|
callYield = true;
|
||||||
|
if (_currentClient.available())
|
||||||
|
// continue serving current client
|
||||||
|
_currentStatus = HC_WAIT_READ;
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
} // switch _currentStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!keepCurrentClient) {
|
if (!keepCurrentClient) {
|
||||||
|
DBGWS("Drop client\n");
|
||||||
_currentClient = ClientType();
|
_currentClient = ClientType();
|
||||||
_currentStatus = HC_NONE;
|
_currentStatus = HC_NONE;
|
||||||
_currentUpload.reset();
|
_currentUpload.reset();
|
||||||
@ -687,17 +699,13 @@ template <typename ServerType>
|
|||||||
void ESP8266WebServerTemplate<ServerType>::_handleRequest() {
|
void ESP8266WebServerTemplate<ServerType>::_handleRequest() {
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
if (!_currentHandler){
|
if (!_currentHandler){
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("request handler not found\n");
|
||||||
DEBUG_OUTPUT.println("request handler not found");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
|
handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
DEBUG_OUTPUT.println("request handler failed to handle request");
|
DBGWS("request handler failed to handle request\n");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
if (!handled && _notFoundHandler) {
|
if (!handled && _notFoundHandler) {
|
||||||
_notFoundHandler();
|
_notFoundHandler();
|
||||||
|
@ -26,11 +26,24 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <FS.h>
|
#include <FS.h>
|
||||||
#include "detail/mimetable.h"
|
#include "detail/mimetable.h"
|
||||||
#include "Uri.h"
|
#include "Uri.h"
|
||||||
|
|
||||||
|
//#define DEBUG_ESP_HTTP_SERVER
|
||||||
|
|
||||||
|
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||||
|
#ifdef DEBUG_ESP_PORT
|
||||||
|
#define DBGWS(f,...) do { DEBUG_ESP_PORT.printf(PSTR(f), ##__VA_ARGS__); } while (0)
|
||||||
|
#else
|
||||||
|
#define DBGWS(f,...) do { Serial.printf(PSTR(f), ##__VA_ARGS__); } while (0)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define DBGWS(x...) do { (void)0; } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_HEAD, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS };
|
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_HEAD, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS };
|
||||||
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
|
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
|
||||||
UPLOAD_FILE_ABORTED };
|
UPLOAD_FILE_ABORTED };
|
||||||
@ -80,6 +93,9 @@ public:
|
|||||||
using ClientType = typename ServerType::ClientType;
|
using ClientType = typename ServerType::ClientType;
|
||||||
using RequestHandlerType = RequestHandler<ServerType>;
|
using RequestHandlerType = RequestHandler<ServerType>;
|
||||||
using WebServerType = ESP8266WebServerTemplate<ServerType>;
|
using WebServerType = ESP8266WebServerTemplate<ServerType>;
|
||||||
|
enum ClientFuture { CLIENT_REQUEST_CAN_CONTINUE, CLIENT_REQUEST_IS_HANDLED, CLIENT_MUST_STOP, CLIENT_IS_GIVEN };
|
||||||
|
typedef String (*ContentTypeFunction) (const String&);
|
||||||
|
using HookFunction = std::function<ClientFuture(const String& method, const String& url, WiFiClient* client, ContentTypeFunction contentType)>;
|
||||||
|
|
||||||
void begin();
|
void begin();
|
||||||
void begin(uint16_t port);
|
void begin(uint16_t port);
|
||||||
@ -200,11 +216,25 @@ public:
|
|||||||
|
|
||||||
static String responseCodeToString(const int code);
|
static String responseCodeToString(const int code);
|
||||||
|
|
||||||
|
void addHook (HookFunction hook) {
|
||||||
|
if (_hook) {
|
||||||
|
auto previousHook = _hook;
|
||||||
|
_hook = [previousHook, hook](const String& method, const String& url, WiFiClient* client, ContentTypeFunction contentType) {
|
||||||
|
auto whatNow = previousHook(method, url, client, contentType);
|
||||||
|
if (whatNow == CLIENT_REQUEST_CAN_CONTINUE)
|
||||||
|
return hook(method, url, client, contentType);
|
||||||
|
return whatNow;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
_hook = hook;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _addRequestHandler(RequestHandlerType* handler);
|
void _addRequestHandler(RequestHandlerType* handler);
|
||||||
void _handleRequest();
|
void _handleRequest();
|
||||||
void _finalizeResponse();
|
void _finalizeResponse();
|
||||||
bool _parseRequest(ClientType& client);
|
ClientFuture _parseRequest(ClientType& client);
|
||||||
void _parseArguments(const String& data);
|
void _parseArguments(const String& data);
|
||||||
int _parseArgumentsPrivate(const String& data, std::function<void(String&,String&,const String&,int,int,int,int)> handler);
|
int _parseArgumentsPrivate(const String& data, std::function<void(String&,String&,const String&,int,int,int,int)> handler);
|
||||||
bool _parseForm(ClientType& client, const String& boundary, uint32_t len);
|
bool _parseForm(ClientType& client, const String& boundary, uint32_t len);
|
||||||
@ -261,8 +291,7 @@ protected:
|
|||||||
String _sopaque;
|
String _sopaque;
|
||||||
String _srealm; // Store the Auth realm between Calls
|
String _srealm; // Store the Auth realm between Calls
|
||||||
|
|
||||||
|
HookFunction _hook;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,13 +25,6 @@
|
|||||||
#include "ESP8266WebServer.h"
|
#include "ESP8266WebServer.h"
|
||||||
#include "detail/mimetable.h"
|
#include "detail/mimetable.h"
|
||||||
|
|
||||||
//#define DEBUG_ESP_HTTP_SERVER
|
|
||||||
#ifdef DEBUG_ESP_PORT
|
|
||||||
#define DEBUG_OUTPUT DEBUG_ESP_PORT
|
|
||||||
#else
|
|
||||||
#define DEBUG_OUTPUT Serial
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WEBSERVER_MAX_POST_ARGS
|
#ifndef WEBSERVER_MAX_POST_ARGS
|
||||||
#define WEBSERVER_MAX_POST_ARGS 32
|
#define WEBSERVER_MAX_POST_ARGS 32
|
||||||
#endif
|
#endif
|
||||||
@ -61,17 +54,14 @@ static bool readBytesWithTimeout(typename ServerType::ClientType& client, size_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename ServerType>
|
template <typename ServerType>
|
||||||
bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
typename ESP8266WebServerTemplate<ServerType>::ClientFuture ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
||||||
// Read the first line of HTTP request
|
// Read the first line of HTTP request
|
||||||
String req = client.readStringUntil('\r');
|
String req = client.readStringUntil('\r');
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("request: %s\n", req.c_str());
|
||||||
DEBUG_OUTPUT.print("request: ");
|
|
||||||
DEBUG_OUTPUT.println(req);
|
|
||||||
#endif
|
|
||||||
client.readStringUntil('\n');
|
client.readStringUntil('\n');
|
||||||
//reset header value
|
//reset header value
|
||||||
for (int i = 0; i < _headerKeysCount; ++i) {
|
for (int i = 0; i < _headerKeysCount; ++i) {
|
||||||
_currentHeaders[i].value =String();
|
_currentHeaders[i].value.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// First line of HTTP request looks like "GET /path HTTP/1.1"
|
// First line of HTTP request looks like "GET /path HTTP/1.1"
|
||||||
@ -79,10 +69,8 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
|||||||
int addr_start = req.indexOf(' ');
|
int addr_start = req.indexOf(' ');
|
||||||
int addr_end = req.indexOf(' ', addr_start + 1);
|
int addr_end = req.indexOf(' ', addr_start + 1);
|
||||||
if (addr_start == -1 || addr_end == -1) {
|
if (addr_start == -1 || addr_end == -1) {
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("Invalid request\n");
|
||||||
DEBUG_OUTPUT.println("Invalid request");
|
return CLIENT_MUST_STOP;
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String methodStr = req.substring(0, addr_start);
|
String methodStr = req.substring(0, addr_start);
|
||||||
@ -98,6 +86,13 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
|||||||
_currentUri = url;
|
_currentUri = url;
|
||||||
_chunked = false;
|
_chunked = false;
|
||||||
|
|
||||||
|
if (_hook)
|
||||||
|
{
|
||||||
|
auto whatNow = _hook(methodStr, url, &client, mime::getContentType);
|
||||||
|
if (whatNow != CLIENT_REQUEST_CAN_CONTINUE)
|
||||||
|
return whatNow;
|
||||||
|
}
|
||||||
|
|
||||||
HTTPMethod method = HTTP_GET;
|
HTTPMethod method = HTTP_GET;
|
||||||
if (methodStr == F("HEAD")) {
|
if (methodStr == F("HEAD")) {
|
||||||
method = HTTP_HEAD;
|
method = HTTP_HEAD;
|
||||||
@ -117,14 +112,8 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
|||||||
_keepAlive = _currentVersion > 0; // Keep the connection alive by default
|
_keepAlive = _currentVersion > 0; // Keep the connection alive by default
|
||||||
// if the protocol version is greater than HTTP 1.0
|
// if the protocol version is greater than HTTP 1.0
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("method: %s url: %s search: %s keepAlive=: %d\n",
|
||||||
DEBUG_OUTPUT.print("method: ");
|
methodStr.c_str(), url.c_str(), searchStr.c_str(), _keepAlive);
|
||||||
DEBUG_OUTPUT.print(methodStr);
|
|
||||||
DEBUG_OUTPUT.print(" url: ");
|
|
||||||
DEBUG_OUTPUT.print(url);
|
|
||||||
DEBUG_OUTPUT.print(" search: ");
|
|
||||||
DEBUG_OUTPUT.println(searchStr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//attach handler
|
//attach handler
|
||||||
RequestHandlerType* handler;
|
RequestHandlerType* handler;
|
||||||
@ -157,12 +146,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
|||||||
headerValue.trim();
|
headerValue.trim();
|
||||||
_collectHeader(headerName.c_str(),headerValue.c_str());
|
_collectHeader(headerName.c_str(),headerValue.c_str());
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("headerName: %s\nheaderValue: %s\n", headerName.c_str(), headerValue.c_str());
|
||||||
DEBUG_OUTPUT.print("headerName: ");
|
|
||||||
DEBUG_OUTPUT.println(headerName);
|
|
||||||
DEBUG_OUTPUT.print("headerValue: ");
|
|
||||||
DEBUG_OUTPUT.println(headerValue);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (headerName.equalsIgnoreCase(FPSTR(Content_Type))){
|
if (headerName.equalsIgnoreCase(FPSTR(Content_Type))){
|
||||||
using namespace mime;
|
using namespace mime;
|
||||||
@ -193,7 +177,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return false;
|
return CLIENT_MUST_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEncoded) {
|
if (isEncoded) {
|
||||||
@ -218,7 +202,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
|||||||
} else { // isForm is true
|
} else { // isForm is true
|
||||||
// here: content is not yet read (plainBuf is still empty)
|
// here: content is not yet read (plainBuf is still empty)
|
||||||
if (!_parseForm(client, boundaryStr, contentLength)) {
|
if (!_parseForm(client, boundaryStr, contentLength)) {
|
||||||
return false;
|
return CLIENT_MUST_STOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -237,12 +221,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
|||||||
headerValue = req.substring(headerDiv + 2);
|
headerValue = req.substring(headerDiv + 2);
|
||||||
_collectHeader(headerName.c_str(),headerValue.c_str());
|
_collectHeader(headerName.c_str(),headerValue.c_str());
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("headerName: %s\nheaderValue: %s\n", headerName.c_str(), headerValue.c_str());
|
||||||
DEBUG_OUTPUT.print(F("headerName: "));
|
|
||||||
DEBUG_OUTPUT.println(headerName);
|
|
||||||
DEBUG_OUTPUT.print(F("headerValue: "));
|
|
||||||
DEBUG_OUTPUT.println(headerValue);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (headerName.equalsIgnoreCase(F("Host"))){
|
if (headerName.equalsIgnoreCase(F("Host"))){
|
||||||
_hostHeader = headerValue;
|
_hostHeader = headerValue;
|
||||||
@ -255,19 +234,15 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
|
|||||||
client.flush();
|
client.flush();
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
#ifdef DEBUG_ESP_HTTP_SERVER
|
||||||
DEBUG_OUTPUT.print(F("Request: "));
|
DBGWS("Request: %s\nArguments: %s\nfinal list of key/value pairs:\n",
|
||||||
DEBUG_OUTPUT.println(url);
|
url.c_str(), searchStr.c_str());
|
||||||
DEBUG_OUTPUT.print(F("Arguments: "));
|
|
||||||
DEBUG_OUTPUT.println(searchStr);
|
|
||||||
|
|
||||||
DEBUG_OUTPUT.println(F("final list of key/value pairs:"));
|
|
||||||
for (int i = 0; i < _currentArgCount; i++)
|
for (int i = 0; i < _currentArgCount; i++)
|
||||||
DEBUG_OUTPUT.printf(" key:'%s' value:'%s'\r\n",
|
DBGWS(" key:'%s' value:'%s'\r\n",
|
||||||
_currentArgs[i].key.c_str(),
|
_currentArgs[i].key.c_str(),
|
||||||
_currentArgs[i].value.c_str());
|
_currentArgs[i].value.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return CLIENT_REQUEST_CAN_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ServerType>
|
template <typename ServerType>
|
||||||
@ -316,10 +291,7 @@ void ESP8266WebServerTemplate<ServerType>::_parseArguments(const String& data) {
|
|||||||
template <typename ServerType>
|
template <typename ServerType>
|
||||||
int ESP8266WebServerTemplate<ServerType>::_parseArgumentsPrivate(const String& data, std::function<void(String&,String&,const String&,int,int,int,int)> handler) {
|
int ESP8266WebServerTemplate<ServerType>::_parseArgumentsPrivate(const String& data, std::function<void(String&,String&,const String&,int,int,int,int)> handler) {
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("args: %s\n", data.c_str());
|
||||||
DEBUG_OUTPUT.print("args: ");
|
|
||||||
DEBUG_OUTPUT.println(data);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
int arg_total = 0;
|
int arg_total = 0;
|
||||||
@ -357,11 +329,7 @@ int ESP8266WebServerTemplate<ServerType>::_parseArgumentsPrivate(const String& d
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("args count: %d\n", (int)arg_total);
|
||||||
DEBUG_OUTPUT.print("args count: ");
|
|
||||||
DEBUG_OUTPUT.println(arg_total);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return arg_total;
|
return arg_total;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,12 +358,7 @@ uint8_t ESP8266WebServerTemplate<ServerType>::_uploadReadByte(ClientType& client
|
|||||||
template <typename ServerType>
|
template <typename ServerType>
|
||||||
bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const String& boundary, uint32_t len){
|
bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const String& boundary, uint32_t len){
|
||||||
(void) len;
|
(void) len;
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("Parse Form: Boundary: '%s' Length: %d\n", boundary.c_str(), (int)len);
|
||||||
DEBUG_OUTPUT.print("Parse Form: Boundary: ");
|
|
||||||
DEBUG_OUTPUT.print(boundary);
|
|
||||||
DEBUG_OUTPUT.print(" Length: ");
|
|
||||||
DEBUG_OUTPUT.println(len);
|
|
||||||
#endif
|
|
||||||
String line;
|
String line;
|
||||||
int retry = 0;
|
int retry = 0;
|
||||||
do {
|
do {
|
||||||
@ -429,18 +392,12 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
|
|||||||
argFilename = argName.substring(nameStart+2, argName.length() - 1);
|
argFilename = argName.substring(nameStart+2, argName.length() - 1);
|
||||||
argName = argName.substring(0, argName.indexOf('"'));
|
argName = argName.substring(0, argName.indexOf('"'));
|
||||||
argIsFile = true;
|
argIsFile = true;
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("PostArg FileName: %s\n", argFilename.c_str());
|
||||||
DEBUG_OUTPUT.print("PostArg FileName: ");
|
|
||||||
DEBUG_OUTPUT.println(argFilename);
|
|
||||||
#endif
|
|
||||||
//use GET to set the filename if uploading using blob
|
//use GET to set the filename if uploading using blob
|
||||||
if (argFilename == F("blob") && hasArg(FPSTR(filename)))
|
if (argFilename == F("blob") && hasArg(FPSTR(filename)))
|
||||||
argFilename = arg(FPSTR(filename));
|
argFilename = arg(FPSTR(filename));
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("PostArg Name: %s\n", argName.c_str());
|
||||||
DEBUG_OUTPUT.print("PostArg Name: ");
|
|
||||||
DEBUG_OUTPUT.println(argName);
|
|
||||||
#endif
|
|
||||||
using namespace mime;
|
using namespace mime;
|
||||||
argType = FPSTR(mimeTable[txt].mimeType);
|
argType = FPSTR(mimeTable[txt].mimeType);
|
||||||
line = client.readStringUntil('\r');
|
line = client.readStringUntil('\r');
|
||||||
@ -451,10 +408,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
|
|||||||
client.readStringUntil('\r');
|
client.readStringUntil('\r');
|
||||||
client.readStringUntil('\n');
|
client.readStringUntil('\n');
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("PostArg Type: %s\n", argType.c_str());
|
||||||
DEBUG_OUTPUT.print("PostArg Type: ");
|
|
||||||
DEBUG_OUTPUT.println(argType);
|
|
||||||
#endif
|
|
||||||
if (!argIsFile){
|
if (!argIsFile){
|
||||||
while(1){
|
while(1){
|
||||||
line = client.readStringUntil('\r');
|
line = client.readStringUntil('\r');
|
||||||
@ -463,20 +417,14 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
|
|||||||
if (argValue.length() > 0) argValue += '\n';
|
if (argValue.length() > 0) argValue += '\n';
|
||||||
argValue += line;
|
argValue += line;
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("PostArg Value: %s\n\n", argValue.c_str());
|
||||||
DEBUG_OUTPUT.print("PostArg Value: ");
|
|
||||||
DEBUG_OUTPUT.println(argValue);
|
|
||||||
DEBUG_OUTPUT.println();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
RequestArgument& arg = _postArgs[_postArgsLen++];
|
RequestArgument& arg = _postArgs[_postArgsLen++];
|
||||||
arg.key = argName;
|
arg.key = argName;
|
||||||
arg.value = argValue;
|
arg.value = argValue;
|
||||||
|
|
||||||
if (line == ("--"+boundary+"--")){
|
if (line == ("--"+boundary+"--")){
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("Done Parsing POST\n");
|
||||||
DEBUG_OUTPUT.println("Done Parsing POST");
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -488,12 +436,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseForm(ClientType& client, const
|
|||||||
_currentUpload->totalSize = 0;
|
_currentUpload->totalSize = 0;
|
||||||
_currentUpload->currentSize = 0;
|
_currentUpload->currentSize = 0;
|
||||||
_currentUpload->contentLength = len;
|
_currentUpload->contentLength = len;
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("Start File: %s Type: %s\n", _currentUpload->filename.c_str(), _currentUpload->type.c_str());
|
||||||
DEBUG_OUTPUT.print("Start File: ");
|
|
||||||
DEBUG_OUTPUT.print(_currentUpload->filename);
|
|
||||||
DEBUG_OUTPUT.print(" Type: ");
|
|
||||||
DEBUG_OUTPUT.println(_currentUpload->type);
|
|
||||||
#endif
|
|
||||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||||
_currentUpload->status = UPLOAD_FILE_WRITE;
|
_currentUpload->status = UPLOAD_FILE_WRITE;
|
||||||
@ -537,20 +480,14 @@ readfile:
|
|||||||
_currentUpload->status = UPLOAD_FILE_END;
|
_currentUpload->status = UPLOAD_FILE_END;
|
||||||
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
if(_currentHandler && _currentHandler->canUpload(_currentUri))
|
||||||
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
_currentHandler->upload(*this, _currentUri, *_currentUpload);
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("End File: %s Type: %s Size: %d\n",
|
||||||
DEBUG_OUTPUT.print("End File: ");
|
_currentUpload->filename.c_str(),
|
||||||
DEBUG_OUTPUT.print(_currentUpload->filename);
|
_currentUpload->type.c_str(),
|
||||||
DEBUG_OUTPUT.print(" Type: ");
|
(int)_currentUpload->totalSize);
|
||||||
DEBUG_OUTPUT.print(_currentUpload->type);
|
|
||||||
DEBUG_OUTPUT.print(" Size: ");
|
|
||||||
DEBUG_OUTPUT.println(_currentUpload->totalSize);
|
|
||||||
#endif
|
|
||||||
line = client.readStringUntil(0x0D);
|
line = client.readStringUntil(0x0D);
|
||||||
client.readStringUntil(0x0A);
|
client.readStringUntil(0x0A);
|
||||||
if (line == "--"){
|
if (line == "--"){
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("Done Parsing POST\n");
|
||||||
DEBUG_OUTPUT.println("Done Parsing POST");
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -598,10 +535,7 @@ readfile:
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_ESP_HTTP_SERVER
|
DBGWS("Error: line: %s\n", line.c_str());
|
||||||
DEBUG_OUTPUT.print("Error: line: ");
|
|
||||||
DEBUG_OUTPUT.println(line);
|
|
||||||
#endif
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user