~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/Modules/websockets/WebSocketHandshake.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 "Cookie.h"
 
40
#include "CookieJar.h"
 
41
#include "Document.h"
 
42
#include "HTTPHeaderMap.h"
 
43
#include "HTTPParsers.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/Base64.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
    StringBuilder name;
 
68
    name.append(url.path());
 
69
    if (name.isEmpty())
 
70
        name.append('/');
 
71
    if (!url.query().isNull()) {
 
72
        name.append('?');
 
73
        name.append(url.query());
 
74
    }
 
75
    String result = name.toString();
 
76
    ASSERT(!result.isEmpty());
 
77
    ASSERT(!result.contains(' '));
 
78
    return result;
 
79
}
 
80
 
 
81
static String hostName(const KURL& url, bool secure)
 
82
{
 
83
    ASSERT(url.protocolIs("wss") == secure);
 
84
    StringBuilder builder;
 
85
    builder.append(url.host().lower());
 
86
    if (url.port() && ((!secure && url.port() != 80) || (secure && url.port() != 443))) {
 
87
        builder.append(':');
 
88
        builder.appendNumber(url.port());
 
89
    }
 
90
    return builder.toString();
 
91
}
 
92
 
 
93
static const size_t maxInputSampleSize = 128;
 
94
static String trimInputSample(const char* p, size_t len)
 
95
{
 
96
    String s = String(p, std::min<size_t>(len, maxInputSampleSize));
 
97
    if (len > maxInputSampleSize)
 
98
        s.append(horizontalEllipsis);
 
99
    return s;
 
100
}
 
101
 
 
102
static String generateSecWebSocketKey()
 
103
{
 
104
    static const size_t nonceSize = 16;
 
105
    unsigned char key[nonceSize];
 
106
    cryptographicallyRandomValues(key, nonceSize);
 
107
    return base64Encode(reinterpret_cast<char*>(key), nonceSize);
 
108
}
 
109
 
 
110
String WebSocketHandshake::getExpectedWebSocketAccept(const String& secWebSocketKey)
 
111
{
 
112
    static const char* const webSocketKeyGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 
113
    static const size_t sha1HashSize = 20; // FIXME: This should be defined in SHA1.h.
 
114
    SHA1 sha1;
 
115
    CString keyData = secWebSocketKey.ascii();
 
116
    sha1.addBytes(reinterpret_cast<const uint8_t*>(keyData.data()), keyData.length());
 
117
    sha1.addBytes(reinterpret_cast<const uint8_t*>(webSocketKeyGUID), strlen(webSocketKeyGUID));
 
118
    Vector<uint8_t, sha1HashSize> hash;
 
119
    sha1.computeHash(hash);
 
120
    return base64Encode(reinterpret_cast<const char*>(hash.data()), sha1HashSize);
 
121
}
 
122
 
 
123
WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context)
 
124
    : m_url(url)
 
125
    , m_clientProtocol(protocol)
 
126
    , m_secure(m_url.protocolIs("wss"))
 
127
    , m_context(context)
 
128
    , m_mode(Incomplete)
 
129
{
 
130
    m_secWebSocketKey = generateSecWebSocketKey();
 
131
    m_expectedAccept = getExpectedWebSocketAccept(m_secWebSocketKey);
 
132
}
 
133
 
 
134
WebSocketHandshake::~WebSocketHandshake()
 
135
{
 
136
}
 
137
 
 
138
const KURL& WebSocketHandshake::url() const
 
139
{
 
140
    return m_url;
 
141
}
 
142
 
 
143
void WebSocketHandshake::setURL(const KURL& url)
 
144
{
 
145
    m_url = url.copy();
 
146
}
 
147
 
 
148
const String WebSocketHandshake::host() const
 
149
{
 
150
    return m_url.host().lower();
 
151
}
 
152
 
 
153
const String& WebSocketHandshake::clientProtocol() const
 
154
{
 
155
    return m_clientProtocol;
 
156
}
 
157
 
 
158
void WebSocketHandshake::setClientProtocol(const String& protocol)
 
159
{
 
160
    m_clientProtocol = protocol;
 
161
}
 
162
 
 
163
bool WebSocketHandshake::secure() const
 
164
{
 
165
    return m_secure;
 
166
}
 
