OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2011, 2012 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 #include "modules/websockets/MainThreadWebSocketChannel.h" | |
33 | |
34 #include "bindings/core/v8/ExceptionStatePlaceholder.h" | |
35 #include "core/dom/Document.h" | |
36 #include "core/dom/ExecutionContext.h" | |
37 #include "core/fileapi/Blob.h" | |
38 #include "core/fileapi/FileReaderLoader.h" | |
39 #include "core/frame/LocalFrame.h" | |
40 #include "core/inspector/ConsoleMessage.h" | |
41 #include "core/inspector/InspectorInstrumentation.h" | |
42 #include "core/inspector/InspectorTraceEvents.h" | |
43 #include "core/loader/FrameLoader.h" | |
44 #include "core/loader/FrameLoaderClient.h" | |
45 #include "core/loader/MixedContentChecker.h" | |
46 #include "core/loader/UniqueIdentifier.h" | |
47 #include "core/page/Page.h" | |
48 #include "modules/websockets/WebSocketChannelClient.h" | |
49 #include "platform/Logging.h" | |
50 #include "platform/network/SocketStreamError.h" | |
51 #include "platform/network/SocketStreamHandle.h" | |
52 #include "wtf/ArrayBuffer.h" | |
53 #include "wtf/FastMalloc.h" | |
54 #include "wtf/HashMap.h" | |
55 #include "wtf/OwnPtr.h" | |
56 #include "wtf/text/StringHash.h" | |
57 #include "wtf/text/WTFString.h" | |
58 | |
59 namespace blink { | |
60 | |
61 const double TCPMaximumSegmentLifetime = 2 * 60.0; | |
62 | |
63 MainThreadWebSocketChannel::MainThreadWebSocketChannel(Document* document, WebSo
cketChannelClient* client, const String& sourceURL, unsigned lineNumber) | |
64 : m_document(document) | |
65 , m_client(client) | |
66 , m_resumeTimer(this, &MainThreadWebSocketChannel::resumeTimerFired) | |
67 , m_suspended(false) | |
68 , m_didSendErrorToClient(false) | |
69 , m_hasCalledDisconnectOnHandle(false) | |
70 , m_receivedClosingHandshake(false) | |
71 , m_closingTimer(this, &MainThreadWebSocketChannel::closingTimerFired) | |
72 , m_state(ChannelIdle) | |
73 , m_shouldDiscardReceivedData(false) | |
74 , m_identifier(0) | |
75 , m_hasContinuousFrame(false) | |
76 , m_closeEventCode(CloseEventCodeAbnormalClosure) | |
77 , m_outgoingFrameQueueStatus(OutgoingFrameQueueOpen) | |
78 , m_numConsumedBytesInCurrentFrame(0) | |
79 , m_blobLoaderStatus(BlobLoaderNotStarted) | |
80 , m_sourceURLAtConstruction(sourceURL) | |
81 , m_lineNumberAtConstruction(lineNumber) | |
82 { | |
83 if (m_document->page()) | |
84 m_identifier = createUniqueIdentifier(); | |
85 } | |
86 | |
87 MainThreadWebSocketChannel::~MainThreadWebSocketChannel() | |
88 { | |
89 } | |
90 | |
91 bool MainThreadWebSocketChannel::connect(const KURL& url, const String& protocol
) | |
92 { | |
93 WTF_LOG(Network, "MainThreadWebSocketChannel %p connect()", this); | |
94 ASSERT(!m_handle); | |
95 ASSERT(!m_suspended); | |
96 | |
97 if (m_document->frame() && !m_document->frame()->loader().mixedContentChecke
r()->canConnectInsecureWebSocket(m_document->securityOrigin(), url)) | |
98 return false; | |
99 if (MixedContentChecker::isMixedContent(m_document->securityOrigin(), url))
{ | |
100 String message = "Connecting to a non-secure WebSocket server from a sec
ure origin is deprecated."; | |
101 m_document->addConsoleMessage(ConsoleMessage::create(JSMessageSource, Wa
rningMessageLevel, message)); | |
102 } | |
103 | |
104 m_handshake = new WebSocketHandshake(url, protocol, m_document); | |
105 m_handshake->reset(); | |
106 m_handshake->addExtensionProcessor(m_perMessageDeflate.createExtensionProces
sor()); | |
107 m_handshake->addExtensionProcessor(m_deflateFramer.createExtensionProcessor(
)); | |
108 if (m_identifier) { | |
109 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "We
bSocketCreate", "data", InspectorWebSocketCreateEvent::data(m_document, m_identi
fier, url, protocol)); | |
110 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"
), "CallStack", "stack", InspectorCallStackEvent::currentCallStack()); | |
111 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Ti
meline migrates to tracing. | |
112 InspectorInstrumentation::didCreateWebSocket(m_document, m_identifier, u
rl, protocol); | |
113 } | |
114 ref(); | |
115 | |
116 m_handle = SocketStreamHandle::create(this); | |
117 ASSERT(m_handle); | |
118 if (m_document->frame()) { | |
119 m_document->frame()->loader().client()->dispatchWillOpenSocketStream(m_h
andle.get()); | |
120 } | |
121 m_handle->connect(m_handshake->url()); | |
122 | |
123 return true; | |
124 } | |
125 | |
126 void MainThreadWebSocketChannel::send(const String& message) | |
127 { | |
128 WTF_LOG(Network, "MainThreadWebSocketChannel %p send() Sending String '%s'",
this, message.utf8().data()); | |
129 CString utf8 = message.utf8(StrictUTF8ConversionReplacingUnpairedSurrogatesW
ithFFFD); | |
130 enqueueTextFrame(utf8); | |
131 processOutgoingFrameQueue(); | |
132 } | |
133 | |
134 void MainThreadWebSocketChannel::send(const ArrayBuffer& binaryData, unsigned by
teOffset, unsigned byteLength) | |
135 { | |
136 WTF_LOG(Network, "MainThreadWebSocketChannel %p send() Sending ArrayBuffer %
p byteOffset=%u byteLength=%u", this, &binaryData, byteOffset, byteLength); | |
137 enqueueRawFrame(WebSocketFrame::OpCodeBinary, static_cast<const char*>(binar
yData.data()) + byteOffset, byteLength); | |
138 processOutgoingFrameQueue(); | |
139 } | |
140 | |
141 void MainThreadWebSocketChannel::send(PassRefPtr<BlobDataHandle> binaryData) | |
142 { | |
143 WTF_LOG(Network, "MainThreadWebSocketChannel %p send() Sending Blob '%s'", t
his, binaryData->uuid().utf8().data()); | |
144 enqueueBlobFrame(WebSocketFrame::OpCodeBinary, binaryData); | |
145 processOutgoingFrameQueue(); | |
146 } | |
147 | |
148 void MainThreadWebSocketChannel::send(PassOwnPtr<Vector<char> > data) | |
149 { | |
150 WTF_LOG(Network, "MainThreadWebSocketChannel %p send() Sending Vector %p", t
his, data.get()); | |
151 enqueueVector(WebSocketFrame::OpCodeBinary, data); | |
152 processOutgoingFrameQueue(); | |
153 } | |
154 | |
155 void MainThreadWebSocketChannel::close(int code, const String& reason) | |
156 { | |
157 WTF_LOG(Network, "MainThreadWebSocketChannel %p close() code=%d reason='%s'"
, this, code, reason.utf8().data()); | |
158 ASSERT(!m_suspended); | |
159 if (!m_handle) | |
160 return; | |
161 startClosingHandshake(code, reason); | |
162 if (!m_closingTimer.isActive()) | |
163 m_closingTimer.startOneShot(2 * TCPMaximumSegmentLifetime, FROM_HERE); | |
164 } | |
165 | |
166 void MainThreadWebSocketChannel::clearDocument() | |
167 { | |
168 if (m_handshake) | |
169 m_handshake->clearDocument(); | |
170 m_document = nullptr; | |
171 } | |
172 | |
173 void MainThreadWebSocketChannel::disconnectHandle() | |
174 { | |
175 if (!m_handle) | |
176 return; | |
177 m_hasCalledDisconnectOnHandle = true; | |
178 m_handle->disconnect(); | |
179 } | |
180 | |
181 void MainThreadWebSocketChannel::callDidError() | |
182 { | |
183 if (!m_client || m_didSendErrorToClient) | |
184 return; | |
185 m_didSendErrorToClient = true; | |
186 m_client->didError(); | |
187 } | |
188 | |
189 void MainThreadWebSocketChannel::fail(const String& reason, MessageLevel level,
const String& sourceURL, unsigned lineNumber) | |
190 { | |
191 WTF_LOG(Network, "MainThreadWebSocketChannel %p fail() reason='%s'", this, r
eason.utf8().data()); | |
192 if (m_document) { | |
193 InspectorInstrumentation::didReceiveWebSocketFrameError(m_document, m_id
entifier, reason); | |
194 const String message = "WebSocket connection to '" + m_handshake->url().
elidedString() + "' failed: " + reason; | |
195 m_document->addConsoleMessage(ConsoleMessage::create(JSMessageSource, le
vel, message, sourceURL, lineNumber)); | |
196 } | |
197 // Hybi-10 specification explicitly states we must not continue to handle in
coming data | |
198 // once the WebSocket connection is failed (section 7.1.7). | |
199 m_shouldDiscardReceivedData = true; | |
200 if (!m_buffer.isEmpty()) | |
201 skipBuffer(m_buffer.size()); // Save memory. | |
202 m_deflateFramer.didFail(); | |
203 m_perMessageDeflate.didFail(); | |
204 m_hasContinuousFrame = false; | |
205 m_continuousFrameData.clear(); | |
206 | |
207 callDidError(); | |
208 | |
209 if (m_state != ChannelClosed) | |
210 disconnectHandle(); // Will call didCloseSocketStream(). | |
211 } | |
212 | |
213 void MainThreadWebSocketChannel::disconnect() | |
214 { | |
215 WTF_LOG(Network, "MainThreadWebSocketChannel %p disconnect()", this); | |
216 if (m_identifier && m_document) { | |
217 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "We
bSocketDestroy", "data", InspectorWebSocketEvent::data(m_document, m_identifier)
); | |
218 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"
), "CallStack", "stack", InspectorCallStackEvent::currentCallStack()); | |
219 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Ti
meline migrates to tracing. | |
220 InspectorInstrumentation::didCloseWebSocket(m_document, m_identifier); | |
221 } | |
222 | |
223 clearDocument(); | |
224 | |
225 m_client = nullptr; | |
226 disconnectHandle(); | |
227 } | |
228 | |
229 void MainThreadWebSocketChannel::suspend() | |
230 { | |
231 m_suspended = true; | |
232 } | |
233 | |
234 void MainThreadWebSocketChannel::resume() | |
235 { | |
236 m_suspended = false; | |
237 if ((!m_buffer.isEmpty() || (m_state == ChannelClosed)) && m_client && !m_re
sumeTimer.isActive()) | |
238 m_resumeTimer.startOneShot(0, FROM_HERE); | |
239 } | |
240 | |
241 void MainThreadWebSocketChannel::didOpenSocketStream(SocketStreamHandle* handle) | |
242 { | |
243 WTF_LOG(Network, "MainThreadWebSocketChannel %p didOpenSocketStream()", this
); | |
244 ASSERT(handle == m_handle); | |
245 if (!m_document) | |
246 return; | |
247 if (m_identifier) { | |
248 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "We
bSocketSendHandshakeRequest", "data", InspectorWebSocketEvent::data(m_document,
m_identifier)); | |
249 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"
), "CallStack", "stack", InspectorCallStackEvent::currentCallStack()); | |
250 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Ti
meline migrates to tracing. | |
251 InspectorInstrumentation::willSendWebSocketHandshakeRequest(m_document,
m_identifier, m_handshake->clientHandshakeRequest().get()); | |
252 } | |
253 CString handshakeMessage = m_handshake->clientHandshakeMessage(); | |
254 if (!handle->send(handshakeMessage.data(), handshakeMessage.length())) | |
255 failAsError("Failed to send WebSocket handshake."); | |
256 } | |
257 | |
258 void MainThreadWebSocketChannel::didCloseSocketStream(SocketStreamHandle* handle
) | |
259 { | |
260 WTF_LOG(Network, "MainThreadWebSocketChannel %p didCloseSocketStream()", thi
s); | |
261 if (m_identifier && m_document) { | |
262 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "We
bSocketDestroy", "data", InspectorWebSocketEvent::data(m_document, m_identifier)
); | |
263 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"
), "CallStack", "stack", InspectorCallStackEvent::currentCallStack()); | |
264 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Ti
meline migrates to tracing. | |
265 InspectorInstrumentation::didCloseWebSocket(m_document, m_identifier); | |
266 } | |
267 ASSERT_UNUSED(handle, handle == m_handle || !m_handle); | |
268 | |
269 // Show error message on JS console if this is unexpected connection close | |
270 // during opening handshake. | |
271 if (!m_hasCalledDisconnectOnHandle && m_handshake->mode() == WebSocketHandsh
ake::Incomplete && m_document) { | |
272 const String message = "WebSocket connection to '" + m_handshake->url().
elidedString() + "' failed: Connection closed before receiving a handshake respo
nse"; | |
273 m_document->addConsoleMessage(ConsoleMessage::create(JSMessageSource, Er
rorMessageLevel, message, m_sourceURLAtConstruction, m_lineNumberAtConstruction)
); | |
274 } | |
275 | |
276 m_state = ChannelClosed; | |
277 if (m_closingTimer.isActive()) | |
278 m_closingTimer.stop(); | |
279 if (m_outgoingFrameQueueStatus != OutgoingFrameQueueClosed) | |
280 abortOutgoingFrameQueue(); | |
281 if (m_handle) { | |
282 WebSocketChannelClient* client = m_client; | |
283 m_client = nullptr; | |
284 clearDocument(); | |
285 m_handle = nullptr; | |
286 if (client) | |
287 client->didClose(m_receivedClosingHandshake ? WebSocketChannelClient
::ClosingHandshakeComplete : WebSocketChannelClient::ClosingHandshakeIncomplete,
m_closeEventCode, m_closeEventReason); | |
288 } | |
289 deref(); | |
290 } | |
291 | |
292 void MainThreadWebSocketChannel::didReceiveSocketStreamData(SocketStreamHandle*
handle, const char* data, int len) | |
293 { | |
294 WTF_LOG(Network, "MainThreadWebSocketChannel %p didReceiveSocketStreamData()
Received %d bytes", this, len); | |
295 ASSERT(handle == m_handle); | |
296 if (!m_document) | |
297 return; | |
298 if (len <= 0) { | |
299 disconnectHandle(); | |
300 return; | |
301 } | |
302 if (!m_client) { | |
303 m_shouldDiscardReceivedData = true; | |
304 disconnectHandle(); | |
305 return; | |
306 } | |
307 if (m_shouldDiscardReceivedData) | |
308 return; | |
309 if (!appendToBuffer(data, len)) { | |
310 m_shouldDiscardReceivedData = true; | |
311 failAsError("Ran out of memory while receiving WebSocket data."); | |
312 return; | |
313 } | |
314 processBuffer(); | |
315 } | |
316 | |
317 void MainThreadWebSocketChannel::didConsumeBufferedAmount(SocketStreamHandle*, s
ize_t consumed) | |
318 { | |
319 if (m_framingOverheadQueue.isEmpty()) { | |
320 // Ignore the handshake consumption. | |
321 return; | |
322 } | |
323 if (!m_client || m_state == ChannelClosed) | |
324 return; | |
325 size_t remain = consumed; | |
326 while (remain > 0) { | |
327 ASSERT(!m_framingOverheadQueue.isEmpty()); | |
328 const FramingOverhead& frame = m_framingOverheadQueue.first(); | |
329 | |
330 ASSERT(m_numConsumedBytesInCurrentFrame <= frame.frameDataSize()); | |
331 size_t consumedInThisFrame = std::min(remain, frame.frameDataSize() - m_
numConsumedBytesInCurrentFrame); | |
332 remain -= consumedInThisFrame; | |
333 m_numConsumedBytesInCurrentFrame += consumedInThisFrame; | |
334 | |
335 if (m_numConsumedBytesInCurrentFrame == frame.frameDataSize()) { | |
336 if (m_client && WebSocketFrame::isNonControlOpCode(frame.opcode()))
{ | |
337 // FIXME: As |consumed| is the number of possibly compressed | |
338 // bytes, we can't determine the number of consumed original | |
339 // bytes in the middle of a frame. | |
340 m_client->didConsumeBufferedAmount(frame.originalPayloadLength()
); | |
341 } | |
342 m_framingOverheadQueue.takeFirst(); | |
343 m_numConsumedBytesInCurrentFrame = 0; | |
344 } | |
345 } | |
346 } | |
347 | |
348 void MainThreadWebSocketChannel::didFailSocketStream(SocketStreamHandle* handle,
const SocketStreamError& error) | |
349 { | |
350 WTF_LOG(Network, "MainThreadWebSocketChannel %p didFailSocketStream()", this
); | |
351 ASSERT_UNUSED(handle, handle == m_handle || !m_handle); | |
352 m_shouldDiscardReceivedData = true; | |
353 String message; | |
354 if (error.isNull()) | |
355 message = "WebSocket network error"; | |
356 else if (error.localizedDescription().isNull()) | |
357 message = "WebSocket network error: error code " + String::number(error.
errorCode()); | |
358 else | |
359 message = "WebSocket network error: error code " + String::number(error.
errorCode()) + ", " + error.localizedDescription(); | |
360 String failingURL = error.failingURL(); | |
361 ASSERT(failingURL.isNull() || m_handshake->url().string() == failingURL); | |
362 if (failingURL.isNull()) | |
363 failingURL = m_handshake->url().string(); | |
364 WTF_LOG(Network, "Error Message: '%s', FailURL: '%s'", message.utf8().data()
, failingURL.utf8().data()); | |
365 | |
366 if (m_state != ChannelClosing && m_state != ChannelClosed) | |
367 callDidError(); | |
368 | |
369 if (m_state != ChannelClosed) | |
370 disconnectHandle(); | |
371 } | |
372 | |
373 void MainThreadWebSocketChannel::didStartLoading() | |
374 { | |
375 WTF_LOG(Network, "MainThreadWebSocketChannel %p didStartLoading()", this); | |
376 ASSERT(m_blobLoader); | |
377 ASSERT(m_blobLoaderStatus == BlobLoaderStarted); | |
378 } | |
379 | |
380 void MainThreadWebSocketChannel::didReceiveData() | |
381 { | |
382 WTF_LOG(Network, "MainThreadWebSocketChannel %p didReceiveData()", this); | |
383 ASSERT(m_blobLoader); | |
384 ASSERT(m_blobLoaderStatus == BlobLoaderStarted); | |
385 } | |
386 | |
387 void MainThreadWebSocketChannel::didFinishLoading() | |
388 { | |
389 WTF_LOG(Network, "MainThreadWebSocketChannel %p didFinishLoading()", this); | |
390 ASSERT(m_blobLoader); | |
391 ASSERT(m_blobLoaderStatus == BlobLoaderStarted); | |
392 m_blobLoaderStatus = BlobLoaderFinished; | |
393 processOutgoingFrameQueue(); | |
394 deref(); | |
395 } | |
396 | |
397 void MainThreadWebSocketChannel::didFail(FileError::ErrorCode errorCode) | |
398 { | |
399 WTF_LOG(Network, "MainThreadWebSocketChannel %p didFail() errorCode=%d", thi
s, errorCode); | |
400 ASSERT(m_blobLoader); | |
401 ASSERT(m_blobLoaderStatus == BlobLoaderStarted); | |
402 m_blobLoader.clear(); | |
403 m_blobLoaderStatus = BlobLoaderFailed; | |
404 failAsError("Failed to load Blob: error code = " + String::number(errorCode)
); // FIXME: Generate human-friendly reason message. | |
405 deref(); | |
406 } | |
407 | |
408 bool MainThreadWebSocketChannel::appendToBuffer(const char* data, size_t len) | |
409 { | |
410 size_t newBufferSize = m_buffer.size() + len; | |
411 if (newBufferSize < m_buffer.size()) { | |
412 WTF_LOG(Network, "MainThreadWebSocketChannel %p appendToBuffer() Buffer
overflow (%lu bytes already in receive buffer and appending %lu bytes)", this, s
tatic_cast<unsigned long>(m_buffer.size()), static_cast<unsigned long>(len)); | |
413 return false; | |
414 } | |
415 m_buffer.append(data, len); | |
416 return true; | |
417 } | |
418 | |
419 void MainThreadWebSocketChannel::skipBuffer(size_t len) | |
420 { | |
421 ASSERT_WITH_SECURITY_IMPLICATION(len <= m_buffer.size()); | |
422 memmove(m_buffer.data(), m_buffer.data() + len, m_buffer.size() - len); | |
423 m_buffer.resize(m_buffer.size() - len); | |
424 } | |
425 | |
426 void MainThreadWebSocketChannel::processBuffer() | |
427 { | |
428 while (!m_suspended && m_client && !m_buffer.isEmpty()) { | |
429 if (!processOneItemFromBuffer()) | |
430 break; | |
431 } | |
432 } | |
433 | |
434 bool MainThreadWebSocketChannel::processOneItemFromBuffer() | |
435 { | |
436 ASSERT(!m_suspended); | |
437 ASSERT(m_client); | |
438 ASSERT(!m_buffer.isEmpty()); | |
439 WTF_LOG(Network, "MainThreadWebSocketChannel %p processBuffer() Receive buff
er has %lu bytes", this, static_cast<unsigned long>(m_buffer.size())); | |
440 | |
441 if (m_shouldDiscardReceivedData) | |
442 return false; | |
443 | |
444 if (m_receivedClosingHandshake) { | |
445 skipBuffer(m_buffer.size()); | |
446 return false; | |
447 } | |
448 | |
449 if (m_handshake->mode() == WebSocketHandshake::Incomplete) { | |
450 int headerLength = m_handshake->readServerHandshake(m_buffer.data(), m_b
uffer.size()); | |
451 if (headerLength <= 0) | |
452 return false; | |
453 if (m_handshake->mode() == WebSocketHandshake::Connected) { | |
454 if (m_identifier) { | |
455 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timelin
e"), "WebSocketReceiveHandshakeResponse", "data", InspectorWebSocketEvent::data(
m_document, m_identifier)); | |
456 // FIXME(361045): remove InspectorInstrumentation calls once Dev
Tools Timeline migrates to tracing. | |
457 InspectorInstrumentation::didReceiveWebSocketHandshakeResponse(m
_document, m_identifier, 0, &m_handshake->serverHandshakeResponse()); | |
458 } | |
459 | |
460 if (m_deflateFramer.enabled() && m_document) { | |
461 const String message = "WebSocket extension \"x-webkit-deflate-f
rame\" is deprecated"; | |
462 m_document->addConsoleMessage(ConsoleMessage::create(JSMessageSo
urce, WarningMessageLevel, message, m_sourceURLAtConstruction, m_lineNumberAtCon
struction)); | |
463 } | |
464 | |
465 WTF_LOG(Network, "MainThreadWebSocketChannel %p Connected", this); | |
466 skipBuffer(headerLength); | |
467 String subprotocol = m_handshake->serverWebSocketProtocol(); | |
468 String extensions = m_handshake->acceptedExtensions(); | |
469 m_client->didConnect(subprotocol.isNull() ? "" : subprotocol, extens
ions.isNull() ? "" : extensions); | |
470 WTF_LOG(Network, "MainThreadWebSocketChannel %p %lu bytes remaining
in m_buffer", this, static_cast<unsigned long>(m_buffer.size())); | |
471 return !m_buffer.isEmpty(); | |
472 } | |
473 ASSERT(m_handshake->mode() == WebSocketHandshake::Failed); | |
474 WTF_LOG(Network, "MainThreadWebSocketChannel %p Connection failed", this
); | |
475 skipBuffer(headerLength); | |
476 m_shouldDiscardReceivedData = true; | |
477 failAsError(m_handshake->failureReason()); | |
478 return false; | |
479 } | |
480 if (m_handshake->mode() != WebSocketHandshake::Connected) | |
481 return false; | |
482 | |
483 return processFrame(); | |
484 } | |
485 | |
486 void MainThreadWebSocketChannel::resumeTimerFired(Timer<MainThreadWebSocketChann
el>* timer) | |
487 { | |
488 ASSERT_UNUSED(timer, timer == &m_resumeTimer); | |
489 | |
490 processBuffer(); | |
491 if (!m_suspended && m_client && (m_state == ChannelClosed) && m_handle) | |
492 didCloseSocketStream(m_handle.get()); | |
493 } | |
494 | |
495 void MainThreadWebSocketChannel::startClosingHandshake(int code, const String& r
eason) | |
496 { | |
497 WTF_LOG(Network, "MainThreadWebSocketChannel %p startClosingHandshake() code
=%d m_state=%d m_receivedClosingHandshake=%d", this, code, m_state, m_receivedCl
osingHandshake); | |
498 if (m_state == ChannelClosing || m_state == ChannelClosed) | |
499 return; | |
500 ASSERT(m_handle); | |
501 | |
502 Vector<char> buf; | |
503 if (!m_receivedClosingHandshake && code != CloseEventCodeNotSpecified) { | |
504 unsigned char highByte = code >> 8; | |
505 unsigned char lowByte = code; | |
506 buf.append(static_cast<char>(highByte)); | |
507 buf.append(static_cast<char>(lowByte)); | |
508 buf.append(reason.utf8().data(), reason.utf8().length()); | |
509 } | |
510 enqueueRawFrame(WebSocketFrame::OpCodeClose, buf.data(), buf.size()); | |
511 processOutgoingFrameQueue(); | |
512 | |
513 m_state = ChannelClosing; | |
514 if (m_client) | |
515 m_client->didStartClosingHandshake(); | |
516 } | |
517 | |
518 void MainThreadWebSocketChannel::closingTimerFired(Timer<MainThreadWebSocketChan
nel>* timer) | |
519 { | |
520 WTF_LOG(Network, "MainThreadWebSocketChannel %p closingTimerFired()", this); | |
521 ASSERT_UNUSED(timer, &m_closingTimer == timer); | |
522 disconnectHandle(); | |
523 } | |
524 | |
525 | |
526 bool MainThreadWebSocketChannel::processFrame() | |
527 { | |
528 ASSERT(!m_buffer.isEmpty()); | |
529 | |
530 WebSocketFrame frame; | |
531 const char* frameEnd; | |
532 String errorString; | |
533 WebSocketFrame::ParseFrameResult result = WebSocketFrame::parseFrame(m_buffe
r.data(), m_buffer.size(), frame, frameEnd, errorString); | |
534 if (result == WebSocketFrame::FrameIncomplete) | |
535 return false; | |
536 if (result == WebSocketFrame::FrameError) { | |
537 failAsError(errorString); | |
538 return false; | |
539 } | |
540 | |
541 ASSERT(m_buffer.data() < frameEnd); | |
542 ASSERT(frameEnd <= m_buffer.data() + m_buffer.size()); | |
543 | |
544 OwnPtr<InflateResultHolder> inflateResult = m_deflateFramer.inflate(frame); | |
545 if (!inflateResult->succeeded()) { | |
546 failAsError(inflateResult->failureReason()); | |
547 return false; | |
548 } | |
549 if (!m_perMessageDeflate.inflate(frame)) { | |
550 failAsError(m_perMessageDeflate.failureReason()); | |
551 return false; | |
552 } | |
553 | |
554 // Validate the frame data. | |
555 if (WebSocketFrame::isReservedOpCode(frame.opCode)) { | |
556 failAsError("Unrecognized frame opcode: " + String::number(frame.opCode)
); | |
557 return false; | |
558 } | |
559 | |
560 if (frame.compress || frame.reserved2 || frame.reserved3) { | |
561 failAsError("One or more reserved bits are on: reserved1 = " + String::n
umber(frame.compress) + ", reserved2 = " + String::number(frame.reserved2) + ",
reserved3 = " + String::number(frame.reserved3)); | |
562 return false; | |
563 } | |
564 | |
565 if (frame.masked) { | |
566 failAsError("A server must not mask any frames that it sends to the clie
nt."); | |
567 return false; | |
568 } | |
569 | |
570 // All control frames must not be fragmented. | |
571 if (WebSocketFrame::isControlOpCode(frame.opCode) && !frame.final) { | |
572 failAsError("Received fragmented control frame: opcode = " + String::num
ber(frame.opCode)); | |
573 return false; | |
574 } | |
575 | |
576 // All control frames must have a payload of 125 bytes or less, which means
the frame must not contain | |
577 // the "extended payload length" field. | |
578 if (WebSocketFrame::isControlOpCode(frame.opCode) && WebSocketFrame::needsEx
tendedLengthField(frame.payloadLength)) { | |
579 failAsError("Received control frame having too long payload: " + String:
:number(frame.payloadLength) + " bytes"); | |
580 return false; | |
581 } | |
582 | |
583 // A new data frame is received before the previous continuous frame finishe
s. | |
584 // Note that control frames are allowed to come in the middle of continuous
frames. | |
585 if (m_hasContinuousFrame && frame.opCode != WebSocketFrame::OpCodeContinuati
on && !WebSocketFrame::isControlOpCode(frame.opCode)) { | |
586 failAsError("Received start of new message but previous message is unfin
ished."); | |
587 return false; | |
588 } | |
589 | |
590 InspectorInstrumentation::didReceiveWebSocketFrame(m_document, m_identifier,
frame.opCode, frame.masked, frame.payload, frame.payloadLength); | |
591 | |
592 switch (frame.opCode) { | |
593 case WebSocketFrame::OpCodeContinuation: | |
594 // An unexpected continuation frame is received without any leading fram
e. | |
595 if (!m_hasContinuousFrame) { | |
596 failAsError("Received unexpected continuation frame."); | |
597 return false; | |
598 } | |
599 m_continuousFrameData.append(frame.payload, frame.payloadLength); | |
600 skipBuffer(frameEnd - m_buffer.data()); | |
601 if (frame.final) { | |
602 // onmessage handler may eventually call the other methods of this c
hannel, | |
603 // so we should pretend that we have finished to read this frame and | |
604 // make sure that the member variables are in a consistent state bef
ore | |
605 // the handler is invoked. | |
606 // Vector<char>::swap() is used here to clear m_continuousFrameData. | |
607 OwnPtr<Vector<char> > continuousFrameData = adoptPtr(new Vector<char
>); | |
608 m_continuousFrameData.swap(*continuousFrameData); | |
609 m_hasContinuousFrame = false; | |
610 if (m_continuousFrameOpCode == WebSocketFrame::OpCodeText) { | |
611 String message; | |
612 if (continuousFrameData->size()) | |
613 message = String::fromUTF8(continuousFrameData->data(), cont
inuousFrameData->size()); | |
614 else | |
615 message = ""; | |
616 if (message.isNull()) | |
617 failAsError("Could not decode a text frame as UTF-8."); | |
618 else | |
619 m_client->didReceiveTextMessage(message); | |
620 } else if (m_continuousFrameOpCode == WebSocketFrame::OpCodeBinary)
{ | |
621 m_client->didReceiveBinaryMessage(continuousFrameData.release())
; | |
622 } | |
623 } | |
624 break; | |
625 | |
626 case WebSocketFrame::OpCodeText: | |
627 if (frame.final) { | |
628 String message; | |
629 if (frame.payloadLength) | |
630 message = String::fromUTF8(frame.payload, frame.payloadLength); | |
631 else | |
632 message = ""; | |
633 skipBuffer(frameEnd - m_buffer.data()); | |
634 if (message.isNull()) | |
635 failAsError("Could not decode a text frame as UTF-8."); | |
636 else | |
637 m_client->didReceiveTextMessage(message); | |
638 } else { | |
639 m_hasContinuousFrame = true; | |
640 m_continuousFrameOpCode = WebSocketFrame::OpCodeText; | |
641 ASSERT(m_continuousFrameData.isEmpty()); | |
642 m_continuousFrameData.append(frame.payload, frame.payloadLength); | |
643 skipBuffer(frameEnd - m_buffer.data()); | |
644 } | |
645 break; | |
646 | |
647 case WebSocketFrame::OpCodeBinary: | |
648 if (frame.final) { | |
649 OwnPtr<Vector<char> > binaryData = adoptPtr(new Vector<char>(frame.p
ayloadLength)); | |
650 memcpy(binaryData->data(), frame.payload, frame.payloadLength); | |
651 skipBuffer(frameEnd - m_buffer.data()); | |
652 m_client->didReceiveBinaryMessage(binaryData.release()); | |
653 } else { | |
654 m_hasContinuousFrame = true; | |
655 m_continuousFrameOpCode = WebSocketFrame::OpCodeBinary; | |
656 ASSERT(m_continuousFrameData.isEmpty()); | |
657 m_continuousFrameData.append(frame.payload, frame.payloadLength); | |
658 skipBuffer(frameEnd - m_buffer.data()); | |
659 } | |
660 break; | |
661 | |
662 case WebSocketFrame::OpCodeClose: | |
663 if (!frame.payloadLength) { | |
664 m_closeEventCode = CloseEventCodeNoStatusRcvd; | |
665 } else if (frame.payloadLength == 1) { | |
666 m_closeEventCode = CloseEventCodeAbnormalClosure; | |
667 failAsError("Received a broken close frame containing an invalid siz
e body."); | |
668 return false; | |
669 } else { | |
670 unsigned char highByte = static_cast<unsigned char>(frame.payload[0]
); | |
671 unsigned char lowByte = static_cast<unsigned char>(frame.payload[1])
; | |
672 m_closeEventCode = highByte << 8 | lowByte; | |
673 if (m_closeEventCode == CloseEventCodeNoStatusRcvd || m_closeEventCo
de == CloseEventCodeAbnormalClosure || m_closeEventCode == CloseEventCodeTLSHand
shake) { | |
674 m_closeEventCode = CloseEventCodeAbnormalClosure; | |
675 failAsError("Received a broken close frame containing a reserved
status code."); | |
676 return false; | |
677 } | |
678 } | |
679 if (frame.payloadLength >= 3) | |
680 m_closeEventReason = String::fromUTF8(&frame.payload[2], frame.paylo
adLength - 2); | |
681 else | |
682 m_closeEventReason = ""; | |
683 skipBuffer(frameEnd - m_buffer.data()); | |
684 m_receivedClosingHandshake = true; | |
685 startClosingHandshake(m_closeEventCode, m_closeEventReason); | |
686 m_outgoingFrameQueueStatus = OutgoingFrameQueueClosing; | |
687 processOutgoingFrameQueue(); | |
688 break; | |
689 | |
690 case WebSocketFrame::OpCodePing: | |
691 enqueueRawFrame(WebSocketFrame::OpCodePong, frame.payload, frame.payload
Length); | |
692 skipBuffer(frameEnd - m_buffer.data()); | |
693 processOutgoingFrameQueue(); | |
694 break; | |
695 | |
696 case WebSocketFrame::OpCodePong: | |
697 // A server may send a pong in response to our ping, or an unsolicited p
ong which is not associated with | |
698 // any specific ping. Either way, there's nothing to do on receipt of po
ng. | |
699 skipBuffer(frameEnd - m_buffer.data()); | |
700 break; | |
701 | |
702 default: | |
703 ASSERT_NOT_REACHED(); | |
704 skipBuffer(frameEnd - m_buffer.data()); | |
705 break; | |
706 } | |
707 | |
708 m_perMessageDeflate.resetInflateBuffer(); | |
709 return !m_buffer.isEmpty(); | |
710 } | |
711 | |
712 void MainThreadWebSocketChannel::enqueueTextFrame(const CString& string) | |
713 { | |
714 ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); | |
715 | |
716 OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame); | |
717 frame->opCode = WebSocketFrame::OpCodeText; | |
718 frame->frameType = QueuedFrameTypeString; | |
719 frame->stringData = string; | |
720 m_outgoingFrameQueue.append(frame.release()); | |
721 } | |
722 | |
723 void MainThreadWebSocketChannel::enqueueRawFrame(WebSocketFrame::OpCode opCode,
const char* data, size_t dataLength) | |
724 { | |
725 ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); | |
726 | |
727 OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame); | |
728 frame->opCode = opCode; | |
729 frame->frameType = QueuedFrameTypeVector; | |
730 frame->vectorData.resize(dataLength); | |
731 if (dataLength) | |
732 memcpy(frame->vectorData.data(), data, dataLength); | |
733 m_outgoingFrameQueue.append(frame.release()); | |
734 } | |
735 | |
736 void MainThreadWebSocketChannel::enqueueVector(WebSocketFrame::OpCode opCode, Pa
ssOwnPtr<Vector<char> > data) | |
737 { | |
738 ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); | |
739 | |
740 OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame); | |
741 frame->opCode = opCode; | |
742 frame->frameType = QueuedFrameTypeVector; | |
743 frame->vectorData.swap(*data); | |
744 m_outgoingFrameQueue.append(frame.release()); | |
745 } | |
746 | |
747 void MainThreadWebSocketChannel::enqueueBlobFrame(WebSocketFrame::OpCode opCode,
PassRefPtr<BlobDataHandle> blobData) | |
748 { | |
749 ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); | |
750 | |
751 OwnPtr<QueuedFrame> frame = adoptPtr(new QueuedFrame); | |
752 frame->opCode = opCode; | |
753 frame->frameType = QueuedFrameTypeBlob; | |
754 frame->blobData = blobData; | |
755 m_outgoingFrameQueue.append(frame.release()); | |
756 } | |
757 | |
758 void MainThreadWebSocketChannel::processOutgoingFrameQueue() | |
759 { | |
760 if (m_outgoingFrameQueueStatus == OutgoingFrameQueueClosed) | |
761 return; | |
762 | |
763 while (!m_outgoingFrameQueue.isEmpty()) { | |
764 OwnPtr<QueuedFrame> frame = m_outgoingFrameQueue.takeFirst(); | |
765 switch (frame->frameType) { | |
766 case QueuedFrameTypeString: { | |
767 if (!sendFrame(frame->opCode, frame->stringData.data(), frame->strin
gData.length())) | |
768 failAsError("Failed to send WebSocket frame."); | |
769 break; | |
770 } | |
771 | |
772 case QueuedFrameTypeVector: | |
773 if (!sendFrame(frame->opCode, frame->vectorData.data(), frame->vecto
rData.size())) | |
774 failAsError("Failed to send WebSocket frame."); | |
775 break; | |
776 | |
777 case QueuedFrameTypeBlob: { | |
778 switch (m_blobLoaderStatus) { | |
779 case BlobLoaderNotStarted: | |
780 ref(); // Will be derefed after didFinishLoading() or didFail(). | |
781 ASSERT(!m_blobLoader); | |
782 m_blobLoader = adoptPtr(new FileReaderLoader(FileReaderLoader::R
eadAsArrayBuffer, this)); | |
783 m_blobLoaderStatus = BlobLoaderStarted; | |
784 m_blobLoader->start(m_document, frame->blobData); | |
785 m_outgoingFrameQueue.prepend(frame.release()); | |
786 return; | |
787 | |
788 case BlobLoaderStarted: | |
789 case BlobLoaderFailed: | |
790 m_outgoingFrameQueue.prepend(frame.release()); | |
791 return; | |
792 | |
793 case BlobLoaderFinished: { | |
794 RefPtr<ArrayBuffer> result = m_blobLoader->arrayBufferResult(); | |
795 m_blobLoader.clear(); | |
796 m_blobLoaderStatus = BlobLoaderNotStarted; | |
797 if (!sendFrame(frame->opCode, static_cast<const char*>(result->d
ata()), result->byteLength())) | |
798 failAsError("Failed to send WebSocket frame."); | |
799 break; | |
800 } | |
801 } | |
802 break; | |
803 } | |
804 | |
805 default: | |
806 ASSERT_NOT_REACHED(); | |
807 break; | |
808 } | |
809 } | |
810 | |
811 ASSERT(m_outgoingFrameQueue.isEmpty()); | |
812 if (m_outgoingFrameQueueStatus == OutgoingFrameQueueClosing) { | |
813 m_outgoingFrameQueueStatus = OutgoingFrameQueueClosed; | |
814 m_handle->close(); | |
815 } | |
816 } | |
817 | |
818 void MainThreadWebSocketChannel::abortOutgoingFrameQueue() | |
819 { | |
820 m_outgoingFrameQueue.clear(); | |
821 m_outgoingFrameQueueStatus = OutgoingFrameQueueClosed; | |
822 if (m_blobLoaderStatus == BlobLoaderStarted) { | |
823 m_blobLoader->cancel(); | |
824 didFail(FileError::ABORT_ERR); | |
825 } | |
826 } | |
827 | |
828 bool MainThreadWebSocketChannel::sendFrame(WebSocketFrame::OpCode opCode, const
char* data, size_t dataLength) | |
829 { | |
830 ASSERT(m_handle); | |
831 ASSERT(!m_suspended); | |
832 | |
833 WebSocketFrame frame(opCode, data, dataLength, WebSocketFrame::Final | WebSo
cketFrame::Masked); | |
834 InspectorInstrumentation::didSendWebSocketFrame(m_document, m_identifier, fr
ame.opCode, frame.masked, frame.payload, frame.payloadLength); | |
835 | |
836 OwnPtr<DeflateResultHolder> deflateResult = m_deflateFramer.deflate(frame); | |
837 if (!deflateResult->succeeded()) { | |
838 failAsError(deflateResult->failureReason()); | |
839 return false; | |
840 } | |
841 | |
842 if (!m_perMessageDeflate.deflate(frame)) { | |
843 failAsError(m_perMessageDeflate.failureReason()); | |
844 return false; | |
845 } | |
846 | |
847 Vector<char> frameData; | |
848 frame.makeFrameData(frameData); | |
849 m_framingOverheadQueue.append(FramingOverhead(opCode, frameData.size(), data
Length)); | |
850 | |
851 m_perMessageDeflate.resetDeflateBuffer(); | |
852 return m_handle->send(frameData.data(), frameData.size()); | |
853 } | |
854 | |
855 void MainThreadWebSocketChannel::trace(Visitor* visitor) | |
856 { | |
857 visitor->trace(m_document); | |
858 visitor->trace(m_client); | |
859 visitor->trace(m_handshake); | |
860 visitor->trace(m_handle); | |
861 WebSocketChannel::trace(visitor); | |
862 SocketStreamHandleClient::trace(visitor); | |
863 } | |
864 | |
865 } // namespace blink | |
OLD | NEW |