[WebSocket]Browser must fail connection if Sec-WebSocket-Protocol mismatched.
[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 "KURL.h"
45 #include "Logging.h"
46 #include "ScriptCallStack.h"
47 #include "ScriptExecutionContext.h"
48 #include "SecurityOrigin.h"
49 #include <wtf/CryptographicallyRandomNumber.h>
50 #include <wtf/MD5.h>
51 #include <wtf/SHA1.h>
52 #include <wtf/StdLibExtras.h>
53 #include <wtf/StringExtras.h>
54 #include <wtf/Vector.h>
55 #include <wtf/text/CString.h>
56 #include <wtf/text/StringBuilder.h>
57 #include <wtf/text/WTFString.h>
58 #include <wtf/unicode/CharacterNames.h>
59
60 namespace WebCore {
61
62 static const char randomCharacterInSecWebSocketKey[] = "!\"#$%&'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
63
64 static String resourceName(const KURL& url)
65 {
66     String name = url.path();
67     if (name.isEmpty())
68         name = "/";
69     if (!url.query().isNull())
70         name += "?" + url.query();
71     ASSERT(!name.isEmpty());
72     ASSERT(!name.contains(' '));
73     return name;
74 }
75
76 static String hostName(const KURL& url, bool secure)
77 {
78     ASSERT(url.protocolIs("wss") == secure);
79     StringBuilder builder;
80     builder.append(url.host().lower());
81     if (url.port() && ((!secure && url.port() != 80) || (secure && url.port() != 443))) {
82         builder.append(':');
83         builder.append(String::number(url.port()));
84     }
85     return builder.toString();
86 }
87
88 static const size_t maxConsoleMessageSize = 128;
89 static String trimConsoleMessage(const char* p, size_t len)
90 {
91     String s = String(p, std::min<size_t>(len, maxConsoleMessageSize));
92     if (len > maxConsoleMessageSize)
93         s.append(horizontalEllipsis);
94     return s;
95 }
96
97 static uint32_t randomNumberLessThan(uint32_t n)
98 {
99     if (!n)
100         return 0;
101     if (n == std::numeric_limits<uint32_t>::max())
102         return cryptographicallyRandomNumber();
103     uint32_t max = std::numeric_limits<uint32_t>::max() - (std::numeric_limits<uint32_t>::max() % n);
104     ASSERT(!(max % n));
105     uint32_t v;
106     do {
107         v = cryptographicallyRandomNumber();
108     } while (v >= max);
109     return v % n;
110 }
111
112 static void generateHixie76SecWebSocketKey(uint32_t& number, String& key)
113 {
114     uint32_t space = randomNumberLessThan(12) + 1;
115     uint32_t max = 4294967295U / space;
116     number = randomNumberLessThan(max);
117     uint32_t product = number * space;
118
119     String s = String::number(product);
120     int n = randomNumberLessThan(12) + 1;
121     DEFINE_STATIC_LOCAL(String, randomChars, (randomCharacterInSecWebSocketKey));
122     for (int i = 0; i < n; i++) {
123         int pos = randomNumberLessThan(s.length() + 1);
124         int chpos = randomNumberLessThan(randomChars.length());
125         s.insert(randomChars.substring(chpos, 1), pos);
126     }
127     DEFINE_STATIC_LOCAL(String, spaceChar, (" "));
128     for (uint32_t i = 0; i < space; i++) {
129         int pos = randomNumberLessThan(s.length() - 1) + 1;
130         s.insert(spaceChar, pos);
131     }
132     ASSERT(s[0] != ' ');
133     ASSERT(s[s.length() - 1] != ' ');
134     key = s;
135 }
136
137 static void generateHixie76Key3(unsigned char key3[8])
138 {
139     cryptographicallyRandomValues(key3, 8);
140 }
141
142 static void setChallengeNumber(unsigned char* buf, uint32_t number)
143 {
144     unsigned char* p = buf + 3;
145     for (int i = 0; i < 4; i++) {
146         *p = number & 0xFF;
147         --p;
148         number >>= 8;
149     }
150 }
151
152 static void generateHixie76ExpectedChallengeResponse(uint32_t number1, uint32_t number2, unsigned char key3[8], unsigned char expectedChallenge[16])
153 {
154     unsigned char challenge[16];
155     setChallengeNumber(&challenge[0], number1);
156     setChallengeNumber(&challenge[4], number2);
157     memcpy(&challenge[8], key3, 8);
158     MD5 md5;
159     md5.addBytes(challenge, sizeof(challenge));
160     Vector<uint8_t, 16> digest;
161     md5.checksum(digest);
162     memcpy(expectedChallenge, digest.data(), 16);
163 }
164
165 static String generateSecWebSocketKey()
166 {
167     static const size_t nonceSize = 16;
168     unsigned char key[nonceSize];
169     cryptographicallyRandomValues(key, nonceSize);
170     return base64Encode(reinterpret_cast<char*>(key), nonceSize);
171 }
172
173 static String getExpectedWebSocketAccept(const String& secWebSocketKey)
174 {
175     static const char* const webSocketKeyGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
176     static const size_t sha1HashSize = 20; // FIXME: This should be defined in SHA1.h.
177     SHA1 sha1;
178     CString keyData = secWebSocketKey.ascii();
179     sha1.addBytes(reinterpret_cast<const uint8_t*>(keyData.data()), keyData.length());
180     sha1.addBytes(reinterpret_cast<const uint8_t*>(webSocketKeyGUID), strlen(webSocketKeyGUID));
181     Vector<uint8_t, sha1HashSize> hash;
182     sha1.computeHash(hash);
183     return base64Encode(reinterpret_cast<const char*>(hash.data()), sha1HashSize);
184 }
185
186 WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context, bool useHixie76Protocol)
187     : m_url(url)
188     , m_clientProtocol(protocol)
189     , m_secure(m_url.protocolIs("wss"))
190     , m_context(context)
191     , m_useHixie76Protocol(useHixie76Protocol)
192     , m_mode(Incomplete)
193 {
194     if (m_useHixie76Protocol) {
195         uint32_t number1;
196         uint32_t number2;
197         generateHixie76SecWebSocketKey(number1, m_hixie76SecWebSocketKey1);
198         generateHixie76SecWebSocketKey(number2, m_hixie76SecWebSocketKey2);
199         generateHixie76Key3(m_hixie76Key3);
200         generateHixie76ExpectedChallengeResponse(number1, number2, m_hixie76Key3, m_hixie76ExpectedChallengeResponse);
201     } else {
202         m_secWebSocketKey = generateSecWebSocketKey();
203         m_expectedAccept = getExpectedWebSocketAccept(m_secWebSocketKey);
204     }
205 }
206
207 WebSocketHandshake::~WebSocketHandshake()
208 {
209 }
210
211 const KURL& WebSocketHandshake::url() const
212 {
213     return m_url;
214 }
215
216 void WebSocketHandshake::setURL(const KURL& url)
217 {
218     m_url = url.copy();
219 }
220
221 const String WebSocketHandshake::host() const
222 {
223     return m_url.host().lower();
224 }
225
226 const String& WebSocketHandshake::clientProtocol() const
227 {
228     return m_clientProtocol;
229 }
230
231 void WebSocketHandshake::setClientProtocol(const String& protocol)
232 {
233     m_clientProtocol = protocol;
234 }
235
236 bool WebSocketHandshake::secure() const
237 {
238     return m_secure;
239 }
240
241 String WebSocketHandshake::clientOrigin() const
242 {
243     return m_context->securityOrigin()->toString();
244 }
245
246 String WebSocketHandshake::clientLocation() const
247 {
248     StringBuilder builder;
249     builder.append(m_secure ? "wss" : "ws");
250     builder.append("://");
251     builder.append(hostName(m_url, m_secure));
252     builder.append(resourceName(m_url));
253     return builder.toString();
254 }
255
256 CString WebSocketHandshake::clientHandshakeMessage() const
257 {
258     // Keep the following consistent with clientHandshakeRequest().
259     StringBuilder builder;
260
261     builder.append("GET ");
262     builder.append(resourceName(m_url));
263     builder.append(" HTTP/1.1\r\n");
264
265     Vector<String> fields;
266     if (m_useHixie76Protocol)
267         fields.append("Upgrade: WebSocket");
268     else
269         fields.append("Upgrade: websocket");
270     fields.append("Connection: Upgrade");
271     fields.append("Host: " + hostName(m_url, m_secure));
272     fields.append("Origin: " + clientOrigin());
273     if (!m_clientProtocol.isEmpty())
274         fields.append("Sec-WebSocket-Protocol: " + m_clientProtocol);
275
276     KURL url = httpURLForAuthenticationAndCookies();
277     if (m_context->isDocument()) {
278         Document* document = static_cast<Document*>(m_context);
279         String cookie = cookieRequestHeaderFieldValue(document, url);
280         if (!cookie.isEmpty())
281             fields.append("Cookie: " + cookie);
282         // Set "Cookie2: <cookie>" if cookies 2 exists for url?
283     }
284
285     if (m_useHixie76Protocol) {
286         fields.append("Sec-WebSocket-Key1: " + m_hixie76SecWebSocketKey1);
287         fields.append("Sec-WebSocket-Key2: " + m_hixie76SecWebSocketKey2);
288     } else {
289         fields.append("Sec-WebSocket-Key: " + m_secWebSocketKey);
290         fields.append("Sec-WebSocket-Version: 13");
291         const String extensionValue = m_extensionDispatcher.createHeaderValue();
292         if (extensionValue.length())
293             fields.append("Sec-WebSocket-Extensions: " + extensionValue);
294     }
295
296     // Fields in the handshake are sent by the client in a random order; the
297     // order is not meaningful.  Thus, it's ok to send the order we constructed
298     // the fields.
299
300     for (size_t i = 0; i < fields.size(); i++) {
301         builder.append(fields[i]);
302         builder.append("\r\n");
303     }
304
305     builder.append("\r\n");
306
307     CString handshakeHeader = builder.toString().utf8();
308     // Hybi-10 handshake is complete at this point.
309     if (!m_useHixie76Protocol)
310         return handshakeHeader;
311     // Hixie-76 protocol requires sending eight-byte data (so-called "key3") after the request header fields.
312     char* characterBuffer = 0;
313     CString msg = CString::newUninitialized(handshakeHeader.length() + sizeof(m_hixie76Key3), characterBuffer);
314     memcpy(characterBuffer, handshakeHeader.data(), handshakeHeader.length());
315     memcpy(characterBuffer + handshakeHeader.length(), m_hixie76Key3, sizeof(m_hixie76Key3));
316     return msg;
317 }
318
319 WebSocketHandshakeRequest WebSocketHandshake::clientHandshakeRequest() const
320 {
321     // Keep the following consistent with clientHandshakeMessage().
322     // FIXME: do we need to store m_secWebSocketKey1, m_secWebSocketKey2 and
323     // m_key3 in WebSocketHandshakeRequest?
324     WebSocketHandshakeRequest request("GET", m_url);
325     if (m_useHixie76Protocol)
326         request.addHeaderField("Upgrade", "WebSocket");
327     else
328         request.addHeaderField("Upgrade", "websocket");
329     request.addHeaderField("Connection", "Upgrade");
330     request.addHeaderField("Host", hostName(m_url, m_secure));
331     request.addHeaderField("Origin", clientOrigin());
332     if (!m_clientProtocol.isEmpty())
333         request.addHeaderField("Sec-WebSocket-Protocol:", m_clientProtocol);
334
335     KURL url = httpURLForAuthenticationAndCookies();
336     if (m_context->isDocument()) {
337         Document* document = static_cast<Document*>(m_context);
338         String cookie = cookieRequestHeaderFieldValue(document, url);
339         if (!cookie.isEmpty())
340             request.addHeaderField("Cookie", cookie);
341         // Set "Cookie2: <cookie>" if cookies 2 exists for url?
342     }
343
344     if (m_useHixie76Protocol) {
345         request.addHeaderField("Sec-WebSocket-Key1", m_hixie76SecWebSocketKey1);
346         request.addHeaderField("Sec-WebSocket-Key2", m_hixie76SecWebSocketKey2);
347         request.setKey3(m_hixie76Key3);
348     } else {
349         request.addHeaderField("Sec-WebSocket-Key", m_secWebSocketKey);
350         request.addHeaderField("Sec-WebSocket-Version", "13");
351         const String extensionValue = m_extensionDispatcher.createHeaderValue();
352         if (extensionValue.length())
353             request.addHeaderField("Sec-WebSocket-Extensions", extensionValue);
354     }
355
356     return request;
357 }
358
359 void WebSocketHandshake::reset()
360 {
361     m_mode = Incomplete;
362     m_extensionDispatcher.reset();
363 }
364
365 void WebSocketHandshake::clearScriptExecutionContext()
366 {
367     m_context = 0;
368 }
369
370 int WebSocketHandshake::readServerHandshake(const char* header, size_t len)
371 {
372     m_mode = Incomplete;
373     int statusCode;
374     String statusText;
375     int lineLength = readStatusLine(header, len, statusCode, statusText);
376     if (lineLength == -1)
377         return -1;
378     if (statusCode == -1) {
379         m_mode = Failed; // m_failureReason is set inside readStatusLine().
380         return len;
381     }
382     LOG(Network, "response code: %d", statusCode);
383     m_response.setStatusCode(statusCode);
384     m_response.setStatusText(statusText);
385     if (statusCode != 101) {
386         m_mode = Failed;
387         m_failureReason = "Unexpected response code: " + String::number(statusCode);
388         return len;
389     }
390     m_mode = Normal;
391     if (!strnstr(header, "\r\n\r\n", len)) {
392         // Just hasn't been received fully yet.
393         m_mode = Incomplete;
394         return -1;
395     }
396     const char* p = readHTTPHeaders(header + lineLength, header + len);
397     if (!p) {
398         LOG(Network, "readHTTPHeaders failed");
399         m_mode = Failed; // m_failureReason is set inside readHTTPHeaders().
400         return len;
401     }
402     if (!checkResponseHeaders()) {
403         LOG(Network, "header process failed");
404         m_mode = Failed;
405         return p - header;
406     }
407
408     if (!m_useHixie76Protocol) { // Hybi-10 handshake is complete at this point.
409         m_mode = Connected;
410         return p - header;
411     }
412
413     // In hixie-76 protocol, server's handshake contains sixteen-byte data (called "challenge response")
414     // after the header fields.
415     if (len < static_cast<size_t>(p - header + sizeof(m_hixie76ExpectedChallengeResponse))) {
416         // Just hasn't been received /expected/ yet.
417         m_mode = Incomplete;
418         return -1;
419     }
420
421     m_response.setChallengeResponse(static_cast<const unsigned char*>(static_cast<const void*>(p)));
422     if (memcmp(p, m_hixie76ExpectedChallengeResponse, sizeof(m_hixie76ExpectedChallengeResponse))) {
423         m_mode = Failed;
424         return (p - header) + sizeof(m_hixie76ExpectedChallengeResponse);
425     }
426     m_mode = Connected;
427     return (p - header) + sizeof(m_hixie76ExpectedChallengeResponse);
428 }
429
430 WebSocketHandshake::Mode WebSocketHandshake::mode() const
431 {
432     return m_mode;
433 }
434
435 String WebSocketHandshake::failureReason() const
436 {
437     return m_failureReason;
438 }
439
440 String WebSocketHandshake::serverWebSocketOrigin() const
441 {
442     return m_response.headerFields().get("sec-websocket-origin");
443 }
444
445 String WebSocketHandshake::serverWebSocketLocation() const
446 {
447     return m_response.headerFields().get("sec-websocket-location");
448 }
449
450 String WebSocketHandshake::serverWebSocketProtocol() const
451 {
452     return m_response.headerFields().get("sec-websocket-protocol");
453 }
454
455 String WebSocketHandshake::serverSetCookie() const
456 {
457     return m_response.headerFields().get("set-cookie");
458 }
459
460 String WebSocketHandshake::serverSetCookie2() const
461 {
462     return m_response.headerFields().get("set-cookie2");
463 }
464
465 String WebSocketHandshake::serverUpgrade() const
466 {
467     return m_response.headerFields().get("upgrade");
468 }
469
470 String WebSocketHandshake::serverConnection() const
471 {
472     return m_response.headerFields().get("connection");
473 }
474
475 String WebSocketHandshake::serverWebSocketAccept() const
476 {
477     return m_response.headerFields().get("sec-websocket-accept");
478 }
479
480 String WebSocketHandshake::acceptedExtensions() const
481 {
482     return m_extensionDispatcher.acceptedExtensions();
483 }
484
485 const WebSocketHandshakeResponse& WebSocketHandshake::serverHandshakeResponse() const
486 {
487     return m_response;
488 }
489
490 void WebSocketHandshake::addExtensionProcessor(PassOwnPtr<WebSocketExtensionProcessor> processor)
491 {
492     m_extensionDispatcher.addProcessor(processor);
493 }
494
495 KURL WebSocketHandshake::httpURLForAuthenticationAndCookies() const
496 {
497     KURL url = m_url.copy();
498     bool couldSetProtocol = url.setProtocol(m_secure ? "https" : "http");
499     ASSERT_UNUSED(couldSetProtocol, couldSetProtocol);
500     return url;
501 }
502
503 // Returns the header length (including "\r\n"), or -1 if we have not received enough data yet.
504 // If the line is malformed or the status code is not a 3-digit number,
505 // statusCode and statusText will be set to -1 and a null string, respectively.
506 int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength, int& statusCode, String& statusText)
507 {
508     // Arbitrary size limit to prevent the server from sending an unbounded
509     // amount of data with no newlines and forcing us to buffer it all.
510     static const int maximumLength = 1024;
511
512     statusCode = -1;
513     statusText = String();
514
515     const char* space1 = 0;
516     const char* space2 = 0;
517     const char* p;
518     size_t consumedLength;
519
520     for (p = header, consumedLength = 0; consumedLength < headerLength; p++, consumedLength++) {
521         if (*p == ' ') {
522             if (!space1)
523                 space1 = p;
524             else if (!space2)
525                 space2 = p;
526         } else if (*p == '\0') {
527             // The caller isn't prepared to deal with null bytes in status
528             // line. WebSockets specification doesn't prohibit this, but HTTP
529             // does, so we'll just treat this as an error.
530             m_failureReason = "Status line contains embedded null";
531             return p + 1 - header;
532         } else if (*p == '\n')
533             break;
534     }
535     if (consumedLength == headerLength)
536         return -1; // We have not received '\n' yet.
537
538     const char* end = p + 1;
539     int lineLength = end - header;
540     if (lineLength > maximumLength) {
541         m_failureReason = "Status line is too long";
542         return maximumLength;
543     }
544
545     // The line must end with "\r\n".
546     if (lineLength < 2 || *(end - 2) != '\r') {
547         m_failureReason = "Status line does not end with CRLF";
548         return lineLength;
549     }
550
551     if (!space1 || !space2) {
552         m_failureReason = "No response code found: " + trimConsoleMessage(header, lineLength - 2);
553         return lineLength;
554     }
555
556     String statusCodeString(space1 + 1, space2 - space1 - 1);
557     if (statusCodeString.length() != 3) // Status code must consist of three digits.
558         return lineLength;
559     for (int i = 0; i < 3; ++i)
560         if (statusCodeString[i] < '0' || statusCodeString[i] > '9') {
561             m_failureReason = "Invalid status code: " + statusCodeString;
562             return lineLength;
563         }
564
565     bool ok = false;
566     statusCode = statusCodeString.toInt(&ok);
567     ASSERT(ok);
568
569     statusText = String(space2 + 1, end - space2 - 3); // Exclude "\r\n".
570     return lineLength;
571 }
572
573 const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end)
574 {
575     m_response.clearHeaderFields();
576
577     Vector<char> name;
578     Vector<char> value;
579     bool sawSecWebSocketAcceptHeaderField = false;
580     bool sawSecWebSocketProtocolHeaderField = false;
581     for (const char* p = start; p < end; p++) {
582         name.clear();
583         value.clear();
584
585         for (; p < end; p++) {
586             switch (*p) {
587             case '\r':
588                 if (name.isEmpty()) {
589                     if (p + 1 < end && *(p + 1) == '\n')
590                         return p + 2;
591                     m_failureReason = "CR doesn't follow LF at " + trimConsoleMessage(p, end - p);
592                     return 0;
593                 }
594                 m_failureReason = "Unexpected CR in name at " + trimConsoleMessage(name.data(), name.size());
595                 return 0;
596             case '\n':
597                 m_failureReason = "Unexpected LF in name at " + trimConsoleMessage(name.data(), name.size());
598                 return 0;
599             case ':':
600                 break;
601             default:
602                 name.append(*p);
603                 continue;
604             }
605             if (*p == ':') {
606                 ++p;
607                 break;
608             }
609         }
610
611         for (; p < end && *p == 0x20; p++) { }
612
613         for (; p < end; p++) {
614             switch (*p) {
615             case '\r':
616                 break;
617             case '\n':
618                 m_failureReason = "Unexpected LF in value at " + trimConsoleMessage(value.data(), value.size());
619                 return 0;
620             default:
621                 value.append(*p);
622             }
623             if (*p == '\r') {
624                 ++p;
625                 break;
626             }
627         }
628         if (p >= end || *p != '\n') {
629             m_failureReason = "CR doesn't follow LF after value at " + trimConsoleMessage(p, end - p);
630             return 0;
631         }
632         AtomicString nameStr = AtomicString::fromUTF8(name.data(), name.size());
633         String valueStr = String::fromUTF8(value.data(), value.size());
634         if (nameStr.isNull()) {
635             m_failureReason = "Invalid UTF-8 sequence in header name";
636             return 0;
637         }
638         if (valueStr.isNull()) {
639             m_failureReason = "Invalid UTF-8 sequence in header value";
640             return 0;
641         }
642         LOG(Network, "name=%s value=%s", nameStr.string().utf8().data(), valueStr.utf8().data());
643         // Sec-WebSocket-Extensions may be split. We parse and check the
644         // header value every time the header appears.
645         if (equalIgnoringCase("sec-websocket-extensions", nameStr)) {
646             if (!m_extensionDispatcher.processHeaderValue(valueStr)) {
647                 m_failureReason = m_extensionDispatcher.failureReason();
648                 return 0;
649             }
650         } else if (equalIgnoringCase("Sec-WebSocket-Accept", nameStr)) {
651             if (sawSecWebSocketAcceptHeaderField) {
652                 m_failureReason = "The Sec-WebSocket-Accept header MUST NOT appear more than once in an HTTP response";
653                 return 0;
654             }
655             m_response.addHeaderField(nameStr, valueStr);
656             sawSecWebSocketAcceptHeaderField = true;
657         } else if (equalIgnoringCase("Sec-WebSocket-Protocol", nameStr)) {
658             if (sawSecWebSocketProtocolHeaderField) {
659                 m_failureReason = "The Sec-WebSocket-Protocol header MUST NOT appear more than once in an HTTP response";
660                 return 0;
661             }
662             m_response.addHeaderField(nameStr, valueStr);
663             sawSecWebSocketProtocolHeaderField = true;
664         } else
665             m_response.addHeaderField(nameStr, valueStr);
666     }
667     ASSERT_NOT_REACHED();
668     return 0;
669 }
670
671 bool WebSocketHandshake::checkResponseHeaders()
672 {
673     const String& serverWebSocketLocation = this->serverWebSocketLocation();
674     const String& serverWebSocketOrigin = this->serverWebSocketOrigin();
675     const String& serverWebSocketProtocol = this->serverWebSocketProtocol();
676     const String& serverUpgrade = this->serverUpgrade();
677     const String& serverConnection = this->serverConnection();
678     const String& serverWebSocketAccept = this->serverWebSocketAccept();
679
680     if (serverUpgrade.isNull()) {
681         m_failureReason = "Error during WebSocket handshake: 'Upgrade' header is missing";
682         return false;
683     }
684     if (serverConnection.isNull()) {
685         m_failureReason = "Error during WebSocket handshake: 'Connection' header is missing";
686         return false;
687     }
688     if (m_useHixie76Protocol) {
689         if (serverWebSocketOrigin.isNull()) {
690             m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Origin' header is missing";
691             return false;
692         }
693         if (serverWebSocketLocation.isNull()) {
694             m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Location' header is missing";
695             return false;
696         }
697     } else {
698         if (serverWebSocketAccept.isNull()) {
699             m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing";
700             return false;
701         }
702     }
703
704     if (!equalIgnoringCase(serverUpgrade, "websocket")) {
705         m_failureReason = "Error during WebSocket handshake: 'Upgrade' header value is not 'WebSocket'";
706         return false;
707     }
708     if (!equalIgnoringCase(serverConnection, "upgrade")) {
709         m_failureReason = "Error during WebSocket handshake: 'Connection' header value is not 'Upgrade'";
710         return false;
711     }
712
713     if (m_useHixie76Protocol) {
714         if (clientOrigin() != serverWebSocketOrigin) {
715             m_failureReason = "Error during WebSocket handshake: origin mismatch: " + clientOrigin() + " != " + serverWebSocketOrigin;
716             return false;
717         }
718         if (clientLocation() != serverWebSocketLocation) {
719             m_failureReason = "Error during WebSocket handshake: location mismatch: " + clientLocation() + " != " + serverWebSocketLocation;
720             return false;
721         }
722         if (!m_clientProtocol.isEmpty() && m_clientProtocol != serverWebSocketProtocol) {
723             m_failureReason = "Error during WebSocket handshake: protocol mismatch: " + m_clientProtocol + " != " + serverWebSocketProtocol;
724             return false;
725         }
726     } else {
727         if (serverWebSocketAccept != m_expectedAccept) {
728             m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Accept mismatch";
729             return false;
730         }
731         if (!serverWebSocketProtocol.isNull()) {
732             if (m_clientProtocol.isEmpty()) {
733                 m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch";
734                 return false;
735             }
736             Vector<String> result;
737             m_clientProtocol.split(String(WebSocket::subProtocolSeperator()), result);
738             if (!result.contains(serverWebSocketProtocol)) {
739                 m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch";
740                 return false;
741             }
742         }
743     }
744     return true;
745 }
746
747 } // namespace WebCore
748
749 #endif // ENABLE(WEB_SOCKETS)