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

Unified Diff: net/quic/quic_http_stream_test.cc

Issue 11364068: Add a QuicHttpStream class. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Move GetPeerAddress from QuicReliableClientStream to ReliableQuicStream Created 8 years, 1 month 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 side-by-side diff with in-line comments
Download patch
Index: net/quic/quic_http_stream_test.cc
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c9e6c95ac14747964f057a49ffafa674a652199b
--- /dev/null
+++ b/net/quic/quic_http_stream_test.cc
@@ -0,0 +1,356 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_http_stream.h"
+
+#include <vector>
+
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/base/upload_data.h"
+#include "net/base/upload_data_stream.h"
+#include "net/http/http_response_headers.h"
+#include "net/quic/quic_client_session.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/test_task_runner.h"
+#include "net/socket/socket_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace net {
+
+class QuicConnectionPeer {
+ public:
+ static void SetScheduler(QuicConnection* connection,
+ QuicSendScheduler* scheduler) {
+ connection->scheduler_.reset(scheduler);
+ }
+};
+
+namespace test {
+
+namespace {
+
+const char kUploadData[] = "hello world!";
+
+class TestSession : public QuicClientSession {
willchan no longer on Chromium 2012/11/23 03:17:43 Can you name these TestQuicSession and TestQuicCon
Ryan Hamilton 2012/11/23 04:19:53 Done. (Though it turned out the Session wasn't ev
+ public:
+ virtual QuicCryptoClientStream* GetCryptoStream() {
+ return QuicClientSession::GetCryptoStream();
+ }
+};
+
+class TestConnection : public QuicConnection {
+ public:
+ TestConnection(QuicGuid guid,
+ IPEndPoint address,
+ QuicConnectionHelper* helper)
+ : QuicConnection(guid, address, helper) {
+ }
+
+ void SetScheduler(QuicSendScheduler* scheduler) {
+ QuicConnectionPeer::SetScheduler(this, scheduler);
+ }
+};
+
+} // namespace
+
+class QuicHttpStreamTest : public ::testing::Test {
+ protected:
+ const static bool kFin = true;
+ const static bool kNoFin = false;
+ // Holds a packet to be written to the wire, and the IO mode that should
+ // be used by the mock socket when performing the write.
+ struct PacketToWrite {
+ PacketToWrite(IoMode mode, QuicEncryptedPacket* packet)
+ : mode(mode),
+ packet(packet) {
+ }
+ IoMode mode;
+ QuicEncryptedPacket* packet;
+ };
+
+ QuicHttpStreamTest()
+ : net_log_(BoundNetLog()),
+ read_buffer_(new IOBufferWithSize(4096)),
+ guid_(2),
willchan no longer on Chromium 2012/11/23 03:17:43 What's up with this guy, is he a constant? He seem
Ryan Hamilton 2012/11/23 04:19:53 Yeah, he's const. I added a const annotation to t
+ framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)),
+ creator_(guid_, &framer_) {
+ IPAddressNumber ip;
+ CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip));
+ peer_addr_ = IPEndPoint(ip, 443);
+ self_addr_ = IPEndPoint(ip, 8435);
+ Initialize();
+ }
+
+ ~QuicHttpStreamTest() {
+ for (size_t i = 0; i < writes_.size(); i++) {
+ delete writes_[i].packet;
+ }
+ }
+
+ // Adds a packet to the list of expected writes.
+ void AddWrite(IoMode mode, QuicEncryptedPacket* packet) {
+ writes_.push_back(PacketToWrite(mode, packet));
+ }
+
+ // Returns the packet to be written at position |pos|.
+ QuicEncryptedPacket* GetWrite(size_t pos) {
+ return writes_[pos].packet;
+ }
+
+ bool AtEof() {
+ return socket_data_->at_read_eof() && socket_data_->at_write_eof();
+ }
+
+ void ProcessPacket(const QuicEncryptedPacket& packet) {
+ connection_->ProcessUdpPacket(self_addr_, peer_addr_, packet);
+ }
+
+ // Configures the test fixture to use the list of expected writes.
+ void Initialize() {
+ mock_writes_.reset(new MockWrite[writes_.size()]);
+ for (size_t i = 0; i < writes_.size(); i++) {
+ mock_writes_[i] = MockWrite(writes_[i].mode,
+ writes_[i].packet->data(),
+ writes_[i].packet->length());
+ };
+
+ socket_data_.reset(new StaticSocketDataProvider(NULL, 0, mock_writes_.get(),
+ writes_.size()));
+
+ socket_.reset(new MockUDPClientSocket(socket_data_.get(),
+ net_log_.net_log()));
+ socket_->Connect(peer_addr_);
+ runner_ = new TestTaskRunner(&clock_);
+ helper_ = new QuicConnectionHelper(runner_.get(), &clock_, socket_.get());
willchan no longer on Chromium 2012/11/23 03:17:43 Why is helper_ a member variable? I don't see it g
Ryan Hamilton 2012/11/23 04:19:53 Inlined it into the connection_ initialization.
+ scheduler_ = new MockScheduler();
+ EXPECT_CALL(*scheduler_, TimeUntilSend(_)).
+ WillRepeatedly(testing::Return(QuicTime::Delta()));
+ connection_ = new TestConnection(guid_, peer_addr_, helper_);
+ connection_->set_visitor(&visitor_);
+ connection_->SetScheduler(scheduler_);
+ session_.reset(new QuicClientSession(connection_));
+ CryptoHandshakeMessage message;
+ message.tag = kSHLO;
+ session_->GetCryptoStream()->OnHandshakeMessage(message);
+ EXPECT_TRUE(session_->IsCryptoHandshakeComplete());
+ stream_.reset(new QuicHttpStream(session_->CreateOutgoingReliableStream()));
+ }
+
+ // Returns a newly created packet to send kData on stream 1.
+ QuicEncryptedPacket* ConstructDataPacket(
+ QuicPacketSequenceNumber sequence_number,
+ bool fin,
+ QuicStreamOffset offset,
+ base::StringPiece data) {
+ InitializeHeader(sequence_number);
+ QuicStreamFrame frame(3, fin, offset, data);
+ return ConstructPacket(header_, QuicFrame(&frame));
+ }
+
+ // Returns a newly created packet to send ack data.
+ QuicEncryptedPacket* ConstructAckPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked) {
+ InitializeHeader(sequence_number);
+
+ QuicAckFrame ack(largest_received, QuicTime(), least_unacked);
+ ack.congestion_info.type = kFixRate;
+ ack.congestion_info.fix_rate.bitrate_in_bytes_per_second = 100000;
+
+ return ConstructPacket(header_, QuicFrame(&ack));
+ }
+
+ // Returns a newly created packet to send a connection close frame.
+ QuicEncryptedPacket* ConstructClosePacket(
+ QuicPacketSequenceNumber sequence_number,
+ bool with_ack) {
+ InitializeHeader(sequence_number);
+
+ QuicFrames frames;
+ QuicAckFrame ack(0, QuicTime(), 0);
+ if (with_ack) {
+ ack.congestion_info.type = kFixRate;
+ ack.congestion_info.fix_rate.bitrate_in_bytes_per_second = 100000;
+ } else {
+ ack.congestion_info.type = kNone;
+ }
+ QuicConnectionCloseFrame close;
+ close.error_code = QUIC_CONNECTION_TIMED_OUT;
+ close.ack_frame = ack;
+
+ return ConstructPacket(header_, QuicFrame(&close));
+ }
+
+ BoundNetLog net_log_;
+ MockScheduler* scheduler_;
+ scoped_refptr<TestTaskRunner> runner_;
+ QuicConnectionHelper* helper_;
+ scoped_array<MockWrite> mock_writes_;
+ MockClock clock_;
+ TestConnection* connection_;
+ testing::StrictMock<MockConnectionVisitor> visitor_;
+ scoped_ptr<QuicHttpStream> stream_;
+ scoped_ptr<QuicClientSession> session_;
+ TestCompletionCallback callback_;
+ HttpRequestInfo request_;
+ HttpRequestHeaders headers_;
+ HttpResponseInfo response_;
+ scoped_refptr<IOBufferWithSize> read_buffer_;
+
+ private:
+ void InitializeHeader(QuicPacketSequenceNumber sequence_number) {
+ header_.guid = guid_;
+ header_.packet_sequence_number = sequence_number;
+ header_.flags = PACKET_FLAGS_NONE;
+ header_.fec_group = 0;
+ }
+
+ QuicEncryptedPacket* ConstructPacket(const QuicPacketHeader& header,
+ const QuicFrame& frame) {
+ QuicFrames frames;
+ frames.push_back(frame);
+ QuicPacket* packet;
+ framer_.ConstructFrameDataPacket(header_, frames, &packet);
+ QuicEncryptedPacket* encrypted = framer_.EncryptPacket(*packet);
+ delete packet;
+ return encrypted;
+ }
+
+ QuicGuid guid_;
+ QuicFramer framer_;
+ IPEndPoint self_addr_;
+ IPEndPoint peer_addr_;
+ QuicPacketCreator creator_;
+ QuicPacketHeader header_;
+ scoped_ptr<MockUDPClientSocket> socket_;
+ scoped_ptr<StaticSocketDataProvider> socket_data_;
+ std::vector<PacketToWrite> writes_;
+};
+
+TEST_F(QuicHttpStreamTest, RenewStreamForAuth) {
+ EXPECT_EQ(NULL, stream_->RenewStreamForAuth());
+}
+
+TEST_F(QuicHttpStreamTest, CanFindEndOfResponse) {
+ EXPECT_TRUE(stream_->CanFindEndOfResponse());
+}
+
+TEST_F(QuicHttpStreamTest, IsMoreDataBuffered) {
+ EXPECT_FALSE(stream_->IsMoreDataBuffered());
+}
+
+TEST_F(QuicHttpStreamTest, IsConnectionReusable) {
+ EXPECT_FALSE(stream_->IsConnectionReusable());
+}
+
+TEST_F(QuicHttpStreamTest, GetRequest) {
+ AddWrite(SYNCHRONOUS, ConstructDataPacket(1, kFin, 0,
+ "GET / HTTP/1.1\r\n\r\n"));
+ AddWrite(SYNCHRONOUS, ConstructAckPacket(2, 2, 0));
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, net_log_,
+ callback_.callback()));
+ EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+ EXPECT_EQ(&response_, stream_->GetResponseInfo());
+
+ // Ack the request.
+ scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 1, 0));
+ ProcessPacket(*ack);
+
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream_->ReadResponseHeaders(callback_.callback()));
+
+ // Send the response without a body.
+ char kResponseHeaders[] = "HTTP/1.1 404 OK\r\n"
willchan no longer on Chromium 2012/11/23 03:17:43 Can this be a const char[]?
Ryan Hamilton 2012/11/23 04:19:53 Done.
+ "Content-Type: text/plain\r\n\r\n";
+ scoped_ptr<QuicEncryptedPacket> resp(
+ ConstructDataPacket(2, kFin, 0, kResponseHeaders));
+ ProcessPacket(*resp);
+
+ // Now that the headers have been processed, the callback will return.
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ ASSERT_TRUE(response_.headers != NULL);
+ EXPECT_EQ(404, response_.headers->response_code());
+ EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+
+ // There is no body, so this should return immediately.
+ EXPECT_EQ(0, stream_->ReadResponseBody(read_buffer_.get(),
+ read_buffer_->size(),
+ callback_.callback()));
+ EXPECT_TRUE(stream_->IsResponseBodyComplete());
+ EXPECT_TRUE(AtEof());
+}
+
willchan no longer on Chromium 2012/11/23 03:17:43 Add a test for response headers and body in the sa
Ryan Hamilton 2012/11/23 04:19:53 Done.
+TEST_F(QuicHttpStreamTest, SendPostRequest) {
+ char kRequestData[] = "POST / HTTP/1.1\r\n\r\n";
+ AddWrite(SYNCHRONOUS, ConstructDataPacket(1, false, 0, kRequestData));
+ AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, strlen(kRequestData),
+ kUploadData));
+ AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 2, 0));
+ AddWrite(SYNCHRONOUS, ConstructAckPacket(4, 3, 0));
+
+ Initialize();
+
+ UploadData* upload_data = new UploadData();
+ upload_data->AppendBytes(kUploadData, strlen(kUploadData));
+ UploadDataStream upload_data_stream(upload_data);
+ request_.method = "POST";
+ request_.url = GURL("http://www.google.com/");
+ request_.upload_data_stream = &upload_data_stream;
+ ASSERT_EQ(OK, request_.upload_data_stream->InitSync());
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, net_log_,
+ callback_.callback()));
+ EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+ EXPECT_EQ(&response_, stream_->GetResponseInfo());
+
+ // Ack both packets in the request.
+ scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 2, 1));
+ ProcessPacket(*ack);
+
+ // Send the response headers (but not the body).
+ char kResponseHeaders[] = "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/plain\r\n\r\n";
+ scoped_ptr<QuicEncryptedPacket> resp(
+ ConstructDataPacket(2, kNoFin, 0, kResponseHeaders));
+ ProcessPacket(*resp);
+
+ // Since the headers have already arrived, this should return immediately.
+ EXPECT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback()));
+ ASSERT_TRUE(response_.headers != NULL);
+ EXPECT_EQ(200, response_.headers->response_code());
+ EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+
+ // Send the response body.
+ char kResponseBody[] = "Hello world!";
+ scoped_ptr<QuicEncryptedPacket> resp_body(
+ ConstructDataPacket(3, kFin, strlen(kResponseHeaders), kResponseBody));
+ ProcessPacket(*resp_body);
+
+ // Since the body has already arrived, this should return immediately.
+ EXPECT_EQ(static_cast<int>(strlen(kResponseBody)),
+ stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
+ callback_.callback()));
+
+ EXPECT_TRUE(stream_->IsResponseBodyComplete());
+ EXPECT_TRUE(AtEof());
+}
willchan no longer on Chromium 2012/11/23 03:22:58 Can you add a test for synchronous completion of t
Ryan Hamilton 2012/11/23 04:19:53 Actually, the existing test already does that. Co
+
+} // namespace test
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698