Add QUIC to Alternate-Protocol support.
Review URL: https://chromiumcodereview.appspot.com/12156005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@183737 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
new file mode 100644
index 0000000..e53349d7
--- /dev/null
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -0,0 +1,320 @@
+// 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 "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/mock_cert_verifier.h"
+#include "net/base/mock_host_resolver.h"
+#include "net/base/ssl_config_service_defaults.h"
+#include "net/base/test_completion_callback.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_network_transaction.h"
+#include "net/http/http_server_properties_impl.h"
+#include "net/http/http_stream.h"
+#include "net/http/http_stream_factory.h"
+#include "net/http/http_transaction_unittest.h"
+#include "net/proxy/proxy_config_service_fixed.h"
+#include "net/proxy/proxy_resolver.h"
+#include "net/proxy/proxy_service.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/mock_client_socket_pool_manager.h"
+#include "net/socket/socket_test_util.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_framer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+// This is the expected return from a current server advertising QUIC.
+static const char kQuicAlternateProtocolHttpHeader[] =
+ "Alternate-Protocol: 443:quic/1\r\n\r\n";
+
+// Returns a vector of NPN protocol strings for negotiating QUIC.
+std::vector<std::string> QuicNextProtos() {
+ std::vector<std::string> protos;
+ protos.push_back("http/1.1");
+ protos.push_back("quic/1");
+ return protos;
+}
+
+} // namespace
+
+namespace net {
+namespace test {
+
+class QuicNetworkTransactionTest : public PlatformTest {
+ protected:
+ QuicNetworkTransactionTest() : clock_(new MockClock()) {}
+
+ virtual void SetUp() {
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ MessageLoop::current()->RunUntilIdle();
+ }
+
+ virtual void TearDown() {
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ // Empty the current queue.
+ MessageLoop::current()->RunUntilIdle();
+ PlatformTest::TearDown();
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ MessageLoop::current()->RunUntilIdle();
+ HttpStreamFactory::set_use_alternate_protocols(false);
+ HttpStreamFactory::SetNextProtos(std::vector<std::string>());
+ }
+
+ // TODO(rch): factor these Construct* methods out into a test helper class.
+ scoped_ptr<QuicEncryptedPacket> ConstructChlo() {
+ const std::string host = "www.google.com";
+ scoped_ptr<QuicPacket> chlo(ConstructClientHelloPacket(0xDEADBEEF,
+ clock_,
+ &random_generator_,
+ host));
+ QuicFramer framer(QuicDecrypter::Create(kNULL),
+ QuicEncrypter::Create(kNULL));
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*chlo));
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructShlo() {
+ scoped_ptr<QuicPacket> shlo(ConstructHandshakePacket(0xDEADBEEF, kSHLO));
+ QuicFramer framer(QuicDecrypter::Create(kNULL),
+ QuicEncrypter::Create(kNULL));
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*shlo));
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructRstPacket(
+ QuicPacketSequenceNumber num,
+ QuicStreamId stream_id) {
+ QuicPacketHeader header;
+ header.public_header.guid = 0xDEADBEEF;
+ header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header.packet_sequence_number = num;
+ header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.fec_group = 0;
+
+ QuicRstStreamFrame rst(stream_id, 0, QUIC_NO_ERROR);
+ return scoped_ptr<QuicEncryptedPacket>(
+ ConstructPacket(header, QuicFrame(&rst)));
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked) {
+ QuicPacketHeader header;
+ header.public_header.guid = 0xDEADBEEF;
+ header.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header.packet_sequence_number = 3;
+ header.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ header.fec_group = 0;
+
+ QuicAckFrame ack(largest_received, least_unacked);
+
+ QuicCongestionFeedbackFrame feedback;
+ feedback.type = kTCP;
+ feedback.tcp.accumulated_number_of_lost_packets = 0;
+ feedback.tcp.receive_window = 256000;
+
+ QuicFramer framer(QuicDecrypter::Create(kNULL),
+ QuicEncrypter::Create(kNULL));
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack));
+ frames.push_back(QuicFrame(&feedback));
+ scoped_ptr<QuicPacket> packet(
+ framer.ConstructFrameDataPacket(header, frames));
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*packet));
+ }
+
+ std::string GetRequestString(const std::string& method,
+ const std::string& path) {
+ SpdyHeaderBlock headers;
+ headers[":method"] = method;
+ headers[":host"] = "www.google.com";
+ headers[":path"] = path;
+ headers[":scheme"] = "http";
+ headers[":version"] = "HTTP/1.1";
+ return SerializeHeaderBlock(headers);
+ }
+
+ std::string GetResponseString(const std::string& status,
+ const std::string& body) {
+ SpdyHeaderBlock headers;
+ headers[":status"] = status;
+ headers[":version"] = "HTTP/1.1";
+ headers["content-type"] = "text/plain";
+ return SerializeHeaderBlock(headers) + body;
+ }
+
+ std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers) {
+ size_t len = SpdyFramer::GetSerializedLength(3, &headers);
+ SpdyFrameBuilder builder(len);
+ SpdyFramer::WriteHeaderBlock(&builder, 3, &headers);
+ scoped_ptr<SpdyFrame> frame(builder.take());
+ return std::string(frame->data(), len);
+ }
+
+ // 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)).release();
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructPacket(
+ const QuicPacketHeader& header,
+ const QuicFrame& frame) {
+ QuicFramer framer(QuicDecrypter::Create(kNULL),
+ QuicEncrypter::Create(kNULL));
+ QuicFrames frames;
+ frames.push_back(frame);
+ scoped_ptr<QuicPacket> packet(
+ framer.ConstructFrameDataPacket(header, frames));
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(*packet));
+ }
+
+ void InitializeHeader(QuicPacketSequenceNumber sequence_number) {
+ header_.public_header.guid = random_generator_.RandUint64();
+ header_.public_header.flags = PACKET_PUBLIC_FLAGS_NONE;
+ header_.packet_sequence_number = sequence_number;
+ header_.fec_group = 0;
+ header_.private_flags = PACKET_PRIVATE_FLAGS_NONE;
+ }
+
+ void CreateSession() {
+ params_.client_socket_factory = &socket_factory_;
+ params_.host_resolver = new MockHostResolver;
+ params_.cert_verifier = new MockCertVerifier;
+ params_.proxy_service = ProxyService::CreateDirect();
+ params_.ssl_config_service = new SSLConfigServiceDefaults;
+ params_.http_auth_handler_factory =
+ HttpAuthHandlerFactory::CreateDefault(params_.host_resolver);
+ params_.http_server_properties = &http_server_properties;
+
+ session_ = new HttpNetworkSession(params_);
+ }
+
+ QuicPacketHeader header_;
+ scoped_refptr<HttpNetworkSession> session_;
+ MockClientSocketFactory socket_factory_;
+ MockClock* clock_;
+ MockRandom random_generator_;
+ HttpServerPropertiesImpl http_server_properties;
+ HttpNetworkSession::Params params_;
+};
+
+TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) {
+ HttpStreamFactory::set_use_alternate_protocols(true);
+ HttpStreamFactory::SetNextProtos(QuicNextProtos());
+ params_.enable_quic = true;
+ params_.quic_clock = clock_;
+ params_.use_spdy_over_quic = true;
+ params_.quic_random = &random_generator_;
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.google.com/");
+ request.load_flags = 0;
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead(kQuicAlternateProtocolHttpHeader),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider first_transaction(
+ data_reads, arraysize(data_reads), NULL, 0);
+ socket_factory_.AddSocketDataProvider(&first_transaction);
+
+
+ scoped_ptr<QuicEncryptedPacket> chlo(ConstructChlo());
+ scoped_ptr<QuicEncryptedPacket> data(
+ ConstructDataPacket(2, true, 0, GetRequestString("GET", "/")));
+ scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(2, 1));
+
+ MockWrite quic_writes[] = {
+ MockWrite(SYNCHRONOUS, chlo->data(), chlo->length()),
+ MockWrite(SYNCHRONOUS, data->data(), data->length()),
+ MockWrite(SYNCHRONOUS, ack->data(), ack->length()),
+ };
+
+ scoped_ptr<QuicEncryptedPacket> shlo(ConstructShlo());
+ scoped_ptr<QuicEncryptedPacket> resp(
+ ConstructDataPacket(2, true, 0, GetResponseString("200 OK", "hello!")));
+ MockRead quic_reads[] = {
+ MockRead(SYNCHRONOUS, shlo->data(), shlo->length()),
+ MockRead(SYNCHRONOUS, resp->data(), resp->length()),
+ MockRead(ASYNC, OK), // EOF
+ };
+
+ DelayedSocketData quic_data(
+ 1, // wait for one write to finish before reading.
+ quic_reads, arraysize(quic_reads),
+ quic_writes, arraysize(quic_writes));
+
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ // The non-alternate protocol job needs to hang in order to guarantee that the
+ // alternate-protocol job will "win".
+ MockConnect never_finishing_connect(SYNCHRONOUS, ERR_IO_PENDING);
+ StaticSocketDataProvider hanging_non_alternate_protocol_socket(
+ NULL, 0, NULL, 0);
+ hanging_non_alternate_protocol_socket.set_connect_data(
+ never_finishing_connect);
+ socket_factory_.AddSocketDataProvider(
+ &hanging_non_alternate_protocol_socket);
+
+ TestCompletionCallback callback;
+
+ CreateSession();
+ scoped_ptr<HttpNetworkTransaction> trans(
+ new HttpNetworkTransaction(session_));
+
+ int rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_TRUE(response->headers != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+ std::string response_data;
+ ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+ EXPECT_EQ("hello world", response_data);
+
+ trans.reset(new HttpNetworkTransaction(session_));
+
+ rv = trans->Start(&request, callback.callback(), BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, callback.WaitForResult());
+
+ response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != NULL);
+ ASSERT_TRUE(response->headers != NULL);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+ EXPECT_TRUE(response->was_fetched_via_spdy);
+
+ ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+ EXPECT_EQ("hello!", response_data);
+}
+
+} // namespace test
+} // namespace net