OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
yhirano
2013/06/26 11:33:29
I think websocket_channel_unittest.cc would be a b
Adam Rice
2013/06/27 17:29:49
The style guide says "_unittest and _regtest are d
yhirano
2013/06/28 06:49:17
I see, thank you!
tyoshino (SeeGerritForStatus)
2013/06/28 07:55:27
I don't have strong opinion. Both make sense. Shal
| |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/websockets/websocket_channel.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/bind_helpers.h" | |
12 #include "base/callback.h" | |
13 #include "base/memory/scoped_ptr.h" | |
14 #include "base/memory/scoped_vector.h" | |
15 #include "base/message_loop/message_loop.h" | |
16 #include "googleurl/src/gurl.h" | |
17 #include "net/base/net_errors.h" | |
18 #include "net/url_request/url_request_context.h" | |
19 #include "net/websockets/websocket_errors.h" | |
20 #include "net/websockets/websocket_event_interface.h" | |
21 #include "net/websockets/websocket_mux.h" | |
22 #include "testing/gmock/include/gmock/gmock.h" | |
23 #include "testing/gtest/include/gtest/gtest.h" | |
24 | |
25 namespace net { | |
26 namespace { | |
27 | |
28 using ::testing::_; | |
29 using ::testing::InSequence; | |
30 using ::testing::MockFunction; | |
31 | |
32 // This mock is for testing expectations about how the EventInterface is used. | |
33 class MockWebSocketEventInterface : public WebSocketEventInterface { | |
34 public: | |
35 MOCK_METHOD2(OnAddChannelResponse, void(bool, const std::string&)); | |
36 MOCK_METHOD3(OnSendFrame, | |
37 void(bool, WebSocketMessageType, const std::vector<char>&)); | |
38 MOCK_METHOD1(OnFlowControl, void(int64)); | |
39 MOCK_METHOD2(OnDropChannel, void(unsigned short, const std::string&)); | |
40 }; | |
41 | |
42 // This fake EventInterface is for tests which need an WebSocketEventInterface | |
43 // implementation but are not verifying how it is used. | |
44 class FakeWebSocketEventInterface : public WebSocketEventInterface { | |
45 virtual void OnAddChannelResponse(bool fail, | |
46 const std::string& selected_protocol) {} | |
47 virtual void OnSendFrame(bool fin, | |
48 WebSocketMessageType type, | |
49 const std::vector<char>& data) {} | |
50 virtual void OnFlowControl(int64 quota) {} | |
51 virtual void OnDropChannel(unsigned short reason, | |
52 const std::string& reason_text) {} | |
53 }; | |
54 | |
55 // This fake WebSocketStream is for tests that require a WebSocketStream but are | |
56 // are not testing the way it is used. It has minimal functionality to return | |
57 // the |protocol| and |extensions| that it was constructed with. | |
58 class FakeWebSocketStream : public WebSocketStream { | |
59 public: | |
60 // Construct with empty protocol and extensions. | |
61 FakeWebSocketStream() : WebSocketStream(), protocol_(), extensions_() {} | |
62 | |
63 // Construct with specified protocol and extensions. | |
64 FakeWebSocketStream(const std::string& protocol, | |
65 const std::string& extensions) | |
66 : WebSocketStream(), protocol_(protocol), extensions_(extensions) {} | |
67 | |
68 virtual int SendHandshakeRequest(const GURL& url, | |
69 const HttpRequestHeaders& headers, | |
70 HttpResponseInfo* response_info, | |
71 const CompletionCallback& callback) { | |
72 return ERR_IO_PENDING; | |
73 } | |
74 | |
75 virtual int ReadHandshakeResponse(const CompletionCallback& callback) { | |
76 return ERR_IO_PENDING; | |
77 } | |
78 | |
79 virtual int ReadFrames(ScopedVector<WebSocketFrameChunk>* frame_chunks, | |
80 const CompletionCallback& callback) { | |
81 return ERR_IO_PENDING; | |
82 } | |
83 | |
84 virtual int WriteFrames(ScopedVector<WebSocketFrameChunk>* frame_chunks, | |
85 const CompletionCallback& callback) { | |
86 return ERR_IO_PENDING; | |
87 } | |
88 | |
89 virtual void Close() {} | |
90 | |
91 // Returns the string passed to the constructor. | |
92 virtual std::string Protocol() { return protocol_; } | |
93 | |
94 // Returns the string passed to the constructor. | |
95 virtual std::string Extensions() { return extensions_; } | |
96 | |
97 private: | |
98 // The string to return from Protocol(). | |
99 std::string protocol_; | |
100 | |
101 // The string to return from Extensions(). | |
102 std::string extensions_; | |
103 }; | |
104 | |
105 // To make the static initialisers easier to read, we use enums rather than | |
106 // bools. | |
107 | |
108 // NO_HEADER means there shouldn't be a header included in the generated | |
109 // WebSocketFrameChunk. The static initialiser always has a header, but we can | |
110 // avoid specifying the rest of the fields. | |
111 enum IsFinal { | |
112 NO_HEADER, | |
113 NOT_FINAL_FRAME, | |
114 FINAL_FRAME | |
115 }; | |
116 | |
117 enum IsMasked { | |
118 NOT_MASKED, | |
119 MASKED | |
120 }; | |
121 | |
122 enum IsFinalChunk { | |
123 NOT_FINAL_CHUNK, | |
124 FINAL_CHUNK | |
125 }; | |
126 | |
127 // This is used to initialise a WebSocketFrameChunk but is statically | |
128 // initialisable. | |
129 struct InitFrameChunk { | |
130 struct FrameHeader { | |
131 IsFinal final; | |
132 // Reserved fields omitted for now. Add them if you need them. | |
133 WebSocketFrameHeader::OpCode opcode; | |
134 IsMasked masked; | |
135 uint64 payload_length; | |
136 }; | |
137 FrameHeader header; | |
138 | |
139 // Directly equivalent to WebSocketFrameChunk::final_chunk | |
140 IsFinalChunk final_chunk; | |
141 | |
142 // Will be used to create the IOBuffer member. Can be NULL for null data. | |
143 const char *const data; | |
144 }; | |
145 | |
146 // Convert a const array of InitFrameChunks to the format used at | |
147 // runtime. Templated on the size of the array to save typing. | |
148 template <size_t N> | |
149 ScopedVector<WebSocketFrameChunk> CreateFrameChunkVector( | |
150 const InitFrameChunk (&chunks)[N]) { | |
151 ScopedVector<WebSocketFrameChunk> vector; | |
152 vector.reserve(N); | |
153 for (size_t i = 0; i < N; ++i) { | |
154 scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk); | |
155 if (chunks[i].header.final != NO_HEADER) { | |
156 const InitFrameChunk::FrameHeader& in_header = chunks[i].header; | |
157 scoped_ptr<WebSocketFrameHeader> out_header( | |
158 new WebSocketFrameHeader(in_header.opcode)); | |
159 out_header->final = in_header.final == FINAL_FRAME; | |
160 out_header->opcode = in_header.opcode; | |
161 out_header->masked = in_header.masked == MASKED; | |
162 out_header->payload_length = in_header.payload_length; | |
163 chunk->header.swap(out_header); | |
164 } | |
165 chunk->final_chunk = chunks[i].final_chunk == FINAL_CHUNK; | |
166 chunk->data = new IOBufferWithSize(strlen(chunks[i].data)); | |
167 memcpy(chunk->data->data(), chunks[i].data, strlen(chunks[i].data)); | |
168 vector.push_back(chunk.release()); | |
169 } | |
170 return vector.Pass(); | |
171 } | |
172 | |
173 // A FakeWebSocketStream whose ReadFrames() function returns data. | |
174 class ReadableFakeWebSocketStream : public FakeWebSocketStream { | |
175 public: | |
176 enum IsSync { | |
177 SYNC, | |
178 ASYNC | |
179 }; | |
180 | |
181 // After constructing the object, call PrepareReadFrames() once for each | |
182 // time you wish it to return from the test. | |
183 ReadableFakeWebSocketStream() | |
184 : FakeWebSocketStream(), responses_(), index_(0) {} | |
185 | |
186 // Call PrepareReadFrames() to prepare the fake responses, in order. If | |
187 // |async| is true, then ReadFrames() will return ERR_IO_PENDING and the | |
188 // callback will be scheduled to run on the message loop. This requires the | |
189 // test case to run the message loop. If |async| is false, the response will | |
190 // be returned synchronously. |error| is returned directly from ReadFrames() | |
191 // in the synchronous case, or passed to the callback in the asynchronous | |
192 // case. |chunks| will be converted to a ScopedVector<WebSocketFrameChunks> | |
193 // and copied to the pointer that was passed to ReadFrames(). | |
194 template <size_t N> | |
195 void PrepareReadFrames(IsSync async, | |
196 int error, | |
197 const InitFrameChunk (&chunks)[N]) { | |
198 responses_.push_back( | |
199 new Response(async, error, CreateFrameChunkVector(chunks))); | |
200 } | |
201 | |
202 // Prepare a fake error response (ie. there is no data). | |
203 void PrepareReadFramesError(IsSync async, int error) { | |
204 responses_.push_back( | |
205 new Response(async, error, ScopedVector<WebSocketFrameChunk>())); | |
206 } | |
207 | |
208 virtual int ReadFrames(ScopedVector<WebSocketFrameChunk>* frame_chunks, | |
209 const CompletionCallback& callback) { | |
210 if (index_ >= responses_.size()) { | |
211 return ERR_IO_PENDING; | |
212 } | |
213 if (responses_[index_]->async == ASYNC) { | |
214 base::MessageLoop::current()->PostTask( | |
215 FROM_HERE, | |
216 base::Bind(&ReadableFakeWebSocketStream::DoCallback, | |
217 base::Unretained(this), | |
218 frame_chunks, | |
219 callback)); | |
220 return ERR_IO_PENDING; | |
221 } else { | |
222 frame_chunks->swap(responses_[index_]->chunks); | |
223 return responses_[index_++]->error; | |
224 } | |
225 } | |
226 | |
227 private: | |
228 void DoCallback(ScopedVector<WebSocketFrameChunk>* frame_chunks, | |
229 const CompletionCallback& callback) { | |
230 frame_chunks->swap(responses_[index_]->chunks); | |
231 callback.Run(responses_[index_++]->error); | |
232 return; | |
233 } | |
234 | |
235 struct Response { | |
236 Response(IsSync async, int error, ScopedVector<WebSocketFrameChunk> chunks) | |
237 : async(async), error(error), chunks(chunks.Pass()) {} | |
238 | |
239 IsSync async; | |
240 int error; | |
241 ScopedVector<WebSocketFrameChunk> chunks; | |
242 | |
243 private: | |
244 // Bad things will happen if we attempt to copy or assign "chunks". | |
245 DISALLOW_COPY_AND_ASSIGN(Response); | |
246 }; | |
247 ScopedVector<Response> responses_; | |
248 size_t index_; | |
249 }; | |
250 | |
251 // A FakeWebSocketStream where writes always complete successfully and | |
252 // synchronously. | |
253 class WriteableFakeWebSocketStream : public FakeWebSocketStream { | |
254 public: | |
255 virtual int WriteFrames(ScopedVector<WebSocketFrameChunk>* frame_chunks, | |
256 const CompletionCallback& callback) { | |
257 return OK; | |
258 } | |
259 }; | |
260 | |
261 struct ArgumentCopyingWebSocketFactory { | |
262 void Factory(const GURL& socket_url, | |
263 const std::vector<std::string>& requested_protocols, | |
264 const GURL& origin, | |
265 URLRequestContext* url_request_context, | |
266 const WebSocketStream::SuccessCallback& on_success, | |
267 const WebSocketStream::FailureCallback& on_failure) { | |
268 this->socket_url = socket_url; | |
269 this->requested_protocols = requested_protocols; | |
270 this->origin = origin; | |
271 this->url_request_context = url_request_context; | |
272 this->on_success = on_success; | |
273 this->on_failure = on_failure; | |
274 } | |
275 | |
276 GURL socket_url; | |
277 GURL origin; | |
278 std::vector<std::string> requested_protocols; | |
279 URLRequestContext* url_request_context; | |
280 WebSocketStream::SuccessCallback on_success; | |
281 WebSocketStream::FailureCallback on_failure; | |
282 }; | |
283 | |
284 // The WebSocketEventInterface handles data as vector<char>. For testing | |
285 // purposes, this function allows a vector<char> to be easily created from a | |
286 // string. | |
287 std::vector<char> AsVector(const std::string& s) { | |
288 return std::vector<char>(s.begin(), s.end()); | |
289 } | |
290 | |
291 // Base class for WebSocketChannelTests. | |
292 class WebSocketChannelTest : public ::testing::Test { | |
293 protected: | |
294 WebSocketChannelTest() | |
295 : data_(), channel_(), stream_(new FakeWebSocketStream) {} | |
296 | |
297 // Create a new WebSocketChannel and connect it, using the settings stored in | |
298 // data_. | |
299 void CreateChannelAndConnect() { | |
300 channel_.reset( | |
301 new WebSocketChannel(data_.url, EventInterface())); | |
302 channel_->SendAddChannelRequestWithFactory( | |
303 data_.requested_protocols, | |
304 data_.origin, | |
305 &data_.url_request_context, | |
306 base::Bind(&ArgumentCopyingWebSocketFactory::Factory, | |
307 base::Unretained(&data_.factory))); | |
308 } | |
309 | |
310 // Same as CreateChannelAndConnect(), but call the on_success callback as | |
311 // well. | |
312 void CreateChannelAndConnectSuccessfully() { | |
313 CreateChannelAndConnect(); | |
314 data_.factory.on_success.Run(stream_.Pass()); | |
315 } | |
316 | |
317 // Return a WebSocketEventInterface to be passed to the WebSocketChannel. | |
318 // This implementation returns a newly-created fake. Subclasses may return a | |
319 // mock instead. | |
320 virtual scoped_ptr<WebSocketEventInterface> EventInterface() { | |
321 return scoped_ptr<WebSocketEventInterface>(new FakeWebSocketEventInterface); | |
322 } | |
323 | |
324 // Downcasting and moving a scoped_ptr in one operation doesn't seem to work | |
325 // (bug?). This function works around that and simplifies initialising stream_ | |
326 // from a subclass. | |
327 template<class T> | |
328 void set_stream(scoped_ptr<T> stream) { | |
329 stream_.reset(stream.release()); | |
330 } | |
331 | |
332 // A struct containing the data that will be used to connect the channel. | |
333 struct ConnectData { | |
334 // URL to (pretend to) connect to. | |
335 GURL url; | |
336 // Origin of the request | |
337 GURL origin; | |
338 // Requested protocols for the request. | |
339 std::vector<std::string> requested_protocols; | |
340 // URLRequestContext object. | |
341 URLRequestContext url_request_context; | |
342 // A fake WebSocketFactory that just records its arguments. | |
343 ArgumentCopyingWebSocketFactory factory; | |
344 }; | |
345 ConnectData data_; | |
346 | |
347 // The channel we are testing. Not initialised until SetChannel() is called. | |
348 scoped_ptr<WebSocketChannel> channel_; | |
349 | |
350 // A mock or fake stream for tests that need one. | |
351 scoped_ptr<WebSocketStream> stream_; | |
352 }; | |
353 | |
354 // Base class for tests which verify that EventInterface methods are called | |
355 // appropriately. | |
356 class WebSocketChannelEventInterfaceTest : public WebSocketChannelTest { | |
357 protected: | |
358 WebSocketChannelEventInterfaceTest() | |
359 : WebSocketChannelTest(), | |
360 event_interface_(new MockWebSocketEventInterface) {} | |
361 | |
362 virtual scoped_ptr<WebSocketEventInterface> EventInterface() OVERRIDE { | |
363 return scoped_ptr<WebSocketEventInterface>(event_interface_.release()); | |
364 } | |
365 | |
366 scoped_ptr<MockWebSocketEventInterface> event_interface_; | |
367 }; | |
368 | |
369 // Simple test that everything that should be passed to the factory function is | |
370 // passed to the factory function. | |
371 TEST_F(WebSocketChannelTest, EverythingIsPassedToTheFactoryFunction) { | |
372 data_.url = GURL("ws://example.com/test"); | |
373 data_.origin = GURL("http://example.com/test"); | |
374 data_.requested_protocols.push_back("Sinbad"); | |
375 | |
376 CreateChannelAndConnect(); | |
377 | |
378 EXPECT_EQ(data_.url, data_.factory.socket_url); | |
379 EXPECT_EQ(data_.origin, data_.factory.origin); | |
380 EXPECT_EQ(data_.requested_protocols, data_.factory.requested_protocols); | |
381 EXPECT_EQ(&data_.url_request_context, data_.factory.url_request_context); | |
382 } | |
383 | |
384 TEST_F(WebSocketChannelEventInterfaceTest, ConnectSuccessReported) { | |
385 // false means success. | |
386 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, "")); | |
387 // OnFlowControl is always called immediately after connect to provide initial | |
388 // quota to the renderer. | |
389 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
390 | |
391 CreateChannelAndConnect(); | |
392 | |
393 data_.factory.on_success.Run(stream_.Pass()); | |
394 } | |
395 | |
396 TEST_F(WebSocketChannelEventInterfaceTest, ConnectFailureReported) { | |
397 // true means failure. | |
398 EXPECT_CALL(*event_interface_, OnAddChannelResponse(true, "")); | |
399 | |
400 CreateChannelAndConnect(); | |
401 | |
402 data_.factory.on_failure.Run(kWebSocketErrorNoStatusReceived); | |
403 } | |
404 | |
405 TEST_F(WebSocketChannelEventInterfaceTest, ProtocolPassed) { | |
406 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, "Bob")); | |
407 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
408 | |
409 CreateChannelAndConnect(); | |
410 | |
411 data_.factory.on_success | |
412 .Run(scoped_ptr<WebSocketStream>(new FakeWebSocketStream("Bob", ""))); | |
413 } | |
414 | |
415 // The first frames from the server can arrive together with the handshake, in | |
416 // which case they will be available as soon as ReadFrames() is called the first | |
417 // time. | |
418 TEST_F(WebSocketChannelEventInterfaceTest, DataLeftFromHandshake) { | |
419 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
420 new ReadableFakeWebSocketStream); | |
421 static const InitFrameChunk chunks[] = { | |
422 {{FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, 5}, | |
423 FINAL_CHUNK, "HELLO"}, | |
424 }; | |
425 stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, chunks); | |
426 set_stream(stream.Pass()); | |
427 { | |
428 InSequence s; | |
429 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
430 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
431 EXPECT_CALL( | |
432 *event_interface_, | |
433 OnSendFrame( | |
434 true, WebSocketFrameHeader::kOpCodeText, AsVector("HELLO"))); | |
435 } | |
436 | |
437 CreateChannelAndConnectSuccessfully(); | |
438 } | |
439 | |
440 // A remote server could accept the handshake, but then immediately send a | |
441 // close frame. | |
442 TEST_F(WebSocketChannelEventInterfaceTest, CloseAfterHandshake) { | |
443 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
444 new ReadableFakeWebSocketStream); | |
445 static const InitFrameChunk chunks[] = { | |
446 {{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, 23}, | |
447 FINAL_CHUNK, "\x03\xf3Internal Server Error"}, | |
448 }; | |
449 stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, chunks); | |
450 set_stream(stream.Pass()); | |
451 { | |
452 InSequence s; | |
453 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
454 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
455 EXPECT_CALL(*event_interface_, | |
456 OnDropChannel(kWebSocketErrorInternalServerError, | |
457 "Internal Server Error")); | |
458 } | |
459 | |
460 CreateChannelAndConnectSuccessfully(); | |
461 } | |
462 | |
463 // A remote server could close the connection immediately after sending the | |
464 // handshake response (most likely a bug in the server). | |
465 TEST_F(WebSocketChannelEventInterfaceTest, ConnectionCloseAfterHandshake) { | |
466 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
467 new ReadableFakeWebSocketStream); | |
468 stream->PrepareReadFramesError(ReadableFakeWebSocketStream::SYNC, | |
469 ERR_CONNECTION_CLOSED); | |
470 set_stream(stream.Pass()); | |
471 { | |
472 InSequence s; | |
473 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
474 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
475 EXPECT_CALL(*event_interface_, | |
476 OnDropChannel(kWebSocketErrorAbnormalClosure, | |
477 "Abnormal Closure")); | |
478 } | |
479 | |
480 CreateChannelAndConnectSuccessfully(); | |
481 } | |
482 | |
483 TEST_F(WebSocketChannelEventInterfaceTest, NormalAsyncRead) { | |
484 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
485 new ReadableFakeWebSocketStream); | |
486 static const InitFrameChunk chunks[] = { | |
487 {{FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, 5}, | |
488 FINAL_CHUNK, "HELLO"}, | |
489 }; | |
490 // We use this checkpoint object to verify that the callback isn't called | |
491 // until we expect it to be. | |
492 MockFunction<void(int)> checkpoint; | |
493 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks); | |
494 set_stream(stream.Pass()); | |
495 { | |
496 InSequence s; | |
497 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
498 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
499 EXPECT_CALL(checkpoint, Call(1)); | |
500 EXPECT_CALL( | |
501 *event_interface_, | |
502 OnSendFrame( | |
503 true, WebSocketFrameHeader::kOpCodeText, AsVector("HELLO"))); | |
504 EXPECT_CALL(checkpoint, Call(2)); | |
505 } | |
506 | |
507 CreateChannelAndConnectSuccessfully(); | |
508 checkpoint.Call(1); | |
509 base::MessageLoop::current()->RunUntilIdle(); | |
510 checkpoint.Call(2); | |
511 } | |
512 | |
513 // Extra data can arrive while a read is being processed, resulting in the next | |
514 // read completing synchronously. | |
515 TEST_F(WebSocketChannelEventInterfaceTest, AsyncThenSyncRead) { | |
516 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
517 new ReadableFakeWebSocketStream); | |
518 static const InitFrameChunk chunks1[] = { | |
519 {{FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, 5}, | |
520 FINAL_CHUNK, "HELLO"}, | |
521 }; | |
522 static const InitFrameChunk chunks2[] = { | |
523 {{FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, 5}, | |
524 FINAL_CHUNK, "WORLD"}, | |
525 }; | |
526 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks1); | |
527 stream->PrepareReadFrames(ReadableFakeWebSocketStream::SYNC, OK, chunks2); | |
528 set_stream(stream.Pass()); | |
529 { | |
530 InSequence s; | |
531 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
532 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
533 EXPECT_CALL( | |
534 *event_interface_, | |
535 OnSendFrame( | |
536 true, WebSocketFrameHeader::kOpCodeText, AsVector("HELLO"))); | |
537 EXPECT_CALL( | |
538 *event_interface_, | |
539 OnSendFrame( | |
540 true, WebSocketFrameHeader::kOpCodeText, AsVector("WORLD"))); | |
541 } | |
542 | |
543 CreateChannelAndConnectSuccessfully(); | |
544 base::MessageLoop::current()->RunUntilIdle(); | |
545 } | |
546 | |
547 // Data frames that arrive in fragments are turned into individual frames | |
548 TEST_F(WebSocketChannelEventInterfaceTest, FragmentedFrames) { | |
549 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
550 new ReadableFakeWebSocketStream); | |
551 // Here we have one message split into 3 frames which arrive in 3 chunks. The | |
552 // first frame is entirely in the first chunk, the second frame is split | |
553 // across all the chunks, and the final frame is entirely in the final | |
554 // chunk. This should be delivered as 5 frames. | |
555 static const InitFrameChunk chunks1[] = { | |
556 {{NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, 5}, | |
557 FINAL_CHUNK, "THREE"}, | |
558 {{NOT_FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, | |
559 7}, | |
560 NOT_FINAL_CHUNK, " "}, | |
561 }; | |
562 static const InitFrameChunk chunks2[] = {{{NO_HEADER}, NOT_FINAL_CHUNK, | |
563 "SMALL"}}; | |
564 static const InitFrameChunk chunks3[] = { | |
565 {{NO_HEADER}, FINAL_CHUNK, " "}, | |
566 {{FINAL_FRAME, WebSocketFrameHeader::kOpCodeContinuation, NOT_MASKED, 6}, | |
567 FINAL_CHUNK, "FRAMES"}, | |
568 }; | |
569 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks1); | |
570 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks2); | |
571 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks3); | |
572 set_stream(stream.Pass()); | |
573 { | |
574 InSequence s; | |
575 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
576 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
577 EXPECT_CALL( | |
578 *event_interface_, | |
579 OnSendFrame( | |
580 false, WebSocketFrameHeader::kOpCodeText, AsVector("THREE"))); | |
581 EXPECT_CALL( | |
582 *event_interface_, | |
583 OnSendFrame( | |
584 false, WebSocketFrameHeader::kOpCodeContinuation, AsVector(" "))); | |
585 EXPECT_CALL(*event_interface_, | |
586 OnSendFrame(false, | |
587 WebSocketFrameHeader::kOpCodeContinuation, | |
588 AsVector("SMALL"))); | |
589 EXPECT_CALL( | |
590 *event_interface_, | |
591 OnSendFrame( | |
592 false, WebSocketFrameHeader::kOpCodeContinuation, AsVector(" "))); | |
593 EXPECT_CALL(*event_interface_, | |
594 OnSendFrame(true, | |
595 WebSocketFrameHeader::kOpCodeContinuation, | |
596 AsVector("FRAMES"))); | |
597 } | |
598 | |
599 CreateChannelAndConnectSuccessfully(); | |
600 base::MessageLoop::current()->RunUntilIdle(); | |
601 } | |
602 | |
603 // In the case when a single-frame message because fragmented, it must be | |
604 // correctly transformed to multiple frames. | |
605 TEST_F(WebSocketChannelEventInterfaceTest, MessageFragmentation) { | |
606 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
607 new ReadableFakeWebSocketStream); | |
608 // Here we have one message split into 3 frames which arrive in 3 chunks. The | |
609 // first frame is entirely in the first chunk, the second frame is split | |
610 // across all the chunks, and the final frame is entirely in the final | |
611 // chunk. This should be delivered as 5 frames. | |
612 static const InitFrameChunk chunks1[] = { | |
613 {{FINAL_FRAME, WebSocketFrameHeader::kOpCodeText, NOT_MASKED, 12}, | |
614 NOT_FINAL_CHUNK, "TIME"}, | |
615 }; | |
616 static const InitFrameChunk chunks2[] = {{{NO_HEADER}, NOT_FINAL_CHUNK, | |
617 " FOR "}}; | |
618 static const InitFrameChunk chunks3[] = {{{NO_HEADER}, FINAL_CHUNK, "TEA"}}; | |
619 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks1); | |
620 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks2); | |
621 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks3); | |
622 set_stream(stream.Pass()); | |
623 { | |
624 InSequence s; | |
625 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
626 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
627 EXPECT_CALL( | |
628 *event_interface_, | |
629 OnSendFrame( | |
630 false, WebSocketFrameHeader::kOpCodeText, AsVector("TIME"))); | |
631 EXPECT_CALL(*event_interface_, | |
632 OnSendFrame(false, | |
633 WebSocketFrameHeader::kOpCodeContinuation, | |
634 AsVector(" FOR "))); | |
635 EXPECT_CALL( | |
636 *event_interface_, | |
637 OnSendFrame( | |
638 true, WebSocketFrameHeader::kOpCodeContinuation, AsVector("TEA"))); | |
639 } | |
640 | |
641 CreateChannelAndConnectSuccessfully(); | |
642 base::MessageLoop::current()->RunUntilIdle(); | |
643 } | |
644 | |
645 // If a control message is fragmented, it must be re-assembled before being | |
646 // delivered. A control message can only be fragmented at the network level; it | |
647 // is not permitted to be split into multiple frames. | |
648 TEST_F(WebSocketChannelEventInterfaceTest, FragmentedControlMessage) { | |
649 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
650 new ReadableFakeWebSocketStream); | |
651 static const InitFrameChunk chunks1[] = { | |
652 {{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, 7}, | |
653 NOT_FINAL_CHUNK, "\x03\xe8"}, | |
654 }; | |
655 static const InitFrameChunk chunks2[] = {{{NO_HEADER}, NOT_FINAL_CHUNK, | |
656 "Clo"}}; | |
657 static const InitFrameChunk chunks3[] = {{{NO_HEADER}, FINAL_CHUNK, | |
658 "se"}}; | |
659 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks1); | |
660 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks2); | |
661 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks3); | |
662 set_stream(stream.Pass()); | |
663 { | |
664 InSequence s; | |
665 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
666 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
667 EXPECT_CALL(*event_interface_, | |
668 OnDropChannel(kWebSocketNormalClosure, "Close")); | |
669 } | |
670 | |
671 CreateChannelAndConnectSuccessfully(); | |
672 base::MessageLoop::current()->RunUntilIdle(); | |
673 } | |
674 | |
675 // Connection closed unexpectedly. | |
676 TEST_F(WebSocketChannelEventInterfaceTest, AsyncAbnormalClosure) { | |
677 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
678 new ReadableFakeWebSocketStream); | |
679 stream->PrepareReadFramesError(ReadableFakeWebSocketStream::ASYNC, | |
680 ERR_CONNECTION_CLOSED); | |
681 set_stream(stream.Pass()); | |
682 { | |
683 InSequence s; | |
684 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
685 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
686 EXPECT_CALL(*event_interface_, | |
687 OnDropChannel(kWebSocketErrorAbnormalClosure, _)); | |
688 } | |
689 | |
690 CreateChannelAndConnectSuccessfully(); | |
691 base::MessageLoop::current()->RunUntilIdle(); | |
692 } | |
693 | |
694 // Connection reset. | |
695 TEST_F(WebSocketChannelEventInterfaceTest, ConnectionReset) { | |
696 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
697 new ReadableFakeWebSocketStream); | |
698 stream->PrepareReadFramesError(ReadableFakeWebSocketStream::ASYNC, | |
699 ERR_CONNECTION_RESET); | |
700 set_stream(stream.Pass()); | |
701 { | |
702 InSequence s; | |
703 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
704 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
705 EXPECT_CALL(*event_interface_, | |
706 OnDropChannel(kWebSocketErrorAbnormalClosure, _)); | |
707 } | |
708 | |
709 CreateChannelAndConnectSuccessfully(); | |
710 base::MessageLoop::current()->RunUntilIdle(); | |
711 } | |
712 | |
713 // Connection closed in the middle of a Close message (server bug, etc.) | |
714 TEST_F(WebSocketChannelEventInterfaceTest, ConnectionClosedInMessage) { | |
715 scoped_ptr<ReadableFakeWebSocketStream> stream( | |
716 new ReadableFakeWebSocketStream); | |
717 static const InitFrameChunk chunks[] = { | |
718 {{FINAL_FRAME, WebSocketFrameHeader::kOpCodeClose, NOT_MASKED, 7}, | |
719 NOT_FINAL_CHUNK, "\x03\xe8"}, | |
720 }; | |
721 | |
722 stream->PrepareReadFrames(ReadableFakeWebSocketStream::ASYNC, OK, chunks); | |
723 stream->PrepareReadFramesError(ReadableFakeWebSocketStream::ASYNC, | |
724 ERR_CONNECTION_CLOSED); | |
725 set_stream(stream.Pass()); | |
726 { | |
727 InSequence s; | |
728 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
729 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
730 EXPECT_CALL(*event_interface_, | |
731 OnDropChannel(kWebSocketErrorAbnormalClosure, _)); | |
732 } | |
733 | |
734 CreateChannelAndConnectSuccessfully(); | |
735 base::MessageLoop::current()->RunUntilIdle(); | |
736 } | |
737 | |
738 | |
739 | |
740 | |
741 // If the renderer sends lots of small writes, we don't want to update the quota | |
742 // for each one. | |
743 TEST_F(WebSocketChannelEventInterfaceTest, SmallWriteDoesntUpdateQuota) { | |
744 set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); | |
745 { | |
746 InSequence s; | |
747 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
748 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
749 } | |
750 | |
751 CreateChannelAndConnectSuccessfully(); | |
752 channel_->SendFrame(true, WebSocketFrameHeader::kOpCodeText, AsVector("B")); | |
753 } | |
754 | |
755 // If we send enough to go below send_quota_low_water_mask_ we should get our | |
756 // quota refreshed. | |
757 TEST_F(WebSocketChannelEventInterfaceTest, LargeWriteUpdatesQuota) { | |
758 set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); | |
759 // We use this checkpoint object to verify that the quota update comes after | |
760 // the write. | |
761 MockFunction<void(int)> checkpoint; | |
762 { | |
763 InSequence s; | |
764 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
765 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
766 EXPECT_CALL(checkpoint, Call(1)); | |
767 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
768 EXPECT_CALL(checkpoint, Call(2)); | |
769 } | |
770 | |
771 CreateChannelAndConnectSuccessfully(); | |
772 checkpoint.Call(1); | |
773 // TODO(ricea): If kDefaultSendQuotaHighWaterMark changes, then this value | |
774 // will need to be updated. | |
775 channel_->SendFrame( | |
776 true, WebSocketFrameHeader::kOpCodeText, std::vector<char>(1 << 17, 'B')); | |
777 checkpoint.Call(2); | |
778 } | |
779 | |
780 // Verify that our quota actually is refreshed when we are told it is. | |
781 TEST_F(WebSocketChannelEventInterfaceTest, QuotaReallyIsRefreshed) { | |
782 set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); | |
783 MockFunction<void(int)> checkpoint; | |
784 { | |
785 InSequence s; | |
786 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
787 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
788 EXPECT_CALL(checkpoint, Call(1)); | |
789 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
790 EXPECT_CALL(checkpoint, Call(2)); | |
791 // If quota was not really refreshed, we would get an OnDropChannel() | |
792 // message. | |
793 EXPECT_CALL(*event_interface_, OnFlowControl(_)); | |
794 EXPECT_CALL(checkpoint, Call(3)); | |
795 } | |
796 | |
797 CreateChannelAndConnectSuccessfully(); | |
798 checkpoint.Call(1); | |
799 // TODO(ricea): If kDefaultSendQuotaLowWaterMark and/or | |
800 // kDefaultSendQuotaHighWaterMark change, then this value will need to be | |
801 // updated. | |
802 channel_->SendFrame(true, | |
803 WebSocketFrameHeader::kOpCodeText, | |
804 std::vector<char>((1 << 16) + 1, 'D')); | |
805 checkpoint.Call(2); | |
806 // We should have received more quota at this point. | |
807 channel_->SendFrame(true, | |
808 WebSocketFrameHeader::kOpCodeText, | |
809 std::vector<char>((1 << 16) + 1, 'E')); | |
810 checkpoint.Call(3); | |
811 } | |
812 | |
813 // If we send more than the available quota then the connection will be closed | |
814 // with an error. | |
815 TEST_F(WebSocketChannelEventInterfaceTest, WriteOverQuotaIsRejected) { | |
816 set_stream(make_scoped_ptr(new WriteableFakeWebSocketStream)); | |
817 { | |
818 InSequence s; | |
819 EXPECT_CALL(*event_interface_, OnAddChannelResponse(false, _)); | |
820 // TODO(ricea): Change this if kDefaultSendQuotaHighWaterMark changes. | |
821 EXPECT_CALL(*event_interface_, OnFlowControl(1 << 17)); | |
822 EXPECT_CALL(*event_interface_, | |
823 OnDropChannel(kWebSocketMuxErrorSendQuotaViolation, _)); | |
824 } | |
825 | |
826 CreateChannelAndConnectSuccessfully(); | |
827 channel_->SendFrame(true, | |
828 WebSocketFrameHeader::kOpCodeText, | |
829 std::vector<char>((1 << 17) + 1, 'C')); | |
830 } | |
831 | |
832 } // namespace | |
833 } // namespace net | |
OLD | NEW |