OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/http/http_stream_parser.h" | 5 #include "net/http/http_stream_parser.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
(...skipping 22 matching lines...) Expand all Loading... |
33 | 33 |
34 namespace net { | 34 namespace net { |
35 | 35 |
36 namespace { | 36 namespace { |
37 | 37 |
38 const size_t kOutputSize = 1024; // Just large enough for this test. | 38 const size_t kOutputSize = 1024; // Just large enough for this test. |
39 // The number of bytes that can fit in a buffer of kOutputSize. | 39 // The number of bytes that can fit in a buffer of kOutputSize. |
40 const size_t kMaxPayloadSize = | 40 const size_t kMaxPayloadSize = |
41 kOutputSize - HttpStreamParser::kChunkHeaderFooterSize; | 41 kOutputSize - HttpStreamParser::kChunkHeaderFooterSize; |
42 | 42 |
| 43 // Helper method to create a connected ClientSocketHandle using |data|. |
| 44 // Modifies |data|. |
| 45 scoped_ptr<ClientSocketHandle> CreateConnectedSocketHandle( |
| 46 DeterministicSocketData* data) { |
| 47 data->set_connect_data(MockConnect(SYNCHRONOUS, OK)); |
| 48 |
| 49 scoped_ptr<DeterministicMockTCPClientSocket> transport( |
| 50 new DeterministicMockTCPClientSocket(nullptr, data)); |
| 51 data->set_delegate(transport->AsWeakPtr()); |
| 52 |
| 53 TestCompletionCallback callback; |
| 54 EXPECT_EQ(OK, transport->Connect(callback.callback())); |
| 55 |
| 56 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); |
| 57 socket_handle->SetSocket(transport.Pass()); |
| 58 return socket_handle.Pass(); |
| 59 } |
| 60 |
43 // The empty payload is how the last chunk is encoded. | 61 // The empty payload is how the last chunk is encoded. |
44 TEST(HttpStreamParser, EncodeChunk_EmptyPayload) { | 62 TEST(HttpStreamParser, EncodeChunk_EmptyPayload) { |
45 char output[kOutputSize]; | 63 char output[kOutputSize]; |
46 | 64 |
47 const base::StringPiece kPayload = ""; | 65 const base::StringPiece kPayload = ""; |
48 const base::StringPiece kExpected = "0\r\n\r\n"; | 66 const base::StringPiece kExpected = "0\r\n\r\n"; |
49 const int num_bytes_written = | 67 const int num_bytes_written = |
50 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); | 68 HttpStreamParser::EncodeChunk(kPayload, output, sizeof(output)); |
51 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written)); | 69 ASSERT_EQ(kExpected.size(), static_cast<size_t>(num_bytes_written)); |
52 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written)); | 70 EXPECT_EQ(kExpected, base::StringPiece(output, num_bytes_written)); |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 | 195 |
178 scoped_ptr<UploadDataStream> body( | 196 scoped_ptr<UploadDataStream> body( |
179 new ElementsUploadDataStream(element_readers.Pass(), 0)); | 197 new ElementsUploadDataStream(element_readers.Pass(), 0)); |
180 ASSERT_EQ(OK, body->Init(CompletionCallback())); | 198 ASSERT_EQ(OK, body->Init(CompletionCallback())); |
181 // Shouldn't be merged if the in-memory body is large here. | 199 // Shouldn't be merged if the in-memory body is large here. |
182 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( | 200 ASSERT_FALSE(HttpStreamParser::ShouldMergeRequestHeadersAndBody( |
183 "some header", body.get())); | 201 "some header", body.get())); |
184 } | 202 } |
185 | 203 |
186 // Test to ensure the HttpStreamParser state machine does not get confused | 204 // Test to ensure the HttpStreamParser state machine does not get confused |
| 205 // when sending a request with a chunked body with only one chunk that becomes |
| 206 // available asynchronously. |
| 207 TEST(HttpStreamParser, AsyncSingleChunkAndAsyncSocket) { |
| 208 static const char kChunk[] = "Chunk"; |
| 209 |
| 210 MockWrite writes[] = { |
| 211 MockWrite(ASYNC, 0, |
| 212 "GET /one.html HTTP/1.1\r\n" |
| 213 "Transfer-Encoding: chunked\r\n\r\n"), |
| 214 MockWrite(ASYNC, 1, "5\r\nChunk\r\n"), |
| 215 MockWrite(ASYNC, 2, "0\r\n\r\n"), |
| 216 }; |
| 217 |
| 218 // The size of the response body, as reflected in the Content-Length of the |
| 219 // MockRead below. |
| 220 static const int kBodySize = 8; |
| 221 |
| 222 MockRead reads[] = { |
| 223 MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n"), |
| 224 MockRead(ASYNC, 4, "Content-Length: 8\r\n\r\n"), |
| 225 MockRead(ASYNC, 5, "one.html"), |
| 226 MockRead(SYNCHRONOUS, 0, 6), // EOF |
| 227 }; |
| 228 |
| 229 ChunkedUploadDataStream upload_stream(0); |
| 230 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); |
| 231 |
| 232 DeterministicSocketData data(reads, arraysize(reads), writes, |
| 233 arraysize(writes)); |
| 234 scoped_ptr<ClientSocketHandle> socket_handle = |
| 235 CreateConnectedSocketHandle(&data); |
| 236 |
| 237 HttpRequestInfo request_info; |
| 238 request_info.method = "GET"; |
| 239 request_info.url = GURL("http://localhost"); |
| 240 request_info.upload_data_stream = &upload_stream; |
| 241 |
| 242 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); |
| 243 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(), |
| 244 BoundNetLog()); |
| 245 |
| 246 HttpRequestHeaders request_headers; |
| 247 request_headers.SetHeader("Transfer-Encoding", "chunked"); |
| 248 |
| 249 HttpResponseInfo response_info; |
| 250 TestCompletionCallback callback; |
| 251 // This will attempt to Write() the initial request and headers, which will |
| 252 // complete asynchronously. |
| 253 ASSERT_EQ(ERR_IO_PENDING, |
| 254 parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers, |
| 255 &response_info, callback.callback())); |
| 256 |
| 257 // Complete the initial request write. |
| 258 data.RunFor(1); |
| 259 ASSERT_FALSE(callback.have_result()); |
| 260 |
| 261 // Now append the only chunk. |
| 262 upload_stream.AppendData(kChunk, arraysize(kChunk) - 1, true); |
| 263 // Write the chunk. |
| 264 data.RunFor(1); |
| 265 ASSERT_FALSE(callback.have_result()); |
| 266 |
| 267 // Write the trailer. |
| 268 data.RunFor(1); |
| 269 ASSERT_TRUE(callback.have_result()); |
| 270 ASSERT_EQ(OK, callback.WaitForResult()); |
| 271 |
| 272 // Attempt to read the response status and the response headers. |
| 273 ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback())); |
| 274 data.RunFor(2); |
| 275 ASSERT_TRUE(callback.have_result()); |
| 276 ASSERT_GT(callback.WaitForResult(), 0); |
| 277 |
| 278 // Finally, attempt to read the response body. |
| 279 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); |
| 280 ASSERT_EQ(ERR_IO_PENDING, |
| 281 parser.ReadResponseBody(body_buffer.get(), kBodySize, |
| 282 callback.callback())); |
| 283 data.RunFor(1); |
| 284 ASSERT_TRUE(callback.have_result()); |
| 285 ASSERT_EQ(kBodySize, callback.WaitForResult()); |
| 286 } |
| 287 |
| 288 // Test to ensure the HttpStreamParser state machine does not get confused |
| 289 // when sending a request with a chunked body with only one chunk that is |
| 290 // available synchronously. |
| 291 TEST(HttpStreamParser, SyncSingleChunkAndAsyncSocket) { |
| 292 static const char kChunk[] = "Chunk"; |
| 293 |
| 294 MockWrite writes[] = { |
| 295 MockWrite(ASYNC, 0, |
| 296 "GET /one.html HTTP/1.1\r\n" |
| 297 "Transfer-Encoding: chunked\r\n\r\n"), |
| 298 MockWrite(ASYNC, 1, "5\r\nChunk\r\n"), |
| 299 MockWrite(ASYNC, 2, "0\r\n\r\n"), |
| 300 }; |
| 301 |
| 302 // The size of the response body, as reflected in the Content-Length of the |
| 303 // MockRead below. |
| 304 static const int kBodySize = 8; |
| 305 |
| 306 MockRead reads[] = { |
| 307 MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n"), |
| 308 MockRead(ASYNC, 4, "Content-Length: 8\r\n\r\n"), |
| 309 MockRead(ASYNC, 5, "one.html"), |
| 310 MockRead(SYNCHRONOUS, 0, 6), // EOF |
| 311 }; |
| 312 |
| 313 ChunkedUploadDataStream upload_stream(0); |
| 314 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); |
| 315 // Append the only chunk. |
| 316 upload_stream.AppendData(kChunk, arraysize(kChunk) - 1, true); |
| 317 |
| 318 DeterministicSocketData data(reads, arraysize(reads), writes, |
| 319 arraysize(writes)); |
| 320 scoped_ptr<ClientSocketHandle> socket_handle = |
| 321 CreateConnectedSocketHandle(&data); |
| 322 |
| 323 HttpRequestInfo request_info; |
| 324 request_info.method = "GET"; |
| 325 request_info.url = GURL("http://localhost"); |
| 326 request_info.upload_data_stream = &upload_stream; |
| 327 |
| 328 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); |
| 329 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(), |
| 330 BoundNetLog()); |
| 331 |
| 332 HttpRequestHeaders request_headers; |
| 333 request_headers.SetHeader("Transfer-Encoding", "chunked"); |
| 334 |
| 335 HttpResponseInfo response_info; |
| 336 TestCompletionCallback callback; |
| 337 // This will attempt to Write() the initial request and headers, which will |
| 338 // complete asynchronously. |
| 339 ASSERT_EQ(ERR_IO_PENDING, |
| 340 parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers, |
| 341 &response_info, callback.callback())); |
| 342 |
| 343 // Write the request and the only chunk. |
| 344 data.RunFor(2); |
| 345 |
| 346 // Write the trailer. |
| 347 data.RunFor(1); |
| 348 ASSERT_TRUE(callback.have_result()); |
| 349 ASSERT_EQ(OK, callback.WaitForResult()); |
| 350 |
| 351 // Attempt to read the response status and the response headers. |
| 352 ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback())); |
| 353 data.RunFor(2); |
| 354 ASSERT_TRUE(callback.have_result()); |
| 355 ASSERT_GT(callback.WaitForResult(), 0); |
| 356 |
| 357 // Finally, attempt to read the response body. |
| 358 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); |
| 359 ASSERT_EQ(ERR_IO_PENDING, |
| 360 parser.ReadResponseBody(body_buffer.get(), kBodySize, |
| 361 callback.callback())); |
| 362 data.RunFor(1); |
| 363 ASSERT_TRUE(callback.have_result()); |
| 364 ASSERT_EQ(kBodySize, callback.WaitForResult()); |
| 365 } |
| 366 |
| 367 // Test to ensure the HttpStreamParser state machine does not get confused |
187 // when sending a request with a chunked body, where chunks become available | 368 // when sending a request with a chunked body, where chunks become available |
188 // asynchronously, over a socket where writes may also complete | 369 // asynchronously, over a socket where writes may also complete |
189 // asynchronously. | 370 // asynchronously. |
190 // This is a regression test for http://crbug.com/132243 | 371 // This is a regression test for http://crbug.com/132243 |
191 TEST(HttpStreamParser, AsyncChunkAndAsyncSocket) { | 372 TEST(HttpStreamParser, AsyncChunkAndAsyncSocketWithMultipleChunks) { |
192 // The chunks that will be written in the request, as reflected in the | 373 // The chunks that will be written in the request, as reflected in the |
193 // MockWrites below. | 374 // MockWrites below. |
194 static const char kChunk1[] = "Chunk 1"; | 375 static const char kChunk1[] = "Chunk 1"; |
195 static const char kChunk2[] = "Chunky 2"; | 376 static const char kChunk2[] = "Chunky 2"; |
196 static const char kChunk3[] = "Test 3"; | 377 static const char kChunk3[] = "Test 3"; |
197 | 378 |
198 MockWrite writes[] = { | 379 MockWrite writes[] = { |
199 MockWrite(ASYNC, 0, | 380 MockWrite(ASYNC, 0, |
200 "GET /one.html HTTP/1.1\r\n" | 381 "GET /one.html HTTP/1.1\r\n" |
201 "Host: localhost\r\n" | 382 "Transfer-Encoding: chunked\r\n\r\n"), |
202 "Transfer-Encoding: chunked\r\n" | 383 MockWrite(ASYNC, 1, "7\r\nChunk 1\r\n"), |
203 "Connection: keep-alive\r\n\r\n"), | 384 MockWrite(ASYNC, 2, "8\r\nChunky 2\r\n"), |
204 MockWrite(ASYNC, 1, "7\r\nChunk 1\r\n"), | 385 MockWrite(ASYNC, 3, "6\r\nTest 3\r\n"), |
205 MockWrite(ASYNC, 2, "8\r\nChunky 2\r\n"), | 386 MockWrite(ASYNC, 4, "0\r\n\r\n"), |
206 MockWrite(ASYNC, 3, "6\r\nTest 3\r\n"), | |
207 MockWrite(ASYNC, 4, "0\r\n\r\n"), | |
208 }; | 387 }; |
209 | 388 |
210 // The size of the response body, as reflected in the Content-Length of the | 389 // The size of the response body, as reflected in the Content-Length of the |
211 // MockRead below. | 390 // MockRead below. |
212 static const int kBodySize = 8; | 391 static const int kBodySize = 8; |
213 | 392 |
214 MockRead reads[] = { | 393 MockRead reads[] = { |
215 MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"), | 394 MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"), |
216 MockRead(ASYNC, 6, "Content-Length: 8\r\n\r\n"), | 395 MockRead(ASYNC, 6, "Content-Length: 8\r\n\r\n"), |
217 MockRead(ASYNC, 7, "one.html"), | 396 MockRead(ASYNC, 7, "one.html"), |
218 MockRead(SYNCHRONOUS, 0, 8), // EOF | 397 MockRead(SYNCHRONOUS, 0, 8), // EOF |
219 }; | 398 }; |
220 | 399 |
221 ChunkedUploadDataStream upload_stream(0); | 400 ChunkedUploadDataStream upload_stream(0); |
222 upload_stream.AppendData(kChunk1, arraysize(kChunk1) - 1, false); | 401 upload_stream.AppendData(kChunk1, arraysize(kChunk1) - 1, false); |
223 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); | 402 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); |
224 | 403 |
225 DeterministicSocketData data(reads, arraysize(reads), | 404 DeterministicSocketData data(reads, arraysize(reads), writes, |
226 writes, arraysize(writes)); | 405 arraysize(writes)); |
227 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); | 406 scoped_ptr<ClientSocketHandle> socket_handle = |
228 | 407 CreateConnectedSocketHandle(&data); |
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.Pass()); | |
240 | 408 |
241 HttpRequestInfo request_info; | 409 HttpRequestInfo request_info; |
242 request_info.method = "GET"; | 410 request_info.method = "GET"; |
243 request_info.url = GURL("http://localhost"); | 411 request_info.url = GURL("http://localhost"); |
244 request_info.load_flags = LOAD_NORMAL; | |
245 request_info.upload_data_stream = &upload_stream; | 412 request_info.upload_data_stream = &upload_stream; |
246 | 413 |
247 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); | 414 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); |
248 HttpStreamParser parser( | 415 HttpStreamParser parser( |
249 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog()); | 416 socket_handle.get(), &request_info, read_buffer.get(), BoundNetLog()); |
250 | 417 |
251 HttpRequestHeaders request_headers; | 418 HttpRequestHeaders request_headers; |
252 request_headers.SetHeader("Host", "localhost"); | |
253 request_headers.SetHeader("Transfer-Encoding", "chunked"); | 419 request_headers.SetHeader("Transfer-Encoding", "chunked"); |
254 request_headers.SetHeader("Connection", "keep-alive"); | |
255 | 420 |
256 HttpResponseInfo response_info; | 421 HttpResponseInfo response_info; |
| 422 TestCompletionCallback callback; |
257 // This will attempt to Write() the initial request and headers, which will | 423 // This will attempt to Write() the initial request and headers, which will |
258 // complete asynchronously. | 424 // complete asynchronously. |
259 rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers, | 425 ASSERT_EQ(ERR_IO_PENDING, |
260 &response_info, callback.callback()); | 426 parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers, |
261 ASSERT_EQ(ERR_IO_PENDING, rv); | 427 &response_info, callback.callback())); |
262 | 428 |
263 // Complete the initial request write. Additionally, this should enqueue the | 429 // Complete the initial request write. Additionally, this should enqueue the |
264 // first chunk. | 430 // first chunk. |
265 data.RunFor(1); | 431 data.RunFor(1); |
266 ASSERT_FALSE(callback.have_result()); | 432 ASSERT_FALSE(callback.have_result()); |
267 | 433 |
268 // Now append another chunk (while the first write is still pending), which | 434 // Now append another chunk (while the first write is still pending), which |
269 // should not confuse the state machine. | 435 // should not confuse the state machine. |
270 upload_stream.AppendData(kChunk2, arraysize(kChunk2) - 1, false); | 436 upload_stream.AppendData(kChunk2, arraysize(kChunk2) - 1, false); |
271 ASSERT_FALSE(callback.have_result()); | 437 ASSERT_FALSE(callback.have_result()); |
(...skipping 15 matching lines...) Expand all Loading... |
287 upload_stream.AppendData(kChunk3, arraysize(kChunk3) - 1, true); | 453 upload_stream.AppendData(kChunk3, arraysize(kChunk3) - 1, true); |
288 ASSERT_FALSE(callback.have_result()); | 454 ASSERT_FALSE(callback.have_result()); |
289 | 455 |
290 // Finalize writing the last chunk, which will enqueue the trailer. | 456 // Finalize writing the last chunk, which will enqueue the trailer. |
291 data.RunFor(1); | 457 data.RunFor(1); |
292 ASSERT_FALSE(callback.have_result()); | 458 ASSERT_FALSE(callback.have_result()); |
293 | 459 |
294 // Finalize writing the trailer. | 460 // Finalize writing the trailer. |
295 data.RunFor(1); | 461 data.RunFor(1); |
296 ASSERT_TRUE(callback.have_result()); | 462 ASSERT_TRUE(callback.have_result()); |
297 | 463 ASSERT_EQ(OK, callback.WaitForResult()); |
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 | 464 |
304 // Attempt to read the response status and the response headers. | 465 // Attempt to read the response status and the response headers. |
305 rv = parser.ReadResponseHeaders(callback.callback()); | 466 ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback())); |
306 ASSERT_EQ(ERR_IO_PENDING, rv); | |
307 data.RunFor(2); | 467 data.RunFor(2); |
308 | |
309 ASSERT_TRUE(callback.have_result()); | 468 ASSERT_TRUE(callback.have_result()); |
310 rv = callback.WaitForResult(); | 469 ASSERT_GT(callback.WaitForResult(), 0); |
311 ASSERT_GT(rv, 0); | |
312 | 470 |
313 // Finally, attempt to read the response body. | 471 // Finally, attempt to read the response body. |
314 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); | 472 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); |
315 rv = parser.ReadResponseBody( | 473 ASSERT_EQ(ERR_IO_PENDING, |
316 body_buffer.get(), kBodySize, callback.callback()); | 474 parser.ReadResponseBody(body_buffer.get(), kBodySize, |
317 ASSERT_EQ(ERR_IO_PENDING, rv); | 475 callback.callback())); |
318 data.RunFor(1); | 476 data.RunFor(1); |
| 477 ASSERT_TRUE(callback.have_result()); |
| 478 ASSERT_EQ(kBodySize, callback.WaitForResult()); |
| 479 } |
319 | 480 |
| 481 // Test to ensure the HttpStreamParser state machine does not get confused |
| 482 // when there's only one "chunk" with 0 bytes, and is received from the |
| 483 // UploadStream only after sending the request headers successfully. |
| 484 TEST(HttpStreamParser, AsyncEmptyChunkedUpload) { |
| 485 MockWrite writes[] = { |
| 486 MockWrite(ASYNC, 0, |
| 487 "GET /one.html HTTP/1.1\r\n" |
| 488 "Transfer-Encoding: chunked\r\n\r\n"), |
| 489 MockWrite(ASYNC, 1, "0\r\n\r\n"), |
| 490 }; |
| 491 |
| 492 // The size of the response body, as reflected in the Content-Length of the |
| 493 // MockRead below. |
| 494 const int kBodySize = 8; |
| 495 |
| 496 MockRead reads[] = { |
| 497 MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"), |
| 498 MockRead(ASYNC, 3, "Content-Length: 8\r\n\r\n"), |
| 499 MockRead(ASYNC, 4, "one.html"), |
| 500 MockRead(SYNCHRONOUS, 0, 5), // EOF |
| 501 }; |
| 502 |
| 503 ChunkedUploadDataStream upload_stream(0); |
| 504 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); |
| 505 |
| 506 DeterministicSocketData data(reads, arraysize(reads), writes, |
| 507 arraysize(writes)); |
| 508 scoped_ptr<ClientSocketHandle> socket_handle = |
| 509 CreateConnectedSocketHandle(&data); |
| 510 |
| 511 HttpRequestInfo request_info; |
| 512 request_info.method = "GET"; |
| 513 request_info.url = GURL("http://localhost"); |
| 514 request_info.upload_data_stream = &upload_stream; |
| 515 |
| 516 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); |
| 517 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(), |
| 518 BoundNetLog()); |
| 519 |
| 520 HttpRequestHeaders request_headers; |
| 521 request_headers.SetHeader("Transfer-Encoding", "chunked"); |
| 522 |
| 523 HttpResponseInfo response_info; |
| 524 TestCompletionCallback callback; |
| 525 // This will attempt to Write() the initial request and headers, which will |
| 526 // complete asynchronously. |
| 527 ASSERT_EQ(ERR_IO_PENDING, |
| 528 parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers, |
| 529 &response_info, callback.callback())); |
| 530 |
| 531 // Complete writing the request headers. |
| 532 data.RunFor(1); |
| 533 ASSERT_FALSE(callback.have_result()); |
| 534 |
| 535 // Now append the terminal 0-byte "chunk". |
| 536 upload_stream.AppendData(nullptr, 0, true); |
| 537 ASSERT_FALSE(callback.have_result()); |
| 538 |
| 539 // Finalize writing the trailer. |
| 540 data.RunFor(1); |
320 ASSERT_TRUE(callback.have_result()); | 541 ASSERT_TRUE(callback.have_result()); |
321 rv = callback.WaitForResult(); | 542 ASSERT_EQ(OK, callback.WaitForResult()); |
322 ASSERT_EQ(kBodySize, rv); | 543 |
| 544 // Attempt to read the response status and the response headers. |
| 545 ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback())); |
| 546 data.RunFor(2); |
| 547 ASSERT_TRUE(callback.have_result()); |
| 548 ASSERT_GT(callback.WaitForResult(), 0); |
| 549 |
| 550 // Finally, attempt to read the response body. |
| 551 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); |
| 552 ASSERT_EQ(ERR_IO_PENDING, |
| 553 parser.ReadResponseBody(body_buffer.get(), kBodySize, |
| 554 callback.callback())); |
| 555 data.RunFor(1); |
| 556 ASSERT_TRUE(callback.have_result()); |
| 557 ASSERT_EQ(kBodySize, callback.WaitForResult()); |
| 558 } |
| 559 |
| 560 // Test to ensure the HttpStreamParser state machine does not get confused |
| 561 // when there's only one "chunk" with 0 bytes, which was already appended before |
| 562 // the request was started. |
| 563 TEST(HttpStreamParser, SyncEmptyChunkedUpload) { |
| 564 MockWrite writes[] = { |
| 565 MockWrite(ASYNC, 0, |
| 566 "GET /one.html HTTP/1.1\r\n" |
| 567 "Transfer-Encoding: chunked\r\n\r\n"), |
| 568 MockWrite(ASYNC, 1, "0\r\n\r\n"), |
| 569 }; |
| 570 |
| 571 // The size of the response body, as reflected in the Content-Length of the |
| 572 // MockRead below. |
| 573 const int kBodySize = 8; |
| 574 |
| 575 MockRead reads[] = { |
| 576 MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"), |
| 577 MockRead(ASYNC, 3, "Content-Length: 8\r\n\r\n"), |
| 578 MockRead(ASYNC, 4, "one.html"), |
| 579 MockRead(SYNCHRONOUS, 0, 5), // EOF |
| 580 }; |
| 581 |
| 582 ChunkedUploadDataStream upload_stream(0); |
| 583 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback())); |
| 584 // Append final empty chunk. |
| 585 upload_stream.AppendData(nullptr, 0, true); |
| 586 |
| 587 DeterministicSocketData data(reads, arraysize(reads), writes, |
| 588 arraysize(writes)); |
| 589 scoped_ptr<ClientSocketHandle> socket_handle = |
| 590 CreateConnectedSocketHandle(&data); |
| 591 |
| 592 HttpRequestInfo request_info; |
| 593 request_info.method = "GET"; |
| 594 request_info.url = GURL("http://localhost"); |
| 595 request_info.upload_data_stream = &upload_stream; |
| 596 |
| 597 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); |
| 598 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(), |
| 599 BoundNetLog()); |
| 600 |
| 601 HttpRequestHeaders request_headers; |
| 602 request_headers.SetHeader("Transfer-Encoding", "chunked"); |
| 603 |
| 604 HttpResponseInfo response_info; |
| 605 TestCompletionCallback callback; |
| 606 // This will attempt to Write() the initial request and headers, which will |
| 607 // complete asynchronously. |
| 608 ASSERT_EQ(ERR_IO_PENDING, |
| 609 parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers, |
| 610 &response_info, callback.callback())); |
| 611 |
| 612 // Complete writing the request headers and body. |
| 613 data.RunFor(2); |
| 614 ASSERT_TRUE(callback.have_result()); |
| 615 ASSERT_EQ(OK, callback.WaitForResult()); |
| 616 |
| 617 // Attempt to read the response status and the response headers. |
| 618 ASSERT_EQ(ERR_IO_PENDING, parser.ReadResponseHeaders(callback.callback())); |
| 619 data.RunFor(2); |
| 620 ASSERT_TRUE(callback.have_result()); |
| 621 ASSERT_GT(callback.WaitForResult(), 0); |
| 622 |
| 623 // Finally, attempt to read the response body. |
| 624 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); |
| 625 ASSERT_EQ(ERR_IO_PENDING, |
| 626 parser.ReadResponseBody(body_buffer.get(), kBodySize, |
| 627 callback.callback())); |
| 628 data.RunFor(1); |
| 629 ASSERT_TRUE(callback.have_result()); |
| 630 ASSERT_EQ(kBodySize, callback.WaitForResult()); |
323 } | 631 } |
324 | 632 |
325 TEST(HttpStreamParser, TruncatedHeaders) { | 633 TEST(HttpStreamParser, TruncatedHeaders) { |
326 MockRead truncated_status_reads[] = { | 634 MockRead truncated_status_reads[] = { |
327 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"), | 635 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"), |
328 MockRead(SYNCHRONOUS, 0, 2), // EOF | 636 MockRead(SYNCHRONOUS, 0, 2), // EOF |
329 }; | 637 }; |
330 | 638 |
331 MockRead truncated_after_status_reads[] = { | 639 MockRead truncated_after_status_reads[] = { |
332 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\n"), | 640 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\n"), |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
380 DeterministicSocketData data(reads[i], 2, writes, arraysize(writes)); | 688 DeterministicSocketData data(reads[i], 2, writes, arraysize(writes)); |
381 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); | 689 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); |
382 data.SetStop(3); | 690 data.SetStop(3); |
383 | 691 |
384 scoped_ptr<DeterministicMockTCPClientSocket> transport( | 692 scoped_ptr<DeterministicMockTCPClientSocket> transport( |
385 new DeterministicMockTCPClientSocket(NULL, &data)); | 693 new DeterministicMockTCPClientSocket(NULL, &data)); |
386 data.set_delegate(transport->AsWeakPtr()); | 694 data.set_delegate(transport->AsWeakPtr()); |
387 | 695 |
388 TestCompletionCallback callback; | 696 TestCompletionCallback callback; |
389 int rv = transport->Connect(callback.callback()); | 697 int rv = transport->Connect(callback.callback()); |
390 rv = callback.GetResult(rv); | |
391 ASSERT_EQ(OK, rv); | 698 ASSERT_EQ(OK, rv); |
392 | 699 |
393 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); | 700 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); |
394 socket_handle->SetSocket(transport.Pass()); | 701 socket_handle->SetSocket(transport.Pass()); |
395 | 702 |
396 HttpRequestInfo request_info; | 703 HttpRequestInfo request_info; |
397 request_info.method = "GET"; | 704 request_info.method = "GET"; |
398 if (protocol == HTTP) { | 705 if (protocol == HTTP) { |
399 request_info.url = GURL("http://localhost"); | 706 request_info.url = GURL("http://localhost"); |
400 } else { | 707 } else { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
449 writes, arraysize(writes)); | 756 writes, arraysize(writes)); |
450 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); | 757 data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); |
451 data.SetStop(2); | 758 data.SetStop(2); |
452 | 759 |
453 scoped_ptr<DeterministicMockTCPClientSocket> transport( | 760 scoped_ptr<DeterministicMockTCPClientSocket> transport( |
454 new DeterministicMockTCPClientSocket(NULL, &data)); | 761 new DeterministicMockTCPClientSocket(NULL, &data)); |
455 data.set_delegate(transport->AsWeakPtr()); | 762 data.set_delegate(transport->AsWeakPtr()); |
456 | 763 |
457 TestCompletionCallback callback; | 764 TestCompletionCallback callback; |
458 int rv = transport->Connect(callback.callback()); | 765 int rv = transport->Connect(callback.callback()); |
459 rv = callback.GetResult(rv); | |
460 ASSERT_EQ(OK, rv); | 766 ASSERT_EQ(OK, rv); |
461 | 767 |
462 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); | 768 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); |
463 socket_handle->SetSocket(transport.Pass()); | 769 socket_handle->SetSocket(transport.Pass()); |
464 | 770 |
465 HttpRequestInfo request_info; | 771 HttpRequestInfo request_info; |
466 request_info.method = "GET"; | 772 request_info.method = "GET"; |
467 request_info.url = GURL("http://localhost"); | 773 request_info.url = GURL("http://localhost"); |
468 request_info.load_flags = LOAD_NORMAL; | 774 request_info.load_flags = LOAD_NORMAL; |
469 | 775 |
(...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
858 response_info.reset(); | 1164 response_info.reset(); |
859 | 1165 |
860 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); | 1166 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); |
861 ASSERT_EQ(kBodySize, parser.ReadResponseBody( | 1167 ASSERT_EQ(kBodySize, parser.ReadResponseBody( |
862 body_buffer.get(), kBodySize, callback.callback())); | 1168 body_buffer.get(), kBodySize, callback.callback())); |
863 } | 1169 } |
864 | 1170 |
865 } // namespace | 1171 } // namespace |
866 | 1172 |
867 } // namespace net | 1173 } // namespace net |
OLD | NEW |