8000 - Implementation of Websocket · proddy/esp32_https_server@24b838a · GitHub
[go: up one dir, main page]

Skip to content

Commit 24b838a

Browse files
committed
- Implementation of Websocket
- first commit
1 parent d59962f commit 24b838a

15 files changed

+697
-6
lines changed

src/ConnectionContext.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
namespace httpsserver {
1111

12+
class WebsocketHandler;
13+
1214
class ConnectionContext {
1315
public:
1416
ConnectionContext();
@@ -24,6 +26,7 @@ class ConnectionContext {
2426
virtual size_t writeBuffer(byte* buffer, size_t length) = 0;
2527

2628
virtual bool isSecure() = 0;
29+
virtual void setWebsocketHandler(WebsocketHandler *wsHandler) = 0;
2730
};
2831

2932
} /* namespace httpsserver */

src/HTTPConnection.cpp

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include "HTTPConnection.hpp"
2+
#include "Websocket.hpp"
3+
#include <hwcrypto/sha.h>
24

35
namespace httpsserver {
46

@@ -18,6 +20,7 @@ HTTPConnection::HTTPConnection(ResourceResolver * resResolver):
1820
_isKeepAlive = false;
1921
_lastTransmissionTS = millis();
2022
_shutdownTS = 0;
23+
_websocket = nullptr;
2124
}
2225

2326
HTTPConnection::~HTTPConnection() {
@@ -121,6 +124,7 @@ void HTTPConnection::closeConnection() {
121124
}
122125

123126
if (_httpHeaders != NULL) {
127+
HTTPS_DLOG("[ ] Free headers");
124128
delete _httpHeaders;
125129
_httpHeaders = NULL;
126130
}
@@ -327,6 +331,10 @@ size_t HTTPConnection::getCacheSize() {
327331
return (_isKeepAlive ? HTTPS_KEEPALIVE_CACHESIZE : 0);
328332
}
329333

334+
void HTTPConnection::setWebsocketHandler(WebsocketHandler *wsHandler) {
335+
_wsHandler = wsHandler;
336+
}
337+
330338
void HTTPConnection::loop() {
331339
// First, update the buffer
332340
// newByteCount will contain the number of new bytes that have to be processed
@@ -397,6 +405,7 @@ void HTTPConnection::loop() {
397405
_parserLine.text.substr(0, idxColon),
398406
_parserLine.text.substr(idxColon+2)
399407
));
408+
HTTPS_DLOG(("[ ] Header: " + _parserLine.text.substr(0, 67E6 idxColon) + ":" + _parserLine.text.substr(idxColon+2)).c_str());
400409
} else {
401410
HTTPS_DLOG("Malformed header line detected. Client error.");
402411
HTTPS_DLOG(_parserLine.text.c_str());
@@ -427,6 +436,36 @@ void HTTPConnection::loop() {
427436
_isKeepAlive = false;
428437
}
429438

439+
// do we have a websocket connection?
440+
if(checkWebsocket()) {
441+
// Create response
442+
HTTPResponse res = HTTPResponse(this);
443+
// Add default headers to the response
444+
auto allDefaultHeaders = _defaultHeaders->getAll();
445+
for(std::vector<HTTPHeader*>::iterator header = allDefaultHeaders->begin(); header != allDefaultHeaders->end(); ++header) {
446+
res.setHeader((*header)->_name, (*header)->_value);
447+
}
448+
// Set the response status
449+
res.setStatusCode(101);
450+
res.setStatusText("Switching Protocols");
451+
res.setHeader("Upgrade", "websocket");
452+
res.setHeader("Connection", "Upgrade");
453+
res.setHeader("Sec-WebSocket-Accept", websocketKeyResponseHash(_httpHeaders->getValue("Sec-WebSocket-Key")));
454+
res.print("");
455+
456+
// Callback for the actual resource
457+
HTTPSCallbackFunction * resourceCallback = resolvedResource.getMatchingNode()->_callback;
458+
// persistent request for websocket. To be destroyed when connection closes.
459+
HTTPRequest *req = new HTTPRequest(this, _httpHeaders, resolvedResource.getParams(), _httpResource, _httpMethod, "");
460+
// bind function call to the actual resource
461+
std::function<void()> next = std::function<void()>(std::bind(resourceCallback, req, nullptr));
462+
_websocket = new Websocket(this); // make websocket with this connection
463+
next(); // call callback
464+
delete req;
465+
_connectionState = STATE_WEBSOCKET;
466+
break;
467+
}
468+
430469
// Create request context
431470
HTTPRequest req = HTTPRequest(this, _httpHeaders, resolvedResource.getParams(), _httpResource, _httpMethod, resolvedResource.getMatchingNode()->_tag);
432471
HTTPResponse res = HTTPResponse(this);
@@ -507,12 +546,54 @@ void HTTPConnection::loop() {
507546
closeConnection();
508547
break;
509548
case STATE_WEBSOCKET: // Do handling of the websocket
510-
549+
refreshTimeout(); // don't timeout websocket connection
550+
if(pendingBufferSize() > 0) {
551+
HTTPS_DLOG("[ ] websocket handler");
552+
if(_websocket->read() < 0) {
553+
_websocket->close();
554+
delete _websocket;
555+
_httpHeaders->clearAll();
556+
_connectionState = STATE_CLOSING;
557+
}
558+
}
559+
//readBuffer();
511560
break;
512561
default:;
513562
}
514563
}
515564

516565
}
517566

