|
1 /* |
|
2 * Copyright (C) 2009 Google Inc. All rights reserved. |
|
3 * |
|
4 * Redistribution and use in source and binary forms, with or without |
|
5 * modification, are permitted provided that the following conditions are |
|
6 * met: |
|
7 * |
|
8 * * Redistributions of source code must retain the above copyright |
|
9 * notice, this list of conditions and the following disclaimer. |
|
10 * * Redistributions in binary form must reproduce the above |
|
11 * copyright notice, this list of conditions and the following disclaimer |
|
12 * in the documentation and/or other materials provided with the |
|
13 * distribution. |
|
14 * * Neither the name of Google Inc. nor the names of its |
|
15 * contributors may be used to endorse or promote products derived from |
|
16 * this software without specific prior written permission. |
|
17 * |
|
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 */ |
|
30 |
|
31 #include "config.h" |
|
32 |
|
33 #if ENABLE(WEB_SOCKETS) |
|
34 |
|
35 #include "WebSocketHandshake.h" |
|
36 |
|
37 #include "AtomicString.h" |
|
38 #include "CharacterNames.h" |
|
39 #include "Cookie.h" |
|
40 #include "CookieJar.h" |
|
41 #include "Document.h" |
|
42 #include "HTTPHeaderMap.h" |
|
43 #include "KURL.h" |
|
44 #include "Logging.h" |
|
45 #include "ScriptExecutionContext.h" |
|
46 #include "SecurityOrigin.h" |
|
47 #include "StringBuilder.h" |
|
48 |
|
49 #include <wtf/MD5.h> |
|
50 #include <wtf/RandomNumber.h> |
|
51 #include <wtf/StdLibExtras.h> |
|
52 #include <wtf/StringExtras.h> |
|
53 #include <wtf/Vector.h> |
|
54 #include <wtf/text/CString.h> |
|
55 |
|
56 namespace WebCore { |
|
57 |
|
58 static const char randomCharacterInSecWebSocketKey[] = "!\"#$%&'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; |
|
59 |
|
60 static String resourceName(const KURL& url) |
|
61 { |
|
62 String name = url.path(); |
|
63 if (name.isEmpty()) |
|
64 name = "/"; |
|
65 if (!url.query().isNull()) |
|
66 name += "?" + url.query(); |
|
67 ASSERT(!name.isEmpty()); |
|
68 ASSERT(!name.contains(' ')); |
|
69 return name; |
|
70 } |
|
71 |
|
72 static String hostName(const KURL& url, bool secure) |
|
73 { |
|
74 ASSERT(url.protocolIs("wss") == secure); |
|
75 StringBuilder builder; |
|
76 builder.append(url.host().lower()); |
|
77 if (url.port() && ((!secure && url.port() != 80) || (secure && url.port() != 443))) { |
|
78 builder.append(":"); |
|
79 builder.append(String::number(url.port())); |
|
80 } |
|
81 return builder.toString(); |
|
82 } |
|
83 |
|
84 static const size_t maxConsoleMessageSize = 128; |
|
85 static String trimConsoleMessage(const char* p, size_t len) |
|
86 { |
|
87 String s = String(p, std::min<size_t>(len, maxConsoleMessageSize)); |
|
88 if (len > maxConsoleMessageSize) |
|
89 s.append(horizontalEllipsis); |
|
90 return s; |
|
91 } |
|
92 |
|
93 static void generateSecWebSocketKey(uint32_t& number, String& key) |
|
94 { |
|
95 uint32_t space = static_cast<uint32_t>(randomNumber() * 12) + 1; |
|
96 uint32_t max = 4294967295U / space; |
|
97 number = static_cast<uint32_t>(randomNumber() * max); |
|
98 uint32_t product = number * space; |
|
99 |
|
100 String s = String::number(product); |
|
101 int n = static_cast<int>(randomNumber() * 12) + 1; |
|
102 DEFINE_STATIC_LOCAL(String, randomChars, (randomCharacterInSecWebSocketKey)); |
|
103 for (int i = 0; i < n; i++) { |
|
104 int pos = static_cast<int>(randomNumber() * (s.length() + 1)); |
|
105 int chpos = static_cast<int>(randomNumber() * randomChars.length()); |
|
106 s.insert(randomChars.substring(chpos, 1), pos); |
|
107 } |
|
108 DEFINE_STATIC_LOCAL(String, spaceChar, (" ")); |
|
109 for (uint32_t i = 0; i < space; i++) { |
|
110 int pos = static_cast<int>(randomNumber() * (s.length() - 1)) + 1; |
|
111 s.insert(spaceChar, pos); |
|
112 } |
|
113 ASSERT(s[0] != ' '); |
|
114 ASSERT(s[s.length() - 1] != ' '); |
|
115 key = s; |
|
116 } |
|
117 |
|
118 static void generateKey3(unsigned char key3[8]) |
|
119 { |
|
120 for (int i = 0; i < 8; i++) |
|
121 key3[i] = randomNumber() * 256; |
|
122 } |
|
123 |
|
124 static void setChallengeNumber(unsigned char* buf, uint32_t number) |
|
125 { |
|
126 unsigned char* p = buf + 3; |
|
127 for (int i = 0; i < 4; i++) { |
|
128 *p = number & 0xFF; |
|
129 --p; |
|
130 number >>= 8; |
|
131 } |
|
132 } |
|
133 |
|
134 static void generateExpectedChallengeResponse(uint32_t number1, uint32_t number2, unsigned char key3[8], unsigned char expectedChallenge[16]) |
|
135 { |
|
136 unsigned char challenge[16]; |
|
137 setChallengeNumber(&challenge[0], number1); |
|
138 setChallengeNumber(&challenge[4], number2); |
|
139 memcpy(&challenge[8], key3, 8); |
|
140 MD5 md5; |
|
141 md5.addBytes(challenge, sizeof(challenge)); |
|
142 Vector<uint8_t, 16> digest; |
|
143 md5.checksum(digest); |
|
144 memcpy(expectedChallenge, digest.data(), 16); |
|
145 } |
|
146 |
|
147 WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context) |
|
148 : m_url(url) |
|
149 , m_clientProtocol(protocol) |
|
150 , m_secure(m_url.protocolIs("wss")) |
|
151 , m_context(context) |
|
152 , m_mode(Incomplete) |
|
153 { |
|
154 uint32_t number1; |
|
155 uint32_t number2; |
|
156 generateSecWebSocketKey(number1, m_secWebSocketKey1); |
|
157 generateSecWebSocketKey(number2, m_secWebSocketKey2); |
|
158 generateKey3(m_key3); |
|
159 generateExpectedChallengeResponse(number1, number2, m_key3, m_expectedChallengeResponse); |
|
160 } |
|
161 |
|
162 WebSocketHandshake::~WebSocketHandshake() |
|
163 { |
|
164 } |
|
165 |
|
166 const KURL& WebSocketHandshake::url() const |
|
167 { |
|
168 return m_url; |
|
169 } |
|
170 |
|
171 void WebSocketHandshake::setURL(const KURL& url) |
|
172 { |
|
173 m_url = url.copy(); |
|
174 } |
|
175 |
|
176 const String WebSocketHandshake::host() const |
|
177 { |
|
178 return m_url.host().lower(); |
|
179 } |
|
180 |
|
181 const String& WebSocketHandshake::clientProtocol() const |
|
182 { |
|
183 return m_clientProtocol; |
|
184 } |
|
185 |
|
186 void WebSocketHandshake::setClientProtocol(const String& protocol) |
|
187 { |
|
188 m_clientProtocol = protocol; |
|
189 } |
|
190 |
|
191 bool WebSocketHandshake::secure() const |
|
192 { |
|
193 return m_secure; |
|
194 } |
|
195 |
|
196 String WebSocketHandshake::clientOrigin() const |
|
197 { |
|
198 return m_context->securityOrigin()->toString(); |
|
199 } |
|
200 |
|
201 String WebSocketHandshake::clientLocation() const |
|
202 { |
|
203 StringBuilder builder; |
|
204 builder.append(m_secure ? "wss" : "ws"); |
|
205 builder.append("://"); |
|
206 builder.append(hostName(m_url, m_secure)); |
|
207 builder.append(resourceName(m_url)); |
|
208 return builder.toString(); |
|
209 } |
|
210 |
|
211 CString WebSocketHandshake::clientHandshakeMessage() const |
|
212 { |
|
213 // Keep the following consistent with clientHandshakeRequest(). |
|
214 StringBuilder builder; |
|
215 |
|
216 builder.append("GET "); |
|
217 builder.append(resourceName(m_url)); |
|
218 builder.append(" HTTP/1.1\r\n"); |
|
219 |
|
220 Vector<String> fields; |
|
221 fields.append("Upgrade: WebSocket"); |
|
222 fields.append("Connection: Upgrade"); |
|
223 fields.append("Host: " + hostName(m_url, m_secure)); |
|
224 fields.append("Origin: " + clientOrigin()); |
|
225 if (!m_clientProtocol.isEmpty()) |
|
226 fields.append("Sec-WebSocket-Protocol: " + m_clientProtocol); |
|
227 |
|
228 KURL url = httpURLForAuthenticationAndCookies(); |
|
229 if (m_context->isDocument()) { |
|
230 Document* document = static_cast<Document*>(m_context); |
|
231 String cookie = cookieRequestHeaderFieldValue(document, url); |
|
232 if (!cookie.isEmpty()) |
|
233 fields.append("Cookie: " + cookie); |
|
234 // Set "Cookie2: <cookie>" if cookies 2 exists for url? |
|
235 } |
|
236 |
|
237 fields.append("Sec-WebSocket-Key1: " + m_secWebSocketKey1); |
|
238 fields.append("Sec-WebSocket-Key2: " + m_secWebSocketKey2); |
|
239 |
|
240 // Fields in the handshake are sent by the client in a random order; the |
|
241 // order is not meaningful. Thus, it's ok to send the order we constructed |
|
242 // the fields. |
|
243 |
|
244 for (size_t i = 0; i < fields.size(); i++) { |
|
245 builder.append(fields[i]); |
|
246 builder.append("\r\n"); |
|
247 } |
|
248 |
|
249 builder.append("\r\n"); |
|
250 |
|
251 CString handshakeHeader = builder.toString().utf8(); |
|
252 char* characterBuffer = 0; |
|
253 CString msg = CString::newUninitialized(handshakeHeader.length() + sizeof(m_key3), characterBuffer); |
|
254 memcpy(characterBuffer, handshakeHeader.data(), handshakeHeader.length()); |
|
255 memcpy(characterBuffer + handshakeHeader.length(), m_key3, sizeof(m_key3)); |
|
256 return msg; |
|
257 } |
|
258 |
|
259 WebSocketHandshakeRequest WebSocketHandshake::clientHandshakeRequest() const |
|
260 { |
|
261 // Keep the following consistent with clientHandshakeMessage(). |
|
262 // FIXME: do we need to store m_secWebSocketKey1, m_secWebSocketKey2 and |
|
263 // m_key3 in WebSocketHandshakeRequest? |
|
264 WebSocketHandshakeRequest request("GET", m_url); |
|
265 request.addHeaderField("Upgrade", "WebSocket"); |
|
266 request.addHeaderField("Connection", "Upgrade"); |
|
267 request.addHeaderField("Host", hostName(m_url, m_secure)); |
|
268 request.addHeaderField("Origin", clientOrigin()); |
|
269 if (!m_clientProtocol.isEmpty()) |
|
270 request.addHeaderField("Sec-WebSocket-Protocol:", m_clientProtocol); |
|
271 |
|
272 KURL url = httpURLForAuthenticationAndCookies(); |
|
273 if (m_context->isDocument()) { |
|
274 Document* document = static_cast<Document*>(m_context); |
|
275 String cookie = cookieRequestHeaderFieldValue(document, url); |
|
276 if (!cookie.isEmpty()) |
|
277 request.addHeaderField("Cookie", cookie); |
|
278 // Set "Cookie2: <cookie>" if cookies 2 exists for url? |
|
279 } |
|
280 |
|
281 request.addHeaderField("Sec-WebSocket-Key1", m_secWebSocketKey1); |
|
282 request.addHeaderField("Sec-WebSocket-Key2", m_secWebSocketKey2); |
|
283 request.setKey3(m_key3); |
|
284 |
|
285 return request; |
|
286 } |
|
287 |
|
288 void WebSocketHandshake::reset() |
|
289 { |
|
290 m_mode = Incomplete; |
|
291 |
|
292 m_wsOrigin = String(); |
|
293 m_wsLocation = String(); |
|
294 m_wsProtocol = String(); |
|
295 m_setCookie = String(); |
|
296 m_setCookie2 = String(); |
|
297 } |
|
298 |
|
299 void WebSocketHandshake::clearScriptExecutionContext() |
|
300 { |
|
301 m_context = 0; |
|
302 } |
|
303 |
|
304 int WebSocketHandshake::readServerHandshake(const char* header, size_t len) |
|
305 { |
|
306 m_mode = Incomplete; |
|
307 int statusCode; |
|
308 String statusText; |
|
309 int lineLength = readStatusLine(header, len, statusCode, statusText); |
|
310 if (lineLength == -1) |
|
311 return -1; |
|
312 if (statusCode == -1) { |
|
313 m_mode = Failed; |
|
314 return len; |
|
315 } |
|
316 LOG(Network, "response code: %d", statusCode); |
|
317 m_response.setStatusCode(statusCode); |
|
318 m_response.setStatusText(statusText); |
|
319 if (statusCode != 101) { |
|
320 m_mode = Failed; |
|
321 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, String::format("Unexpected response code: %d", statusCode), 0, clientOrigin()); |
|
322 return len; |
|
323 } |
|
324 m_mode = Normal; |
|
325 if (!strnstr(header, "\r\n\r\n", len)) { |
|
326 // Just hasn't been received fully yet. |
|
327 m_mode = Incomplete; |
|
328 return -1; |
|
329 } |
|
330 const char* p = readHTTPHeaders(header + lineLength, header + len); |
|
331 if (!p) { |
|
332 LOG(Network, "readHTTPHeaders failed"); |
|
333 m_mode = Failed; |
|
334 return len; |
|
335 } |
|
336 processHeaders(); |
|
337 if (!checkResponseHeaders()) { |
|
338 LOG(Network, "header process failed"); |
|
339 m_mode = Failed; |
|
340 return p - header; |
|
341 } |
|
342 if (len < static_cast<size_t>(p - header + sizeof(m_expectedChallengeResponse))) { |
|
343 // Just hasn't been received /expected/ yet. |
|
344 m_mode = Incomplete; |
|
345 return -1; |
|
346 } |
|
347 m_response.setChallengeResponse(static_cast<const unsigned char*>(static_cast<const void*>(p))); |
|
348 if (memcmp(p, m_expectedChallengeResponse, sizeof(m_expectedChallengeResponse))) { |
|
349 m_mode = Failed; |
|
350 return (p - header) + sizeof(m_expectedChallengeResponse); |
|
351 } |
|
352 m_mode = Connected; |
|
353 return (p - header) + sizeof(m_expectedChallengeResponse); |
|
354 } |
|
355 |
|
356 WebSocketHandshake::Mode WebSocketHandshake::mode() const |
|
357 { |
|
358 return m_mode; |
|
359 } |
|
360 |
|
361 const String& WebSocketHandshake::serverWebSocketOrigin() const |
|
362 { |
|
363 return m_wsOrigin; |
|
364 } |
|
365 |
|
366 void WebSocketHandshake::setServerWebSocketOrigin(const String& webSocketOrigin) |
|
367 { |
|
368 m_wsOrigin = webSocketOrigin; |
|
369 } |
|
370 |
|
371 const String& WebSocketHandshake::serverWebSocketLocation() const |
|
372 { |
|
373 return m_wsLocation; |
|
374 } |
|
375 |
|
376 void WebSocketHandshake::setServerWebSocketLocation(const String& webSocketLocation) |
|
377 { |
|
378 m_wsLocation = webSocketLocation; |
|
379 } |
|
380 |
|
381 const String& WebSocketHandshake::serverWebSocketProtocol() const |
|
382 { |
|
383 return m_wsProtocol; |
|
384 } |
|
385 |
|
386 void WebSocketHandshake::setServerWebSocketProtocol(const String& webSocketProtocol) |
|
387 { |
|
388 m_wsProtocol = webSocketProtocol; |
|
389 } |
|
390 |
|
391 const String& WebSocketHandshake::serverSetCookie() const |
|
392 { |
|
393 return m_setCookie; |
|
394 } |
|
395 |
|
396 void WebSocketHandshake::setServerSetCookie(const String& setCookie) |
|
397 { |
|
398 m_setCookie = setCookie; |
|
399 } |
|
400 |
|
401 const String& WebSocketHandshake::serverSetCookie2() const |
|
402 { |
|
403 return m_setCookie2; |
|
404 } |
|
405 |
|
406 void WebSocketHandshake::setServerSetCookie2(const String& setCookie2) |
|
407 { |
|
408 m_setCookie2 = setCookie2; |
|
409 } |
|
410 |
|
411 const WebSocketHandshakeResponse& WebSocketHandshake::serverHandshakeResponse() const |
|
412 { |
|
413 return m_response; |
|
414 } |
|
415 |
|
416 KURL WebSocketHandshake::httpURLForAuthenticationAndCookies() const |
|
417 { |
|
418 KURL url = m_url.copy(); |
|
419 bool couldSetProtocol = url.setProtocol(m_secure ? "https" : "http"); |
|
420 ASSERT_UNUSED(couldSetProtocol, couldSetProtocol); |
|
421 return url; |
|
422 } |
|
423 |
|
424 // Returns the header length (including "\r\n"), or -1 if we have not received enough data yet. |
|
425 // If the line is malformed or the status code is not a 3-digit number, |
|
426 // statusCode and statusText will be set to -1 and a null string, respectively. |
|
427 int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength, int& statusCode, String& statusText) |
|
428 { |
|
429 statusCode = -1; |
|
430 statusText = String(); |
|
431 |
|
432 const char* space1 = 0; |
|
433 const char* space2 = 0; |
|
434 const char* p; |
|
435 size_t consumedLength; |
|
436 |
|
437 for (p = header, consumedLength = 0; consumedLength < headerLength; p++, consumedLength++) { |
|
438 if (*p == ' ') { |
|
439 if (!space1) |
|
440 space1 = p; |
|
441 else if (!space2) |
|
442 space2 = p; |
|
443 } else if (*p == '\n') |
|
444 break; |
|
445 } |
|
446 if (consumedLength == headerLength) |
|
447 return -1; // We have not received '\n' yet. |
|
448 |
|
449 const char* end = p + 1; |
|
450 if (end - header > INT_MAX) { |
|
451 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Status line is too long: " + trimConsoleMessage(header, maxConsoleMessageSize + 1), 0, clientOrigin()); |
|
452 return INT_MAX; |
|
453 } |
|
454 int lineLength = end - header; |
|
455 |
|
456 if (!space1 || !space2) { |
|
457 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "No response code found: " + trimConsoleMessage(header, lineLength - 1), 0, clientOrigin()); |
|
458 return lineLength; |
|
459 } |
|
460 |
|
461 // The line must end with "\r\n". |
|
462 if (*(end - 2) != '\r') { |
|
463 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Status line does not end with CRLF", 0, clientOrigin()); |
|
464 return lineLength; |
|
465 } |
|
466 |
|
467 String statusCodeString(space1 + 1, space2 - space1 - 1); |
|
468 if (statusCodeString.length() != 3) // Status code must consist of three digits. |
|
469 return lineLength; |
|
470 for (int i = 0; i < 3; ++i) |
|
471 if (statusCodeString[i] < '0' || statusCodeString[i] > '9') { |
|
472 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Invalid status code: " + statusCodeString, 0, clientOrigin()); |
|
473 return lineLength; |
|
474 } |
|
475 |
|
476 bool ok = false; |
|
477 statusCode = statusCodeString.toInt(&ok); |
|
478 ASSERT(ok); |
|
479 |
|
480 statusText = String(space2 + 1, end - space2 - 3); // Exclude "\r\n". |
|
481 return lineLength; |
|
482 } |
|
483 |
|
484 const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end) |
|
485 { |
|
486 m_response.clearHeaderFields(); |
|
487 |
|
488 Vector<char> name; |
|
489 Vector<char> value; |
|
490 for (const char* p = start; p < end; p++) { |
|
491 name.clear(); |
|
492 value.clear(); |
|
493 |
|
494 for (; p < end; p++) { |
|
495 switch (*p) { |
|
496 case '\r': |
|
497 if (name.isEmpty()) { |
|
498 if (p + 1 < end && *(p + 1) == '\n') |
|
499 return p + 2; |
|
500 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "CR doesn't follow LF at " + trimConsoleMessage(p, end - p), 0, clientOrigin()); |
|
501 return 0; |
|
502 } |
|
503 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected CR in name at " + trimConsoleMessage(name.data(), name.size()), 0, clientOrigin()); |
|
504 return 0; |
|
505 case '\n': |
|
506 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in name at " + trimConsoleMessage(name.data(), name.size()), 0, clientOrigin()); |
|
507 return 0; |
|
508 case ':': |
|
509 break; |
|
510 default: |
|
511 name.append(*p); |
|
512 continue; |
|
513 } |
|
514 if (*p == ':') { |
|
515 ++p; |
|
516 break; |
|
517 } |
|
518 } |
|
519 |
|
520 for (; p < end && *p == 0x20; p++) { } |
|
521 |
|
522 for (; p < end; p++) { |
|
523 switch (*p) { |
|
524 case '\r': |
|
525 break; |
|
526 case '\n': |
|
527 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in value at " + trimConsoleMessage(value.data(), value.size()), 0, clientOrigin()); |
|
528 return 0; |
|
529 default: |
|
530 value.append(*p); |
|
531 } |
|
532 if (*p == '\r') { |
|
533 ++p; |
|
534 break; |
|
535 } |
|
536 } |
|
537 if (p >= end || *p != '\n') { |
|
538 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "CR doesn't follow LF after value at " + trimConsoleMessage(p, end - p), 0, clientOrigin()); |
|
539 return 0; |
|
540 } |
|
541 AtomicString nameStr(String::fromUTF8(name.data(), name.size())); |
|
542 String valueStr = String::fromUTF8(value.data(), value.size()); |
|
543 if (nameStr.isNull()) { |
|
544 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "invalid UTF-8 sequence in header name", 0, clientOrigin()); |
|
545 return 0; |
|
546 } |
|
547 if (valueStr.isNull()) { |
|
548 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "invalid UTF-8 sequence in header value", 0, clientOrigin()); |
|
549 return 0; |
|
550 } |
|
551 LOG(Network, "name=%s value=%s", nameStr.string().utf8().data(), valueStr.utf8().data()); |
|
552 m_response.addHeaderField(nameStr, valueStr); |
|
553 } |
|
554 ASSERT_NOT_REACHED(); |
|
555 return 0; |
|
556 } |
|
557 |
|
558 void WebSocketHandshake::processHeaders() |
|
559 { |
|
560 ASSERT(m_mode == Normal); |
|
561 const HTTPHeaderMap& headers = m_response.headerFields(); |
|
562 m_wsOrigin = headers.get("sec-websocket-origin"); |
|
563 m_wsLocation = headers.get("sec-websocket-location"); |
|
564 m_wsProtocol = headers.get("sec-websocket-protocol"); |
|
565 m_setCookie = headers.get("set-cookie"); |
|
566 m_setCookie2 = headers.get("set-cookie2"); |
|
567 } |
|
568 |
|
569 bool WebSocketHandshake::checkResponseHeaders() |
|
570 { |
|
571 if (m_wsOrigin.isNull()) { |
|
572 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'sec-websocket-origin' header is missing", 0, clientOrigin()); |
|
573 return false; |
|
574 } |
|
575 if (m_wsLocation.isNull()) { |
|
576 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'sec-websocket-location' header is missing", 0, clientOrigin()); |
|
577 return false; |
|
578 } |
|
579 |
|
580 if (clientOrigin() != m_wsOrigin) { |
|
581 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: origin mismatch: " + clientOrigin() + " != " + m_wsOrigin, 0, clientOrigin()); |
|
582 return false; |
|
583 } |
|
584 if (clientLocation() != m_wsLocation) { |
|
585 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: location mismatch: " + clientLocation() + " != " + m_wsLocation, 0, clientOrigin()); |
|
586 return false; |
|
587 } |
|
588 if (!m_clientProtocol.isEmpty() && m_clientProtocol != m_wsProtocol) { |
|
589 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: protocol mismatch: " + m_clientProtocol + " != " + m_wsProtocol, 0, clientOrigin()); |
|
590 return false; |
|
591 } |
|
592 return true; |
|
593 } |
|
594 |
|
595 } // namespace WebCore |
|
596 |
|
597 #endif // ENABLE(WEB_SOCKETS) |