Home | History | Annotate | Download | only in websockets
      1 /*
      2  * Copyright (C) 2009 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 
     38 #include "Cookie.h"
     39 #include "CookieJar.h"
     40 #include "Document.h"
     41 #include "HTTPHeaderMap.h"
     42 #include "KURL.h"
     43 #include "Logging.h"
     44 #include "ScriptCallStack.h"
     45 #include "ScriptExecutionContext.h"
     46 #include "SecurityOrigin.h"
     47 #include <wtf/CryptographicallyRandomNumber.h>
     48 #include <wtf/MD5.h>
     49 #include <wtf/StdLibExtras.h>
     50 #include <wtf/StringExtras.h>
     51 #include <wtf/Vector.h>
     52 #include <wtf/text/AtomicString.h>
     53 #include <wtf/text/CString.h>
     54 #include <wtf/text/StringBuilder.h>
     55 #include <wtf/text/StringConcatenate.h>
     56 #include <wtf/unicode/CharacterNames.h>
     57 
     58 namespace WebCore {
     59 
     60 static const char randomCharacterInSecWebSocketKey[] = "!\"#$%&'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
     61 
     62 static String resourceName(const KURL& url)
     63 {
     64     String name = url.path();
     65     if (name.isEmpty())
     66         name = "/";
     67     if (!url.query().isNull())
     68         name += "?" + url.query();
     69     ASSERT(!name.isEmpty());
     70     ASSERT(!name.contains(' '));
     71     return name;
     72 }
     73 
     74 static String hostName(const KURL& url, bool secure)
     75 {
     76     ASSERT(url.protocolIs("wss") == secure);
     77     StringBuilder builder;
     78     builder.append(url.host().lower());
     79     if (url.port() && ((!secure && url.port() != 80) || (secure && url.port() != 443))) {
     80         builder.append(':');
     81         builder.append(String::number(url.port()));
     82     }
     83     return builder.toString();
     84 }
     85 
     86 static const size_t maxConsoleMessageSize = 128;
     87 static String trimConsoleMessage(const char* p, size_t len)
     88 {
     89     String s = String(p, std::min<size_t>(len, maxConsoleMessageSize));
     90     if (len > maxConsoleMessageSize)
     91         s.append(horizontalEllipsis);
     92     return s;
     93 }
     94 
     95 static uint32_t randomNumberLessThan(uint32_t n)
     96 {
     97     if (!n)
     98         return 0;
     99     if (n == std::numeric_limits<uint32_t>::max())
    100         return cryptographicallyRandomNumber();
    101     uint32_t max = std::numeric_limits<uint32_t>::max() - (std::numeric_limits<uint32_t>::max() % n);
    102     ASSERT(!(max % n));
    103     uint32_t v;
    104     do {
    105         v = cryptographicallyRandomNumber();
    106     } while (v >= max);
    107     return v % n;
    108 }
    109 
    110 static void generateSecWebSocketKey(uint32_t& number, String& key)
    111 {
    112     uint32_t space = randomNumberLessThan(12) + 1;
    113     uint32_t max = 4294967295U / space;
    114     number = randomNumberLessThan(max);
    115     uint32_t product = number * space;
    116 
    117     String s = String::number(product);
    118     int n = randomNumberLessThan(12) + 1;
    119     DEFINE_STATIC_LOCAL(String, randomChars, (randomCharacterInSecWebSocketKey));
    120     for (int i = 0; i < n; i++) {
    121         int pos = randomNumberLessThan(s.length() + 1);
    122         int chpos = randomNumberLessThan(randomChars.length());
    123         s.insert(randomChars.substring(chpos, 1), pos);
    124     }
    125     DEFINE_STATIC_LOCAL(String, spaceChar, (" "));
    126     for (uint32_t i = 0; i < space; i++) {
    127         int pos = randomNumberLessThan(s.length() - 1) + 1;
    128         s.insert(spaceChar, pos);
    129     }
    130     ASSERT(s[0] != ' ');
    131     ASSERT(s[s.length() - 1] != ' ');
    132     key = s;
    133 }
    134 
    135 static void generateKey3(unsigned char key3[8])
    136 {
    137     cryptographicallyRandomValues(key3, 8);
    138 }
    139 
    140 static void setChallengeNumber(unsigned char* buf, uint32_t number)
    141 {
    142     unsigned char* p = buf + 3;
    143     for (int i = 0; i < 4; i++) {
    144         *p = number & 0xFF;
    145         --p;
    146         number >>= 8;
    147     }
    148 }
    149 
    150 static void generateExpectedChallengeResponse(uint32_t number1, uint32_t number2, unsigned char key3[8], unsigned char expectedChallenge[16])
    151 {
    152     unsigned char challenge[16];
    153     setChallengeNumber(&challenge[0], number1);
    154     setChallengeNumber(&challenge[4], number2);
    155     memcpy(&challenge[8], key3, 8);
    156     MD5 md5;
    157     md5.addBytes(challenge, sizeof(challenge));
    158     Vector<uint8_t, 16> digest;
    159     md5.checksum(digest);
    160     memcpy(expectedChallenge, digest.data(), 16);
    161 }
    162 
    163 WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context)
    164     : m_url(url)
    165     , m_clientProtocol(protocol)
    166     , m_secure(m_url.protocolIs("wss"))
    167     , m_context(context)
    168     , m_mode(Incomplete)
    169 {
    170     uint32_t number1;
    171     uint32_t number2;
    172     generateSecWebSocketKey(number1, m_secWebSocketKey1);
    173     generateSecWebSocketKey(number2, m_secWebSocketKey2);
    174     generateKey3(m_key3);
    175     generateExpectedChallengeResponse(number1, number2, m_key3, m_expectedChallengeResponse);
    176 }
    177 
    178 WebSocketHandshake::~WebSocketHandshake()
    179 {
    180 }
    181 
    182 const KURL& WebSocketHandshake::url() const
    183 {
    184     return m_url;
    185 }
    186 
    187 void WebSocketHandshake::setURL(const KURL& url)
    188 {
    189     m_url = url.copy();
    190 }
    191 
    192 const String WebSocketHandshake::host() const
    193 {
    194     return m_url.host().lower();
    195 }
    196 
    197 const String& WebSocketHandshake::clientProtocol() const
    198 {
    199     return m_clientProtocol;
    200 }
    201 
    202 void WebSocketHandshake::setClientProtocol(const String& protocol)
    203 {
    204     m_clientProtocol = protocol;
    205 }
    206 
    207 bool WebSocketHandshake::secure() const
    208 {
    209     return m_secure;
    210 }
    211 
    212 String WebSocketHandshake::clientOrigin() const
    213 {
    214     return m_context->securityOrigin()->toString();
    215 }
    216 
    217 String WebSocketHandshake::clientLocation() const
    218 {
    219     StringBuilder builder;
    220     builder.append(m_secure ? "wss" : "ws");
    221     builder.append("://");
    222     builder.append(hostName(m_url, m_secure));
    223     builder.append(resourceName(m_url));
    224     return builder.toString();
    225 }
    226 
    227 CString WebSocketHandshake::clientHandshakeMessage() const
    228 {
    229     // Keep the following consistent with clientHandshakeRequest().
    230     StringBuilder builder;
    231 
    232     builder.append("GET ");
    233     builder.append(resourceName(m_url));
    234     builder.append(" HTTP/1.1\r\n");
    235 
    236     Vector<String> fields;
    237     fields.append("Upgrade: WebSocket");
    238     fields.append("Connection: Upgrade");
    239     fields.append("Host: " + hostName(m_url, m_secure));
    240     fields.append("Origin: " + clientOrigin());
    241     if (!m_clientProtocol.isEmpty())
    242         fields.append("Sec-WebSocket-Protocol: " + m_clientProtocol);
    243 
    244     KURL url = httpURLForAuthenticationAndCookies();
    245     if (m_context->isDocument()) {
    246         Document* document = static_cast<Document*>(m_context);
    247         String cookie = cookieRequestHeaderFieldValue(document, url);
    248         if (!cookie.isEmpty())
    249             fields.append("Cookie: " + cookie);
    250         // Set "Cookie2: <cookie>" if cookies 2 exists for url?
    251     }
    252 
    253     fields.append("Sec-WebSocket-Key1: " + m_secWebSocketKey1);
    254     fields.append("Sec-WebSocket-Key2: " + m_secWebSocketKey2);
    255 
    256     // Fields in the handshake are sent by the client in a random order; the
    257     // order is not meaningful.  Thus, it's ok to send the order we constructed
    258     // the fields.
    259 
    260     for (size_t i = 0; i < fields.size(); i++) {
    261         builder.append(fields[i]);
    262         builder.append("\r\n");
    263     }
    264 
    265     builder.append("\r\n");
    266 
    267     CString handshakeHeader = builder.toString().utf8();
    268     char* characterBuffer = 0;
    269     CString msg = CString::newUninitialized(handshakeHeader.length() + sizeof(m_key3), characterBuffer);
    270     memcpy(characterBuffer, handshakeHeader.data(), handshakeHeader.length());
    271     memcpy(characterBuffer + handshakeHeader.length(), m_key3, sizeof(m_key3));
    272     return msg;
    273 }
    274 
    275 WebSocketHandshakeRequest WebSocketHandshake::clientHandshakeRequest() const
    276 {
    277     // Keep the following consistent with clientHandshakeMessage().
    278     // FIXME: do we need to store m_secWebSocketKey1, m_secWebSocketKey2 and
    279     // m_key3 in WebSocketHandshakeRequest?
    280     WebSocketHandshakeRequest request("GET", m_url);
    281     request.addHeaderField("Upgrade", "WebSocket");
    282     request.addHeaderField("Connection", "Upgrade");
    283     request.addHeaderField("Host", hostName(m_url, m_secure));
    284     request.addHeaderField("Origin", clientOrigin());
    285     if (!m_clientProtocol.isEmpty())
    286         request.addHeaderField("Sec-WebSocket-Protocol:", m_clientProtocol);
    287 
    288     KURL url = httpURLForAuthenticationAndCookies();
    289     if (m_context->isDocument()) {
    290         Document* document = static_cast<Document*>(m_context);
    291         String cookie = cookieRequestHeaderFieldValue(document, url);
    292         if (!cookie.isEmpty())
    293             request.addHeaderField("Cookie", cookie);
    294         // Set "Cookie2: <cookie>" if cookies 2 exists for url?
    295     }
    296 
    297     request.addHeaderField("Sec-WebSocket-Key1", m_secWebSocketKey1);
    298     request.addHeaderField("Sec-WebSocket-Key2", m_secWebSocketKey2);
    299     request.setKey3(m_key3);
    300 
    301     return request;
    302 }
    303 
    304 void WebSocketHandshake::reset()
    305 {
    306     m_mode = Incomplete;
    307 }
    308 
    309 void WebSocketHandshake::clearScriptExecutionContext()
    310 {
    311     m_context = 0;
    312 }
    313 
    314 int WebSocketHandshake::readServerHandshake(const char* header, size_t len)
    315 {
    316     m_mode = Incomplete;
    317     int statusCode;
    318     String statusText;
    319     int lineLength = readStatusLine(header, len, statusCode, statusText);
    320     if (lineLength == -1)
    321         return -1;
    322     if (statusCode == -1) {
    323         m_mode = Failed;
    324         return len;
    325     }
    326     LOG(Network, "response code: %d", statusCode);
    327     m_response.setStatusCode(statusCode);
    328     m_response.setStatusText(statusText);
    329     if (statusCode != 101) {
    330         m_mode = Failed;
    331         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, makeString("Unexpected response code: ", String::number(statusCode)), 0, clientOrigin(), 0);
    332         return len;
    333     }
    334     m_mode = Normal;
    335     if (!strnstr(header, "\r\n\r\n", len)) {
    336         // Just hasn't been received fully yet.
    337         m_mode = Incomplete;
    338         return -1;
    339     }
    340     const char* p = readHTTPHeaders(header + lineLength, header + len);
    341     if (!p) {
    342         LOG(Network, "readHTTPHeaders failed");
    343         m_mode = Failed;
    344         return len;
    345     }
    346     if (!checkResponseHeaders()) {
    347         LOG(Network, "header process failed");
    348         m_mode = Failed;
    349         return p - header;
    350     }
    351     if (len < static_cast<size_t>(p - header + sizeof(m_expectedChallengeResponse))) {
    352         // Just hasn't been received /expected/ yet.
    353         m_mode = Incomplete;
    354         return -1;
    355     }
    356     m_response.setChallengeResponse(static_cast<const unsigned char*>(static_cast<const void*>(p)));
    357     if (memcmp(p, m_expectedChallengeResponse, sizeof(m_expectedChallengeResponse))) {
    358         m_mode = Failed;
    359         return (p - header) + sizeof(m_expectedChallengeResponse);
    360     }
    361     m_mode = Connected;
    362     return (p - header) + sizeof(m_expectedChallengeResponse);
    363 }
    364 
    365 WebSocketHandshake::Mode WebSocketHandshake::mode() const
    366 {
    367     return m_mode;
    368 }
    369 
    370 String WebSocketHandshake::serverWebSocketOrigin() const
    371 {
    372     return m_response.headerFields().get("sec-websocket-origin");
    373 }
    374 
    375 String WebSocketHandshake::serverWebSocketLocation() const
    376 {
    377     return m_response.headerFields().get("sec-websocket-location");
    378 }
    379 
    380 String WebSocketHandshake::serverWebSocketProtocol() const
    381 {
    382     return m_response.headerFields().get("sec-websocket-protocol");
    383 }
    384 
    385 String WebSocketHandshake::serverSetCookie() const
    386 {
    387     return m_response.headerFields().get("set-cookie");
    388 }
    389 
    390 String WebSocketHandshake::serverSetCookie2() const
    391 {
    392     return m_response.headerFields().get("set-cookie2");
    393 }
    394 
    395 String WebSocketHandshake::serverUpgrade() const
    396 {
    397     return m_response.headerFields().get("upgrade");
    398 }
    399 
    400 String WebSocketHandshake::serverConnection() const
    401 {
    402     return m_response.headerFields().get("connection");
    403 }
    404 
    405 const WebSocketHandshakeResponse& WebSocketHandshake::serverHandshakeResponse() const
    406 {
    407     return m_response;
    408 }
    409 
    410 KURL WebSocketHandshake::httpURLForAuthenticationAndCookies() const
    411 {
    412     KURL url = m_url.copy();
    413     bool couldSetProtocol = url.setProtocol(m_secure ? "https" : "http");
    414     ASSERT_UNUSED(couldSetProtocol, couldSetProtocol);
    415     return url;
    416 }
    417 
    418 // Returns the header length (including "\r\n"), or -1 if we have not received enough data yet.
    419 // If the line is malformed or the status code is not a 3-digit number,
    420 // statusCode and statusText will be set to -1 and a null string, respectively.
    421 int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength, int& statusCode, String& statusText)
    422 {
    423     // Arbitrary size limit to prevent the server from sending an unbounded
    424     // amount of data with no newlines and forcing us to buffer it all.
    425     static const int maximumLength = 1024;
    426 
    427     statusCode = -1;
    428     statusText = String();
    429 
    430     const char* space1 = 0;
    431     const char* space2 = 0;
    432     const char* p;
    433     size_t consumedLength;
    434 
    435     for (p = header, consumedLength = 0; consumedLength < headerLength; p++, consumedLength++) {
    436         if (*p == ' ') {
    437             if (!space1)
    438                 space1 = p;
    439             else if (!space2)
    440                 space2 = p;
    441         } else if (*p == '\0') {
    442             // The caller isn't prepared to deal with null bytes in status
    443             // line. WebSockets specification doesn't prohibit this, but HTTP
    444             // does, so we'll just treat this as an error.
    445             m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Status line contains embedded null", 0, clientOrigin(), 0);
    446             return p + 1 - header;
    447         } else if (*p == '\n')
    448             break;
    449     }
    450     if (consumedLength == headerLength)
    451         return -1; // We have not received '\n' yet.
    452 
    453     const char* end = p + 1;
    454     if (end - header > maximumLength) {
    455         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Status line is too long", 0, clientOrigin(), 0);
    456         return maximumLength;
    457     }
    458     int lineLength = end - header;
    459 
    460     if (!space1 || !space2) {
    461         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "No response code found: " + trimConsoleMessage(header, lineLength - 1), 0, clientOrigin(), 0);
    462         return lineLength;
    463     }
    464 
    465     // The line must end with "\r\n".
    466     if (*(end - 2) != '\r') {
    467         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Status line does not end with CRLF", 0, clientOrigin(), 0);
    468         return lineLength;
    469     }
    470 
    471     String statusCodeString(space1 + 1, space2 - space1 - 1);
    472     if (statusCodeString.length() != 3) // Status code must consist of three digits.
    473         return lineLength;
    474     for (int i = 0; i < 3; ++i)
    475         if (statusCodeString[i] < '0' || statusCodeString[i] > '9') {
    476             m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Invalid status code: " + statusCodeString, 0, clientOrigin(), 0);
    477             return lineLength;
    478         }
    479 
    480     bool ok = false;
    481     statusCode = statusCodeString.toInt(&ok);
    482     ASSERT(ok);
    483 
    484     statusText = String(space2 + 1, end - space2 - 3); // Exclude "\r\n".
    485     return lineLength;
    486 }
    487 
    488 const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end)
    489 {
    490     m_response.clearHeaderFields();
    491 
    492     Vector<char> name;
    493     Vector<char> value;
    494     for (const char* p = start; p < end; p++) {
    495         name.clear();
    496         value.clear();
    497 
    498         for (; p < end; p++) {
    499             switch (*p) {
    500             case '\r':
    501                 if (name.isEmpty()) {
    502                     if (p + 1 < end && *(p + 1) == '\n')
    503                         return p + 2;
    504                     m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "CR doesn't follow LF at " + trimConsoleMessage(p, end - p), 0, clientOrigin(), 0);
    505                     return 0;
    506                 }
    507                 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected CR in name at " + trimConsoleMessage(name.data(), name.size()), 0, clientOrigin(), 0);
    508                 return 0;
    509             case '\n':
    510                 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in name at " + trimConsoleMessage(name.data(), name.size()), 0, clientOrigin(), 0);
    511                 return 0;
    512             case ':':
    513                 break;
    514             default:
    515                 name.append(*p);
    516                 continue;
    517             }
    518             if (*p == ':') {
    519                 ++p;
    520                 break;
    521             }
    522         }
    523 
    524         for (; p < end && *p == 0x20; p++) { }
    525 
    526         for (; p < end; p++) {
    527             switch (*p) {
    528             case '\r':
    529                 break;
    530             case '\n':
    531                 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in value at " + trimConsoleMessage(value.data(), value.size()), 0, clientOrigin(), 0);
    532                 return 0;
    533             default:
    534                 value.append(*p);
    535             }
    536             if (*p == '\r') {
    537                 ++p;
    538                 break;
    539             }
    540         }
    541         if (p >= end || *p != '\n') {
    542             m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "CR doesn't follow LF after value at " + trimConsoleMessage(p, end - p), 0, clientOrigin(), 0);
    543             return 0;
    544         }
    545         AtomicString nameStr = AtomicString::fromUTF8(name.data(), name.size());
    546         String valueStr = String::fromUTF8(value.data(), value.size());
    547         if (nameStr.isNull()) {
    548             m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "invalid UTF-8 sequence in header name", 0, clientOrigin(), 0);
    549             return 0;
    550         }
    551         if (valueStr.isNull()) {
    552             m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "invalid UTF-8 sequence in header value", 0, clientOrigin(), 0);
    553             return 0;
    554         }
    555         LOG(Network, "name=%s value=%s", nameStr.string().utf8().data(), valueStr.utf8().data());
    556         m_response.addHeaderField(nameStr, valueStr);
    557     }
    558     ASSERT_NOT_REACHED();
    559     return 0;
    560 }
    561 
    562 bool WebSocketHandshake::checkResponseHeaders()
    563 {
    564     const String& serverWebSocketLocation = this->serverWebSocketLocation();
    565     const String& serverWebSocketOrigin = this->serverWebSocketOrigin();
    566     const String& serverWebSocketProtocol = this->serverWebSocketProtocol();
    567     const String& serverUpgrade = this->serverUpgrade();
    568     const String& serverConnection = this->serverConnection();
    569 
    570     if (serverUpgrade.isNull()) {
    571         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'Upgrade' header is missing", 0, clientOrigin(), 0);
    572         return false;
    573     }
    574     if (serverConnection.isNull()) {
    575         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'Connection' header is missing", 0, clientOrigin(), 0);
    576         return false;
    577     }
    578     if (serverWebSocketOrigin.isNull()) {
    579         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'Sec-WebSocket-Origin' header is missing", 0, clientOrigin(), 0);
    580         return false;
    581     }
    582     if (serverWebSocketLocation.isNull()) {
    583         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'Sec-WebSocket-Location' header is missing", 0, clientOrigin(), 0);
    584         return false;
    585     }
    586 
    587     if (!equalIgnoringCase(serverUpgrade, "websocket")) {
    588         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'Upgrade' header value is not 'WebSocket'", 0, clientOrigin(), 0);
    589         return false;
    590     }
    591     if (!equalIgnoringCase(serverConnection, "upgrade")) {
    592         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'Connection' header value is not 'Upgrade'", 0, clientOrigin(), 0);
    593         return false;
    594     }
    595 
    596     if (clientOrigin() != serverWebSocketOrigin) {
    597         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: origin mismatch: " + clientOrigin() + " != " + serverWebSocketOrigin, 0, clientOrigin(), 0);
    598         return false;
    599     }
    600     if (clientLocation() != serverWebSocketLocation) {
    601         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: location mismatch: " + clientLocation() + " != " + serverWebSocketLocation, 0, clientOrigin(), 0);
    602         return false;
    603     }
    604     if (!m_clientProtocol.isEmpty() && m_clientProtocol != serverWebSocketProtocol) {
    605         m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: protocol mismatch: " + m_clientProtocol + " != " + serverWebSocketProtocol, 0, clientOrigin(), 0);
    606         return false;
    607     }
    608     return true;
    609 }
    610 
    611 } // namespace WebCore
    612 
    613 #endif // ENABLE(WEB_SOCKETS)
    614