Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(36)

Side by Side Diff: net/websockets/websocket_channel_test.cc

Issue 12764006: WebSocketChannel implementation (Closed) Base URL: http://git.chromium.org/chromium/src.git@web_socket_dispatcher
Patch Set: Add unit tests and fix bugs. Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698