567+
568+
bool HTTPConnection::checkWebsocket() {
569+
// check criteria according to RFC6455
570+
// HTTPS_DLOG(("[ ] Method:" + _httpMethod).c_str());
571+
// HTTPS_DLOG(("[ ] Header Host:" + _httpHeaders->getValue("Host")).c_str());
572+
// HTTPS_DLOG(("[ ] Header Upgrade:" + _httpHeaders->getValue("Upgrade")).c_str());
573+
// HTTPS_DLOG(("[ ] Header Connection:" + _httpHeaders->getValue("Connection")).c_str());
574+
// HTTPS_DLOG(("[ ] Header Sec-WebSocket-Key:" + _httpHeaders->getValue("Sec-WebSocket-Key")).c_str());
575+
// HTTPS_DLOG(("[ ] Header Sec-WebSocket-Version:" + _httpHeaders->getValue("Sec-WebSocket-Version")).c_str());
576+
if(_httpMethod == "GET" &&
577+
!_httpHeaders->getValue("Host").empty() &&
578+
_httpHeaders->getValue("Upgrade") == "websocket" &&
579+
_httpHeaders->getValue("Connection").find("Upgrade") != std::string::npos &&
580+
!_httpHeaders->getValue("Sec-WebSocket-Key").empty() &&
581+
_httpHeaders->getValue("Sec-WebSocket-Version") == "13") {
582+
583+
HTTPS_DLOG("[-->] Websocket detected");
584+
return true;
585+
} else
586+
return false;
587+
}
588+
589+
std::string HTTPConnection::websocketKeyResponseHash(std::string key) {
590+
std::string newKey = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
591+
uint8_t shaData[20];
592+
esp_sha(SHA1, (uint8_t*)newKey.data(), newKey.length(), shaData);
593+
//GeneralUtils::hexDump(shaData, 20);
594+
std::string retStr;
595+
base64Encode(std::string((char*)shaData, sizeof(shaData)), &retStr);
596+
return retStr;
597+
} // WebsocketKeyResponseHash
598+
518599
} /* namespace httpsserver */

src/HTTPConnection.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@
1919
#include "ResourceNode.hpp"
2020
#include "HTTPRequest.hpp"
2121
#include "HTTPResponse.hpp"
22+
#include "ConnectionContext.hpp"
2223

