| 1 |  |  | <?php | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | namespace Helix\Socket\WebSocket; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | use Throwable; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  |  * Initial WebSocket connection handshake. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  |  * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  |  * https://tools.ietf.org/html/rfc6455#section-1.3 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  |  */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | class Handshake { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  |     const RFC_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |      * @var string | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  |     protected $buffer = ''; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |      * @var WebSocketClient | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  |     protected $client; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  |      * @var string[] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  |     protected $headers = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  |      * The connection is closed (HTTP 413) if the received headers exceed this many bytes. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  |      * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  |      * @var int | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  |     protected $maxLength = 4096; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |      * @var string | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |     protected $method; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  |      * @param WebSocketClient $client | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 45 |  |  |      */ | 
            
                                                                        
                            
            
                                    
            
            
                | 46 |  |  |     public function __construct (WebSocketClient $client) { | 
            
                                                                        
                            
            
                                    
            
            
                | 47 |  |  |         $this->client = $client; | 
            
                                                                        
                            
            
                                    
            
            
                | 48 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  |      * @return string[] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |     public function getHeaders () { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  |         return $this->headers; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  |      * @return string | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  |     public function getMethod (): string { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |         return $this->method; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  |      * Negotiates the initial connection. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  |      * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  |      * @return bool | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  |      * @throws WebSocketError | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 |  |  |      * @throws Throwable | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 |  |  |     public function onReadable (): bool { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  |         // read into the buffer | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 |  |  |         $this->buffer .= $bytes = $this->client->recvAll(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  |         // check for peer disconnection | 
            
                                                                                                            
                            
            
                                    
            
            
                | 76 |  |  |         if (!strlen($bytes)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 |  |  |             $this->client->close(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 |  |  |             return false; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |         // read frames from the buffer and yield | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  |         try { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 |  |  |             // length check | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  |             if (strlen($this->buffer) > $this->maxLength) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  |                 throw new WebSocketError(413, "{$this->client} exceeded the maximum handshake size."); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 |  |  |             } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  |             // still reading? | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 |  |  |             if (false === $end = strpos($this->buffer, "\r\n\r\n")) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  |                 return false; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |             } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  |             // parse the headers | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |             $head = explode("\r\n", substr($this->buffer, 0, $end)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  |             $this->method = array_shift($head); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  |             foreach ($head as $header) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  |                 $header = explode(':', $header, 2); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  |                 if (count($header) !== 2) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  |                     throw new WebSocketError(400, "{$this->client} sent a malformed header."); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  |                 [$key, $value] = $header; | 
                            
                    |  |  |  | 
                                                                                        
                                                                                            
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  |                 $key = strtolower(trim($key)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 |  |  |                 $value = trim($value); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 |  |  |                 if (isset($this->headers[$key])) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  |                     $this->headers[$key] .= ', ' . $value; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  |                 else { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 |  |  |                     $this->headers[$key] = $value; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 107 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 |  |  |             } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 |  |  |             $this->buffer = ''; // wipe the buffer | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  |             $this->validate(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  |         catch (WebSocketError $e) { // catch and respond with HTTP error and rethrow | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  |             $this->client->write("HTTP/1.1 {$e->getCode()} WebSocket Handshake Failure\r\n\r\n"); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  |             throw $e; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  |         catch (Throwable $e) { // catch everything else and respond with HTTP 500 and rethrow | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  |             $this->client->write("HTTP/1.1 500 WebSocket Internal Error\r\n\r\n"); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 |  |  |             throw $e; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 |  |  |         // send upgrade headers | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 |  |  |         $this->upgrade(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 |  |  |         $this->client->write("\r\n\r\n"); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  |         // success | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  |         return true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  |      * Sends the connection upgrade headers. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 132 |  |  |     protected function upgrade (): void { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 133 |  |  |         $key = base64_encode(sha1($this->headers['sec-websocket-key'] . self::RFC_GUID, true)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 134 |  |  |         $this->client->write(implode("\r\n", [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 135 |  |  |             "HTTP/1.1 101 Switching Protocols", | 
            
                                                                                                            
                            
            
                                    
            
            
                | 136 |  |  |             "Connection: Upgrade", | 
            
                                                                                                            
                            
            
                                    
            
            
                | 137 |  |  |             "Upgrade: websocket", | 
            
                                                                                                            
                            
            
                                    
            
            
                | 138 |  |  |             "Sec-WebSocket-Accept: {$key}" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 139 |  |  |         ])); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 140 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 141 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 |  |  |      * Validates the received HTTP handshake headers, or throws. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 |  |  |      * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  |      * @throws WebSocketError | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 |  |  |     protected function validate (): void { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 |  |  |         if (!( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 |  |  |             $check = 'method = http 1.1' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  |             and preg_match('/HTTP\/1\.1$/i', $this->method) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 |  |  |             and $check = 'connection = upgrade' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 |  |  |             and preg_match('/^upgrade$/i', $this->headers['connection'] ?? '') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 |  |  |             and $check = 'upgrade = websocket' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 154 |  |  |             and preg_match('/^websocket$/i', $this->headers['upgrade'] ?? '') | 
            
                                                                                                            
                            
            
                                    
            
            
                | 155 |  |  |             and $check = 'version = 13' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 156 |  |  |             and ($this->headers['sec-websocket-version'] ?? '') === '13' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 157 |  |  |             and $check = 'key length = 16' | 
            
                                                                                                            
                            
            
                                    
            
            
                | 158 |  |  |             and strlen(base64_decode($this->headers['sec-websocket-key'] ?? '')) === 16 | 
            
                                                                                                            
                            
            
                                    
            
            
                | 159 |  |  |         )) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 160 |  |  |             throw new WebSocketError(400, "Handshake with {$this->client} failed on validation: {$check}"); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 161 |  |  |         } | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 162 |  |  |     } | 
            
                                                        
            
                                    
            
            
                | 163 |  |  | } | 
            
                        
If you define a variable conditionally, it can happen that it is not defined for all execution paths.
Let’s take a look at an example:
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.
Available Fixes
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: