| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,6 +9,8 @@ | |||
| 9 | 9 | ||
| 10 | 10 | namespace httpsserver { | |
| 11 | 11 | ||
| 12 | + class WebsocketHandler; | ||
| 13 | + | ||
| 12 | 14 | class ConnectionContext { | |
| 13 | 15 | public: | |
| 14 | 16 | ConnectionContext(); | |
@@ -24,6 +26,7 @@ class ConnectionContext { | |||
| 24 | 26 | virtual size_t writeBuffer(byte* buffer, size_t length) = 0; | |
| 25 | 27 | ||
| 26 | 28 | virtual bool isSecure() = 0; | |
| 29 | + virtual void setWebsocketHandler(WebsocketHandler *wsHandler) = 0; | ||
| 27 | 30 | }; | |
| 28 | 31 | ||
| 29 | 32 | } /* namespace httpsserver */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,6 @@ | |||
| 1 | 1 | #include "HTTPConnection.hpp" | |
| 2 | + #include "Websocket.hpp" | ||
| 3 | + #include <hwcrypto/sha.h> | ||
| 2 | 4 | ||
| 3 | 5 | namespace httpsserver { | |
| 4 | 6 | ||
@@ -18,6 +20,7 @@ HTTPConnection::HTTPConnection(ResourceResolver * resResolver): | |||
| 18 | 20 | _isKeepAlive = false; | |
| 19 | 21 | _lastTransmissionTS = millis(); | |
| 20 | 22 | _shutdownTS = 0; | |
| 23 | + _websocket = nullptr; | ||
| 21 | 24 | } | |
| 22 | 25 | ||
| 23 | 26 | HTTPConnection::~HTTPConnection() { | |
@@ -121,6 +124,7 @@ void HTTPConnection::closeConnection() { | |||
| 121 | 124 | } | |
| 122 | 125 | ||
| 123 | 126 | if (_httpHeaders != NULL) { | |
| 127 | + HTTPS_DLOG("[ ] Free headers"); | ||
| 124 | 128 | delete _httpHeaders; | |
| 125 | 129 | _httpHeaders = NULL; | |
| 126 | 130 | } | |
@@ -327,6 +331,10 @@ size_t HTTPConnection::getCacheSize() { | |||
| 327 | 331 | return (_isKeepAlive ? HTTPS_KEEPALIVE_CACHESIZE : 0); | |
| 328 | 332 | } | |
| 329 | 333 | ||
| 334 | + void HTTPConnection::setWebsocketHandler(WebsocketHandler *wsHandler) { | ||
| 335 | + _wsHandler = wsHandler; | ||
| 336 | + } | ||
| 337 | + | ||
| 330 | 338 | void HTTPConnection::loop() { | |
| 331 | 339 | // First, update the buffer | |
| 332 | 340 | // newByteCount will contain the number of new bytes that have to be processed | |
@@ -397,6 +405,7 @@ void HTTPConnection::loop() { | |||
| 397 | 405 | _parserLine.text.substr(0, idxColon), | |
| 398 | 406 | _parserLine.text.substr(idxColon+2) | |
| 399 | 407 | )); | |
| 408 | + HTTPS_DLOG(("[ ] Header: " + _parserLine.text.substr(0, idxColon) + ":" + _parserLine.text.substr(idxColon+2)).c_str()); | ||
| 400 | 409 | } else { | |
| 401 | 410 | HTTPS_DLOG("Malformed header line detected. Client error."); | |
| 402 | 411 | HTTPS_DLOG(_parserLine.text.c_str()); | |
@@ -427,6 +436,36 @@ void HTTPConnection::loop() { | |||
| 427 | 436 | _isKeepAlive = false; | |
| 428 | 437 | } | |
| 429 | 438 | ||
| 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 | + | ||
| 430 | 469 | // Create request context | |
| 431 | 470 | HTTPRequest req = HTTPRequest(this, _httpHeaders, resolvedResource.getParams(), _httpResource, _httpMethod, resolvedResource.getMatchingNode()->_tag); | |
| 432 | 471 | HTTPResponse res = HTTPResponse(this); | |
@@ -507,12 +546,54 @@ void HTTPConnection::loop() { | |||
| 507 | 546 | closeConnection(); | |
| 508 | 547 | break; | |
| 509 | 548 | 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(); | ||
| 511 | 560 | break; | |
| 512 | 561 | default:; | |
| 513 | 562 | } | |
| 514 | 563 | } | |
| 515 | 564 | ||
| 516 | 565 | } | |
| 517 | 566 | ||
| 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 | + | ||
| 518 | 599 | } /* namespace httpsserver */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,10 +19,14 @@ | |||
| 19 | 19 | #include "ResourceNode.hpp" | |
| 20 | 20 | #include "HTTPRequest.hpp" | |
| 21 | 21 | #include "HTTPResponse.hpp" | |
| 22 | + #include "ConnectionContext.hpp" | ||
| 22 | 23 | ||
| 23 | 24 | namespace httpsserver { | |
| 24 | 25 | ||
| 26 | + class Websocket; | ||
| 27 | + | ||
| 25 | 28 | class HTTPConnection : private ConnectionContext { | |
| 29 | + friend class Websocket; | ||
| 26 | 30 | public: | |
| 27 | 31 | HTTPConnection(ResourceResolver * resResolver); | |
| 28 | 32 | virtual ~HTTPConnection(); | |
@@ -34,10 +38,12 @@ class HTTPConnection : private ConnectionContext { | |||
| 34 | 38 | void loop(); | |
| 35 | 39 | bool isClosed(); | |
| 36 | 40 | bool isError(); | |
| 41 | + void setWebsocketHandler(WebsocketHandler *wsHandler); | ||
| 37 | 42 | ||
| 38 | 43 | protected: | |
| 39 | 44 | friend class HTTPRequest; | |
| 40 | 45 | friend class HTTPResponse; | |
| 46 | + friend class WebsocketInputStreambuf; | ||
| 41 | 47 | ||
| 42 | 48 | virtual size_t writeBuffer(byte* buffer, size_t length); | |
| 43 | 49 | virtual size_t readBytesToBuffer(byte* buffer, size_t length); | |
@@ -108,7 +114,8 @@ class HTTPConnection : private ConnectionContext { | |||
| 108 | 114 | void signalRequestError(); | |
| 109 | 115 | size_t readBuffer(byte* buffer, size_t length); | |
| 110 | 116 | size_t getCacheSize(); | |
| 111 | - | ||
| 117 | + bool checkWebsocket(); | ||
| 118 | + std::string websocketKeyResponseHash(std::string key); | ||
| 112 | 119 | ||
| 113 | 120 | // The receive buffer | |
| 114 | 121 | char _receiveBuffer[HTTPS_CONNECTION_DATA_CHUNK_SIZE]; | |
@@ -143,6 +150,9 @@ class HTTPConnection : private ConnectionContext { | |||
| 143 | 150 | // Should we use keep alive | |
| 144 | 151 | bool _isKeepAlive; | |
| 145 | 152 | ||
| 153 | + //Websocket connection | ||
| 154 | + WebsocketHandler * _wsHandler; | ||
| 155 | + Websocket * _websocket; | ||
| 146 | 156 | ||
| 147 | 157 | }; | |
| 148 | 158 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,6 +21,16 @@ HTTPHeader * HTTPHeaders::get(const std::string name) { | |||
| 21 | 21 | return NULL; | |
| 22 | 22 | } | |
| 23 | 23 | ||
| 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 | + | ||
| 24 | 34 | void HTTPHeaders::set(HTTPHeader * header) { | |
| 25 | 35 | for(int i = 0; i < _headers->size(); i++) { | |
| 26 | 36 | if ((*_headers)[i]->_name.compare(header->_name)==0) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,6 +18,7 @@ class HTTPHeaders { | |||
| 18 | 18 | virtual ~HTTPHeaders(); | |
| 19 | 19 | ||
| 20 | 20 | HTTPHeader * get(std::string name); | |
| 21 | + std::string getValue(std::string name); | ||
| 21 | 22 | void set(HTTPHeader * header); | |
| 22 | 23 | ||
| 23 | 24 | std::vector<HTTPHeader *> * getAll(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,6 +8,7 @@ | |||
| 8 | 8 | #include "HTTPSCallbackFunction.hpp" | |
| 9 | 9 | ||
| 10 | 10 | namespace httpsserver { | |
| 11 | + class HTTPRequest; | ||
| 11 | 12 | /** | |
| 12 | 13 | * A middleware function that can be registered at the server. | |
| 13 | 14 | * | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,7 +22,7 @@ HTTPRequest::HTTPRequest(ConnectionContext * con, HTTPHeaders * headers, Resourc | |||
| 22 | 22 | } | |
| 23 | 23 | ||
| 24 | 24 | HTTPRequest::~HTTPRequest() { | |
| 25 | - | ||
| 25 | + _headers->clearAll(); | ||
| 26 | 26 | } | |
| 27 | 27 | ||
| 28 | 28 | ||
@@ -156,4 +156,9 @@ bool HTTPRequest::isSecure() { | |||
| 156 | 156 | return _con->isSecure(); | |
| 157 | 157 | } | |
| 158 | 158 | ||
| 159 | + | ||
| 160 | + void HTTPRequest::setWebsocketHandler(WebsocketHandler *wsHandler) { | ||
| 161 | + _con->setWebsocketHandler(wsHandler); | ||
| 162 | + } | ||
| 163 | + | ||
| 159 | 164 | } /* namespace httpsserver */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -34,6 +34,8 @@ class HTTPRequest { | |||
| 34 | 34 | std::string getBasicAuthUser(); | |
| 35 | 35 | std::string getBasicAuthPassword(); | |
| 36 | 36 | bool isSecure(); | |
| 37 | + void setWebsocketHandler(WebsocketHandler *wsHandler); | ||
| 38 | + | ||
| 37 | 39 | private: | |
| 38 | 40 | std::string decodeBasicAuthToken(); | |
| 39 | 41 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -30,6 +30,7 @@ HTTPResponse::~HTTPResponse() { | |||
| 30 | 30 | if (_responseCache != NULL) { | |
| 31 | 31 | delete[] _responseCache; | |
| 32 | 32 | } | |
| 33 | + _headers.clearAll(); | ||
| 33 | 34 | } | |
| 34 | 35 | ||
| 35 | 36 | void HTTPResponse::setStatusCode(uint16_t statusCode) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,8 +4,9 @@ | |||
| 4 | 4 | #include "Arduino.h" | |
| 5 | 5 | ||
| 6 | 6 | // 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); | ||
| 9 | 10 | ||
| 10 | 11 | // The following lines define limits of the protocol. Exceeding these limits will lead to a 500 error | |
| 11 | 12 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments