Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webserver hook: allow to handle external http protocol #7459

Merged
merged 14 commits into from
Jul 28, 2020
Prev Previous commit
Next Next commit
ditto
  • Loading branch information
d-a-v committed Jul 15, 2020
commit 457272ccdfff0d94f43f60cdae7e7c5241f51d2a
58 changes: 46 additions & 12 deletions libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
#define DEBUG_OUTPUT Serial
#endif

#ifdef DEBUG_ESP_HTTP_SERVER
d-a-v marked this conversation as resolved.
Show resolved Hide resolved
#define DBGWS(x...) do { DEBUG_OUTPUT.printf(x); } while (0)
#else
#define DBGWS(x...) do { (void)0; } while (0)
#endif

static const char AUTHORIZATION_HEADER[] PROGMEM = "Authorization";
static const char qop_auth[] PROGMEM = "qop=auth";
static const char qop_auth_quoted[] PROGMEM = "qop=\"auth\"";
Expand Down Expand Up @@ -324,6 +330,13 @@ void ESP8266WebServerTemplate<ServerType>::handleClient() {
bool keepCurrentClient = 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()) {
switch (_currentStatus) {
case HC_NONE:
Expand All @@ -332,36 +345,57 @@ void ESP8266WebServerTemplate<ServerType>::handleClient() {
case HC_WAIT_READ:
// Wait for data from client to become available
if (_currentClient.available()) {
if (_parseRequest(_currentClient)) {
switch (_parseRequest(_currentClient))
{
case CLIENT_REQUEST_CAN_CONTINUE:
_currentClient.setTimeout(HTTP_MAX_SEND_WAIT);
_contentLength = CONTENT_LENGTH_NOT_SET;
_handleRequest();
} else {
keepCurrentClient = false;
/* fallthrough */
case CLIENT_REQUEST_IS_HANDLED:
if (_currentClient.connected() || _currentClient.available()) {
_currentStatus = HC_WAIT_CLOSE;
_statusChange = millis();
keepCurrentClient = true;
}
else
DBGWS("webserver: peer has closed after served\n");
break;
case CLIENT_MUST_STOP:
DBGWS("Close client\n");
_currentClient.stop();
}
if (_currentClient.connected()) {
_currentStatus = HC_WAIT_CLOSE;
_statusChange = millis();
keepCurrentClient = true;
}
} else { // !_currentClient.available()
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) {
keepCurrentClient = true;
}
else
DBGWS("webserver: closing after read timeout\n");
callYield = true;
}
break;
case HC_WAIT_CLOSE:
// 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;
callYield = true;
if (_currentClient.available())
// continue serving current client
_currentStatus = HC_WAIT_READ;
}
}
break;
} // switch _currentStatus
}

if (!keepCurrentClient) {
DBGWS("Drop client\n");
_currentClient = ClientType();
_currentStatus = HC_NONE;
_currentUpload.reset();
Expand Down
22 changes: 12 additions & 10 deletions libraries/ESP8266WebServer/src/ESP8266WebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ class ESP8266WebServerTemplate
using ClientType = typename ServerType::ClientType;
using RequestHandlerType = RequestHandler<ServerType>;
using WebServerType = ESP8266WebServerTemplate<ServerType>;
using hook_f = std::function<bool(const String& method, const String& url, ClientType& client)>;
using ContentType_f = std::function<String(const String&)>;
d-a-v marked this conversation as resolved.
Show resolved Hide resolved
enum ClientFuture_e { CLIENT_REQUEST_CAN_CONTINUE, CLIENT_REQUEST_IS_HANDLED, CLIENT_MUST_STOP, CLIENT_IS_GIVEN };
using Hook_f = std::function<ClientFuture_e(const String& method, const String& url, WiFiClient* client, ContentType_f contentType)>;
d-a-v marked this conversation as resolved.
Show resolved Hide resolved

void begin();
void begin(uint16_t port);
Expand Down Expand Up @@ -194,16 +196,17 @@ class ESP8266WebServerTemplate

static String responseCodeToString(const int code);

void addHook (hook_f hook)
void addHook (Hook_f hook)
{
d-a-v marked this conversation as resolved.
Show resolved Hide resolved
if (_hook)
{
auto old = _hook;
_hook = [old, hook](const String& method, const String& url, ClientType& client)
auto previous = _hook;
_hook = [previous, hook](const String& method, const String& url, WiFiClient* client, ContentType_f contentType)
{
if (first(method, url, client))
return true;
return hook(method, url, client);
auto whatNow = previous(method, url, client, contentType);
if (whatNow == CLIENT_REQUEST_CAN_CONTINUE)
return hook(method, url, client, contentType);
return whatNow;
};
}
else
Expand All @@ -214,7 +217,7 @@ class ESP8266WebServerTemplate
void _addRequestHandler(RequestHandlerType* handler);
void _handleRequest();
void _finalizeResponse();
bool _parseRequest(ClientType& client);
ClientFuture_e _parseRequest(ClientType& client);
void _parseArguments(const String& data);
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);
Expand Down Expand Up @@ -269,8 +272,7 @@ class ESP8266WebServerTemplate
String _sopaque;
String _srealm; // Store the Auth realm between Calls

hook_f _hook;

Hook_f _hook;
};


Expand Down
20 changes: 9 additions & 11 deletions libraries/ESP8266WebServer/src/Parsing-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ static bool readBytesWithTimeout(typename ServerType::ClientType& client, size_t
}

template <typename ServerType>
bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
typename ESP8266WebServerTemplate<ServerType>::ClientFuture_e ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
// Read the first line of HTTP request
String req = client.readStringUntil('\r');
#ifdef DEBUG_ESP_HTTP_SERVER
Expand All @@ -82,7 +82,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.println("Invalid request");
#endif
return false;
return CLIENT_MUST_STOP;
}

String methodStr = req.substring(0, addr_start);
Expand All @@ -98,13 +98,11 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
_currentUri = url;
_chunked = false;

if (_hook && _hook(methodStr, url, client))
if (_hook)
{
// _hook() must return true when method is recognized
// (even when request goes wrong)
// in any case, request header and content must be read
// returning false because request is now already handled
return false;
auto whatNow = _hook(methodStr, url, &client, mime::getContentType);
if (whatNow != CLIENT_REQUEST_CAN_CONTINUE)
return whatNow;
}

HTTPMethod method = HTTP_GET;
Expand Down Expand Up @@ -197,7 +195,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
)
)
{
return false;
return CLIENT_MUST_STOP;
}

if (isEncoded) {
Expand All @@ -221,7 +219,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
} else { // isForm is true
// here: content is not yet read (plainBuf is still empty)
if (!_parseForm(client, boundaryStr, contentLength)) {
return false;
return CLIENT_MUST_STOP;
}
}
} else {
Expand Down Expand Up @@ -268,7 +266,7 @@ bool ESP8266WebServerTemplate<ServerType>::_parseRequest(ClientType& client) {
_currentArgs[i].value.c_str());
#endif

return true;
return CLIENT_REQUEST_CAN_CONTINUE;
}

template <typename ServerType>
Expand Down