167
 
 
168
String WebSocketHandshake::clientOrigin() const
 
169
{
 
170
    return m_context->securityOrigin()->toString();
 
171
}
 
172
 
 
173
String WebSocketHandshake::clientLocation() const
 
174
{
 
175
    StringBuilder builder;
 
176
    builder.append(m_secure ? "wss" : "ws");
 
177
    builder.append("://");
 
178
    builder.append(hostName(m_url, m_secure));
 
179
    builder.append(resourceName(m_url));
 
180
    return builder.toString();
 
181
}
 
182
 
 
183
CString WebSocketHandshake::clientHandshakeMessage() const
 
184
{
 
185
    // Keep the following consistent with clientHandshakeRequest().
 
186
    StringBuilder builder;
 
187
 
 
188
    builder.append("GET ");
 
189
    builder.append(resourceName(m_url));
 
190
    builder.append(" HTTP/1.1\r\n");
 
191
 
 
192
    Vector<String> fields;
 
193
    fields.append("Upgrade: websocket");
 
194
    fields.append("Connection: Upgrade");
 
195
    fields.append("Host: " + hostName(m_url, m_secure));
 
196
    fields.append("Origin: " + clientOrigin());
 
197
    if (!m_clientProtocol.isEmpty())
 
198
        fields.append("Sec-WebSocket-Protocol: " + m_clientProtocol);
 
199
 
 
200
    KURL url = httpURLForAuthenticationAndCookies();
 
201
    if (m_context->isDocument()) {
 
202
        Document* document = static_cast<Document*>(m_context);
 
203
        String cookie = cookieRequestHeaderFieldValue(document, url);
 
204
        if (!cookie.isEmpty())
 
205
            fields.append("Cookie: " + cookie);
 
206
        // Set "Cookie2: <cookie>" if cookies 2 exists for url?
 
207
    }
 
208
 
 
209
    // Add no-cache headers to avoid compatibility issue.
 
210
    // There are some proxies that rewrite "Connection: upgrade"
 
211
    // to "Connection: close" in the response if a request doesn't contain
 
212
    // these headers.
 
213
    fields.append("Pragma: no-cache");
 
214
    fields.append("Cache-Control: no-cache");
 
215
 
 
216
    fields.append("Sec-WebSocket-Key: " + m_secWebSocketKey);
 
217
    fields.append("Sec-WebSocket-Version: 13");
 
218
    const String extensionValue = m_extensionDispatcher.createHeaderValue();
 
219
    if (extensionValue.length())
 
220
        fields.append("Sec-WebSocket-Extensions: " + extensionValue);
 
221
 
 
222
    // Fields in the handshake are sent by the client in a random order; the
 
223
    // order is not meaningful.  Thus, it's ok to send the order we constructed
 
224
    // the fields.
 
225
 
 
226
    for (size_t i = 0; i < fields.size(); i++) {
 
227
        builder.append(fields[i]);
 
228
        builder.append("\r\n");
 
229
    }
 
230
 
 
231
    builder.append("\r\n");
 
232
 
 
233
    return builder.toString().utf8();
 
234
}
 
235
 
 
236
PassRefPtr<WebSocketHandshakeRequest> WebSocketHandshake::clientHandshakeRequest() const
 
237
{
 
238
    // Keep the following consistent with clientHandshakeMessage().
 
239
    // FIXME: do we need to store m_secWebSocketKey1, m_secWebSocketKey2 and
 
240
    // m_key3 in WebSocketHandshakeRequest?
 
241
    RefPtr<WebSocketHandshakeRequest> request = WebSocketHandshakeRequest::create("GET", m_url);
 
242
    request->addHeaderField("Upgrade", "websocket");
 
243
    request->addHeaderField("Connection", "Upgrade");
 
244
    request->addHeaderField("Host", hostName(m_url, m_secure));
 
245
    request->addHeaderField("Origin", clientOrigin());
 
246
    if (!m_clientProtocol.isEmpty())
 
247
        request->addHeaderField("Sec-WebSocket-Protocol", m_clientProtocol);
 
248
 
 
249
    KURL url = httpURLForAuthenticationAndCookies();
 
250
    if (m_context->isDocument()) {
 
251
        Document* document = static_cast<Document*>(m_context);
 
252
        String cookie = cookieRequestHeaderFieldValue(document, url);
 
253
        if (!cookie.isEmpty())
 
254
            request->addHeaderField("Cookie", cookie);
 
255
        // Set "Cookie2: <cookie>" if cookies 2 exists for url?
 
256
    }
 
257
 
 
258
    request->addHeaderField("Pragma", "no-cache");
 
259
    request->addHeaderField("Cache-Control", "no-cache");
 
260
 
 
261
    request->addHeaderField("Sec-WebSocket-Key", m_secWebSocketKey);
 
262
    request->addHeaderField("Sec-WebSocket-Version", "13");
 
263
    const String extensionValue = m_extensionDispatcher.createHeaderValue();
 
264
    if (extensionValue.length())
 
265
        request->addHeaderField("Sec-WebSocket-Extensions", extensionValue);
 
266
 
 
267
    return request.release();
 
268
}
 
