1// Copyright (c) 2012 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#include "net/http/http_stream_parser.h" 6 7#include <algorithm> 8#include <string> 9#include <vector> 10 11#include "base/files/file_path.h" 12#include "base/files/file_util.h" 13#include "base/files/scoped_temp_dir.h" 14#include "base/memory/ref_counted.h" 15#include "base/run_loop.h" 16#include "base/strings/string_piece.h" 17#include "base/strings/stringprintf.h" 18#include "net/base/io_buffer.h" 19#include "net/base/net_errors.h" 20#include "net/base/test_completion_callback.h" 21#include "net/base/upload_bytes_element_reader.h" 22#include "net/base/upload_data_stream.h" 23#include "net/base/upload_file_element_reader.h" 24#include "net/http/http_request_headers.h" 25#include "net/http/http_request_info.h" 26#include "net/http/http_response_headers.h" 27#include "net/http/http_response_info.h" 28#include "net/socket/client_socket_handle.h" 29#include "net/socket/socket_test_util.h" 30#include "testing/gtest/include/gtest/gtest.h" 31#include "url/gurl.h" 32 33namespace net { 34 35namespace { 36 37const size_t kOutputSize = 1024; // Just large enough for this test. 38// The number of bytes that can fit in a buffer of kOutputSize. 39const size_t kMaxPayloadSize = 40 kOutputSize - HttpStreamParser::kChunkHeaderFooterSize; 41 42// The empty payload is how the last chunk is encoded. 43TEST(HttpStreamParser, EncodeChunk_EmptyPayload) { 44 char output[kOutputSize]; 45 46 const base::StringPiece kPayload = ""; 47 const base::StringPiece kExpected = "0\r\n\r\n"; 48 const int num_bytes_written = 49 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); 50 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written)); 51 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written)); 52} 53 54TEST(HttpStreamParser, EncodeChunk_ShortPayload) { 55 char output[kOutputSize]; 56 57 const std::string kPayload("foo\x00\x11\x22", 6); 58 // 11 = payload size + sizeof("6") + CRLF x 2. 59 const std::string kExpected("6\r\nfoo\x00\x11\x22\r\n", 11); 60 const int num_bytes_written = 61 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); 62 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written)); 63 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written)); 64} 65 66TEST(HttpStreamParser, EncodeChunk_LargePayload) { 67 char output[kOutputSize]; 68 69 const std::string kPayload(1000, '\xff'); // '\xff' x 1000. 70 // 3E8 = 1000 in hex. 71 const std::string kExpected = "3E8\r\n" + kPayload + "\r\n"; 72 const int num_bytes_written = 73 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); 74 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written)); 75 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written)); 76} 77 78TEST(HttpStreamParser, EncodeChunk_FullPayload) { 79 char output[kOutputSize]; 80 81 const std::string kPayload(kMaxPayloadSize, '\xff'); 82 // 3F4 = 1012 in hex. 83 const std::string kExpected = "3F4\r\n" + kPayload + "\r\n"; 84 const int num_bytes_written = 85 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); 86 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written)); 87 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written)); 88} 89 90TEST(HttpStreamParser, EncodeChunk_TooLargePayload) { 91 char output[kOutputSize]; 92 93 // The payload is one byte larger the output buffer size. 94 const std::string kPayload(kMaxPayloadSize + 1, '\xff'); 95 const int num_bytes_written = 96 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); 97 ASSERT_EQ(ERR_INVALID_ARGUMENT, num_bytes_written); 98} 99 100TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_NoBody) { 101 // Shouldn't be merged if upload data is non-existent. 102 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( 103 "some header", NULL)); 104} 105 106TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_EmptyBody) { 107 ScopedVector<UploadElementReader> element_readers; 108 scoped_ptr<UploadDataStream> body( 109 new UploadDataStream(element_readers.Pass(), 0)); 110 ASSERT_EQ(OK, body->Init(CompletionCallback())); 111 // Shouldn't be merged if upload data is empty. 112 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( 113 "some header", body.get())); 114} 115 116TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_ChunkedBody) { 117 const std::string payload = "123"; 118 scoped_ptr<UploadDataStream> body( 119 new UploadDataStream(UploadDataStream::CHUNKED, 0)); 120 body->AppendChunk(payload.data(), payload.size(), true); 121 ASSERT_EQ(OK, body->Init(CompletionCallback())); 122 // Shouldn't be merged if upload data carries chunked data. 123 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( 124 "some header", body.get())); 125} 126 127TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_FileBody) { 128 { 129 ScopedVector<UploadElementReader> element_readers; 130 131 // Create an empty temporary file. 132 base::ScopedTempDir temp_dir; 133 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 134 base::FilePath temp_file_path; 135 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), 136 &temp_file_path)); 137 138 element_readers.push_back( 139 new UploadFileElementReader(base::MessageLoopProxy::current().get(), 140 temp_file_path, 141 0, 142 0, 143 base::Time())); 144 145 scoped_ptr<UploadDataStream> body( 146 new UploadDataStream(element_readers.Pass(), 0)); 147 TestCompletionCallback callback; 148 ASSERT_EQ(ERR_IO_PENDING, body->Init(callback.callback())); 149 ASSERT_EQ(OK, callback.WaitForResult()); 150 // Shouldn't be merged if upload data carries a file, as it's not in-memory. 151 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( 152 "some header", body.get())); 153 } 154 // UploadFileElementReaders may post clean-up tasks on destruction. 155 base::RunLoop().RunUntilIdle(); 156} 157 158TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_SmallBodyInMemory) { 159 ScopedVector<UploadElementReader> element_readers; 160 const std::string payload = "123"; 161 element_readers.push_back(new UploadBytesElementReader( 162 payload.data(), payload.size())); 163 164 scoped_ptr<UploadDataStream> body( 165 new UploadDataStream(element_readers.Pass(), 0)); 166 ASSERT_EQ(OK, body->Init(CompletionCallback())); 167 // Yes, should be merged if the in-memory body is small here. 168 ASSERT_TRUE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( 169 "some header", body.get())); 170} 171 172TEST(HttpStreamParser, ShouldMergeRequestHeadersAndBody_LargeBodyInMemory) { 173 ScopedVector<UploadElementReader> element_readers; 174 const std::string payload(10000, 'a'); // 'a' x 10000. 175 element_readers.push_back(new UploadBytesElementReader( 176 payload.data(), payload.size())); 177 178 scoped_ptr<UploadDataStream> body( 179 new UploadDataStream(element_readers.Pass(), 0)); 180 ASSERT_EQ(OK, body->Init(CompletionCallback())); 181 // Shouldn't be merged if the in-memory body is large here. 182 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( 183 "some header", body.get())); 184} 185 186// Test to ensure the HttpStreamParser state machine does not get confused 187// when sending a request with a chunked body, where chunks become available 188// asynchronously, over a socket where writes may also complete 189// asynchronously. 190// This is a regression test for http://crbug.com/132243 191TEST(HttpStreamParser, AsyncChunkAndAsyncSocket) { 192 // The chunks that will be written in the request, as reflected in the 193 // MockWrites below. 194 static const char kChunk1[] = "Chunk 1"; 195 static const char kChunk2[] = "Chunky 2"; 196 static const char kChunk3[] = "Test 3"; 197 198 MockWrite writes[] = { 199 MockWrite(ASYNC, 0, 200 "GET /one.html HTTP/1.1\r\n" 201 "Host: localhost\r\n" 202 "Transfer-Encoding: chunked\r\n" 203 "Connection: keep-alive\r\n\r\n"), 204 MockWrite(ASYNC, 1, "7\r\nChunk 1\r\n"), 205 MockWrite(ASYNC, 2, "8\r\nChunky 2\r\n"), 206 MockWrite(ASYNC, 3, "6\r\nTest 3\r\n"), 207 MockWrite(ASYNC, 4, "0\r\n\r\n"), 208 }; 209 210 // The size of the response body, as reflected in the Content-Length of the 211 // MockRead below. 212 static const int kBodySize = 8; 213 214 MockRead reads[] = { 215 MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"), 216 MockRead(ASYNC, 6, "Content-Length: 8\r\n\r\n"), 217 MockRead(ASYNC, 7, "one.html"), 218 MockRead(SYNCHRONOUS, 0, 8), // EOF 219 }; 220 221 UploadDataStream upload_stream(UploadDataStream::CHUNKED, 0); 222 upload_stream.AppendChunk(kChunk1, arraysize(kChunk1) - 1, false); 223 ASSERT_EQ(OK, upload_stream.Init(CompletionCallback())); 224 225 DeterministicSocketData data(reads, arraysize(reads), 226 writes, arraysize(writes)); 227 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); 228 229 scoped_ptr<DeterministicMockTCPClientSocket> transport( 230 new DeterministicMockTCPClientSocket(NULL, &data)); 231 data.set_delegate(transport->AsWeakPtr()); 232 233 TestCompletionCallback callback; 234 int rv = transport->Connect(callback.callback()); 235 rv = callback.GetResult(rv); 236 ASSERT_EQ(OK, rv); 237 238 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); 239 socket_handle->SetSocket(transport.PassAs<StreamSocket>()); 240 241 HttpRequestInfo request_info; 242 request_info.method = "GET"; 243 request_info.url = GURL("http://localhost"); 244 request_info.load_flags = LOAD_NORMAL; 245 request_info.upload_data_stream = &upload_stream; 246 247 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); 248 HttpStreamParser parser( 249 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog()); 250 251 HttpRequestHeaders request_headers; 252 request_headers.SetHeader("Host", "localhost"); 253 request_headers.SetHeader("Transfer-Encoding", "chunked"); 254 request_headers.SetHeader("Connection", "keep-alive"); 255 256 HttpResponseInfo response_info; 257 // This will attempt to Write() the initial request and headers, which will 258 // complete asynchronously. 259 rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers, 260 &response_info, callback.callback()); 261 ASSERT_EQ(ERR_IO_PENDING, rv); 262 263 // Complete the initial request write. Additionally, this should enqueue the 264 // first chunk. 265 data.RunFor(1); 266 ASSERT_FALSE(callback.have_result()); 267 268 // Now append another chunk (while the first write is still pending), which 269 // should not confuse the state machine. 270 upload_stream.AppendChunk(kChunk2, arraysize(kChunk2) - 1, false); 271 ASSERT_FALSE(callback.have_result()); 272 273 // Complete writing the first chunk, which should then enqueue the second 274 // chunk for writing and return, because it is set to complete 275 // asynchronously. 276 data.RunFor(1); 277 ASSERT_FALSE(callback.have_result()); 278 279 // Complete writing the second chunk. However, because no chunks are 280 // available yet, no further writes should be called until a new chunk is 281 // added. 282 data.RunFor(1); 283 ASSERT_FALSE(callback.have_result()); 284 285 // Add the final chunk. This will enqueue another write, but it will not 286 // complete due to the async nature. 287 upload_stream.AppendChunk(kChunk3, arraysize(kChunk3) - 1, true); 288 ASSERT_FALSE(callback.have_result()); 289 290 // Finalize writing the last chunk, which will enqueue the trailer. 291 data.RunFor(1); 292 ASSERT_FALSE(callback.have_result()); 293 294 // Finalize writing the trailer. 295 data.RunFor(1); 296 ASSERT_TRUE(callback.have_result()); 297 298 // Warning: This will hang if the callback doesn't already have a result, 299 // due to the deterministic socket provider. Do not remove the above 300 // ASSERT_TRUE, which will avoid this hang. 301 rv = callback.WaitForResult(); 302 ASSERT_EQ(OK, rv); 303 304 // Attempt to read the response status and the response headers. 305 rv = parser.ReadResponseHeaders(callback.callback()); 306 ASSERT_EQ(ERR_IO_PENDING, rv); 307 data.RunFor(2); 308 309 ASSERT_TRUE(callback.have_result()); 310 rv = callback.WaitForResult(); 311 ASSERT_GT(rv, 0); 312 313 // Finally, attempt to read the response body. 314 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); 315 rv = parser.ReadResponseBody( 316 body_buffer.get(), kBodySize, callback.callback()); 317 ASSERT_EQ(ERR_IO_PENDING, rv); 318 data.RunFor(1); 319 320 ASSERT_TRUE(callback.have_result()); 321 rv = callback.WaitForResult(); 322 ASSERT_EQ(kBodySize, rv); 323} 324 325TEST(HttpStreamParser, TruncatedHeaders) { 326 MockRead truncated_status_reads[] = { 327 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"), 328 MockRead(SYNCHRONOUS, 0, 2), // EOF 329 }; 330 331 MockRead truncated_after_status_reads[] = { 332 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\n"), 333 MockRead(SYNCHRONOUS, 0, 2), // EOF 334 }; 335 336 MockRead truncated_in_header_reads[] = { 337 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHead"), 338 MockRead(SYNCHRONOUS, 0, 2), // EOF 339 }; 340 341 MockRead truncated_after_header_reads[] = { 342 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n"), 343 MockRead(SYNCHRONOUS, 0, 2), // EOF 344 }; 345 346 MockRead truncated_after_final_newline_reads[] = { 347 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r"), 348 MockRead(SYNCHRONOUS, 0, 2), // EOF 349 }; 350 351 MockRead not_truncated_reads[] = { 352 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\nHeader: foo\r\n\r\n"), 353 MockRead(SYNCHRONOUS, 0, 2), // EOF 354 }; 355 356 MockRead* reads[] = { 357 truncated_status_reads, 358 truncated_after_status_reads, 359 truncated_in_header_reads, 360 truncated_after_header_reads, 361 truncated_after_final_newline_reads, 362 not_truncated_reads, 363 }; 364 365 MockWrite writes[] = { 366 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"), 367 }; 368 369 enum { 370 HTTP = 0, 371 HTTPS, 372 NUM_PROTOCOLS, 373 }; 374 375 for (size_t protocol = 0; protocol < NUM_PROTOCOLS; protocol++) { 376 SCOPED_TRACE(protocol); 377 378 for (size_t i = 0; i < arraysize(reads); i++) { 379 SCOPED_TRACE(i); 380 DeterministicSocketData data(reads[i], 2, writes, arraysize(writes)); 381 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); 382 data.SetStop(3); 383 384 scoped_ptr<DeterministicMockTCPClientSocket> transport( 385 new DeterministicMockTCPClientSocket(NULL, &data)); 386 data.set_delegate(transport->AsWeakPtr()); 387 388 TestCompletionCallback callback; 389 int rv = transport->Connect(callback.callback()); 390 rv = callback.GetResult(rv); 391 ASSERT_EQ(OK, rv); 392 393 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); 394 socket_handle->SetSocket(transport.PassAs<StreamSocket>()); 395 396 HttpRequestInfo request_info; 397 request_info.method = "GET"; 398 if (protocol == HTTP) { 399 request_info.url = GURL("http://localhost"); 400 } else { 401 request_info.url = GURL("https://localhost"); 402 } 403 request_info.load_flags = LOAD_NORMAL; 404 405 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); 406 HttpStreamParser parser( 407 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog()); 408 409 HttpRequestHeaders request_headers; 410 HttpResponseInfo response_info; 411 rv = parser.SendRequest("GET / HTTP/1.1\r\n", request_headers, 412 &response_info, callback.callback()); 413 ASSERT_EQ(OK, rv); 414 415 rv = parser.ReadResponseHeaders(callback.callback()); 416 if (i == arraysize(reads) - 1) { 417 EXPECT_EQ(OK, rv); 418 EXPECT_TRUE(response_info.headers.get()); 419 } else { 420 if (protocol == HTTP) { 421 EXPECT_EQ(ERR_CONNECTION_CLOSED, rv); 422 EXPECT_TRUE(response_info.headers.get()); 423 } else { 424 EXPECT_EQ(ERR_RESPONSE_HEADERS_TRUNCATED, rv); 425 EXPECT_FALSE(response_info.headers.get()); 426 } 427 } 428 } 429 } 430} 431 432// Confirm that on 101 response, the headers are parsed but the data that 433// follows remains in the buffer. 434TEST(HttpStreamParser, Websocket101Response) { 435 MockRead reads[] = { 436 MockRead(SYNCHRONOUS, 1, 437 "HTTP/1.1 101 Switching Protocols\r\n" 438 "Upgrade: websocket\r\n" 439 "Connection: Upgrade\r\n" 440 "\r\n" 441 "a fake websocket frame"), 442 }; 443 444 MockWrite writes[] = { 445 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n\r\n"), 446 }; 447 448 DeterministicSocketData data(reads, arraysize(reads), 449 writes, arraysize(writes)); 450 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); 451 data.SetStop(2); 452 453 scoped_ptr<DeterministicMockTCPClientSocket> transport( 454 new DeterministicMockTCPClientSocket(NULL, &data)); 455 data.set_delegate(transport->AsWeakPtr()); 456 457 TestCompletionCallback callback; 458 int rv = transport->Connect(callback.callback()); 459 rv = callback.GetResult(rv); 460 ASSERT_EQ(OK, rv); 461 462 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); 463 socket_handle->SetSocket(transport.PassAs<StreamSocket>()); 464 465 HttpRequestInfo request_info; 466 request_info.method = "GET"; 467 request_info.url = GURL("http://localhost"); 468 request_info.load_flags = LOAD_NORMAL; 469 470 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); 471 HttpStreamParser parser( 472 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog()); 473 474 HttpRequestHeaders request_headers; 475 HttpResponseInfo response_info; 476 rv = parser.SendRequest("GET / HTTP/1.1\r\n", request_headers, 477 &response_info, callback.callback()); 478 ASSERT_EQ(OK, rv); 479 480 rv = parser.ReadResponseHeaders(callback.callback()); 481 EXPECT_EQ(OK, rv); 482 ASSERT_TRUE(response_info.headers.get()); 483 EXPECT_EQ(101, response_info.headers->response_code()); 484 EXPECT_TRUE(response_info.headers->HasHeaderValue("Connection", "Upgrade")); 485 EXPECT_TRUE(response_info.headers->HasHeaderValue("Upgrade", "websocket")); 486 EXPECT_EQ(read_buffer->capacity(), read_buffer->offset()); 487 EXPECT_EQ("a fake websocket frame", 488 base::StringPiece(read_buffer->StartOfBuffer(), 489 read_buffer->capacity())); 490} 491 492// Helper class for constructing HttpStreamParser and running GET requests. 493class SimpleGetRunner { 494 public: 495 SimpleGetRunner() : read_buffer_(new GrowableIOBuffer), sequence_number_(0) { 496 writes_.push_back(MockWrite( 497 SYNCHRONOUS, sequence_number_++, "GET / HTTP/1.1\r\n\r\n")); 498 } 499 500 HttpStreamParser* parser() { return parser_.get(); } 501 GrowableIOBuffer* read_buffer() { return read_buffer_.get(); } 502 HttpResponseInfo* response_info() { return &response_info_; } 503 504 void AddInitialData(const std::string& data) { 505 int offset = read_buffer_->offset(); 506 int size = data.size(); 507 read_buffer_->SetCapacity(offset + size); 508 memcpy(read_buffer_->StartOfBuffer() + offset, data.data(), size); 509 read_buffer_->set_offset(offset + size); 510 } 511 512 void AddRead(const std::string& data) { 513 reads_.push_back(MockRead(SYNCHRONOUS, sequence_number_++, data.data())); 514 } 515 516 void SetupParserAndSendRequest() { 517 reads_.push_back(MockRead(SYNCHRONOUS, 0, sequence_number_++)); // EOF 518 519 socket_handle_.reset(new ClientSocketHandle); 520 data_.reset(new DeterministicSocketData( 521 &reads_.front(), reads_.size(), &writes_.front(), writes_.size())); 522 data_->set_connect_data(MockConnect(SYNCHRONOUS, OK)); 523 data_->SetStop(reads_.size() + writes_.size()); 524 525 transport_.reset(new DeterministicMockTCPClientSocket(NULL, data_.get())); 526 data_->set_delegate(transport_->AsWeakPtr()); 527 528 TestCompletionCallback callback; 529 int rv = transport_->Connect(callback.callback()); 530 rv = callback.GetResult(rv); 531 ASSERT_EQ(OK, rv); 532 533 socket_handle_->SetSocket(transport_.PassAs<StreamSocket>()); 534 535 request_info_.method = "GET"; 536 request_info_.url = GURL("http://localhost"); 537 request_info_.load_flags = LOAD_NORMAL; 538 539 parser_.reset(new HttpStreamParser( 540 socket_handle_.get(), &request_info_, read_buffer(), BoundNetLog())); 541 542 rv = parser_->SendRequest("GET / HTTP/1.1\r\n", request_headers_, 543 &response_info_, callback.callback()); 544 ASSERT_EQ(OK, rv); 545 } 546 547 void ReadHeaders() { 548 TestCompletionCallback callback; 549 EXPECT_EQ(OK, parser_->ReadResponseHeaders(callback.callback())); 550 } 551 552 void ReadBody(int user_buf_len, int* read_lengths) { 553 TestCompletionCallback callback; 554 scoped_refptr<IOBuffer> buffer = new IOBuffer(user_buf_len); 555 int rv; 556 int i = 0; 557 while (true) { 558 rv = parser_->ReadResponseBody( 559 buffer.get(), user_buf_len, callback.callback()); 560 EXPECT_EQ(read_lengths[i], rv); 561 i++; 562 if (rv <= 0) 563 return; 564 } 565 } 566 567 private: 568 HttpRequestHeaders request_headers_; 569 HttpResponseInfo response_info_; 570 HttpRequestInfo request_info_; 571 scoped_refptr<GrowableIOBuffer> read_buffer_; 572 std::vector<MockRead> reads_; 573 std::vector<MockWrite> writes_; 574 scoped_ptr<ClientSocketHandle> socket_handle_; 575 scoped_ptr<DeterministicSocketData> data_; 576 scoped_ptr<DeterministicMockTCPClientSocket> transport_; 577 scoped_ptr<HttpStreamParser> parser_; 578 int sequence_number_; 579}; 580 581// Test that HTTP/0.9 response size is correctly calculated. 582TEST(HttpStreamParser, ReceivedBytesNoHeaders) { 583 std::string response = "hello\r\nworld\r\n"; 584 585 SimpleGetRunner get_runner; 586 get_runner.AddRead(response); 587 get_runner.SetupParserAndSendRequest(); 588 get_runner.ReadHeaders(); 589 EXPECT_EQ(0, get_runner.parser()->received_bytes()); 590 int response_size = response.size(); 591 int read_lengths[] = {response_size, 0}; 592 get_runner.ReadBody(response_size, read_lengths); 593 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); 594} 595 596// Test basic case where there is no keep-alive or extra data from the socket, 597// and the entire response is received in a single read. 598TEST(HttpStreamParser, ReceivedBytesNormal) { 599 std::string headers = "HTTP/1.1 200 OK\r\n" 600 "Content-Length: 7\r\n\r\n"; 601 std::string body = "content"; 602 std::string response = headers + body; 603 604 SimpleGetRunner get_runner; 605 get_runner.AddRead(response); 606 get_runner.SetupParserAndSendRequest(); 607 get_runner.ReadHeaders(); 608 int64 headers_size = headers.size(); 609 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); 610 int body_size = body.size(); 611 int read_lengths[] = {body_size, 0}; 612 get_runner.ReadBody(body_size, read_lengths); 613 int64 response_size = response.size(); 614 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); 615} 616 617// Test that bytes that represent "next" response are not counted 618// as current response "received_bytes". 619TEST(HttpStreamParser, ReceivedBytesExcludesNextResponse) { 620 std::string headers = "HTTP/1.1 200 OK\r\n" 621 "Content-Length: 8\r\n\r\n"; 622 std::string body = "content8"; 623 std::string response = headers + body; 624 std::string next_response = "HTTP/1.1 200 OK\r\n\r\nFOO"; 625 std::string data = response + next_response; 626 627 SimpleGetRunner get_runner; 628 get_runner.AddRead(data); 629 get_runner.SetupParserAndSendRequest(); 630 get_runner.ReadHeaders(); 631 EXPECT_EQ(39, get_runner.parser()->received_bytes()); 632 int64 headers_size = headers.size(); 633 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); 634 int body_size = body.size(); 635 int read_lengths[] = {body_size, 0}; 636 get_runner.ReadBody(body_size, read_lengths); 637 int64 response_size = response.size(); 638 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); 639 int64 next_response_size = next_response.size(); 640 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset()); 641} 642 643// Test that "received_bytes" calculation works fine when last read 644// contains more data than requested by user. 645// We send data in two reads: 646// 1) Headers + beginning of response 647// 2) remaining part of response + next response start 648// We setup user read buffer so it fully accepts the beginnig of response 649// body, but it is larger that remaining part of body. 650TEST(HttpStreamParser, ReceivedBytesMultiReadExcludesNextResponse) { 651 std::string headers = "HTTP/1.1 200 OK\r\n" 652 "Content-Length: 36\r\n\r\n"; 653 int64 user_buf_len = 32; 654 std::string body_start = std::string(user_buf_len, '#'); 655 int body_start_size = body_start.size(); 656 EXPECT_EQ(user_buf_len, body_start_size); 657 std::string response_start = headers + body_start; 658 std::string body_end = "abcd"; 659 std::string next_response = "HTTP/1.1 200 OK\r\n\r\nFOO"; 660 std::string response_end = body_end + next_response; 661 662 SimpleGetRunner get_runner; 663 get_runner.AddRead(response_start); 664 get_runner.AddRead(response_end); 665 get_runner.SetupParserAndSendRequest(); 666 get_runner.ReadHeaders(); 667 int64 headers_size = headers.size(); 668 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); 669 int body_end_size = body_end.size(); 670 int read_lengths[] = {body_start_size, body_end_size, 0}; 671 get_runner.ReadBody(body_start_size, read_lengths); 672 int64 response_size = response_start.size() + body_end_size; 673 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); 674 int64 next_response_size = next_response.size(); 675 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset()); 676} 677 678// Test that "received_bytes" calculation works fine when there is no 679// network activity at all; that is when all data is read from read buffer. 680// In this case read buffer contains two responses. We expect that only 681// bytes that correspond to the first one are taken into account. 682TEST(HttpStreamParser, ReceivedBytesFromReadBufExcludesNextResponse) { 683 std::string headers = "HTTP/1.1 200 OK\r\n" 684 "Content-Length: 7\r\n\r\n"; 685 std::string body = "content"; 686 std::string response = headers + body; 687 std::string next_response = "HTTP/1.1 200 OK\r\n\r\nFOO"; 688 std::string data = response + next_response; 689 690 SimpleGetRunner get_runner; 691 get_runner.AddInitialData(data); 692 get_runner.SetupParserAndSendRequest(); 693 get_runner.ReadHeaders(); 694 int64 headers_size = headers.size(); 695 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); 696 int body_size = body.size(); 697 int read_lengths[] = {body_size, 0}; 698 get_runner.ReadBody(body_size, read_lengths); 699 int64 response_size = response.size(); 700 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); 701 int64 next_response_size = next_response.size(); 702 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset()); 703} 704 705// Test calculating "received_bytes" when part of request has been already 706// loaded and placed to read buffer by previous stream parser. 707TEST(HttpStreamParser, ReceivedBytesUseReadBuf) { 708 std::string buffer = "HTTP/1.1 200 OK\r\n"; 709 std::string remaining_headers = "Content-Length: 7\r\n\r\n"; 710 int64 headers_size = buffer.size() + remaining_headers.size(); 711 std::string body = "content"; 712 std::string response = remaining_headers + body; 713 714 SimpleGetRunner get_runner; 715 get_runner.AddInitialData(buffer); 716 get_runner.AddRead(response); 717 get_runner.SetupParserAndSendRequest(); 718 get_runner.ReadHeaders(); 719 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); 720 int body_size = body.size(); 721 int read_lengths[] = {body_size, 0}; 722 get_runner.ReadBody(body_size, read_lengths); 723 EXPECT_EQ(headers_size + body_size, get_runner.parser()->received_bytes()); 724 EXPECT_EQ(0, get_runner.read_buffer()->offset()); 725} 726 727// Test the case when the resulting read_buf contains both unused bytes and 728// bytes ejected by chunked-encoding filter. 729TEST(HttpStreamParser, ReceivedBytesChunkedTransferExcludesNextResponse) { 730 std::string response = "HTTP/1.1 200 OK\r\n" 731 "Transfer-Encoding: chunked\r\n\r\n" 732 "7\r\nChunk 1\r\n" 733 "8\r\nChunky 2\r\n" 734 "6\r\nTest 3\r\n" 735 "0\r\n\r\n"; 736 std::string next_response = "foo bar\r\n"; 737 std::string data = response + next_response; 738 739 SimpleGetRunner get_runner; 740 get_runner.AddInitialData(data); 741 get_runner.SetupParserAndSendRequest(); 742 get_runner.ReadHeaders(); 743 int read_lengths[] = {4, 3, 6, 2, 6, 0}; 744 get_runner.ReadBody(7, read_lengths); 745 int64 response_size = response.size(); 746 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); 747 int64 next_response_size = next_response.size(); 748 EXPECT_EQ(next_response_size, get_runner.read_buffer()->offset()); 749} 750 751// Test that data transfered in multiple reads is correctly processed. 752// We feed data into 4-bytes reads. Also we set length of read 753// buffer to 5-bytes to test all possible buffer misaligments. 754TEST(HttpStreamParser, ReceivedBytesMultipleReads) { 755 std::string headers = "HTTP/1.1 200 OK\r\n" 756 "Content-Length: 33\r\n\r\n"; 757 std::string body = "foo bar baz\r\n" 758 "sputnik mir babushka"; 759 std::string response = headers + body; 760 761 size_t receive_length = 4; 762 std::vector<std::string> blocks; 763 for (size_t i = 0; i < response.size(); i += receive_length) { 764 size_t length = std::min(receive_length, response.size() - i); 765 blocks.push_back(response.substr(i, length)); 766 } 767 768 SimpleGetRunner get_runner; 769 for (std::vector<std::string>::size_type i = 0; i < blocks.size(); ++i) 770 get_runner.AddRead(blocks[i]); 771 get_runner.SetupParserAndSendRequest(); 772 get_runner.ReadHeaders(); 773 int64 headers_size = headers.size(); 774 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); 775 int read_lengths[] = {1, 4, 4, 4, 4, 4, 4, 4, 4, 0}; 776 get_runner.ReadBody(receive_length + 1, read_lengths); 777 int64 response_size = response.size(); 778 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); 779} 780 781// Test that "continue" HTTP header is counted as "received_bytes". 782TEST(HttpStreamParser, ReceivedBytesIncludesContinueHeader) { 783 std::string status100 = "HTTP/1.1 100 OK\r\n\r\n"; 784 std::string headers = "HTTP/1.1 200 OK\r\n" 785 "Content-Length: 7\r\n\r\n"; 786 int64 headers_size = status100.size() + headers.size(); 787 std::string body = "content"; 788 std::string response = headers + body; 789 790 SimpleGetRunner get_runner; 791 get_runner.AddRead(status100); 792 get_runner.AddRead(response); 793 get_runner.SetupParserAndSendRequest(); 794 get_runner.ReadHeaders(); 795 EXPECT_EQ(100, get_runner.response_info()->headers->response_code()); 796 int64 status100_size = status100.size(); 797 EXPECT_EQ(status100_size, get_runner.parser()->received_bytes()); 798 get_runner.ReadHeaders(); 799 EXPECT_EQ(200, get_runner.response_info()->headers->response_code()); 800 EXPECT_EQ(headers_size, get_runner.parser()->received_bytes()); 801 int64 response_size = headers_size + body.size(); 802 int body_size = body.size(); 803 int read_lengths[] = {body_size, 0}; 804 get_runner.ReadBody(body_size, read_lengths); 805 EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); 806} 807 808// Test that an HttpStreamParser can be read from after it's received headers 809// and data structures owned by its owner have been deleted. This happens 810// when a ResponseBodyDrainer is used. 811TEST(HttpStreamParser, ReadAfterUnownedObjectsDestroyed) { 812 MockWrite writes[] = { 813 MockWrite(SYNCHRONOUS, 0, 814 "GET /foo.html HTTP/1.1\r\n\r\n"), 815 MockWrite(SYNCHRONOUS, 1, "1"), 816 }; 817 818 const int kBodySize = 1; 819 MockRead reads[] = { 820 MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), 821 MockRead(SYNCHRONOUS, 6, "Content-Length: 1\r\n\r\n"), 822 MockRead(SYNCHRONOUS, 6, "Connection: Keep-Alive\r\n\r\n"), 823 MockRead(SYNCHRONOUS, 7, "1"), 824 MockRead(SYNCHRONOUS, 0, 8), // EOF 825 }; 826 827 StaticSocketDataProvider data(reads, arraysize(reads), writes, 828 arraysize(writes)); 829 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); 830 831 scoped_ptr<MockTCPClientSocket> transport( 832 new MockTCPClientSocket(AddressList(), NULL, &data)); 833 834 TestCompletionCallback callback; 835 ASSERT_EQ(OK, transport->Connect(callback.callback())); 836 837 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); 838 socket_handle->SetSocket(transport.PassAs<StreamSocket>()); 839 840 scoped_ptr<HttpRequestInfo> request_info(new HttpRequestInfo()); 841 request_info->method = "GET"; 842 request_info->url = GURL("http://somewhere/foo.html"); 843 844 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); 845 HttpStreamParser parser(socket_handle.get(), request_info.get(), 846 read_buffer.get(), BoundNetLog()); 847 848 scoped_ptr<HttpRequestHeaders> request_headers(new HttpRequestHeaders()); 849 scoped_ptr<HttpResponseInfo> response_info(new HttpResponseInfo()); 850 ASSERT_EQ(OK, parser.SendRequest("GET /foo.html HTTP/1.1\r\n", 851 *request_headers, response_info.get(), callback.callback())); 852 ASSERT_EQ(OK, parser.ReadResponseHeaders(callback.callback())); 853 854 // If the object that owns the HttpStreamParser is deleted, it takes the 855 // objects passed to the HttpStreamParser with it. 856 request_info.reset(); 857 request_headers.reset(); 858 response_info.reset(); 859 860 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); 861 ASSERT_EQ(kBodySize, parser.ReadResponseBody( 862 body_buffer.get(), kBodySize, callback.callback())); 863} 864 865} // namespace 866 867} // namespace net 868