OLD | NEW |
| (Empty) |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 // | |
5 // Tests for WebSocketBasicStream. Note that we do not attempt to verify that | |
6 // frame parsing itself functions correctly, as that is covered by the | |
7 // WebSocketFrameParser tests. | |
8 | |
9 #include "net/websockets/websocket_basic_stream.h" | |
10 | |
11 #include <string.h> // for memcpy() and memset(). | |
12 | |
13 #include <string> | |
14 | |
15 #include "base/basictypes.h" | |
16 #include "base/big_endian.h" | |
17 #include "base/port.h" | |
18 #include "net/base/capturing_net_log.h" | |
19 #include "net/base/test_completion_callback.h" | |
20 #include "net/socket/socket_test_util.h" | |
21 #include "testing/gtest/include/gtest/gtest.h" | |
22 | |
23 namespace net { | |
24 namespace { | |
25 | |
26 #define WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(name, value) \ | |
27 const char k##name[] = value; \ | |
28 const size_t k##name##Size = arraysize(k##name) - 1; | |
29 | |
30 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(SampleFrame, "\x81\x06Sample"); | |
31 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT( | |
32 PartialLargeFrame, | |
33 "\x81\x7F\x00\x00\x00\x00\x7F\xFF\xFF\xFF" | |
34 "chromiunum ad pasco per loca insanis pullum manducat frumenti"); | |
35 const size_t kLargeFrameHeaderSize = 10; | |
36 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(MultipleFrames, | |
37 "\x81\x01X\x81\x01Y\x81\x01Z"); | |
38 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyFirstFrame, "\x01\x00"); | |
39 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyMiddleFrame, "\x00\x00"); | |
40 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyFinalTextFrame, "\x81\x00"); | |
41 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(EmptyFinalContinuationFrame, | |
42 "\x80\x00"); | |
43 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(ValidPong, "\x8A\x00"); | |
44 // This frame encodes a payload length of 7 in two bytes, which is always | |
45 // invalid. | |
46 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(InvalidFrame, | |
47 "\x81\x7E\x00\x07Invalid"); | |
48 // Control frames must have the FIN bit set. This one does not. | |
49 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(PingFrameWithoutFin, "\x09\x00"); | |
50 // Control frames must have a payload of 125 bytes or less. This one has | |
51 // a payload of 126 bytes. | |
52 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT( | |
53 126BytePong, | |
54 "\x8a\x7e\x00\x7eZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" | |
55 "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"); | |
56 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(CloseFrame, | |
57 "\x88\x09\x03\xe8occludo"); | |
58 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(WriteFrame, | |
59 "\x81\x85\x00\x00\x00\x00Write"); | |
60 WEBSOCKET_BASIC_STREAM_TEST_DEFINE_CONSTANT(MaskedEmptyPong, | |
61 "\x8A\x80\x00\x00\x00\x00"); | |
62 const WebSocketMaskingKey kNulMaskingKey = {{'\0', '\0', '\0', '\0'}}; | |
63 const WebSocketMaskingKey kNonNulMaskingKey = { | |
64 {'\x0d', '\x1b', '\x06', '\x17'}}; | |
65 | |
66 // A masking key generator function which generates the identity mask, | |
67 // ie. "\0\0\0\0". | |
68 WebSocketMaskingKey GenerateNulMaskingKey() { return kNulMaskingKey; } | |
69 | |
70 // A masking key generation function which generates a fixed masking key with no | |
71 // nul characters. | |
72 WebSocketMaskingKey GenerateNonNulMaskingKey() { return kNonNulMaskingKey; } | |
73 | |
74 // Base class for WebSocketBasicStream test fixtures. | |
75 class WebSocketBasicStreamTest : public ::testing::Test { | |
76 protected: | |
77 scoped_ptr<WebSocketBasicStream> stream_; | |
78 CapturingNetLog net_log_; | |
79 }; | |
80 | |
81 // A subclass of StaticSocketDataProvider modified to require that all data | |
82 // expected to be read or written actually is. | |
83 class StrictStaticSocketDataProvider : public StaticSocketDataProvider { | |
84 public: | |
85 StrictStaticSocketDataProvider(MockRead* reads, | |
86 size_t reads_count, | |
87 MockWrite* writes, | |
88 size_t writes_count, | |
89 bool strict_mode) | |
90 : StaticSocketDataProvider(reads, reads_count, writes, writes_count), | |
91 strict_mode_(strict_mode) {} | |
92 | |
93 ~StrictStaticSocketDataProvider() override { | |
94 if (strict_mode_) { | |
95 EXPECT_EQ(read_count(), read_index()); | |
96 EXPECT_EQ(write_count(), write_index()); | |
97 } | |
98 } | |
99 | |
100 private: | |
101 const bool strict_mode_; | |
102 }; | |
103 | |
104 // A fixture for tests which only perform normal socket operations. | |
105 class WebSocketBasicStreamSocketTest : public WebSocketBasicStreamTest { | |
106 protected: | |
107 WebSocketBasicStreamSocketTest() | |
108 : histograms_("a"), | |
109 pool_(1, 1, &histograms_, &factory_), | |
110 generator_(&GenerateNulMaskingKey), | |
111 expect_all_io_to_complete_(true) {} | |
112 | |
113 ~WebSocketBasicStreamSocketTest() override { | |
114 // stream_ has a reference to socket_data_ (via MockTCPClientSocket) and so | |
115 // should be destroyed first. | |
116 stream_.reset(); | |
117 } | |
118 | |
119 scoped_ptr<ClientSocketHandle> MakeTransportSocket(MockRead reads[], | |
120 size_t reads_count, | |
121 MockWrite writes[], | |
122 size_t writes_count) { | |
123 socket_data_.reset(new StrictStaticSocketDataProvider( | |
124 reads, reads_count, writes, writes_count, expect_all_io_to_complete_)); | |
125 socket_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK)); | |
126 factory_.AddSocketDataProvider(socket_data_.get()); | |
127 | |
128 scoped_ptr<ClientSocketHandle> transport_socket(new ClientSocketHandle); | |
129 scoped_refptr<MockTransportSocketParams> params; | |
130 transport_socket->Init("a", | |
131 params, | |
132 MEDIUM, | |
133 CompletionCallback(), | |
134 &pool_, | |
135 bound_net_log_.bound()); | |
136 return transport_socket.Pass(); | |
137 } | |
138 | |
139 void SetHttpReadBuffer(const char* data, size_t size) { | |
140 http_read_buffer_ = new GrowableIOBuffer; | |
141 http_read_buffer_->SetCapacity(size); | |
142 memcpy(http_read_buffer_->data(), data, size); | |
143 http_read_buffer_->set_offset(size); | |
144 } | |
145 | |
146 void CreateStream(MockRead reads[], | |
147 size_t reads_count, | |
148 MockWrite writes[], | |
149 size_t writes_count) { | |
150 stream_ = WebSocketBasicStream::CreateWebSocketBasicStreamForTesting( | |
151 MakeTransportSocket(reads, reads_count, writes, writes_count), | |
152 http_read_buffer_, | |
153 sub_protocol_, | |
154 extensions_, | |
155 generator_); | |
156 } | |
157 | |
158 template <size_t N> | |
159 void CreateReadOnly(MockRead (&reads)[N]) { | |
160 CreateStream(reads, N, NULL, 0); | |
161 } | |
162 | |
163 void CreateNullStream() { CreateStream(NULL, 0, NULL, 0); } | |
164 | |
165 scoped_ptr<SocketDataProvider> socket_data_; | |
166 MockClientSocketFactory factory_; | |
167 ClientSocketPoolHistograms histograms_; | |
168 MockTransportClientSocketPool pool_; | |
169 CapturingBoundNetLog(bound_net_log_); | |
170 ScopedVector<WebSocketFrame> frames_; | |
171 TestCompletionCallback cb_; | |
172 scoped_refptr<GrowableIOBuffer> http_read_buffer_; | |
173 std::string sub_protocol_; | |
174 std::string extensions_; | |
175 WebSocketBasicStream::WebSocketMaskingKeyGeneratorFunction generator_; | |
176 bool expect_all_io_to_complete_; | |
177 }; | |
178 | |
179 // A test fixture for the common case of tests that only perform a single read. | |
180 class WebSocketBasicStreamSocketSingleReadTest | |
181 : public WebSocketBasicStreamSocketTest { | |
182 protected: | |
183 void CreateRead(const MockRead& read) { | |
184 reads_[0] = read; | |
185 CreateStream(reads_, 1U, NULL, 0); | |
186 } | |
187 | |
188 MockRead reads_[1]; | |
189 }; | |
190 | |
191 // A test fixture for tests that perform chunked reads. | |
192 class WebSocketBasicStreamSocketChunkedReadTest | |
193 : public WebSocketBasicStreamSocketTest { | |
194 protected: | |
195 // Specify the behaviour if there aren't enough chunks to use all the data. If | |
196 // LAST_FRAME_BIG is specified, then the rest of the data will be | |
197 // put in the last chunk. If LAST_FRAME_NOT_BIG is specified, then the last | |
198 // frame will be no bigger than the rest of the frames (but it can be smaller, | |
199 // if not enough data remains). | |
200 enum LastFrameBehaviour { | |
201 LAST_FRAME_BIG, | |
202 LAST_FRAME_NOT_BIG | |
203 }; | |
204 | |
205 // Prepares a read from |data| of |data_size|, split into |number_of_chunks|, | |
206 // each of |chunk_size| (except that the last chunk may be larger or | |
207 // smaller). All reads must be either SYNCHRONOUS or ASYNC (not a mixture), | |
208 // and errors cannot be simulated. Once data is exhausted, further reads will | |
209 // return 0 (ie. connection closed). | |
210 void CreateChunkedRead(IoMode mode, | |
211 const char data[], | |
212 size_t data_size, | |
213 int chunk_size, | |
214 int number_of_chunks, | |
215 LastFrameBehaviour last_frame_behaviour) { | |
216 reads_.reset(new MockRead[number_of_chunks]); | |
217 const char* start = data; | |
218 for (int i = 0; i < number_of_chunks; ++i) { | |
219 int len = chunk_size; | |
220 const bool is_last_chunk = (i == number_of_chunks - 1); | |
221 if ((last_frame_behaviour == LAST_FRAME_BIG && is_last_chunk) || | |
222 static_cast<int>(data + data_size - start) < len) { | |
223 len = static_cast<int>(data + data_size - start); | |
224 } | |
225 reads_[i] = MockRead(mode, start, len); | |
226 start += len; | |
227 } | |
228 CreateStream(reads_.get(), number_of_chunks, NULL, 0); | |
229 } | |
230 | |
231 scoped_ptr<MockRead[]> reads_; | |
232 }; | |
233 | |
234 // Test fixture for write tests. | |
235 class WebSocketBasicStreamSocketWriteTest | |
236 : public WebSocketBasicStreamSocketTest { | |
237 protected: | |
238 // All write tests use the same frame, so it is easiest to create it during | |
239 // test creation. | |
240 void SetUp() override { PrepareWriteFrame(); } | |
241 | |
242 // Creates a WebSocketFrame with a wire format matching kWriteFrame and adds | |
243 // it to |frames_|. | |
244 void PrepareWriteFrame() { | |
245 scoped_ptr<WebSocketFrame> frame( | |
246 new WebSocketFrame(WebSocketFrameHeader::kOpCodeText)); | |
247 const size_t payload_size = | |
248 kWriteFrameSize - (WebSocketFrameHeader::kBaseHeaderSize + | |
249 WebSocketFrameHeader::kMaskingKeyLength); | |
250 frame->data = new IOBuffer(payload_size); | |
251 memcpy(frame->data->data(), | |
252 kWriteFrame + kWriteFrameSize - payload_size, | |
253 payload_size); | |
254 WebSocketFrameHeader& header = frame->header; | |
255 header.final = true; | |
256 header.masked = true; | |
257 header.payload_length = payload_size; | |
258 frames_.push_back(frame.release()); | |
259 } | |
260 | |
261 // Creates a stream that expects the listed writes. | |
262 template <size_t N> | |
263 void CreateWriteOnly(MockWrite (&writes)[N]) { | |
264 CreateStream(NULL, 0, writes, N); | |
265 } | |
266 }; | |
267 | |
268 TEST_F(WebSocketBasicStreamSocketTest, ConstructionWorks) { | |
269 CreateNullStream(); | |
270 } | |
271 | |
272 TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncReadWorks) { | |
273 CreateRead(MockRead(SYNCHRONOUS, kSampleFrame, kSampleFrameSize)); | |
274 int result = stream_->ReadFrames(&frames_, cb_.callback()); | |
275 EXPECT_EQ(OK, result); | |
276 ASSERT_EQ(1U, frames_.size()); | |
277 EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length); | |
278 EXPECT_TRUE(frames_[0]->header.final); | |
279 } | |
280 | |
281 TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncReadWorks) { | |
282 CreateRead(MockRead(ASYNC, kSampleFrame, kSampleFrameSize)); | |
283 int result = stream_->ReadFrames(&frames_, cb_.callback()); | |
284 ASSERT_EQ(ERR_IO_PENDING, result); | |
285 EXPECT_EQ(OK, cb_.WaitForResult()); | |
286 ASSERT_EQ(1U, frames_.size()); | |
287 EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length); | |
288 // Don't repeat all the tests from SyncReadWorks; just enough to be sure the | |
289 // frame was really read. | |
290 } | |
291 | |
292 // ReadFrames will not return a frame whose header has not been wholly received. | |
293 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, HeaderFragmentedSync) { | |
294 CreateChunkedRead( | |
295 SYNCHRONOUS, kSampleFrame, kSampleFrameSize, 1, 2, LAST_FRAME_BIG); | |
296 int result = stream_->ReadFrames(&frames_, cb_.callback()); | |
297 EXPECT_EQ(OK, result); | |
298 ASSERT_EQ(1U, frames_.size()); | |
299 EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length); | |
300 } | |
301 | |
302 // The same behaviour applies to asynchronous reads. | |
303 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, HeaderFragmentedAsync) { | |
304 CreateChunkedRead( | |
305 ASYNC, kSampleFrame, kSampleFrameSize, 1, 2, LAST_FRAME_BIG); | |
306 int result = stream_->ReadFrames(&frames_, cb_.callback()); | |
307 ASSERT_EQ(ERR_IO_PENDING, result); | |
308 EXPECT_EQ(OK, cb_.WaitForResult()); | |
309 ASSERT_EQ(1U, frames_.size()); | |
310 EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length); | |
311 } | |
312 | |
313 // If it receives an incomplete header in a synchronous call, then has to wait | |
314 // for the rest of the frame, ReadFrames will return ERR_IO_PENDING. | |
315 TEST_F(WebSocketBasicStreamSocketTest, HeaderFragmentedSyncAsync) { | |
316 MockRead reads[] = {MockRead(SYNCHRONOUS, kSampleFrame, 1), | |
317 MockRead(ASYNC, kSampleFrame + 1, kSampleFrameSize - 1)}; | |
318 CreateReadOnly(reads); | |
319 int result = stream_->ReadFrames(&frames_, cb_.callback()); | |
320 ASSERT_EQ(ERR_IO_PENDING, result); | |
321 EXPECT_EQ(OK, cb_.WaitForResult()); | |
322 ASSERT_EQ(1U, frames_.size()); | |
323 EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length); | |
324 } | |
325 | |
326 // An extended header should also return ERR_IO_PENDING if it is not completely | |
327 // received. | |
328 TEST_F(WebSocketBasicStreamSocketTest, FragmentedLargeHeader) { | |
329 MockRead reads[] = { | |
330 MockRead(SYNCHRONOUS, kPartialLargeFrame, kLargeFrameHeaderSize - 1), | |
331 MockRead(SYNCHRONOUS, ERR_IO_PENDING)}; | |
332 CreateReadOnly(reads); | |
333 EXPECT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
334 } | |
335 | |
336 // A frame that does not arrive in a single read should be broken into separate | |
337 // frames. | |
338 TEST_F(WebSocketBasicStreamSocketSingleReadTest, LargeFrameFirstChunk) { | |
339 CreateRead(MockRead(SYNCHRONOUS, kPartialLargeFrame, kPartialLargeFrameSize)); | |
340 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
341 ASSERT_EQ(1U, frames_.size()); | |
342 EXPECT_FALSE(frames_[0]->header.final); | |
343 EXPECT_EQ(kPartialLargeFrameSize - kLargeFrameHeaderSize, | |
344 static_cast<size_t>(frames_[0]->header.payload_length)); | |
345 } | |
346 | |
347 // If only the header of a data frame arrives, we should receive a frame with a | |
348 // zero-size payload. | |
349 TEST_F(WebSocketBasicStreamSocketSingleReadTest, HeaderOnlyChunk) { | |
350 CreateRead(MockRead(SYNCHRONOUS, kPartialLargeFrame, kLargeFrameHeaderSize)); | |
351 | |
352 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
353 ASSERT_EQ(1U, frames_.size()); | |
354 EXPECT_EQ(NULL, frames_[0]->data.get()); | |
355 EXPECT_EQ(0U, frames_[0]->header.payload_length); | |
356 EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode); | |
357 } | |
358 | |
359 // If the header and the body of a data frame arrive seperately, we should see | |
360 // them as separate frames. | |
361 TEST_F(WebSocketBasicStreamSocketTest, HeaderBodySeparated) { | |
362 MockRead reads[] = { | |
363 MockRead(SYNCHRONOUS, kPartialLargeFrame, kLargeFrameHeaderSize), | |
364 MockRead(ASYNC, | |
365 kPartialLargeFrame + kLargeFrameHeaderSize, | |
366 kPartialLargeFrameSize - kLargeFrameHeaderSize)}; | |
367 CreateReadOnly(reads); | |
368 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
369 ASSERT_EQ(1U, frames_.size()); | |
370 EXPECT_EQ(NULL, frames_[0]->data.get()); | |
371 EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode); | |
372 frames_.clear(); | |
373 EXPECT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
374 EXPECT_EQ(OK, cb_.WaitForResult()); | |
375 ASSERT_EQ(1U, frames_.size()); | |
376 EXPECT_EQ(kPartialLargeFrameSize - kLargeFrameHeaderSize, | |
377 frames_[0]->header.payload_length); | |
378 EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, | |
379 frames_[0]->header.opcode); | |
380 } | |
381 | |
382 // Every frame has a header with a correct payload_length field. | |
383 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, LargeFrameTwoChunks) { | |
384 const size_t kChunkSize = 16; | |
385 CreateChunkedRead(ASYNC, | |
386 kPartialLargeFrame, | |
387 kPartialLargeFrameSize, | |
388 kChunkSize, | |
389 2, | |
390 LAST_FRAME_NOT_BIG); | |
391 TestCompletionCallback cb[2]; | |
392 | |
393 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback())); | |
394 EXPECT_EQ(OK, cb[0].WaitForResult()); | |
395 ASSERT_EQ(1U, frames_.size()); | |
396 EXPECT_EQ(kChunkSize - kLargeFrameHeaderSize, | |
397 frames_[0]->header.payload_length); | |
398 | |
399 frames_.clear(); | |
400 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[1].callback())); | |
401 EXPECT_EQ(OK, cb[1].WaitForResult()); | |
402 ASSERT_EQ(1U, frames_.size()); | |
403 EXPECT_EQ(kChunkSize, frames_[0]->header.payload_length); | |
404 } | |
405 | |
406 // Only the final frame of a fragmented message has |final| bit set. | |
407 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, OnlyFinalChunkIsFinal) { | |
408 static const size_t kFirstChunkSize = 4; | |
409 CreateChunkedRead(ASYNC, | |
410 kSampleFrame, | |
411 kSampleFrameSize, | |
412 kFirstChunkSize, | |
413 2, | |
414 LAST_FRAME_BIG); | |
415 TestCompletionCallback cb[2]; | |
416 | |
417 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback())); | |
418 EXPECT_EQ(OK, cb[0].WaitForResult()); | |
419 ASSERT_EQ(1U, frames_.size()); | |
420 ASSERT_FALSE(frames_[0]->header.final); | |
421 | |
422 frames_.clear(); | |
423 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[1].callback())); | |
424 EXPECT_EQ(OK, cb[1].WaitForResult()); | |
425 ASSERT_EQ(1U, frames_.size()); | |
426 ASSERT_TRUE(frames_[0]->header.final); | |
427 } | |
428 | |
429 // All frames after the first have their opcode changed to Continuation. | |
430 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, ContinuationOpCodeUsed) { | |
431 const size_t kFirstChunkSize = 3; | |
432 const int kChunkCount = 3; | |
433 // The input data is one frame with opcode Text, which arrives in three | |
434 // separate chunks. | |
435 CreateChunkedRead(ASYNC, | |
436 kSampleFrame, | |
437 kSampleFrameSize, | |
438 kFirstChunkSize, | |
439 kChunkCount, | |
440 LAST_FRAME_BIG); | |
441 TestCompletionCallback cb[kChunkCount]; | |
442 | |
443 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback())); | |
444 EXPECT_EQ(OK, cb[0].WaitForResult()); | |
445 ASSERT_EQ(1U, frames_.size()); | |
446 EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode); | |
447 | |
448 // This test uses a loop to verify that the opcode for every frames generated | |
449 // after the first is converted to Continuation. | |
450 for (int i = 1; i < kChunkCount; ++i) { | |
451 frames_.clear(); | |
452 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[i].callback())); | |
453 EXPECT_EQ(OK, cb[i].WaitForResult()); | |
454 ASSERT_EQ(1U, frames_.size()); | |
455 EXPECT_EQ(WebSocketFrameHeader::kOpCodeContinuation, | |
456 frames_[0]->header.opcode); | |
457 } | |
458 } | |
459 | |
460 // Multiple frames that arrive together should be parsed correctly. | |
461 TEST_F(WebSocketBasicStreamSocketSingleReadTest, ThreeFramesTogether) { | |
462 CreateRead(MockRead(SYNCHRONOUS, kMultipleFrames, kMultipleFramesSize)); | |
463 | |
464 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
465 ASSERT_EQ(3U, frames_.size()); | |
466 EXPECT_TRUE(frames_[0]->header.final); | |
467 EXPECT_TRUE(frames_[1]->header.final); | |
468 EXPECT_TRUE(frames_[2]->header.final); | |
469 } | |
470 | |
471 // ERR_CONNECTION_CLOSED must be returned on close. | |
472 TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncClose) { | |
473 CreateRead(MockRead(SYNCHRONOUS, "", 0)); | |
474 | |
475 EXPECT_EQ(ERR_CONNECTION_CLOSED, | |
476 stream_->ReadFrames(&frames_, cb_.callback())); | |
477 } | |
478 | |
479 TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncClose) { | |
480 CreateRead(MockRead(ASYNC, "", 0)); | |
481 | |
482 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
483 EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult()); | |
484 } | |
485 | |
486 // The result should be the same if the socket returns | |
487 // ERR_CONNECTION_CLOSED. This is not expected to happen on an established | |
488 // connection; a Read of size 0 is the expected behaviour. The key point of this | |
489 // test is to confirm that ReadFrames() behaviour is identical in both cases. | |
490 TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncCloseWithErr) { | |
491 CreateRead(MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED)); | |
492 | |
493 EXPECT_EQ(ERR_CONNECTION_CLOSED, | |
494 stream_->ReadFrames(&frames_, cb_.callback())); | |
495 } | |
496 | |
497 TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncCloseWithErr) { | |
498 CreateRead(MockRead(ASYNC, ERR_CONNECTION_CLOSED)); | |
499 | |
500 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
501 EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult()); | |
502 } | |
503 | |
504 TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncErrorsPassedThrough) { | |
505 // ERR_INSUFFICIENT_RESOURCES here represents an arbitrary error that | |
506 // WebSocketBasicStream gives no special handling to. | |
507 CreateRead(MockRead(SYNCHRONOUS, ERR_INSUFFICIENT_RESOURCES)); | |
508 | |
509 EXPECT_EQ(ERR_INSUFFICIENT_RESOURCES, | |
510 stream_->ReadFrames(&frames_, cb_.callback())); | |
511 } | |
512 | |
513 TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncErrorsPassedThrough) { | |
514 CreateRead(MockRead(ASYNC, ERR_INSUFFICIENT_RESOURCES)); | |
515 | |
516 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
517 EXPECT_EQ(ERR_INSUFFICIENT_RESOURCES, cb_.WaitForResult()); | |
518 } | |
519 | |
520 // If we get a frame followed by a close, we should receive them separately. | |
521 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, CloseAfterFrame) { | |
522 // The chunk size equals the data size, so the second chunk is 0 size, closing | |
523 // the connection. | |
524 CreateChunkedRead(SYNCHRONOUS, | |
525 kSampleFrame, | |
526 kSampleFrameSize, | |
527 kSampleFrameSize, | |
528 2, | |
529 LAST_FRAME_NOT_BIG); | |
530 | |
531 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
532 EXPECT_EQ(1U, frames_.size()); | |
533 frames_.clear(); | |
534 EXPECT_EQ(ERR_CONNECTION_CLOSED, | |
535 stream_->ReadFrames(&frames_, cb_.callback())); | |
536 } | |
537 | |
538 // Synchronous close after an async frame header is handled by a different code | |
539 // path. | |
540 TEST_F(WebSocketBasicStreamSocketTest, AsyncCloseAfterIncompleteHeader) { | |
541 MockRead reads[] = {MockRead(ASYNC, kSampleFrame, 1U), | |
542 MockRead(SYNCHRONOUS, "", 0)}; | |
543 CreateReadOnly(reads); | |
544 | |
545 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
546 EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult()); | |
547 } | |
548 | |
549 // When Stream::Read returns ERR_CONNECTION_CLOSED we get the same result via a | |
550 // slightly different code path. | |
551 TEST_F(WebSocketBasicStreamSocketTest, AsyncErrCloseAfterIncompleteHeader) { | |
552 MockRead reads[] = {MockRead(ASYNC, kSampleFrame, 1U), | |
553 MockRead(SYNCHRONOUS, ERR_CONNECTION_CLOSED)}; | |
554 CreateReadOnly(reads); | |
555 | |
556 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
557 EXPECT_EQ(ERR_CONNECTION_CLOSED, cb_.WaitForResult()); | |
558 } | |
559 | |
560 // An empty first frame is not ignored. | |
561 TEST_F(WebSocketBasicStreamSocketSingleReadTest, EmptyFirstFrame) { | |
562 CreateRead(MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize)); | |
563 | |
564 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
565 ASSERT_EQ(1U, frames_.size()); | |
566 EXPECT_EQ(NULL, frames_[0]->data.get()); | |
567 EXPECT_EQ(0U, frames_[0]->header.payload_length); | |
568 } | |
569 | |
570 // An empty frame in the middle of a message is ignored. | |
571 TEST_F(WebSocketBasicStreamSocketTest, EmptyMiddleFrame) { | |
572 MockRead reads[] = { | |
573 MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize), | |
574 MockRead(SYNCHRONOUS, kEmptyMiddleFrame, kEmptyMiddleFrameSize), | |
575 MockRead(SYNCHRONOUS, ERR_IO_PENDING)}; | |
576 CreateReadOnly(reads); | |
577 | |
578 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
579 EXPECT_EQ(1U, frames_.size()); | |
580 frames_.clear(); | |
581 EXPECT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
582 } | |
583 | |
584 // An empty frame in the middle of a message that arrives separately is still | |
585 // ignored. | |
586 TEST_F(WebSocketBasicStreamSocketTest, EmptyMiddleFrameAsync) { | |
587 MockRead reads[] = { | |
588 MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize), | |
589 MockRead(ASYNC, kEmptyMiddleFrame, kEmptyMiddleFrameSize), | |
590 // We include a pong message to verify the middle frame was actually | |
591 // processed. | |
592 MockRead(ASYNC, kValidPong, kValidPongSize)}; | |
593 CreateReadOnly(reads); | |
594 | |
595 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
596 EXPECT_EQ(1U, frames_.size()); | |
597 frames_.clear(); | |
598 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
599 EXPECT_EQ(OK, cb_.WaitForResult()); | |
600 ASSERT_EQ(1U, frames_.size()); | |
601 EXPECT_EQ(WebSocketFrameHeader::kOpCodePong, frames_[0]->header.opcode); | |
602 } | |
603 | |
604 // An empty final frame is not ignored. | |
605 TEST_F(WebSocketBasicStreamSocketSingleReadTest, EmptyFinalFrame) { | |
606 CreateRead( | |
607 MockRead(SYNCHRONOUS, kEmptyFinalTextFrame, kEmptyFinalTextFrameSize)); | |
608 | |
609 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
610 ASSERT_EQ(1U, frames_.size()); | |
611 EXPECT_EQ(NULL, frames_[0]->data.get()); | |
612 EXPECT_EQ(0U, frames_[0]->header.payload_length); | |
613 } | |
614 | |
615 // An empty middle frame is ignored with a final frame present. | |
616 TEST_F(WebSocketBasicStreamSocketTest, ThreeFrameEmptyMessage) { | |
617 MockRead reads[] = { | |
618 MockRead(SYNCHRONOUS, kEmptyFirstFrame, kEmptyFirstFrameSize), | |
619 MockRead(SYNCHRONOUS, kEmptyMiddleFrame, kEmptyMiddleFrameSize), | |
620 MockRead(SYNCHRONOUS, | |
621 kEmptyFinalContinuationFrame, | |
622 kEmptyFinalContinuationFrameSize)}; | |
623 CreateReadOnly(reads); | |
624 | |
625 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
626 ASSERT_EQ(1U, frames_.size()); | |
627 EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode); | |
628 frames_.clear(); | |
629 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
630 ASSERT_EQ(1U, frames_.size()); | |
631 EXPECT_TRUE(frames_[0]->header.final); | |
632 } | |
633 | |
634 // If there was a frame read at the same time as the response headers (and the | |
635 // handshake succeeded), then we should parse it. | |
636 TEST_F(WebSocketBasicStreamSocketTest, HttpReadBufferIsUsed) { | |
637 SetHttpReadBuffer(kSampleFrame, kSampleFrameSize); | |
638 CreateNullStream(); | |
639 | |
640 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
641 ASSERT_EQ(1U, frames_.size()); | |
642 ASSERT_TRUE(frames_[0]->data.get()); | |
643 EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length); | |
644 } | |
645 | |
646 // Check that a frame whose header partially arrived at the end of the response | |
647 // headers works correctly. | |
648 TEST_F(WebSocketBasicStreamSocketSingleReadTest, | |
649 PartialFrameHeaderInHttpResponse) { | |
650 SetHttpReadBuffer(kSampleFrame, 1); | |
651 CreateRead(MockRead(ASYNC, kSampleFrame + 1, kSampleFrameSize - 1)); | |
652 | |
653 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
654 EXPECT_EQ(OK, cb_.WaitForResult()); | |
655 ASSERT_EQ(1U, frames_.size()); | |
656 ASSERT_TRUE(frames_[0]->data.get()); | |
657 EXPECT_EQ(GG_UINT64_C(6), frames_[0]->header.payload_length); | |
658 EXPECT_EQ(WebSocketFrameHeader::kOpCodeText, frames_[0]->header.opcode); | |
659 } | |
660 | |
661 // Check that a control frame which partially arrives at the end of the response | |
662 // headers works correctly. | |
663 TEST_F(WebSocketBasicStreamSocketSingleReadTest, | |
664 PartialControlFrameInHttpResponse) { | |
665 const size_t kPartialFrameBytes = 3; | |
666 SetHttpReadBuffer(kCloseFrame, kPartialFrameBytes); | |
667 CreateRead(MockRead(ASYNC, | |
668 kCloseFrame + kPartialFrameBytes, | |
669 kCloseFrameSize - kPartialFrameBytes)); | |
670 | |
671 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
672 EXPECT_EQ(OK, cb_.WaitForResult()); | |
673 ASSERT_EQ(1U, frames_.size()); | |
674 EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode); | |
675 EXPECT_EQ(kCloseFrameSize - 2, frames_[0]->header.payload_length); | |
676 EXPECT_EQ( | |
677 0, | |
678 memcmp(frames_[0]->data->data(), kCloseFrame + 2, kCloseFrameSize - 2)); | |
679 } | |
680 | |
681 // Check that a control frame which partially arrives at the end of the response | |
682 // headers works correctly. Synchronous version (unlikely in practice). | |
683 TEST_F(WebSocketBasicStreamSocketSingleReadTest, | |
684 PartialControlFrameInHttpResponseSync) { | |
685 const size_t kPartialFrameBytes = 3; | |
686 SetHttpReadBuffer(kCloseFrame, kPartialFrameBytes); | |
687 CreateRead(MockRead(SYNCHRONOUS, | |
688 kCloseFrame + kPartialFrameBytes, | |
689 kCloseFrameSize - kPartialFrameBytes)); | |
690 | |
691 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
692 ASSERT_EQ(1U, frames_.size()); | |
693 EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode); | |
694 } | |
695 | |
696 // Check that an invalid frame results in an error. | |
697 TEST_F(WebSocketBasicStreamSocketSingleReadTest, SyncInvalidFrame) { | |
698 CreateRead(MockRead(SYNCHRONOUS, kInvalidFrame, kInvalidFrameSize)); | |
699 | |
700 EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, | |
701 stream_->ReadFrames(&frames_, cb_.callback())); | |
702 } | |
703 | |
704 TEST_F(WebSocketBasicStreamSocketSingleReadTest, AsyncInvalidFrame) { | |
705 CreateRead(MockRead(ASYNC, kInvalidFrame, kInvalidFrameSize)); | |
706 | |
707 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
708 EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, cb_.WaitForResult()); | |
709 } | |
710 | |
711 // A control frame without a FIN flag is invalid and should not be passed | |
712 // through to higher layers. RFC6455 5.5 "All control frames ... MUST NOT be | |
713 // fragmented." | |
714 TEST_F(WebSocketBasicStreamSocketSingleReadTest, ControlFrameWithoutFin) { | |
715 CreateRead( | |
716 MockRead(SYNCHRONOUS, kPingFrameWithoutFin, kPingFrameWithoutFinSize)); | |
717 | |
718 EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, | |
719 stream_->ReadFrames(&frames_, cb_.callback())); | |
720 EXPECT_TRUE(frames_.empty()); | |
721 } | |
722 | |
723 // A control frame over 125 characters is invalid. RFC6455 5.5 "All control | |
724 // frames MUST have a payload length of 125 bytes or less". Since we use a | |
725 // 125-byte buffer to assemble fragmented control frames, we need to detect this | |
726 // error before attempting to assemble the fragments. | |
727 TEST_F(WebSocketBasicStreamSocketSingleReadTest, OverlongControlFrame) { | |
728 CreateRead(MockRead(SYNCHRONOUS, k126BytePong, k126BytePongSize)); | |
729 | |
730 EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, | |
731 stream_->ReadFrames(&frames_, cb_.callback())); | |
732 EXPECT_TRUE(frames_.empty()); | |
733 } | |
734 | |
735 // A control frame over 125 characters should still be rejected if it is split | |
736 // into multiple chunks. | |
737 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, SplitOverlongControlFrame) { | |
738 const size_t kFirstChunkSize = 16; | |
739 expect_all_io_to_complete_ = false; | |
740 CreateChunkedRead(SYNCHRONOUS, | |
741 k126BytePong, | |
742 k126BytePongSize, | |
743 kFirstChunkSize, | |
744 2, | |
745 LAST_FRAME_BIG); | |
746 | |
747 EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, | |
748 stream_->ReadFrames(&frames_, cb_.callback())); | |
749 EXPECT_TRUE(frames_.empty()); | |
750 } | |
751 | |
752 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, | |
753 AsyncSplitOverlongControlFrame) { | |
754 const size_t kFirstChunkSize = 16; | |
755 expect_all_io_to_complete_ = false; | |
756 CreateChunkedRead(ASYNC, | |
757 k126BytePong, | |
758 k126BytePongSize, | |
759 kFirstChunkSize, | |
760 2, | |
761 LAST_FRAME_BIG); | |
762 | |
763 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
764 EXPECT_EQ(ERR_WS_PROTOCOL_ERROR, cb_.WaitForResult()); | |
765 // The caller should not call ReadFrames() again after receiving an error | |
766 // other than ERR_IO_PENDING. | |
767 EXPECT_TRUE(frames_.empty()); | |
768 } | |
769 | |
770 // In the synchronous case, ReadFrames assembles the whole control frame before | |
771 // returning. | |
772 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, SyncControlFrameAssembly) { | |
773 const size_t kChunkSize = 3; | |
774 CreateChunkedRead( | |
775 SYNCHRONOUS, kCloseFrame, kCloseFrameSize, kChunkSize, 3, LAST_FRAME_BIG); | |
776 | |
777 EXPECT_EQ(OK, stream_->ReadFrames(&frames_, cb_.callback())); | |
778 ASSERT_EQ(1U, frames_.size()); | |
779 EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode); | |
780 } | |
781 | |
782 // In the asynchronous case, the callback is not called until the control frame | |
783 // has been completely assembled. | |
784 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, AsyncControlFrameAssembly) { | |
785 const size_t kChunkSize = 3; | |
786 CreateChunkedRead( | |
787 ASYNC, kCloseFrame, kCloseFrameSize, kChunkSize, 3, LAST_FRAME_BIG); | |
788 | |
789 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
790 EXPECT_EQ(OK, cb_.WaitForResult()); | |
791 ASSERT_EQ(1U, frames_.size()); | |
792 EXPECT_EQ(WebSocketFrameHeader::kOpCodeClose, frames_[0]->header.opcode); | |
793 } | |
794 | |
795 // A frame with a 1MB payload that has to be read in chunks. | |
796 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, OneMegFrame) { | |
797 // This should be equal to the definition of kReadBufferSize in | |
798 // websocket_basic_stream.cc. | |
799 const int kReadBufferSize = 32 * 1024; | |
800 const uint64 kPayloadSize = 1 << 20; | |
801 const size_t kWireSize = kPayloadSize + kLargeFrameHeaderSize; | |
802 const size_t kExpectedFrameCount = | |
803 (kWireSize + kReadBufferSize - 1) / kReadBufferSize; | |
804 scoped_ptr<char[]> big_frame(new char[kWireSize]); | |
805 memcpy(big_frame.get(), "\x81\x7F", 2); | |
806 base::WriteBigEndian(big_frame.get() + 2, kPayloadSize); | |
807 memset(big_frame.get() + kLargeFrameHeaderSize, 'A', kPayloadSize); | |
808 | |
809 CreateChunkedRead(ASYNC, | |
810 big_frame.get(), | |
811 kWireSize, | |
812 kReadBufferSize, | |
813 kExpectedFrameCount, | |
814 LAST_FRAME_BIG); | |
815 | |
816 for (size_t frame = 0; frame < kExpectedFrameCount; ++frame) { | |
817 frames_.clear(); | |
818 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb_.callback())); | |
819 EXPECT_EQ(OK, cb_.WaitForResult()); | |
820 ASSERT_EQ(1U, frames_.size()); | |
821 size_t expected_payload_size = kReadBufferSize; | |
822 if (frame == 0) { | |
823 expected_payload_size = kReadBufferSize - kLargeFrameHeaderSize; | |
824 } else if (frame == kExpectedFrameCount - 1) { | |
825 expected_payload_size = kLargeFrameHeaderSize; | |
826 } | |
827 EXPECT_EQ(expected_payload_size, frames_[0]->header.payload_length); | |
828 } | |
829 } | |
830 | |
831 // A frame with reserved flag(s) set that arrives in chunks should only have the | |
832 // reserved flag(s) set on the first chunk when split. | |
833 TEST_F(WebSocketBasicStreamSocketChunkedReadTest, ReservedFlagCleared) { | |
834 static const char kReservedFlagFrame[] = "\x41\x05Hello"; | |
835 const size_t kReservedFlagFrameSize = arraysize(kReservedFlagFrame) - 1; | |
836 const size_t kChunkSize = 5; | |
837 | |
838 CreateChunkedRead(ASYNC, | |
839 kReservedFlagFrame, | |
840 kReservedFlagFrameSize, | |
841 kChunkSize, | |
842 2, | |
843 LAST_FRAME_BIG); | |
844 | |
845 TestCompletionCallback cb[2]; | |
846 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[0].callback())); | |
847 EXPECT_EQ(OK, cb[0].WaitForResult()); | |
848 ASSERT_EQ(1U, frames_.size()); | |
849 EXPECT_TRUE(frames_[0]->header.reserved1); | |
850 | |
851 frames_.clear(); | |
852 ASSERT_EQ(ERR_IO_PENDING, stream_->ReadFrames(&frames_, cb[1].callback())); | |
853 EXPECT_EQ(OK, cb[1].WaitForResult()); | |
854 ASSERT_EQ(1U, frames_.size()); | |
855 EXPECT_FALSE(frames_[0]->header.reserved1); | |
856 } | |
857 | |
858 // Check that writing a frame all at once works. | |
859 TEST_F(WebSocketBasicStreamSocketWriteTest, WriteAtOnce) { | |
860 MockWrite writes[] = {MockWrite(SYNCHRONOUS, kWriteFrame, kWriteFrameSize)}; | |
861 CreateWriteOnly(writes); | |
862 | |
863 EXPECT_EQ(OK, stream_->WriteFrames(&frames_, cb_.callback())); | |
864 } | |
865 | |
866 // Check that completely async writing works. | |
867 TEST_F(WebSocketBasicStreamSocketWriteTest, AsyncWriteAtOnce) { | |
868 MockWrite writes[] = {MockWrite(ASYNC, kWriteFrame, kWriteFrameSize)}; | |
869 CreateWriteOnly(writes); | |
870 | |
871 ASSERT_EQ(ERR_IO_PENDING, stream_->WriteFrames(&frames_, cb_.callback())); | |
872 EXPECT_EQ(OK, cb_.WaitForResult()); | |
873 } | |
874 | |
875 // Check that writing a frame to an extremely full kernel buffer (so that it | |
876 // ends up being sent in bits) works. The WriteFrames() callback should not be | |
877 // called until all parts have been written. | |
878 TEST_F(WebSocketBasicStreamSocketWriteTest, WriteInBits) { | |
879 MockWrite writes[] = {MockWrite(SYNCHRONOUS, kWriteFrame, 4), | |
880 MockWrite(ASYNC, kWriteFrame + 4, 4), | |
881 MockWrite(ASYNC, kWriteFrame + 8, kWriteFrameSize - 8)}; | |
882 CreateWriteOnly(writes); | |
883 | |
884 ASSERT_EQ(ERR_IO_PENDING, stream_->WriteFrames(&frames_, cb_.callback())); | |
885 EXPECT_EQ(OK, cb_.WaitForResult()); | |
886 } | |
887 | |
888 // Check that writing a Pong frame with a NULL body works. | |
889 TEST_F(WebSocketBasicStreamSocketWriteTest, WriteNullPong) { | |
890 MockWrite writes[] = { | |
891 MockWrite(SYNCHRONOUS, kMaskedEmptyPong, kMaskedEmptyPongSize)}; | |
892 CreateWriteOnly(writes); | |
893 | |
894 scoped_ptr<WebSocketFrame> frame( | |
895 new WebSocketFrame(WebSocketFrameHeader::kOpCodePong)); | |
896 WebSocketFrameHeader& header = frame->header; | |
897 header.final = true; | |
898 header.masked = true; | |
899 header.payload_length = 0; | |
900 ScopedVector<WebSocketFrame> frames; | |
901 frames.push_back(frame.release()); | |
902 EXPECT_EQ(OK, stream_->WriteFrames(&frames, cb_.callback())); | |
903 } | |
904 | |
905 // Check that writing with a non-NULL mask works correctly. | |
906 TEST_F(WebSocketBasicStreamSocketTest, WriteNonNulMask) { | |
907 std::string masked_frame = std::string("\x81\x88"); | |
908 masked_frame += std::string(kNonNulMaskingKey.key, 4); | |
909 masked_frame += "jiggered"; | |
910 MockWrite writes[] = { | |
911 MockWrite(SYNCHRONOUS, masked_frame.data(), masked_frame.size())}; | |
912 generator_ = &GenerateNonNulMaskingKey; | |
913 CreateStream(NULL, 0, writes, arraysize(writes)); | |
914 | |
915 scoped_ptr<WebSocketFrame> frame( | |
916 new WebSocketFrame(WebSocketFrameHeader::kOpCodeText)); | |
917 const std::string unmasked_payload = "graphics"; | |
918 const size_t payload_size = unmasked_payload.size(); | |
919 frame->data = new IOBuffer(payload_size); | |
920 memcpy(frame->data->data(), unmasked_payload.data(), payload_size); | |
921 WebSocketFrameHeader& header = frame->header; | |
922 header.final = true; | |
923 header.masked = true; | |
924 header.payload_length = payload_size; | |
925 frames_.push_back(frame.release()); | |
926 | |
927 EXPECT_EQ(OK, stream_->WriteFrames(&frames_, cb_.callback())); | |
928 } | |
929 | |
930 TEST_F(WebSocketBasicStreamSocketTest, GetExtensionsWorks) { | |
931 extensions_ = "inflate-uuencode"; | |
932 CreateNullStream(); | |
933 | |
934 EXPECT_EQ("inflate-uuencode", stream_->GetExtensions()); | |
935 } | |
936 | |
937 TEST_F(WebSocketBasicStreamSocketTest, GetSubProtocolWorks) { | |
938 sub_protocol_ = "cyberchat"; | |
939 CreateNullStream(); | |
940 | |
941 EXPECT_EQ("cyberchat", stream_->GetSubProtocol()); | |
942 } | |
943 | |
944 } // namespace | |
945 } // namespace net | |
OLD | NEW |