@@ -17,7 +17,7 @@ HTTPConnection::HTTPConnection(ResourceResolver * resResolver):
1717 _isKeepAlive = false ;
1818 _lastTransmissionTS = millis ();
1919 _shutdownTS = 0 ;
20- _websocket = nullptr ;
20+ _wsHandler = nullptr ;
2121}
2222
2323HTTPConnection::~HTTPConnection () {
@@ -125,6 +125,11 @@ void HTTPConnection::closeConnection() {
125125 delete _httpHeaders;
126126 _httpHeaders = NULL ;
127127 }
128+
129+ if (_wsHandler != nullptr ) {
130+ HTTPS_DLOG (" [ ] Freeing WS Handler" );
131+ delete _wsHandler;
132+ }
128133}
129134
130135/* *
@@ -417,48 +422,29 @@ void HTTPConnection::loop() {
417422 {
418423 HTTPS_DLOG (" [ ] Resolving resource..." );
419424 ResolvedResource resolvedResource;
420- _resResolver->resolveNode (_httpMethod, _httpResource, resolvedResource);
425+
426+ // Check which kind of node we need (Websocket or regular)
427+ bool websocketRequested = checkWebsocket ();
428+
429+ _resResolver->resolveNode (_httpMethod, _httpResource, resolvedResource, websocketRequested ? WEBSOCKET : HANDLER_CALLBACK);
430+
431+ // Is there any match (may be the defaultNode, if it is configured)
421432 if (resolvedResource.didMatch ()) {
422- // Did the client request connection:keep-alive?
423- HTTPHeader * connectionHeader = _httpHeaders->get (" Connection" );
424- if (connectionHeader != NULL && std::string (" keep-alive" ).compare (connectionHeader->_value )==0 ) {
425- HTTPS_DLOGHEX (" [ ] Keep-Alive activated. fid=" , _socket);
426- _isKeepAlive = true ;
433+ // Check for client's request to keep-alive if we have a handler function.
434+ if (resolvedResource.getMatchingNode ()->_nodeType == HANDLER_CALLBACK) {
435+ // Did the client set connection:keep-alive?
436+ HTTPHeader * connectionHeader = _httpHeaders->get (" Connection" );
437+ if (connectionHeader != NULL && std::string (" keep-alive" ).compare (connectionHeader->_value )==0 ) {
438+ HTTPS_DLOGHEX (" [ ] Keep-Alive activated. fid=" , _socket);
439+ _isKeepAlive = true ;
440+ } else {
441+ HTTPS_DLOGHEX (" [ ] Keep-Alive disabled. fid=" , _socket);
442+ _isKeepAlive = false ;
443+ }
427444 } else {
428- HTTPS_DLOGHEX (" [ ] Keep-Alive disabled. fid=" , _socket);
429445 _isKeepAlive = false ;
430446 }
431447
432- // do we have a websocket connection?
433- if (checkWebsocket ()) {
434- // Create response
435- HTTPResponse res = HTTPResponse (this );
436- // Add default headers to the response
437- auto allDefaultHeaders = _defaultHeaders->getAll ();
438- for (std::vector<HTTPHeader*>::iterator header = allDefaultHeaders->begin (); header != allDefaultHeaders->end (); ++header) {
439- res.setHeader ((*header)->_name , (*header)->_value );
440- }
441- // Set the response status
442- res.setStatusCode (101 );
443- res.setStatusText (" Switching Protocols" );
444- res.setHeader (" Upgrade" , " websocket" );
445- res.setHeader (" Connection" , " Upgrade" );
446- res.setHeader (" Sec-WebSocket-Accept" , websocketKeyResponseHash (_httpHeaders->getValue (" Sec-WebSocket-Key" )));
447- res.print (" " );
448-
449- // Callback for the actual resource
450- HTTPSCallbackFunction * resourceCallback = resolvedResource.getMatchingNode ()->_callback ;
451- // persistent request for websocket. To be destroyed when connection closes.
452- HTTPRequest *req = new HTTPRequest (this , _httpHeaders, resolvedResource.getParams (), _httpResource, _httpMethod, " " );
453- // bind function call to the actual resource
454- std::function<void ()> next = std::function<void ()>(std::bind (resourceCallback, req, nullptr ));
455- _websocket = new Websocket (this ); // make websocket with this connection
456- next (); // call callback
457- delete req;
458- _connectionState = STATE_WEBSOCKET;
459- break ;
460- }
461-
462448 // Create request context
463449 HTTPRequest req = HTTPRequest (this , _httpHeaders, resolvedResource.getParams (), _httpResource, _httpMethod, resolvedResource.getMatchingNode ()->_tag );
464450 HTTPResponse res = HTTPResponse (this );
@@ -469,8 +455,15 @@ void HTTPConnection::loop() {
469455 res.setHeader ((*header)->_name , (*header)->_value );
470456 }
471457
472- // Callback for the actual resource
473- HTTPSCallbackFunction * resourceCallback = resolvedResource.getMatchingNode ()->_callback ;
458+ // Find the request handler callback
459+ HTTPSCallbackFunction * resourceCallback;
460+ if (websocketRequested) {
461+ // For the websocket, we use the handshake callback defined below
462+ resourceCallback = &handleWebsocketHandshake;
463+ } else {
464+ // For resource nodes, we use the callback defined by the node itself
465+ resourceCallback = ((ResourceNode*)resolvedResource.getMatchingNode ())->_callback ;
466+ }
474467
475468 // Get the current middleware chain
476469 auto vecMw = _resResolver->getMiddleware ();
@@ -495,37 +488,45 @@ void HTTPConnection::loop() {
495488 req.discardRequestBody ();
496489 }
497490
498- // Handling the request is done
499- HTTPS_DLOG (" [ ] Handler function done, request complete" );
500-
501- // Now we need to check if we can use keep-alive to reuse the SSL connection
502- // However, if the client did not set content-size or defined connection: close,
503- // we have no chance to do so.
504- if (!_isKeepAlive) {
505- // No KeepAlive -> We are done. Transition to next state.
506- if (!isClosed ()) {
507- _connectionState = STATE_BODY_FINISHED;
508- }
491+ // Finally, after the handshake is done, we create the WebsocketHandler and change the internal state.
492+ if (websocketRequested) {
493+ _wsHandler = ((WebsocketNode*)resolvedResource.getMatchingNode ())->newHandler ();
494+ _wsHandler->initialize (this ); // make websocket with this connection
495+ _connectionState = STATE_WEBSOCKET;
509496 } else {
510- if (res.isResponseBuffered ()) {
511- // If the response could be buffered:
512- res.setHeader (" Connection" , " keep-alive" );
513- res.finalize ();
514- if (_clientState != CSTATE_CLOSED) {
515- // Refresh the timeout for the new request
516- refreshTimeout ();
517- // Reset headers for the new connection
518- _httpHeaders->clearAll ();
519- // Go back to initial state
520- _connectionState = STATE_INITIAL;
497+ // Handling the request is done
498+ HTTPS_DLOG (" [ ] Handler function done, request complete" );
499+
500+ // Now we need to check if we can use keep-alive to reuse the SSL connection
501+ // However, if the client did not set content-size or defined connection: close,
502+ // we have no chance to do so.
503+ if (!_isKeepAlive) {
504+ // No KeepAlive -> We are done. Transition to next state.
505+ if (!isClosed ()) {
506+ _connectionState = STATE_BODY_FINISHED;
507+ }
508+ } else {
509+ if (res.isResponseBuffered ()) {
510+ // If the response could be buffered:
511+ res.setHeader (" Connection" , " keep-alive" );
512+ res.finalize ();
513+ if (_clientState != CSTATE_CLOSED) {
514+ // Refresh the timeout for the new request
515+ refreshTimeout ();
516+ // Reset headers for the new connection
517+ _httpHeaders->clearAll ();
518+ // Go back to initial state
519+ _connectionState = STATE_INITIAL;
520+ }
521+ }
522+ // The response could not be buffered or the client has closed:
523+ if (!isClosed () && _connectionState!=STATE_INITIAL) {
524+ _connectionState = STATE_BODY_FINISHED;
521525 }
522- }
523- // The response could not be buffered or the client has closed:
524- if (!isClosed () && _connectionState!=STATE_INITIAL) {
525- _connectionState = STATE_BODY_FINISHED;
526526 }
527527 }
528528 } else {
529+ // No match (no default route configured, nothing does match)
529530 HTTPS_DLOG (" [ERR] Could not find a matching resource. Server error." );
530531 serverError ();
531532 }
@@ -542,14 +543,15 @@ void HTTPConnection::loop() {
542543 refreshTimeout (); // don't timeout websocket connection
543544 if (pendingBufferSize () > 0 ) {
544545 HTTPS_DLOG (" [ ] websocket handler" );
545- if (_websocket->read () < 0 ) {
546- _websocket->close ();
547- delete _websocket;
548- _httpHeaders->clearAll ();
549- _connectionState = STATE_CLOSING;
550- }
546+ _wsHandler->loop ();
547+ }
548+ // If the handler has terminated the connection, clean up and close the socket too
549+ if (_wsHandler->closed ()) {
550+ HTTPS_DLOG (" [ ] WS Connection closed. Freeing WS Handler" );
551+ delete _wsHandler;
552+ _wsHandler = nullptr ;
553+ _connectionState = STATE_CLOSING;
551554 }
552- // readBuffer();
553555 break ;
554556 default :;
555557 }
@@ -579,7 +581,22 @@ bool HTTPConnection::checkWebsocket() {
579581 return false ;
580582}
581583
582- std::string HTTPConnection::websocketKeyResponseHash (std::string key) {
584+ /* *
585+ * Handler function for the websocket handshake. Will be used by HTTPConnection if a websocket is detected
586+ */
587+ void handleWebsocketHandshake (HTTPRequest * req, HTTPResponse * res) {
588+ res->setStatusCode (101 );
589+ res->setStatusText (" Switching Protocols" );
590+ res->setHeader (" Upgrade" , " websocket" );
591+ res->setHeader (" Connection" , " Upgrade" );
592+ res->setHeader (" Sec-WebSocket-Accept" , websocketKeyResponseHash (req->getHeader (" Sec-WebSocket-Key" )));
593+ res->print (" " );
594+ }
595+
596+ /* *
597+ * Function used to compute the value of the Sec-WebSocket-Accept during Websocket handshake
598+ */
599+ std::string websocketKeyResponseHash (std::string key) {
583600 std::string newKey = key + " 258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ;
584601 uint8_t shaData[HTTPS_SHA1_LENGTH];
585602 esp_sha (SHA1, (uint8_t *)newKey.data (), newKey.length (), shaData);
0 commit comments