269
 
 
270
void WebSocketHandshake::reset()
 
271
{
 
272
    m_mode = Incomplete;
 
273
    m_extensionDispatcher.reset();
 
274
}
 
275
 
 
276
void WebSocketHandshake::clearScriptExecutionContext()
 
277
{
 
278
    m_context = 0;
 
279
}
 
280
 
 
281
int WebSocketHandshake::readServerHandshake(const char* header, size_t len)
 
282
{
 
283
    m_mode = Incomplete;
 
284
    int statusCode;
 
285
    String statusText;
 
286
    int lineLength = readStatusLine(header, len, statusCode, statusText);
 
287
    if (lineLength == -1)
 
288
        return -1;
 
289
    if (statusCode == -1) {
 
290
        m_mode = Failed; // m_failureReason is set inside readStatusLine().
 
291
        return len;
 
292
    }
 
293
    LOG(Network, "response code: %d", statusCode);
 
294
    m_response.setStatusCode(statusCode);
 
295
    m_response.setStatusText(statusText);
 
296
    if (statusCode != 101) {
 
297
        m_mode = Failed;
 
298
        m_failureReason = "Unexpected response code: " + String::number(statusCode);
 
299
        return len;
 
300
    }
 
301
    m_mode = Normal;
 
302
    if (!strnstr(header, "\r\n\r\n", len)) {
 
303
        // Just hasn't been received fully yet.
 
304
        m_mode = Incomplete;
 
305
        return -1;
 
306
    }
 
307
    const char* p = readHTTPHeaders(header + lineLength, header + len);
 
308
    if (!p) {
 
309
        LOG(Network, "readHTTPHeaders failed");
 
310
        m_mode = Failed; // m_failureReason is set inside readHTTPHeaders().
 
311
        return len;
 
312
    }
 
313
    if (!checkResponseHeaders()) {
 
314
        LOG(Network, "header process failed");
 
315
        m_mode = Failed;
 
316
        return p - header;
 
317
    }
 
318
 
 
319
    m_mode = Connected;
 
320
    return p - header;
 
321
}
 
322
 
 
323
WebSocketHandshake::Mode WebSocketHandshake::mode() const
 
324
{
 
325
    return m_mode;
 
326
}
 
327
 
 
328
String WebSocketHandshake::failureReason() const
 
329
{
 
330
    return m_failureReason;
 
331
}
 
332
 
 
333
String WebSocketHandshake::serverWebSocketProtocol() const
 
334
{
 
335
    return m_response.headerFields().get("sec-websocket-protocol");
 
336
}
 
337
 
 
338
String WebSocketHandshake::serverSetCookie() const
 
339
{
 
340
    return m_response.headerFields().get("set-cookie");
 
341
}
 
342
 
 
343
String WebSocketHandshake::serverSetCookie2() const
 
344
{
 
345
    return m_response.headerFields().get("set-cookie2");
 
346
}
 
347
 
 
348
String WebSocketHandshake::serverUpgrade() const
 
349
{
 
350
    return m_response.headerFields().get("upgrade");
 
351
}
 
352
 
 
353
String WebSocketHandshake::serverConnection() const
 
354
{
 
355
    return m_response.headerFields().get("connection");
 
356
}
 
357
 
 
358
String WebSocketHandshake::serverWebSocketAccept() const
 
359
{
 
360
    return m_response.headerFields().get("sec-websocket-accept");
 
361
}
 