2324
namespace httpsserver {
2425

26+
class Websocket;
27+
2528
class HTTPConnection : private ConnectionContext {
29+
friend class Websocket;
2630
public:
2731
HTTPConnection(ResourceResolver * resResolver);
2832
virtual ~HTTPConnection();
@@ -34,10 +38,12 @@ class HTTPConnection : private ConnectionContext {
3438
void loop();
3539
bool isClosed();
3640
bool isError();
41+
void setWebsocketHandler(WebsocketHandler *wsHandler);
3742

3843
protected:
3944
friend class HTTPRequest;
4045
friend class HTTPResponse;
46+
friend class WebsocketInputStreambuf;
4147

4248
virtual size_t writeBuffer(byte* buffer, size_t length);
4349
virtual size_t readBytesToBuffer(byte* buffer, size_t length);
@@ -108,7 +114,8 @@ class HTTPConnection : private ConnectionContext {
108114
void signalRequestError();
109115
size_t readBuffer(byte* buffer, size_t length);
110116
size_t getCacheSize();
111-
117+
bool checkWebsocket();
118+
std::string websocketKeyResponseHash(std::string key);
112119

113120
// The receive buffer
114121
char _receiveBuffer[HTTPS_CONNECTION_DATA_CHUNK_SIZE];
@@ -143,6 +150,9 @@ class HTTPConnection : private ConnectionContext {
143150
// Should we use keep alive
144151
bool _isKeepAlive;
145152

153+
//Websocket connection
154+
WebsocketHandler * _wsHandler;
155+
Websocket * _websocket;
146156

147157
};
148158

src/HTTPHeaders.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ HTTPHeader * HTTPHeaders::get(const std::string name) {
2121
return NULL;
2222
}
2323

24+
std::string HTTPHeaders::getValue(std::string name) {
25+
for(std::vector<HTTPHeader*>::iterator header = _headers->begin(); header != _headers->end(); ++header) {
26+
if ((*header)->_name.compare(name)==0) {
27+
return ((*header)->_value);
28+
}
29+
}
30+
return "";
31+
}
32+
33+
2434
void HTTPHeaders::set(HTTPHeader * header) {
2535
for(int i = 0; i < _headers->size(); i++) {
2636
if ((*_headers)[i]->_name.compare(header->_name)==0) {

src/HTTPHeaders.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class HTTPHeaders {
1818
virtual ~HTTPHeaders();
1919

2020
HTTPHeader * get(std::string name);
21+
std::string getValue(std::string name);
2122
void set(HTTPHeader * header);
2223

2324
std::vector<HTTPHeader *> * getAll();

src/HTTPMiddlewareFunction.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "HTTPSCallbackFunction.hpp"
99

1010
namespace httpsserver {
11+
class HTTPRequest;
1112
/**
1213
* A middleware function that can be registered at the server.
1314
*

src/HTTPRequest.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ HTTPRequest::HTTPRequest(ConnectionContext * con, HTTPHeaders * headers, Resourc
2222
}
2323

2424
HTTPRequest::~HTTPRequest() {
25-
25+
_headers->clearAll();
2626
}
2727

2828

@@ -156,4 +156,9 @@ bool HTTPRequest::isSecure() {
156156
return _con->isSecure();
157157
}
158158

159+
160+
void HTTPRequest::setWebsocketHandler(WebsocketHandler *wsHandler) {
161+
_con->setWebsocketHandler(wsHandler);
162+
}
163+
159164
} /* namespace httpsserver */

src/HTTPRequest.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class HTTPRequest {
3434
std::string getBasicAuthUser();
3535
std::string getBasicAuthPassword();
3636
bool isSecure();
37+
void setWebsocketHandler(WebsocketHandler *wsHandler);
38+
3739
private:
3840
std::string decodeBasicAuthToken();
3941

src/HTTPResponse.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ HTTPResponse::~HTTPResponse() {
3030
if (_responseCache != NULL) {
3131
delete[] _responseCache;
3232
}
33+
_headers.clearAll();
3334
}
3435

3536
void HTTPResponse::setStatusCode(uint16_t statusCode) {

src/HTTPSServerConstants.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
#include "Arduino.h"
55

66
// Debug Code. Undefine it to disable debugging output
7-
#define HTTPS_DLOG(X) Serial.print("HTTPSServer->debug: ");Serial.println(X);
8-
#define HTTPS_DLOGHEX(X,Y) Serial.print("HTTPSServer->debug: ");Serial.print(X);Serial.print(" 0x");Serial.println(Y, HEX);
7+
#define HTTPS_DLOG(X) Serial.print(millis());Serial.print(" HTTPSServer->debug: ");Serial.println(X);
8+
#define HTTPS_DLOGHEX(X,Y) Serial.print(millis());Serial.print(" HTTPSServer->debug: ");Serial.print(X);Serial.print(" 0x");Serial.println(Y, HEX);
9+
#define HTTPS_DLOGINT(X,Y) Serial.print(millis());Serial.print(" HTTPSServer->debug: ");Serial.print(X);Serial.println(Y);
910

1011
// The following lines define limits of the protocol. Exceeding these limits will lead to a 500 error
1112

0 commit comments

Comments
 (0)
0