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

Side by Side Diff: net/http/http_stream_parser_unittest.cc

Issue 921453003: Allow URLRequest::AppendChunkToUpload to take 0-byte final chunks. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | net/url_request/url_request.cc » ('j') | net/url_request/url_request.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 304 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 rv = parser.ReadResponseBody( 315 rv = parser.ReadResponseBody(
316 body_buffer.get(), kBodySize, callback.callback()); 316 body_buffer.get(), kBodySize, callback.callback());
317 ASSERT_EQ(ERR_IO_PENDING, rv); 317 ASSERT_EQ(ERR_IO_PENDING, rv);
318 data.RunFor(1); 318 data.RunFor(1);
319 319
320 ASSERT_TRUE(callback.have_result()); 320 ASSERT_TRUE(callback.have_result());
321 rv = callback.WaitForResult(); 321 rv = callback.WaitForResult();
322 ASSERT_EQ(kBodySize, rv); 322 ASSERT_EQ(kBodySize, rv);
323 } 323 }
324 324
325 // Test to ensure the HttpStreamParser state machine does not get confused
326 // when the final "chunk" has 0 bytes, and is received from the UploadStream
327 // only after sending other chunk.
328 TEST(HttpStreamParser, AsyncChunkWithEmptyFinalChunk) {
329 const char kChunk[] = "Chunk";
330
331 MockWrite writes[] = {
332 MockWrite(ASYNC, 0,
333 "GET /one.html HTTP/1.1\r\n"
334 "Host: localhost\r\n"
335 "Transfer-Encoding: chunked\r\n"
336 "Connection: keep-alive\r\n\r\n"),
337 MockWrite(ASYNC, 1, "5\r\nChunk\r\n"),
338 MockWrite(ASYNC, 2, "0\r\n\r\n"),
339 };
340
341 // The size of the response body, as reflected in the Content-Length of the
342 // MockRead below.
343 const int kBodySize = 8;
344
345 MockRead reads[] = {
346 MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n"),
347 MockRead(ASYNC, 4, "Content-Length: 8\r\n\r\n"),
348 MockRead(ASYNC, 5, "one.html"),
349 MockRead(SYNCHRONOUS, 0, 6), // EOF
350 };
351
352 ChunkedUploadDataStream upload_stream(0);
353 upload_stream.AppendData(kChunk, arraysize(kChunk) - 1, false);
354 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
355
356 DeterministicSocketData data(reads, arraysize(reads), writes,
357 arraysize(writes));
358 data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
359
360 scoped_ptr<DeterministicMockTCPClientSocket> transport(
361 new DeterministicMockTCPClientSocket(NULL, &data));
362 data.set_delegate(transport->AsWeakPtr());
363
364 TestCompletionCallback callback;
365 int rv = transport->Connect(callback.callback());
366 rv = callback.GetResult(rv);
367 ASSERT_EQ(OK, rv);
368
369 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
370 socket_handle->SetSocket(transport.Pass());
371
372 HttpRequestInfo request_info;
373 request_info.method = "GET";
374 request_info.url = GURL("http://localhost");
375 request_info.load_flags = LOAD_NORMAL;
376 request_info.upload_data_stream = &upload_stream;
mef 2015/02/11 21:32:10 is it possible to set upload_data_stream to stream
mmenke 2015/02/11 22:37:22 Yes...SyncEmptyChunkedUpload is the only test in t
377
378 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
379 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
380 BoundNetLog());
381
382 HttpRequestHeaders request_headers;
383 request_headers.SetHeader("Host", "localhost");
384 request_headers.SetHeader("Transfer-Encoding", "chunked");
385 request_headers.SetHeader("Connection", "keep-alive");
386
387 HttpResponseInfo response_info;
388 // This will attempt to Write() the initial request and headers, which will
389 // complete asynchronously.
390 rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
391 &response_info, callback.callback());
392 ASSERT_EQ(ERR_IO_PENDING, rv);
393
394 // Complete the initial request write and the chunk with data.
395 data.RunFor(2);
396 ASSERT_FALSE(callback.have_result());
397
398 // Now append the terminal 0-byte "chunk".
399 upload_stream.AppendData(nullptr, 0, true);
400 ASSERT_FALSE(callback.have_result());
401
402 // Finalize writing the trailer.
403 data.RunFor(1);
404 ASSERT_TRUE(callback.have_result());
405
406 // Warning: This will hang if the callback doesn't already have a result,
407 // due to the deterministic socket provider. Do not remove the above
408 // ASSERT_TRUE, which will avoid this hang.
409 rv = callback.WaitForResult();
410 ASSERT_EQ(OK, rv);
411
412 // Attempt to read the response status and the response headers.
413 rv = parser.ReadResponseHeaders(callback.callback());
414 ASSERT_EQ(ERR_IO_PENDING, rv);
415 data.RunFor(2);
416
417 ASSERT_TRUE(callback.have_result());
418 rv = callback.WaitForResult();
419 ASSERT_GT(rv, 0);
420
421 // Finally, attempt to read the response body.
422 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
423 rv = parser.ReadResponseBody(body_buffer.get(), kBodySize,
424 callback.callback());
425 ASSERT_EQ(ERR_IO_PENDING, rv);
426 data.RunFor(1);
427
428 ASSERT_TRUE(callback.have_result());
429 rv = callback.WaitForResult();
430 ASSERT_EQ(kBodySize, rv);
431 }
432
433 // Test to ensure the HttpStreamParser state machine does not get confused
434 // when there's only one "chunk" with 0 bytes, and is received from the
435 // UploadStream only after sending the request headers successfully.
436 TEST(HttpStreamParser, AsyncEmptyChunkedUpload) {
437 MockWrite writes[] = {
438 MockWrite(ASYNC, 0,
439 "GET /one.html HTTP/1.1\r\n"
440 "Host: localhost\r\n"
441 "Transfer-Encoding: chunked\r\n"
442 "Connection: keep-alive\r\n\r\n"),
443 MockWrite(ASYNC, 1, "0\r\n\r\n"),
444 };
445
446 // The size of the response body, as reflected in the Content-Length of the
447 // MockRead below.
448 const int kBodySize = 8;
449
450 MockRead reads[] = {
451 MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"),
452 MockRead(ASYNC, 3, "Content-Length: 8\r\n\r\n"),
453 MockRead(ASYNC, 4, "one.html"),
454 MockRead(SYNCHRONOUS, 0, 5), // EOF
455 };
456
457 ChunkedUploadDataStream upload_stream(0);
458 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
459
460 DeterministicSocketData data(reads, arraysize(reads), writes,
461 arraysize(writes));
462 data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
463
464 scoped_ptr<DeterministicMockTCPClientSocket> transport(
465 new DeterministicMockTCPClientSocket(NULL, &data));
466 data.set_delegate(transport->AsWeakPtr());
467
468 TestCompletionCallback callback;
469 int rv = transport->Connect(callback.callback());
470 rv = callback.GetResult(rv);
471 ASSERT_EQ(OK, rv);
472
473 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
474 socket_handle->SetSocket(transport.Pass());
475
476 HttpRequestInfo request_info;
477 request_info.method = "GET";
478 request_info.url = GURL("http://localhost");
479 request_info.load_flags = LOAD_NORMAL;
480 request_info.upload_data_stream = &upload_stream;
481
482 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
483 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
484 BoundNetLog());
485
486 HttpRequestHeaders request_headers;
487 request_headers.SetHeader("Host", "localhost");
488 request_headers.SetHeader("Transfer-Encoding", "chunked");
489 request_headers.SetHeader("Connection", "keep-alive");
490
491 HttpResponseInfo response_info;
492 // This will attempt to Write() the initial request and headers, which will
493 // complete asynchronously.
494 rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
495 &response_info, callback.callback());
496 ASSERT_EQ(ERR_IO_PENDING, rv);
497
498 // Complete writing the request headers.
499 data.RunFor(1);
500 ASSERT_FALSE(callback.have_result());
501
502 // Now append the terminal 0-byte "chunk".
503 upload_stream.AppendData(nullptr, 0, true);
504 ASSERT_FALSE(callback.have_result());
505
506 // Finalize writing the trailer.
507 data.RunFor(1);
508 ASSERT_TRUE(callback.have_result());
509
510 // Warning: This will hang if the callback doesn't already have a result,
511 // due to the deterministic socket provider. Do not remove the above
512 // ASSERT_TRUE, which will avoid this hang.
513 rv = callback.WaitForResult();
514 ASSERT_EQ(OK, rv);
515
516 // Attempt to read the response status and the response headers.
517 rv = parser.ReadResponseHeaders(callback.callback());
518 ASSERT_EQ(ERR_IO_PENDING, rv);
519 data.RunFor(2);
520
521 ASSERT_TRUE(callback.have_result());
522 rv = callback.WaitForResult();
523 ASSERT_GT(rv, 0);
524
525 // Finally, attempt to read the response body.
526 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
527 rv = parser.ReadResponseBody(body_buffer.get(), kBodySize,
528 callback.callback());
529 ASSERT_EQ(ERR_IO_PENDING, rv);
530 data.RunFor(1);
531
532 ASSERT_TRUE(callback.have_result());
533 rv = callback.WaitForResult();
534 ASSERT_EQ(kBodySize, rv);
535 }
536
537 // Test to ensure the HttpStreamParser state machine does not get confused
538 // when there's only one "chunk" with 0 bytes, which was already appended before
539 // the request was started.
540 TEST(HttpStreamParser, SyncEmptyChunkedUpload) {
541 MockWrite writes[] = {
542 MockWrite(ASYNC, 0,
543 "GET /one.html HTTP/1.1\r\n"
544 "Host: localhost\r\n"
545 "Transfer-Encoding: chunked\r\n"
546 "Connection: keep-alive\r\n\r\n"),
547 MockWrite(ASYNC, 1, "0\r\n\r\n"),
548 };
549
550 // The size of the response body, as reflected in the Content-Length of the
551 // MockRead below.
552 const int kBodySize = 8;
553
554 MockRead reads[] = {
555 MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"),
556 MockRead(ASYNC, 3, "Content-Length: 8\r\n\r\n"),
557 MockRead(ASYNC, 4, "one.html"),
558 MockRead(SYNCHRONOUS, 0, 5), // EOF
559 };
560
561 ChunkedUploadDataStream upload_stream(0);
562 ASSERT_EQ(OK, upload_stream.Init(TestCompletionCallback().callback()));
563 // Append final empty chunk.
564 upload_stream.AppendData(nullptr, 0, true);
565
566 DeterministicSocketData data(reads, arraysize(reads), writes,
567 arraysize(writes));
568 data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
569
570 scoped_ptr<DeterministicMockTCPClientSocket> transport(
571 new DeterministicMockTCPClientSocket(NULL, &data));
572 data.set_delegate(transport->AsWeakPtr());
573
574 TestCompletionCallback callback;
575 int rv = transport->Connect(callback.callback());
576 rv = callback.GetResult(rv);
577 ASSERT_EQ(OK, rv);
578
579 scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle);
580 socket_handle->SetSocket(transport.Pass());
581
582 HttpRequestInfo request_info;
583 request_info.method = "GET";
584 request_info.url = GURL("http://localhost");
585 request_info.load_flags = LOAD_NORMAL;
586 request_info.upload_data_stream = &upload_stream;
587
588 scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer);
589 HttpStreamParser parser(socket_handle.get(), &request_info, read_buffer.get(),
590 BoundNetLog());
591
592 HttpRequestHeaders request_headers;
593 request_headers.SetHeader("Host", "localhost");
594 request_headers.SetHeader("Transfer-Encoding", "chunked");
595 request_headers.SetHeader("Connection", "keep-alive");
596
597 HttpResponseInfo response_info;
598 // This will attempt to Write() the initial request and headers, which will
599 // complete asynchronously.
600 rv = parser.SendRequest("GET /one.html HTTP/1.1\r\n", request_headers,
601 &response_info, callback.callback());
602 ASSERT_EQ(ERR_IO_PENDING, rv);
603
604 // Complete writing the request headers and body.
605 data.RunFor(2);
606 ASSERT_TRUE(callback.have_result());
607
608 // Warning: This will hang if the callback doesn't already have a result,
609 // due to the deterministic socket provider. Do not remove the above
610 // ASSERT_TRUE, which will avoid this hang.
611 rv = callback.WaitForResult();
612 ASSERT_EQ(OK, rv);
613
614 // Attempt to read the response status and the response headers.
615 rv = parser.ReadResponseHeaders(callback.callback());
616 ASSERT_EQ(ERR_IO_PENDING, rv);
617 data.RunFor(2);
618
619 ASSERT_TRUE(callback.have_result());
620 rv = callback.WaitForResult();
621 ASSERT_GT(rv, 0);
622
623 // Finally, attempt to read the response body.
624 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
625 rv = parser.ReadResponseBody(body_buffer.get(), kBodySize,
626 callback.callback());
627 ASSERT_EQ(ERR_IO_PENDING, rv);
628 data.RunFor(1);
629
630 ASSERT_TRUE(callback.have_result());
631 rv = callback.WaitForResult();
632 ASSERT_EQ(kBodySize, rv);
633 }
634
325 TEST(HttpStreamParser, TruncatedHeaders) { 635 TEST(HttpStreamParser, TruncatedHeaders) {
326 MockRead truncated_status_reads[] = { 636 MockRead truncated_status_reads[] = {
327 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"), 637 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 20"),
328 MockRead(SYNCHRONOUS, 0, 2), // EOF 638 MockRead(SYNCHRONOUS, 0, 2), // EOF
329 }; 639 };
330 640
331 MockRead truncated_after_status_reads[] = { 641 MockRead truncated_after_status_reads[] = {
332 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\n"), 642 MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 Ok\r\n"),
333 MockRead(SYNCHRONOUS, 0, 2), // EOF 643 MockRead(SYNCHRONOUS, 0, 2), // EOF
334 }; 644 };
(...skipping 523 matching lines...) Expand 10 before | Expand all | Expand 10 after
858 response_info.reset(); 1168 response_info.reset();
859 1169
860 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); 1170 scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize));
861 ASSERT_EQ(kBodySize, parser.ReadResponseBody( 1171 ASSERT_EQ(kBodySize, parser.ReadResponseBody(
862 body_buffer.get(), kBodySize, callback.callback())); 1172 body_buffer.get(), kBodySize, callback.callback()));
863 } 1173 }
864 1174
865 } // namespace 1175 } // namespace
866 1176
867 } // namespace net 1177 } // namespace net
OLDNEW
« no previous file with comments | « no previous file | net/url_request/url_request.cc » ('j') | net/url_request/url_request.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698