Commit 605851c7 authored by paivanof@gmail.com's avatar paivanof@gmail.com

Treat 0 returned from HttpStream::ReadResponseBody correctly.

According to comment to HttpStream::ReadResponseBody() 0 means end of response
while HttpResponseBodyDrainer treated it as error.

BUG=154712
TEST=net_unittests


Review URL: https://chromiumcodereview.appspot.com/11112021

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162415 0039d316-1c4b-4281-b951-d872f2087c98
parent 2db8b425
...@@ -98,9 +98,6 @@ int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) { ...@@ -98,9 +98,6 @@ int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) {
if (result < 0) if (result < 0)
return result; return result;
if (result == 0)
return ERR_CONNECTION_CLOSED;
total_read_ += result; total_read_ += result;
if (stream_->IsResponseBodyComplete()) if (stream_->IsResponseBodyComplete())
return OK; return OK;
......
...@@ -38,7 +38,7 @@ class CloseResultWaiter { ...@@ -38,7 +38,7 @@ class CloseResultWaiter {
waiting_for_result_(false) {} waiting_for_result_(false) {}
int WaitForResult() { int WaitForResult() {
DCHECK(!waiting_for_result_); CHECK(!waiting_for_result_);
while (!have_result_) { while (!have_result_) {
waiting_for_result_ = true; waiting_for_result_ = true;
MessageLoop::current()->Run(); MessageLoop::current()->Run();
...@@ -66,9 +66,12 @@ class MockHttpStream : public HttpStream { ...@@ -66,9 +66,12 @@ class MockHttpStream : public HttpStream {
public: public:
MockHttpStream(CloseResultWaiter* result_waiter) MockHttpStream(CloseResultWaiter* result_waiter)
: result_waiter_(result_waiter), : result_waiter_(result_waiter),
buf_len_(0),
closed_(false), closed_(false),
stall_reads_forever_(false), stall_reads_forever_(false),
num_chunks_(0), num_chunks_(0),
is_sync_(false),
is_last_chunk_zero_size_(false),
is_complete_(false), is_complete_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {} ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {}
virtual ~MockHttpStream() {} virtual ~MockHttpStream() {}
...@@ -108,7 +111,7 @@ class MockHttpStream : public HttpStream { ...@@ -108,7 +111,7 @@ class MockHttpStream : public HttpStream {
virtual int ReadResponseBody(IOBuffer* buf, int buf_len, virtual int ReadResponseBody(IOBuffer* buf, int buf_len,
const CompletionCallback& callback) OVERRIDE; const CompletionCallback& callback) OVERRIDE;
virtual void Close(bool not_reusable) OVERRIDE { virtual void Close(bool not_reusable) OVERRIDE {
DCHECK(!closed_); CHECK(!closed_);
closed_ = true; closed_ = true;
result_waiter_->set_result(not_reusable); result_waiter_->set_result(not_reusable);
} }
...@@ -126,11 +129,16 @@ class MockHttpStream : public HttpStream { ...@@ -126,11 +129,16 @@ class MockHttpStream : public HttpStream {
virtual void Drain(HttpNetworkSession*) OVERRIDE {} virtual void Drain(HttpNetworkSession*) OVERRIDE {}
// Methods to tweak/observer mock behavior: // Methods to tweak/observer mock behavior:
void StallReadsForever() { stall_reads_forever_ = true; } void set_stall_reads_forever() { stall_reads_forever_ = true; }
void set_num_chunks(int num_chunks) { num_chunks_ = num_chunks; } void set_num_chunks(int num_chunks) { num_chunks_ = num_chunks; }
void set_sync() { is_sync_ = true; }
void set_is_last_chunk_zero_size() { is_last_chunk_zero_size_ = true; }
private: private:
int ReadResponseBodyImpl(IOBuffer* buf, int buf_len);
void CompleteRead(); void CompleteRead();
bool closed() const { return closed_; } bool closed() const { return closed_; }
...@@ -138,34 +146,50 @@ class MockHttpStream : public HttpStream { ...@@ -138,34 +146,50 @@ class MockHttpStream : public HttpStream {
CloseResultWaiter* const result_waiter_; CloseResultWaiter* const result_waiter_;
scoped_refptr<IOBuffer> user_buf_; scoped_refptr<IOBuffer> user_buf_;
CompletionCallback callback_; CompletionCallback callback_;
int buf_len_;
bool closed_; bool closed_;
bool stall_reads_forever_; bool stall_reads_forever_;
int num_chunks_; int num_chunks_;
bool is_sync_;
bool is_last_chunk_zero_size_;
bool is_complete_; bool is_complete_;
base::WeakPtrFactory<MockHttpStream> weak_factory_; base::WeakPtrFactory<MockHttpStream> weak_factory_;
}; };
int MockHttpStream::ReadResponseBody( int MockHttpStream::ReadResponseBody(IOBuffer* buf,
IOBuffer* buf, int buf_len, const CompletionCallback& callback) { int buf_len,
DCHECK(!callback.is_null()); const CompletionCallback& callback) {
DCHECK(callback_.is_null()); CHECK(!callback.is_null());
DCHECK(buf); CHECK(callback_.is_null());
CHECK(buf);
if (stall_reads_forever_) if (stall_reads_forever_)
return ERR_IO_PENDING; return ERR_IO_PENDING;
if (num_chunks_ == 0) if (is_complete_)
return ERR_UNEXPECTED; return ERR_UNEXPECTED;
if (buf_len > kMagicChunkSize && num_chunks_ > 1) { if (!is_sync_) {
user_buf_ = buf; user_buf_ = buf;
buf_len_ = buf_len;
callback_ = callback; callback_ = callback;
MessageLoop::current()->PostTask( MessageLoop::current()->PostTask(
FROM_HERE, FROM_HERE,
base::Bind(&MockHttpStream::CompleteRead, weak_factory_.GetWeakPtr())); base::Bind(&MockHttpStream::CompleteRead, weak_factory_.GetWeakPtr()));
return ERR_IO_PENDING; return ERR_IO_PENDING;
} else {
return ReadResponseBodyImpl(buf, buf_len);
} }
}
int MockHttpStream::ReadResponseBodyImpl(IOBuffer* buf, int buf_len) {
if (is_last_chunk_zero_size_ && num_chunks_ == 1) {
buf_len = 0;
} else {
if (buf_len > kMagicChunkSize)
buf_len = kMagicChunkSize;
std::memset(buf->data(), 1, buf_len);
}
num_chunks_--; num_chunks_--;
if (!num_chunks_) if (!num_chunks_)
is_complete_ = true; is_complete_ = true;
...@@ -174,14 +198,11 @@ int MockHttpStream::ReadResponseBody( ...@@ -174,14 +198,11 @@ int MockHttpStream::ReadResponseBody(
} }
void MockHttpStream::CompleteRead() { void MockHttpStream::CompleteRead() {
CompletionCallback callback = callback_; int result = ReadResponseBodyImpl(user_buf_, buf_len_);
std::memset(user_buf_->data(), 1, kMagicChunkSize);
user_buf_ = NULL; user_buf_ = NULL;
CompletionCallback callback = callback_;
callback_.Reset(); callback_.Reset();
num_chunks_--; callback.Run(result);
if (!num_chunks_)
is_complete_ = true;
callback.Run(kMagicChunkSize);
} }
class HttpResponseBodyDrainerTest : public testing::Test { class HttpResponseBodyDrainerTest : public testing::Test {
...@@ -213,8 +234,16 @@ class HttpResponseBodyDrainerTest : public testing::Test { ...@@ -213,8 +234,16 @@ class HttpResponseBodyDrainerTest : public testing::Test {
HttpResponseBodyDrainer* const drainer_; // Deletes itself. HttpResponseBodyDrainer* const drainer_; // Deletes itself.
}; };
TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncOK) { TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncSingleOK) {
mock_stream_->set_num_chunks(1); mock_stream_->set_num_chunks(1);
mock_stream_->set_sync();
drainer_->Start(session_);
EXPECT_FALSE(result_waiter_.WaitForResult());
}
TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncOK) {
mock_stream_->set_num_chunks(3);
mock_stream_->set_sync();
drainer_->Start(session_); drainer_->Start(session_);
EXPECT_FALSE(result_waiter_.WaitForResult()); EXPECT_FALSE(result_waiter_.WaitForResult());
} }
...@@ -225,6 +254,24 @@ TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) { ...@@ -225,6 +254,24 @@ TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) {
EXPECT_FALSE(result_waiter_.WaitForResult()); EXPECT_FALSE(result_waiter_.WaitForResult());
} }
// Test the case when the final chunk is 0 bytes. This can happen when
// the final 0-byte chunk of a chunk-encoded http response is read in a last
// call to ReadResponseBody, after all data were returned from HttpStream.
TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncEmptyChunk) {
mock_stream_->set_num_chunks(4);
mock_stream_->set_is_last_chunk_zero_size();
drainer_->Start(session_);
EXPECT_FALSE(result_waiter_.WaitForResult());
}
TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncEmptyChunk) {
mock_stream_->set_num_chunks(4);
mock_stream_->set_sync();
mock_stream_->set_is_last_chunk_zero_size();
drainer_->Start(session_);
EXPECT_FALSE(result_waiter_.WaitForResult());
}
TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) { TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) {
mock_stream_->set_num_chunks( mock_stream_->set_num_chunks(
HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize); HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize);
...@@ -234,14 +281,14 @@ TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) { ...@@ -234,14 +281,14 @@ TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) {
TEST_F(HttpResponseBodyDrainerTest, DrainBodyTimeOut) { TEST_F(HttpResponseBodyDrainerTest, DrainBodyTimeOut) {
mock_stream_->set_num_chunks(2); mock_stream_->set_num_chunks(2);
mock_stream_->StallReadsForever(); mock_stream_->set_stall_reads_forever();
drainer_->Start(session_); drainer_->Start(session_);
EXPECT_TRUE(result_waiter_.WaitForResult()); EXPECT_TRUE(result_waiter_.WaitForResult());
} }
TEST_F(HttpResponseBodyDrainerTest, CancelledBySession) { TEST_F(HttpResponseBodyDrainerTest, CancelledBySession) {
mock_stream_->set_num_chunks(2); mock_stream_->set_num_chunks(2);
mock_stream_->StallReadsForever(); mock_stream_->set_stall_reads_forever();
drainer_->Start(session_); drainer_->Start(session_);
// HttpNetworkSession should delete |drainer_|. // HttpNetworkSession should delete |drainer_|.
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment