| 113 | }; |
| 114 | |
| 115 | function callback(message, value) { |
| 116 | let socket = this.socket; |
| 117 | |
| 118 | if (1 == message) { // connected |
| 119 | if (0 != this.state) |
| 120 | throw new Error("socket connected but ws not in connecting state"); |
| 121 | |
| 122 | this.callback(Client.connect); // connected socket |
| 123 | if (4 === this.state) |
| 124 | return; |
| 125 | |
| 126 | let key = new Uint8Array(16); |
| 127 | for (let i = 0; i < 16; i++) |
| 128 | key[i] = (Math.random() * 256) | 0 |
| 129 | |
| 130 | let response = [ |
| 131 | "GET ", this.path, " HTTP/1.1\r\n", |
| 132 | "Host: ", this.host, "\r\n", |
| 133 | "Upgrade: websocket\r\n", |
| 134 | "Connection: keep-alive, Upgrade\r\n", |
| 135 | "Sec-WebSocket-Version: 13\r\n", |
| 136 | "Sec-WebSocket-Key: ", key.toBase64() + "\r\n", |
| 137 | ]; |
| 138 | |
| 139 | if (this.protocol) |
| 140 | response.push(`Sec-WebSocket-Protocol: ${this.protocol}\r\n`); |
| 141 | |
| 142 | let hdr = undefined; |
| 143 | if (this.headers) for (let w of this.headers) { |
| 144 | if (hdr === undefined) { |
| 145 | hdr = w; |
| 146 | } else { |
| 147 | response.push(`${hdr}: ${w}\r\n`); |
| 148 | hdr = undefined; |
| 149 | } |
| 150 | } |
| 151 | if (hdr != undefined) |
| 152 | throw new Error("invalid header array: need a value for every header"); |
| 153 | |
| 154 | response.push("\r\n"); |
| 155 | socket.write.apply(socket, response); |
| 156 | |
| 157 | delete this.path; |
| 158 | delete this.host; |
| 159 | delete this.headers; |
| 160 | delete this.protocol; |
| 161 | |
| 162 | this.state = 1; |
| 163 | } |
| 164 | |
| 165 | if (2 == message) { // data available to read |
| 166 | if (1 === this.state) { |
| 167 | let line = socket.read(String, "\n"); |
| 168 | if ("HTTP/1.1 101" !== line.substring(0,12)) { |
| 169 | trace("web socket upgrade failed\n"); |
| 170 | this.callback(Client.disconnect); |
| 171 | this.close(); |
| 172 | return; |