| 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 |