WebSocket: Remove hixie76 protocol implementation
[WebKit-https.git] / Source / WebCore / Modules / websockets / WebSocketHandshake.cpp
1 /*
2  * Copyright (C) 2011 Google Inc.  All rights reserved.
3  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33
34 #if ENABLE(WEB_SOCKETS)
35
36 #include "WebSocketHandshake.h"
37 #include "WebSocket.h"
38
39 #include "Base64.h"
40 #include "Cookie.h"
41 #include "CookieJar.h"
42 #include "Document.h"
43 #include "HTTPHeaderMap.h"
44 #include "HTTPParsers.h"
45 #include "KURL.h"
46 #include "Logging.h"
47 #include "ScriptCallStack.h"
48 #include "ScriptExecutionContext.h"
49 #include "SecurityOrigin.h"
50 #include <wtf/CryptographicallyRandomNumber.h>
51 #include <wtf/MD5.h>
52 #include <wtf/SHA1.h>
53 #include <wtf/StdLibExtras.h>
54 #include <wtf/StringExtras.h>
55 #include <wtf/Vector.h>
56 #include <wtf/text/CString.h>
57 #include <wtf/text/StringBuilder.h>
58 #include <wtf/text/WTFString.h>
59 #include <wtf/unicode/CharacterNames.h>
60
61 namespace WebCore {
62
63 static const char randomCharacterInSecWebSocketKey[] = "!\"#$%&'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
64
65 static String resourceName(const KURL& url)
66 {
67     String name = url.path();
68     if (name.isEmpty())
69         name = "/";
70     if (!url.query().isNull())
71         name += "?" + url.query();
72     ASSERT(!name.isEmpty());
73     ASSERT(!name.contains(' '));
74     return name;
75 }
76
77 static String hostName(const KURL& url, bool secure)
78 {
79     ASSERT(url.protocolIs("wss") == secure);
80     StringBuilder builder;
81     builder.append(url.host().lower());
82     if (url.port() && ((!secure && url.port() != 80) || (secure && url.port() != 443))) {
83         builder.append(':');
84         builder.append(String::number(url.port()));
85     }
86     return builder.toString();
87 }
88
89 static const size_t maxInputSampleSize = 128;
90 static String trimInputSample(const char* p, size_t len)
91 {
92     String s = String(p, std::min<size_t>(len, maxInputSampleSize));
93     if (len > maxInputSampleSize)
94         s.append(horizontalEllipsis);
95     return s;
96 }
97
98 static String generateSecWebSocketKey()
99 {
100     static const size_t nonceSize = 16;
101     unsigned char key[nonceSize];
102     cryptographicallyRandomValues(key, nonceSize);
103     return base64Encode(reinterpret_cast<char*>(key), nonceSize);
104 }
105
106 String WebSocketHandshake::getExpectedWebSocketAccept(const String& secWebSocketKey)
107 {
108     static const char* const webSocketKeyGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
109     static const size_t sha1HashSize = 20; // FIXME: This should be defined in SHA1.h.
110     SHA1 sha1;
111     CString keyData = secWebSocketKey.ascii();
112     sha1.addBytes(reinterpret_cast<const uint8_t*>(keyData.data()), keyData.length());
113     sha1.addBytes(reinterpret_cast<const uint8_t*>(webSocketKeyGUID), strlen(webSocketKeyGUID));
114     Vector<uint8_t, sha1HashSize> hash;
115     sha1.computeHash(hash);
116     return base64Encode(reinterpret_cast<const char*>(hash.data()), sha1HashSize);
117 }
118
119 WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context)
120     : m_url(url)
121     , m_clientProtocol(protocol)
122     , m_secure(m_url.protocolIs("wss"))
123     , m_context(context)
124     , m_mode(Incomplete)
125 {
126     m_secWebSocketKey = generateSecWebSocketKey();
127     m_expectedAccept = getExpectedWebSocketAccept(m_secWebSocketKey);
128 }
129
130 WebSocketHandshake::~WebSocketHandshake()
131 {
132 }
133
134 const KURL& WebSocketHandshake::url() const
135 {
136     return m_url;
137 }
138
139 void WebSocketHandshake::setURL(const KURL& url)
140 {
141     m_url = url.copy();
142 }
143
144 const String WebSocketHandshake::host() const
145 {
146     return m_url.host().lower();
147 }
148
149 const String& WebSocketHandshake::clientProtocol() const
150 {
151     return m_clientProtocol;
152 }
153
154 void WebSocketHandshake::setClientProtocol(const String& protocol)
155 {
156     m_clientProtocol = protocol;
157 }
158
159 bool WebSocketHandshake::secure() const
160 {
161     return m_secure;
162 }
163
164 String WebSocketHandshake::clientOrigin() const
165 {
166     return m_context->securityOrigin()->toString();
167 }
168
169 String WebSocketHandshake::clientLocation() const
170 {
171     StringBuilder builder;
172     builder.append(m_secure ? "wss" : "ws");
173     builder.append("://");
174     builder.append(hostName(m_url, m_secure));
175     builder.append(resourceName(m_url));
176     return builder.toString();
177 }
178
179 CString WebSocketHandshake::clientHandshakeMessage() const
180 {
181     // Keep the following consistent with clientHandshakeRequest().
182     StringBuilder builder;
183
184     builder.append("GET ");
185     builder.append(resourceName(m_url));
186     builder.append(" HTTP/1.1\r\n");
187
188     Vector<String> fields;
189     fields.append("Upgrade: websocket");
190     fields.append("Connection: Upgrade");
191     fields.append("Host: " + hostName(m_url, m_secure));
192     fields.append("Origin: " + clientOrigin());
193     if (!m_clientProtocol.isEmpty())
194         fields.append("Sec-WebSocket-Protocol: " + m_clientProtocol);
195
196     KURL url = httpURLForAuthenticationAndCookies();
197     if (m_context->isDocument()) {
198         Document* document = static_cast<Document*>(m_context);
199         String cookie = cookieRequestHeaderFieldValue(document, url);
200         if (!cookie.isEmpty())
201             fields.append("Cookie: " + cookie);
202         // Set "Cookie2: <cookie>" if cookies 2 exists for url?
203     }
204
205     fields.append("Sec-WebSocket-Key: " + m_secWebSocketKey);
206     fields.append("Sec-WebSocket-Version: 13");
207     const String extensionValue = m_extensionDispatcher.createHeaderValue();
208     if (extensionValue.length())
209         fields.append("Sec-WebSocket-Extensions: " + extensionValue);
210
211     // Fields in the handshake are sent by the client in a random order; the
212     // order is not meaningful.  Thus, it's ok to send the order we constructed
213     // the fields.
214
215     for (size_t i = 0; i < fields.size(); i++) {
216         builder.append(fields[i]);
217         builder.append("\r\n");
218     }
219
220     builder.append("\r\n");
221
222     return builder.toString().utf8();
223 }
224
225 PassRefPtr<WebSocketHandshakeRequest> WebSocketHandshake::clientHandshakeRequest() const
226 {
227     // Keep the following consistent with clientHandshakeMessage().
228     // FIXME: do we need to store m_secWebSocketKey1, m_secWebSocketKey2 and
229     // m_key3 in WebSocketHandshakeRequest?
230     RefPtr<WebSocketHandshakeRequest> request = WebSocketHandshakeRequest::create("GET", m_url);
231     request->addHeaderField("Upgrade", "websocket");
232     request->addHeaderField("Connection", "Upgrade");
233     request->addHeaderField("Host", hostName(m_url, m_secure));
234     request->addHeaderField("Origin", clientOrigin());
235     if (!m_clientProtocol.isEmpty())
236         request->addHeaderField("Sec-WebSocket-Protocol", m_clientProtocol);
237
238     KURL url = httpURLForAuthenticationAndCookies();
239     if (m_context->isDocument()) {
240         Document* document = static_cast<Document*>(m_context);
241         String cookie = cookieRequestHeaderFieldValue(document, url);
242         if (!cookie.isEmpty())
243             request->addHeaderField("Cookie", cookie);
244         // Set "Cookie2: <cookie>" if cookies 2 exists for url?
245     }
246
247     request->addHeaderField("Sec-WebSocket-Key", m_secWebSocketKey);
248     request->addHeaderField("Sec-WebSocket-Version", "13");
249     const String extensionValue = m_extensionDispatcher.createHeaderValue();
250     if (extensionValue.length())
251         request->addHeaderField("Sec-WebSocket-Extensions", extensionValue);
252
253     return request.release();
254 }
255
256 void WebSocketHandshake::reset()
257 {
258     m_mode = Incomplete;
259     m_extensionDispatcher.reset();
260 }
261
262 void WebSocketHandshake::clearScriptExecutionContext()
263 {
264     m_context = 0;
265 }
266
267 int WebSocketHandshake::readServerHandshake(const char* header, size_t len)
268 {
269     m_mode = Incomplete;
270     int statusCode;
271     String statusText;
272     int lineLength = readStatusLine(header, len, statusCode, statusText);
273     if (lineLength == -1)
274         return -1;
275     if (statusCode == -1) {
276         m_mode = Failed; // m_failureReason is set inside readStatusLine().
277         return len;
278     }
279     LOG(Network, "response code: %d", statusCode);
280     m_response.setStatusCode(statusCode);
281     m_response.setStatusText(statusText);
282     if (statusCode != 101) {
283         m_mode = Failed;
284         m_failureReason = "Unexpected response code: " + String::number(statusCode);
285         return len;
286     }
287     m_mode = Normal;
288     if (!strnstr(header, "\r\n\r\n", len)) {
289         // Just hasn't been received fully yet.
290         m_mode = Incomplete;
291         return -1;
292     }
293     const char* p = readHTTPHeaders(header + lineLength, header + len);
294     if (!p) {
295         LOG(Network, "readHTTPHeaders failed");
296         m_mode = Failed; // m_failureReason is set inside readHTTPHeaders().
297         return len;
298     }
299     if (!checkResponseHeaders()) {
300         LOG(Network, "header process failed");
301         m_mode = Failed;
302         return p - header;
303     }
304
305     m_mode = Connected;
306     return p - header;
307 }
308
309 WebSocketHandshake::Mode WebSocketHandshake::mode() const
310 {
311     return m_mode;
312 }
313
314 String WebSocketHandshake::failureReason() const
315 {
316     return m_failureReason;
317 }
318
319 String WebSocketHandshake::serverWebSocketProtocol() const
320 {
321     return m_response.headerFields().get("sec-websocket-protocol");
322 }
323
324 String WebSocketHandshake::serverSetCookie() const
325 {
326     return m_response.headerFields().get("set-cookie");
327 }
328
329 String WebSocketHandshake::serverSetCookie2() const
330 {
331     return m_response.headerFields().get("set-cookie2");
332 }
333
334 String WebSocketHandshake::serverUpgrade() const
335 {
336     return m_response.headerFields().get("upgrade");
337 }
338
339 String WebSocketHandshake::serverConnection() const
340 {
341     return m_response.headerFields().get("connection");
342 }
343
344 String WebSocketHandshake::serverWebSocketAccept() const
345 {
346     return m_response.headerFields().get("sec-websocket-accept");
347 }
348
349 String WebSocketHandshake::acceptedExtensions() const
350 {
351     return m_extensionDispatcher.acceptedExtensions();
352 }
353
354 const WebSocketHandshakeResponse& WebSocketHandshake::serverHandshakeResponse() const
355 {
356     return m_response;
357 }
358
359 void WebSocketHandshake::addExtensionProcessor(PassOwnPtr<WebSocketExtensionProcessor> processor)
360 {
361     m_extensionDispatcher.addProcessor(processor);
362 }
363
364 KURL WebSocketHandshake::httpURLForAuthenticationAndCookies() const
365 {
366     KURL url = m_url.copy();
367     bool couldSetProtocol = url.setProtocol(m_secure ? "https" : "http");
368     ASSERT_UNUSED(couldSetProtocol, couldSetProtocol);
369     return url;
370 }
371
372 // Returns the header length (including "\r\n"), or -1 if we have not received enough data yet.
373 // If the line is malformed or the status code is not a 3-digit number,
374 // statusCode and statusText will be set to -1 and a null string, respectively.
375 int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength, int& statusCode, String& statusText)
376 {
377     // Arbitrary size limit to prevent the server from sending an unbounded
378     // amount of data with no newlines and forcing us to buffer it all.
379     static const int maximumLength = 1024;
380
381     statusCode = -1;
382     statusText = String();
383
384     const char* space1 = 0;
385     const char* space2 = 0;
386     const char* p;
387     size_t consumedLength;
388
389     for (p = header, consumedLength = 0; consumedLength < headerLength; p++, consumedLength++) {
390         if (*p == ' ') {
391             if (!space1)
392                 space1 = p;
393             else if (!space2)
394                 space2 = p;
395         } else if (*p == '\0') {
396             // The caller isn't prepared to deal with null bytes in status
397             // line. WebSockets specification doesn't prohibit this, but HTTP
398             // does, so we'll just treat this as an error.
399             m_failureReason = "Status line contains embedded null";
400             return p + 1 - header;
401         } else if (*p == '\n')
402             break;
403     }
404     if (consumedLength == headerLength)
405         return -1; // We have not received '\n' yet.
406
407     const char* end = p + 1;
408     int lineLength = end - header;
409     if (lineLength > maximumLength) {
410         m_failureReason = "Status line is too long";
411         return maximumLength;
412     }
413
414     // The line must end with "\r\n".
415     if (lineLength < 2 || *(end - 2) != '\r') {
416         m_failureReason = "Status line does not end with CRLF";
417         return lineLength;
418     }
419
420     if (!space1 || !space2) {
421         m_failureReason = "No response code found: " + trimInputSample(header, lineLength - 2);
422         return lineLength;
423     }
424
425     String statusCodeString(space1 + 1, space2 - space1 - 1);
426     if (statusCodeString.length() != 3) // Status code must consist of three digits.
427         return lineLength;
428     for (int i = 0; i < 3; ++i)
429         if (statusCodeString[i] < '0' || statusCodeString[i] > '9') {
430             m_failureReason = "Invalid status code: " + statusCodeString;
431             return lineLength;
432         }
433
434     bool ok = false;
435     statusCode = statusCodeString.toInt(&ok);
436     ASSERT(ok);
437
438     statusText = String(space2 + 1, end - space2 - 3); // Exclude "\r\n".
439     return lineLength;
440 }
441
442 const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end)
443 {
444     m_response.clearHeaderFields();
445
446     AtomicString name;
447     String value;
448     bool sawSecWebSocketAcceptHeaderField = false;
449     bool sawSecWebSocketProtocolHeaderField = false;
450     const char* p = start;
451     for (; p < end; p++) {
452         size_t consumedLength = parseHTTPHeader(p, end - p, m_failureReason, name, value);
453         if (!consumedLength)
454             return 0;
455         p += consumedLength;
456
457         // Stop once we consumed an empty line.
458         if (name.isEmpty())
459             break;
460
461         // Sec-WebSocket-Extensions may be split. We parse and check the
462         // header value every time the header appears.
463         if (equalIgnoringCase("sec-websocket-extensions", name)) {
464             if (!m_extensionDispatcher.processHeaderValue(value)) {
465                 m_failureReason = m_extensionDispatcher.failureReason();
466                 return 0;
467             }
468         } else if (equalIgnoringCase("Sec-WebSocket-Accept", name)) {
469             if (sawSecWebSocketAcceptHeaderField) {
470                 m_failureReason = "The Sec-WebSocket-Accept header MUST NOT appear more than once in an HTTP response";
471                 return 0;
472             }
473             m_response.addHeaderField(name, value);
474             sawSecWebSocketAcceptHeaderField = true;
475         } else if (equalIgnoringCase("Sec-WebSocket-Protocol", name)) {
476             if (sawSecWebSocketProtocolHeaderField) {
477                 m_failureReason = "The Sec-WebSocket-Protocol header MUST NOT appear more than once in an HTTP response";
478                 return 0;
479             }
480             m_response.addHeaderField(name, value);
481             sawSecWebSocketProtocolHeaderField = true;
482         } else
483             m_response.addHeaderField(name, value);
484     }
485     return p;
486 }
487
488 bool WebSocketHandshake::checkResponseHeaders()
489 {
490     const String& serverWebSocketProtocol = this->serverWebSocketProtocol();
491     const String& serverUpgrade = this->serverUpgrade();
492     const String& serverConnection = this->serverConnection();
493     const String& serverWebSocketAccept = this->serverWebSocketAccept();
494
495     if (serverUpgrade.isNull()) {
496         m_failureReason = "Error during WebSocket handshake: 'Upgrade' header is missing";
497         return false;
498     }
499     if (serverConnection.isNull()) {
500         m_failureReason = "Error during WebSocket handshake: 'Connection' header is missing";
501         return false;
502     }
503     if (serverWebSocketAccept.isNull()) {
504         m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing";
505         return false;
506     }
507
508     if (!equalIgnoringCase(serverUpgrade, "websocket")) {
509         m_failureReason = "Error during WebSocket handshake: 'Upgrade' header value is not 'WebSocket'";
510         return false;
511     }
512     if (!equalIgnoringCase(serverConnection, "upgrade")) {
513         m_failureReason = "Error during WebSocket handshake: 'Connection' header value is not 'Upgrade'";
514         return false;
515     }
516
517     if (serverWebSocketAccept != m_expectedAccept) {
518         m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Accept mismatch";
519         return false;
520     }
521     if (!serverWebSocketProtocol.isNull()) {
522         if (m_clientProtocol.isEmpty()) {
523             m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch";
524             return false;
525         }
526         Vector<String> result;
527         m_clientProtocol.split(String(WebSocket::subProtocolSeperator()), result);
528         if (!result.contains(serverWebSocketProtocol)) {
529             m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch";
530             return false;
531         }
532     }
533     return true;
534 }
535
536 } // namespace WebCore
537
538 #endif // ENABLE(WEB_SOCKETS)