summaryrefslogtreecommitdiff
path: root/chromium/net/tools/quic
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/net/tools/quic
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/net/tools/quic')
-rwxr-xr-xchromium/net/tools/quic/benchmark/run_client.py186
-rw-r--r--chromium/net/tools/quic/benchmark/test_urls.json41
-rw-r--r--chromium/net/tools/quic/end_to_end_test.cc651
-rw-r--r--chromium/net/tools/quic/quic_client.cc289
-rw-r--r--chromium/net/tools/quic/quic_client.h203
-rw-r--r--chromium/net/tools/quic/quic_client_bin.cc56
-rw-r--r--chromium/net/tools/quic/quic_client_session.cc65
-rw-r--r--chromium/net/tools/quic/quic_client_session.h56
-rw-r--r--chromium/net/tools/quic/quic_client_session_test.cc96
-rw-r--r--chromium/net/tools/quic/quic_dispatcher.cc203
-rw-r--r--chromium/net/tools/quic/quic_dispatcher.h147
-rw-r--r--chromium/net/tools/quic/quic_dispatcher_test.cc390
-rw-r--r--chromium/net/tools/quic/quic_epoll_clock.cc29
-rw-r--r--chromium/net/tools/quic/quic_epoll_clock.h39
-rw-r--r--chromium/net/tools/quic/quic_epoll_clock_test.cc57
-rw-r--r--chromium/net/tools/quic/quic_epoll_connection_helper.cc140
-rw-r--r--chromium/net/tools/quic/quic_epoll_connection_helper.h72
-rw-r--r--chromium/net/tools/quic/quic_epoll_connection_helper_test.cc207
-rw-r--r--chromium/net/tools/quic/quic_in_memory_cache.cc225
-rw-r--r--chromium/net/tools/quic/quic_in_memory_cache.h88
-rw-r--r--chromium/net/tools/quic/quic_in_memory_cache_test.cc137
-rw-r--r--chromium/net/tools/quic/quic_packet_writer.h33
-rw-r--r--chromium/net/tools/quic/quic_reliable_client_stream.cc27
-rw-r--r--chromium/net/tools/quic/quic_reliable_client_stream.h66
-rw-r--r--chromium/net/tools/quic/quic_reliable_client_stream_test.cc96
-rw-r--r--chromium/net/tools/quic/quic_reliable_server_stream.cc56
-rw-r--r--chromium/net/tools/quic/quic_reliable_server_stream.h65
-rw-r--r--chromium/net/tools/quic/quic_reliable_server_stream_test.cc219
-rw-r--r--chromium/net/tools/quic/quic_server.cc223
-rw-r--r--chromium/net/tools/quic/quic_server.h115
-rw-r--r--chromium/net/tools/quic/quic_server_bin.cc51
-rw-r--r--chromium/net/tools/quic/quic_server_session.cc74
-rw-r--r--chromium/net/tools/quic/quic_server_session.h78
-rw-r--r--chromium/net/tools/quic/quic_server_test.cc74
-rw-r--r--chromium/net/tools/quic/quic_socket_utils.cc191
-rw-r--r--chromium/net/tools/quic/quic_socket_utils.h59
-rw-r--r--chromium/net/tools/quic/quic_spdy_client_stream.cc104
-rw-r--r--chromium/net/tools/quic/quic_spdy_client_stream.h47
-rw-r--r--chromium/net/tools/quic/quic_spdy_server_stream.cc104
-rw-r--r--chromium/net/tools/quic/quic_spdy_server_stream.h45
-rw-r--r--chromium/net/tools/quic/quic_spdy_server_stream_test.cc70
-rw-r--r--chromium/net/tools/quic/quic_time_wait_list_manager.cc322
-rw-r--r--chromium/net/tools/quic/quic_time_wait_list_manager.h194
-rw-r--r--chromium/net/tools/quic/spdy_utils.cc266
-rw-r--r--chromium/net/tools/quic/spdy_utils.h45
-rw-r--r--chromium/net/tools/quic/test_tools/http_message_test_utils.cc175
-rw-r--r--chromium/net/tools/quic/test_tools/http_message_test_utils.h133
-rw-r--r--chromium/net/tools/quic/test_tools/mock_epoll_server.cc68
-rw-r--r--chromium/net/tools/quic/test_tools/mock_epoll_server.h106
-rw-r--r--chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc21
-rw-r--r--chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h38
-rw-r--r--chromium/net/tools/quic/test_tools/quic_client_peer.cc27
-rw-r--r--chromium/net/tools/quic/test_tools/quic_client_peer.h25
-rw-r--r--chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc21
-rw-r--r--chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h31
-rw-r--r--chromium/net/tools/quic/test_tools/quic_test_client.cc342
-rw-r--r--chromium/net/tools/quic/test_tools/quic_test_client.h143
-rw-r--r--chromium/net/tools/quic/test_tools/quic_test_utils.cc81
-rw-r--r--chromium/net/tools/quic/test_tools/quic_test_utils.h112
-rw-r--r--chromium/net/tools/quic/test_tools/run_all_unittests.cc11
60 files changed, 7335 insertions, 0 deletions
diff --git a/chromium/net/tools/quic/benchmark/run_client.py b/chromium/net/tools/quic/benchmark/run_client.py
new file mode 100755
index 00000000000..7df37ab7fa0
--- /dev/null
+++ b/chromium/net/tools/quic/benchmark/run_client.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+
+# Copyright 2013 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.
+
+import csv
+import datetime
+import json
+import os
+import shlex
+import subprocess
+import sys
+from optparse import OptionParser
+
+"""Start a client to fetch web pages either using wget or using quic_client.
+If --use_wget is set, it uses wget.
+Usage: This invocation
+ run_client.py --quic_binary_dir=../../../../out/Debug \
+ --address=127.0.0.1 --port=5000 --infile=test_urls.json \
+ --delay_file=delay.csv --packets_file=packets.csv
+ fetches pages listed in test_urls.json from a quic server running at
+ 127.0.0.1 on port 5000 using quic binary ../../../../out/Debug/quic_client
+ and stores the delay in delay.csv and the max received packet number (for
+ QUIC) in packets.csv.
+ If --use_wget is present, it will fetch the URLs using wget and ignores
+ the flags --address, --port, --quic_binary_dir, etc.
+"""
+
+def Timestamp(datetm=None):
+ """Get the timestamp in microseconds.
+ Args:
+ datetm: the date and time to be converted to timestamp.
+ If not set, use the current UTC time.
+ Returns:
+ The timestamp in microseconds.
+ """
+ datetm = datetm or datetime.datetime.utcnow()
+ diff = datetm - datetime.datetime.utcfromtimestamp(0)
+ timestamp = (diff.days * 86400 + diff.seconds) * 1000000 + diff.microseconds
+ return timestamp
+
+class PageloadExperiment:
+ def __init__(self, use_wget, quic_binary_dir, quic_server_address,
+ quic_server_port):
+ """Initialize PageloadExperiment.
+
+ Args:
+ use_wget: Whether to use wget.
+ quic_binary_dir: Directory for quic_binary.
+ quic_server_address: IP address of quic server.
+ quic_server_port: Port of the quic server.
+ """
+ self.use_wget = use_wget
+ self.quic_binary_dir = quic_binary_dir
+ self.quic_server_address = quic_server_address
+ self.quic_server_port = quic_server_port
+ if not use_wget and not os.path.isfile(quic_binary_dir + '/quic_client'):
+ raise IOError('There is no quic_client in the given dir: %s.'
+ % quic_binary_dir)
+
+ @classmethod
+ def ReadPages(cls, json_file):
+ """Return the list of URLs from the json_file.
+
+ One entry of the list may contain a html link and multiple resources.
+ """
+ page_list = []
+ with open(json_file) as f:
+ data = json.load(f)
+ for page in data['pages']:
+ url = page['url']
+ if 'resources' in page:
+ resources = page['resources']
+ else:
+ resources = None
+ if not resources:
+ page_list.append([url])
+ else:
+ urls = [url]
+ # For url http://x.com/z/y.html, url_dir is http://x.com/z
+ url_dir = url.rsplit('/', 1)[0]
+ for resource in resources:
+ urls.append(url_dir + '/' + resource)
+ page_list.append(urls)
+ return page_list
+
+ def DownloadOnePage(self, urls):
+ """Download a page emulated by a list of urls.
+
+ Args:
+ urls: list of URLs to fetch.
+ Returns:
+ A tuple (page download time, max packet number).
+ """
+ if self.use_wget:
+ cmd = 'wget -O -'
+ else:
+ cmd = '%s/quic_client --port=%s --address=%s' % (
+ self.quic_binary_dir, self.quic_server_port, self.quic_server_address)
+ cmd_in_list = shlex.split(cmd)
+ cmd_in_list.extend(urls)
+ start_time = Timestamp()
+ ps_proc = subprocess.Popen(cmd_in_list,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ _std_out, std_err = ps_proc.communicate()
+ end_time = Timestamp()
+ delta_time = end_time - start_time
+ max_packets = 0
+ if not self.use_wget:
+ for line in std_err.splitlines():
+ if line.find('Client: Got packet') >= 0:
+ elems = line.split()
+ packet_num = int(elems[4])
+ max_packets = max(max_packets, packet_num)
+ return delta_time, max_packets
+
+ def RunExperiment(self, infile, delay_file, packets_file=None, num_it=1):
+ """Run the pageload experiment.
+
+ Args:
+ infile: Input json file describing the page list.
+ delay_file: Output file storing delay in csv format.
+ packets_file: Output file storing max packet number in csv format.
+ num_it: Number of iterations to run in this experiment.
+ """
+ page_list = self.ReadPages(infile)
+ header = [urls[0].rsplit('/', 1)[1] for urls in page_list]
+ header0 = 'wget' if self.use_wget else 'quic'
+ header = [header0] + header
+
+ plt_list = []
+ packets_list = []
+ for i in range(num_it):
+ plt_one_row = [str(i)]
+ packets_one_row = [str(i)]
+ for urls in page_list:
+ time_micros, num_packets = self.DownloadOnePage(urls)
+ time_secs = time_micros / 1000000.0
+ plt_one_row.append('%6.3f' % time_secs)
+ packets_one_row.append('%5d' % num_packets)
+ plt_list.append(plt_one_row)
+ packets_list.append(packets_one_row)
+
+ with open(delay_file, 'w') as f:
+ csv_writer = csv.writer(f, delimiter=',')
+ csv_writer.writerow(header)
+ for one_row in plt_list:
+ csv_writer.writerow(one_row)
+ if packets_file:
+ with open(packets_file, 'w') as f:
+ csv_writer = csv.writer(f, delimiter=',')
+ csv_writer.writerow(header)
+ for one_row in packets_list:
+ csv_writer.writerow(one_row)
+
+
+def main():
+ parser = OptionParser()
+ parser.add_option('--use_wget', dest='use_wget', action='store_true',
+ default=False)
+ # Note that only debug version generates the log containing packets
+ # information.
+ parser.add_option('--quic_binary_dir', dest='quic_binary_dir',
+ default='../../../../out/Debug')
+ # For whatever server address you specify, you need to run the
+ # quic_server on that machine and populate it with the cache containing
+ # the URLs requested in the --infile.
+ parser.add_option('--address', dest='quic_server_address',
+ default='127.0.0.1')
+ parser.add_option('--port', dest='quic_server_port',
+ default='5002')
+ parser.add_option('--delay_file', dest='delay_file', default='delay.csv')
+ parser.add_option('--packets_file', dest='packets_file',
+ default='packets.csv')
+ parser.add_option('--infile', dest='infile', default='test_urls.json')
+ (options, _) = parser.parse_args()
+
+ exp = PageloadExperiment(options.use_wget, options.quic_binary_dir,
+ options.quic_server_address,
+ options.quic_server_port)
+ exp.RunExperiment(options.infile, options.delay_file, options.packets_file)
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/chromium/net/tools/quic/benchmark/test_urls.json b/chromium/net/tools/quic/benchmark/test_urls.json
new file mode 100644
index 00000000000..79c20cfe3d1
--- /dev/null
+++ b/chromium/net/tools/quic/benchmark/test_urls.json
@@ -0,0 +1,41 @@
+{
+ "description": "List of pages (URLs) for measuring quic performance using standalone quic client and server. These URLs are for testing only and are provided without guarantee.",
+ "pages": [
+ {
+ "url": "http://dev1.mdw.la/test/warmup.html",
+ "why": "A warmup page."
+ },
+ {
+ "url": "http://dev1.mdw.la/test/test_1KB.jpg",
+ "why": "A tiny page, about 1K Bytes."
+ },
+ {
+ "url": "http://dev1.mdw.la/test/test_10KB.jpg",
+ "why": "A small page, about 10K Bytes."
+ },
+ {
+ "url": "http://dev1.mdw.la/test/test_100KB.jpg",
+ "why": "A medium page, about 100K Bytes."
+ },
+ {
+ "url": "http://dev1.mdw.la/test/test_1MB.jpg",
+ "why": "A large page, about 1M Bytes."
+ },
+ {
+ "url": "http://dev1.mdw.la/test/ten_img.html",
+ "why": "A large page, with 1 html and 10 images totaling about 1M Bytes.",
+ "resources": [
+ "imgs/test_100KB_0.jpg",
+ "imgs/test_100KB_1.jpg",
+ "imgs/test_100KB_2.jpg",
+ "imgs/test_100KB_3.jpg",
+ "imgs/test_100KB_4.jpg",
+ "imgs/test_100KB_5.jpg",
+ "imgs/test_100KB_6.jpg",
+ "imgs/test_100KB_7.jpg",
+ "imgs/test_100KB_8.jpg",
+ "imgs/test_100KB_9.jpg"
+ ]
+ }
+ ]
+}
diff --git a/chromium/net/tools/quic/end_to_end_test.cc b/chromium/net/tools/quic/end_to_end_test.cc
new file mode 100644
index 00000000000..3903df965ee
--- /dev/null
+++ b/chromium/net/tools/quic/end_to_end_test.cc
@@ -0,0 +1,651 @@
+// 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 <stddef.h>
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/simple_thread.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
+#include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_server.h"
+#include "net/tools/quic/quic_socket_utils.h"
+#include "net/tools/quic/test_tools/http_message_test_utils.h"
+#include "net/tools/quic/test_tools/quic_client_peer.h"
+#include "net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h"
+#include "net/tools/quic/test_tools/quic_test_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using base::WaitableEvent;
+using net::test::QuicConnectionPeer;
+using net::test::QuicSessionPeer;
+using net::test::ReliableQuicStreamPeer;
+using std::string;
+
+namespace net {
+namespace tools {
+namespace test {
+namespace {
+
+const char* kFooResponseBody = "Artichoke hearts make me happy.";
+const char* kBarResponseBody = "Palm hearts are pretty delicious, also.";
+const size_t kCongestionFeedbackFrameSize = 25;
+// If kCongestionFeedbackFrameSize increase we need to expand this string
+// accordingly.
+const char* kLargeRequest =
+ "https://www.google.com/foo/test/a/request/string/longer/than/25/bytes";
+
+void GenerateBody(string* body, int length) {
+ body->clear();
+ body->reserve(length);
+ for (int i = 0; i < length; ++i) {
+ body->append(1, static_cast<char>(32 + i % (126 - 32)));
+ }
+}
+
+
+// Simple wrapper class to run server in a thread.
+class ServerThread : public base::SimpleThread {
+ public:
+ explicit ServerThread(IPEndPoint address, const QuicConfig& config)
+ : SimpleThread("server_thread"),
+ listening_(true, false),
+ quit_(true, false),
+ server_(config),
+ address_(address),
+ port_(0) {
+ }
+ virtual ~ServerThread() {
+ }
+
+ virtual void Run() OVERRIDE {
+ server_.Listen(address_);
+
+ port_lock_.Acquire();
+ port_ = server_.port();
+ port_lock_.Release();
+
+ listening_.Signal();
+ while (!quit_.IsSignaled()) {
+ server_.WaitForEvents();
+ }
+ server_.Shutdown();
+ }
+
+ int GetPort() {
+ port_lock_.Acquire();
+ int rc = port_;
+ port_lock_.Release();
+ return rc;
+ }
+
+ WaitableEvent* listening() { return &listening_; }
+ WaitableEvent* quit() { return &quit_; }
+
+ private:
+ WaitableEvent listening_;
+ WaitableEvent quit_;
+ base::Lock port_lock_;
+ QuicServer server_;
+ IPEndPoint address_;
+ int port_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerThread);
+};
+
+class EndToEndTest : public ::testing::TestWithParam<QuicVersion> {
+ public:
+ static void SetUpTestCase() {
+ QuicInMemoryCache::GetInstance()->ResetForTests();
+ }
+
+ protected:
+ EndToEndTest()
+ : server_hostname_("example.com"),
+ server_started_(false) {
+ net::IPAddressNumber ip;
+ CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &ip));
+ server_address_ = IPEndPoint(ip, 0);
+ client_config_.SetDefaults();
+ server_config_.SetDefaults();
+
+ AddToCache("GET", kLargeRequest, "HTTP/1.1", "200", "OK", kFooResponseBody);
+ AddToCache("GET", "https://www.google.com/foo",
+ "HTTP/1.1", "200", "OK", kFooResponseBody);
+ AddToCache("GET", "https://www.google.com/bar",
+ "HTTP/1.1", "200", "OK", kBarResponseBody);
+ version_ = GetParam();
+ }
+
+ virtual QuicTestClient* CreateQuicClient() {
+ QuicTestClient* client = new QuicTestClient(server_address_,
+ server_hostname_,
+ false, // not secure
+ client_config_,
+ version_);
+ client->Connect();
+ return client;
+ }
+
+ virtual bool Initialize() {
+ // Start the server first, because CreateQuicClient() attempts
+ // to connect to the server.
+ StartServer();
+ client_.reset(CreateQuicClient());
+ return client_->client()->connected();
+ }
+
+ virtual void TearDown() {
+ StopServer();
+ }
+
+ void StartServer() {
+ server_thread_.reset(new ServerThread(server_address_, server_config_));
+ server_thread_->Start();
+ server_thread_->listening()->Wait();
+ server_address_ = IPEndPoint(server_address_.address(),
+ server_thread_->GetPort());
+ server_started_ = true;
+ }
+
+ void StopServer() {
+ if (!server_started_)
+ return;
+ if (server_thread_.get()) {
+ server_thread_->quit()->Signal();
+ server_thread_->Join();
+ }
+ }
+
+ void AddToCache(const StringPiece& method,
+ const StringPiece& path,
+ const StringPiece& version,
+ const StringPiece& response_code,
+ const StringPiece& response_detail,
+ const StringPiece& body) {
+ BalsaHeaders request_headers, response_headers;
+ request_headers.SetRequestFirstlineFromStringPieces(method,
+ path,
+ version);
+ response_headers.SetRequestFirstlineFromStringPieces(version,
+ response_code,
+ response_detail);
+ response_headers.AppendHeader("content-length",
+ base::IntToString(body.length()));
+
+ // Check if response already exists and matches.
+ QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance();
+ const QuicInMemoryCache::Response* cached_response =
+ cache->GetResponse(request_headers);
+ if (cached_response != NULL) {
+ string cached_response_headers_str, response_headers_str;
+ cached_response->headers().DumpToString(&cached_response_headers_str);
+ response_headers.DumpToString(&response_headers_str);
+ CHECK_EQ(cached_response_headers_str, response_headers_str);
+ CHECK_EQ(cached_response->body(), body);
+ return;
+ }
+ cache->AddResponse(request_headers, response_headers, body);
+ }
+
+ IPEndPoint server_address_;
+ string server_hostname_;
+ scoped_ptr<ServerThread> server_thread_;
+ scoped_ptr<QuicTestClient> client_;
+ bool server_started_;
+ QuicConfig client_config_;
+ QuicConfig server_config_;
+ QuicVersion version_;
+};
+
+// Run all end to end tests with all supported versions.
+INSTANTIATE_TEST_CASE_P(EndToEndTests,
+ EndToEndTest,
+ ::testing::ValuesIn(kSupportedQuicVersions));
+
+TEST_P(EndToEndTest, SimpleRequestResponse) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+}
+
+// TODO(rch): figure out how to detect missing v6 supprt (like on the linux
+// try bots) and selectively disable this test.
+TEST_P(EndToEndTest, DISABLED_SimpleRequestResponsev6) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ IPAddressNumber ip;
+ CHECK(net::ParseIPLiteralToNumber("::1", &ip));
+ server_address_ = IPEndPoint(ip, server_address_.port());
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+}
+
+TEST_P(EndToEndTest, SeparateFinPacket) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.set_has_complete_message(false);
+
+ client_->SendMessage(request);
+
+ client_->SendData(string(), true);
+
+ client_->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client_->response_body());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+
+ request.AddBody("foo", true);
+
+ client_->SendMessage(request);
+ client_->SendData(string(), true);
+ client_->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client_->response_body());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+}
+
+TEST_P(EndToEndTest, MultipleRequestResponse) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+}
+
+TEST_P(EndToEndTest, MultipleClients) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+ scoped_ptr<QuicTestClient> client2(CreateQuicClient());
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddHeader("content-length", "3");
+ request.set_has_complete_message(false);
+
+ client_->SendMessage(request);
+ client2->SendMessage(request);
+
+ client_->SendData("bar", true);
+ client_->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client_->response_body());
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+
+ client2->SendData("eep", true);
+ client2->WaitForResponse();
+ EXPECT_EQ(kFooResponseBody, client2->response_body());
+ EXPECT_EQ(200u, client2->response_headers()->parsed_response_code());
+}
+
+TEST_P(EndToEndTest, RequestOverMultiplePackets) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+ // Set things up so we have a small payload, to guarantee fragmentation.
+ // A congestion feedback frame can't be split into multiple packets, make sure
+ // that our packet have room for at least this amount after the normal headers
+ // are added.
+
+ // TODO(rch) handle this better when we have different encryption options.
+ const size_t kStreamDataLength = 3;
+ const QuicStreamId kStreamId = 1u;
+ const QuicStreamOffset kStreamOffset = 0u;
+ size_t stream_payload_size =
+ QuicFramer::GetMinStreamFrameSize(
+ GetParam(), kStreamId, kStreamOffset, true) + kStreamDataLength;
+ size_t min_payload_size =
+ std::max(kCongestionFeedbackFrameSize, stream_payload_size);
+ size_t ciphertext_size = NullEncrypter().GetCiphertextSize(min_payload_size);
+ // TODO(satyashekhar): Fix this when versioning is implemented.
+ client_->options()->max_packet_length =
+ GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP) +
+ ciphertext_size;
+
+ // Make sure our request is too large to fit in one packet.
+ EXPECT_GT(strlen(kLargeRequest), min_payload_size);
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest(kLargeRequest));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+}
+
+TEST_P(EndToEndTest, MultipleFramesRandomOrder) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+ // Set things up so we have a small payload, to guarantee fragmentation.
+ // A congestion feedback frame can't be split into multiple packets, make sure
+ // that our packet have room for at least this amount after the normal headers
+ // are added.
+
+ // TODO(rch) handle this better when we have different encryption options.
+ const size_t kStreamDataLength = 3;
+ const QuicStreamId kStreamId = 1u;
+ const QuicStreamOffset kStreamOffset = 0u;
+ size_t stream_payload_size =
+ QuicFramer::GetMinStreamFrameSize(
+ GetParam(), kStreamId, kStreamOffset, true) + kStreamDataLength;
+ size_t min_payload_size =
+ std::max(kCongestionFeedbackFrameSize, stream_payload_size);
+ size_t ciphertext_size = NullEncrypter().GetCiphertextSize(min_payload_size);
+ // TODO(satyashekhar): Fix this when versioning is implemented.
+ client_->options()->max_packet_length =
+ GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP) +
+ ciphertext_size;
+ client_->options()->random_reorder = true;
+
+ // Make sure our request is too large to fit in one packet.
+ EXPECT_GT(strlen(kLargeRequest), min_payload_size);
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest(kLargeRequest));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+}
+
+TEST_P(EndToEndTest, PostMissingBytes) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+
+ // Add a content length header with no body.
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddHeader("content-length", "3");
+ request.set_skip_message_validation(true);
+
+ // This should be detected as stream fin without complete request,
+ // triggering an error response.
+ client_->SendCustomSynchronousRequest(request);
+ EXPECT_EQ("bad", client_->response_body());
+ EXPECT_EQ(500u, client_->response_headers()->parsed_response_code());
+}
+
+TEST_P(EndToEndTest, LargePost) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ // Connect with lower fake packet loss than we'd like to test. Until
+ // b/10126687 is fixed, losing handshake packets is pretty brutal.
+ // FLAGS_fake_packet_loss_percentage = 5;
+ ASSERT_TRUE(Initialize());
+
+ // Wait for the server SHLO before upping the packet loss.
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+ // FLAGS_fake_packet_loss_percentage = 30;
+
+ string body;
+ GenerateBody(&body, 10240);
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddBody(body, true);
+
+ EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
+}
+
+// TODO(ianswett): Enable once b/9295090 is fixed.
+TEST_P(EndToEndTest, DISABLED_LargePostFEC) {
+ // FLAGS_fake_packet_loss_percentage = 30;
+ ASSERT_TRUE(Initialize());
+ client_->options()->max_packets_per_fec_group = 6;
+
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ // FLAGS_fake_packet_loss_percentage = 30;
+ ASSERT_TRUE(Initialize());
+ client_->options()->max_packets_per_fec_group = 6;
+
+ string body;
+ GenerateBody(&body, 10240);
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddBody(body, true);
+
+ EXPECT_EQ(kFooResponseBody, client_->SendCustomSynchronousRequest(request));
+}
+
+/*TEST_P(EndToEndTest, PacketTooLarge) {
+ FLAGS_quic_allow_oversized_packets_for_test = true;
+ ASSERT_TRUE(Initialize());
+
+ string body;
+ GenerateBody(&body, kMaxPacketSize);
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddBody(body, true);
+ client_->options()->max_packet_length = 20480;
+
+ EXPECT_EQ("", client_->SendCustomSynchronousRequest(request));
+ EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
+ EXPECT_EQ(QUIC_PACKET_TOO_LARGE, client_->connection_error());
+}*/
+
+TEST_P(EndToEndTest, InvalidStream) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+
+ string body;
+ GenerateBody(&body, kMaxPacketSize);
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddBody(body, true);
+ // Force the client to write with a stream ID belonging to a nonexistent
+ // server-side stream.
+ QuicSessionPeer::SetNextStreamId(client_->client()->session(), 2);
+
+ client_->SendCustomSynchronousRequest(request);
+// EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
+ EXPECT_EQ(QUIC_PACKET_FOR_NONEXISTENT_STREAM, client_->connection_error());
+}
+
+// TODO(rch): this test seems to cause net_unittests timeouts :|
+TEST_P(EndToEndTest, DISABLED_MultipleTermination) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+ scoped_ptr<QuicTestClient> client2(CreateQuicClient());
+
+ HTTPMessage request(HttpConstants::HTTP_1_1,
+ HttpConstants::POST, "/foo");
+ request.AddHeader("content-length", "3");
+ request.set_has_complete_message(false);
+
+ // Set the offset so we won't frame. Otherwise when we pick up termination
+ // before HTTP framing is complete, we send an error and close the stream,
+ // and the second write is picked up as writing on a closed stream.
+ QuicReliableClientStream* stream = client_->GetOrCreateStream();
+ ASSERT_TRUE(stream != NULL);
+ ReliableQuicStreamPeer::SetStreamBytesWritten(3, stream);
+
+ client_->SendData("bar", true);
+
+ // By default the stream protects itself from writes after terminte is set.
+ // Override this to test the server handling buggy clients.
+ ReliableQuicStreamPeer::SetWriteSideClosed(
+ false, client_->GetOrCreateStream());
+
+#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST)
+#if !defined(DCHECK_ALWAYS_ON)
+ EXPECT_DEBUG_DEATH({
+ client_->SendData("eep", true);
+ client_->WaitForResponse();
+ EXPECT_EQ(QUIC_MULTIPLE_TERMINATION_OFFSETS, client_->stream_error());
+ },
+ "Check failed: !fin_buffered_");
+#else
+ EXPECT_DEATH({
+ client_->SendData("eep", true);
+ client_->WaitForResponse();
+ EXPECT_EQ(QUIC_MULTIPLE_TERMINATION_OFFSETS, client_->stream_error());
+ },
+ "Check failed: !fin_buffered_");
+#endif
+#endif
+}
+
+TEST_P(EndToEndTest, Timeout) {
+ client_config_.set_idle_connection_state_lifetime(
+ QuicTime::Delta::FromMicroseconds(500),
+ QuicTime::Delta::FromMicroseconds(500));
+ // Note: we do NOT ASSERT_TRUE: we may time out during initial handshake:
+ // that's enough to validate timeout in this case.
+ Initialize();
+ while (client_->client()->connected()) {
+ client_->client()->WaitForEvents();
+ }
+}
+
+TEST_P(EndToEndTest, LimitMaxOpenStreams) {
+ // Server limits the number of max streams to 2.
+ server_config_.set_max_streams_per_connection(2, 2);
+ // Client tries to negotiate for 10.
+ client_config_.set_max_streams_per_connection(10, 5);
+
+ ASSERT_TRUE(Initialize());
+ client_->client()->WaitForCryptoHandshakeConfirmed();
+ QuicConfig* client_negotiated_config = client_->client()->session()->config();
+ EXPECT_EQ(2u, client_negotiated_config->max_streams_per_connection());
+}
+
+TEST_P(EndToEndTest, ResetConnection) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+ client_->ResetConnection();
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+}
+
+class WrongAddressWriter : public QuicPacketWriter {
+ public:
+ explicit WrongAddressWriter(int fd) : fd_(fd) {
+ IPAddressNumber ip;
+ CHECK(net::ParseIPLiteralToNumber("127.0.0.2", &ip));
+ self_address_ = IPEndPoint(ip, 0);
+ }
+
+ virtual int WritePacket(const char* buffer, size_t buf_len,
+ const IPAddressNumber& real_self_address,
+ const IPEndPoint& peer_address,
+ QuicBlockedWriterInterface* blocked_writer,
+ int* error) OVERRIDE {
+ return QuicSocketUtils::WritePacket(fd_, buffer, buf_len,
+ self_address_.address(), peer_address,
+ error);
+ }
+
+ IPEndPoint self_address_;
+ int fd_;
+};
+
+TEST_P(EndToEndTest, ConnectionMigration) {
+ // TODO(rtenneti): Delete this when NSS is supported.
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ ASSERT_TRUE(Initialize());
+
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+ EXPECT_EQ(200u, client_->response_headers()->parsed_response_code());
+
+ WrongAddressWriter writer(QuicClientPeer::GetFd(client_->client()));
+ QuicEpollConnectionHelper* helper =
+ reinterpret_cast<QuicEpollConnectionHelper*>(
+ QuicConnectionPeer::GetHelper(
+ client_->client()->session()->connection()));
+ QuicEpollConnectionHelperPeer::SetWriter(helper, &writer);
+
+ client_->SendSynchronousRequest("/bar");
+ QuicEpollConnectionHelperPeer::SetWriter(helper, NULL);
+
+ EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
+ EXPECT_EQ(QUIC_ERROR_MIGRATING_ADDRESS, client_->connection_error());
+}
+
+} // namespace
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_client.cc b/chromium/net/tools/quic/quic_client.cc
new file mode 100644
index 00000000000..86fa6c48b75
--- /dev/null
+++ b/chromium/net/tools/quic/quic_client.cc
@@ -0,0 +1,289 @@
+// 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/tools/quic/quic_client.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_data_reader.h"
+#include "net/quic/quic_protocol.h"
+#include "net/tools/flip_server/balsa_headers.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
+#include "net/tools/quic/quic_reliable_client_stream.h"
+#include "net/tools/quic/quic_socket_utils.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+namespace net {
+namespace tools {
+
+const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
+
+QuicClient::QuicClient(IPEndPoint server_address,
+ const string& server_hostname,
+ const QuicVersion version)
+ : server_address_(server_address),
+ server_hostname_(server_hostname),
+ local_port_(0),
+ fd_(-1),
+ initialized_(false),
+ packets_dropped_(0),
+ overflow_supported_(false),
+ version_(version) {
+ config_.SetDefaults();
+}
+
+QuicClient::QuicClient(IPEndPoint server_address,
+ const string& server_hostname,
+ const QuicConfig& config,
+ const QuicVersion version)
+ : server_address_(server_address),
+ server_hostname_(server_hostname),
+ config_(config),
+ local_port_(0),
+ fd_(-1),
+ initialized_(false),
+ packets_dropped_(0),
+ overflow_supported_(false),
+ version_(version) {
+}
+
+QuicClient::~QuicClient() {
+ if (connected()) {
+ session()->connection()->SendConnectionClosePacket(
+ QUIC_PEER_GOING_AWAY, "");
+ }
+}
+
+bool QuicClient::Initialize() {
+ DCHECK(!initialized_);
+
+ epoll_server_.set_timeout_in_us(50 * 1000);
+ crypto_config_.SetDefaults();
+
+ int address_family = server_address_.GetSockAddrFamily();
+ fd_ = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
+ if (fd_ < 0) {
+ LOG(ERROR) << "CreateSocket() failed: " << strerror(errno);
+ return false;
+ }
+
+ int get_overflow = 1;
+ int rc = setsockopt(fd_, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow,
+ sizeof(get_overflow));
+ if (rc < 0) {
+ DLOG(WARNING) << "Socket overflow detection not supported";
+ } else {
+ overflow_supported_ = true;
+ }
+
+ int get_local_ip = 1;
+ if (address_family == AF_INET) {
+ rc = setsockopt(fd_, IPPROTO_IP, IP_PKTINFO,
+ &get_local_ip, sizeof(get_local_ip));
+ } else {
+ rc = setsockopt(fd_, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &get_local_ip, sizeof(get_local_ip));
+ }
+
+ if (rc < 0) {
+ LOG(ERROR) << "IP detection not supported" << strerror(errno);
+ return false;
+ }
+
+ if (bind_to_address_.size() != 0) {
+ client_address_ = IPEndPoint(bind_to_address_, local_port_);
+ } else if (address_family == AF_INET) {
+ IPAddressNumber any4;
+ CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4));
+ client_address_ = IPEndPoint(any4, local_port_);
+ } else {
+ IPAddressNumber any6;
+ CHECK(net::ParseIPLiteralToNumber("::", &any6));
+ client_address_ = IPEndPoint(any6, local_port_);
+ }
+
+ sockaddr_storage raw_addr;
+ socklen_t raw_addr_len = sizeof(raw_addr);
+ CHECK(client_address_.ToSockAddr(reinterpret_cast<sockaddr*>(&raw_addr),
+ &raw_addr_len));
+ rc = bind(fd_,
+ reinterpret_cast<const sockaddr*>(&raw_addr),
+ sizeof(raw_addr));
+ if (rc < 0) {
+ LOG(ERROR) << "Bind failed: " << strerror(errno);
+ return false;
+ }
+
+ SockaddrStorage storage;
+ if (getsockname(fd_, storage.addr, &storage.addr_len) != 0 ||
+ !client_address_.FromSockAddr(storage.addr, storage.addr_len)) {
+ LOG(ERROR) << "Unable to get self address. Error: " << strerror(errno);
+ }
+
+ epoll_server_.RegisterFD(fd_, this, kEpollFlags);
+ initialized_ = true;
+ return true;
+}
+
+bool QuicClient::Connect() {
+ if (!StartConnect()) {
+ return false;
+ }
+ while (EncryptionBeingEstablished()) {
+ WaitForEvents();
+ }
+ return session_->connection()->connected();
+}
+
+bool QuicClient::StartConnect() {
+ DCHECK(!connected() && initialized_);
+
+ QuicGuid guid = QuicRandom::GetInstance()->RandUint64();
+ session_.reset(new QuicClientSession(
+ server_hostname_,
+ config_,
+ new QuicConnection(guid, server_address_,
+ CreateQuicConnectionHelper(), false,
+ version_),
+ &crypto_config_));
+ return session_->CryptoConnect();
+}
+
+bool QuicClient::EncryptionBeingEstablished() {
+ return !session_->IsEncryptionEstablished() &&
+ session_->connection()->connected();
+}
+
+void QuicClient::Disconnect() {
+ DCHECK(connected());
+
+ session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
+ epoll_server_.UnregisterFD(fd_);
+ close(fd_);
+ fd_ = -1;
+ initialized_ = false;
+}
+
+void QuicClient::SendRequestsAndWaitForResponse(
+ const CommandLine::StringVector& args) {
+ for (uint32_t i = 0; i < args.size(); i++) {
+ BalsaHeaders headers;
+ headers.SetRequestFirstlineFromStringPieces("GET", args[i], "HTTP/1.1");
+ CreateReliableClientStream()->SendRequest(headers, "", true);
+ }
+
+ while (WaitForEvents()) { }
+}
+
+QuicReliableClientStream* QuicClient::CreateReliableClientStream() {
+ if (!connected()) {
+ return NULL;
+ }
+
+ return session_->CreateOutgoingReliableStream();
+}
+
+void QuicClient::WaitForStreamToClose(QuicStreamId id) {
+ DCHECK(connected());
+
+ while (!session_->IsClosedStream(id)) {
+ epoll_server_.WaitForEventsAndExecuteCallbacks();
+ }
+}
+
+void QuicClient::WaitForCryptoHandshakeConfirmed() {
+ DCHECK(connected());
+
+ while (!session_->IsCryptoHandshakeConfirmed()) {
+ epoll_server_.WaitForEventsAndExecuteCallbacks();
+ }
+}
+
+bool QuicClient::WaitForEvents() {
+ DCHECK(connected());
+
+ epoll_server_.WaitForEventsAndExecuteCallbacks();
+ return session_->num_active_requests() != 0;
+}
+
+void QuicClient::OnEvent(int fd, EpollEvent* event) {
+ DCHECK_EQ(fd, fd_);
+
+ if (event->in_events & EPOLLIN) {
+ while (connected() && ReadAndProcessPacket()) {
+ }
+ }
+ if (connected() && (event->in_events & EPOLLOUT)) {
+ session_->connection()->OnCanWrite();
+ }
+ if (event->in_events & EPOLLERR) {
+ DLOG(INFO) << "Epollerr";
+ }
+}
+
+QuicPacketCreator::Options* QuicClient::options() {
+ if (session() == NULL) {
+ return NULL;
+ }
+ return session_->options();
+}
+
+bool QuicClient::connected() const {
+ return session_.get() && session_->connection() &&
+ session_->connection()->connected();
+}
+
+QuicEpollConnectionHelper* QuicClient::CreateQuicConnectionHelper() {
+ return new QuicEpollConnectionHelper(fd_, &epoll_server_);
+}
+
+bool QuicClient::ReadAndProcessPacket() {
+ // Allocate some extra space so we can send an error if the server goes over
+ // the limit.
+ char buf[2 * kMaxPacketSize];
+
+ IPEndPoint server_address;
+ IPAddressNumber client_ip;
+
+ int bytes_read = QuicSocketUtils::ReadPacket(
+ fd_, buf, arraysize(buf), overflow_supported_ ? &packets_dropped_ : NULL,
+ &client_ip, &server_address);
+
+ if (bytes_read < 0) {
+ return false;
+ }
+
+ QuicEncryptedPacket packet(buf, bytes_read, false);
+ QuicGuid our_guid = session_->connection()->guid();
+ QuicGuid packet_guid;
+
+ if (!QuicFramer::ReadGuidFromPacket(packet, &packet_guid)) {
+ DLOG(INFO) << "Could not read GUID from packet";
+ return true;
+ }
+ if (packet_guid != our_guid) {
+ DLOG(INFO) << "Ignoring packet from unexpected GUID: "
+ << packet_guid << " instead of " << our_guid;
+ return true;
+ }
+
+ IPEndPoint client_address(client_ip, client_address_.port());
+ session_->connection()->ProcessUdpPacket(
+ client_address, server_address, packet);
+ return true;
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_client.h b/chromium/net/tools/quic/quic_client.h
new file mode 100644
index 00000000000..ca20a8d2158
--- /dev/null
+++ b/chromium/net/tools/quic/quic_client.h
@@ -0,0 +1,203 @@
+// 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.
+//
+// A toy client, which connects to a specified port and sends QUIC
+// request to that endpoint.
+
+#ifndef NET_TOOLS_QUIC_QUIC_CLIENT_H_
+#define NET_TOOLS_QUIC_QUIC_CLIENT_H_
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_packet_creator.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_reliable_client_stream.h"
+
+namespace net {
+
+class ProofVerifier;
+
+namespace tools {
+
+class QuicEpollConnectionHelper;
+
+namespace test {
+class QuicClientPeer;
+} // namespace test
+
+class QuicClient : public EpollCallbackInterface {
+ public:
+ QuicClient(IPEndPoint server_address, const std::string& server_hostname,
+ const QuicVersion version);
+ QuicClient(IPEndPoint server_address,
+ const std::string& server_hostname,
+ const QuicConfig& config,
+ const QuicVersion version);
+
+ virtual ~QuicClient();
+
+ // Initializes the client to create a connection. Should be called exactly
+ // once before calling StartConnect or Connect. Returns true if the
+ // initialization succeeds, false otherwise.
+ bool Initialize();
+
+ // "Connect" to the QUIC server, including performing synchronous crypto
+ // handshake.
+ bool Connect();
+
+ // Start the crypto handshake. This can be done in place of the synchronous
+ // Connect(), but callers are responsible for making sure the crypto handshake
+ // completes.
+ bool StartConnect();
+
+ // Returns true if the crypto handshake has yet to establish encryption.
+ // Returns false if encryption is active (even if the server hasn't confirmed
+ // the handshake) or if the connection has been closed.
+ bool EncryptionBeingEstablished();
+
+ // Disconnects from the QUIC server.
+ void Disconnect();
+
+ // Sends a request simple GET for each URL in arg, and then waits for
+ // each to complete.
+ void SendRequestsAndWaitForResponse(const CommandLine::StringVector& args);
+
+ // Returns a newly created CreateReliableClientStream, owned by the
+ // QuicClient.
+ QuicReliableClientStream* CreateReliableClientStream();
+
+ // Wait for events until the stream with the given ID is closed.
+ void WaitForStreamToClose(QuicStreamId id);
+
+ // Wait for events until the handshake is confirmed.
+ void WaitForCryptoHandshakeConfirmed();
+
+ // Wait up to 50ms, and handle any events which occur.
+ // Returns true if there are any outstanding requests.
+ bool WaitForEvents();
+
+ // From EpollCallbackInterface
+ virtual void OnRegistration(
+ EpollServer* eps, int fd, int event_mask) OVERRIDE {}
+ virtual void OnModification(int fd, int event_mask) OVERRIDE {}
+ virtual void OnEvent(int fd, EpollEvent* event) OVERRIDE;
+ // |fd_| can be unregistered without the client being disconnected. This
+ // happens in b3m QuicProber where we unregister |fd_| to feed in events to
+ // the client from the SelectServer.
+ virtual void OnUnregistration(int fd, bool replaced) OVERRIDE {}
+ virtual void OnShutdown(EpollServer* eps, int fd) OVERRIDE {}
+
+ QuicPacketCreator::Options* options();
+
+ QuicClientSession* session() { return session_.get(); }
+
+ bool connected() const;
+
+ int packets_dropped() { return packets_dropped_; }
+
+ void set_bind_to_address(IPAddressNumber address) {
+ bind_to_address_ = address;
+ }
+
+ IPAddressNumber bind_to_address() const { return bind_to_address_; }
+
+ void set_local_port(int local_port) { local_port_ = local_port; }
+
+ int local_port() { return local_port_; }
+
+ const IPEndPoint& server_address() const { return server_address_; }
+
+ const IPEndPoint& client_address() const { return client_address_; }
+
+ EpollServer* epoll_server() { return &epoll_server_; }
+
+ int fd() { return fd_; }
+
+ // This should only be set before the initial Connect()
+ void set_server_hostname(const string& hostname) {
+ server_hostname_ = hostname;
+ }
+
+ // SetProofVerifier sets the ProofVerifier that will be used to verify the
+ // server's certificate and takes ownership of |verifier|.
+ void SetProofVerifier(ProofVerifier* verifier) {
+ // TODO(rtenneti): We should set ProofVerifier in QuicClientSession.
+ crypto_config_.SetProofVerifier(verifier);
+ }
+
+ // SetChannelIDSigner sets a ChannelIDSigner that will be called when the
+ // server supports channel IDs to sign a message proving possession of the
+ // given ChannelID. This object takes ownership of |signer|.
+ void SetChannelIDSigner(ChannelIDSigner* signer) {
+ crypto_config_.SetChannelIDSigner(signer);
+ }
+
+ protected:
+ virtual QuicEpollConnectionHelper* CreateQuicConnectionHelper();
+
+ private:
+ friend class net::tools::test::QuicClientPeer;
+
+ // Read a UDP packet and hand it to the framer.
+ bool ReadAndProcessPacket();
+
+ // Set of streams created (and owned) by this client
+ base::hash_set<QuicReliableClientStream*> streams_;
+
+ // Address of the server.
+ const IPEndPoint server_address_;
+
+ // Hostname of the server. This may be a DNS name or an IP address literal.
+ std::string server_hostname_;
+
+ // config_ and crypto_config_ contain configuration and cached state about
+ // servers.
+ QuicConfig config_;
+ QuicCryptoClientConfig crypto_config_;
+
+ // Address of the client if the client is connected to the server.
+ IPEndPoint client_address_;
+
+ // If initialized, the address to bind to.
+ IPAddressNumber bind_to_address_;
+ // Local port to bind to. Initialize to 0.
+ int local_port_;
+
+ // Session which manages streams.
+ scoped_ptr<QuicClientSession> session_;
+ // Listens for events on the client socket.
+ EpollServer epoll_server_;
+ // UDP socket.
+ int fd_;
+
+ // Tracks if the client is initialized to connect.
+ bool initialized_;
+
+ // If overflow_supported_ is true, this will be the number of packets dropped
+ // during the lifetime of the server. This may overflow if enough packets
+ // are dropped.
+ int packets_dropped_;
+
+ // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped
+ // because the socket would otherwise overflow.
+ bool overflow_supported_;
+
+ // Which QUIC version does this client talk?
+ QuicVersion version_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicClient);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_CLIENT_H_
diff --git a/chromium/net/tools/quic/quic_client_bin.cc b/chromium/net/tools/quic/quic_client_bin.cc
new file mode 100644
index 00000000000..e13bea5e8c1
--- /dev/null
+++ b/chromium/net/tools/quic/quic_client_bin.cc
@@ -0,0 +1,56 @@
+// 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.
+
+// A binary wrapper for QuicClient. Connects to --hostname or --address on
+// --port and requests URLs specified on the command line.
+//
+// For example:
+// quic_client --port=6122 /index.html /favicon.ico
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_protocol.h"
+#include "net/tools/quic/quic_client.h"
+
+int32 FLAGS_port = 6121;
+std::string FLAGS_address = "127.0.0.1";
+std::string FLAGS_hostname = "localhost";
+
+int main(int argc, char *argv[]) {
+ CommandLine::Init(argc, argv);
+ CommandLine* line = CommandLine::ForCurrentProcess();
+ if (line->HasSwitch("port")) {
+ int port;
+ if (base::StringToInt(line->GetSwitchValueASCII("port"), &port)) {
+ FLAGS_port = port;
+ }
+ }
+ if (line->HasSwitch("address")) {
+ FLAGS_address = line->GetSwitchValueASCII("address");
+ }
+ if (line->HasSwitch("hostname")) {
+ FLAGS_hostname = line->GetSwitchValueASCII("hostname");
+ }
+ LOG(INFO) << "server port: " << FLAGS_port
+ << " address: " << FLAGS_address
+ << " hostname: " << FLAGS_hostname;
+
+ base::AtExitManager exit_manager;
+
+ net::IPAddressNumber addr;
+ CHECK(net::ParseIPLiteralToNumber(FLAGS_address, &addr));
+ // TODO(rjshade): Set version on command line.
+ net::tools::QuicClient client(
+ net::IPEndPoint(addr, FLAGS_port), FLAGS_hostname, net::QuicVersionMax());
+
+ client.Initialize();
+
+ if (!client.Connect()) return 1;
+
+ client.SendRequestsAndWaitForResponse(line->GetArgs());
+ return 0;
+}
diff --git a/chromium/net/tools/quic/quic_client_session.cc b/chromium/net/tools/quic/quic_client_session.cc
new file mode 100644
index 00000000000..c41df6702b5
--- /dev/null
+++ b/chromium/net/tools/quic/quic_client_session.cc
@@ -0,0 +1,65 @@
+// 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/tools/quic/quic_client_session.h"
+
+#include "base/logging.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/tools/quic/quic_reliable_client_stream.h"
+#include "net/tools/quic/quic_spdy_client_stream.h"
+
+using std::string;
+
+namespace net {
+namespace tools {
+
+QuicClientSession::QuicClientSession(
+ const string& server_hostname,
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicCryptoClientConfig* crypto_config)
+ : QuicSession(connection, config, false),
+ crypto_stream_(server_hostname, this, crypto_config) {
+}
+
+QuicClientSession::~QuicClientSession() {
+}
+
+QuicReliableClientStream* QuicClientSession::CreateOutgoingReliableStream() {
+ if (!crypto_stream_.encryption_established()) {
+ DLOG(INFO) << "Encryption not active so no outgoing stream created.";
+ return NULL;
+ }
+ if (GetNumOpenStreams() >= get_max_open_streams()) {
+ DLOG(INFO) << "Failed to create a new outgoing stream. "
+ << "Already " << GetNumOpenStreams() << " open.";
+ return NULL;
+ }
+ if (goaway_received()) {
+ DLOG(INFO) << "Failed to create a new outgoing stream. "
+ << "Already received goaway.";
+ return NULL;
+ }
+ QuicReliableClientStream* stream
+ = new QuicSpdyClientStream(GetNextStreamId(), this);
+ ActivateStream(stream);
+ return stream;
+}
+
+QuicCryptoClientStream* QuicClientSession::GetCryptoStream() {
+ return &crypto_stream_;
+}
+
+bool QuicClientSession::CryptoConnect() {
+ return crypto_stream_.CryptoConnect();
+}
+
+ReliableQuicStream* QuicClientSession::CreateIncomingReliableStream(
+ QuicStreamId id) {
+ DLOG(ERROR) << "Server push not supported";
+ return NULL;
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_client_session.h b/chromium/net/tools/quic/quic_client_session.h
new file mode 100644
index 00000000000..f51aeeaff1b
--- /dev/null
+++ b/chromium/net/tools/quic/quic_client_session.h
@@ -0,0 +1,56 @@
+// 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.
+//
+// A client specific QuicSession subclass.
+
+#ifndef NET_TOOLS_QUIC_QUIC_CLIENT_SESSION_H_
+#define NET_TOOLS_QUIC_QUIC_CLIENT_SESSION_H_
+
+#include <string>
+
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_session.h"
+#include "net/tools/quic/quic_reliable_client_stream.h"
+
+namespace net {
+
+class QuicConnection;
+class ReliableQuicStream;
+
+namespace tools {
+
+class QuicReliableClientStream;
+
+class QuicClientSession : public QuicSession {
+ public:
+ QuicClientSession(const std::string& server_hostname,
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicCryptoClientConfig* crypto_config);
+ virtual ~QuicClientSession();
+
+ // QuicSession methods:
+ virtual QuicReliableClientStream* CreateOutgoingReliableStream() OVERRIDE;
+ virtual QuicCryptoClientStream* GetCryptoStream() OVERRIDE;
+
+ // Performs a crypto handshake with the server. Returns true if the crypto
+ // handshake is started successfully.
+ bool CryptoConnect();
+
+ protected:
+ // QuicSession methods:
+ virtual ReliableQuicStream* CreateIncomingReliableStream(
+ QuicStreamId id) OVERRIDE;
+
+ private:
+ QuicCryptoClientStream crypto_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicClientSession);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_CLIENT_SESSION_H_
diff --git a/chromium/net/tools/quic/quic_client_session_test.cc b/chromium/net/tools/quic/quic_client_session_test.cc
new file mode 100644
index 00000000000..1b1ece6597e
--- /dev/null
+++ b/chromium/net/tools/quic/quic_client_session_test.cc
@@ -0,0 +1,96 @@
+// 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/tools/quic/quic_client_session.h"
+
+#include <vector>
+
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/tools/quic/quic_reliable_client_stream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using net::test::CryptoTestUtils;
+using net::test::PacketSavingConnection;
+
+namespace net {
+namespace tools {
+namespace test {
+namespace {
+
+const char kServerHostname[] = "www.example.com";
+
+class ToolsQuicClientSessionTest : public ::testing::Test {
+ protected:
+ ToolsQuicClientSessionTest()
+ : guid_(1),
+ connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)) {
+ crypto_config_.SetDefaults();
+ session_.reset(new QuicClientSession(kServerHostname, QuicConfig(),
+ connection_, &crypto_config_));
+ session_->config()->SetDefaults();
+ }
+
+ void CompleteCryptoHandshake() {
+ ASSERT_TRUE(session_->CryptoConnect());
+ CryptoTestUtils::HandshakeWithFakeServer(
+ connection_, session_->GetCryptoStream());
+ }
+
+ QuicGuid guid_;
+ PacketSavingConnection* connection_;
+ scoped_ptr<QuicClientSession> session_;
+ QuicCryptoClientConfig crypto_config_;
+};
+
+TEST_F(ToolsQuicClientSessionTest, CryptoConnect) {
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+ CompleteCryptoHandshake();
+}
+
+TEST_F(ToolsQuicClientSessionTest, MaxNumStreams) {
+ session_->config()->set_max_streams_per_connection(1, 1);
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+ // FLAGS_max_streams_per_connection = 1;
+ // Initialize crypto before the client session will create a stream.
+ CompleteCryptoHandshake();
+
+ QuicReliableClientStream* stream =
+ session_->CreateOutgoingReliableStream();
+ ASSERT_TRUE(stream);
+ EXPECT_FALSE(session_->CreateOutgoingReliableStream());
+
+ // Close a stream and ensure I can now open a new one.
+ session_->CloseStream(stream->id());
+ stream = session_->CreateOutgoingReliableStream();
+ EXPECT_TRUE(stream);
+}
+
+TEST_F(ToolsQuicClientSessionTest, GoAwayReceived) {
+ if (!Aes128Gcm12Encrypter::IsSupported()) {
+ LOG(INFO) << "AES GCM not supported. Test skipped.";
+ return;
+ }
+
+ CompleteCryptoHandshake();
+
+ // After receiving a GoAway, I should no longer be able to create outgoing
+ // streams.
+ session_->OnGoAway(QuicGoAwayFrame(QUIC_PEER_GOING_AWAY, 1u, "Going away."));
+ EXPECT_EQ(NULL, session_->CreateOutgoingReliableStream());
+}
+
+} // namespace
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_dispatcher.cc b/chromium/net/tools/quic/quic_dispatcher.cc
new file mode 100644
index 00000000000..68691f7dc58
--- /dev/null
+++ b/chromium/net/tools/quic/quic_dispatcher.cc
@@ -0,0 +1,203 @@
+// 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/tools/quic/quic_dispatcher.h"
+
+#include <errno.h>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_utils.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
+#include "net/tools/quic/quic_socket_utils.h"
+
+namespace net {
+namespace tools {
+
+using std::make_pair;
+
+class DeleteSessionsAlarm : public EpollAlarm {
+ public:
+ explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher) {
+ }
+
+ virtual int64 OnAlarm() OVERRIDE {
+ EpollAlarm::OnAlarm();
+ dispatcher_->DeleteSessions();
+ return 0;
+ }
+
+ private:
+ QuicDispatcher* dispatcher_;
+};
+
+QuicDispatcher::QuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ int fd,
+ EpollServer* epoll_server)
+ : config_(config),
+ crypto_config_(crypto_config),
+ time_wait_list_manager_(
+ new QuicTimeWaitListManager(this, epoll_server)),
+ delete_sessions_alarm_(new DeleteSessionsAlarm(this)),
+ epoll_server_(epoll_server),
+ fd_(fd),
+ write_blocked_(false) {
+}
+
+QuicDispatcher::~QuicDispatcher() {
+ STLDeleteValues(&session_map_);
+ STLDeleteElements(&closed_session_list_);
+}
+
+int QuicDispatcher::WritePacket(const char* buffer, size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address,
+ QuicBlockedWriterInterface* writer,
+ int* error) {
+ if (write_blocked_) {
+ write_blocked_list_.AddBlockedObject(writer);
+ *error = EAGAIN;
+ return -1;
+ }
+
+ int rc = QuicSocketUtils::WritePacket(fd_, buffer, buf_len,
+ self_address, peer_address,
+ error);
+ if (rc == -1 && (*error == EWOULDBLOCK || *error == EAGAIN)) {
+ write_blocked_list_.AddBlockedObject(writer);
+ write_blocked_ = true;
+ }
+ return rc;
+}
+
+void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicGuid guid,
+ const QuicEncryptedPacket& packet) {
+ QuicSession* session;
+
+ SessionMap::iterator it = session_map_.find(guid);
+ if (it == session_map_.end()) {
+ if (time_wait_list_manager_->IsGuidInTimeWait(guid)) {
+ time_wait_list_manager_->ProcessPacket(server_address,
+ client_address,
+ guid,
+ packet);
+ return;
+ }
+ session = CreateQuicSession(guid, client_address, fd_, epoll_server_);
+
+ if (session == NULL) {
+ DLOG(INFO) << "Failed to create session for " << guid;
+ // Add this guid fo the time-wait state, to safely nack future packets.
+ // We don't know the version here, so assume latest.
+ time_wait_list_manager_->AddGuidToTimeWait(guid, QuicVersionMax());
+ time_wait_list_manager_->ProcessPacket(server_address,
+ client_address,
+ guid,
+ packet);
+ return;
+ }
+ DLOG(INFO) << "Created new session for " << guid;
+ session_map_.insert(make_pair(guid, session));
+ } else {
+ session = it->second;
+ }
+
+ session->connection()->ProcessUdpPacket(
+ server_address, client_address, packet);
+}
+
+void QuicDispatcher::CleanUpSession(SessionMap::iterator it) {
+ QuicSession* session = it->second;
+ write_blocked_list_.RemoveBlockedObject(session->connection());
+ time_wait_list_manager_->AddGuidToTimeWait(it->first,
+ session->connection()->version());
+ session_map_.erase(it);
+}
+
+void QuicDispatcher::DeleteSessions() {
+ STLDeleteElements(&closed_session_list_);
+}
+
+bool QuicDispatcher::OnCanWrite() {
+ // We got an EPOLLOUT: the socket should not be blocked.
+ write_blocked_ = false;
+
+ // Give each writer one attempt to write.
+ int num_writers = write_blocked_list_.NumObjects();
+ for (int i = 0; i < num_writers; ++i) {
+ if (write_blocked_list_.IsEmpty()) {
+ break;
+ }
+ QuicBlockedWriterInterface* writer =
+ write_blocked_list_.GetNextBlockedObject();
+ bool can_write_more = writer->OnCanWrite();
+ if (write_blocked_) {
+ // We were unable to write. Wait for the next EPOLLOUT.
+ // In this case, the session would have been added to the blocked list
+ // up in WritePacket.
+ return false;
+ }
+ // The socket is not blocked but the writer has ceded work. Add it to the
+ // end of the list.
+ if (can_write_more) {
+ write_blocked_list_.AddBlockedObject(writer);
+ }
+ }
+
+ // We're not write blocked. Return true if there's more work to do.
+ return !write_blocked_list_.IsEmpty();
+}
+
+void QuicDispatcher::Shutdown() {
+ while (!session_map_.empty()) {
+ QuicSession* session = session_map_.begin()->second;
+ session->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
+ // Validate that the session removes itself from the session map on close.
+ DCHECK(session_map_.empty() || session_map_.begin()->second != session);
+ }
+ DeleteSessions();
+}
+
+void QuicDispatcher::OnConnectionClose(QuicGuid guid, QuicErrorCode error) {
+ SessionMap::iterator it = session_map_.find(guid);
+ if (it == session_map_.end()) {
+ LOG(DFATAL) << "GUID " << guid << " does not exist in the session map. "
+ << "Error: " << QuicUtils::ErrorToString(error);
+ return;
+ }
+
+ DLOG_IF(INFO, error != QUIC_NO_ERROR) << "Closing connection due to error: "
+ << QuicUtils::ErrorToString(error);
+
+ if (closed_session_list_.empty()) {
+ epoll_server_->RegisterAlarmApproximateDelta(
+ 0, delete_sessions_alarm_.get());
+ }
+ closed_session_list_.push_back(it->second);
+ CleanUpSession(it);
+}
+
+QuicSession* QuicDispatcher::CreateQuicSession(
+ QuicGuid guid,
+ const IPEndPoint& client_address,
+ int fd,
+ EpollServer* epoll_server) {
+ QuicConnectionHelperInterface* helper =
+ new QuicEpollConnectionHelper(this, epoll_server);
+ QuicServerSession* session = new QuicServerSession(
+ config_, new QuicConnection(guid, client_address, helper, true,
+ QuicVersionMax()), this);
+ session->InitializeSession(crypto_config_);
+ return session;
+}
+
+} // namespace tools
+} // namespace net
+
+
diff --git a/chromium/net/tools/quic/quic_dispatcher.h b/chromium/net/tools/quic/quic_dispatcher.h
new file mode 100644
index 00000000000..bbf8d9b4941
--- /dev/null
+++ b/chromium/net/tools/quic/quic_dispatcher.h
@@ -0,0 +1,147 @@
+// 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.
+//
+// A server side dispatcher which dispatches a given client's data to their
+// stream.
+
+#ifndef NET_TOOLS_QUIC_QUIC_DISPATCHER_H_
+#define NET_TOOLS_QUIC_QUIC_DISPATCHER_H_
+
+#include <list>
+
+#include "base/containers/hash_tables.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/blocked_list.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "net/tools/quic/quic_packet_writer.h"
+#include "net/tools/quic/quic_server_session.h"
+#include "net/tools/quic/quic_time_wait_list_manager.h"
+
+#if defined(COMPILER_GCC)
+namespace BASE_HASH_NAMESPACE {
+template<>
+struct hash<net::QuicBlockedWriterInterface*> {
+ std::size_t operator()(
+ const net::QuicBlockedWriterInterface* ptr) const {
+ return hash<size_t>()(reinterpret_cast<size_t>(ptr));
+ }
+};
+}
+#endif
+
+namespace net {
+
+class EpollServer;
+class QuicConfig;
+class QuicCryptoServerConfig;
+class QuicSession;
+
+namespace tools {
+
+namespace test {
+class QuicDispatcherPeer;
+} // namespace test
+
+class DeleteSessionsAlarm;
+class QuicDispatcher : public QuicPacketWriter, public QuicSessionOwner {
+ public:
+ typedef BlockedList<QuicBlockedWriterInterface*> WriteBlockedList;
+
+ // Due to the way delete_sessions_closure_ is registered, the Dispatcher
+ // must live until epoll_server Shutdown.
+ QuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ int fd,
+ EpollServer* epoll_server);
+ virtual ~QuicDispatcher();
+
+ // QuicPacketWriter
+ virtual int WritePacket(const char* buffer, size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address,
+ QuicBlockedWriterInterface* writer,
+ int* error) OVERRIDE;
+
+ virtual void ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicGuid guid,
+ const QuicEncryptedPacket& packet);
+
+ // Called when the underyling connection becomes writable to allow
+ // queued writes to happen.
+ //
+ // Returns true if more writes are possible, false otherwise.
+ virtual bool OnCanWrite();
+
+ // Sends ConnectionClose frames to all connected clients.
+ void Shutdown();
+
+ // Ensure that the closed connection is cleaned up asynchronously.
+ virtual void OnConnectionClose(QuicGuid guid, QuicErrorCode error) OVERRIDE;
+
+ int fd() { return fd_; }
+ void set_fd(int fd) { fd_ = fd; }
+
+ typedef base::hash_map<QuicGuid, QuicSession*> SessionMap;
+
+ virtual QuicSession* CreateQuicSession(
+ QuicGuid guid,
+ const IPEndPoint& client_address,
+ int fd,
+ EpollServer* epoll_server);
+
+ // Deletes all sessions on the closed session list and clears the list.
+ void DeleteSessions();
+
+ const SessionMap& session_map() const { return session_map_; }
+
+ WriteBlockedList* write_blocked_list() { return &write_blocked_list_; }
+
+ protected:
+ const QuicConfig& config_;
+ const QuicCryptoServerConfig& crypto_config_;
+
+ QuicTimeWaitListManager* time_wait_list_manager() {
+ return time_wait_list_manager_.get();
+ }
+
+ private:
+ friend class net::tools::test::QuicDispatcherPeer;
+
+ // Removes the session from the session map and write blocked list, and
+ // adds the GUID to the time-wait list.
+ void CleanUpSession(SessionMap::iterator it);
+
+ // The list of connections waiting to write.
+ WriteBlockedList write_blocked_list_;
+
+ SessionMap session_map_;
+
+ // Entity that manages guids in time wait state.
+ scoped_ptr<QuicTimeWaitListManager> time_wait_list_manager_;
+
+ // An alarm which deletes closed sessions.
+ scoped_ptr<DeleteSessionsAlarm> delete_sessions_alarm_;
+
+ // The list of closed but not-yet-deleted sessions.
+ std::list<QuicSession*> closed_session_list_;
+
+ EpollServer* epoll_server_; // Owned by the server.
+
+ // The connection for client-server communication
+ int fd_;
+
+ // True if the session is write blocked due to the socket returning EAGAIN.
+ // False if we have gotten a call to OnCanWrite after the last failed write.
+ bool write_blocked_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDispatcher);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_DISPATCHER_H_
diff --git a/chromium/net/tools/quic/quic_dispatcher_test.cc b/chromium/net/tools/quic/quic_dispatcher_test.cc
new file mode 100644
index 00000000000..059d8334dc0
--- /dev/null
+++ b/chromium/net/tools/quic/quic_dispatcher_test.cc
@@ -0,0 +1,390 @@
+// 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/tools/quic/quic_dispatcher.h"
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_server_config.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "net/tools/quic/quic_time_wait_list_manager.h"
+#include "net/tools/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using net::EpollServer;
+using net::test::MockSession;
+using net::tools::test::MockConnection;
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::InSequence;
+using testing::Return;
+using testing::WithoutArgs;
+
+namespace net {
+namespace tools {
+namespace test {
+class QuicDispatcherPeer {
+ public:
+ static void SetTimeWaitListManager(
+ QuicDispatcher* dispatcher,
+ QuicTimeWaitListManager* time_wait_list_manager) {
+ dispatcher->time_wait_list_manager_.reset(time_wait_list_manager);
+ }
+
+ static void SetWriteBlocked(QuicDispatcher* dispatcher) {
+ dispatcher->write_blocked_ = true;
+ }
+};
+
+namespace {
+
+class TestDispatcher : public QuicDispatcher {
+ public:
+ explicit TestDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ EpollServer* eps)
+ : QuicDispatcher(config, crypto_config, 1, eps) {}
+
+ MOCK_METHOD4(CreateQuicSession, QuicSession*(
+ QuicGuid guid,
+ const IPEndPoint& client_address,
+ int fd,
+ EpollServer* eps));
+ using QuicDispatcher::write_blocked_list;
+};
+
+// A Connection class which unregisters the session from the dispatcher
+// when sending connection close.
+// It'd be slightly more realistic to do this from the Session but it would
+// involve a lot more mocking.
+class MockServerConnection : public MockConnection {
+ public:
+ MockServerConnection(QuicGuid guid,
+ IPEndPoint address,
+ int fd,
+ EpollServer* eps,
+ QuicDispatcher* dispatcher)
+ : MockConnection(guid, address, fd, eps, true),
+ dispatcher_(dispatcher) {
+ }
+ void UnregisterOnConnectionClose() {
+ LOG(ERROR) << "Unregistering " << guid();
+ dispatcher_->OnConnectionClose(guid(), QUIC_NO_ERROR);
+ }
+ private:
+ QuicDispatcher* dispatcher_;
+};
+
+QuicSession* CreateSession(QuicDispatcher* dispatcher,
+ QuicGuid guid,
+ const IPEndPoint& addr,
+ MockSession** session,
+ EpollServer* eps) {
+ MockServerConnection* connection =
+ new MockServerConnection(guid, addr, 0, eps, dispatcher);
+ *session = new MockSession(connection, true);
+ ON_CALL(*connection, SendConnectionClose(_)).WillByDefault(
+ WithoutArgs(Invoke(
+ connection, &MockServerConnection::UnregisterOnConnectionClose)));
+ EXPECT_CALL(*reinterpret_cast<MockConnection*>((*session)->connection()),
+ ProcessUdpPacket(_, addr, _));
+
+ return *session;
+}
+
+class QuicDispatcherTest : public ::testing::Test {
+ public:
+ QuicDispatcherTest()
+ : crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance()),
+ dispatcher_(config_, crypto_config_, &eps_),
+ session1_(NULL),
+ session2_(NULL) {
+ }
+
+ virtual ~QuicDispatcherTest() {}
+
+ MockConnection* connection1() {
+ return reinterpret_cast<MockConnection*>(session1_->connection());
+ }
+
+ MockConnection* connection2() {
+ return reinterpret_cast<MockConnection*>(session2_->connection());
+ }
+
+ void ProcessPacket(IPEndPoint addr,
+ QuicGuid guid,
+ const string& data) {
+ QuicEncryptedPacket packet(data.data(), data.length());
+ dispatcher_.ProcessPacket(IPEndPoint(), addr, guid, packet);
+ }
+
+ void ValidatePacket(const QuicEncryptedPacket& packet) {
+ EXPECT_TRUE(packet.AsStringPiece().find(data_) != StringPiece::npos);
+ }
+
+ IPAddressNumber Loopback4() {
+ net::IPAddressNumber addr;
+ CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &addr));
+ return addr;
+ }
+
+ EpollServer eps_;
+ QuicConfig config_;
+ QuicCryptoServerConfig crypto_config_;
+ TestDispatcher dispatcher_;
+ MockSession* session1_;
+ MockSession* session2_;
+ string data_;
+};
+
+TEST_F(QuicDispatcherTest, ProcessPackets) {
+ IPEndPoint addr(Loopback4(), 1);
+
+ EXPECT_CALL(dispatcher_, CreateQuicSession(1, addr, _, &eps_))
+ .WillOnce(testing::Return(CreateSession(
+ &dispatcher_, 1, addr, &session1_, &eps_)));
+ ProcessPacket(addr, 1, "foo");
+
+ EXPECT_CALL(dispatcher_, CreateQuicSession(2, addr, _, &eps_))
+ .WillOnce(testing::Return(CreateSession(
+ &dispatcher_, 2, addr, &session2_, &eps_)));
+ ProcessPacket(addr, 2, "bar");
+
+ data_ = "eep";
+ EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _)).Times(1).
+ WillOnce(testing::WithArgs<2>(Invoke(
+ this, &QuicDispatcherTest::ValidatePacket)));
+ ProcessPacket(addr, 1, "eep");
+}
+
+TEST_F(QuicDispatcherTest, Shutdown) {
+ IPEndPoint addr(Loopback4(), 1);
+
+ EXPECT_CALL(dispatcher_, CreateQuicSession(_, addr, _, &eps_))
+ .WillOnce(testing::Return(CreateSession(
+ &dispatcher_, 1, addr, &session1_, &eps_)));
+
+ ProcessPacket(addr, 1, "foo");
+
+ EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()),
+ SendConnectionClose(QUIC_PEER_GOING_AWAY));
+
+ dispatcher_.Shutdown();
+}
+
+class MockTimeWaitListManager : public QuicTimeWaitListManager {
+ public:
+ MockTimeWaitListManager(QuicPacketWriter* writer,
+ EpollServer* eps)
+ : QuicTimeWaitListManager(writer, eps) {
+ }
+
+ MOCK_METHOD4(ProcessPacket, void(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicGuid guid,
+ const QuicEncryptedPacket& packet));
+};
+
+TEST_F(QuicDispatcherTest, TimeWaitListManager) {
+ MockTimeWaitListManager* time_wait_list_manager =
+ new MockTimeWaitListManager(&dispatcher_, &eps_);
+ // dispatcher takes the ownership of time_wait_list_manager.
+ QuicDispatcherPeer::SetTimeWaitListManager(&dispatcher_,
+ time_wait_list_manager);
+ // Create a new session.
+ IPEndPoint addr(Loopback4(), 1);
+ QuicGuid guid = 1;
+ EXPECT_CALL(dispatcher_, CreateQuicSession(guid, addr, _, &eps_))
+ .WillOnce(testing::Return(CreateSession(
+ &dispatcher_, guid, addr, &session1_, &eps_)));
+ ProcessPacket(addr, guid, "foo");
+
+ // Close the connection by sending public reset packet.
+ QuicPublicResetPacket packet;
+ packet.public_header.guid = guid;
+ packet.public_header.reset_flag = true;
+ packet.public_header.version_flag = false;
+ packet.rejected_sequence_number = 19191;
+ packet.nonce_proof = 132232;
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ QuicFramer::BuildPublicResetPacket(packet));
+ EXPECT_CALL(*session1_, ConnectionClose(QUIC_PUBLIC_RESET, true)).Times(1)
+ .WillOnce(WithoutArgs(Invoke(
+ reinterpret_cast<MockServerConnection*>(session1_->connection()),
+ &MockServerConnection::UnregisterOnConnectionClose)));
+ EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()),
+ ProcessUdpPacket(_, _, _))
+ .WillOnce(Invoke(
+ reinterpret_cast<MockConnection*>(session1_->connection()),
+ &MockConnection::ReallyProcessUdpPacket));
+ dispatcher_.ProcessPacket(IPEndPoint(), addr, guid, *encrypted);
+ EXPECT_TRUE(time_wait_list_manager->IsGuidInTimeWait(guid));
+
+ // Dispatcher forwards subsequent packets for this guid to the time wait list
+ // manager.
+ EXPECT_CALL(*time_wait_list_manager, ProcessPacket(_, _, guid, _)).Times(1);
+ ProcessPacket(addr, guid, "foo");
+}
+
+class WriteBlockedListTest : public QuicDispatcherTest {
+ public:
+ virtual void SetUp() {
+ IPEndPoint addr(Loopback4(), 1);
+
+ EXPECT_CALL(dispatcher_, CreateQuicSession(_, addr, _, &eps_))
+ .WillOnce(testing::Return(CreateSession(
+ &dispatcher_, 1, addr, &session1_, &eps_)));
+ ProcessPacket(addr, 1, "foo");
+
+ EXPECT_CALL(dispatcher_, CreateQuicSession(_, addr, _, &eps_))
+ .WillOnce(testing::Return(CreateSession(
+ &dispatcher_, 2, addr, &session2_, &eps_)));
+ ProcessPacket(addr, 2, "bar");
+
+ blocked_list_ = dispatcher_.write_blocked_list();
+ }
+
+ virtual void TearDown() {
+ EXPECT_CALL(*connection1(), SendConnectionClose(QUIC_PEER_GOING_AWAY));
+ EXPECT_CALL(*connection2(), SendConnectionClose(QUIC_PEER_GOING_AWAY));
+ dispatcher_.Shutdown();
+ }
+
+ bool SetBlocked() {
+ QuicDispatcherPeer::SetWriteBlocked(&dispatcher_);
+ return true;
+ }
+
+ protected:
+ QuicDispatcher::WriteBlockedList* blocked_list_;
+};
+
+TEST_F(WriteBlockedListTest, BasicOnCanWrite) {
+ // No OnCanWrite calls because no connections are blocked.
+ dispatcher_.OnCanWrite();
+
+ // Register connection 1 for events, and make sure it's nofitied.
+ blocked_list_->AddBlockedObject(connection1());
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ dispatcher_.OnCanWrite();
+
+ // It should get only one notification.
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
+ EXPECT_FALSE(dispatcher_.OnCanWrite());
+}
+
+TEST_F(WriteBlockedListTest, OnCanWriteOrder) {
+ // Make sure we handle events in order.
+ InSequence s;
+ blocked_list_->AddBlockedObject(connection1());
+ blocked_list_->AddBlockedObject(connection2());
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ dispatcher_.OnCanWrite();
+
+ // Check the other ordering.
+ blocked_list_->AddBlockedObject(connection2());
+ blocked_list_->AddBlockedObject(connection1());
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ dispatcher_.OnCanWrite();
+}
+
+TEST_F(WriteBlockedListTest, OnCanWriteRemove) {
+ // Add and remove one connction.
+ blocked_list_->AddBlockedObject(connection1());
+ blocked_list_->RemoveBlockedObject(connection1());
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
+ dispatcher_.OnCanWrite();
+
+ // Add and remove one connction and make sure it doesn't affect others.
+ blocked_list_->AddBlockedObject(connection1());
+ blocked_list_->AddBlockedObject(connection2());
+ blocked_list_->RemoveBlockedObject(connection1());
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ dispatcher_.OnCanWrite();
+
+ // Add it, remove it, and add it back and make sure things are OK.
+ blocked_list_->AddBlockedObject(connection1());
+ blocked_list_->RemoveBlockedObject(connection1());
+ blocked_list_->AddBlockedObject(connection1());
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(1);
+ dispatcher_.OnCanWrite();
+}
+
+TEST_F(WriteBlockedListTest, DoubleAdd) {
+ // Make sure a double add does not necessitate a double remove.
+ blocked_list_->AddBlockedObject(connection1());
+ blocked_list_->AddBlockedObject(connection1());
+ blocked_list_->RemoveBlockedObject(connection1());
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
+ dispatcher_.OnCanWrite();
+
+ // Make sure a double add does not result in two OnCanWrite calls.
+ blocked_list_->AddBlockedObject(connection1());
+ blocked_list_->AddBlockedObject(connection1());
+ EXPECT_CALL(*connection1(), OnCanWrite()).Times(1);
+ dispatcher_.OnCanWrite();
+}
+
+TEST_F(WriteBlockedListTest, OnCanWriteHandleBlock) {
+ // Finally make sure if we write block on a write call, we stop calling.
+ InSequence s;
+ blocked_list_->AddBlockedObject(connection1());
+ blocked_list_->AddBlockedObject(connection2());
+ EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce(
+ Invoke(this, &WriteBlockedListTest::SetBlocked));
+ EXPECT_CALL(*connection2(), OnCanWrite()).Times(0);
+ dispatcher_.OnCanWrite();
+
+ // And we'll resume where we left off when we get another call.
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ dispatcher_.OnCanWrite();
+}
+
+TEST_F(WriteBlockedListTest, LimitedWrites) {
+ // Make sure we call both writers. The first will register for more writing
+ // but should not be immediately called due to limits.
+ InSequence s;
+ blocked_list_->AddBlockedObject(connection1());
+ blocked_list_->AddBlockedObject(connection2());
+ EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce(Return(true));
+ EXPECT_CALL(*connection2(), OnCanWrite()).WillOnce(Return(false));
+ dispatcher_.OnCanWrite();
+
+ // Now call OnCanWrite again, and connection1 should get its second chance
+ EXPECT_CALL(*connection1(), OnCanWrite());
+ dispatcher_.OnCanWrite();
+}
+
+TEST_F(WriteBlockedListTest, TestWriteLimits) {
+ // Finally make sure if we write block on a write call, we stop calling.
+ InSequence s;
+ blocked_list_->AddBlockedObject(connection1());
+ blocked_list_->AddBlockedObject(connection2());
+ EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce(
+ Invoke(this, &WriteBlockedListTest::SetBlocked));
+ EXPECT_CALL(*connection2(), OnCanWrite()).Times(0);
+ dispatcher_.OnCanWrite();
+
+ // And we'll resume where we left off when we get another call.
+ EXPECT_CALL(*connection2(), OnCanWrite());
+ dispatcher_.OnCanWrite();
+}
+
+
+} // namespace
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_epoll_clock.cc b/chromium/net/tools/quic/quic_epoll_clock.cc
new file mode 100644
index 00000000000..2a3abf1f1f5
--- /dev/null
+++ b/chromium/net/tools/quic/quic_epoll_clock.cc
@@ -0,0 +1,29 @@
+// 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/tools/quic/quic_epoll_clock.h"
+
+#include "net/tools/flip_server/epoll_server.h"
+
+namespace net {
+namespace tools {
+
+QuicEpollClock::QuicEpollClock(EpollServer* epoll_server)
+ : epoll_server_(epoll_server) {
+}
+
+QuicEpollClock::~QuicEpollClock() {}
+
+QuicTime QuicEpollClock::ApproximateNow() const {
+ return QuicTime::Zero().Add(
+ QuicTime::Delta::FromMicroseconds(epoll_server_->ApproximateNowInUsec()));
+}
+
+QuicTime QuicEpollClock::Now() const {
+ return QuicTime::Zero().Add(
+ QuicTime::Delta::FromMicroseconds(epoll_server_->NowInUsec()));
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_epoll_clock.h b/chromium/net/tools/quic/quic_epoll_clock.h
new file mode 100644
index 00000000000..fb21354a9e0
--- /dev/null
+++ b/chromium/net/tools/quic/quic_epoll_clock.h
@@ -0,0 +1,39 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_QUIC_EPOLL_CLOCK_H_
+#define NET_TOOLS_QUIC_QUIC_EPOLL_CLOCK_H_
+
+#include "base/compiler_specific.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class EpollServer;
+
+namespace tools {
+
+// Clock to efficiently retrieve an approximately accurate time from an
+// EpollServer.
+class QuicEpollClock : public QuicClock {
+ public:
+ explicit QuicEpollClock(EpollServer* epoll_server);
+ virtual ~QuicEpollClock();
+
+ // Returns the approximate current time as a QuicTime object.
+ virtual QuicTime ApproximateNow() const OVERRIDE;
+
+ // Returns the current time as a QuicTime object.
+ // Note: this use significant resources please use only if needed.
+ virtual QuicTime Now() const OVERRIDE;
+
+ protected:
+ EpollServer* epoll_server_;
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_EPOLL_CLOCK_H_
diff --git a/chromium/net/tools/quic/quic_epoll_clock_test.cc b/chromium/net/tools/quic/quic_epoll_clock_test.cc
new file mode 100644
index 00000000000..c774d0adba2
--- /dev/null
+++ b/chromium/net/tools/quic/quic_epoll_clock_test.cc
@@ -0,0 +1,57 @@
+// 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/tools/quic/quic_epoll_clock.h"
+
+#include "net/tools/quic/test_tools/mock_epoll_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+TEST(QuicEpollClockTest, ApproximateNowInUsec) {
+ MockEpollServer epoll_server;
+ QuicEpollClock clock(&epoll_server);
+
+ epoll_server.set_now_in_usec(1000000);
+ EXPECT_EQ(1000000,
+ clock.ApproximateNow().Subtract(QuicTime::Zero()).ToMicroseconds());
+
+ epoll_server.AdvanceBy(5);
+ EXPECT_EQ(1000005,
+ clock.ApproximateNow().Subtract(QuicTime::Zero()).ToMicroseconds());
+}
+
+TEST(QuicEpollClockTest, NowInUsec) {
+ MockEpollServer epoll_server;
+ QuicEpollClock clock(&epoll_server);
+
+ epoll_server.set_now_in_usec(1000000);
+ EXPECT_EQ(1000000,
+ clock.Now().Subtract(QuicTime::Zero()).ToMicroseconds());
+
+ epoll_server.AdvanceBy(5);
+ EXPECT_EQ(1000005,
+ clock.Now().Subtract(QuicTime::Zero()).ToMicroseconds());
+}
+
+TEST(QuicClockTest, WallNow) {
+ MockEpollServer epoll_server;
+ QuicEpollClock clock(&epoll_server);
+
+ base::Time start = base::Time::Now();
+ QuicWallTime now = clock.WallNow();
+ base::Time end = base::Time::Now();
+
+ // If end > start, then we can check now is between start and end.
+ if (end > start) {
+ EXPECT_LE(static_cast<uint64>(start.ToTimeT()), now.ToUNIXSeconds());
+ EXPECT_LE(now.ToUNIXSeconds(), static_cast<uint64>(end.ToTimeT()));
+ }
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_epoll_connection_helper.cc b/chromium/net/tools/quic/quic_epoll_connection_helper.cc
new file mode 100644
index 00000000000..4db1d4cd426
--- /dev/null
+++ b/chromium/net/tools/quic/quic_epoll_connection_helper.cc
@@ -0,0 +1,140 @@
+// 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/tools/quic/quic_epoll_connection_helper.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "net/tools/quic/quic_socket_utils.h"
+
+namespace net {
+namespace tools {
+
+namespace {
+
+class QuicEpollAlarm : public QuicAlarm {
+ public:
+ QuicEpollAlarm(EpollServer* epoll_server,
+ QuicAlarm::Delegate* delegate)
+ : QuicAlarm(delegate),
+ epoll_server_(epoll_server),
+ epoll_alarm_impl_(this) {}
+
+ protected:
+ virtual void SetImpl() OVERRIDE {
+ DCHECK(deadline().IsInitialized());
+ epoll_server_->RegisterAlarm(
+ deadline().Subtract(QuicTime::Zero()).ToMicroseconds(),
+ &epoll_alarm_impl_);
+ }
+
+ virtual void CancelImpl() OVERRIDE {
+ DCHECK(!deadline().IsInitialized());
+ epoll_alarm_impl_.UnregisterIfRegistered();
+ }
+
+ private:
+ class EpollAlarmImpl : public EpollAlarm {
+ public:
+ explicit EpollAlarmImpl(QuicEpollAlarm* alarm) : alarm_(alarm) {}
+
+ virtual int64 OnAlarm() OVERRIDE {
+ EpollAlarm::OnAlarm();
+ alarm_->Fire();
+ // Fire will take care of registering the alarm, if needed.
+ return 0;
+ }
+
+ private:
+ QuicEpollAlarm* alarm_;
+ };
+
+ EpollServer* epoll_server_;
+ EpollAlarmImpl epoll_alarm_impl_;
+};
+
+} // namespace
+
+QuicEpollConnectionHelper::QuicEpollConnectionHelper(
+ int fd, EpollServer* epoll_server)
+ : writer_(NULL),
+ epoll_server_(epoll_server),
+ fd_(fd),
+ connection_(NULL),
+ clock_(epoll_server),
+ random_generator_(QuicRandom::GetInstance()) {
+}
+
+QuicEpollConnectionHelper::QuicEpollConnectionHelper(QuicPacketWriter* writer,
+ EpollServer* epoll_server)
+ : writer_(writer),
+ epoll_server_(epoll_server),
+ fd_(-1),
+ connection_(NULL),
+ clock_(epoll_server),
+ random_generator_(QuicRandom::GetInstance()) {
+}
+
+QuicEpollConnectionHelper::~QuicEpollConnectionHelper() {
+}
+
+void QuicEpollConnectionHelper::SetConnection(QuicConnection* connection) {
+ DCHECK(!connection_);
+ connection_ = connection;
+}
+
+const QuicClock* QuicEpollConnectionHelper::GetClock() const {
+ return &clock_;
+}
+
+QuicRandom* QuicEpollConnectionHelper::GetRandomGenerator() {
+ return random_generator_;
+}
+
+int QuicEpollConnectionHelper::WritePacketToWire(
+ const QuicEncryptedPacket& packet,
+ int* error) {
+ if (connection_->ShouldSimulateLostPacket()) {
+ DLOG(INFO) << "Dropping packet due to fake packet loss.";
+ *error = 0;
+ return packet.length();
+ }
+
+ // If we have a writer, delgate the write to it.
+ if (writer_) {
+ return writer_->WritePacket(packet.data(), packet.length(),
+ connection_->self_address().address(),
+ connection_->peer_address(),
+ connection_,
+ error);
+ } else {
+ return QuicSocketUtils::WritePacket(
+ fd_, packet.data(), packet.length(),
+ connection_->self_address().address(),
+ connection_->peer_address(),
+ error);
+ }
+}
+
+bool QuicEpollConnectionHelper::IsWriteBlockedDataBuffered() {
+ return false;
+}
+
+bool QuicEpollConnectionHelper::IsWriteBlocked(int error) {
+ return error == EAGAIN || error == EWOULDBLOCK;
+}
+
+QuicAlarm* QuicEpollConnectionHelper::CreateAlarm(
+ QuicAlarm::Delegate* delegate) {
+ return new QuicEpollAlarm(epoll_server_, delegate);
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_epoll_connection_helper.h b/chromium/net/tools/quic/quic_epoll_connection_helper.h
new file mode 100644
index 00000000000..5bd526f8469
--- /dev/null
+++ b/chromium/net/tools/quic/quic_epoll_connection_helper.h
@@ -0,0 +1,72 @@
+// 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.
+//
+// The Google-specific helper for QuicConnection which uses
+// EpollAlarm for alarms, and used an int fd_ for writing data.
+
+#ifndef NET_TOOLS_QUIC_QUIC_EPOLL_CONNECTION_HELPER_H_
+#define NET_TOOLS_QUIC_QUIC_EPOLL_CONNECTION_HELPER_H_
+
+#include <sys/types.h>
+#include <set>
+
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/tools/quic/quic_epoll_clock.h"
+#include "net/tools/quic/quic_packet_writer.h"
+
+namespace net {
+
+class EpollServer;
+class QuicRandom;
+
+namespace tools {
+
+class AckAlarm;
+class RetransmissionAlarm;
+class SendAlarm;
+class TimeoutAlarm;
+
+namespace test {
+class QuicEpollConnectionHelperPeer;
+} // namespace test
+
+class QuicEpollConnectionHelper : public QuicConnectionHelperInterface {
+ public:
+ QuicEpollConnectionHelper(int fd, EpollServer* eps);
+ QuicEpollConnectionHelper(QuicPacketWriter* writer, EpollServer* eps);
+ virtual ~QuicEpollConnectionHelper();
+
+ // QuicEpollConnectionHelperInterface
+ virtual void SetConnection(QuicConnection* connection) OVERRIDE;
+ virtual const QuicClock* GetClock() const OVERRIDE;
+ virtual QuicRandom* GetRandomGenerator() OVERRIDE;
+ virtual int WritePacketToWire(const QuicEncryptedPacket& packet,
+ int* error) OVERRIDE;
+ virtual bool IsWriteBlockedDataBuffered() OVERRIDE;
+ virtual bool IsWriteBlocked(int error) OVERRIDE;
+ virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) OVERRIDE;
+
+ EpollServer* epoll_server() { return epoll_server_; }
+
+ private:
+ friend class QuicConnectionPeer;
+ friend class net::tools::test::QuicEpollConnectionHelperPeer;
+
+ QuicPacketWriter* writer_; // Not owned
+ EpollServer* epoll_server_; // Not owned.
+ int fd_;
+
+ QuicConnection* connection_;
+ const QuicEpollClock clock_;
+ QuicRandom* random_generator_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicEpollConnectionHelper);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_EPOLL_CONNECTION_HELPER_H_
diff --git a/chromium/net/tools/quic/quic_epoll_connection_helper_test.cc b/chromium/net/tools/quic/quic_epoll_connection_helper_test.cc
new file mode 100644
index 00000000000..0bfaee27d07
--- /dev/null
+++ b/chromium/net/tools/quic/quic_epoll_connection_helper_test.cc
@@ -0,0 +1,207 @@
+// 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/tools/quic/quic_epoll_connection_helper.h"
+
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/tools/quic/test_tools/mock_epoll_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using net::test::GetMinStreamFrameSize;
+using net::test::FramerVisitorCapturingFrames;
+using net::test::MockSendAlgorithm;
+using net::test::QuicConnectionPeer;
+using net::test::MockConnectionVisitor;
+using net::tools::test::MockEpollServer;
+using testing::_;
+using testing::Return;
+
+namespace net {
+namespace tools {
+namespace test {
+namespace {
+
+const char data1[] = "foo";
+const bool kFromPeer = true;
+
+class TestConnectionHelper : public QuicEpollConnectionHelper {
+ public:
+ TestConnectionHelper(int fd, EpollServer* eps)
+ : QuicEpollConnectionHelper(fd, eps) {
+ }
+
+ virtual int WritePacketToWire(const QuicEncryptedPacket& packet,
+ int* error) OVERRIDE {
+ QuicFramer framer(QuicVersionMax(), QuicTime::Zero(), true);
+ FramerVisitorCapturingFrames visitor;
+ framer.set_visitor(&visitor);
+ EXPECT_TRUE(framer.ProcessPacket(packet));
+ header_ = *visitor.header();
+ *error = 0;
+ return packet.length();
+ }
+
+ QuicPacketHeader* header() { return &header_; }
+
+ private:
+ QuicPacketHeader header_;
+};
+
+class TestConnection : public QuicConnection {
+ public:
+ TestConnection(QuicGuid guid,
+ IPEndPoint address,
+ TestConnectionHelper* helper)
+ : QuicConnection(guid, address, helper, false, QuicVersionMax()) {
+ }
+
+ void SendAck() {
+ QuicConnectionPeer::SendAck(this);
+ }
+
+ void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) {
+ QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
+ }
+
+ using QuicConnection::SendOrQueuePacket;
+};
+
+class QuicEpollConnectionHelperTest : public ::testing::Test {
+ protected:
+ QuicEpollConnectionHelperTest()
+ : guid_(42),
+ framer_(QuicVersionMax(), QuicTime::Zero(), false),
+ send_algorithm_(new testing::StrictMock<MockSendAlgorithm>),
+ helper_(new TestConnectionHelper(0, &epoll_server_)),
+ connection_(guid_, IPEndPoint(), helper_),
+ frame1_(1, false, 0, data1) {
+ connection_.set_visitor(&visitor_);
+ connection_.SetSendAlgorithm(send_algorithm_);
+ epoll_server_.set_timeout_in_us(-1);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)).
+ WillRepeatedly(Return(QuicTime::Delta::Zero()));
+ }
+
+ QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number,
+ QuicFecGroupNumber fec_group) {
+ header_.public_header.version_flag = false;
+ header_.public_header.reset_flag = false;
+ header_.fec_flag = false;
+ header_.entropy_flag = false;
+ header_.packet_sequence_number = number;
+ header_.is_in_fec_group = fec_group == 0 ? NOT_IN_FEC_GROUP : IN_FEC_GROUP;
+ header_.fec_group = fec_group;
+
+ QuicFrames frames;
+ QuicFrame frame(&frame1_);
+ frames.push_back(frame);
+ return framer_.BuildUnsizedDataPacket(header_, frames).packet;
+ }
+
+ QuicGuid guid_;
+ QuicFramer framer_;
+
+ MockEpollServer epoll_server_;
+ testing::StrictMock<MockSendAlgorithm>* send_algorithm_;
+ TestConnectionHelper* helper_;
+ TestConnection connection_;
+ testing::StrictMock<MockConnectionVisitor> visitor_;
+
+ QuicPacketHeader header_;
+ QuicStreamFrame frame1_;
+};
+
+TEST_F(QuicEpollConnectionHelperTest, DISABLED_TestRetransmission) {
+ //FLAGS_fake_packet_loss_percentage = 100;
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
+ Return(QuicTime::Delta::Zero()));
+ const int64 kDefaultRetransmissionTimeMs = 500;
+
+ const char buffer[] = "foo";
+ const size_t packet_size =
+ GetPacketHeaderSize(PACKET_8BYTE_GUID, kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP) +
+ GetMinStreamFrameSize(framer_.version()) + arraysize(buffer) - 1;
+ EXPECT_CALL(*send_algorithm_,
+ SentPacket(_, 1, packet_size, NOT_RETRANSMISSION));
+ EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, packet_size));
+ connection_.SendStreamData(1, buffer, 0, false);
+ EXPECT_EQ(1u, helper_->header()->packet_sequence_number);
+ EXPECT_CALL(*send_algorithm_,
+ SentPacket(_, 2, packet_size, IS_RETRANSMISSION));
+ epoll_server_.AdvanceByAndCallCallbacks(kDefaultRetransmissionTimeMs * 1000);
+
+ EXPECT_EQ(2u, helper_->header()->packet_sequence_number);
+}
+
+TEST_F(QuicEpollConnectionHelperTest, InitialTimeout) {
+ EXPECT_TRUE(connection_.connected());
+
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
+ epoll_server_.WaitForEventsAndExecuteCallbacks();
+ EXPECT_FALSE(connection_.connected());
+ EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000, epoll_server_.NowInUsec());
+}
+
+TEST_F(QuicEpollConnectionHelperTest, TimeoutAfterSend) {
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_EQ(0, epoll_server_.NowInUsec());
+
+ // When we send a packet, the timeout will change to 5000 +
+ // kDefaultInitialTimeoutSecs.
+ epoll_server_.AdvanceBy(5000);
+ EXPECT_EQ(5000, epoll_server_.NowInUsec());
+
+ // Send an ack so we don't set the retransmission alarm.
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ connection_.SendAck();
+
+ // The original alarm will fire. We should not time out because we had a
+ // network event at t=5000. The alarm will reregister.
+ epoll_server_.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000, epoll_server_.NowInUsec());
+
+ // This time, we should time out.
+ EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer));
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION));
+ epoll_server_.WaitForEventsAndExecuteCallbacks();
+ EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000 + 5000,
+ epoll_server_.NowInUsec());
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_F(QuicEpollConnectionHelperTest, SendSchedulerDelayThenSend) {
+ // Test that if we send a packet with a delay, it ends up queued.
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
+ Return(QuicTime::Delta::Zero()));
+ QuicPacket* packet = ConstructDataPacket(1, 0);
+ EXPECT_CALL(
+ *send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(1)));
+ connection_.SendOrQueuePacket(ENCRYPTION_NONE, 1, packet, 0,
+ HAS_RETRANSMITTABLE_DATA);
+ EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION));
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ // Advance the clock to fire the alarm, and configure the scheduler
+ // to permit the packet to be sent.
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).
+ WillRepeatedly(testing::Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true));
+ epoll_server_.AdvanceByAndCallCallbacks(1);
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+}
+
+} // namespace
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_in_memory_cache.cc b/chromium/net/tools/quic/quic_in_memory_cache.cc
new file mode 100644
index 00000000000..b840d79370d
--- /dev/null
+++ b/chromium/net/tools/quic/quic_in_memory_cache.cc
@@ -0,0 +1,225 @@
+// 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/tools/quic/quic_in_memory_cache.h"
+
+#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
+#include "base/stl_util.h"
+
+using base::FilePath;
+using base::StringPiece;
+using std::string;
+
+// Specifies the directory used during QuicInMemoryCache
+// construction to seed the cache. Cache directory can be
+// generated using `wget -p --save-headers <url>
+
+namespace net {
+namespace tools {
+
+std::string FLAGS_quic_in_memory_cache_dir = "/tmp/quic-data";
+
+namespace {
+
+// BalsaVisitor implementation (glue) which caches response bodies.
+class CachingBalsaVisitor : public BalsaVisitorInterface {
+ public:
+ CachingBalsaVisitor() : done_framing_(false) {}
+ virtual void ProcessBodyData(const char* input, size_t size) OVERRIDE {
+ AppendToBody(input, size);
+ }
+ virtual void ProcessTrailers(const BalsaHeaders& trailer) {
+ LOG(DFATAL) << "Trailers not supported.";
+ }
+ virtual void MessageDone() OVERRIDE {
+ done_framing_ = true;
+ }
+ virtual void HandleHeaderError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleHeaderWarning(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleTrailerError(BalsaFrame* framer) { UnhandledError(); }
+ virtual void HandleTrailerWarning(BalsaFrame* framer) { UnhandledError(); }
+ virtual void HandleChunkingError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ virtual void HandleBodyError(BalsaFrame* framer) OVERRIDE {
+ UnhandledError();
+ }
+ void UnhandledError() {
+ LOG(DFATAL) << "Unhandled error framing HTTP.";
+ }
+ virtual void ProcessBodyInput(const char*, size_t) OVERRIDE {}
+ virtual void ProcessHeaderInput(const char*, size_t) OVERRIDE {}
+ virtual void ProcessTrailerInput(const char*, size_t) OVERRIDE {}
+ virtual void ProcessHeaders(const net::BalsaHeaders&) OVERRIDE {}
+ virtual void ProcessRequestFirstLine(
+ const char*, size_t, const char*, size_t,
+ const char*, size_t, const char*, size_t) OVERRIDE {}
+ virtual void ProcessResponseFirstLine(
+ const char*, size_t, const char*,
+ size_t, const char*, size_t, const char*, size_t) OVERRIDE {}
+ virtual void ProcessChunkLength(size_t) OVERRIDE {}
+ virtual void ProcessChunkExtensions(const char*, size_t) OVERRIDE {}
+ virtual void HeaderDone() OVERRIDE {}
+
+ void AppendToBody(const char* input, size_t size) {
+ body_.append(input, size);
+ }
+ bool done_framing() const { return done_framing_; }
+ const string& body() const { return body_; }
+
+ private:
+ bool done_framing_;
+ string body_;
+};
+
+} // namespace
+
+QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
+ return Singleton<QuicInMemoryCache>::get();
+}
+
+const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
+ const BalsaHeaders& request_headers) const {
+ ResponseMap::const_iterator it = responses_.find(GetKey(request_headers));
+ if (it == responses_.end()) {
+ return NULL;
+ }
+ return it->second;
+}
+
+void QuicInMemoryCache::AddResponse(const BalsaHeaders& request_headers,
+ const BalsaHeaders& response_headers,
+ StringPiece response_body) {
+ LOG(INFO) << "Adding response for: " << GetKey(request_headers);
+ if (ContainsKey(responses_, GetKey(request_headers))) {
+ LOG(DFATAL) << "Response for given request already exists!";
+ }
+ Response* new_response = new Response();
+ new_response->set_headers(response_headers);
+ new_response->set_body(response_body);
+ responses_[GetKey(request_headers)] = new_response;
+}
+
+void QuicInMemoryCache::ResetForTests() {
+ STLDeleteValues(&responses_);
+ Initialize();
+}
+
+QuicInMemoryCache::QuicInMemoryCache() {
+ Initialize();
+}
+
+void QuicInMemoryCache::Initialize() {
+ // If there's no defined cache dir, we have no initialization to do.
+ if (FLAGS_quic_in_memory_cache_dir.empty()) {
+ LOG(WARNING) << "No cache directory found. Skipping initialization.";
+ return;
+ }
+ LOG(INFO) << "Attempting to initialize QuicInMemoryCache from directory: "
+ << FLAGS_quic_in_memory_cache_dir;
+
+ FilePath directory(FLAGS_quic_in_memory_cache_dir);
+ base::FileEnumerator file_list(directory,
+ true,
+ base::FileEnumerator::FILES);
+
+ FilePath file = file_list.Next();
+ while (!file.empty()) {
+ // Need to skip files in .svn directories
+ if (file.value().find("/.svn/") != std::string::npos) {
+ file = file_list.Next();
+ continue;
+ }
+
+ BalsaHeaders request_headers, response_headers;
+
+ string file_contents;
+ file_util::ReadFileToString(file, &file_contents);
+
+ // Frame HTTP.
+ CachingBalsaVisitor caching_visitor;
+ BalsaFrame framer;
+ framer.set_balsa_headers(&response_headers);
+ framer.set_balsa_visitor(&caching_visitor);
+ size_t processed = 0;
+ while (processed < file_contents.length() &&
+ !caching_visitor.done_framing()) {
+ processed += framer.ProcessInput(file_contents.c_str() + processed,
+ file_contents.length() - processed);
+ }
+
+ string response_headers_str;
+ response_headers.DumpToString(&response_headers_str);
+ if (!caching_visitor.done_framing()) {
+ LOG(DFATAL) << "Did not frame entire message from file: " << file.value()
+ << " (" << processed << " of " << file_contents.length()
+ << " bytes).";
+ }
+ if (processed < file_contents.length()) {
+ // Didn't frame whole file. Assume remainder is body.
+ // This sometimes happens as a result of incompatibilities between
+ // BalsaFramer and wget's serialization of HTTP sans content-length.
+ caching_visitor.AppendToBody(file_contents.c_str() + processed,
+ file_contents.length() - processed);
+ processed += file_contents.length();
+ }
+
+ StringPiece base = file.value();
+ if (response_headers.HasHeader("X-Original-Url")) {
+ base = response_headers.GetHeader("X-Original-Url");
+ response_headers.RemoveAllOfHeader("X-Original-Url");
+ // Remove the protocol so that the string is of the form host + path,
+ // which is parsed properly below.
+ if (StringPieceUtils::StartsWithIgnoreCase(base, "https://")) {
+ base.remove_prefix(8);
+ } else if (StringPieceUtils::StartsWithIgnoreCase(base, "http://")) {
+ base.remove_prefix(7);
+ }
+ }
+ int path_start = base.find_first_of('/');
+ DCHECK_LT(0, path_start);
+ StringPiece host(base.substr(0, path_start));
+ StringPiece path(base.substr(path_start));
+ if (path[path.length() - 1] == ',') {
+ path.remove_suffix(1);
+ }
+ // Set up request headers. Assume method is GET and protocol is HTTP/1.1.
+ request_headers.SetRequestFirstlineFromStringPieces("GET",
+ path,
+ "HTTP/1.1");
+ request_headers.ReplaceOrAppendHeader("host", host);
+
+ LOG(INFO) << "Inserting 'http://" << GetKey(request_headers)
+ << "' into QuicInMemoryCache.";
+
+ AddResponse(request_headers, response_headers, caching_visitor.body());
+
+ file = file_list.Next();
+ }
+}
+
+QuicInMemoryCache::~QuicInMemoryCache() {
+ STLDeleteValues(&responses_);
+}
+
+string QuicInMemoryCache::GetKey(const BalsaHeaders& request_headers) const {
+ StringPiece uri = request_headers.request_uri();
+ StringPiece host;
+ if (uri[0] == '/') {
+ host = request_headers.GetHeader("host");
+ } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "https://")) {
+ uri.remove_prefix(8);
+ } else if (StringPieceUtils::StartsWithIgnoreCase(uri, "http://")) {
+ uri.remove_prefix(7);
+ }
+ return host.as_string() + uri.as_string();
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_in_memory_cache.h b/chromium/net/tools/quic/quic_in_memory_cache.h
new file mode 100644
index 00000000000..6322e2da49d
--- /dev/null
+++ b/chromium/net/tools/quic/quic_in_memory_cache.h
@@ -0,0 +1,88 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_QUIC_IN_MEMORY_CACHE_H_
+#define NET_TOOLS_QUIC_QUIC_IN_MEMORY_CACHE_H_
+
+#include <string>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_piece.h"
+#include "net/tools/flip_server/balsa_frame.h"
+#include "net/tools/flip_server/balsa_headers.h"
+
+template <typename T> struct DefaultSingletonTraits;
+
+namespace net {
+namespace tools {
+
+extern std::string FLAGS_quic_in_memory_cache_dir;
+
+class QuicServer;
+
+// In-memory cache for HTTP responses.
+// Reads from disk cache generated by:
+// `wget -p --save_headers <url>`
+class QuicInMemoryCache {
+ public:
+ // Container for response header/body pairs.
+ class Response {
+ public:
+ Response() {}
+ ~Response() {}
+
+ const BalsaHeaders& headers() const { return headers_; }
+ const base::StringPiece body() const { return base::StringPiece(body_); }
+
+ private:
+ friend class QuicInMemoryCache;
+
+ void set_headers(const BalsaHeaders& headers) {
+ headers_.CopyFrom(headers);
+ }
+ void set_body(base::StringPiece body) {
+ body.CopyToString(&body_);
+ }
+
+ BalsaHeaders headers_;
+ std::string body_;
+
+ DISALLOW_COPY_AND_ASSIGN(Response);
+ };
+ static QuicInMemoryCache* GetInstance();
+
+ // Retrieve a response from this cache for a given request.
+ // If no appropriate response exists, NULL is returned.
+ // Currently, responses are selected based on request URI only.
+ const Response* GetResponse(const BalsaHeaders& request_headers) const;
+
+ // Add a response to the cache.
+ void AddResponse(const BalsaHeaders& request_headers,
+ const BalsaHeaders& response_headers,
+ base::StringPiece response_body);
+
+ void ResetForTests();
+
+ private:
+ typedef base::hash_map<std::string, Response*> ResponseMap;
+
+
+ QuicInMemoryCache();
+ friend struct DefaultSingletonTraits<QuicInMemoryCache>;
+ ~QuicInMemoryCache();
+
+ void Initialize();
+ std::string GetKey(const BalsaHeaders& response_headers) const;
+
+ // Cached responses.
+ ResponseMap responses_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicInMemoryCache);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_IN_MEMORY_CACHE_H_
diff --git a/chromium/net/tools/quic/quic_in_memory_cache_test.cc b/chromium/net/tools/quic/quic_in_memory_cache_test.cc
new file mode 100644
index 00000000000..065ecc3b437
--- /dev/null
+++ b/chromium/net/tools/quic/quic_in_memory_cache_test.cc
@@ -0,0 +1,137 @@
+// Copyright 2013 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 <string>
+
+#include "base/files/file_path.h"
+#include "base/memory/singleton.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "net/tools/flip_server/balsa_headers.h"
+#include "net/tools/quic/quic_in_memory_cache.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::IntToString;
+using base::StringPiece;
+
+namespace net {
+namespace tools {
+namespace test {
+
+class QuicInMemoryCacheTest : public ::testing::Test {
+ protected:
+ QuicInMemoryCacheTest() {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.AppendASCII("net").AppendASCII("data")
+ .AppendASCII("quic_in_memory_cache_data");
+ // The file path is known to be an ascii string.
+ FLAGS_quic_in_memory_cache_dir = path.MaybeAsASCII();
+ }
+
+ void CreateRequest(std::string host,
+ std::string path,
+ net::BalsaHeaders* headers) {
+ headers->SetRequestFirstlineFromStringPieces("GET", path, "HTTP/1.1");
+ headers->ReplaceOrAppendHeader("host", host);
+ }
+
+ virtual void SetUp() {
+ QuicInMemoryCache::GetInstance()->ResetForTests();
+ }
+
+ // This method was copied from end_to_end_test.cc in this directory.
+ void AddToCache(const StringPiece& method,
+ const StringPiece& path,
+ const StringPiece& version,
+ const StringPiece& response_code,
+ const StringPiece& response_detail,
+ const StringPiece& body) {
+ BalsaHeaders request_headers, response_headers;
+ request_headers.SetRequestFirstlineFromStringPieces(method,
+ path,
+ version);
+ response_headers.SetRequestFirstlineFromStringPieces(version,
+ response_code,
+ response_detail);
+ response_headers.AppendHeader("content-length",
+ base::IntToString(body.length()));
+
+ // Check if response already exists and matches.
+ QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance();
+ const QuicInMemoryCache::Response* cached_response =
+ cache->GetResponse(request_headers);
+ if (cached_response != NULL) {
+ std::string cached_response_headers_str, response_headers_str;
+ cached_response->headers().DumpToString(&cached_response_headers_str);
+ response_headers.DumpToString(&response_headers_str);
+ CHECK_EQ(cached_response_headers_str, response_headers_str);
+ CHECK_EQ(cached_response->body(), body);
+ return;
+ }
+ cache->AddResponse(request_headers, response_headers, body);
+ }
+};
+
+TEST_F(QuicInMemoryCacheTest, AddResponseGetResponse) {
+ std::string response_body("hello response");
+ AddToCache("GET", "https://www.google.com/bar",
+ "HTTP/1.1", "200", "OK", response_body);
+ net::BalsaHeaders request_headers;
+ CreateRequest("www.google.com", "/bar", &request_headers);
+ QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance();
+ const QuicInMemoryCache::Response* response =
+ cache->GetResponse(request_headers);
+ ASSERT_TRUE(response);
+ EXPECT_EQ("200", response->headers().response_code());
+ EXPECT_EQ(response_body.size(), response->body().length());
+
+ CreateRequest("", "https://www.google.com/bar", &request_headers);
+ response = cache->GetResponse(request_headers);
+ ASSERT_TRUE(response);
+ EXPECT_EQ("200", response->headers().response_code());
+ EXPECT_EQ(response_body.size(), response->body().length());
+}
+
+TEST_F(QuicInMemoryCacheTest, ReadsCacheDir) {
+ net::BalsaHeaders request_headers;
+ CreateRequest("quic.test.url", "/index.html", &request_headers);
+
+ const QuicInMemoryCache::Response* response =
+ QuicInMemoryCache::GetInstance()->GetResponse(request_headers);
+ ASSERT_TRUE(response);
+ std::string value;
+ response->headers().GetAllOfHeaderAsString("Connection", &value);
+ EXPECT_EQ("200", response->headers().response_code());
+ EXPECT_EQ("Keep-Alive", value);
+ EXPECT_LT(0U, response->body().length());
+}
+
+TEST_F(QuicInMemoryCacheTest, ReadsCacheDirHttp) {
+ net::BalsaHeaders request_headers;
+ CreateRequest("", "http://quic.test.url/index.html", &request_headers);
+
+ const QuicInMemoryCache::Response* response =
+ QuicInMemoryCache::GetInstance()->GetResponse(request_headers);
+ ASSERT_TRUE(response);
+ std::string value;
+ response->headers().GetAllOfHeaderAsString("Connection", &value);
+ EXPECT_EQ("200", response->headers().response_code());
+ EXPECT_EQ("Keep-Alive", value);
+ EXPECT_LT(0U, response->body().length());
+}
+
+TEST_F(QuicInMemoryCacheTest, GetResponseNoMatch) {
+ net::BalsaHeaders request_headers;
+ CreateRequest("www.google.com", "/index.html", &request_headers);
+
+ const QuicInMemoryCache::Response* response =
+ QuicInMemoryCache::GetInstance()->GetResponse(request_headers);
+ ASSERT_FALSE(response);
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_packet_writer.h b/chromium/net/tools/quic/quic_packet_writer.h
new file mode 100644
index 00000000000..b10e852d299
--- /dev/null
+++ b/chromium/net/tools/quic/quic_packet_writer.h
@@ -0,0 +1,33 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_QUIC_PACKET_WRITER_H_
+#define NET_TOOLS_QUIC_QUIC_PACKET_WRITER_H_
+
+#include "net/base/ip_endpoint.h"
+
+namespace net {
+
+class QuicBlockedWriterInterface;
+
+namespace tools {
+
+// An interface between writers and the entity managing the
+// socket (in our case the QuicDispatcher). This allows the Dispatcher to
+// control writes, and manage any writers who end up write blocked.
+class QuicPacketWriter {
+ public:
+ virtual ~QuicPacketWriter() {}
+
+ virtual int WritePacket(const char* buffer, size_t buf_len,
+ const net::IPAddressNumber& self_address,
+ const net::IPEndPoint& peer_address,
+ QuicBlockedWriterInterface* blocked_writer,
+ int* error) = 0;
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_PACKET_WRITER_H_
diff --git a/chromium/net/tools/quic/quic_reliable_client_stream.cc b/chromium/net/tools/quic/quic_reliable_client_stream.cc
new file mode 100644
index 00000000000..359fec4dd90
--- /dev/null
+++ b/chromium/net/tools/quic/quic_reliable_client_stream.cc
@@ -0,0 +1,27 @@
+// 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/tools/quic/quic_reliable_client_stream.h"
+
+using std::string;
+
+namespace net {
+namespace tools {
+
+// Sends body data to the server and returns the number of bytes sent.
+ssize_t QuicReliableClientStream::SendBody(const string& data, bool fin) {
+ return WriteData(data, fin).bytes_consumed;
+}
+
+bool QuicReliableClientStream::OnStreamFrame(const QuicStreamFrame& frame) {
+ if (!write_side_closed()) {
+ DLOG(INFO) << "Got a response before the request was complete. "
+ << "Aborting request.";
+ CloseWriteSide();
+ }
+ return ReliableQuicStream::OnStreamFrame(frame);
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_reliable_client_stream.h b/chromium/net/tools/quic/quic_reliable_client_stream.h
new file mode 100644
index 00000000000..10b60c2f696
--- /dev/null
+++ b/chromium/net/tools/quic/quic_reliable_client_stream.h
@@ -0,0 +1,66 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_QUIC_RELIABLE_CLIENT_STREAM_H_
+#define NET_TOOLS_QUIC_QUIC_RELIABLE_CLIENT_STREAM_H_
+
+#include <sys/types.h>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/tools/flip_server/balsa_frame.h"
+#include "net/tools/flip_server/balsa_headers.h"
+
+namespace net {
+
+class QuicSession;
+
+namespace tools {
+
+class QuicClientSession;
+
+// A base class for spdy/http client streams which handles the concept
+// of sending and receiving headers and bodies.
+class QuicReliableClientStream : public ReliableQuicStream {
+ public:
+ QuicReliableClientStream(QuicStreamId id, QuicSession* session)
+ : ReliableQuicStream(id, session) {
+ }
+
+ // Serializes the headers and body, sends it to the server, and
+ // returns the number of bytes sent.
+ virtual ssize_t SendRequest(const BalsaHeaders& headers,
+ base::StringPiece body,
+ bool fin) = 0;
+ // Sends body data to the server and returns the number of bytes sent.
+ virtual ssize_t SendBody(const std::string& data, bool fin);
+
+ // Override the base class to close the Write side as soon as we get a
+ // response.
+ // SPDY/HTTP do not support bidirectional streaming.
+ virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
+
+ // Returns the response data.
+ const std::string& data() { return data_; }
+
+ // Returns whatever headers have been received for this stream.
+ const BalsaHeaders& headers() { return headers_; }
+
+ protected:
+ std::string* mutable_data() { return &data_; }
+ BalsaHeaders* mutable_headers() { return &headers_; }
+
+ private:
+ BalsaHeaders headers_;
+ std::string data_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicReliableClientStream);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_RELIABLE_CLIENT_STREAM_H_
diff --git a/chromium/net/tools/quic/quic_reliable_client_stream_test.cc b/chromium/net/tools/quic/quic_reliable_client_stream_test.cc
new file mode 100644
index 00000000000..af0ffd58f81
--- /dev/null
+++ b/chromium/net/tools/quic/quic_reliable_client_stream_test.cc
@@ -0,0 +1,96 @@
+// 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/tools/quic/quic_reliable_client_stream.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/quic_spdy_client_stream.h"
+#include "net/tools/quic/spdy_utils.h"
+#include "net/tools/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::TestWithParam;
+
+namespace net {
+namespace tools {
+namespace test {
+namespace {
+
+class QuicClientStreamTest : public ::testing::Test {
+ public:
+ QuicClientStreamTest()
+ : session_("example.com", QuicConfig(),
+ new MockConnection(1, IPEndPoint(), 0, &eps_, false),
+ &crypto_config_),
+ body_("hello world") {
+ session_.config()->SetDefaults();
+ crypto_config_.SetDefaults();
+
+ headers_.SetResponseFirstlineFromStringPieces("HTTP/1.1", "200", "Ok");
+ headers_.ReplaceOrAppendHeader("content-length", "11");
+
+ headers_string_ = SpdyUtils::SerializeResponseHeaders(headers_);
+ stream_.reset(new QuicSpdyClientStream(3, &session_));
+ }
+
+ EpollServer eps_;
+ QuicClientSession session_;
+ scoped_ptr<QuicReliableClientStream> stream_;
+ BalsaHeaders headers_;
+ string headers_string_;
+ string body_;
+ QuicCryptoClientConfig crypto_config_;
+};
+
+TEST_F(QuicClientStreamTest, TestFraming) {
+ EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
+ headers_string_.c_str(), headers_string_.size()));
+ EXPECT_EQ(body_.size(),
+ stream_->ProcessData(body_.c_str(), body_.size()));
+ EXPECT_EQ(200u, stream_->headers().parsed_response_code());
+ EXPECT_EQ(body_, stream_->data());
+}
+
+TEST_F(QuicClientStreamTest, TestFramingOnePacket) {
+ string message = headers_string_ + body_;
+
+ EXPECT_EQ(message.size(), stream_->ProcessData(
+ message.c_str(), message.size()));
+ EXPECT_EQ(200u, stream_->headers().parsed_response_code());
+ EXPECT_EQ(body_, stream_->data());
+}
+
+TEST_F(QuicClientStreamTest, TestFramingExtraData) {
+ string large_body = "hello world!!!!!!";
+
+ EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
+ headers_string_.c_str(), headers_string_.size()));
+ // The headers should parse successfully.
+ EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
+ EXPECT_EQ(200u, stream_->headers().parsed_response_code());
+
+ stream_->ProcessData(large_body.c_str(), large_body.size());
+ stream_->TerminateFromPeer(true);
+
+ EXPECT_NE(QUIC_STREAM_NO_ERROR, stream_->stream_error());
+}
+
+TEST_F(QuicClientStreamTest, TestNoBidirectionalStreaming) {
+ QuicStreamFrame frame(3, false, 3, "asd");
+
+ EXPECT_FALSE(stream_->write_side_closed());
+ EXPECT_TRUE(stream_->OnStreamFrame(frame));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+} // namespace
+} // namespace test
+} // namespace tools
+} // namespace net
+
diff --git a/chromium/net/tools/quic/quic_reliable_server_stream.cc b/chromium/net/tools/quic/quic_reliable_server_stream.cc
new file mode 100644
index 00000000000..58b884a1082
--- /dev/null
+++ b/chromium/net/tools/quic/quic_reliable_server_stream.cc
@@ -0,0 +1,56 @@
+// 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/tools/quic/quic_reliable_server_stream.h"
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "net/tools/quic/quic_in_memory_cache.h"
+
+using base::StringPiece;
+
+namespace net {
+namespace tools {
+
+QuicReliableServerStream::QuicReliableServerStream(QuicStreamId id,
+ QuicSession* session)
+ : ReliableQuicStream(id, session) {
+}
+
+
+void QuicReliableServerStream::SendResponse() {
+ // Find response in cache. If not found, send error response.
+ const QuicInMemoryCache::Response* response =
+ QuicInMemoryCache::GetInstance()->GetResponse(headers_);
+ if (response == NULL) {
+ SendErrorResponse();
+ return;
+ }
+
+ DLOG(INFO) << "Sending response for stream " << id();
+ SendHeaders(response->headers());
+ WriteData(response->body(), true);
+}
+
+void QuicReliableServerStream::SendErrorResponse() {
+ DLOG(INFO) << "Sending error response for stream " << id();
+ BalsaHeaders headers;
+ headers.SetResponseFirstlineFromStringPieces(
+ "HTTP/1.1", "500", "Server Error");
+ headers.ReplaceOrAppendHeader("content-length", "3");
+ SendHeaders(headers);
+ WriteData("bad", true);
+}
+
+QuicConsumedData QuicReliableServerStream::WriteData(StringPiece data,
+ bool fin) {
+ // We only support SPDY and HTTP, and neither handles bidirectional streaming.
+ if (!read_side_closed()) {
+ CloseReadSide();
+ }
+ return ReliableQuicStream::WriteData(data, fin);
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_reliable_server_stream.h b/chromium/net/tools/quic/quic_reliable_server_stream.h
new file mode 100644
index 00000000000..2669f42649c
--- /dev/null
+++ b/chromium/net/tools/quic/quic_reliable_server_stream.h
@@ -0,0 +1,65 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_QUIC_RELIABLE_SERVER_STREAM_H_
+#define NET_TOOLS_QUIC_QUIC_RELIABLE_SERVER_STREAM_H_
+
+#include <string>
+
+#include "net/quic/quic_protocol.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/tools/flip_server/balsa_headers.h"
+
+namespace net {
+
+class QuicSession;
+
+namespace tools {
+
+namespace test {
+class QuicReliableServerStreamPeer;
+} // namespace test
+
+// A base class for spdy/http server streams which handles the concept
+// of sending and receiving headers and bodies.
+class QuicReliableServerStream : public ReliableQuicStream {
+ public:
+ QuicReliableServerStream(QuicStreamId id, QuicSession* session);
+ virtual ~QuicReliableServerStream() {}
+
+ // Subclasses should process and frame data when this is called, returning
+ // how many bytes are processed.
+ virtual uint32 ProcessData(const char* data, uint32 data_len) = 0;
+ // Subclasses should implement this to serialize headers in a
+ // protocol-specific manner, and send it out to the client.
+ virtual void SendHeaders(const BalsaHeaders& response_headers) = 0;
+
+ // Sends a basic 200 response using SendHeaders for the headers and WriteData
+ // for the body.
+ void SendResponse();
+ // Sends a basic 500 response using SendHeaders for the headers and WriteData
+ // for the body
+ void SendErrorResponse();
+ // Make sure that as soon as we start writing data, we stop reading.
+ virtual QuicConsumedData WriteData(base::StringPiece data, bool fin) OVERRIDE;
+
+ // Returns whatever headers have been received for this stream.
+ const BalsaHeaders& headers() { return headers_; }
+
+ const string& body() { return body_; }
+ protected:
+ BalsaHeaders* mutable_headers() { return &headers_; }
+ string* mutable_body() { return &body_; }
+
+ private:
+ friend class test::QuicReliableServerStreamPeer;
+
+ BalsaHeaders headers_;
+ string body_;
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_RELIABLE_SERVER_STREAM_H_
diff --git a/chromium/net/tools/quic/quic_reliable_server_stream_test.cc b/chromium/net/tools/quic/quic_reliable_server_stream_test.cc
new file mode 100644
index 00000000000..b946d94cfc3
--- /dev/null
+++ b/chromium/net/tools/quic/quic_reliable_server_stream_test.cc
@@ -0,0 +1,219 @@
+// 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/tools/quic/quic_reliable_server_stream.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "net/quic/quic_spdy_compressor.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_spdy_server_stream.h"
+#include "net/tools/quic/spdy_utils.h"
+#include "net/tools/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using net::tools::test::MockConnection;
+using net::test::MockSession;
+using std::string;
+using testing::_;
+using testing::AnyNumber;
+using testing::Invoke;
+using testing::InvokeArgument;
+using testing::InSequence;
+using testing::Return;
+using testing::StrEq;
+using testing::StrictMock;
+using testing::WithArgs;
+
+namespace net {
+namespace tools {
+namespace test {
+
+class QuicReliableServerStreamPeer {
+ public:
+ static BalsaHeaders* GetMutableHeaders(
+ QuicReliableServerStream* stream) {
+ return &(stream->headers_);
+ }
+};
+
+namespace {
+
+class QuicReliableServerStreamTest : public ::testing::Test {
+ public:
+ QuicReliableServerStreamTest()
+ : session_(new MockConnection(1, IPEndPoint(), 0, &eps_, true), true),
+ body_("hello world") {
+ BalsaHeaders request_headers;
+ request_headers.SetRequestFirstlineFromStringPieces(
+ "POST", "https://www.google.com/", "HTTP/1.1");
+ request_headers.ReplaceOrAppendHeader("content-length", "11");
+
+ headers_string_ = SpdyUtils::SerializeRequestHeaders(request_headers);
+ stream_.reset(new QuicSpdyServerStream(3, &session_));
+ }
+
+ QuicConsumedData ValidateHeaders(StringPiece headers) {
+ headers_string_ = SpdyUtils::SerializeResponseHeaders(
+ response_headers_);
+ QuicSpdyDecompressor decompressor;
+ TestDecompressorVisitor visitor;
+
+ // First the header id, then the compressed data.
+ EXPECT_EQ(1, headers[0]);
+ EXPECT_EQ(0, headers[1]);
+ EXPECT_EQ(0, headers[2]);
+ EXPECT_EQ(0, headers[3]);
+ EXPECT_EQ(static_cast<size_t>(headers.length() - 4),
+ decompressor.DecompressData(headers.substr(4), &visitor));
+
+ EXPECT_EQ(headers_string_, visitor.data());
+
+ return QuicConsumedData(headers.size(), false);
+ }
+
+ static void SetUpTestCase() {
+ QuicInMemoryCache::GetInstance()->ResetForTests();
+ }
+
+ virtual void SetUp() {
+ QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance();
+
+ BalsaHeaders request_headers, response_headers;
+ StringPiece body("Yum");
+ request_headers.SetRequestFirstlineFromStringPieces(
+ "GET",
+ "https://www.google.com/foo",
+ "HTTP/1.1");
+ response_headers.SetRequestFirstlineFromStringPieces("HTTP/1.1",
+ "200",
+ "OK");
+ response_headers.AppendHeader("content-length",
+ base::IntToString(body.length()));
+
+ // Check if response already exists and matches.
+ const QuicInMemoryCache::Response* cached_response =
+ cache->GetResponse(request_headers);
+ if (cached_response != NULL) {
+ string cached_response_headers_str, response_headers_str;
+ cached_response->headers().DumpToString(&cached_response_headers_str);
+ response_headers.DumpToString(&response_headers_str);
+ CHECK_EQ(cached_response_headers_str, response_headers_str);
+ CHECK_EQ(cached_response->body(), body);
+ return;
+ }
+
+ cache->AddResponse(request_headers, response_headers, body);
+ }
+
+ BalsaHeaders response_headers_;
+ EpollServer eps_;
+ StrictMock<MockSession> session_;
+ scoped_ptr<QuicReliableServerStream> stream_;
+ string headers_string_;
+ string body_;
+};
+
+QuicConsumedData ConsumeAllData(QuicStreamId id, StringPiece data,
+ QuicStreamOffset offset, bool fin) {
+ return QuicConsumedData(data.size(), fin);
+}
+
+TEST_F(QuicReliableServerStreamTest, TestFraming) {
+ EXPECT_CALL(session_, WriteData(_, _, _, _)).Times(AnyNumber()).
+ WillRepeatedly(Invoke(ConsumeAllData));
+
+ EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
+ headers_string_.c_str(), headers_string_.size()));
+ EXPECT_EQ(body_.size(), stream_->ProcessData(body_.c_str(), body_.size()));
+ EXPECT_EQ(11u, stream_->headers().content_length());
+ EXPECT_EQ("https://www.google.com/", stream_->headers().request_uri());
+ EXPECT_EQ("POST", stream_->headers().request_method());
+ EXPECT_EQ(body_, stream_->body());
+}
+
+TEST_F(QuicReliableServerStreamTest, TestFramingOnePacket) {
+ EXPECT_CALL(session_, WriteData(_, _, _, _)).Times(AnyNumber()).
+ WillRepeatedly(Invoke(ConsumeAllData));
+
+ string message = headers_string_ + body_;
+
+ EXPECT_EQ(message.size(), stream_->ProcessData(
+ message.c_str(), message.size()));
+ EXPECT_EQ(11u, stream_->headers().content_length());
+ EXPECT_EQ("https://www.google.com/",
+ stream_->headers().request_uri());
+ EXPECT_EQ("POST", stream_->headers().request_method());
+ EXPECT_EQ(body_, stream_->body());
+}
+
+TEST_F(QuicReliableServerStreamTest, TestFramingExtraData) {
+ string large_body = "hello world!!!!!!";
+
+ // We'll automatically write out an error (headers + body)
+ EXPECT_CALL(session_, WriteData(_, _, _, _)).Times(2).
+ WillRepeatedly(Invoke(ConsumeAllData));
+
+ EXPECT_EQ(headers_string_.size(), stream_->ProcessData(
+ headers_string_.c_str(), headers_string_.size()));
+ // Content length is still 11. This will register as an error and we won't
+ // accept the bytes.
+ stream_->ProcessData(large_body.c_str(), large_body.size());
+ stream_->TerminateFromPeer(true);
+ EXPECT_EQ(11u, stream_->headers().content_length());
+ EXPECT_EQ("https://www.google.com/", stream_->headers().request_uri());
+ EXPECT_EQ("POST", stream_->headers().request_method());
+}
+
+TEST_F(QuicReliableServerStreamTest, TestSendResponse) {
+ BalsaHeaders* request_headers =
+ QuicReliableServerStreamPeer::GetMutableHeaders(stream_.get());
+ request_headers->SetRequestFirstlineFromStringPieces(
+ "GET",
+ "https://www.google.com/foo",
+ "HTTP/1.1");
+
+ response_headers_.SetResponseFirstlineFromStringPieces(
+ "HTTP/1.1", "200", "OK");
+ response_headers_.ReplaceOrAppendHeader("content-length", "3");
+
+ InSequence s;
+ EXPECT_CALL(session_, WriteData(_, _, _, _)).Times(1)
+ .WillOnce(WithArgs<1>(Invoke(
+ this, &QuicReliableServerStreamTest::ValidateHeaders)));
+ StringPiece kBody = "Yum";
+ EXPECT_CALL(session_, WriteData(_, kBody, _, _)).Times(1).
+ WillOnce(Return(QuicConsumedData(3, true)));
+
+ stream_->SendResponse();
+ EXPECT_TRUE(stream_->read_side_closed());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+TEST_F(QuicReliableServerStreamTest, TestSendErrorResponse) {
+ response_headers_.SetResponseFirstlineFromStringPieces(
+ "HTTP/1.1", "500", "Server Error");
+ response_headers_.ReplaceOrAppendHeader("content-length", "3");
+
+ InSequence s;
+ EXPECT_CALL(session_, WriteData(_, _, _, _)).Times(1)
+ .WillOnce(WithArgs<1>(Invoke(
+ this, &QuicReliableServerStreamTest::ValidateHeaders)));
+ StringPiece kBody = "bad";
+ EXPECT_CALL(session_, WriteData(_, kBody, _, _)).Times(1).
+ WillOnce(Return(QuicConsumedData(3, true)));
+
+ stream_->SendErrorResponse();
+ EXPECT_TRUE(stream_->read_side_closed());
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
+} // namespace
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_server.cc b/chromium/net/tools/quic/quic_server.cc
new file mode 100644
index 00000000000..d18525f634f
--- /dev/null
+++ b/chromium/net/tools/quic/quic_server.cc
@@ -0,0 +1,223 @@
+// 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/tools/quic/quic_server.h"
+
+#include <errno.h>
+#include <features.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_data_reader.h"
+#include "net/quic/quic_protocol.h"
+#include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_socket_utils.h"
+
+#define MMSG_MORE 0
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
+const int kNumPacketsPerReadCall = 5; // Arbitrary
+static const char kSourceAddressTokenSecret[] = "secret";
+
+namespace net {
+namespace tools {
+
+QuicServer::QuicServer()
+ : port_(0),
+ packets_dropped_(0),
+ overflow_supported_(false),
+ use_recvmmsg_(false),
+ crypto_config_(kSourceAddressTokenSecret, QuicRandom::GetInstance()) {
+ // Use hardcoded crypto parameters for now.
+ config_.SetDefaults();
+ Initialize();
+}
+
+QuicServer::QuicServer(const QuicConfig& config)
+ : port_(0),
+ packets_dropped_(0),
+ overflow_supported_(false),
+ use_recvmmsg_(false),
+ config_(config),
+ crypto_config_(kSourceAddressTokenSecret, QuicRandom::GetInstance()) {
+ Initialize();
+}
+
+void QuicServer::Initialize() {
+#if MMSG_MORE
+ use_recvmmsg_ = true;
+#endif
+ epoll_server_.set_timeout_in_us(50 * 1000);
+ // Initialize the in memory cache now.
+ QuicInMemoryCache::GetInstance();
+
+ QuicEpollClock clock(&epoll_server_);
+
+ scoped_ptr<CryptoHandshakeMessage> scfg(
+ crypto_config_.AddDefaultConfig(
+ QuicRandom::GetInstance(), &clock,
+ QuicCryptoServerConfig::ConfigOptions()));
+}
+
+QuicServer::~QuicServer() {
+}
+
+bool QuicServer::Listen(const IPEndPoint& address) {
+ port_ = address.port();
+ int address_family = address.GetSockAddrFamily();
+ fd_ = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
+ if (fd_ < 0) {
+ LOG(ERROR) << "CreateSocket() failed: " << strerror(errno);
+ return false;
+ }
+
+ int rc = QuicSocketUtils::SetGetAddressInfo(fd_, address_family);
+
+ if (rc < 0) {
+ LOG(ERROR) << "IP detection not supported" << strerror(errno);
+ return false;
+ }
+
+ int get_overflow = 1;
+ rc = setsockopt(
+ fd_, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow, sizeof(get_overflow));
+
+ if (rc < 0) {
+ DLOG(WARNING) << "Socket overflow detection not supported";
+ } else {
+ overflow_supported_ = true;
+ }
+
+ // Enable the socket option that allows the local address to be
+ // returned if the socket is bound to more than on address.
+ int get_local_ip = 1;
+ rc = setsockopt(fd_, IPPROTO_IP, IP_PKTINFO,
+ &get_local_ip, sizeof(get_local_ip));
+ if (rc == 0 && address_family == AF_INET6) {
+ rc = setsockopt(fd_, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &get_local_ip, sizeof(get_local_ip));
+ }
+ if (rc != 0) {
+ LOG(ERROR) << "Failed to set required socket options";
+ return false;
+ }
+
+ sockaddr_storage raw_addr;
+ socklen_t raw_addr_len = sizeof(raw_addr);
+ CHECK(address.ToSockAddr(reinterpret_cast<sockaddr*>(&raw_addr),
+ &raw_addr_len));
+ rc = bind(fd_,
+ reinterpret_cast<const sockaddr*>(&raw_addr),
+ sizeof(raw_addr));
+ if (rc < 0) {
+ LOG(ERROR) << "Bind failed: " << strerror(errno);
+ return false;
+ }
+
+ LOG(INFO) << "Listening on " << address.ToString();
+ if (port_ == 0) {
+ SockaddrStorage storage;
+ IPEndPoint server_address;
+ if (getsockname(fd_, storage.addr, &storage.addr_len) != 0 ||
+ !server_address.FromSockAddr(storage.addr, storage.addr_len)) {
+ LOG(ERROR) << "Unable to get self address. Error: " << strerror(errno);
+ return false;
+ }
+ port_ = server_address.port();
+ LOG(INFO) << "Kernel assigned port is " << port_;
+ }
+
+ epoll_server_.RegisterFD(fd_, this, kEpollFlags);
+ dispatcher_.reset(new QuicDispatcher(config_, crypto_config_, fd_,
+ &epoll_server_));
+
+ return true;
+}
+
+void QuicServer::WaitForEvents() {
+ epoll_server_.WaitForEventsAndExecuteCallbacks();
+}
+
+void QuicServer::Shutdown() {
+ // Before we shut down the epoll server, give all active sessions a chance to
+ // notify clients that they're closing.
+ dispatcher_->Shutdown();
+}
+
+void QuicServer::OnEvent(int fd, EpollEvent* event) {
+ DCHECK_EQ(fd, fd_);
+ event->out_ready_mask = 0;
+
+ if (event->in_events & EPOLLIN) {
+ LOG(ERROR) << "EPOLLIN";
+ bool read = true;
+ while (read) {
+ read = ReadAndDispatchSinglePacket(
+ fd_, port_, dispatcher_.get(),
+ overflow_supported_ ? &packets_dropped_ : NULL);
+ }
+ }
+ if (event->in_events & EPOLLOUT) {
+ bool can_write_more = dispatcher_->OnCanWrite();
+ if (can_write_more) {
+ event->out_ready_mask |= EPOLLOUT;
+ }
+ }
+ if (event->in_events & EPOLLERR) {
+ }
+}
+
+/* static */
+void QuicServer::MaybeDispatchPacket(QuicDispatcher* dispatcher,
+ const QuicEncryptedPacket& packet,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address) {
+ QuicGuid guid;
+ if (!QuicFramer::ReadGuidFromPacket(packet, &guid)) {
+ return;
+ }
+
+ dispatcher->ProcessPacket(server_address, client_address, guid, packet);
+}
+
+bool QuicServer::ReadAndDispatchSinglePacket(int fd,
+ int port,
+ QuicDispatcher* dispatcher,
+ int* packets_dropped) {
+ // Allocate some extra space so we can send an error if the client goes over
+ // the limit.
+ char buf[2 * kMaxPacketSize];
+
+ IPEndPoint client_address;
+ IPAddressNumber server_ip;
+ int bytes_read =
+ QuicSocketUtils::ReadPacket(fd, buf, arraysize(buf),
+ packets_dropped,
+ &server_ip, &client_address);
+
+ if (bytes_read < 0) {
+ return false; // We failed to read.
+ }
+
+ QuicEncryptedPacket packet(buf, bytes_read, false);
+
+ IPEndPoint server_address(server_ip, port);
+ MaybeDispatchPacket(dispatcher, packet, server_address, client_address);
+
+ return true;
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_server.h b/chromium/net/tools/quic/quic_server.h
new file mode 100644
index 00000000000..142d1d1572c
--- /dev/null
+++ b/chromium/net/tools/quic/quic_server.h
@@ -0,0 +1,115 @@
+// 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.
+//
+// A toy server, which listens on a specified address for QUIC traffic and
+// handles incoming responses.
+
+#ifndef NET_TOOLS_QUIC_QUIC_SERVER_H_
+#define NET_TOOLS_QUIC_QUIC_SERVER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/crypto_server_config.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_framer.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "net/tools/quic/quic_dispatcher.h"
+
+namespace net {
+
+class QuicCryptoServerConfig;
+
+namespace tools {
+
+class QuicDispatcher;
+
+class QuicServer : public EpollCallbackInterface {
+ public:
+ QuicServer();
+ explicit QuicServer(const QuicConfig& config);
+
+ virtual ~QuicServer();
+
+ // Start listening on the specified address.
+ bool Listen(const IPEndPoint& address);
+
+ // Wait up to 50ms, and handle any events which occur.
+ void WaitForEvents();
+
+ // Server deletion is imminent. Start cleaning up the epoll server.
+ void Shutdown();
+
+ // From EpollCallbackInterface
+ virtual void OnRegistration(
+ EpollServer* eps, int fd, int event_mask) OVERRIDE {}
+ virtual void OnModification(int fd, int event_mask) OVERRIDE {}
+ virtual void OnEvent(int fd, EpollEvent* event) OVERRIDE;
+ virtual void OnUnregistration(int fd, bool replaced) OVERRIDE {}
+
+ // Reads a packet from the given fd, and then passes it off to
+ // the QuicDispatcher. Returns true if a packet is read, false
+ // otherwise.
+ // If packets_dropped is non-null, the socket is configured to track
+ // dropped packets, and some packets are read, it will be set to the number of
+ // dropped packets.
+ static bool ReadAndDispatchSinglePacket(int fd, int port,
+ QuicDispatcher* dispatcher,
+ int* packets_dropped);
+
+ virtual void OnShutdown(EpollServer* eps, int fd) OVERRIDE {}
+
+ // Dispatches the given packet only if it looks like a valid QUIC packet.
+ // TODO(rjshade): Return a status describing why a packet was dropped, and log
+ // somehow. Maybe expose as a varz.
+ static void MaybeDispatchPacket(QuicDispatcher* dispatcher,
+ const QuicEncryptedPacket& packet,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address);
+
+ bool overflow_supported() { return overflow_supported_; }
+
+ int packets_dropped() { return packets_dropped_; }
+
+ int port() { return port_; }
+
+ private:
+ // Initialize the internal state of the server.
+ void Initialize();
+
+ // Accepts data from the framer and demuxes clients to sessions.
+ scoped_ptr<QuicDispatcher> dispatcher_;
+ // Frames incoming packets and hands them to the dispatcher.
+ EpollServer epoll_server_;
+
+ // The port the server is listening on.
+ int port_;
+
+ // Listening connection. Also used for outbound client communication.
+ int fd_;
+
+ // If overflow_supported_ is true this will be the number of packets dropped
+ // during the lifetime of the server. This may overflow if enough packets
+ // are dropped.
+ int packets_dropped_;
+
+ // True if the kernel supports SO_RXQ_OVFL, the number of packets dropped
+ // because the socket would otherwise overflow.
+ bool overflow_supported_;
+
+ // If true, use recvmmsg for reading.
+ bool use_recvmmsg_;
+
+ // config_ contains non-crypto parameters that are negotiated in the crypto
+ // handshake.
+ QuicConfig config_;
+ // crypto_config_ contains crypto parameters for the handshake.
+ QuicCryptoServerConfig crypto_config_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServer);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_SERVER_H_
diff --git a/chromium/net/tools/quic/quic_server_bin.cc b/chromium/net/tools/quic/quic_server_bin.cc
new file mode 100644
index 00000000000..cccf57819f5
--- /dev/null
+++ b/chromium/net/tools/quic/quic_server_bin.cc
@@ -0,0 +1,51 @@
+// 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.
+//
+// A binary wrapper for QuicServer. It listens forever on --port
+// (default 6121) until it's killed or ctrl-cd to death.
+
+#include "base/at_exit.h"
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/base/ip_endpoint.h"
+#include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_server.h"
+
+// The port the quic server will listen on.
+
+int32 FLAGS_port = 6121;
+
+int main(int argc, char *argv[]) {
+ CommandLine::Init(argc, argv);
+ CommandLine* line = CommandLine::ForCurrentProcess();
+ if (line->HasSwitch("quic_in_memory_cache_dir")) {
+ net::tools::FLAGS_quic_in_memory_cache_dir =
+ line->GetSwitchValueASCII("quic_in_memory_cache_dir");
+ }
+
+ if (line->HasSwitch("port")) {
+ int port;
+ if (base::StringToInt(line->GetSwitchValueASCII("port"), &port)) {
+ FLAGS_port = port;
+ }
+ }
+
+ base::AtExitManager exit_manager;
+
+ net::IPAddressNumber ip;
+ CHECK(net::ParseIPLiteralToNumber("::", &ip));
+
+ net::tools::QuicServer server;
+
+ if (!server.Listen(net::IPEndPoint(ip, FLAGS_port))) {
+ return 1;
+ }
+
+ while (1) {
+ server.WaitForEvents();
+ }
+
+ return 0;
+}
diff --git a/chromium/net/tools/quic/quic_server_session.cc b/chromium/net/tools/quic/quic_server_session.cc
new file mode 100644
index 00000000000..7ec991c4aa9
--- /dev/null
+++ b/chromium/net/tools/quic/quic_server_session.cc
@@ -0,0 +1,74 @@
+// 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/tools/quic/quic_server_session.h"
+
+#include "base/logging.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/tools/quic/quic_spdy_server_stream.h"
+
+namespace net {
+namespace tools {
+
+QuicServerSession::QuicServerSession(
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicSessionOwner* owner)
+ : QuicSession(connection, config, true),
+ owner_(owner) {
+}
+
+QuicServerSession::~QuicServerSession() {
+}
+
+void QuicServerSession::InitializeSession(
+ const QuicCryptoServerConfig& crypto_config) {
+ crypto_stream_.reset(CreateQuicCryptoServerStream(crypto_config));
+}
+
+QuicCryptoServerStream* QuicServerSession::CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig& crypto_config) {
+ return new QuicCryptoServerStream(crypto_config, this);
+}
+
+void QuicServerSession::ConnectionClose(QuicErrorCode error, bool from_peer) {
+ QuicSession::ConnectionClose(error, from_peer);
+ owner_->OnConnectionClose(connection()->guid(), error);
+}
+
+bool QuicServerSession::ShouldCreateIncomingReliableStream(QuicStreamId id) {
+ if (id % 2 == 0) {
+ DLOG(INFO) << "Invalid incoming even stream_id:" << id;
+ connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
+ return false;
+ }
+ if (GetNumOpenStreams() >= get_max_open_streams()) {
+ DLOG(INFO) << "Failed to create a new incoming stream with id:" << id
+ << " Already " << GetNumOpenStreams() << " open.";
+ connection()->SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS);
+ return false;
+ }
+ return true;
+}
+
+ReliableQuicStream* QuicServerSession::CreateIncomingReliableStream(
+ QuicStreamId id) {
+ if (!ShouldCreateIncomingReliableStream(id)) {
+ return NULL;
+ }
+
+ return new QuicSpdyServerStream(id, this);
+}
+
+ReliableQuicStream* QuicServerSession::CreateOutgoingReliableStream() {
+ DLOG(ERROR) << "Server push not yet supported";
+ return NULL;
+}
+
+QuicCryptoServerStream* QuicServerSession::GetCryptoStream() {
+ return crypto_stream_.get();
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_server_session.h b/chromium/net/tools/quic/quic_server_session.h
new file mode 100644
index 00000000000..604b9fc0c42
--- /dev/null
+++ b/chromium/net/tools/quic/quic_server_session.h
@@ -0,0 +1,78 @@
+// 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.
+//
+// A server specific QuicSession subclass.
+
+#ifndef NET_TOOLS_QUIC_QUIC_SERVER_SESSION_H_
+#define NET_TOOLS_QUIC_QUIC_SERVER_SESSION_H_
+
+#include <set>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/quic_crypto_server_stream.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_session.h"
+
+namespace net {
+
+class QuicConfig;
+class QuicConnection;
+class QuicCryptoServerConfig;
+class ReliableQuicStream;
+
+namespace tools {
+
+// An interface from the session to the entity owning the session.
+// This lets the session notify its owner (the Dispatcher) when the connection
+// is closed.
+class QuicSessionOwner {
+ public:
+ virtual ~QuicSessionOwner() {}
+
+ virtual void OnConnectionClose(QuicGuid guid, QuicErrorCode error) = 0;
+};
+
+class QuicServerSession : public QuicSession {
+ public:
+ QuicServerSession(const QuicConfig& config,
+ QuicConnection *connection,
+ QuicSessionOwner* owner);
+
+ // Override the base class to notify the owner of the connection close.
+ virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE;
+
+ virtual ~QuicServerSession();
+
+ virtual void InitializeSession(const QuicCryptoServerConfig& crypto_config);
+
+ const QuicCryptoServerStream* crypto_stream() { return crypto_stream_.get(); }
+
+ protected:
+ // QuicSession methods:
+ virtual ReliableQuicStream* CreateIncomingReliableStream(
+ QuicStreamId id) OVERRIDE;
+ virtual ReliableQuicStream* CreateOutgoingReliableStream() OVERRIDE;
+ virtual QuicCryptoServerStream* GetCryptoStream() OVERRIDE;
+
+ // If we should create an incoming stream, returns true. Otherwise
+ // does error handling, including communicating the error to the client and
+ // possibly closing the connection, and returns false.
+ virtual bool ShouldCreateIncomingReliableStream(QuicStreamId id);
+
+ virtual QuicCryptoServerStream* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig& crypto_config);
+
+ private:
+ scoped_ptr<QuicCryptoServerStream> crypto_stream_;
+ QuicSessionOwner* owner_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerSession);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_SERVER_SESSION_H_
diff --git a/chromium/net/tools/quic/quic_server_test.cc b/chromium/net/tools/quic/quic_server_test.cc
new file mode 100644
index 00000000000..7d1d66939c0
--- /dev/null
+++ b/chromium/net/tools/quic/quic_server_test.cc
@@ -0,0 +1,74 @@
+// Copyright 2013 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/tools/quic/quic_server.h"
+
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_utils.h"
+#include "net/tools/quic/test_tools/mock_quic_dispatcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+
+namespace net {
+namespace tools {
+namespace test {
+
+namespace {
+
+class QuicServerDispatchPacketTest : public ::testing::Test {
+ public:
+ QuicServerDispatchPacketTest()
+ : crypto_config_("blah", QuicRandom::GetInstance()),
+ dispatcher_(config_, crypto_config_, 1234, &eps_) {}
+
+
+ void MaybeDispatchPacket(const QuicEncryptedPacket& packet) {
+ IPEndPoint client_addr, server_addr;
+ QuicServer::MaybeDispatchPacket(&dispatcher_, packet,
+ client_addr, server_addr);
+ }
+
+ protected:
+ QuicConfig config_;
+ QuicCryptoServerConfig crypto_config_;
+ EpollServer eps_;
+ MockQuicDispatcher dispatcher_;
+};
+
+TEST_F(QuicServerDispatchPacketTest, DoNotDispatchPacketWithoutGUID) {
+ // Packet too short to be considered valid.
+ unsigned char invalid_packet[] = { 0x00 };
+ QuicEncryptedPacket encrypted_invalid_packet(
+ QuicUtils::AsChars(invalid_packet), arraysize(invalid_packet), false);
+
+ // We expect the invalid packet to be dropped, and ProcessPacket should never
+ // be called.
+ EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _, _)).Times(0);
+ MaybeDispatchPacket(encrypted_invalid_packet);
+}
+
+TEST_F(QuicServerDispatchPacketTest, DispatchValidPacket) {
+ unsigned char valid_packet[] = {
+ // public flags (8 byte guid)
+ 0x3C,
+ // guid
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00 };
+ QuicEncryptedPacket encrypted_valid_packet(QuicUtils::AsChars(valid_packet),
+ arraysize(valid_packet), false);
+
+ EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _, _)).Times(1);
+ MaybeDispatchPacket(encrypted_valid_packet);
+}
+
+} // namespace
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_socket_utils.cc b/chromium/net/tools/quic/quic_socket_utils.cc
new file mode 100644
index 00000000000..4d7d3600760
--- /dev/null
+++ b/chromium/net/tools/quic/quic_socket_utils.cc
@@ -0,0 +1,191 @@
+// 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/tools/quic/quic_socket_utils.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <string>
+
+#include "base/logging.h"
+
+#ifndef SO_RXQ_OVFL
+#define SO_RXQ_OVFL 40
+#endif
+
+namespace net {
+namespace tools {
+
+// static
+IPAddressNumber QuicSocketUtils::GetAddressFromMsghdr(struct msghdr *hdr) {
+ IPAddressNumber ret;
+ if (hdr->msg_controllen > 0) {
+ for (cmsghdr* cmsg = CMSG_FIRSTHDR(hdr);
+ cmsg != NULL;
+ cmsg = CMSG_NXTHDR(hdr, cmsg)) {
+ const uint8* addr_data = reinterpret_cast<const uint8*>CMSG_DATA(cmsg);
+ int len = 0;
+ if (cmsg->cmsg_type == IPV6_PKTINFO) {
+ len = sizeof(in6_pktinfo);
+ } else if (cmsg->cmsg_type == IP_PKTINFO) {
+ len = sizeof(in_pktinfo);
+ }
+ ret.assign(addr_data, addr_data + len);
+ break;
+ }
+ }
+ return ret;
+}
+
+// static
+bool QuicSocketUtils::GetOverflowFromMsghdr(struct msghdr *hdr,
+ int *dropped_packets) {
+ if (hdr->msg_controllen > 0) {
+ struct cmsghdr *cmsg;
+ for (cmsg = CMSG_FIRSTHDR(hdr);
+ cmsg != NULL;
+ cmsg = CMSG_NXTHDR(hdr, cmsg)) {
+ if (cmsg->cmsg_type == SO_RXQ_OVFL) {
+ *dropped_packets = *(reinterpret_cast<int*>CMSG_DATA(cmsg));
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// static
+int QuicSocketUtils::SetGetAddressInfo(int fd, int address_family) {
+ int get_local_ip = 1;
+ if (address_family == AF_INET) {
+ return setsockopt(fd, IPPROTO_IP, IP_PKTINFO,
+ &get_local_ip, sizeof(get_local_ip));
+ } else {
+ return setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &get_local_ip, sizeof(get_local_ip));
+ }
+}
+
+// static
+int QuicSocketUtils::ReadPacket(int fd, char* buffer, size_t buf_len,
+ int* dropped_packets,
+ IPAddressNumber* self_address,
+ IPEndPoint* peer_address) {
+ CHECK(peer_address != NULL);
+ const int kSpaceForOverflowAndIp =
+ CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(in6_pktinfo));
+ char cbuf[kSpaceForOverflowAndIp];
+ memset(cbuf, 0, arraysize(cbuf));
+
+ iovec iov = {buffer, buf_len};
+ struct sockaddr_storage raw_address;
+ msghdr hdr;
+
+ hdr.msg_name = &raw_address;
+ hdr.msg_namelen = sizeof(sockaddr_storage);
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ hdr.msg_flags = 0;
+
+ struct cmsghdr *cmsg = (struct cmsghdr *) cbuf;
+ cmsg->cmsg_len = arraysize(cbuf);
+ hdr.msg_control = cmsg;
+ hdr.msg_controllen = arraysize(cbuf);
+
+ int bytes_read = recvmsg(fd, &hdr, 0);
+
+ // Return before setting dropped packets: if we get EAGAIN, it will
+ // be 0.
+ if (bytes_read < 0 && errno != 0) {
+ if (errno != EAGAIN) {
+ LOG(ERROR) << "Error reading " << strerror(errno);
+ }
+ return -1;
+ }
+
+ if (dropped_packets != NULL) {
+ GetOverflowFromMsghdr(&hdr, dropped_packets);
+ }
+ if (self_address != NULL) {
+ *self_address = QuicSocketUtils::GetAddressFromMsghdr(&hdr);
+ }
+
+ if (raw_address.ss_family == AF_INET) {
+ CHECK(peer_address->FromSockAddr(
+ reinterpret_cast<const sockaddr*>(&raw_address),
+ sizeof(struct sockaddr_in)));
+ } else if (raw_address.ss_family == AF_INET6) {
+ CHECK(peer_address->FromSockAddr(
+ reinterpret_cast<const sockaddr*>(&raw_address),
+ sizeof(struct sockaddr_in6)));
+ }
+
+ return bytes_read;
+}
+
+// static
+int QuicSocketUtils::WritePacket(int fd, const char* buffer, size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address,
+ int* error) {
+ sockaddr_storage raw_address;
+ socklen_t address_len = sizeof(raw_address);
+ CHECK(peer_address.ToSockAddr(
+ reinterpret_cast<struct sockaddr*>(&raw_address),
+ &address_len));
+ iovec iov = {const_cast<char*>(buffer), buf_len};
+
+ msghdr hdr;
+ hdr.msg_name = &raw_address;
+ hdr.msg_namelen = address_len;
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ hdr.msg_flags = 0;
+
+ const int kSpaceForIpv4 = CMSG_SPACE(sizeof(in_pktinfo));
+ const int kSpaceForIpv6 = CMSG_SPACE(sizeof(in6_pktinfo));
+ // kSpaceForIp should be big enough to hold both IPv4 and IPv6 packet info.
+ const int kSpaceForIp =
+ (kSpaceForIpv4 < kSpaceForIpv6) ? kSpaceForIpv6 : kSpaceForIpv4;
+ char cbuf[kSpaceForIp];
+ if (self_address.empty()) {
+ hdr.msg_control = 0;
+ hdr.msg_controllen = 0;
+ } else if (GetAddressFamily(self_address) == ADDRESS_FAMILY_IPV4) {
+ hdr.msg_control = cbuf;
+ hdr.msg_controllen = kSpaceForIp;
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+
+ cmsg->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ in_pktinfo* pktinfo = reinterpret_cast<in_pktinfo*>(CMSG_DATA(cmsg));
+ memset(pktinfo, 0, sizeof(in_pktinfo));
+ pktinfo->ipi_ifindex = 0;
+ memcpy(&pktinfo->ipi_spec_dst, &self_address[0], self_address.size());
+ hdr.msg_controllen = cmsg->cmsg_len;
+ } else {
+ hdr.msg_control = cbuf;
+ hdr.msg_controllen = kSpaceForIp;
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);
+
+ cmsg->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+ in6_pktinfo* pktinfo = reinterpret_cast<in6_pktinfo*>(CMSG_DATA(cmsg));
+ memset(pktinfo, 0, sizeof(in6_pktinfo));
+ memcpy(&pktinfo->ipi6_addr, &self_address[0], self_address.size());
+ hdr.msg_controllen = cmsg->cmsg_len;
+ }
+
+ int rc = sendmsg(fd, &hdr, 0);
+ *error = (rc >= 0) ? 0 : errno;
+ return rc;
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_socket_utils.h b/chromium/net/tools/quic/quic_socket_utils.h
new file mode 100644
index 00000000000..bfacc6c4719
--- /dev/null
+++ b/chromium/net/tools/quic/quic_socket_utils.h
@@ -0,0 +1,59 @@
+// 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.
+//
+// Some socket related helper methods for quic.
+
+#ifndef NET_TOOLS_QUIC_QUIC_SOCKET_UTILS_H_
+#define NET_TOOLS_QUIC_QUIC_SOCKET_UTILS_H_
+
+#include <stddef.h>
+#include <sys/socket.h>
+#include <string>
+
+#include "net/base/ip_endpoint.h"
+
+namespace net {
+namespace tools {
+
+class QuicSocketUtils {
+ public:
+ // If the msghdr contains IP_PKTINFO or IPV6_PKTINFO, this will return the
+ // IPAddressNumber in that header. Returns an uninitialized IPAddress on
+ // failure.
+ static IPAddressNumber GetAddressFromMsghdr(struct msghdr *hdr);
+
+ // If the msghdr contains an SO_RXQ_OVFL entry, this will set dropped_packets
+ // to the correct value and return true. Otherwise it will return false.
+ static bool GetOverflowFromMsghdr(struct msghdr *hdr, int *dropped_packets);
+
+ // Sets either IP_PKTINFO or IPV6_PKTINFO on the socket, based on
+ // address_family. Returns the return code from setsockopt.
+ static int SetGetAddressInfo(int fd, int address_family);
+
+ // Reads buf_len from the socket. If reading is successful, returns bytes
+ // read and sets peer_address to the peer address. Otherwise returns -1.
+ //
+ // If dropped_packets is non-null, it will be set to the number of packets
+ // dropped on the socket since the socket was created, assuming the kernel
+ // supports this feature.
+ //
+ // If self_address is non-null, it will be set to the address the peer sent
+ // packets to, assuming a packet was read.
+ static int ReadPacket(int fd, char* buffer, size_t buf_len,
+ int* dropped_packets,
+ IPAddressNumber* self_address,
+ IPEndPoint* peer_address);
+
+ // Writes buf_len to the socket. If writing is successful returns the number
+ // of bytes written otherwise returns -1 and sets error to errno.
+ static int WritePacket(int fd, const char* buffer, size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address,
+ int* error);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_SOCKET_UTILS_H_
diff --git a/chromium/net/tools/quic/quic_spdy_client_stream.cc b/chromium/net/tools/quic/quic_spdy_client_stream.cc
new file mode 100644
index 00000000000..62949534b3d
--- /dev/null
+++ b/chromium/net/tools/quic/quic_spdy_client_stream.cc
@@ -0,0 +1,104 @@
+// 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/tools/quic/quic_spdy_client_stream.h"
+
+#include "net/spdy/spdy_framer.h"
+#include "net/tools/quic/quic_client_session.h"
+#include "net/tools/quic/spdy_utils.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace tools {
+
+static const size_t kHeaderBufInitialSize = 4096;
+
+QuicSpdyClientStream::QuicSpdyClientStream(QuicStreamId id,
+ QuicClientSession* session)
+ : QuicReliableClientStream(id, session),
+ read_buf_(new GrowableIOBuffer()),
+ response_headers_received_(false) {
+}
+
+QuicSpdyClientStream::~QuicSpdyClientStream() {
+}
+
+uint32 QuicSpdyClientStream::ProcessData(const char* data, uint32 length) {
+ uint32 total_bytes_processed = 0;
+
+ // Are we still reading the response headers.
+ if (!response_headers_received_) {
+ // Grow the read buffer if necessary.
+ if (read_buf_->RemainingCapacity() < (int)length) {
+ read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
+ }
+ memcpy(read_buf_->data(), data, length);
+ read_buf_->set_offset(read_buf_->offset() + length);
+ ParseResponseHeaders();
+ } else {
+ mutable_data()->append(data + total_bytes_processed,
+ length - total_bytes_processed);
+ }
+ return length;
+}
+
+void QuicSpdyClientStream::TerminateFromPeer(bool half_close) {
+ ReliableQuicStream::TerminateFromPeer(half_close);
+ if (!response_headers_received_) {
+ Close(QUIC_BAD_APPLICATION_PAYLOAD);
+ } else if ((headers().content_length_status() ==
+ BalsaHeadersEnums::VALID_CONTENT_LENGTH) &&
+ mutable_data()->size() != headers().content_length()) {
+ Close(QUIC_BAD_APPLICATION_PAYLOAD);
+ }
+}
+
+ssize_t QuicSpdyClientStream::SendRequest(const BalsaHeaders& headers,
+ StringPiece body,
+ bool fin) {
+ SpdyHeaderBlock header_block =
+ SpdyUtils::RequestHeadersToSpdyHeaders(headers);
+
+ string headers_string =
+ session()->compressor()->CompressHeaders(header_block);
+
+ bool has_body = !body.empty();
+
+ WriteData(headers_string, fin && !has_body); // last_data
+
+ if (has_body) {
+ WriteData(body, fin);
+ }
+
+ return headers_string.size() + body.size();
+}
+
+int QuicSpdyClientStream::ParseResponseHeaders() {
+ size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
+ SpdyFramer framer(SPDY3);
+ SpdyHeaderBlock headers;
+ char* data = read_buf_->StartOfBuffer();
+ size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(),
+ &headers);
+ if (len == 0) {
+ return -1;
+ }
+
+ if (!SpdyUtils::FillBalsaResponseHeaders(headers, mutable_headers())) {
+ Close(QUIC_BAD_APPLICATION_PAYLOAD);
+ return -1;
+ }
+
+ size_t delta = read_buf_len - len;
+ if (delta > 0) {
+ mutable_data()->append(data + len, delta);
+ }
+
+ return len;
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_spdy_client_stream.h b/chromium/net/tools/quic/quic_spdy_client_stream.h
new file mode 100644
index 00000000000..ec4d25747f7
--- /dev/null
+++ b/chromium/net/tools/quic/quic_spdy_client_stream.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_QUIC_SPDY_CLIENT_STREAM_H_
+#define NET_TOOLS_QUIC_QUIC_SPDY_CLIENT_STREAM_H_
+
+#include "base/strings/string_piece.h"
+#include "net/base/io_buffer.h"
+#include "net/tools/quic/quic_reliable_client_stream.h"
+
+namespace net {
+
+class BalsaHeaders;
+
+namespace tools {
+
+class QuicClientSession;
+
+// All this does right now is send an SPDY request, and aggregate the
+// SPDY response.
+class QuicSpdyClientStream : public QuicReliableClientStream {
+ public:
+ QuicSpdyClientStream(QuicStreamId id, QuicClientSession* session);
+ virtual ~QuicSpdyClientStream();
+
+ // ReliableQuicStream implementation called by the session when there's
+ // data for us.
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE;
+
+ virtual void TerminateFromPeer(bool half_close) OVERRIDE;
+
+ virtual ssize_t SendRequest(const BalsaHeaders& headers,
+ base::StringPiece body,
+ bool fin) OVERRIDE;
+
+ private:
+ int ParseResponseHeaders();
+
+ scoped_refptr<GrowableIOBuffer> read_buf_;
+ bool response_headers_received_;
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_SPDY_CLIENT_STREAM_H_
diff --git a/chromium/net/tools/quic/quic_spdy_server_stream.cc b/chromium/net/tools/quic/quic_spdy_server_stream.cc
new file mode 100644
index 00000000000..d6f3b7590ff
--- /dev/null
+++ b/chromium/net/tools/quic/quic_spdy_server_stream.cc
@@ -0,0 +1,104 @@
+// 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/tools/quic/quic_spdy_server_stream.h"
+
+#include "net/quic/quic_session.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/tools/quic/spdy_utils.h"
+
+using std::string;
+
+namespace net {
+namespace tools {
+
+static const size_t kHeaderBufInitialSize = 4096;
+
+QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id,
+ QuicSession* session)
+ : QuicReliableServerStream(id, session),
+ read_buf_(new GrowableIOBuffer()),
+ request_headers_received_(false) {
+}
+
+QuicSpdyServerStream::~QuicSpdyServerStream() {
+}
+
+uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 length) {
+ uint32 total_bytes_processed = 0;
+
+ // Are we still reading the request headers.
+ if (!request_headers_received_) {
+ // Grow the read buffer if necessary.
+ if (read_buf_->RemainingCapacity() < (int)length) {
+ read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize);
+ }
+ memcpy(read_buf_->data(), data, length);
+ read_buf_->set_offset(read_buf_->offset() + length);
+ ParseRequestHeaders();
+ } else {
+ mutable_body()->append(data + total_bytes_processed,
+ length - total_bytes_processed);
+ }
+ return length;
+}
+
+void QuicSpdyServerStream::TerminateFromPeer(bool half_close) {
+ ReliableQuicStream::TerminateFromPeer(half_close);
+ // This is a full close: do not send a response.
+ if (!half_close) {
+ return;
+ }
+ if (write_side_closed() || fin_buffered()) {
+ return;
+ }
+
+ if (!request_headers_received_) {
+ SendErrorResponse(); // We're not done writing headers.
+ } else if ((headers().content_length_status() ==
+ BalsaHeadersEnums::VALID_CONTENT_LENGTH) &&
+ mutable_body()->size() != headers().content_length()) {
+ SendErrorResponse(); // Invalid content length
+ } else {
+ SendResponse();
+ }
+}
+
+void QuicSpdyServerStream::SendHeaders(
+ const BalsaHeaders& response_headers) {
+ SpdyHeaderBlock header_block =
+ SpdyUtils::ResponseHeadersToSpdyHeaders(response_headers);
+ string headers =
+ session()->compressor()->CompressHeaders(header_block);
+
+ WriteData(headers, false);
+}
+
+int QuicSpdyServerStream::ParseRequestHeaders() {
+ size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
+ SpdyFramer framer(SPDY3);
+ SpdyHeaderBlock headers;
+ char* data = read_buf_->StartOfBuffer();
+ size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(),
+ &headers);
+ if (len == 0) {
+ return -1;
+ }
+
+ if (!SpdyUtils::FillBalsaRequestHeaders(headers, mutable_headers())) {
+ SendErrorResponse();
+ return -1;
+ }
+
+ size_t delta = read_buf_len - len;
+ if (delta > 0) {
+ mutable_body()->append(data + len, delta);
+ }
+
+ request_headers_received_ = true;
+ return len;
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_spdy_server_stream.h b/chromium/net/tools/quic/quic_spdy_server_stream.h
new file mode 100644
index 00000000000..8ed2a8f3d65
--- /dev/null
+++ b/chromium/net/tools/quic/quic_spdy_server_stream.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_QUIC_SPDY_SERVER_STREAM_H_
+#define NET_TOOLS_QUIC_QUIC_SPDY_SERVER_STREAM_H_
+
+#include <string>
+
+#include "net/base/io_buffer.h"
+#include "net/tools/quic/quic_reliable_server_stream.h"
+
+namespace net {
+
+class QuicSession;
+
+namespace tools {
+
+// All this does right now is aggregate data, and on fin, send a cached
+// response.
+class QuicSpdyServerStream : public QuicReliableServerStream {
+ public:
+ QuicSpdyServerStream(QuicStreamId id, QuicSession* session);
+ virtual ~QuicSpdyServerStream();
+
+ // ReliableQuicStream implementation called by the session when there's
+ // data for us.
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE;
+
+ virtual void SendHeaders(const BalsaHeaders& response_headers) OVERRIDE;
+
+ int ParseRequestHeaders();
+
+ protected:
+ virtual void TerminateFromPeer(bool half_close) OVERRIDE;
+
+ // Buffer into which response header data is read.
+ scoped_refptr<GrowableIOBuffer> read_buf_;
+ bool request_headers_received_;
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_SPDY_SERVER_STREAM_H_
diff --git a/chromium/net/tools/quic/quic_spdy_server_stream_test.cc b/chromium/net/tools/quic/quic_spdy_server_stream_test.cc
new file mode 100644
index 00000000000..8168fcbd4c1
--- /dev/null
+++ b/chromium/net/tools/quic/quic_spdy_server_stream_test.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2013 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/tools/quic/quic_spdy_server_stream.h"
+
+#include "base/strings/string_piece.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/tools/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using net::tools::test::MockConnection;
+using net::test::MockSession;
+
+namespace net {
+namespace tools {
+namespace test {
+namespace {
+
+class QuicSpdyServerStreamTest : public ::testing::Test {
+ public:
+ QuicSpdyServerStreamTest()
+ : connection_(new MockConnection(1, IPEndPoint(), false)),
+ session_(connection_, true),
+ stream_(1, &session_) {
+ }
+
+ MockConnection* connection_;
+ MockSession session_;
+ QuicSpdyServerStream stream_;
+};
+
+TEST_F(QuicSpdyServerStreamTest, InvalidHeadersWithFin) {
+ char arr[] = {
+ 0x00, 0x00, 0x00, 0x05, // ....
+ 0x00, 0x00, 0x00, 0x05, // ....
+ 0x3a, 0x68, 0x6f, 0x73, // :hos
+ 0x74, 0x00, 0x00, 0x00, // t...
+ 0x00, 0x00, 0x00, 0x00, // ....
+ 0x07, 0x3a, 0x6d, 0x65, // .:me
+ 0x74, 0x68, 0x6f, 0x64, // thod
+ 0x00, 0x00, 0x00, 0x03, // ....
+ 0x47, 0x45, 0x54, 0x00, // GET.
+ 0x00, 0x00, 0x05, 0x3a, // ...:
+ 0x70, 0x61, 0x74, 0x68, // path
+ 0x00, 0x00, 0x00, 0x04, // ....
+ 0x2f, 0x66, 0x6f, 0x6f, // /foo
+ 0x00, 0x00, 0x00, 0x07, // ....
+ 0x3a, 0x73, 0x63, 0x68, // :sch
+ 0x65, 0x6d, 0x65, 0x00, // eme.
+ 0x00, 0x00, 0x00, 0x00, // ....
+ 0x00, 0x00, 0x08, 0x3a, // ...:
+ 0x76, 0x65, 0x72, 0x73, // vers
+ '\x96', 0x6f, 0x6e, 0x00, // <i(69)>on.
+ 0x00, 0x00, 0x08, 0x48, // ...H
+ 0x54, 0x54, 0x50, 0x2f, // TTP/
+ 0x31, 0x2e, 0x31, // 1.1
+ };
+ QuicStreamFrame frame(1, true, 0, StringPiece(arr, arraysize(arr)));
+ // Verify that we don't crash when we get a invalid headers in stream frame.
+ stream_.OnStreamFrame(frame);
+}
+
+} // namespace
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager.cc b/chromium/net/tools/quic/quic_time_wait_list_manager.cc
new file mode 100644
index 00000000000..a2fc4f84cb6
--- /dev/null
+++ b/chromium/net/tools/quic/quic_time_wait_list_manager.cc
@@ -0,0 +1,322 @@
+// 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/tools/quic/quic_time_wait_list_manager.h"
+
+#include <errno.h>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_utils.h"
+
+using std::make_pair;
+
+namespace net {
+namespace tools {
+
+namespace {
+
+// Time period for which the guid should live in time wait state..
+const int kTimeWaitSeconds = 5;
+
+} // namespace
+
+// A very simple alarm that just informs the QuicTimeWaitListManager to clean
+// up old guids. This alarm should be unregistered and deleted before the
+// QuicTimeWaitListManager is deleted.
+class GuidCleanUpAlarm : public EpollAlarm {
+ public:
+ explicit GuidCleanUpAlarm(QuicTimeWaitListManager* time_wait_list_manager)
+ : time_wait_list_manager_(time_wait_list_manager) {
+ }
+
+ virtual int64 OnAlarm() OVERRIDE {
+ EpollAlarm::OnAlarm();
+ time_wait_list_manager_->CleanUpOldGuids();
+ // Let the time wait manager register the alarm at appropriate time.
+ return 0;
+ }
+
+ private:
+ // Not owned.
+ QuicTimeWaitListManager* time_wait_list_manager_;
+};
+
+struct QuicTimeWaitListManager::GuidAddTime {
+ GuidAddTime(QuicGuid guid, const QuicTime& time)
+ : guid(guid),
+ time_added(time) {
+ }
+
+ QuicGuid guid;
+ QuicTime time_added;
+};
+
+// This class stores pending public reset packets to be sent to clients.
+// server_address - server address on which a packet what was received for
+// a guid in time wait state.
+// client_address - address of the client that sent that packet. Needed to send
+// the public reset packet back to the client.
+// packet - the pending public reset packet that is to be sent to the client.
+// created instance takes the ownership of this packet.
+class QuicTimeWaitListManager::QueuedPacket {
+ public:
+ QueuedPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicEncryptedPacket* packet)
+ : server_address_(server_address),
+ client_address_(client_address),
+ packet_(packet) {
+ }
+
+ const IPEndPoint& server_address() const { return server_address_; }
+ const IPEndPoint& client_address() const { return client_address_; }
+ QuicEncryptedPacket* packet() { return packet_.get(); }
+
+ private:
+ const IPEndPoint server_address_;
+ const IPEndPoint client_address_;
+ scoped_ptr<QuicEncryptedPacket> packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
+};
+
+QuicTimeWaitListManager::QuicTimeWaitListManager(
+ QuicPacketWriter* writer,
+ EpollServer* epoll_server)
+ : framer_(QuicVersionMax(),
+ QuicTime::Zero(), // unused
+ true),
+ epoll_server_(epoll_server),
+ kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),
+ guid_clean_up_alarm_(new GuidCleanUpAlarm(this)),
+ clock_(epoll_server),
+ writer_(writer),
+ is_write_blocked_(false) {
+ framer_.set_visitor(this);
+ SetGuidCleanUpAlarm();
+}
+
+QuicTimeWaitListManager::~QuicTimeWaitListManager() {
+ guid_clean_up_alarm_->UnregisterIfRegistered();
+ STLDeleteElements(&time_ordered_guid_list_);
+ STLDeleteElements(&pending_packets_queue_);
+}
+
+void QuicTimeWaitListManager::AddGuidToTimeWait(QuicGuid guid,
+ QuicVersion version) {
+ DCHECK(!IsGuidInTimeWait(guid));
+ // Initialize the guid with 0 packets received.
+ GuidData data(0, version);
+ guid_map_.insert(make_pair(guid, data));
+ time_ordered_guid_list_.push_back(new GuidAddTime(guid,
+ clock_.ApproximateNow()));
+}
+
+bool QuicTimeWaitListManager::IsGuidInTimeWait(QuicGuid guid) const {
+ return guid_map_.find(guid) != guid_map_.end();
+}
+
+void QuicTimeWaitListManager::ProcessPacket(
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicGuid guid,
+ const QuicEncryptedPacket& packet) {
+ DCHECK(IsGuidInTimeWait(guid));
+ server_address_ = server_address;
+ client_address_ = client_address;
+
+ // Set the framer to the appropriate version for this GUID, before processing.
+ QuicVersion version = GetQuicVersionFromGuid(guid);
+ framer_.set_version(version);
+
+ framer_.ProcessPacket(packet);
+}
+
+QuicVersion QuicTimeWaitListManager::GetQuicVersionFromGuid(QuicGuid guid) {
+ GuidMapIterator it = guid_map_.find(guid);
+ DCHECK(it != guid_map_.end());
+ return (it->second).version;
+}
+
+bool QuicTimeWaitListManager::OnCanWrite() {
+ is_write_blocked_ = false;
+ while (!is_write_blocked_ && !pending_packets_queue_.empty()) {
+ QueuedPacket* queued_packet = pending_packets_queue_.front();
+ WriteToWire(queued_packet);
+ if (!is_write_blocked_) {
+ pending_packets_queue_.pop_front();
+ delete queued_packet;
+ }
+ }
+
+ return !is_write_blocked_;
+}
+
+void QuicTimeWaitListManager::OnError(QuicFramer* framer) {
+ DLOG(INFO) << QuicUtils::ErrorToString(framer->error());
+}
+
+bool QuicTimeWaitListManager::OnProtocolVersionMismatch(
+ QuicVersion received_version) {
+ // Drop such packets whose version don't match.
+ return false;
+}
+
+bool QuicTimeWaitListManager::OnStreamFrame(const QuicStreamFrame& frame) {
+ return false;
+}
+
+bool QuicTimeWaitListManager::OnAckFrame(const QuicAckFrame& frame) {
+ return false;
+}
+
+bool QuicTimeWaitListManager::OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) {
+ return false;
+}
+
+bool QuicTimeWaitListManager::OnRstStreamFrame(
+ const QuicRstStreamFrame& frame) {
+ return false;
+}
+
+bool QuicTimeWaitListManager::OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame & frame) {
+ return false;
+}
+
+bool QuicTimeWaitListManager::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ return false;
+}
+
+bool QuicTimeWaitListManager::OnPacketHeader(const QuicPacketHeader& header) {
+ // TODO(satyamshekhar): Think about handling packets from different client
+ // addresses.
+ GuidMapIterator it = guid_map_.find(header.public_header.guid);
+ DCHECK(it != guid_map_.end());
+ // Increment the received packet count.
+ ++((it->second).num_packets);
+ if (ShouldSendPublicReset((it->second).num_packets)) {
+ // We don't need the packet anymore. Just tell the client what sequence
+ // number we rejected.
+ SendPublicReset(server_address_,
+ client_address_,
+ header.public_header.guid,
+ header.packet_sequence_number);
+ }
+ // Never process the body of the packet in time wait state.
+ return false;
+}
+
+// Returns true if the number of packets received for this guid is a power of 2
+// to throttle the number of public reset packets we send to a client.
+bool QuicTimeWaitListManager::ShouldSendPublicReset(int received_packet_count) {
+ return (received_packet_count & (received_packet_count - 1)) == 0;
+}
+
+void QuicTimeWaitListManager::SendPublicReset(
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicGuid guid,
+ QuicPacketSequenceNumber rejected_sequence_number) {
+ QuicPublicResetPacket packet;
+ packet.public_header.guid = guid;
+ packet.public_header.reset_flag = true;
+ packet.public_header.version_flag = false;
+ packet.rejected_sequence_number = rejected_sequence_number;
+ // TODO(satyamshekhar): generate a valid nonce for this guid.
+ packet.nonce_proof = 1010101;
+ QueuedPacket* queued_packet = new QueuedPacket(
+ server_address,
+ client_address,
+ framer_.BuildPublicResetPacket(packet));
+ // Takes ownership of the packet.
+ SendOrQueuePacket(queued_packet);
+}
+
+// Either sends the packet and deletes it or makes pending queue the
+// owner of the packet.
+void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
+ if (!is_write_blocked_) {
+ // TODO(satyamshekhar): Handle packets that fail due to error other than
+ // EAGAIN or EWOULDBLOCK.
+ WriteToWire(packet);
+ }
+
+ if (is_write_blocked_) {
+ // pending_packets_queue takes the ownership of the queued packet.
+ pending_packets_queue_.push_back(packet);
+ } else {
+ delete packet;
+ }
+}
+
+void QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
+ DCHECK(!is_write_blocked_);
+ int error;
+ int rc = writer_->WritePacket(queued_packet->packet()->data(),
+ queued_packet->packet()->length(),
+ queued_packet->server_address().address(),
+ queued_packet->client_address(),
+ this,
+ &error);
+
+ if (rc == -1) {
+ if (error == EAGAIN || error == EWOULDBLOCK) {
+ is_write_blocked_ = true;
+ } else {
+ LOG(WARNING) << "Received unknown error while sending reset packet to "
+ << queued_packet->client_address().ToString() << ": "
+ << strerror(error);
+ }
+ }
+}
+
+void QuicTimeWaitListManager::SetGuidCleanUpAlarm() {
+ guid_clean_up_alarm_->UnregisterIfRegistered();
+ int64 next_alarm_interval;
+ if (!time_ordered_guid_list_.empty()) {
+ GuidAddTime* oldest_guid = time_ordered_guid_list_.front();
+ QuicTime now = clock_.ApproximateNow();
+ DCHECK(now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_);
+ next_alarm_interval = oldest_guid->time_added
+ .Add(kTimeWaitPeriod_)
+ .Subtract(now)
+ .ToMicroseconds();
+ } else {
+ // No guids added so none will expire before kTimeWaitPeriod_.
+ next_alarm_interval = kTimeWaitPeriod_.ToMicroseconds();
+ }
+
+ epoll_server_->RegisterAlarmApproximateDelta(next_alarm_interval,
+ guid_clean_up_alarm_.get());
+}
+
+void QuicTimeWaitListManager::CleanUpOldGuids() {
+ QuicTime now = clock_.ApproximateNow();
+ while (time_ordered_guid_list_.size() > 0) {
+ DCHECK_EQ(time_ordered_guid_list_.size(), guid_map_.size());
+ GuidAddTime* oldest_guid = time_ordered_guid_list_.front();
+ if (now.Subtract(oldest_guid->time_added) < kTimeWaitPeriod_) {
+ break;
+ }
+ // This guid has lived its age, retire it now.
+ guid_map_.erase(oldest_guid->guid);
+ time_ordered_guid_list_.pop_front();
+ delete oldest_guid;
+ }
+ SetGuidCleanUpAlarm();
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/quic_time_wait_list_manager.h b/chromium/net/tools/quic/quic_time_wait_list_manager.h
new file mode 100644
index 00000000000..815b9d913bf
--- /dev/null
+++ b/chromium/net/tools/quic/quic_time_wait_list_manager.h
@@ -0,0 +1,194 @@
+// 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.
+//
+// Handles packets for guids in time wait state by discarding the packet and
+// sending the clients a public reset packet with exponential backoff.
+
+#ifndef NET_TOOLS_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
+#define NET_TOOLS_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
+
+#include <deque>
+
+#include "base/containers/hash_tables.h"
+#include "base/strings/string_piece.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "net/tools/quic/quic_epoll_clock.h"
+#include "net/tools/quic/quic_packet_writer.h"
+
+namespace net {
+namespace tools {
+
+class GuidCleanUpAlarm;
+
+// Maintains a list of all guids that have been recently closed. A guid lives in
+// this state for kTimeWaitPeriod. All packets received for guids in this state
+// are handed over to the QuicTimeWaitListManager by the QuicDispatcher. It also
+// decides whether we should send a public reset packet to the client which sent
+// a packet with the guid in time wait state and sends it when appropriate.
+// After the guid expires its time wait period, a new connection/session will be
+// created if a packet is received for this guid.
+class QuicTimeWaitListManager : public QuicBlockedWriterInterface,
+ public QuicFramerVisitorInterface {
+ public:
+ // writer - the entity that writes to the socket. (Owned by the dispatcher)
+ // epoll_server - used to run clean up alarms. (Owned by the dispatcher)
+ QuicTimeWaitListManager(QuicPacketWriter* writer,
+ EpollServer* epoll_server);
+ virtual ~QuicTimeWaitListManager();
+
+ // Adds the given guid to time wait state for kTimeWaitPeriod. Henceforth,
+ // any packet bearing this guid should not be processed while the guid remains
+ // in this list. Public reset packets are sent to the clients by the time wait
+ // list manager that send packets to guids in this state. DCHECKs that guid is
+ // not already on the list. Pass in the version as well so that if a public
+ // reset packet needs to be sent the framer version can be set first.
+ void AddGuidToTimeWait(QuicGuid guid, QuicVersion version);
+
+ // Returns true if the guid is in time wait state, false otherwise. Packets
+ // received for this guid should not lead to creation of new QuicSessions.
+ bool IsGuidInTimeWait(QuicGuid guid) const;
+
+ // Called when a packet is received for a guid that is in time wait state.
+ // Sends a public reset packet to the client which sent this guid. Sending
+ // of the public reset packet is throttled by using exponential back off.
+ // DCHECKs for the guid to be in time wait state.
+ // virtual to override in tests.
+ virtual void ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicGuid guid,
+ const QuicEncryptedPacket& packet);
+
+ // Called by the dispatcher when the underlying socket becomes writable again,
+ // since we might need to send pending public reset packets which we didn't
+ // send because the underlying socket was write blocked.
+ virtual bool OnCanWrite() OVERRIDE;
+
+ // Used to delete guid entries that have outlived their time wait period.
+ void CleanUpOldGuids();
+
+ // FramerVisitorInterface
+ virtual void OnError(QuicFramer* framer) OVERRIDE;
+ virtual bool OnProtocolVersionMismatch(QuicVersion received_version) OVERRIDE;
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual void OnPacket() OVERRIDE {}
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& packet) OVERRIDE {}
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& /*packet*/) OVERRIDE {}
+
+ virtual void OnPacketComplete() OVERRIDE {}
+ // The following methods should never get called because we always return
+ // false from OnPacketHeader(). We never need to process body of a packet.
+ virtual void OnRevivedPacket() OVERRIDE {}
+ virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE {}
+ virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
+ virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
+ virtual bool OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame & frame) OVERRIDE;
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual void OnFecData(const QuicFecData& fec) OVERRIDE {}
+
+ QuicVersion version() const { return framer_.version(); }
+
+ protected:
+ // Exposed for tests.
+ bool is_write_blocked() const { return is_write_blocked_; }
+
+ // Decides if public reset packet should be sent for this guid based on the
+ // number of received pacekts.
+ bool ShouldSendPublicReset(int received_packet_count);
+
+ // Exposed for tests.
+ const QuicTime::Delta time_wait_period() const { return kTimeWaitPeriod_; }
+
+ // Given a GUID that exists in the time wait list, returns the QuicVersion
+ // associated with it. Used internally to set the framer version before
+ // writing the public reset packet.
+ QuicVersion GetQuicVersionFromGuid(QuicGuid guid);
+
+ private:
+ // Stores the guid and the time it was added to time wait state.
+ struct GuidAddTime;
+ // Internal structure to store pending public reset packets.
+ class QueuedPacket;
+
+ // Creates a public reset packet and sends it or queues it to be sent later.
+ void SendPublicReset(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicGuid guid,
+ QuicPacketSequenceNumber rejected_sequence_number);
+
+ // Either sends the packet and deletes it or makes pending_packets_queue_ the
+ // owner of the packet.
+ void SendOrQueuePacket(QueuedPacket* packet);
+
+ // Should only be called when write_blocked_ == false. We only care if the
+ // writing was unsuccessful because the socket got blocked, which can be
+ // tested using write_blocked_ == true. In case of all other errors we drop
+ // the packet. Hence, we return void.
+ void WriteToWire(QueuedPacket* packet);
+
+ // Register the alarm with the epoll server to wake up at appropriate time.
+ void SetGuidCleanUpAlarm();
+
+ // A map from a recently closed guid to the number of packets received after
+ // the termination of the connection bound to the guid.
+ struct GuidData {
+ GuidData(int num_packets_, QuicVersion version_)
+ : num_packets(num_packets_), version(version_) {}
+ int num_packets;
+ QuicVersion version;
+ };
+ base::hash_map<QuicGuid, GuidData> guid_map_;
+ typedef base::hash_map<QuicGuid, GuidData>::iterator GuidMapIterator;
+
+ // Maintains a list of GuidAddTime elements which it owns, in the
+ // order they should be deleted.
+ std::deque<GuidAddTime*> time_ordered_guid_list_;
+
+ // Pending public reset packets that need to be sent out to the client
+ // when we are given a chance to write by the dispatcher.
+ std::deque<QueuedPacket*> pending_packets_queue_;
+
+ // Used to parse incoming packets.
+ QuicFramer framer_;
+
+ // Server and client address of the last packet processed.
+ IPEndPoint server_address_;
+ IPEndPoint client_address_;
+
+ // Used to schedule alarms to delete old guids which have been in the list for
+ // too long. Owned by the dispatcher.
+ EpollServer* epoll_server_;
+
+ // Time period for which guids should remain in time wait state.
+ const QuicTime::Delta kTimeWaitPeriod_;
+
+ // Alarm registered with the epoll server to clean up guids that have out
+ // lived their duration in time wait state.
+ scoped_ptr<GuidCleanUpAlarm> guid_clean_up_alarm_;
+
+ // Clock to efficiently measure approximate time from the epoll server.
+ QuicEpollClock clock_;
+
+ // Interface that writes given buffer to the socket. Owned by the dispatcher.
+ QuicPacketWriter* writer_;
+
+ // True if the underlying udp socket is write blocked, i.e will return EAGAIN
+ // on sendmsg.
+ bool is_write_blocked_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicTimeWaitListManager);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
diff --git a/chromium/net/tools/quic/spdy_utils.cc b/chromium/net/tools/quic/spdy_utils.cc
new file mode 100644
index 00000000000..13d05e5e706
--- /dev/null
+++ b/chromium/net/tools/quic/spdy_utils.cc
@@ -0,0 +1,266 @@
+// 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/tools/quic/spdy_utils.h"
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/tools/flip_server/balsa_headers.h"
+#include "url/gurl.h"
+
+using base::StringPiece;
+using std::pair;
+using std::string;
+
+namespace net {
+namespace tools {
+
+const char* const kV3Host = ":host";
+const char* const kV3Path = ":path";
+const char* const kV3Scheme = ":scheme";
+const char* const kV3Status = ":status";
+const char* const kV3Method = ":method";
+const char* const kV3Version = ":version";
+
+void PopulateSpdyHeaderBlock(const BalsaHeaders& headers,
+ SpdyHeaderBlock* block,
+ bool allow_empty_values) {
+ for (BalsaHeaders::const_header_lines_iterator hi =
+ headers.header_lines_begin();
+ hi != headers.header_lines_end();
+ ++hi) {
+ if ((hi->second.length() == 0) && !allow_empty_values) {
+ DLOG(INFO) << "Dropping empty header " << hi->first.as_string()
+ << " from headers";
+ continue;
+ }
+
+ // This unfortunately involves loads of copying, but its the simplest way
+ // to sort the headers and leverage the framer.
+ string name = hi->first.as_string();
+ StringToLowerASCII(&name);
+ SpdyHeaderBlock::iterator it = block->find(name);
+ if (it != block->end()) {
+ it->second.reserve(it->second.size() + 1 + hi->second.size());
+ it->second.append("\0", 1);
+ it->second.append(hi->second.data(), hi->second.size());
+ } else {
+ block->insert(make_pair(name, hi->second.as_string()));
+ }
+ }
+}
+
+void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders& headers,
+ const string& scheme,
+ const string& host_and_port,
+ const string& path,
+ SpdyHeaderBlock* block) {
+ PopulateSpdyHeaderBlock(headers, block, true);
+ StringPiece host_header = headers.GetHeader("Host");
+ if (!host_header.empty()) {
+ DCHECK(host_and_port.empty() || host_header == host_and_port);
+ block->insert(make_pair(kV3Host, host_header.as_string()));
+ } else {
+ block->insert(make_pair(kV3Host, host_and_port));
+ }
+ block->insert(make_pair(kV3Path, path));
+ block->insert(make_pair(kV3Scheme, scheme));
+
+ if (!headers.request_method().empty()) {
+ block->insert(make_pair(kV3Method, headers.request_method().as_string()));
+ }
+
+ if (!headers.request_version().empty()) {
+ (*block)[kV3Version] = headers.request_version().as_string();
+ }
+}
+
+void PopulateSpdyResponseHeaderBlock(const BalsaHeaders& headers,
+ SpdyHeaderBlock* block) {
+ string status = headers.response_code().as_string();
+ status.append(" ");
+ status.append(headers.response_reason_phrase().as_string());
+ (*block)[kV3Status] = status;
+ (*block)[kV3Version] =
+ headers.response_version().as_string();
+
+ // Empty header values are only allowed because this is spdy3.
+ PopulateSpdyHeaderBlock(headers, block, true);
+}
+
+// static
+SpdyHeaderBlock SpdyUtils::RequestHeadersToSpdyHeaders(
+ const BalsaHeaders& request_headers) {
+ string scheme;
+ string host_and_port;
+ string path;
+
+ string url = request_headers.request_uri().as_string();
+ if (url.empty() || url[0] == '/') {
+ path = url;
+ } else {
+ GURL request_uri(url);
+ if (request_headers.request_method() == "CONNECT") {
+ path = url;
+ } else {
+ path = request_uri.path();
+ if (!request_uri.query().empty()) {
+ path = path + "?" + request_uri.query();
+ }
+ host_and_port = request_uri.host();
+ scheme = request_uri.scheme();
+ }
+ }
+
+ DCHECK(!scheme.empty());
+ DCHECK(!host_and_port.empty());
+ DCHECK(!path.empty());
+
+ SpdyHeaderBlock block;
+ PopulateSpdy3RequestHeaderBlock(
+ request_headers, scheme, host_and_port, path, &block);
+ if (block.find("host") != block.end()) {
+ block.erase(block.find("host"));
+ }
+ return block;
+}
+
+// static
+string SpdyUtils::SerializeRequestHeaders(const BalsaHeaders& request_headers) {
+ SpdyHeaderBlock block = RequestHeadersToSpdyHeaders(request_headers);
+ return SerializeUncompressedHeaders(block);
+}
+
+// static
+SpdyHeaderBlock SpdyUtils::ResponseHeadersToSpdyHeaders(
+ const BalsaHeaders& response_headers) {
+ SpdyHeaderBlock block;
+ PopulateSpdyResponseHeaderBlock(response_headers, &block);
+ return block;
+}
+
+// static
+string SpdyUtils::SerializeResponseHeaders(
+ const BalsaHeaders& response_headers) {
+ SpdyHeaderBlock block = ResponseHeadersToSpdyHeaders(response_headers);
+
+ return SerializeUncompressedHeaders(block);
+}
+
+// static
+string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) {
+ int length = SpdyFramer::GetSerializedLength(SPDY3, &headers);
+ SpdyFrameBuilder builder(length);
+ SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers);
+ scoped_ptr<SpdyFrame> block(builder.take());
+ return string(block->data(), length);
+}
+
+bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header,
+ BalsaHeaders* headers) {
+ if (header->first.empty() || header->second.empty()) {
+ return true;
+ }
+ const string& header_name = header->first;
+ return header_name.c_str()[0] == ':';
+}
+
+bool SpdyUtils::FillBalsaRequestHeaders(
+ const SpdyHeaderBlock& header_block,
+ BalsaHeaders* request_headers) {
+ typedef SpdyHeaderBlock::const_iterator BlockIt;
+
+ BlockIt host_it = header_block.find(kV3Host);
+ BlockIt path_it = header_block.find(kV3Path);
+ BlockIt scheme_it = header_block.find(kV3Scheme);
+ BlockIt method_it = header_block.find(kV3Method);
+ BlockIt end_it = header_block.end();
+ if (host_it == end_it || path_it == end_it || scheme_it == end_it ||
+ method_it == end_it) {
+ return false;
+ }
+ string url = scheme_it->second;
+ url.append("://");
+ url.append(host_it->second);
+ url.append(path_it->second);
+ request_headers->SetRequestUri(url);
+ request_headers->SetRequestMethod(method_it->second);
+
+ BlockIt cl_it = header_block.find("content-length");
+ if (cl_it != header_block.end()) {
+ int content_length;
+ if (!base::StringToInt(cl_it->second, &content_length)) {
+ return false;
+ }
+ request_headers->SetContentLength(content_length);
+ }
+
+ for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
+ if (!IsSpecialSpdyHeader(it, request_headers)) {
+ request_headers->AppendHeader(it->first, it->second);
+ }
+ }
+
+ return true;
+}
+
+// The reason phrase should match regexp [\d\d\d [^\r\n]+]. If not, we will
+// fail to parse it.
+bool ParseReasonAndStatus(StringPiece status_and_reason,
+ BalsaHeaders* headers) {
+ if (status_and_reason.size() < 5)
+ return false;
+
+ if (status_and_reason[3] != ' ')
+ return false;
+
+ const StringPiece status_str = StringPiece(status_and_reason.data(), 3);
+ int status;
+ if (!base::StringToInt(status_str, &status)) {
+ return false;
+ }
+
+ headers->SetResponseCode(status_str);
+ headers->set_parsed_response_code(status);
+
+ StringPiece reason(status_and_reason.data() + 4,
+ status_and_reason.length() - 4);
+
+ headers->SetResponseReasonPhrase(reason);
+ return true;
+}
+
+bool SpdyUtils::FillBalsaResponseHeaders(
+ const SpdyHeaderBlock& header_block,
+ BalsaHeaders* request_headers) {
+ typedef SpdyHeaderBlock::const_iterator BlockIt;
+
+ BlockIt status_it = header_block.find(kV3Status);
+ BlockIt version_it = header_block.find(kV3Version);
+ BlockIt end_it = header_block.end();
+ if (status_it == end_it || version_it == end_it) {
+ return false;
+ }
+
+ request_headers->SetRequestVersion(version_it->second);
+ if (!ParseReasonAndStatus(status_it->second, request_headers)) {
+ return false;
+ }
+ for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
+ if (!IsSpecialSpdyHeader(it, request_headers)) {
+ request_headers->AppendHeader(it->first, it->second);
+ }
+ }
+ return true;
+}
+
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/spdy_utils.h b/chromium/net/tools/quic/spdy_utils.h
new file mode 100644
index 00000000000..44782744526
--- /dev/null
+++ b/chromium/net/tools/quic/spdy_utils.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_SPDY_UTILS_H_
+#define NET_TOOLS_QUIC_SPDY_UTILS_H_
+
+#include <string>
+
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_header_block.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/tools/flip_server/balsa_headers.h"
+
+namespace net {
+namespace tools {
+
+class SpdyUtils {
+ public:
+ static std::string SerializeRequestHeaders(
+ const BalsaHeaders& request_headers);
+
+ static std::string SerializeResponseHeaders(
+ const BalsaHeaders& response_headers);
+
+ static bool FillBalsaRequestHeaders(const SpdyHeaderBlock& header_block,
+ BalsaHeaders* request_headers);
+
+ static bool FillBalsaResponseHeaders(const SpdyHeaderBlock& header_block,
+ BalsaHeaders* response_headers);
+
+ static SpdyHeaderBlock RequestHeadersToSpdyHeaders(
+ const BalsaHeaders& request_headers);
+
+ static SpdyHeaderBlock ResponseHeadersToSpdyHeaders(
+ const BalsaHeaders& response_headers);
+
+ static std::string SerializeUncompressedHeaders(
+ const SpdyHeaderBlock& headers);
+};
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_SPDY_UTILS_H_
diff --git a/chromium/net/tools/quic/test_tools/http_message_test_utils.cc b/chromium/net/tools/quic/test_tools/http_message_test_utils.cc
new file mode 100644
index 00000000000..7d6df7a7649
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/http_message_test_utils.cc
@@ -0,0 +1,175 @@
+// 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/tools/quic/test_tools/http_message_test_utils.h"
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+
+using base::StringPiece;
+using std::string;
+using std::vector;
+
+namespace net {
+namespace tools {
+namespace test {
+
+namespace {
+
+//const char* kContentEncoding = "content-encoding";
+const char* kContentLength = "content-length";
+const char* kTransferCoding = "transfer-encoding";
+
+// Both kHTTPVersionString and kMethodString arrays are constructed to match
+// the enum values defined in Version and Method of HTTPMessage.
+const char* kHTTPVersionString[] = {
+ "",
+ "HTTP/0.9",
+ "HTTP/1.0",
+ "HTTP/1.1"
+};
+
+const char* kMethodString[] = {
+ "",
+ "OPTIONS",
+ "GET",
+ "HEAD",
+ "POST",
+ "PUT",
+ "DELETE",
+ "TRACE",
+ "CONNECT",
+ "MKCOL",
+ "UNLOCK",
+};
+
+// Returns true if the message represents a complete request or response.
+// Messages are considered complete if:
+// - Transfer-Encoding: chunked is present and message has a final chunk.
+// - Content-Length header is present and matches the message body length.
+// - Neither Transfer-Encoding nor Content-Length is present and message
+// is tagged as complete.
+bool IsCompleteMessage(const HTTPMessage& message) {
+ return true;
+ const BalsaHeaders* headers = message.headers();
+ StringPiece content_length = headers->GetHeader(kContentLength);
+ if (!content_length.empty()) {
+ int parsed_content_length;
+ if (!base::StringToInt(content_length, &parsed_content_length)) {
+ return false;
+ }
+ return (message.body().size() == (uint)parsed_content_length);
+ } else {
+ // Assume messages without transfer coding or content-length are
+ // tagged correctly.
+ return message.has_complete_message();
+ }
+}
+
+} // namespace
+
+HTTPMessage::Method HTTPMessage::StringToMethod(StringPiece str) {
+ // Skip the first element of the array since it is empty string.
+ for (unsigned long i = 1; i < arraysize(kMethodString); ++i) {
+ if (strncmp(str.data(), kMethodString[i], str.length()) == 0) {
+ return static_cast<HTTPMessage::Method>(i);
+ }
+ }
+ return HttpConstants::UNKNOWN_METHOD;
+}
+
+HTTPMessage::Version HTTPMessage::StringToVersion(StringPiece str) {
+ // Skip the first element of the array since it is empty string.
+ for (unsigned long i = 1; i < arraysize(kHTTPVersionString); ++i) {
+ if (strncmp(str.data(), kHTTPVersionString[i], str.length()) == 0) {
+ return static_cast<HTTPMessage::Version>(i);
+ }
+ }
+ return HttpConstants::HTTP_UNKNOWN;
+}
+
+const char* HTTPMessage::MethodToString(Method method) {
+ CHECK_LT(static_cast<size_t>(method), arraysize(kMethodString));
+ return kMethodString[method];
+}
+
+const char* HTTPMessage::VersionToString(Version version) {
+ CHECK_LT(static_cast<size_t>(version), arraysize(kHTTPVersionString));
+ return kHTTPVersionString[version];
+}
+
+HTTPMessage::HTTPMessage()
+ : is_request_(true) {
+ InitializeFields();
+}
+
+HTTPMessage::HTTPMessage(Version ver, Method request, const string& path)
+ : is_request_(true) {
+ InitializeFields();
+ if (ver != HttpConstants::HTTP_0_9) {
+ headers()->SetRequestVersion(VersionToString(ver));
+ }
+ headers()->SetRequestMethod(MethodToString(request));
+ headers()->SetRequestUri(path);
+}
+
+HTTPMessage::~HTTPMessage() {
+}
+
+void HTTPMessage::InitializeFields() {
+ has_complete_message_ = true;
+ skip_message_validation_ = false;
+}
+
+void HTTPMessage::AddHeader(const string& header, const string& value) {
+ headers()->AppendHeader(header, value);
+}
+
+void HTTPMessage::RemoveHeader(const string& header) {
+ headers()->RemoveAllOfHeader(header);
+}
+
+void HTTPMessage::ReplaceHeader(const string& header, const string& value) {
+ headers()->ReplaceOrAppendHeader(header, value);
+}
+
+void HTTPMessage::AddBody(const string& body, bool add_content_length) {
+ body_ = body;
+ // Remove any transfer-encoding that was left by a previous body.
+ RemoveHeader(kTransferCoding);
+ if (add_content_length) {
+ ReplaceHeader(kContentLength, base::IntToString(body.size()));
+ } else {
+ RemoveHeader(kContentLength);
+ }
+}
+
+void HTTPMessage::ValidateMessage() const {
+ if (skip_message_validation_) {
+ return;
+ }
+
+ vector<StringPiece> transfer_encodings;
+ headers()->GetAllOfHeader(kTransferCoding, &transfer_encodings);
+ CHECK_GE(1ul, transfer_encodings.size());
+ for (vector<StringPiece>::iterator it = transfer_encodings.begin();
+ it != transfer_encodings.end();
+ ++it) {
+ CHECK(StringPieceUtils::EqualIgnoreCase("identity", *it) ||
+ StringPieceUtils::EqualIgnoreCase("chunked", *it)) << *it;
+ }
+
+ vector<StringPiece> content_lengths;
+ headers()->GetAllOfHeader(kContentLength, &content_lengths);
+ CHECK_GE(1ul, content_lengths.size());
+
+ CHECK_EQ(has_complete_message_, IsCompleteMessage(*this));
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/test_tools/http_message_test_utils.h b/chromium/net/tools/quic/test_tools/http_message_test_utils.h
new file mode 100644
index 00000000000..d389e21c8f2
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/http_message_test_utils.h
@@ -0,0 +1,133 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "net/tools/flip_server/balsa_enums.h"
+#include "net/tools/flip_server/balsa_headers.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+class HttpConstants {
+ public:
+ enum Version {
+ HTTP_UNKNOWN = 0,
+ HTTP_0_9,
+ HTTP_1_0,
+ HTTP_1_1
+ };
+
+ enum Method {
+ UNKNOWN_METHOD = 0,
+ OPTIONS,
+ GET,
+ HEAD,
+ POST,
+ PUT,
+ DELETE,
+ TRACE,
+ CONNECT,
+
+ MKCOL,
+ UNLOCK,
+ };
+};
+
+// Stripped down wrapper class which basically contains headers and a body.
+class HTTPMessage {
+ public:
+ typedef HttpConstants::Version Version;
+ typedef HttpConstants::Method Method;
+
+ // Convenient functions to map strings into enums. The string passed in is
+ // not assumed to be NULL-terminated.
+ static Version StringToVersion(base::StringPiece str);
+ static Method StringToMethod(base::StringPiece str);
+
+ static const char* MethodToString(Method method);
+ static const char* VersionToString(Version version);
+
+ // Default constructor makes an empty HTTP/1.1 GET request. This is typically
+ // used to construct a message that will be Initialize()-ed.
+ HTTPMessage();
+
+ // Build a request message
+ HTTPMessage(Version version, Method request, const std::string& path);
+
+ virtual ~HTTPMessage();
+
+ const std::string& body() const { return body_; }
+
+ // Adds a header line to the message.
+ void AddHeader(const std::string& header, const std::string& value);
+
+ // Removes a header line from the message.
+ void RemoveHeader(const std::string& header);
+
+ // A utility function which calls RemoveHeader followed by AddHeader.
+ void ReplaceHeader(const std::string& header, const std::string& value);
+
+ // Adds a body and the optional content-length header field (omitted to test
+ // read until close test case). To generate a message that has a header field
+ // of 0 content-length, call AddBody("", true).
+ // Multiple calls to AddBody()/AddChunkedBody() has the effect of overwriting
+ // the previous entry without warning.
+ void AddBody(const std::string& body, bool add_content_length);
+
+ bool has_complete_message() const { return has_complete_message_; }
+ void set_has_complete_message(bool value) { has_complete_message_ = value; }
+
+ // Do some basic http message consistency checks like:
+ // - Valid transfer-encoding header
+ // - Valid content-length header
+ // - Messages we expect to be complete are complete.
+ // This check can be disabled by setting skip_message_validation.
+ void ValidateMessage() const;
+
+ bool skip_message_validation() const { return skip_message_validation_; }
+ void set_skip_message_validation(bool value) {
+ skip_message_validation_ = value;
+ }
+
+ // Allow direct access to the body string. This should be used with caution:
+ // it will not update the request headers like AddBody and AddChunkedBody do.
+ void set_body(const std::string& body) { body_ = body; }
+
+ const BalsaHeaders* headers() const { return &headers_; }
+ BalsaHeaders* headers() { return &headers_; }
+
+ protected:
+ BalsaHeaders headers_;
+
+ std::string body_; // the body with chunked framing/gzip compression
+
+ bool is_request_;
+
+ // True if the message should be considered complete during serialization.
+ // Used by SPDY and Streamed RPC clients to decide wherever or not
+ // to include fin flags and during message validation (if enabled).
+ bool has_complete_message_;
+
+ // Allows disabling message validation when creating test messages
+ // that are intentionally invalid.
+ bool skip_message_validation_;
+
+ private:
+ void InitializeFields();
+
+ DISALLOW_COPY_AND_ASSIGN(HTTPMessage);
+};
+
+} // namespace test
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_TEST_TOOLS_HTTP_MESSAGE_TEST_UTILS_H_
diff --git a/chromium/net/tools/quic/test_tools/mock_epoll_server.cc b/chromium/net/tools/quic/test_tools/mock_epoll_server.cc
new file mode 100644
index 00000000000..4101c5e9ab0
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/mock_epoll_server.cc
@@ -0,0 +1,68 @@
+// 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/tools/quic/test_tools/mock_epoll_server.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+FakeTimeEpollServer::FakeTimeEpollServer(): now_in_usec_(0) {
+}
+
+FakeTimeEpollServer::~FakeTimeEpollServer() {
+}
+
+int64 FakeTimeEpollServer::NowInUsec() const {
+ return now_in_usec_;
+}
+
+MockEpollServer::MockEpollServer() : until_in_usec_(-1) {
+}
+
+MockEpollServer::~MockEpollServer() {
+}
+
+int MockEpollServer::epoll_wait_impl(int epfd,
+ struct epoll_event* events,
+ int max_events,
+ int timeout_in_ms) {
+ int num_events = 0;
+ while (!event_queue_.empty() &&
+ num_events < max_events &&
+ event_queue_.begin()->first <= NowInUsec() &&
+ ((until_in_usec_ == -1) ||
+ (event_queue_.begin()->first < until_in_usec_))
+ ) {
+ int64 event_time_in_usec = event_queue_.begin()->first;
+ events[num_events] = event_queue_.begin()->second;
+ if (event_time_in_usec > NowInUsec()) {
+ set_now_in_usec(event_time_in_usec);
+ }
+ event_queue_.erase(event_queue_.begin());
+ ++num_events;
+ }
+ if (num_events == 0) { // then we'd have waited 'till the timeout.
+ if (until_in_usec_ < 0) { // then we don't care what the final time is.
+ if (timeout_in_ms > 0) {
+ AdvanceBy(timeout_in_ms * 1000);
+ }
+ } else { // except we assume that we don't wait for the timeout
+ // period if until_in_usec_ is a positive number.
+ set_now_in_usec(until_in_usec_);
+ // And reset until_in_usec_ to signal no waiting (as
+ // the AdvanceByExactly* stuff is meant to be one-shot,
+ // as are all similar EpollServer functions)
+ until_in_usec_ = -1;
+ }
+ }
+ if (until_in_usec_ >= 0) {
+ CHECK(until_in_usec_ >= NowInUsec());
+ }
+ return num_events;
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/test_tools/mock_epoll_server.h b/chromium/net/tools/quic/test_tools/mock_epoll_server.h
new file mode 100644
index 00000000000..710d5fdf772
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/mock_epoll_server.h
@@ -0,0 +1,106 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_TEST_TOOLS_MOCK_EPOLL_SERVER_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_MOCK_EPOLL_SERVER_H_
+
+#include "base/logging.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+// Unlike the full MockEpollServer, this only lies about the time but lets
+// fd events operate normally. Usefully when interacting with real backends
+// but wanting to skip forward in time to trigger timeouts.
+class FakeTimeEpollServer : public EpollServer {
+ public:
+ FakeTimeEpollServer();
+ virtual ~FakeTimeEpollServer();
+
+ // Replaces the EpollServer NowInUsec.
+ virtual int64 NowInUsec() const OVERRIDE;
+
+ void set_now_in_usec(int64 nius) { now_in_usec_ = nius; }
+
+ // Advances the virtual 'now' by advancement_usec.
+ void AdvanceBy(int64 advancement_usec) {
+ set_now_in_usec(NowInUsec() + advancement_usec);
+ }
+
+ // Advances the virtual 'now' by advancement_usec, and
+ // calls WaitForEventAndExecteCallbacks.
+ // Note that the WaitForEventsAndExecuteCallbacks invocation
+ // may cause NowInUs to advance beyond what was specified here.
+ // If that is not desired, use the AdvanceByExactly calls.
+ void AdvanceByAndCallCallbacks(int64 advancement_usec) {
+ AdvanceBy(advancement_usec);
+ WaitForEventsAndExecuteCallbacks();
+ }
+
+ private:
+ int64 now_in_usec_;
+};
+
+class MockEpollServer : public FakeTimeEpollServer {
+ public: // type definitions
+ typedef base::hash_multimap<int64, struct epoll_event> EventQueue;
+
+ MockEpollServer();
+ virtual ~MockEpollServer();
+
+ // time_in_usec is the time at which the event specified
+ // by 'ee' will be delivered. Note that it -is- possible
+ // to add an event for a time which has already been passed..
+ // .. upon the next time that the callbacks are invoked,
+ // all events which are in the 'past' will be delivered.
+ void AddEvent(int64 time_in_usec, const struct epoll_event& ee) {
+ event_queue_.insert(std::make_pair(time_in_usec, ee));
+ }
+
+ // Advances the virtual 'now' by advancement_usec,
+ // and ensure that the next invocation of
+ // WaitForEventsAndExecuteCallbacks goes no farther than
+ // advancement_usec from the current time.
+ void AdvanceByExactly(int64 advancement_usec) {
+ until_in_usec_ = NowInUsec() + advancement_usec;
+ set_now_in_usec(NowInUsec() + advancement_usec);
+ }
+
+ // As above, except calls WaitForEventsAndExecuteCallbacks.
+ void AdvanceByExactlyAndCallCallbacks(int64 advancement_usec) {
+ AdvanceByExactly(advancement_usec);
+ WaitForEventsAndExecuteCallbacks();
+ }
+
+ base::hash_set<AlarmCB*>::size_type NumberOfAlarms() const {
+ return all_alarms_.size();
+ }
+
+ protected: // functions
+ // These functions do nothing here, as we're not actually
+ // using the epoll_* syscalls.
+ virtual void DelFD(int fd) const OVERRIDE { }
+ virtual void AddFD(int fd, int event_mask) const OVERRIDE { }
+ virtual void ModFD(int fd, int event_mask) const OVERRIDE { }
+
+ // Replaces the epoll_server's epoll_wait_impl.
+ virtual int epoll_wait_impl(int epfd,
+ struct epoll_event* events,
+ int max_events,
+ int timeout_in_ms) OVERRIDE;
+ virtual void SetNonblocking (int fd) OVERRIDE { }
+
+ private: // members
+ EventQueue event_queue_;
+ int64 until_in_usec_;
+};
+
+} // namespace test
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_MOCK_EPOLL_SERVER_H_
diff --git a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc
new file mode 100644
index 00000000000..3a2b1d98252
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.cc
@@ -0,0 +1,21 @@
+// Copyright 2013 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/tools/quic/test_tools/mock_quic_dispatcher.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+MockQuicDispatcher::MockQuicDispatcher(
+ const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ QuicGuid guid,
+ EpollServer* eps)
+ : QuicDispatcher(config, crypto_config, guid, eps) { }
+MockQuicDispatcher::~MockQuicDispatcher() {}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h
new file mode 100644
index 00000000000..563ab0de5fd
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/mock_quic_dispatcher.h
@@ -0,0 +1,38 @@
+// Copyright 2013 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.
+
+#ifndef NET_TOOLS_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/crypto_server_config.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_protocol.h"
+#include "net/tools/flip_server/epoll_server.h"
+#include "net/tools/quic/quic_dispatcher.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+class MockQuicDispatcher : public QuicDispatcher {
+ public:
+ MockQuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ QuicGuid guid,
+ EpollServer* eps);
+ virtual ~MockQuicDispatcher();
+
+ MOCK_METHOD4(ProcessPacket, void(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicGuid guid,
+ const QuicEncryptedPacket& packet));
+};
+
+} // namespace test
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
diff --git a/chromium/net/tools/quic/test_tools/quic_client_peer.cc b/chromium/net/tools/quic/test_tools/quic_client_peer.cc
new file mode 100644
index 00000000000..858359474c8
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/quic_client_peer.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 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/tools/quic/test_tools/quic_client_peer.h"
+
+#include "net/tools/quic/quic_client.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+// static
+void QuicClientPeer::Reinitialize(QuicClient* client) {
+ client->initialized_ = false;
+ client->epoll_server_.UnregisterFD(client->fd_);
+ client->Initialize();
+}
+
+// static
+int QuicClientPeer::GetFd(QuicClient* client) {
+ return client->fd_;
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/test_tools/quic_client_peer.h b/chromium/net/tools/quic/test_tools/quic_client_peer.h
new file mode 100644
index 00000000000..8eaa17e675d
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/quic_client_peer.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2013 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.
+
+#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
+
+namespace net {
+namespace tools {
+
+class QuicClient;
+
+namespace test {
+
+class QuicClientPeer {
+ public:
+ static void Reinitialize(QuicClient* client);
+ static int GetFd(QuicClient* client);
+};
+
+} // namespace test
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_CLIENT_PEER_H_
diff --git a/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc b/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc
new file mode 100644
index 00000000000..e358273d0a2
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2013 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/tools/quic/test_tools/quic_epoll_connection_helper_peer.h"
+
+#include "net/tools/quic/quic_epoll_connection_helper.h"
+
+namespace net {
+namespace tools {
+namespace test {
+
+// static
+void QuicEpollConnectionHelperPeer::SetWriter(QuicEpollConnectionHelper* helper,
+ QuicPacketWriter* writer) {
+ helper->writer_ = writer;
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h b/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h
new file mode 100644
index 00000000000..72085dfde1c
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/quic_epoll_connection_helper_peer.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2013 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.
+
+#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_
+
+#include "base/basictypes.h"
+
+namespace net {
+namespace tools {
+
+class QuicPacketWriter;
+class QuicEpollConnectionHelper;
+
+namespace test {
+
+class QuicEpollConnectionHelperPeer {
+ public:
+ static void SetWriter(QuicEpollConnectionHelper* helper,
+ QuicPacketWriter* writer);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicEpollConnectionHelperPeer);
+};
+
+} // namespace test
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_EPOLL_CONNECTION_HELPER_PEER_H_
diff --git a/chromium/net/tools/quic/test_tools/quic_test_client.cc b/chromium/net/tools/quic/test_tools/quic_test_client.cc
new file mode 100644
index 00000000000..859d7a56044
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/quic_test_client.cc
@@ -0,0 +1,342 @@
+// 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/tools/quic/test_tools/quic_test_client.h"
+
+#include "net/base/completion_callback.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_certificate.h"
+#include "net/quic/crypto/proof_verifier.h"
+#include "net/tools/flip_server/balsa_headers.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
+#include "net/tools/quic/test_tools/http_message_test_utils.h"
+#include "url/gurl.h"
+
+using std::string;
+using std::vector;
+using base::StringPiece;
+
+namespace {
+
+// RecordingProofVerifier accepts any certificate chain and records the common
+// name of the leaf.
+class RecordingProofVerifier : public net::ProofVerifier {
+ public:
+ // ProofVerifier interface.
+ virtual net::ProofVerifier::Status VerifyProof(
+ net::QuicVersion version,
+ const string& hostname,
+ const string& server_config,
+ const vector<string>& certs,
+ const string& signature,
+ string* error_details,
+ scoped_ptr<net::ProofVerifyDetails>* details,
+ net::ProofVerifierCallback* callback) OVERRIDE {
+ delete callback;
+
+ common_name_.clear();
+ if (certs.empty()) {
+ return FAILURE;
+ }
+
+ // Convert certs to X509Certificate.
+ vector<StringPiece> cert_pieces(certs.size());
+ for (unsigned i = 0; i < certs.size(); i++) {
+ cert_pieces[i] = StringPiece(certs[i]);
+ }
+ scoped_refptr<net::X509Certificate> cert =
+ net::X509Certificate::CreateFromDERCertChain(cert_pieces);
+ if (!cert.get()) {
+ return FAILURE;
+ }
+
+ common_name_ = cert->subject().GetDisplayName();
+ return SUCCESS;
+ }
+
+ const string& common_name() const { return common_name_; }
+
+ private:
+ string common_name_;
+};
+
+} // anonymous namespace
+
+namespace net {
+namespace tools {
+namespace test {
+
+BalsaHeaders* MungeHeaders(const BalsaHeaders* const_headers,
+ bool secure) {
+ StringPiece uri = const_headers->request_uri();
+ if (uri.empty()) {
+ return NULL;
+ }
+ if (const_headers->request_method() == "CONNECT") {
+ return NULL;
+ }
+ BalsaHeaders* headers = new BalsaHeaders;
+ headers->CopyFrom(*const_headers);
+ if (!uri.starts_with("https://") &&
+ !uri.starts_with("http://")) {
+ // If we have a relative URL, set some defaults.
+ string full_uri = secure ? "https://www.google.com" :
+ "http://www.google.com";
+ full_uri.append(uri.as_string());
+ headers->SetRequestUri(full_uri);
+ }
+ return headers;
+}
+
+// A quic client which allows mocking out writes.
+class QuicEpollClient : public QuicClient {
+ public:
+ typedef QuicClient Super;
+
+ QuicEpollClient(IPEndPoint server_address,
+ const string& server_hostname,
+ const QuicVersion version)
+ : Super(server_address, server_hostname, version) {
+ }
+
+ QuicEpollClient(IPEndPoint server_address,
+ const string& server_hostname,
+ const QuicConfig& config,
+ const QuicVersion version)
+ : Super(server_address, server_hostname, config, version) {
+ }
+
+ virtual ~QuicEpollClient() {
+ if (connected()) {
+ Disconnect();
+ }
+ }
+
+ virtual QuicEpollConnectionHelper* CreateQuicConnectionHelper() OVERRIDE {
+ if (writer_.get() != NULL) {
+ writer_->set_fd(fd());
+ return new QuicEpollConnectionHelper(writer_.get(), epoll_server());
+ } else {
+ return Super::CreateQuicConnectionHelper();
+ }
+ }
+
+ void UseWriter(QuicTestWriter* writer) { writer_.reset(writer); }
+
+ private:
+ scoped_ptr<QuicTestWriter> writer_;
+};
+
+QuicTestClient::QuicTestClient(IPEndPoint address, const string& hostname,
+ const QuicVersion version)
+ : client_(new QuicEpollClient(address, hostname, version)) {
+ Initialize(address, hostname, true);
+}
+
+QuicTestClient::QuicTestClient(IPEndPoint address,
+ const string& hostname,
+ bool secure,
+ const QuicVersion version)
+ : client_(new QuicEpollClient(address, hostname, version)) {
+ Initialize(address, hostname, secure);
+}
+
+QuicTestClient::QuicTestClient(IPEndPoint address,
+ const string& hostname,
+ bool secure,
+ const QuicConfig& config,
+ const QuicVersion version)
+ : client_(new QuicEpollClient(address, hostname, config, version)) {
+ Initialize(address, hostname, secure);
+}
+
+void QuicTestClient::Initialize(IPEndPoint address,
+ const string& hostname,
+ bool secure) {
+ server_address_ = address;
+ stream_ = NULL;
+ stream_error_ = QUIC_STREAM_NO_ERROR;
+ bytes_read_ = 0;
+ bytes_written_= 0;
+ never_connected_ = true;
+ secure_ = secure;
+ auto_reconnect_ = false;
+ proof_verifier_ = NULL;
+ ExpectCertificates(secure_);
+}
+
+QuicTestClient::~QuicTestClient() {
+ if (stream_) {
+ stream_->set_visitor(NULL);
+ }
+}
+
+void QuicTestClient::ExpectCertificates(bool on) {
+ if (on) {
+ proof_verifier_ = new RecordingProofVerifier;
+ client_->SetProofVerifier(proof_verifier_);
+ } else {
+ proof_verifier_ = NULL;
+ client_->SetProofVerifier(NULL);
+ }
+}
+
+ssize_t QuicTestClient::SendRequest(const string& uri) {
+ HTTPMessage message(HttpConstants::HTTP_1_1, HttpConstants::GET, uri);
+ return SendMessage(message);
+}
+
+ssize_t QuicTestClient::SendMessage(const HTTPMessage& message) {
+ stream_ = NULL; // Always force creation of a stream for SendMessage.
+
+ // If we're not connected, try to find an sni hostname.
+ if (!connected()) {
+ GURL url(message.headers()->request_uri().as_string());
+ if (!url.host().empty()) {
+ client_->set_server_hostname(url.host());
+ }
+ }
+
+ QuicReliableClientStream* stream = GetOrCreateStream();
+ if (!stream) { return 0; }
+
+ scoped_ptr<BalsaHeaders> munged_headers(MungeHeaders(message.headers(),
+ secure_));
+ return GetOrCreateStream()->SendRequest(
+ munged_headers.get() ? *munged_headers.get() : *message.headers(),
+ message.body(),
+ message.has_complete_message());
+}
+
+ssize_t QuicTestClient::SendData(string data, bool last_data) {
+ QuicReliableClientStream* stream = GetOrCreateStream();
+ if (!stream) { return 0; }
+ GetOrCreateStream()->SendBody(data, last_data);
+ return data.length();
+}
+
+string QuicTestClient::SendCustomSynchronousRequest(
+ const HTTPMessage& message) {
+ SendMessage(message);
+ WaitForResponse();
+ return response_;
+}
+
+string QuicTestClient::SendSynchronousRequest(const string& uri) {
+ if (SendRequest(uri) == 0) {
+ DLOG(ERROR) << "Failed the request for uri:" << uri;
+ return "";
+ }
+ WaitForResponse();
+ return response_;
+}
+
+QuicReliableClientStream* QuicTestClient::GetOrCreateStream() {
+ if (never_connected_ == true || auto_reconnect_) {
+ if (!connected()) {
+ Connect();
+ }
+ if (!connected()) {
+ return NULL;
+ }
+ }
+ if (!stream_) {
+ stream_ = client_->CreateReliableClientStream();
+ if (stream_ != NULL) {
+ stream_->set_visitor(this);
+ }
+ }
+ return stream_;
+}
+
+const string& QuicTestClient::cert_common_name() const {
+ return reinterpret_cast<RecordingProofVerifier*>(proof_verifier_)
+ ->common_name();
+}
+
+bool QuicTestClient::connected() const {
+ return client_->connected();
+}
+
+void QuicTestClient::WaitForResponse() {
+ if (stream_ == NULL) {
+ // The client has likely disconnected.
+ return;
+ }
+ client_->WaitForStreamToClose(stream_->id());
+}
+
+void QuicTestClient::Connect() {
+ DCHECK(!connected());
+ client_->Initialize();
+ client_->Connect();
+ never_connected_ = false;
+}
+
+void QuicTestClient::ResetConnection() {
+ Disconnect();
+ Connect();
+}
+
+void QuicTestClient::Disconnect() {
+ client_->Disconnect();
+}
+
+IPEndPoint QuicTestClient::LocalSocketAddress() const {
+ return client_->client_address();
+}
+
+void QuicTestClient::ClearPerRequestState() {
+ stream_error_ = QUIC_STREAM_NO_ERROR;
+ stream_ = NULL;
+ response_ = "";
+ headers_.Clear();
+ bytes_read_ = 0;
+ bytes_written_ = 0;
+}
+
+void QuicTestClient::WaitForInitialResponse() {
+ DCHECK(stream_ != NULL);
+ while (stream_ && stream_->stream_bytes_read() == 0) {
+ client_->WaitForEvents();
+ }
+}
+
+ssize_t QuicTestClient::Send(const void *buffer, size_t size) {
+ return SendData(string(static_cast<const char*>(buffer), size), false);
+}
+
+int QuicTestClient::response_size() const {
+ return bytes_read_;
+}
+
+size_t QuicTestClient::bytes_read() const {
+ return bytes_read_;
+}
+
+size_t QuicTestClient::bytes_written() const {
+ return bytes_written_;
+}
+
+void QuicTestClient::OnClose(ReliableQuicStream* stream) {
+ if (stream_ != stream) {
+ return;
+ }
+ response_ = stream_->data();
+ headers_.CopyFrom(stream_->headers());
+ stream_error_ = stream_->stream_error();
+ bytes_read_ = stream_->stream_bytes_read();
+ bytes_written_ = stream_->stream_bytes_written();
+ stream_ = NULL;
+}
+
+void QuicTestClient::UseWriter(QuicTestWriter* writer) {
+ DCHECK(!connected());
+ reinterpret_cast<QuicEpollClient*>(client_.get())->UseWriter(writer);
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/test_tools/quic_test_client.h b/chromium/net/tools/quic/test_tools/quic_test_client.h
new file mode 100644
index 00000000000..74bfc24646a
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/quic_test_client.h
@@ -0,0 +1,143 @@
+// 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.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_CLIENT_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_CLIENT_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_protocol.h"
+#include "net/tools/quic/quic_client.h"
+#include "net/tools/quic/quic_packet_writer.h"
+
+namespace net {
+
+class ProofVerifier;
+
+namespace tools {
+
+namespace test {
+
+// Allows setting a writer for the client's QuicConnectionHelper, to allow
+// fine-grained control of writes.
+class QuicTestWriter : public QuicPacketWriter {
+ public:
+ virtual ~QuicTestWriter() {}
+ virtual void set_fd(int fd) = 0;
+};
+
+class HTTPMessage;
+
+// A toy QUIC client used for testing.
+class QuicTestClient : public ReliableQuicStream::Visitor {
+ public:
+ QuicTestClient(IPEndPoint server_address, const string& server_hostname,
+ const QuicVersion version);
+ QuicTestClient(IPEndPoint server_address,
+ const string& server_hostname,
+ bool secure,
+ const QuicVersion version);
+ QuicTestClient(IPEndPoint server_address,
+ const string& server_hostname,
+ bool secure,
+ const QuicConfig& config,
+ const QuicVersion version);
+
+ virtual ~QuicTestClient();
+
+ // ExpectCertificates controls whether the server is expected to provide
+ // certificates. The certificates, if any, are not verified, but the common
+ // name is recorded and available with |cert_common_name()|.
+ void ExpectCertificates(bool on);
+
+ // Clears any outstanding state and sends a simple GET of 'uri' to the
+ // server. Returns 0 if the request failed and no bytes were written.
+ ssize_t SendRequest(const string& uri);
+ ssize_t SendMessage(const HTTPMessage& message);
+
+ string SendCustomSynchronousRequest(const HTTPMessage& message);
+ string SendSynchronousRequest(const string& uri);
+
+ // Wraps data in a quic packet and sends it.
+ ssize_t SendData(string data, bool last_data);
+
+ QuicPacketCreator::Options* options() { return client_->options(); }
+
+ const BalsaHeaders *response_headers() const {return &headers_;}
+
+ void WaitForResponse();
+
+ void Connect();
+ void ResetConnection();
+ void Disconnect();
+ IPEndPoint LocalSocketAddress() const;
+ void ClearPerRequestState();
+ void WaitForInitialResponse();
+ ssize_t Send(const void *buffer, size_t size);
+ int response_size() const;
+ size_t bytes_read() const;
+ size_t bytes_written() const;
+
+ // From ReliableQuicStream::Visitor
+ virtual void OnClose(ReliableQuicStream* stream) OVERRIDE;
+
+ // Configures client_ to take ownership of and use the writer.
+ // Must be called before initial connect.
+ void UseWriter(QuicTestWriter* writer);
+
+ // Returns NULL if the maximum number of streams have already been created.
+ QuicReliableClientStream* GetOrCreateStream();
+
+ QuicRstStreamErrorCode stream_error() { return stream_error_; }
+ QuicErrorCode connection_error() { return client()->session()->error(); }
+
+ QuicClient* client() { return client_.get(); }
+
+ // cert_common_name returns the common name value of the server's certificate,
+ // or the empty string if no certificate was presented.
+ const string& cert_common_name() const;
+
+ const string& response_body() {return response_;}
+ bool connected() const;
+
+ void set_auto_reconnect(bool reconnect) { auto_reconnect_ = reconnect; }
+
+ private:
+ void Initialize(IPEndPoint address, const string& hostname, bool secure);
+
+ IPEndPoint server_address_;
+ IPEndPoint client_address_;
+ scoped_ptr<QuicClient> client_; // The actual client
+ QuicReliableClientStream* stream_;
+
+ QuicRstStreamErrorCode stream_error_;
+
+ BalsaHeaders headers_;
+ string response_;
+ uint64 bytes_read_;
+ uint64 bytes_written_;
+ // True if the client has never connected before. The client will
+ // auto-connect exactly once before sending data. If something causes a
+ // connection reset, it will not automatically reconnect.
+ bool never_connected_;
+ bool secure_;
+ // If true, the client will always reconnect if necessary before creating a
+ // stream.
+ bool auto_reconnect_;
+
+ // proof_verifier_ points to a RecordingProofVerifier that is owned by
+ // client_.
+ ProofVerifier* proof_verifier_;
+};
+
+} // namespace test
+
+} // namespace tools
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_CLIENT_H_
diff --git a/chromium/net/tools/quic/test_tools/quic_test_utils.cc b/chromium/net/tools/quic/test_tools/quic_test_utils.cc
new file mode 100644
index 00000000000..95f1fb215ea
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/quic_test_utils.cc
@@ -0,0 +1,81 @@
+// 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/tools/quic/test_tools/quic_test_utils.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/tools/quic/quic_epoll_connection_helper.h"
+
+using base::StringPiece;
+using net::test::MockHelper;
+
+namespace net {
+namespace tools {
+namespace test {
+
+MockConnection::MockConnection(QuicGuid guid,
+ IPEndPoint address,
+ int fd,
+ EpollServer* eps,
+ bool is_server)
+ : QuicConnection(guid, address,
+ new QuicEpollConnectionHelper(fd, eps), is_server,
+ QuicVersionMax()),
+ has_mock_helper_(false) {
+}
+
+MockConnection::MockConnection(QuicGuid guid,
+ IPEndPoint address,
+ bool is_server)
+ : QuicConnection(guid, address, new testing::NiceMock<MockHelper>(),
+ is_server, QuicVersionMax()),
+ has_mock_helper_(true) {
+}
+
+MockConnection::MockConnection(QuicGuid guid,
+ IPEndPoint address,
+ QuicConnectionHelperInterface* helper,
+ bool is_server)
+ : QuicConnection(guid, address, helper, is_server, QuicVersionMax()),
+ has_mock_helper_(false) {
+}
+
+MockConnection::~MockConnection() {
+}
+
+void MockConnection::AdvanceTime(QuicTime::Delta delta) {
+ CHECK(has_mock_helper_) << "Cannot advance time unless a MockClock is being"
+ " used";
+ static_cast<MockHelper*>(helper())->AdvanceTime(delta);
+}
+
+bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) {
+ data.AppendToString(&data_);
+ return true;
+}
+
+void TestDecompressorVisitor::OnDecompressionError() {
+ error_ = true;
+}
+
+TestSession::TestSession(QuicConnection* connection,
+ const QuicConfig& config,
+ bool is_server)
+ : QuicSession(connection, config, is_server),
+ crypto_stream_(NULL) {
+}
+
+TestSession::~TestSession() {}
+
+void TestSession::SetCryptoStream(QuicCryptoStream* stream) {
+ crypto_stream_ = stream;
+}
+
+QuicCryptoStream* TestSession::GetCryptoStream() {
+ return crypto_stream_;
+}
+
+} // namespace test
+} // namespace tools
+} // namespace net
diff --git a/chromium/net/tools/quic/test_tools/quic_test_utils.h b/chromium/net/tools/quic/test_tools/quic_test_utils.h
new file mode 100644
index 00000000000..31ea1815e06
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/quic_test_utils.h
@@ -0,0 +1,112 @@
+// 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.
+
+#ifndef NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
+#define NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_session.h"
+#include "net/quic/quic_spdy_decompressor.h"
+#include "net/spdy/spdy_framer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+
+class EpollServer;
+class IPEndPoint;
+
+namespace tools {
+namespace test {
+
+std::string SerializeUncompressedHeaders(const SpdyHeaderBlock& headers);
+
+class MockConnection : public QuicConnection {
+ public:
+ // Uses a QuicConnectionHelper created with fd and eps.
+ MockConnection(QuicGuid guid,
+ IPEndPoint address,
+ int fd,
+ EpollServer* eps,
+ bool is_server);
+ // Uses a MockHelper.
+ MockConnection(QuicGuid guid, IPEndPoint address, bool is_server);
+ MockConnection(QuicGuid guid,
+ IPEndPoint address,
+ QuicConnectionHelperInterface* helper, bool is_server);
+ virtual ~MockConnection();
+
+ // If the constructor that uses a MockHelper has been used then this method
+ // will advance the time of the MockClock.
+ void AdvanceTime(QuicTime::Delta delta);
+
+ MOCK_METHOD3(ProcessUdpPacket, void(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address,
+ const QuicEncryptedPacket& packet));
+ MOCK_METHOD1(SendConnectionClose, void(QuicErrorCode error));
+ MOCK_METHOD2(SendConnectionCloseWithDetails, void(
+ QuicErrorCode error,
+ const std::string& details));
+ MOCK_METHOD2(SendRstStream, void(QuicStreamId id,
+ QuicRstStreamErrorCode error));
+ MOCK_METHOD3(SendGoAway, void(QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const std::string& reason));
+ MOCK_METHOD0(OnCanWrite, bool());
+
+ void ReallyProcessUdpPacket(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address,
+ const QuicEncryptedPacket& packet) {
+ return QuicConnection::ProcessUdpPacket(self_address, peer_address, packet);
+ }
+
+ virtual bool OnProtocolVersionMismatch(QuicVersion version) { return false; }
+
+ private:
+ const bool has_mock_helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockConnection);
+};
+
+class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor {
+ public:
+ virtual ~TestDecompressorVisitor() {}
+ virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE;
+ virtual void OnDecompressionError() OVERRIDE;
+
+ std::string data() { return data_; }
+ bool error() { return error_; }
+
+ private:
+ std::string data_;
+ bool error_;
+};
+
+class TestSession : public QuicSession {
+ public:
+ TestSession(QuicConnection* connection,
+ const QuicConfig& config,
+ bool is_server);
+ virtual ~TestSession();
+
+ MOCK_METHOD1(CreateIncomingReliableStream,
+ ReliableQuicStream*(QuicStreamId id));
+ MOCK_METHOD0(CreateOutgoingReliableStream, ReliableQuicStream*());
+
+ void SetCryptoStream(QuicCryptoStream* stream);
+
+ virtual QuicCryptoStream* GetCryptoStream();
+
+ private:
+ QuicCryptoStream* crypto_stream_;
+ DISALLOW_COPY_AND_ASSIGN(TestSession);
+};
+
+} // namespace test
+} // namespace tools
+} // namespace net
+
+#endif // NET_TOOLS_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
diff --git a/chromium/net/tools/quic/test_tools/run_all_unittests.cc b/chromium/net/tools/quic/test_tools/run_all_unittests.cc
new file mode 100644
index 00000000000..6d42d33eb8f
--- /dev/null
+++ b/chromium/net/tools/quic/test_tools/run_all_unittests.cc
@@ -0,0 +1,11 @@
+// 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/test/test_suite.h"
+
+int main(int argc, char** argv) {
+ base::TestSuite test_suite(argc, argv);
+
+ return test_suite.Run();
+}