362
 
 
363
String WebSocketHandshake::acceptedExtensions() const
 
364
{
 
365
    return m_extensionDispatcher.acceptedExtensions();
 
366
}
 
367
 
 
368
const WebSocketHandshakeResponse& WebSocketHandshake::serverHandshakeResponse() const
 
369
{
 
370
    return m_response;
 
371
}
 
372
 
 
373
void WebSocketHandshake::addExtensionProcessor(PassOwnPtr<WebSocketExtensionProcessor> processor)
 
374
{
 
375
    m_extensionDispatcher.addProcessor(processor);
 
376
}
 
377
 
 
378
KURL WebSocketHandshake::httpURLForAuthenticationAndCookies() const
 
379
{
 
380
    KURL url = m_url.copy();
 
381
    bool couldSetProtocol = url.setProtocol(m_secure ? "https" : "http");
 
382
    ASSERT_UNUSED(couldSetProtocol, couldSetProtocol);
 
383
    return url;
 
384
}
 
385
 
 
386
// Returns the header length (including "\r\n"), or -1 if we have not received enough data yet.
 
387
// If the line is malformed or the status code is not a 3-digit number,
 
388
// statusCode and statusText will be set to -1 and a null string, respectively.
 
389
int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength, int& statusCode, String& statusText)
 
390
{
 
391
    // Arbitrary size limit to prevent the server from sending an unbounded
 
392
    // amount of data with no newlines and forcing us to buffer it all.
 
393
    static const int maximumLength = 1024;
 
394
 
 
395
    statusCode = -1;
 
396
    statusText = String();
 
397
 
 
398
    const char* space1 = 0;
 
399
    const char* space2 = 0;
 
400
    const char* p;
 
401
    size_t consumedLength;
 
402
 
 
403
    for (p = header, consumedLength = 0; consumedLength < headerLength; p++, consumedLength++) {
 
404
        if (*p == ' ') {
 
405
            if (!space1)
 
406
                space1 = p;
 
407
            else if (!space2)
 
408
                space2 = p;
 
409
        } else if (*p == '\0') {
 
410
            // The caller isn't prepared to deal with null bytes in status
 
411
            // line. WebSockets specification doesn't prohibit this, but HTTP
 
412
            // does, so we'll just treat this as an error.
 
413
            m_failureReason = "Status line contains embedded null";
 
414
            return p + 1 - header;
 
415
        } else if (*p == '\n')
 
416
            break;
 
417
    }
 
418
    if (consumedLength == headerLength)
 
419
        return -1; // We have not received '\n' yet.
 
420
 
 
421
    const char* end = p + 1;
 
422
    int lineLength = end - header;
 
423
    if (lineLength > maximumLength) {
 
424
        m_failureReason = "Status line is too long";
 
425
        return maximumLength;
 
426
    }
 
427
 
 
428
    // The line must end with "\r\n".
 
429
    if (lineLength < 2 || *(end - 2) != '\r') {
 
430
        m_failureReason = "Status line does not end with CRLF";
 
431
        return lineLength;
 
432
    }
 
433
 
 
434
    if (!space1 || !space2) {
 
435
        m_failureReason = "No response code found: " + trimInputSample(header, lineLength - 2);
 
436
        return lineLength;
 
437
    }
 
438
 
 
439
    String statusCodeString(space1 + 1, space2 - space1 - 1);
 
440
    if (statusCodeString.length() != 3) // Status code must consist of three digits.
 
441
        return lineLength;
 
442
    for (int i = 0; i < 3; ++i)
 
443
        if (statusCodeString[i] < '0' || statusCodeString[i] > '9') {
 
444
            m_failureReason = "Invalid status code: " + statusCodeString;
 
445
            return lineLength;
 
446
        }
 
447
 
 
448
    bool ok = false;
 
449
    statusCode = statusCodeString.toInt(&ok);
 
450
    ASSERT(ok);
 
451
 
 
452
    statusText = String(space2 + 1, end - space2 - 3); // Exclude "\r\n".
 
453
    return lineLength;
 
454
}
 
455
 
 
456
const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end)
 
457
{
 
458
    m_response.clearHeaderFields();
 
459
 
 
460
    AtomicString name;
 
461
    String value;
 
462
    bool sawSecWebSocketAcceptHeaderField = false;
 
463
    bool sawSecWebSocketProtocolHeaderField = false;
 
464
    const char* p = start;
 
465
    for (; p < end; p++) {
 
466
        size_t consumedLength = parseHTTPHeader(p, end - p, m_failureReason, name, value);
 
467
        if (!consumedLength)
 
468
            return 0;
 
469
        p += consumedLength;
 
470
 
 
471
        // Stop once we consumed an empty line.
 
472
        if (name.isEmpty())
 
473
            break;
 
474
 
 
475
        // Sec-WebSocket-Extensions may be split. We parse and check the
 
476
        // header value every time the header appears.
 
477
        if (equalIgnoringCase("sec-websocket-extensions", name)) {
 
478
            if (!m_extensionDispatcher.processHeaderValue(value)) {
 
479
                m_failureReason = m_extensionDispatcher.failureReason();
 
480
                return 0;
 
481
            }
 
482
        } else if (equalIgnoringCase("Sec-WebSocket-Accept", name)) {
 
483
            if (sawSecWebSocketAcceptHeaderField) {
 
484
                m_failureReason = "The Sec-WebSocket-Accept header MUST NOT appear more than once in an HTTP response";
 
485
                return 0;
 
486
            }
 
487
            m_response.addHeaderField(name, value);
 
488
            sawSecWebSocketAcceptHeaderField = true;
 
489
        } else if (equalIgnoringCase("Sec-WebSocket-Protocol", name)) {
 
490
            if (sawSecWebSocketProtocolHeaderField) {
 
491
                m_failureReason = "The Sec-WebSocket-Protocol header MUST NOT appear more than once in an HTTP response";
 
492
                return 0;
 
493
            }
 
494
            m_response.addHeaderField(name, value);
 
495
            sawSecWebSocketProtocolHeaderField = true;
 
496
        } else
 
497
            m_response.addHeaderField(name, value);
 
498
    }
 
499
    return p;
 
500
}
 
501
 
 
502
bool WebSocketHandshake::checkResponseHeaders()
 
503
{
 
504
    const String& serverWebSocketProtocol = this->serverWebSocketProtocol();
 
505
    const String& serverUpgrade = this->serverUpgrade();
 
506
    const String& serverConnection = this->serverConnection();
 
507
    const String& serverWebSocketAccept = this->serverWebSocketAccept();
 
508
 
 
509
    if (serverUpgrade.isNull()) {
 
510
        m_failureReason = "Error during WebSocket handshake: 'Upgrade' header is missing";
 
511
        return false;
 
512
    }
 
513
    if (serverConnection.isNull()) {
 
514
        m_failureReason = "Error during WebSocket handshake: 'Connection' header is missing";
 
515
        return false;
 
516
    }
 
517
    if (serverWebSocketAccept.isNull()) {
 
518
        m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing";
 
519
        return false;
 
520
    }
 
521
 
 
522
    if (!equalIgnoringCase(serverUpgrade, "websocket")) {
 
523
        m_failureReason = "Error during WebSocket handshake: 'Upgrade' header value is not 'WebSocket'";
 
524
        return false;
 
525
    }
 
526
    if (!equalIgnoringCase(serverConnection, "upgrade")) {
 
527
        m_failureReason = "Error during WebSocket handshake: 'Connection' header value is not 'Upgrade'";
 
528
        return false;
 
529
    }
 
530
 
 
531
    if (serverWebSocketAccept != m_expectedAccept) {
 
532
        m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Accept mismatch";
 
533
        return false;
 
534
    }
 
535
    if (!serverWebSocketProtocol.isNull()) {
 
536
        if (m_clientProtocol.isEmpty()) {
 
537
            m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch";
 
538
            return false;
 
539
        }
 
540
        Vector<String> result;
 
541
        m_clientProtocol.split(String(WebSocket::subProtocolSeperator()), result);
 
542
        if (!result.contains(serverWebSocketProtocol)) {
 
543
            m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Protocol mismatch";
 
544
            return false;
 
545
        }
 
546
    }
 
547
    return true;
 
548
}
 
549
 
 
550
} // namespace WebCore
 
551
 
 
552
#endif // ENABLE(WEB_SOCKETS)