Merge Chromium src@r53293

Change-Id: Ia79acf8670f385cee48c45b0a75371d8e950af34
diff --git a/net/base/address_family.h b/net/base/address_family.h
index 065927f..fee0584 100644
--- a/net/base/address_family.h
+++ b/net/base/address_family.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -15,6 +15,13 @@
   ADDRESS_FAMILY_IPV6,          // AF_INET6
 };
 
-}  // namesapce net
+// HostResolverFlags is a bitflag enum wrapper around the addrinfo.ai_flags
+// supported by host resolver procedures.
+enum {
+  HOST_RESOLVER_CANONNAME = 1 << 0,  // 0x1, AI_CANONNAME
+};
+typedef int HostResolverFlags;
+
+}  // namespace net
 
 #endif  // NET_BASE_ADDRESS_FAMILY_H_
diff --git a/net/base/address_list.cc b/net/base/address_list.cc
index 93ec009..1c97311 100644
--- a/net/base/address_list.cc
+++ b/net/base/address_list.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -7,15 +7,28 @@
 #include <stdlib.h>
 
 #include "base/logging.h"
+#include "net/base/net_util.h"
 #include "net/base/sys_addrinfo.h"
 
 namespace net {
 
 namespace {
 
-// Make a deep copy of |info|. This copy should be deleted using
+char* do_strdup(const char* src) {
+#if defined(OS_WIN)
+  return _strdup(src);
+#else
+  return strdup(src);
+#endif
+}
+
+// Make a copy of |info| (the dynamically-allocated parts are copied as well).
+// If |recursive| is true, chained entries via ai_next are copied too.
+// Copy returned by this function should be deleted using
 // DeleteCopyOfAddrinfo(), and NOT freeaddrinfo().
-struct addrinfo* CreateCopyOfAddrinfo(const struct addrinfo* info) {
+struct addrinfo* CreateCopyOfAddrinfo(const struct addrinfo* info,
+                                      bool recursive) {
+  DCHECK(info);
   struct addrinfo* copy = new addrinfo;
 
   // Copy all the fields (some of these are pointers, we will fix that next).
@@ -23,11 +36,7 @@
 
   // ai_canonname is a NULL-terminated string.
   if (info->ai_canonname) {
-#ifdef OS_WIN
-    copy->ai_canonname = _strdup(info->ai_canonname);
-#else
-    copy->ai_canonname = strdup(info->ai_canonname);
-#endif
+    copy->ai_canonname = do_strdup(info->ai_canonname);
   }
 
   // ai_addr is a buffer of length ai_addrlen.
@@ -37,14 +46,17 @@
   }
 
   // Recursive copy.
-  if (info->ai_next)
-    copy->ai_next = CreateCopyOfAddrinfo(info->ai_next);
+  if (recursive && info->ai_next)
+    copy->ai_next = CreateCopyOfAddrinfo(info->ai_next, recursive);
+  else
+    copy->ai_next = NULL;
 
   return copy;
 }
 
 // Free an addrinfo that was created by CreateCopyOfAddrinfo().
 void FreeMyAddrinfo(struct addrinfo* info) {
+  DCHECK(info);
   if (info->ai_canonname)
     free(info->ai_canonname);  // Allocated by strdup.
 
@@ -60,28 +72,11 @@
     FreeMyAddrinfo(next);
 }
 
-// Returns the address to port field in |info|.
-uint16* GetPortField(const struct addrinfo* info) {
-  if (info->ai_family == AF_INET) {
-    DCHECK_EQ(sizeof(sockaddr_in), info->ai_addrlen);
-    struct sockaddr_in* sockaddr =
-        reinterpret_cast<struct sockaddr_in*>(info->ai_addr);
-    return &sockaddr->sin_port;
-  } else if (info->ai_family == AF_INET6) {
-    DCHECK_EQ(sizeof(sockaddr_in6), info->ai_addrlen);
-    struct sockaddr_in6* sockaddr =
-        reinterpret_cast<struct sockaddr_in6*>(info->ai_addr);
-    return &sockaddr->sin6_port;
-  } else {
-    NOTREACHED();
-    return NULL;
-  }
-}
-
 // Assign the port for all addresses in the list.
 void SetPortRecursive(struct addrinfo* info, int port) {
-  uint16* port_field = GetPortField(info);
-  *port_field = htons(port);
+  uint16* port_field = GetPortFieldFromAddrinfo(info);
+  if (port_field)
+    *port_field = htons(port);
 
   // Assign recursively.
   if (info->ai_next)
@@ -94,8 +89,27 @@
   data_ = new Data(head, true /*is_system_created*/);
 }
 
-void AddressList::Copy(const struct addrinfo* head) {
-  data_ = new Data(CreateCopyOfAddrinfo(head), false /*is_system_created*/);
+void AddressList::Copy(const struct addrinfo* head, bool recursive) {
+  data_ = new Data(CreateCopyOfAddrinfo(head, recursive),
+                   false /*is_system_created*/);
+}
+
+void AddressList::Append(const struct addrinfo* head) {
+  DCHECK(head);
+  struct addrinfo* new_head;
+  if (data_->is_system_created) {
+    new_head = CreateCopyOfAddrinfo(data_->head, true);
+    data_ = new Data(new_head, false /*is_system_created*/);
+  } else {
+    new_head = data_->head;
+  }
+
+  // Find the end of current linked list and append new data there.
+  struct addrinfo* copy_ptr = new_head;
+  while (copy_ptr->ai_next)
+    copy_ptr = copy_ptr->ai_next;
+  DCHECK(!head->ai_canonname);
+  copy_ptr->ai_next = CreateCopyOfAddrinfo(head, true);
 }
 
 void AddressList::SetPort(int port) {
@@ -103,8 +117,15 @@
 }
 
 int AddressList::GetPort() const {
-  uint16* port_field = GetPortField(data_->head);
-  return ntohs(*port_field);
+  return GetPortFromAddrinfo(data_->head);
+}
+
+bool AddressList::GetCanonicalName(std::string* canonical_name) const {
+  DCHECK(canonical_name);
+  if (!data_ || !data_->head->ai_canonname)
+    return false;
+  canonical_name->assign(data_->head->ai_canonname);
+  return true;
 }
 
 void AddressList::SetFrom(const AddressList& src, int port) {
@@ -113,7 +134,7 @@
     *this = src;
   } else {
     // Otherwise we need to make a copy in order to change the port number.
-    Copy(src.head());
+    Copy(src.head(), true);
     SetPort(port);
   }
 }
@@ -123,25 +144,54 @@
 }
 
 // static
-AddressList AddressList::CreateIPv6Address(unsigned char data[16]) {
+AddressList AddressList::CreateIPv4Address(unsigned char data[4],
+                                           const std::string& canonical_name) {
   struct addrinfo* ai = new addrinfo;
   memset(ai, 0, sizeof(addrinfo));
-
-  ai->ai_family = AF_INET6;
+  ai->ai_family = AF_INET;
   ai->ai_socktype = SOCK_STREAM;
-  ai->ai_addrlen = sizeof(struct sockaddr_in6);
+  const size_t sockaddr_in_size = sizeof(struct sockaddr_in);
+  ai->ai_addrlen = sockaddr_in_size;
+  if (!canonical_name.empty())
+    ai->ai_canonname = do_strdup(canonical_name.c_str());
 
-  struct sockaddr_in6* addr6 = reinterpret_cast<struct sockaddr_in6*>(
-      new char[ai->ai_addrlen]);
-  memset(addr6, 0, sizeof(struct sockaddr_in6));
-
-  ai->ai_addr = reinterpret_cast<struct sockaddr*>(addr6);
-  addr6->sin6_family = AF_INET6;
-  memcpy(&addr6->sin6_addr, data, 16);
+  struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(
+      new char[sockaddr_in_size]);
+  memset(addr, 0, sockaddr_in_size);
+  addr->sin_family = AF_INET;
+  memcpy(&addr->sin_addr, data, 4);
+  ai->ai_addr = reinterpret_cast<struct sockaddr*>(addr);
 
   return AddressList(new Data(ai, false /*is_system_created*/));
 }
 
+// static
+AddressList AddressList::CreateIPv6Address(unsigned char data[16],
+                                           const std::string& canonical_name) {
+  struct addrinfo* ai = new addrinfo;
+  memset(ai, 0, sizeof(addrinfo));
+  ai->ai_family = AF_INET6;
+  ai->ai_socktype = SOCK_STREAM;
+  const size_t sockaddr_in6_size = sizeof(struct sockaddr_in6);
+  ai->ai_addrlen = sockaddr_in6_size;
+  if (!canonical_name.empty())
+    ai->ai_canonname = do_strdup(canonical_name.c_str());
+
+  struct sockaddr_in6* addr6 = reinterpret_cast<struct sockaddr_in6*>(
+      new char[sockaddr_in6_size]);
+  memset(addr6, 0, sockaddr_in6_size);
+  addr6->sin6_family = AF_INET6;
+  memcpy(&addr6->sin6_addr, data, 16);
+  ai->ai_addr = reinterpret_cast<struct sockaddr*>(addr6);
+
+  return AddressList(new Data(ai, false /*is_system_created*/));
+}
+
+AddressList::Data::Data(struct addrinfo* ai, bool is_system_created)
+    : head(ai), is_system_created(is_system_created) {
+  DCHECK(head);
+}
+
 AddressList::Data::~Data() {
   // Call either freeaddrinfo(head), or FreeMyAddrinfo(head), depending who
   // created the data.
diff --git a/net/base/address_list.h b/net/base/address_list.h
index 3087472..ce17645 100644
--- a/net/base/address_list.h
+++ b/net/base/address_list.h
@@ -1,10 +1,12 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_BASE_ADDRESS_LIST_H_
 #define NET_BASE_ADDRESS_LIST_H_
 
+#include <string>
+
 #include "base/ref_counted.h"
 
 struct addrinfo;
@@ -23,8 +25,14 @@
   // object.
   void Adopt(struct addrinfo* head);
 
-  // Copies the given addrinfo rather than adopting it.
-  void Copy(const struct addrinfo* head);
+  // Copies the given addrinfo rather than adopting it. If |recursive| is true,
+  // all linked struct addrinfos will be copied as well. Otherwise only the head
+  // will be copied, and the rest of linked entries will be ignored.
+  void Copy(const struct addrinfo* head, bool recursive);
+
+  // Appends a copy of |head| and all its linked addrinfos to the stored
+  // addrinfo.
+  void Append(const struct addrinfo* head);
 
   // Sets the port of all addresses in the list to |port| (that is the
   // sin[6]_port field for the sockaddrs).
@@ -40,20 +48,37 @@
   // a reference to |src|'s data.) Otherwise we will make a copy.
   void SetFrom(const AddressList& src, int port);
 
+  // Gets the canonical name for the address.
+  // If the canonical name exists, |*canonical_name| is filled in with the
+  // value and true is returned. If it does not exist, |*canonical_name| is
+  // not altered and false is returned.
+  // |canonical_name| must be a non-null value.
+  bool GetCanonicalName(std::string* canonical_name) const;
+
   // Clears all data from this address list. This leaves the list in the same
   // empty state as when first constructed.
   void Reset();
 
-  // Used by unit-tests to manually set the TCP socket address.
-  static AddressList CreateIPv6Address(unsigned char data[16]);
+  // Used by unit-tests to manually create an IPv4 AddressList. |data| should
+  // be an IPv4 address in network order (big endian).
+  // If |canonical_name| is non-empty, it will be duplicated in the
+  // ai_canonname field of the addrinfo struct.
+  static AddressList CreateIPv4Address(unsigned char data[4],
+                                       const std::string& canonical_name);
+
+  // Used by unit-tests to manually create an IPv6 AddressList. |data| should
+  // be an IPv6 address in network order (big endian).
+  // If |canonical_name| is non-empty, it will be duplicated in the
+  // ai_canonname field of the addrinfo struct.
+  static AddressList CreateIPv6Address(unsigned char data[16],
+                                       const std::string& canonical_name);
 
   // Get access to the head of the addrinfo list.
   const struct addrinfo* head() const { return data_->head; }
 
  private:
   struct Data : public base::RefCountedThreadSafe<Data> {
-    Data(struct addrinfo* ai, bool is_system_created)
-        : head(ai), is_system_created(is_system_created) {}
+    Data(struct addrinfo* ai, bool is_system_created);
     struct addrinfo* head;
 
     // Indicates which free function to use for |head|.
diff --git a/net/base/address_list_net_log_param.cc b/net/base/address_list_net_log_param.cc
new file mode 100644
index 0000000..05b1a0d
--- /dev/null
+++ b/net/base/address_list_net_log_param.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2010 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/base/address_list_net_log_param.h"
+
+#include "base/values.h"
+#include "net/base/net_util.h"
+#include "net/base/sys_addrinfo.h"
+
+namespace net {
+
+AddressListNetLogParam::AddressListNetLogParam(const AddressList& address_list)
+    : address_list_(address_list) {
+}
+
+Value* AddressListNetLogParam::ToValue() const {
+  DictionaryValue* dict = new DictionaryValue();
+  ListValue* list = new ListValue();
+
+  for (const addrinfo* head = address_list_.head();
+       head != NULL ; head = head->ai_next) {
+    list->Append(Value::CreateStringValue(NetAddressToStringWithPort(head)));
+  }
+
+  dict->Set(L"address_list", list);
+  return dict;
+}
+
+}  // namespace
diff --git a/net/base/address_list_net_log_param.h b/net/base/address_list_net_log_param.h
new file mode 100644
index 0000000..2a56961
--- /dev/null
+++ b/net/base/address_list_net_log_param.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2010 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_BASE_ADDRESS_LIST_NET_LOG_PARAM_H_
+#define NET_BASE_ADDRESS_LIST_NET_LOG_PARAM_H_
+
+#include "net/base/address_list.h"
+#include "net/base/net_log.h"
+
+namespace net {
+
+// NetLog parameter to describe an address list.
+// Note that AddressList uses ref-counted data, so this doesn't introduce
+// much of a memory overhead.
+class AddressListNetLogParam : public NetLog::EventParameters {
+ public:
+  explicit AddressListNetLogParam(const AddressList& address_list);
+
+  virtual Value* ToValue() const;
+
+ private:
+  AddressList address_list_;
+};
+
+}  // namespace net
+
+#endif  // NET_BASE_ADDRESS_LIST_NET_LOG_PARAM_H_
diff --git a/net/base/address_list_unittest.cc b/net/base/address_list_unittest.cc
index d440c15..6e38c7d 100644
--- a/net/base/address_list_unittest.cc
+++ b/net/base/address_list_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -7,6 +7,7 @@
 #include "base/string_util.h"
 #include "net/base/host_resolver_proc.h"
 #include "net/base/net_util.h"
+#include "net/base/sys_addrinfo.h"
 #if defined(OS_WIN)
 #include "net/base/winsock_init.h"
 #endif
@@ -15,20 +16,30 @@
 namespace {
 
 // Use getaddrinfo() to allocate an addrinfo structure.
-void CreateAddressList(net::AddressList* addrlist, int port) {
+void CreateAddressList(const std::string& hostname,
+                       int port,
+                       net::AddressList* addrlist) {
 #if defined(OS_WIN)
   net::EnsureWinsockInit();
 #endif
-  int rv = SystemHostResolverProc("192.168.1.1",
+  int rv = SystemHostResolverProc(hostname,
                                   net::ADDRESS_FAMILY_UNSPECIFIED,
-                                  addrlist);
+                                  0,
+                                  addrlist, NULL);
   EXPECT_EQ(0, rv);
   addrlist->SetPort(port);
 }
 
+void CreateLongAddressList(net::AddressList* addrlist, int port) {
+  CreateAddressList("192.168.1.1", port, addrlist);
+  net::AddressList second_list;
+  CreateAddressList("192.168.1.2", port, &second_list);
+  addrlist->Append(second_list.head());
+}
+
 TEST(AddressListTest, GetPort) {
   net::AddressList addrlist;
-  CreateAddressList(&addrlist, 81);
+  CreateAddressList("192.168.1.1", 81, &addrlist);
   EXPECT_EQ(81, addrlist.GetPort());
 
   addrlist.SetPort(83);
@@ -37,7 +48,7 @@
 
 TEST(AddressListTest, Assignment) {
   net::AddressList addrlist1;
-  CreateAddressList(&addrlist1, 85);
+  CreateAddressList("192.168.1.1", 85, &addrlist1);
   EXPECT_EQ(85, addrlist1.GetPort());
 
   // Should reference the same data as addrlist1 -- so when we change addrlist1
@@ -50,13 +61,15 @@
   EXPECT_EQ(80, addrlist2.GetPort());
 }
 
-TEST(AddressListTest, Copy) {
+TEST(AddressListTest, CopyRecursive) {
   net::AddressList addrlist1;
-  CreateAddressList(&addrlist1, 85);
+  CreateLongAddressList(&addrlist1, 85);
   EXPECT_EQ(85, addrlist1.GetPort());
 
   net::AddressList addrlist2;
-  addrlist2.Copy(addrlist1.head());
+  addrlist2.Copy(addrlist1.head(), true);
+
+  ASSERT_TRUE(addrlist2.head()->ai_next != NULL);
 
   // addrlist1 is the same as addrlist2 at this point.
   EXPECT_EQ(85, addrlist1.GetPort());
@@ -70,4 +83,93 @@
   EXPECT_EQ(90, addrlist2.GetPort());
 }
 
+TEST(AddressListTest, CopyNonRecursive) {
+  net::AddressList addrlist1;
+  CreateLongAddressList(&addrlist1, 85);
+  EXPECT_EQ(85, addrlist1.GetPort());
+
+  net::AddressList addrlist2;
+  addrlist2.Copy(addrlist1.head(), false);
+
+  ASSERT_TRUE(addrlist2.head()->ai_next == NULL);
+
+  // addrlist1 is the same as addrlist2 at this point.
+  EXPECT_EQ(85, addrlist1.GetPort());
+  EXPECT_EQ(85, addrlist2.GetPort());
+
+  // Changes to addrlist1 are not reflected in addrlist2.
+  addrlist1.SetPort(70);
+  addrlist2.SetPort(90);
+
+  EXPECT_EQ(70, addrlist1.GetPort());
+  EXPECT_EQ(90, addrlist2.GetPort());
+}
+
+TEST(AddressListTest, Append) {
+  net::AddressList addrlist1;
+  CreateAddressList("192.168.1.1", 11, &addrlist1);
+  EXPECT_EQ(11, addrlist1.GetPort());
+  net::AddressList addrlist2;
+  CreateAddressList("192.168.1.2", 12, &addrlist2);
+  EXPECT_EQ(12, addrlist2.GetPort());
+
+  ASSERT_TRUE(addrlist1.head()->ai_next == NULL);
+  addrlist1.Append(addrlist2.head());
+  ASSERT_TRUE(addrlist1.head()->ai_next != NULL);
+
+  net::AddressList addrlist3;
+  addrlist3.Copy(addrlist1.head()->ai_next, false);
+  EXPECT_EQ(12, addrlist3.GetPort());
+}
+
+static const char* kCanonicalHostname = "canonical.bar.com";
+
+TEST(AddressListTest, Canonical) {
+  // Create an addrinfo with a canonical name.
+  sockaddr_in address;
+  // The contents of address do not matter for this test,
+  // so just zero-ing them out for consistency.
+  memset(&address, 0x0, sizeof(address));
+  struct addrinfo ai;
+  memset(&ai, 0x0, sizeof(ai));
+  ai.ai_family = AF_INET;
+  ai.ai_socktype = SOCK_STREAM;
+  ai.ai_addrlen = sizeof(address);
+  ai.ai_addr = reinterpret_cast<sockaddr*>(&address);
+  ai.ai_canonname = const_cast<char *>(kCanonicalHostname);
+
+  // Copy the addrinfo struct into an AddressList object and
+  // make sure it seems correct.
+  net::AddressList addrlist1;
+  addrlist1.Copy(&ai, true);
+  const struct addrinfo* addrinfo1 = addrlist1.head();
+  EXPECT_TRUE(addrinfo1 != NULL);
+  EXPECT_TRUE(addrinfo1->ai_next == NULL);
+  std::string canon_name1;
+  EXPECT_TRUE(addrlist1.GetCanonicalName(&canon_name1));
+  EXPECT_EQ("canonical.bar.com", canon_name1);
+
+  // Copy the AddressList to another one.
+  net::AddressList addrlist2;
+  addrlist2.Copy(addrinfo1, true);
+  const struct addrinfo* addrinfo2 = addrlist2.head();
+  EXPECT_TRUE(addrinfo2 != NULL);
+  EXPECT_TRUE(addrinfo2->ai_next == NULL);
+  EXPECT_TRUE(addrinfo2->ai_canonname != NULL);
+  EXPECT_NE(addrinfo1, addrinfo2);
+  EXPECT_NE(addrinfo1->ai_canonname, addrinfo2->ai_canonname);
+  std::string canon_name2;
+  EXPECT_TRUE(addrlist2.GetCanonicalName(&canon_name2));
+  EXPECT_EQ("canonical.bar.com", canon_name2);
+
+  // Make sure that GetCanonicalName correctly returns false
+  // when ai_canonname is NULL.
+  ai.ai_canonname = NULL;
+  net::AddressList addrlist_no_canon;
+  addrlist_no_canon.Copy(&ai, true);
+  std::string canon_name3 = "blah";
+  EXPECT_FALSE(addrlist_no_canon.GetCanonicalName(&canon_name3));
+  EXPECT_EQ("blah", canon_name3);
+}
+
 }  // namespace
diff --git a/net/base/auth.h b/net/base/auth.h
index ec31bfc..1704e5b 100644
--- a/net/base/auth.h
+++ b/net/base/auth.h
@@ -16,6 +16,17 @@
 class AuthChallengeInfo :
     public base::RefCountedThreadSafe<AuthChallengeInfo> {
  public:
+  bool operator==(const AuthChallengeInfo& that) const {
+    return (this->is_proxy == that.is_proxy &&
+            this->host_and_port == that.host_and_port &&
+            this->scheme == that.scheme &&
+            this->realm == that.realm);
+  }
+
+  bool operator!=(const AuthChallengeInfo& that) const {
+    return !(*this == that);
+  }
+
   bool is_proxy;  // true for Proxy-Authenticate, false for WWW-Authenticate.
   std::wstring host_and_port;  // <host>:<port> of the server asking for auth
                                // (could be the proxy).
diff --git a/net/base/cache_type.h b/net/base/cache_type.h
index 341ce7a..0823389 100644
--- a/net/base/cache_type.h
+++ b/net/base/cache_type.h
@@ -12,6 +12,7 @@
   DISK_CACHE,  // Disk is used as the backing storage.
   MEMORY_CACHE,  // Data is stored only in memory.
   MEDIA_CACHE,  // Optimized to handle media files.
+  APP_CACHE  // Backing store for an AppCache.
 };
 
 }  // namespace disk_cache
diff --git a/net/base/capturing_net_log.cc b/net/base/capturing_net_log.cc
new file mode 100644
index 0000000..08f3b8d
--- /dev/null
+++ b/net/base/capturing_net_log.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2010 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/base/capturing_net_log.h"
+
+namespace net {
+
+CapturingNetLog::CapturingNetLog(size_t max_num_entries)
+    : next_id_(0), max_num_entries_(max_num_entries) {
+}
+
+void CapturingNetLog::AddEntry(EventType type,
+                               const base::TimeTicks& time,
+                               const Source& source,
+                               EventPhase phase,
+                               EventParameters* extra_parameters) {
+  Entry entry(type, time, source, phase, extra_parameters);
+  if (entries_.size() + 1 < max_num_entries_)
+    entries_.push_back(entry);
+}
+
+uint32 CapturingNetLog::NextID() {
+  return next_id_++;
+}
+
+void CapturingNetLog::Clear() {
+  entries_.clear();
+}
+
+void CapturingBoundNetLog::Clear() {
+  capturing_net_log_->Clear();
+}
+
+void CapturingBoundNetLog::AppendTo(const BoundNetLog& net_log) const {
+  for (size_t i = 0; i < entries().size(); ++i) {
+    const CapturingNetLog::Entry& entry = entries()[i];
+    net_log.AddEntryWithTime(entry.type, entry.time, entry.phase,
+                             entry.extra_parameters);
+  }
+}
+
+}  // namespace net
diff --git a/net/base/capturing_net_log.h b/net/base/capturing_net_log.h
new file mode 100644
index 0000000..cad2d26
--- /dev/null
+++ b/net/base/capturing_net_log.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2010 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_BASE_CAPTURING_NET_LOG_H_
+#define NET_BASE_CAPTURING_NET_LOG_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "net/base/net_log.h"
+
+namespace net {
+
+// CapturingNetLog is an implementation of NetLog that saves messages to a
+// bounded buffer.
+class CapturingNetLog : public NetLog {
+ public:
+  struct Entry {
+    Entry(EventType type,
+          const base::TimeTicks& time,
+          Source source,
+          EventPhase phase,
+          EventParameters* extra_parameters)
+        : type(type), time(time), source(source), phase(phase),
+          extra_parameters(extra_parameters) {
+    }
+
+    EventType type;
+    base::TimeTicks time;
+    Source source;
+    EventPhase phase;
+    scoped_refptr<EventParameters> extra_parameters;
+  };
+
+  // Ordered set of entries that were logged.
+  typedef std::vector<Entry> EntryList;
+
+  enum { kUnbounded = -1 };
+
+  // Creates a CapturingNetLog that logs a maximum of |max_num_entries|
+  // messages.
+  explicit CapturingNetLog(size_t max_num_entries);
+
+  // NetLog implementation:
+  virtual void AddEntry(EventType type,
+                        const base::TimeTicks& time,
+                        const Source& source,
+                        EventPhase phase,
+                        EventParameters* extra_parameters);
+  virtual uint32 NextID();
+  virtual bool HasListener() const { return true; }
+
+  // Returns the list of all entries in the log.
+  const EntryList& entries() const { return entries_; }
+
+  void Clear();
+
+ private:
+  uint32 next_id_;
+  size_t max_num_entries_;
+  EntryList entries_;
+
+  DISALLOW_COPY_AND_ASSIGN(CapturingNetLog);
+};
+
+// Helper class that exposes a similar API as BoundNetLog, but uses a
+// CapturingNetLog rather than the more generic NetLog.
+//
+// CapturingBoundNetLog can easily be converted to a BoundNetLog using the
+// bound() method.
+class CapturingBoundNetLog {
+ public:
+  CapturingBoundNetLog(const NetLog::Source& source, CapturingNetLog* net_log)
+      : source_(source), capturing_net_log_(net_log) {
+  }
+
+  explicit CapturingBoundNetLog(size_t max_num_entries)
+      : capturing_net_log_(new CapturingNetLog(max_num_entries)) {}
+
+  // The returned BoundNetLog is only valid while |this| is alive.
+  BoundNetLog bound() const {
+    return BoundNetLog(source_, capturing_net_log_.get());
+  }
+
+  // Returns the list of all entries in the log.
+  const CapturingNetLog::EntryList& entries() const {
+    return capturing_net_log_->entries();
+  }
+
+  void Clear();
+
+  // Sends all of captured messages to |net_log|, using the same source ID
+  // as |net_log|.
+  void AppendTo(const BoundNetLog& net_log) const;
+
+ private:
+  NetLog::Source source_;
+  scoped_ptr<CapturingNetLog> capturing_net_log_;
+
+  DISALLOW_COPY_AND_ASSIGN(CapturingBoundNetLog);
+};
+
+}  // namespace net
+
+#endif  // NET_BASE_CAPTURING_NET_LOG_H_
+
diff --git a/net/base/cert_database.h b/net/base/cert_database.h
index e78c22e..31e3401 100644
--- a/net/base/cert_database.h
+++ b/net/base/cert_database.h
@@ -1,14 +1,16 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_BASE_CERT_DATABASE_H_
 #define NET_BASE_CERT_DATABASE_H_
 
-#include "net/base/x509_certificate.h"
+#include "base/basictypes.h"
 
 namespace net {
 
+class X509Certificate;
+
 // This class provides functions to manipulate the local
 // certificate store.
 
@@ -20,12 +22,16 @@
  public:
   CertDatabase();
 
-  // Extract and Store User (Client) Certificate from a data blob.
-  // Return true if successful.
-  bool AddUserCert(const char* data, int len);
+  // Check whether this is a valid user cert that we have the private key for.
+  // Returns OK or a network error code such as ERR_CERT_CONTAINS_ERRORS.
+  int CheckUserCert(X509Certificate* cert);
+
+  // Store user (client) certificate. Assumes CheckUserCert has already passed.
+  // Returns OK, or ERR_ADD_USER_CERT_FAILED if there was a problem saving to
+  // the platform cert database, or possibly other network error codes.
+  int AddUserCert(X509Certificate* cert);
 
  private:
-  void Init();
   DISALLOW_COPY_AND_ASSIGN(CertDatabase);
 };
 
diff --git a/net/base/cert_database_mac.cc b/net/base/cert_database_mac.cc
index 5caf9db..b0afd50 100644
--- a/net/base/cert_database_mac.cc
+++ b/net/base/cert_database_mac.cc
@@ -1,24 +1,60 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/base/cert_database.h"
 
+#include <Security/Security.h>
+
+#include "base/crypto/cssm_init.h"
+#include "base/lock.h"
 #include "base/logging.h"
+#include "net/base/net_errors.h"
+#include "net/base/x509_certificate.h"
 
 namespace net {
 
 CertDatabase::CertDatabase() {
-  NOTIMPLEMENTED();
 }
 
-bool CertDatabase::AddUserCert(const char* data, int len) {
-  NOTIMPLEMENTED();
-  return false;
+int CertDatabase::CheckUserCert(X509Certificate* cert) {
+  if (!cert)
+    return ERR_CERT_INVALID;
+  if (cert->HasExpired())
+    return ERR_CERT_DATE_INVALID;
+
+  // Verify the Keychain already has the corresponding private key:
+  SecIdentityRef identity = NULL;
+  OSStatus err = SecIdentityCreateWithCertificate(NULL, cert->os_cert_handle(),
+                                                  &identity);
+  if (err == errSecItemNotFound) {
+    LOG(ERROR) << "CertDatabase couldn't find private key for user cert";
+    return ERR_NO_PRIVATE_KEY_FOR_CERT;
+  }
+  if (err != noErr || !identity) {
+    // TODO(snej): Map the error code more intelligently.
+    return ERR_CERT_INVALID;
+  }
+
+  CFRelease(identity);
+  return OK;
 }
 
-void CertDatabase::Init() {
-  NOTIMPLEMENTED();
+int CertDatabase::AddUserCert(X509Certificate* cert) {
+  OSStatus err;
+  {
+    AutoLock locked(base::GetMacSecurityServicesLock());
+    err = SecCertificateAddToKeychain(cert->os_cert_handle(), NULL);
+  }
+  switch (err) {
+    case noErr:
+    case errSecDuplicateItem:
+      return OK;
+    default:
+      LOG(ERROR) << "CertDatabase failed to add cert to keychain: " << err;
+      // TODO(snej): Map the error code more intelligently.
+      return ERR_ADD_USER_CERT_FAILED;
+  }
 }
 
 }  // namespace net
diff --git a/net/base/cert_database_nss.cc b/net/base/cert_database_nss.cc
index e3c1a09..98930ff 100644
--- a/net/base/cert_database_nss.cc
+++ b/net/base/cert_database_nss.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -15,31 +15,20 @@
 #include "base/logging.h"
 #include "base/scoped_ptr.h"
 #include "base/nss_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/x509_certificate.h"
 
 namespace net {
 
 CertDatabase::CertDatabase() {
-  Init();
+  base::EnsureNSSInit();
 }
 
-bool CertDatabase::AddUserCert(const char* data, int len) {
-  CERTCertificate* cert = NULL;
-  PK11SlotInfo* slot = NULL;
-  std::string nickname;
-  bool is_success = true;
-
-  // Make a copy of "data" since CERT_DecodeCertPackage
-  // might modify it.
-  char* data_copy = new char[len];
-  memcpy(data_copy, data, len);
-
-  // Parse into a certificate structure.
-  cert = CERT_DecodeCertFromPackage(data_copy, len);
-  delete [] data_copy;
-  if (!cert) {
-    LOG(ERROR) << "Couldn't create a temporary certificate";
-    return false;
-  }
+int CertDatabase::CheckUserCert(X509Certificate* cert_obj) {
+  if (!cert_obj)
+    return ERR_CERT_INVALID;
+  if (cert_obj->HasExpired())
+    return ERR_CERT_DATE_INVALID;
 
   // Check if the private key corresponding to the certificate exist
   // We shouldn't accept any random client certificate sent by a CA.
@@ -48,22 +37,25 @@
   // also imports the certificate if the private key exists. This
   // doesn't seem to be the case.
 
-  slot = PK11_KeyForCertExists(cert, NULL, NULL);
+  CERTCertificate* cert = cert_obj->os_cert_handle();
+  PK11SlotInfo* slot = PK11_KeyForCertExists(cert, NULL, NULL);
   if (!slot) {
     LOG(ERROR) << "No corresponding private key in store";
-    CERT_DestroyCertificate(cert);
-    return false;
+    return ERR_NO_PRIVATE_KEY_FOR_CERT;
   }
   PK11_FreeSlot(slot);
-  slot = NULL;
 
-  // TODO(gauravsh): We also need to make sure another certificate
-  // doesn't already exist for the same private key.
+  return OK;
+}
+
+int CertDatabase::AddUserCert(X509Certificate* cert_obj) {
+  CERTCertificate* cert = cert_obj->os_cert_handle();
+  PK11SlotInfo* slot = NULL;
+  std::string nickname;
 
   // Create a nickname for this certificate.
   // We use the scheme used by Firefox:
   // --> <subject's common name>'s <issuer's common name> ID.
-  //
 
   std::string username, ca_name;
   char* temp_username = CERT_GetCommonName(&cert->subject);
@@ -78,21 +70,19 @@
   }
   nickname = username + "'s " + ca_name + " ID";
 
-  slot = PK11_ImportCertForKey(cert,
-                               const_cast<char*>(nickname.c_str()),
-                               NULL);
-  if (slot) {
-    PK11_FreeSlot(slot);
-  } else {
-    LOG(ERROR) << "Couldn't import user certificate.";
-    is_success = false;
+  {
+    base::AutoNSSWriteLock lock;
+    slot = PK11_ImportCertForKey(cert,
+                                 const_cast<char*>(nickname.c_str()),
+                                 NULL);
   }
-  CERT_DestroyCertificate(cert);
-  return is_success;
-}
 
-void CertDatabase::Init() {
-  base::EnsureNSSInit();
+  if (!slot) {
+    LOG(ERROR) << "Couldn't import user certificate.";
+    return ERR_ADD_USER_CERT_FAILED;
+  }
+  PK11_FreeSlot(slot);
+  return OK;
 }
 
 }  // namespace net
diff --git a/net/base/cert_database_win.cc b/net/base/cert_database_win.cc
index 5caf9db..4c5e8df 100644
--- a/net/base/cert_database_win.cc
+++ b/net/base/cert_database_win.cc
@@ -1,24 +1,56 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/base/cert_database.h"
 
-#include "base/logging.h"
+#include <windows.h>
+#include <wincrypt.h>
+#pragma comment(lib, "crypt32.lib")
+
+#include "net/base/net_errors.h"
+#include "net/base/x509_certificate.h"
 
 namespace net {
 
 CertDatabase::CertDatabase() {
-  NOTIMPLEMENTED();
 }
 
-bool CertDatabase::AddUserCert(const char* data, int len) {
-  NOTIMPLEMENTED();
-  return false;
+int CertDatabase::CheckUserCert(X509Certificate* cert) {
+  if (!cert)
+    return ERR_CERT_INVALID;
+  if (cert->HasExpired())
+    return ERR_CERT_DATE_INVALID;
+
+  // TODO(rsleevi): Should CRYPT_FIND_SILENT_KEYSET_FLAG be specified? A UI
+  // may be shown here / this call may block.
+  if (!CryptFindCertificateKeyProvInfo(cert->os_cert_handle(), 0, NULL))
+    return ERR_NO_PRIVATE_KEY_FOR_CERT;
+
+  return OK;
 }
 
-void CertDatabase::Init() {
-  NOTIMPLEMENTED();
+int CertDatabase::AddUserCert(X509Certificate* cert) {
+  // TODO(rsleevi): Would it be more appropriate to have the CertDatabase take
+  // construction parameters (Keychain filepath on Mac OS X, PKCS #11 slot on
+  // NSS, and Store Type / Path) here? For now, certs will be stashed into the
+  // user's personal store, which will not automatically mark them as trusted,
+  // but will allow them to be used for client auth.
+  HCERTSTORE cert_db = CertOpenSystemStore(NULL, L"MY");
+  if (!cert_db)
+    return ERR_ADD_USER_CERT_FAILED;
+
+  BOOL added = CertAddCertificateContextToStore(cert_db,
+                                                cert->os_cert_handle(),
+                                                CERT_STORE_ADD_USE_EXISTING,
+                                                NULL);
+
+  CertCloseStore(cert_db, 0);
+
+  if (!added)
+    return ERR_ADD_USER_CERT_FAILED;
+
+  return OK;
 }
 
 }  // namespace net
diff --git a/net/base/cert_test_util.cc b/net/base/cert_test_util.cc
new file mode 100644
index 0000000..9fc6573
--- /dev/null
+++ b/net/base/cert_test_util.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2010 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/base/cert_test_util.h"
+
+#include "build/build_config.h"
+
+#if defined(USE_NSS)
+#include <cert.h>
+#include "base/nss_util.h"
+#elif defined(OS_MACOSX)
+#include <Security/Security.h>
+#include "base/scoped_cftyperef.h"
+#endif
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "net/base/x509_certificate.h"
+
+namespace net {
+
+#if defined(USE_NSS)
+X509Certificate* LoadTemporaryRootCert(const FilePath& filename) {
+  base::EnsureNSSInit();
+
+  std::string rawcert;
+  if (!file_util::ReadFileToString(filename, &rawcert)) {
+    LOG(ERROR) << "Can't load certificate " << filename.value();
+    return NULL;
+  }
+
+  CERTCertificate *cert;
+  cert = CERT_DecodeCertFromPackage(const_cast<char *>(rawcert.c_str()),
+                                    rawcert.length());
+  if (!cert) {
+    LOG(ERROR) << "Can't convert certificate " << filename.value();
+    return NULL;
+  }
+
+  // TODO(port): remove this const_cast after NSS 3.12.3 is released
+  CERTCertTrust trust;
+  int rv = CERT_DecodeTrustString(&trust, const_cast<char *>("TCu,Cu,Tu"));
+  if (rv != SECSuccess) {
+    LOG(ERROR) << "Can't decode trust string";
+    CERT_DestroyCertificate(cert);
+    return NULL;
+  }
+
+  rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, &trust);
+  if (rv != SECSuccess) {
+    LOG(ERROR) << "Can't change trust for certificate " << filename.value();
+    CERT_DestroyCertificate(cert);
+    return NULL;
+  }
+
+  X509Certificate* result = X509Certificate::CreateFromHandle(
+      cert,
+      X509Certificate::SOURCE_LONE_CERT_IMPORT,
+      X509Certificate::OSCertHandles());
+  CERT_DestroyCertificate(cert);
+  return result;
+}
+#endif
+
+#if defined(OS_MACOSX)
+X509Certificate* LoadTemporaryRootCert(const FilePath& filename) {
+  std::string rawcert;
+  if (!file_util::ReadFileToString(filename, &rawcert)) {
+    LOG(ERROR) << "Can't load certificate " << filename.value();
+    return NULL;
+  }
+
+  CFDataRef pem = CFDataCreate(kCFAllocatorDefault,
+                               reinterpret_cast<const UInt8*>(rawcert.data()),
+                               static_cast<CFIndex>(rawcert.size()));
+  if (!pem)
+    return NULL;
+  scoped_cftyperef<CFDataRef> scoped_pem(pem);
+
+  SecExternalFormat input_format = kSecFormatUnknown;
+  SecExternalItemType item_type = kSecItemTypeUnknown;
+  CFArrayRef cert_array = NULL;
+  if (SecKeychainItemImport(pem, NULL, &input_format, &item_type, 0, NULL, NULL,
+                            &cert_array))
+    return NULL;
+  scoped_cftyperef<CFArrayRef> scoped_cert_array(cert_array);
+
+  if (!CFArrayGetCount(cert_array))
+    return NULL;
+
+  SecCertificateRef cert_ref = static_cast<SecCertificateRef>(
+      const_cast<void*>(CFArrayGetValueAtIndex(cert_array, 0)));
+
+  return X509Certificate::CreateFromHandle(cert_ref,
+      X509Certificate::SOURCE_LONE_CERT_IMPORT,
+      X509Certificate::OSCertHandles());
+}
+#endif
+
+}  // namespace net
diff --git a/net/base/cert_test_util.h b/net/base/cert_test_util.h
new file mode 100644
index 0000000..a288774
--- /dev/null
+++ b/net/base/cert_test_util.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2010 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_BASE_CERT_TEST_UTIL_H_
+#define NET_BASE_CERT_TEST_UTIL_H_
+
+#include "base/file_path.h"
+#include "build/build_config.h"
+
+namespace net {
+
+class X509Certificate;
+
+#if defined(USE_NSS) || defined(OS_MACOSX)
+// Loads and trusts a root CA certificate (stored in a file) temporarily.
+// TODO(wtc): Implement this function on Windows (http://crbug.com/8470).
+X509Certificate* LoadTemporaryRootCert(const FilePath& filename);
+#endif
+
+}  // namespace net
+
+#endif  // NET_BASE_CERT_TEST_UTIL_H_
diff --git a/net/base/completion_callback.h b/net/base/completion_callback.h
index 7a5655b..11a6b3a 100644
--- a/net/base/completion_callback.h
+++ b/net/base/completion_callback.h
@@ -5,7 +5,7 @@
 #ifndef NET_BASE_COMPLETION_CALLBACK_H__
 #define NET_BASE_COMPLETION_CALLBACK_H__
 
-#include "base/task.h"
+#include "base/callback.h"
 
 namespace net {
 
diff --git a/net/base/connection_type_histograms.cc b/net/base/connection_type_histograms.cc
index 7326a92..de780f2 100644
--- a/net/base/connection_type_histograms.cc
+++ b/net/base/connection_type_histograms.cc
@@ -20,22 +20,18 @@
 //
 // Each histogram has an unused bucket at the end to allow seamless future
 // expansion.
-void UpdateConnectionTypeHistograms(ConnectionType type, bool success) {
+void UpdateConnectionTypeHistograms(ConnectionType type) {
   static bool had_connection_type[NUM_OF_CONNECTION_TYPES];
 
   if (type >= 0 && type < NUM_OF_CONNECTION_TYPES) {
     if (!had_connection_type[type]) {
       had_connection_type[type] = true;
-      UMA_HISTOGRAM_ENUMERATION("Net.HadConnectionType2",
+      UMA_HISTOGRAM_ENUMERATION("Net.HadConnectionType3",
           type, NUM_OF_CONNECTION_TYPES);
     }
 
-    if (success)
-      UMA_HISTOGRAM_ENUMERATION("Net.ConnectionTypeCount2",
-          type, NUM_OF_CONNECTION_TYPES);
-    else
-      UMA_HISTOGRAM_ENUMERATION("Net.ConnectionTypeFailCount2",
-          type, NUM_OF_CONNECTION_TYPES);
+    UMA_HISTOGRAM_ENUMERATION("Net.ConnectionTypeCount3",
+        type, NUM_OF_CONNECTION_TYPES);
   } else {
     NOTREACHED();  // Someone's logging an invalid type!
   }
diff --git a/net/base/connection_type_histograms.h b/net/base/connection_type_histograms.h
index c8517ff..582f09d 100644
--- a/net/base/connection_type_histograms.h
+++ b/net/base/connection_type_histograms.h
@@ -33,8 +33,7 @@
 };
 
 // Update the connection type histograms.  |type| is the connection type.
-// |success| is whether or not the connection was successful or not.
-void UpdateConnectionTypeHistograms(ConnectionType type, bool success);
+void UpdateConnectionTypeHistograms(ConnectionType type);
 
 }  // namespace net
 
diff --git a/net/base/cookie_monster.cc b/net/base/cookie_monster.cc
index 805bfc1..e79cf47 100644
--- a/net/base/cookie_monster.cc
+++ b/net/base/cookie_monster.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -53,6 +53,7 @@
 #include "base/string_tokenizer.h"
 #include "base/string_util.h"
 #include "googleurl/src/gurl.h"
+#include "googleurl/src/url_canon.h"
 #include "net/base/net_util.h"
 #include "net/base/registry_controlled_domain.h"
 
@@ -66,21 +67,36 @@
 using base::Time;
 using base::TimeDelta;
 
+static const int kMinutesInTenYears = 10 * 365 * 24 * 60;
+
 namespace net {
 
+namespace {
+
 // Cookie garbage collection thresholds.  Based off of the Mozilla defaults.
 // It might seem scary to have a high purge value, but really it's not.  You
 // just make sure that you increase the max to cover the increase in purge,
 // and we would have been purging the same amount of cookies.  We're just
 // going through the garbage collection process less often.
-static const size_t kNumCookiesPerHost      = 70;  // ~50 cookies
-static const size_t kNumCookiesPerHostPurge = 20;
-static const size_t kNumCookiesTotal        = 3300;  // ~3000 cookies
-static const size_t kNumCookiesTotalPurge   = 300;
+const size_t kNumCookiesPerHost      = 70;  // ~50 cookies
+const size_t kNumCookiesPerHostPurge = 20;
+const size_t kNumCookiesTotal        = 3300;  // ~3000 cookies
+const size_t kNumCookiesTotalPurge   = 300;
 
 // Default minimum delay after updating a cookie's LastAccessDate before we
 // will update it again.
-static const int kDefaultAccessUpdateThresholdSeconds = 60;
+const int kDefaultAccessUpdateThresholdSeconds = 60;
+
+// Comparator to sort cookies from highest creation date to lowest
+// creation date.
+struct OrderByCreationTimeDesc {
+  bool operator()(const CookieMonster::CookieMap::iterator& a,
+                  const CookieMonster::CookieMap::iterator& b) const {
+    return a->second->CreationDate() > b->second->CreationDate();
+  }
+};
+
+}  // namespace
 
 // static
 bool CookieMonster::enable_file_scheme_ = false;
@@ -90,19 +106,14 @@
   enable_file_scheme_ = true;
 }
 
-CookieMonster::CookieMonster()
-    : initialized_(false),
-      store_(NULL),
-      last_access_threshold_(
-          TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)) {
-  SetDefaultCookieableSchemes();
-}
-
-CookieMonster::CookieMonster(PersistentCookieStore* store)
+CookieMonster::CookieMonster(PersistentCookieStore* store, Delegate* delegate)
     : initialized_(false),
       store_(store),
       last_access_threshold_(
-          TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)) {
+          TimeDelta::FromSeconds(kDefaultAccessUpdateThresholdSeconds)),
+      delegate_(delegate),
+      last_statistic_record_time_(Time::Now()) {
+  InitializeHistograms();
   SetDefaultCookieableSchemes();
 }
 
@@ -110,6 +121,59 @@
   DeleteAll(false);
 }
 
+// Initialize all histogram counter variables used in this class.
+//
+// Normal histogram usage involves using the macros defined in
+// histogram.h, which automatically takes care of declaring these
+// variables (as statics), initializing them, and accumulating into
+// them, all from a single entry point.  Unfortunately, that solution
+// doesn't work for the CookieMonster, as it's vulnerable to races between
+// separate threads executing the same functions and hence initializing the
+// same static variables.  There isn't a race danger in the histogram
+// accumulation calls; they are written to be resilient to simultaneous
+// calls from multiple threads.
+//
+// The solution taken here is to have per-CookieMonster instance
+// variables that are constructed during CookieMonster construction.
+// Note that these variables refer to the same underlying histogram,
+// so we still race (but safely) with other CookieMonster instances
+// for accumulation.
+//
+// To do this we've expanded out the individual histogram macros calls,
+// with declarations of the variables in the class decl, initialization here
+// (done from the class constructor) and direct calls to the accumulation
+// methods where needed.  The specific histogram macro calls on which the
+// initialization is based are included in comments below.
+void CookieMonster::InitializeHistograms() {
+  // From UMA_HISTOGRAM_CUSTOM_COUNTS
+  histogram_expiration_duration_minutes_ = Histogram::FactoryGet(
+      "net.CookieExpirationDurationMinutes",
+      1, kMinutesInTenYears, 50,
+      Histogram::kUmaTargetedHistogramFlag);
+  histogram_between_access_interval_minutes_ = Histogram::FactoryGet(
+      "net.CookieBetweenAccessIntervalMinutes",
+      1, kMinutesInTenYears, 50,
+      Histogram::kUmaTargetedHistogramFlag);
+  histogram_evicted_last_access_minutes_ = Histogram::FactoryGet(
+      "net.CookieEvictedLastAccessMinutes",
+      1, kMinutesInTenYears, 50,
+      Histogram::kUmaTargetedHistogramFlag);
+  histogram_count_  = Histogram::FactoryGet(
+      "net.CookieCount", 1, 4000, 50,
+      Histogram::kUmaTargetedHistogramFlag);
+
+  // From UMA_HISTOGRAM_COUNTS_10000 & UMA_HISTOGRAM_CUSTOM_COUNTS
+  histogram_number_duplicate_db_cookies_ = Histogram::FactoryGet(
+      "Net.NumDuplicateCookiesInDb", 1, 10000, 50,
+      Histogram::kUmaTargetedHistogramFlag);
+
+  // From UMA_HISTOGRAM_ENUMERATION
+  histogram_cookie_deletion_cause_ = LinearHistogram::FactoryGet(
+      "net.CookieDeletionCause", 1,
+      DELETE_COOKIE_LAST_ENTRY, DELETE_COOKIE_LAST_ENTRY + 1,
+      Histogram::kUmaTargetedHistogramFlag);
+}
+
 void CookieMonster::InitStore() {
   DCHECK(store_) << "Store must exist to initialize";
 
@@ -125,6 +189,153 @@
        it != cookies.end(); ++it) {
     InternalInsertCookie(it->first, it->second, false);
   }
+
+  // After importing cookies from the PersistentCookieStore, verify that
+  // none of our constraints are violated.
+  //
+  // In particular, the backing store might have given us duplicate cookies.
+  EnsureCookiesMapIsValid();
+}
+
+void CookieMonster::EnsureCookiesMapIsValid() {
+  lock_.AssertAcquired();
+
+  int num_duplicates_trimmed = 0;
+
+  // Iterate through all the of the cookies, grouped by host.
+  CookieMap::iterator prev_range_end = cookies_.begin();
+  while (prev_range_end != cookies_.end()) {
+    CookieMap::iterator cur_range_begin = prev_range_end;
+    const std::string key = cur_range_begin->first;  // Keep a copy.
+    CookieMap::iterator cur_range_end = cookies_.upper_bound(key);
+    prev_range_end = cur_range_end;
+
+    // Ensure no equivalent cookies for this host.
+    num_duplicates_trimmed +=
+        TrimDuplicateCookiesForHost(key, cur_range_begin, cur_range_end);
+  }
+
+  // Record how many duplicates were found in the database.
+  // See InitializeHistograms() for details.
+  histogram_cookie_deletion_cause_->Add(num_duplicates_trimmed);
+
+  // TODO(eroman): Should also verify that there are no cookies with the same
+  // creation time, since that is assumed to be unique by the rest of the code.
+}
+
+// Our strategy to find duplicates is:
+// (1) Build a map from (cookiename, cookiepath) to
+//     {list of cookies with this signature, sorted by creation time}.
+// (2) For each list with more than 1 entry, keep the cookie having the
+//     most recent creation time, and delete the others.
+namespace {
+// Two cookies are considered equivalent if they have the same domain,
+// name, and path.
+struct CookieSignature {
+ public:
+  CookieSignature(const std::string& name, const std::string& domain,
+                  const std::string& path)
+      : name(name),
+        domain(domain),
+        path(path) {}
+
+  // To be a key for a map this class needs to be assignable, copyable,
+  // and have an operator<.  The default assignment operator
+  // and copy constructor are exactly what we want.
+
+  bool operator<(const CookieSignature& cs) const {
+    // Name compare dominates, then domain, then path.
+    int diff = name.compare(cs.name);
+    if (diff != 0)
+      return diff < 0;
+
+    diff = domain.compare(cs.domain);
+    if (diff != 0)
+      return diff < 0;
+
+    return path.compare(cs.path) < 0;
+  }
+
+  std::string name;
+  std::string domain;
+  std::string path;
+};
+}
+
+int CookieMonster::TrimDuplicateCookiesForHost(
+    const std::string& key,
+    CookieMap::iterator begin,
+    CookieMap::iterator end) {
+  lock_.AssertAcquired();
+
+  // Set of cookies ordered by creation time.
+  typedef std::set<CookieMap::iterator, OrderByCreationTimeDesc> CookieSet;
+
+  // Helper map we populate to find the duplicates.
+  typedef std::map<CookieSignature, CookieSet> EquivalenceMap;
+  EquivalenceMap equivalent_cookies;
+
+  // The number of duplicate cookies that have been found.
+  int num_duplicates = 0;
+
+  // Iterate through all of the cookies in our range, and insert them into
+  // the equivalence map.
+  for (CookieMap::iterator it = begin; it != end; ++it) {
+    DCHECK_EQ(key, it->first);
+    CanonicalCookie* cookie = it->second;
+
+    CookieSignature signature(cookie->Name(), cookie->Domain(),
+                              cookie->Path());
+    CookieSet& list = equivalent_cookies[signature];
+
+    // We found a duplicate!
+    if (!list.empty())
+      num_duplicates++;
+
+    // We save the iterator into |cookies_| rather than the actual cookie
+    // pointer, since we may need to delete it later.
+    list.insert(it);
+  }
+
+  // If there were no duplicates, we are done!
+  if (num_duplicates == 0)
+    return 0;
+
+  // Otherwise, delete all the duplicate cookies, both from our in-memory store
+  // and from the backing store.
+  for (EquivalenceMap::iterator it = equivalent_cookies.begin();
+       it != equivalent_cookies.end();
+       ++it) {
+    const CookieSignature& signature = it->first;
+    CookieSet& dupes = it->second;
+
+    if (dupes.size() <= 1)
+      continue;  // This cookiename/path has no duplicates.
+
+    // Since |dups| is sorted by creation time (descending), the first cookie
+    // is the most recent one, so we will keep it. The rest are duplicates.
+    dupes.erase(dupes.begin());
+
+    LOG(ERROR) << StringPrintf("Found %d duplicate cookies for host='%s', "
+                               "with {name='%s', domain='%s', path='%s'}",
+                               static_cast<int>(dupes.size()),
+                               key.c_str(),
+                               signature.name.c_str(),
+                               signature.domain.c_str(),
+                               signature.path.c_str());
+
+    // Remove all the cookies identified by |dupes|. It is valid to delete our
+    // list of iterators one at a time, since |cookies_| is a multimap (they
+    // don't invalidate existing iterators following deletion).
+    for (CookieSet::iterator dupes_it = dupes.begin();
+         dupes_it != dupes.end();
+         ++dupes_it) {
+      InternalDeleteCookie(*dupes_it, true /*sync_to_store*/,
+                           DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
+    }
+  }
+
+  return num_duplicates;
 }
 
 void CookieMonster::SetDefaultCookieableSchemes() {
@@ -260,6 +471,10 @@
   return Time();
 }
 
+bool CookieMonster::DomainIsHostOnly(const std::string& domain_string) {
+  return (domain_string.empty() || domain_string[0] != '.');
+}
+
 // Returns the effective TLD+1 for a given host. This only makes sense for http
 // and https schemes. For other schemes, the host will be returned unchanged
 // (minus any leading .).
@@ -268,27 +483,28 @@
   if (scheme == "http" || scheme == "https")
     return RegistryControlledDomainService::GetDomainAndRegistry(host);
 
-  if (!host.empty() && host[0] == '.')
+  if (!CookieMonster::DomainIsHostOnly(host))
     return host.substr(1);
   return host;
 }
 
-// Determine the cookie domain key to use for setting the specified cookie.
+// Determine the cookie domain key to use for setting a cookie with the
+// specified domain attribute string.
 // On success returns true, and sets cookie_domain_key to either a
 //   -host cookie key (ex: "google.com")
 //   -domain cookie key (ex: ".google.com")
-static bool GetCookieDomainKey(const GURL& url,
-                               const CookieMonster::ParsedCookie& pc,
-                               std::string* cookie_domain_key) {
+static bool GetCookieDomainKeyWithString(const GURL& url,
+                                         const std::string& domain_string,
+                                         std::string* cookie_domain_key) {
   const std::string url_host(url.host());
 
-  // If no domain was specified in the cookie, default to a host cookie.
+  // If no domain was specified in the domain string, default to a host cookie.
   // We match IE/Firefox in allowing a domain=IPADDR if it matches the url
   // ip address hostname exactly.  It should be treated as a host cookie.
-  if (!pc.HasDomain() || pc.Domain().empty() ||
-      (url.HostIsIPAddress() && url_host == pc.Domain())) {
+  if (domain_string.empty() ||
+      (url.HostIsIPAddress() && url_host == domain_string)) {
     *cookie_domain_key = url_host;
-    DCHECK((*cookie_domain_key)[0] != '.');
+    DCHECK(CookieMonster::DomainIsHostOnly(*cookie_domain_key));
     return true;
   }
 
@@ -300,7 +516,7 @@
   // also treats domain=.....my.domain.com like domain=.my.domain.com, but
   // neither IE nor Safari do this, and we don't either.
   url_canon::CanonHostInfo ignored;
-  std::string cookie_domain(net::CanonicalizeHost(pc.Domain(), &ignored));
+  std::string cookie_domain(net::CanonicalizeHost(domain_string, &ignored));
   if (cookie_domain.empty())
     return false;
   if (cookie_domain[0] != '.')
@@ -330,8 +546,18 @@
   return true;
 }
 
-static std::string CanonPath(const GURL& url,
-                             const CookieMonster::ParsedCookie& pc) {
+// Determine the cookie domain key to use for setting the specified cookie.
+static bool GetCookieDomainKey(const GURL& url,
+                               const CookieMonster::ParsedCookie& pc,
+                               std::string* cookie_domain_key) {
+  std::string domain_string;
+  if (pc.HasDomain())
+    domain_string = pc.Domain();
+  return GetCookieDomainKeyWithString(url, domain_string, cookie_domain_key);
+}
+
+static std::string CanonPathWithString(const GURL& url,
+                                       const std::string& path_string) {
   // The RFC says the path should be a prefix of the current URL path.
   // However, Mozilla allows you to set any path for compatibility with
   // broken websites.  We unfortunately will mimic this behavior.  We try
@@ -339,8 +565,8 @@
   // default the path to something reasonable.
 
   // The path was supplied in the cookie, we'll take it.
-  if (pc.HasPath() && !pc.Path().empty() && pc.Path()[0] == '/')
-    return pc.Path();
+  if (!path_string.empty() && path_string[0] == '/')
+    return path_string;
 
   // The path was not supplied in the cookie or invalid, we will default
   // to the current URL path.
@@ -360,8 +586,20 @@
   return url_path.substr(0, idx);
 }
 
+static std::string CanonPath(const GURL& url,
+                             const CookieMonster::ParsedCookie& pc) {
+  std::string path_string;
+  if (pc.HasPath())
+    path_string = pc.Path();
+  return CanonPathWithString(url, path_string);
+}
+
 static Time CanonExpiration(const CookieMonster::ParsedCookie& pc,
-                            const Time& current) {
+                            const Time& current,
+                            const CookieOptions& options) {
+  if (options.force_session())
+    return Time();
+
   // First, try the Max-Age attribute.
   uint64 max_age = 0;
   if (pc.HasMaxAge() &&
@@ -383,6 +621,8 @@
 }
 
 bool CookieMonster::HasCookieableScheme(const GURL& url) {
+  lock_.AssertAcquired();
+
   // Make sure the request is on a cookie-able url scheme.
   for (size_t i = 0; i < cookieable_schemes_.size(); ++i) {
     // We matched a scheme.
@@ -399,6 +639,11 @@
 
 void CookieMonster::SetCookieableSchemes(
     const char* schemes[], size_t num_schemes) {
+  AutoLock autolock(lock_);
+
+  // Cookieable Schemes must be set before first use of function.
+  DCHECK(!initialized_);
+
   cookieable_schemes_.clear();
   cookieable_schemes_.insert(cookieable_schemes_.end(),
                              schemes, schemes + num_schemes);
@@ -409,11 +654,12 @@
     const std::string& cookie_line,
     const Time& creation_time_or_null,
     const CookieOptions& options) {
+  AutoLock autolock(lock_);
+
   if (!HasCookieableScheme(url)) {
     return false;
   }
 
-  AutoLock autolock(lock_);
   InitIfNecessary();
 
   COOKIE_DLOG(INFO) << "SetCookie() line: " << cookie_line;
@@ -445,9 +691,10 @@
   std::string cookie_path = CanonPath(url, pc);
 
   scoped_ptr<CanonicalCookie> cc;
-  Time cookie_expires = CanonExpiration(pc, creation_time);
+  Time cookie_expires = CanonExpiration(pc, creation_time, options);
 
-  cc.reset(new CanonicalCookie(pc.Name(), pc.Value(), cookie_path,
+  cc.reset(new CanonicalCookie(pc.Name(), pc.Value(), cookie_domain,
+                               cookie_path,
                                pc.IsSecure(), pc.IsHttpOnly(),
                                creation_time, creation_time,
                                !cookie_expires.is_null(), cookie_expires));
@@ -456,20 +703,58 @@
     COOKIE_DLOG(WARNING) << "Failed to allocate CanonicalCookie";
     return false;
   }
+  return SetCanonicalCookie(&cc, cookie_domain, creation_time, options);
+}
 
-  if (DeleteAnyEquivalentCookie(cookie_domain,
-                                *cc,
+bool CookieMonster::SetCookieWithDetails(
+    const GURL& url, const std::string& name, const std::string& value,
+    const std::string& domain, const std::string& path,
+    const base::Time& expiration_time, bool secure, bool http_only) {
+
+  AutoLock autolock(lock_);
+
+  if (!HasCookieableScheme(url))
+    return false;
+
+  InitIfNecessary();
+
+  Time creation_time = CurrentTime();
+  last_time_seen_ = creation_time;
+
+  scoped_ptr<CanonicalCookie> cc;
+  cc.reset(CanonicalCookie::Create(
+      url, name, value, domain, path,
+      creation_time, expiration_time,
+      secure, http_only));
+
+  if (!cc.get())
+    return false;
+
+  CookieOptions options;
+  options.set_include_httponly();
+  return SetCanonicalCookie(&cc, cc->Domain(), creation_time, options);
+}
+
+bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc,
+                                       const std::string& cookie_domain,
+                                       const Time& creation_time,
+                                       const CookieOptions& options) {
+  if (DeleteAnyEquivalentCookie(cookie_domain, **cc,
                                 options.exclude_httponly())) {
     COOKIE_DLOG(INFO) << "SetCookie() not clobbering httponly cookie";
     return false;
   }
 
-  COOKIE_DLOG(INFO) << "SetCookie() cc: " << cc->DebugString();
+  COOKIE_DLOG(INFO) << "SetCookie() cc: " << (*cc)->DebugString();
 
   // Realize that we might be setting an expired cookie, and the only point
   // was to delete the cookie which we've already done.
-  if (!cc->IsExpired(creation_time))
-    InternalInsertCookie(cookie_domain, cc.release(), true);
+  if (!(*cc)->IsExpired(creation_time)) {
+    // See InitializeHistograms() for details.
+    histogram_expiration_duration_minutes_->Add(
+        ((*cc)->ExpiryDate() - creation_time).InMinutes());
+    InternalInsertCookie(cookie_domain, cc->release(), true);
+  }
 
   // We assume that hopefully setting a cookie will be less common than
   // querying a cookie.  Since setting a cookie can put us over our limits,
@@ -484,12 +769,18 @@
 void CookieMonster::InternalInsertCookie(const std::string& key,
                                          CanonicalCookie* cc,
                                          bool sync_to_store) {
+  lock_.AssertAcquired();
+
   if (cc->IsPersistent() && store_ && sync_to_store)
     store_->AddCookie(key, *cc);
   cookies_.insert(CookieMap::value_type(key, cc));
+  if (delegate_.get())
+    delegate_->OnCookieChanged(*cc, false);
 }
 
 void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc) {
+  lock_.AssertAcquired();
+
   // Based off the Mozilla code.  When a cookie has been accessed recently,
   // don't bother updating its access time again.  This reduces the number of
   // updates we do during pageload, which in turn reduces the chance our storage
@@ -498,17 +789,29 @@
   if ((current - cc->LastAccessDate()) < last_access_threshold_)
     return;
 
+  // See InitializeHistograms() for details.
+  histogram_between_access_interval_minutes_->Add(
+      (current - cc->LastAccessDate()).InMinutes());
+
   cc->SetLastAccessDate(current);
   if (cc->IsPersistent() && store_)
     store_->UpdateCookieAccessTime(*cc);
 }
 
 void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
-                                         bool sync_to_store) {
+                                         bool sync_to_store,
+                                         DeletionCause deletion_cause) {
+  lock_.AssertAcquired();
+
+  // See InitializeHistograms() for details.
+  histogram_cookie_deletion_cause_->Add(deletion_cause);
+
   CanonicalCookie* cc = it->second;
   COOKIE_DLOG(INFO) << "InternalDeleteCookie() cc: " << cc->DebugString();
   if (cc->IsPersistent() && store_ && sync_to_store)
     store_->DeleteCookie(*cc);
+  if (delegate_.get())
+    delegate_->OnCookieChanged(*cc, true);
   cookies_.erase(it);
   delete cc;
 }
@@ -516,6 +819,8 @@
 bool CookieMonster::DeleteAnyEquivalentCookie(const std::string& key,
                                               const CanonicalCookie& ecc,
                                               bool skip_httponly) {
+  lock_.AssertAcquired();
+
   bool found_equivalent_cookie = false;
   bool skipped_httponly = false;
   for (CookieMapItPair its = cookies_.equal_range(key);
@@ -527,19 +832,14 @@
     if (ecc.IsEquivalent(*cc)) {
       // We should never have more than one equivalent cookie, since they should
       // overwrite each other.
-      DCHECK(!found_equivalent_cookie) <<
+      CHECK(!found_equivalent_cookie) <<
           "Duplicate equivalent cookies found, cookie store is corrupted.";
       if (skip_httponly && cc->IsHttpOnly()) {
         skipped_httponly = true;
       } else {
-        InternalDeleteCookie(curit, true);
+        InternalDeleteCookie(curit, true, DELETE_COOKIE_OVERWRITE);
       }
       found_equivalent_cookie = true;
-#ifdef NDEBUG
-      // Speed optimization: No point looping through the rest of the cookies
-      // since we're only doing it as a consistency check.
-      break;
-#endif
     }
   }
   return skipped_httponly;
@@ -547,6 +847,8 @@
 
 int CookieMonster::GarbageCollect(const Time& current,
                                   const std::string& key) {
+  lock_.AssertAcquired();
+
   int num_deleted = 0;
 
   // Collect garbage for this key.
@@ -583,6 +885,8 @@
                                        const CookieMapItPair& itpair,
                                        size_t num_max,
                                        size_t num_purge) {
+  lock_.AssertAcquired();
+
   // First, delete anything that's expired.
   std::vector<CookieMap::iterator> cookie_its;
   int num_deleted = GarbageCollectExpired(current, itpair, &cookie_its);
@@ -596,8 +900,12 @@
 
     std::partial_sort(cookie_its.begin(), cookie_its.begin() + num_purge,
                       cookie_its.end(), LRUCookieSorter);
-    for (size_t i = 0; i < num_purge; ++i)
-      InternalDeleteCookie(cookie_its[i], true);
+    for (size_t i = 0; i < num_purge; ++i) {
+      // See InitializeHistograms() for details.
+      histogram_evicted_last_access_minutes_->Add(
+          (current - cookie_its[i]->second->LastAccessDate()).InMinutes());
+      InternalDeleteCookie(cookie_its[i], true, DELETE_COOKIE_EVICTED);
+    }
 
     num_deleted += num_purge;
   }
@@ -609,13 +917,15 @@
     const Time& current,
     const CookieMapItPair& itpair,
     std::vector<CookieMap::iterator>* cookie_its) {
+  lock_.AssertAcquired();
+
   int num_deleted = 0;
   for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) {
     CookieMap::iterator curit = it;
     ++it;
 
     if (curit->second->IsExpired(current)) {
-      InternalDeleteCookie(curit, true);
+      InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED);
       ++num_deleted;
     } else if (cookie_its) {
       cookie_its->push_back(curit);
@@ -633,7 +943,9 @@
   for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) {
     CookieMap::iterator curit = it;
     ++it;
-    InternalDeleteCookie(curit, sync_to_store);
+    InternalDeleteCookie(curit, sync_to_store,
+                         sync_to_store ? DELETE_COOKIE_EXPLICIT :
+                             DELETE_COOKIE_DONT_RECORD /* Destruction. */);
     ++num_deleted;
   }
 
@@ -654,7 +966,7 @@
 
     if (cc->CreationDate() >= delete_begin &&
         (delete_end.is_null() || cc->CreationDate() < delete_end)) {
-      InternalDeleteCookie(curit, sync_to_store);
+      InternalDeleteCookie(curit, sync_to_store, DELETE_COOKIE_EXPLICIT);
       ++num_deleted;
     }
   }
@@ -667,6 +979,28 @@
   return DeleteAllCreatedBetween(delete_begin, Time(), sync_to_store);
 }
 
+int CookieMonster::DeleteAllForHost(const GURL& url) {
+  AutoLock autolock(lock_);
+  InitIfNecessary();
+
+  if (!HasCookieableScheme(url))
+    return 0;
+
+  // We store host cookies in the store by their canonical host name;
+  // domain cookies are stored with a leading ".".  So this is a pretty
+  // simple lookup and per-cookie delete.
+  int num_deleted = 0;
+  for (CookieMapItPair its = cookies_.equal_range(url.host());
+       its.first != its.second;) {
+    CookieMap::iterator curit = its.first;
+    ++its.first;
+    num_deleted++;
+
+    InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT);
+  }
+  return num_deleted;
+}
+
 bool CookieMonster::DeleteCookie(const std::string& domain,
                                  const CanonicalCookie& cookie,
                                  bool sync_to_store) {
@@ -677,7 +1011,7 @@
        its.first != its.second; ++its.first) {
     // The creation date acts as our unique index...
     if (its.first->second->CreationDate() == cookie.CreationDate()) {
-      InternalDeleteCookie(its.first, sync_to_store);
+      InternalDeleteCookie(its.first, sync_to_store, DELETE_COOKIE_EXPLICIT);
       return true;
     }
   }
@@ -714,6 +1048,9 @@
 // should be fast and simple enough for now.
 std::string CookieMonster::GetCookiesWithOptions(const GURL& url,
                                                  const CookieOptions& options) {
+  AutoLock autolock(lock_);
+  InitIfNecessary();
+
   if (!HasCookieableScheme(url)) {
     return std::string();
   }
@@ -743,6 +1080,9 @@
 
 void CookieMonster::DeleteCookie(const GURL& url,
                                  const std::string& cookie_name) {
+  AutoLock autolock(lock_);
+  InitIfNecessary();
+
   if (!HasCookieableScheme(url))
     return;
 
@@ -765,8 +1105,9 @@
   for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end();) {
     CookieMap::iterator curit = it;
     ++it;
-    if (matching_cookies.find(curit->second) != matching_cookies.end())
-      InternalDeleteCookie(curit, true);
+    if (matching_cookies.find(curit->second) != matching_cookies.end()) {
+      InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPLICIT);
+    }
   }
 }
 
@@ -788,7 +1129,7 @@
 
   CookieList cookie_list;
   for (CookieMap::iterator it = cookies_.begin(); it != cookies_.end(); ++it)
-    cookie_list.push_back(CookieListPair(it->first, *it->second));
+    cookie_list.push_back(*it->second);
 
   return cookie_list;
 }
@@ -797,47 +1138,22 @@
   AutoLock autolock(lock_);
   InitIfNecessary();
 
-  // Do not return removed cookies.
-  GarbageCollectExpired(Time::Now(),
-                        CookieMapItPair(cookies_.begin(), cookies_.end()),
-                        NULL);
-
-  CookieList cookie_list;
-  if (!HasCookieableScheme(url))
-    return cookie_list;
-
-  bool secure = url.SchemeIsSecure();
-
-  // Query for the full host, For example: 'a.c.blah.com'.
-  std::string key(url.host());
-  FindRawCookies(key, secure, &cookie_list);
-
-  // See if we can search for domain cookies, i.e. if the host has a TLD + 1.
-  const std::string domain(GetEffectiveDomain(url.scheme(), key));
-  if (domain.empty())
-    return cookie_list;
-
-  // Use same logic as in FindCookiesForHostAndDomain.
-  DCHECK_LE(domain.length(), key.length());
-  DCHECK_EQ(0, key.compare(key.length() - domain.length(), domain.length(),
-                           domain));
-  for (key = "." + key; key.length() > domain.length(); ) {
-    FindRawCookies(key, secure, &cookie_list);
-    const size_t next_dot = key.find('.', 1);  // Skip over leading dot.
-    key.erase(0, next_dot);
-  }
-  return cookie_list;
+  return InternalGetAllCookiesForURL(url);
 }
 
 void CookieMonster::FindCookiesForHostAndDomain(
     const GURL& url,
     const CookieOptions& options,
     std::vector<CanonicalCookie*>* cookies) {
-  AutoLock autolock(lock_);
-  InitIfNecessary();
+  lock_.AssertAcquired();
 
   const Time current_time(CurrentTime());
 
+  // Probe to save statistics relatively frequently.  We do it here rather
+  // than in the set path as many websites won't set cookies, and we
+  // want to collect statistics whenever the browser's being used.
+  RecordPeriodicStats(current_time);
+
   // Query for the full host, For example: 'a.c.blah.com'.
   std::string key(url.host());
   FindCookiesForKey(key, url, options, current_time, cookies);
@@ -868,6 +1184,8 @@
     const CookieOptions& options,
     const Time& current,
     std::vector<CanonicalCookie*>* cookies) {
+  lock_.AssertAcquired();
+
   bool secure = url.SchemeIsSecure();
 
   for (CookieMapItPair its = cookies_.equal_range(key);
@@ -878,7 +1196,7 @@
 
     // If the cookie is expired, delete it.
     if (cc->IsExpired(current)) {
-      InternalDeleteCookie(curit, true);
+      InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED);
       continue;
     }
 
@@ -902,15 +1220,76 @@
 
 void CookieMonster::FindRawCookies(const std::string& key,
                                    bool include_secure,
+                                   const std::string& path,
                                    CookieList* list) {
+  lock_.AssertAcquired();
+
   for (CookieMapItPair its = cookies_.equal_range(key);
        its.first != its.second; ++its.first) {
     CanonicalCookie* cc = its.first->second;
-    if (include_secure || !cc->IsSecure())
-      list->push_back(CookieListPair(key, *cc));
+    if (!include_secure && cc->IsSecure())
+      continue;
+    if (!cc->IsOnPath(path))
+      continue;
+    list->push_back(*cc);
   }
 }
 
+CookieMonster::CookieList CookieMonster::InternalGetAllCookiesForURL(
+    const GURL& url) {
+  lock_.AssertAcquired();
+
+  // Do not return removed cookies.
+  GarbageCollectExpired(Time::Now(),
+                        CookieMapItPair(cookies_.begin(), cookies_.end()),
+                        NULL);
+
+  CookieList cookie_list;
+  if (!HasCookieableScheme(url))
+    return cookie_list;
+
+  bool secure = url.SchemeIsSecure();
+
+  // Query for the full host, For example: 'a.c.blah.com'.
+  std::string key(url.host());
+  FindRawCookies(key, secure, url.path(), &cookie_list);
+
+  // See if we can search for domain cookies, i.e. if the host has a TLD + 1.
+  const std::string domain(GetEffectiveDomain(url.scheme(), key));
+  if (domain.empty())
+    return cookie_list;
+
+  // Use same logic as in FindCookiesForHostAndDomain.
+  DCHECK_LE(domain.length(), key.length());
+  DCHECK_EQ(0, key.compare(key.length() - domain.length(), domain.length(),
+                           domain));
+  for (key = "." + key; key.length() > domain.length(); ) {
+    FindRawCookies(key, secure, url.path(), &cookie_list);
+    const size_t next_dot = key.find('.', 1);  // Skip over leading dot.
+    key.erase(0, next_dot);
+  }
+  return cookie_list;
+}
+
+// Test to see if stats should be recorded, and record them if so.
+// The goal here is to get sampling for the average browser-hour of
+// activity.  We won't take samples when the web isn't being surfed,
+// and when the web is being surfed, we'll take samples about every
+// kRecordStatisticsIntervalSeconds.
+// last_statistic_record_time_ is initialized to Now() rather than null
+// in the constructor so that we won't take statistics right after
+// startup, to avoid bias from browsers that are started but not used.
+void CookieMonster::RecordPeriodicStats(const base::Time& current_time) {
+  const base::TimeDelta kRecordStatisticsIntervalTime(
+      base::TimeDelta::FromSeconds(kRecordStatisticsIntervalSeconds));
+
+  if (current_time - last_statistic_record_time_ >
+      kRecordStatisticsIntervalTime) {
+    // See InitializeHistograms() for details.
+    histogram_count_->Add(cookies_.size());
+    last_statistic_record_time_ = current_time;
+  }
+}
 
 CookieMonster::ParsedCookie::ParsedCookie(const std::string& cookie_line)
     : is_valid_(false),
@@ -943,7 +1322,7 @@
 static inline bool SeekTo(std::string::const_iterator* it,
                           const std::string::const_iterator& end,
                           const char* chars) {
-  for (; *it != end && !CharIsA(**it, chars); ++(*it));
+  for (; *it != end && !CharIsA(**it, chars); ++(*it)) {}
   return *it == end;
 }
 // Seek the iterator to the first occurrence of a character not in |chars|.
@@ -951,72 +1330,157 @@
 static inline bool SeekPast(std::string::const_iterator* it,
                             const std::string::const_iterator& end,
                             const char* chars) {
-  for (; *it != end && CharIsA(**it, chars); ++(*it));
+  for (; *it != end && CharIsA(**it, chars); ++(*it)) {}
   return *it == end;
 }
 static inline bool SeekBackPast(std::string::const_iterator* it,
                                 const std::string::const_iterator& end,
                                 const char* chars) {
-  for (; *it != end && CharIsA(**it, chars); --(*it));
+  for (; *it != end && CharIsA(**it, chars); --(*it)) {}
   return *it == end;
 }
 
+const char CookieMonster::ParsedCookie::kTerminator[] = "\n\r\0";
+const int CookieMonster::ParsedCookie::kTerminatorLen =
+    sizeof(kTerminator) - 1;
+const char CookieMonster::ParsedCookie::kWhitespace[] = " \t";
+const char CookieMonster::ParsedCookie::kValueSeparator[] = ";";
+const char CookieMonster::ParsedCookie::kTokenSeparator[] = ";=";
+
+std::string::const_iterator CookieMonster::ParsedCookie::FindFirstTerminator(
+    const std::string& s) {
+  std::string::const_iterator end = s.end();
+  size_t term_pos =
+      s.find_first_of(std::string(kTerminator, kTerminatorLen));
+  if (term_pos != std::string::npos) {
+    // We found a character we should treat as an end of string.
+    end = s.begin() + term_pos;
+  }
+  return end;
+}
+
+bool CookieMonster::ParsedCookie::ParseToken(
+    std::string::const_iterator* it,
+    const std::string::const_iterator& end,
+    std::string::const_iterator* token_start,
+    std::string::const_iterator* token_end) {
+  DCHECK(it && token_start && token_end);
+  std::string::const_iterator token_real_end;
+
+  // Seek past any whitespace before the "token" (the name).
+  // token_start should point at the first character in the token
+  if (SeekPast(it, end, kWhitespace))
+    return false;  // No token, whitespace or empty.
+  *token_start = *it;
+
+  // Seek over the token, to the token separator.
+  // token_real_end should point at the token separator, i.e. '='.
+  // If it == end after the seek, we probably have a token-value.
+  SeekTo(it, end, kTokenSeparator);
+  token_real_end = *it;
+
+  // Ignore any whitespace between the token and the token separator.
+  // token_end should point after the last interesting token character,
+  // pointing at either whitespace, or at '=' (and equal to token_real_end).
+  if (*it != *token_start) {  // We could have an empty token name.
+    --(*it);  // Go back before the token separator.
+    // Skip over any whitespace to the first non-whitespace character.
+    SeekBackPast(it, *token_start, kWhitespace);
+    // Point after it.
+    ++(*it);
+  }
+  *token_end = *it;
+
+  // Seek us back to the end of the token.
+  *it = token_real_end;
+  return true;
+}
+
+void CookieMonster::ParsedCookie::ParseValue(
+    std::string::const_iterator* it,
+    const std::string::const_iterator& end,
+    std::string::const_iterator* value_start,
+    std::string::const_iterator* value_end) {
+  DCHECK(it && value_start && value_end);
+
+  // Seek past any whitespace that might in-between the token and value.
+  SeekPast(it, end, kWhitespace);
+  // value_start should point at the first character of the value.
+  *value_start = *it;
+
+  // It is unclear exactly how quoted string values should be handled.
+  // Major browsers do different things, for example, Firefox supports
+  // semicolons embedded in a quoted value, while IE does not.  Looking at
+  // the specs, RFC 2109 and 2965 allow for a quoted-string as the value.
+  // However, these specs were apparently written after browsers had
+  // implemented cookies, and they seem very distant from the reality of
+  // what is actually implemented and used on the web.  The original spec
+  // from Netscape is possibly what is closest to the cookies used today.
+  // This spec didn't have explicit support for double quoted strings, and
+  // states that ; is not allowed as part of a value.  We had originally
+  // implement the Firefox behavior (A="B;C"; -> A="B;C";).  However, since
+  // there is no standard that makes sense, we decided to follow the behavior
+  // of IE and Safari, which is closer to the original Netscape proposal.
+  // This means that A="B;C" -> A="B;.  This also makes the code much simpler
+  // and reduces the possibility for invalid cookies, where other browsers
+  // like Opera currently reject those invalid cookies (ex A="B" "C";).
+
+  // Just look for ';' to terminate ('=' allowed).
+  // We can hit the end, maybe they didn't terminate.
+  SeekTo(it, end, kValueSeparator);
+
+  // Will be pointed at the ; seperator or the end.
+  *value_end = *it;
+
+  // Ignore any unwanted whitespace after the value.
+  if (*value_end != *value_start) {  // Could have an empty value
+    --(*value_end);
+    SeekBackPast(value_end, *value_start, kWhitespace);
+    ++(*value_end);
+  }
+}
+
+std::string CookieMonster::ParsedCookie::ParseTokenString(
+    const std::string& token) {
+  std::string::const_iterator it = token.begin();
+  std::string::const_iterator end = FindFirstTerminator(token);
+
+  std::string::const_iterator token_start, token_end;
+  if (ParseToken(&it, end, &token_start, &token_end))
+    return std::string(token_start, token_end);
+  return std::string();
+}
+
+std::string CookieMonster::ParsedCookie::ParseValueString(
+    const std::string& value) {
+  std::string::const_iterator it = value.begin();
+  std::string::const_iterator end = FindFirstTerminator(value);
+
+  std::string::const_iterator value_start, value_end;
+  ParseValue(&it, end, &value_start, &value_end);
+  return std::string(value_start, value_end);
+}
+
 // Parse all token/value pairs and populate pairs_.
 void CookieMonster::ParsedCookie::ParseTokenValuePairs(
     const std::string& cookie_line) {
-  static const char kTerminator[]      = "\n\r\0";
-  static const int  kTerminatorLen     = sizeof(kTerminator) - 1;
-  static const char kWhitespace[]      = " \t";
-  static const char kValueSeparator[]  = ";";
-  static const char kTokenSeparator[]  = ";=";
-
   pairs_.clear();
 
   // Ok, here we go.  We should be expecting to be starting somewhere
   // before the cookie line, not including any header name...
   std::string::const_iterator start = cookie_line.begin();
-  std::string::const_iterator end = cookie_line.end();
   std::string::const_iterator it = start;
 
   // TODO Make sure we're stripping \r\n in the network code.  Then we
   // can log any unexpected terminators.
-  size_t term_pos =
-      cookie_line.find_first_of(std::string(kTerminator, kTerminatorLen));
-  if (term_pos != std::string::npos) {
-    // We found a character we should treat as an end of string.
-    end = start + term_pos;
-  }
+  std::string::const_iterator end = FindFirstTerminator(cookie_line);
 
   for (int pair_num = 0; pair_num < kMaxPairs && it != end; ++pair_num) {
     TokenValuePair pair;
-    std::string::const_iterator token_start, token_real_end, token_end;
 
-    // Seek past any whitespace before the "token" (the name).
-    // token_start should point at the first character in the token
-    if (SeekPast(&it, end, kWhitespace))
-      break;  // No token, whitespace or empty.
-    token_start = it;
-
-    // Seek over the token, to the token separator.
-    // token_real_end should point at the token separator, i.e. '='.
-    // If it == end after the seek, we probably have a token-value.
-    SeekTo(&it, end, kTokenSeparator);
-    token_real_end = it;
-
-    // Ignore any whitespace between the token and the token separator.
-    // token_end should point after the last interesting token character,
-    // pointing at either whitespace, or at '=' (and equal to token_real_end).
-    if (it != token_start) {  // We could have an empty token name.
-      --it;  // Go back before the token separator.
-      // Skip over any whitespace to the first non-whitespace character.
-      SeekBackPast(&it, token_start, kWhitespace);
-      // Point after it.
-      ++it;
-    }
-    token_end = it;
-
-    // Seek us back to the end of the token.
-    it = token_real_end;
+    std::string::const_iterator token_start, token_end;
+    if (!ParseToken(&it, end, &token_start, &token_end))
+      break;
 
     if (it == end || *it != '=') {
       // We have a token-value, we didn't have any token name.
@@ -1043,45 +1507,10 @@
 
     // OK, now try to parse a value.
     std::string::const_iterator value_start, value_end;
-
-    // Seek past any whitespace that might in-between the token and value.
-    SeekPast(&it, end, kWhitespace);
-    // value_start should point at the first character of the value.
-    value_start = it;
-
-    // It is unclear exactly how quoted string values should be handled.
-    // Major browsers do different things, for example, Firefox supports
-    // semicolons embedded in a quoted value, while IE does not.  Looking at
-    // the specs, RFC 2109 and 2965 allow for a quoted-string as the value.
-    // However, these specs were apparently written after browsers had
-    // implemented cookies, and they seem very distant from the reality of
-    // what is actually implemented and used on the web.  The original spec
-    // from Netscape is possibly what is closest to the cookies used today.
-    // This spec didn't have explicit support for double quoted strings, and
-    // states that ; is not allowed as part of a value.  We had originally
-    // implement the Firefox behavior (A="B;C"; -> A="B;C";).  However, since
-    // there is no standard that makes sense, we decided to follow the behavior
-    // of IE and Safari, which is closer to the original Netscape proposal.
-    // This means that A="B;C" -> A="B;.  This also makes the code much simpler
-    // and reduces the possibility for invalid cookies, where other browsers
-    // like Opera currently reject those invalid cookies (ex A="B" "C";).
-
-    // Just look for ';' to terminate ('=' allowed).
-    // We can hit the end, maybe they didn't terminate.
-    SeekTo(&it, end, kValueSeparator);
-
-    // Will be pointed at the ; seperator or the end.
-    value_end = it;
-
-    // Ignore any unwanted whitespace after the value.
-    if (value_end != value_start) {  // Could have an empty value
-      --value_end;
-      SeekBackPast(&value_end, value_start, kWhitespace);
-      ++value_end;
-    }
-
+    ParseValue(&it, end, &value_start, &value_end);
     // OK, we're finished with a Token/Value.
     pair.second = std::string(value_start, value_end);
+
     // From RFC2109: "Attributes (names) (attr) are case-insensitive."
     if (pair_num != 0)
       StringToLowerASCII(&pair.first);
@@ -1104,19 +1533,21 @@
 
   // We skip over the first token/value, the user supplied one.
   for (size_t i = 1; i < pairs_.size(); ++i) {
-    if (pairs_[i].first == kPathTokenName)
+    if (pairs_[i].first == kPathTokenName) {
       path_index_ = i;
-    else if (pairs_[i].first == kDomainTokenName)
+    } else if (pairs_[i].first == kDomainTokenName) {
       domain_index_ = i;
-    else if (pairs_[i].first == kExpiresTokenName)
+    } else if (pairs_[i].first == kExpiresTokenName) {
       expires_index_ = i;
-    else if (pairs_[i].first == kMaxAgeTokenName)
+    } else if (pairs_[i].first == kMaxAgeTokenName) {
       maxage_index_ = i;
-    else if (pairs_[i].first == kSecureTokenName)
+    } else if (pairs_[i].first == kSecureTokenName) {
       secure_index_ = i;
-    else if (pairs_[i].first == kHttpOnlyTokenName)
+    } else if (pairs_[i].first == kHttpOnlyTokenName) {
       httponly_index_ = i;
-    else { /* some attribute we don't know or don't care about. */ }
+    } else {
+      /* some attribute we don't know or don't care about. */
+    }
   }
 }
 
@@ -1135,6 +1566,77 @@
   return out;
 }
 
+CookieMonster::CanonicalCookie::CanonicalCookie(const GURL& url,
+                                                const ParsedCookie& pc)
+    : name_(pc.Name()),
+      value_(pc.Value()),
+      path_(CanonPath(url, pc)),
+      creation_date_(Time::Now()),
+      last_access_date_(Time()),
+      has_expires_(pc.HasExpires()),
+      secure_(pc.IsSecure()),
+      httponly_(pc.IsHttpOnly()) {
+  if (has_expires_)
+    expiry_date_ = CanonExpiration(pc, creation_date_, CookieOptions());
+
+  // Do the best we can with the domain.
+  std::string cookie_domain;
+  std::string domain_string;
+  if (pc.HasDomain()) {
+    domain_string = pc.Domain();
+  }
+  bool result
+      = GetCookieDomainKeyWithString(url, domain_string,
+                                     &cookie_domain);
+  // Caller is responsible for passing in good arguments.
+  DCHECK(result);
+  domain_ = cookie_domain;
+}
+
+CookieMonster::CanonicalCookie* CookieMonster::CanonicalCookie::Create(
+      const GURL& url, const std::string& name, const std::string& value,
+      const std::string& domain, const std::string& path,
+      const base::Time& creation_time, const base::Time& expiration_time,
+      bool secure, bool http_only) {
+  // Expect valid attribute tokens and values, as defined by the ParsedCookie
+  // logic, otherwise don't create the cookie.
+  std::string parsed_name = ParsedCookie::ParseTokenString(name);
+  if (parsed_name != name)
+    return NULL;
+  std::string parsed_value = ParsedCookie::ParseValueString(value);
+  if (parsed_value != value)
+    return NULL;
+
+  std::string parsed_domain = ParsedCookie::ParseValueString(domain);
+  if (parsed_domain != domain)
+    return NULL;
+  std::string cookie_domain;
+  if (!GetCookieDomainKeyWithString(url, parsed_domain, &cookie_domain))
+    return false;
+
+  std::string parsed_path = ParsedCookie::ParseValueString(path);
+  if (parsed_path != path)
+    return NULL;
+
+  std::string cookie_path = CanonPathWithString(url, parsed_path);
+  // Expect that the path was either not specified (empty), or is valid.
+  if (!parsed_path.empty() && cookie_path != parsed_path)
+    return NULL;
+  // Canonicalize path again to make sure it escapes characters as needed.
+  url_parse::Component path_component(0, cookie_path.length());
+  url_canon::RawCanonOutputT<char> canon_path;
+  url_parse::Component canon_path_component;
+  url_canon::CanonicalizePath(cookie_path.data(), path_component,
+                              &canon_path, &canon_path_component);
+  cookie_path = std::string(canon_path.data() + canon_path_component.begin,
+                            canon_path_component.len);
+
+  return new CanonicalCookie(parsed_name, parsed_value, cookie_domain,
+                             cookie_path, secure, http_only,
+                             creation_time, creation_time,
+                             !expiration_time.is_null(), expiration_time);
+}
+
 bool CookieMonster::CanonicalCookie::IsOnPath(
     const std::string& url_path) const {
 
@@ -1172,9 +1674,12 @@
 }
 
 std::string CookieMonster::CanonicalCookie::DebugString() const {
-  return StringPrintf("name: %s value: %s path: %s creation: %" PRId64,
-                      name_.c_str(), value_.c_str(), path_.c_str(),
+  return StringPrintf("name: %s value: %s domain: %s path: %s creation: %"
+                      PRId64,
+                      name_.c_str(), value_.c_str(),
+                      domain_.c_str(), path_.c_str(),
                       static_cast<int64>(creation_date_.ToTimeT()));
 }
 
 }  // namespace
+
diff --git a/net/base/cookie_monster.h b/net/base/cookie_monster.h
index 7578c46..623363f 100644
--- a/net/base/cookie_monster.h
+++ b/net/base/cookie_monster.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -13,7 +13,10 @@
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/histogram.h"
 #include "base/lock.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
 #include "base/time.h"
 #include "net/base/cookie_store.h"
 
@@ -33,8 +36,9 @@
 //  - Verify that our domain enforcement and non-dotted handling is correct
 class CookieMonster : public CookieStore {
  public:
-  class ParsedCookie;
   class CanonicalCookie;
+  class Delegate;
+  class ParsedCookie;
   class PersistentCookieStore;
 
   // NOTE(deanm):
@@ -47,24 +51,27 @@
   typedef std::multimap<std::string, CanonicalCookie*> CookieMap;
   typedef std::pair<CookieMap::iterator, CookieMap::iterator> CookieMapItPair;
   typedef std::pair<std::string, CanonicalCookie*> KeyedCanonicalCookie;
-  typedef std::pair<std::string, CanonicalCookie> CookieListPair;
-  typedef std::vector<CookieListPair> CookieList;
+  typedef std::vector<CanonicalCookie> CookieList;
 
-
-  CookieMonster();
-
-  // The store passed in should not have had Init() called on it yet. This class
-  // will take care of initializing it. The backing store is NOT owned by this
-  // class, but it must remain valid for the duration of the cookie monster's
-  // existence.
-  CookieMonster(PersistentCookieStore* store);
+  // The store passed in should not have had Init() called on it yet. This
+  // class will take care of initializing it. The backing store is NOT owned by
+  // this class, but it must remain valid for the duration of the cookie
+  // monster's existence. If |store| is NULL, then no backing store will be
+  // updated. If |delegate| is non-NULL, it will be notified on
+  // creation/deletion of cookies.
+  CookieMonster(PersistentCookieStore* store, Delegate* delegate);
 
 #ifdef UNIT_TEST
-  CookieMonster(int last_access_threshold_milliseconds)
+  CookieMonster(PersistentCookieStore* store,
+                Delegate* delegate,
+                int last_access_threshold_milliseconds)
       : initialized_(false),
-        store_(NULL),
+        store_(store),
         last_access_threshold_(base::TimeDelta::FromMilliseconds(
-            last_access_threshold_milliseconds)) {
+            last_access_threshold_milliseconds)),
+        delegate_(delegate),
+        last_statistic_record_time_(base::Time::Now()) {
+    InitializeHistograms();
     SetDefaultCookieableSchemes();
   }
 #endif
@@ -72,6 +79,10 @@
   // Parse the string with the cookie time (very forgivingly).
   static base::Time ParseCookieTime(const std::string& time_string);
 
+  // Returns true if a domain string represents a host-only cookie,
+  // i.e. it doesn't begin with a leading '.' character.
+  static bool DomainIsHostOnly(const std::string& domain_string);
+
   // CookieStore implementation.
   virtual bool SetCookieWithOptions(const GURL& url,
                                     const std::string& cookie_line,
@@ -81,6 +92,19 @@
   virtual void DeleteCookie(const GURL& url, const std::string& cookie_name);
   virtual CookieMonster* GetCookieMonster() { return this; }
 
+  // Sets a cookie given explicit user-provided cookie attributes. The cookie
+  // name, value, domain, etc. are each provided as separate strings. This
+  // function expects each attribute to be well-formed. It will check for
+  // disallowed characters (e.g. the ';' character is disallowed within the
+  // cookie value attribute) and will return false without setting the cookie
+  // if such characters are found.
+  bool SetCookieWithDetails(const GURL& url,
+                            const std::string& name,
+                            const std::string& value,
+                            const std::string& domain,
+                            const std::string& path,
+                            const base::Time& expiration_time,
+                            bool secure, bool http_only);
 
   // Exposed for unit testing.
   bool SetCookieWithCreationTimeAndOptions(const GURL& url,
@@ -99,8 +123,8 @@
   CookieList GetAllCookies();
 
   // Returns all the cookies, for use in management UI, etc. Filters results
-  // using given url scheme and host / domain. This does not mark the cookies
-  // as having been accessed.
+  // using given url scheme, host / domain and path. This does not mark the
+  // cookies as having been accessed.
   CookieList GetAllCookiesForURL(const GURL& url);
 
   // Delete all of the cookies.
@@ -114,6 +138,12 @@
   // one passed into the function via |delete_after|.
   int DeleteAllCreatedAfter(const base::Time& delete_begin, bool sync_to_store);
 
+  // Delete all cookies that match the host of the given URL
+  // regardless of path.  This includes all http_only and secure cookies,
+  // but does not include any domain cookies that may apply to this host.
+  // Returns the number of cookies deleted.
+  int DeleteAllForHost(const GURL& url);
+
   // Delete one specific cookie.
   bool DeleteCookie(const std::string& domain,
                     const CanonicalCookie& cookie,
@@ -122,6 +152,8 @@
   // Override the default list of schemes that are allowed to be set in
   // this cookie store.  Calling his overrides the value of
   // "enable_file_scheme_".
+  // If this this method is called, it must be called before first use of
+  // the instance (i.e. as part of the instance initialization process.)
   void SetCookieableSchemes(const char* schemes[], size_t num_schemes);
 
   // There are some unknowns about how to correctly handle file:// cookies,
@@ -150,6 +182,17 @@
   // Should only be called by InitIfNecessary().
   void InitStore();
 
+  // Checks that |cookies_| matches our invariants, and tries to repair any
+  // inconsistencies. (In other words, it does not have duplicate cookies).
+  void EnsureCookiesMapIsValid();
+
+  // Checks for any duplicate cookies for host |key|, which lie between
+  // |begin| and |end|. If any are found, all but the most recent are deleted.
+  // Returns the number of duplicate cookies that were deleted.
+  int TrimDuplicateCookiesForHost(const std::string& key,
+                                  CookieMap::iterator begin,
+                                  CookieMap::iterator end);
+
   void SetDefaultCookieableSchemes();
 
   void FindCookiesForHostAndDomain(const GURL& url,
@@ -164,8 +207,13 @@
 
   void FindRawCookies(const std::string& key,
                       bool include_secure,
+                      const std::string& path,
                       CookieList* list);
 
+  // Internal helper returning all cookies for a given URL. The caller is
+  // assumed to hold lock_ and having called InitIfNecessary().
+  CookieList InternalGetAllCookiesForURL(const GURL& url);
+
   // Delete any cookies that are equivalent to |ecc| (same path, key, etc).
   // If |skip_httponly| is true, httponly cookies will not be deleted.  The
   // return value with be true if |skip_httponly| skipped an httponly cookie.
@@ -178,9 +226,28 @@
                             CanonicalCookie* cc,
                             bool sync_to_store);
 
+  // Helper function that sets a canonical cookie, deleting equivalents and
+  // performing garbage collection.
+  bool SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc,
+                          const std::string& cookie_domain,
+                          const base::Time& creation_time,
+                          const CookieOptions& options);
+
   void InternalUpdateCookieAccessTime(CanonicalCookie* cc);
 
-  void InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store);
+  enum DeletionCause {
+    DELETE_COOKIE_EXPLICIT,
+    DELETE_COOKIE_OVERWRITE,
+    DELETE_COOKIE_EXPIRED,
+    DELETE_COOKIE_EVICTED,
+    DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE,
+    DELETE_COOKIE_DONT_RECORD,
+    DELETE_COOKIE_LAST_ENTRY = DELETE_COOKIE_DONT_RECORD
+  };
+
+  // |deletion_cause| argument is for collecting statistics.
+  void InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store,
+                            DeletionCause deletion_cause);
 
   // If the number of cookies for host |key|, or globally, are over preset
   // maximums, garbage collects, first for the host and then globally, as
@@ -212,6 +279,27 @@
 
   bool HasCookieableScheme(const GURL& url);
 
+  // Statistics support
+  // Record statistics every kRecordStatisticsIntervalSeconds of uptime.
+  static const int kRecordStatisticsIntervalSeconds = 10 * 60;
+
+  // This function should be called repeatedly, and will record
+  // statistics if a sufficient time period has passed.
+  void RecordPeriodicStats(const base::Time& current_time);
+
+  // Histogram variables; see CookieMonster::InitializeHistograms() in
+  // cookie_monster.cc for details.
+  scoped_refptr<Histogram> histogram_expiration_duration_minutes_;
+  scoped_refptr<Histogram> histogram_between_access_interval_minutes_;
+  scoped_refptr<Histogram> histogram_evicted_last_access_minutes_;
+  scoped_refptr<Histogram> histogram_count_;
+  scoped_refptr<Histogram> histogram_number_duplicate_db_cookies_;
+  scoped_refptr<Histogram> histogram_cookie_deletion_cause_;
+
+  // Initialize the above variables; should only be called from
+  // the constructor.
+  void InitializeHistograms();
+
   CookieMap cookies_;
 
   // Indicates whether the cookie store has been initialized. This happens
@@ -231,12 +319,125 @@
 
   std::vector<std::string> cookieable_schemes_;
 
+  scoped_refptr<Delegate> delegate_;
+
   // Lock for thread-safety
   Lock lock_;
 
+  base::Time last_statistic_record_time_;
+
   DISALLOW_COPY_AND_ASSIGN(CookieMonster);
 };
 
+class CookieMonster::CanonicalCookie {
+ public:
+
+  // These constructors do no validation or canonicalization of their inputs;
+  // the resulting CanonicalCookies should not be relied on to be canonical
+  // unless the caller has done appropriate validation and canonicalization
+  // themselves.
+  CanonicalCookie() { }
+  CanonicalCookie(const std::string& name,
+                  const std::string& value,
+                  const std::string& domain,
+                  const std::string& path,
+                  bool secure,
+                  bool httponly,
+                  const base::Time& creation,
+                  const base::Time& last_access,
+                  bool has_expires,
+                  const base::Time& expires)
+      : name_(name),
+        value_(value),
+        domain_(domain),
+        path_(path),
+        creation_date_(creation),
+        last_access_date_(last_access),
+        expiry_date_(expires),
+        has_expires_(has_expires),
+        secure_(secure),
+        httponly_(httponly) {
+  }
+
+  // This constructor does canonicalization but not validation.
+  // The result of this constructor should not be relied on in contexts
+  // in which pre-validation of the ParsedCookie has not been done.
+  CanonicalCookie(const GURL& url, const ParsedCookie& pc);
+
+  // Supports the default copy constructor.
+
+  // Creates a canonical cookie from unparsed attribute values.
+  // Canonicalizes and validates inputs.  May return NULL if an attribute
+  // value is invalid.
+  static CanonicalCookie* Create(
+      const GURL& url, const std::string& name, const std::string& value,
+      const std::string& domain, const std::string& path,
+      const base::Time& creation_time, const base::Time& expiration_time,
+      bool secure, bool http_only);
+
+  const std::string& Name() const { return name_; }
+  const std::string& Value() const { return value_; }
+  const std::string& Domain() const { return domain_; }
+  const std::string& Path() const { return path_; }
+  const base::Time& CreationDate() const { return creation_date_; }
+  const base::Time& LastAccessDate() const { return last_access_date_; }
+  bool DoesExpire() const { return has_expires_; }
+  bool IsPersistent() const { return DoesExpire(); }
+  const base::Time& ExpiryDate() const { return expiry_date_; }
+  bool IsSecure() const { return secure_; }
+  bool IsHttpOnly() const { return httponly_; }
+
+  bool IsExpired(const base::Time& current) {
+    return has_expires_ && current >= expiry_date_;
+  }
+
+  // Are the cookies considered equivalent in the eyes of RFC 2965.
+  // The RFC says that name must match (case-sensitive), domain must
+  // match (case insensitive), and path must match (case sensitive).
+  // For the case insensitive domain compare, we rely on the domain
+  // having been canonicalized (in
+  // GetCookieDomainKeyWithString->CanonicalizeHost).
+  bool IsEquivalent(const CanonicalCookie& ecc) const {
+    // It seems like it would make sense to take secure and httponly into
+    // account, but the RFC doesn't specify this.
+    // NOTE: Keep this logic in-sync with TrimDuplicateCookiesForHost().
+    return (name_ == ecc.Name() && domain_ == ecc.Domain()
+            && path_ == ecc.Path());
+  }
+
+  void SetLastAccessDate(const base::Time& date) {
+    last_access_date_ = date;
+  }
+
+  bool IsOnPath(const std::string& url_path) const;
+
+  std::string DebugString() const;
+ private:
+  std::string name_;
+  std::string value_;
+  std::string domain_;
+  std::string path_;
+  base::Time creation_date_;
+  base::Time last_access_date_;
+  base::Time expiry_date_;
+  bool has_expires_;
+  bool secure_;
+  bool httponly_;
+};
+
+class CookieMonster::Delegate
+    : public base::RefCountedThreadSafe<CookieMonster::Delegate> {
+ public:
+  // Will be called when a cookie is added or removed. The function is passed
+  // the respective |cookie| which was added to or removed from the cookies.
+  // If |removed| is true, the cookie was deleted.
+  virtual void OnCookieChanged(const CookieMonster::CanonicalCookie& cookie,
+                               bool removed) = 0;
+ protected:
+  friend class base::RefCountedThreadSafe<CookieMonster::Delegate>;
+  virtual ~Delegate() {}
+};
+
 class CookieMonster::ParsedCookie {
  public:
   typedef std::pair<std::string, std::string> TokenValuePair;
@@ -269,14 +470,48 @@
   bool IsSecure() const { return secure_index_ != 0; }
   bool IsHttpOnly() const { return httponly_index_ != 0; }
 
-  // Return the number of attributes, for example, returning 2 for:
+  // Returns the number of attributes, for example, returning 2 for:
   //   "BLAH=hah; path=/; domain=.google.com"
   size_t NumberOfAttributes() const { return pairs_.size() - 1; }
 
   // For debugging only!
   std::string DebugString() const;
 
+  // Returns an iterator pointing to the first terminator character found in
+  // the given string.
+  static std::string::const_iterator FindFirstTerminator(const std::string& s);
+
+  // Given iterators pointing to the beginning and end of a string segment,
+  // returns as output arguments token_start and token_end to the start and end
+  // positions of a cookie attribute token name parsed from the segment, and
+  // updates the segment iterator to point to the next segment to be parsed.
+  // If no token is found, the function returns false.
+  static bool ParseToken(std::string::const_iterator* it,
+                         const std::string::const_iterator& end,
+                         std::string::const_iterator* token_start,
+                         std::string::const_iterator* token_end);
+
+  // Given iterators pointing to the beginning and end of a string segment,
+  // returns as output arguments value_start and value_end to the start and end
+  // positions of a cookie attribute value parsed from the segment, and updates
+  // the segment iterator to point to the next segment to be parsed.
+  static void ParseValue(std::string::const_iterator* it,
+                         const std::string::const_iterator& end,
+                         std::string::const_iterator* value_start,
+                         std::string::const_iterator* value_end);
+
+  // Same as the above functions, except the input is assumed to contain the
+  // desired token/value and nothing else.
+  static std::string ParseTokenString(const std::string& token);
+  static std::string ParseValueString(const std::string& value);
+
  private:
+  static const char kTerminator[];
+  static const int  kTerminatorLen;
+  static const char kWhitespace[];
+  static const char kValueSeparator[];
+  static const char kTokenSeparator[];
+
   void ParseTokenValuePairs(const std::string& cookie_line);
   void SetupAttributes();
 
@@ -296,74 +531,6 @@
   DISALLOW_COPY_AND_ASSIGN(ParsedCookie);
 };
 
-
-class CookieMonster::CanonicalCookie {
- public:
-  CanonicalCookie() { }
-  CanonicalCookie(const std::string& name,
-                  const std::string& value,
-                  const std::string& path,
-                  bool secure,
-                  bool httponly,
-                  const base::Time& creation,
-                  const base::Time& last_access,
-                  bool has_expires,
-                  const base::Time& expires)
-      : name_(name),
-        value_(value),
-        path_(path),
-        creation_date_(creation),
-        last_access_date_(last_access),
-        expiry_date_(expires),
-        has_expires_(has_expires),
-        secure_(secure),
-        httponly_(httponly) {
-  }
-
-  // Supports the default copy constructor.
-
-  const std::string& Name() const { return name_; }
-  const std::string& Value() const { return value_; }
-  const std::string& Path() const { return path_; }
-  const base::Time& CreationDate() const { return creation_date_; }
-  const base::Time& LastAccessDate() const { return last_access_date_; }
-  bool DoesExpire() const { return has_expires_; }
-  bool IsPersistent() const { return DoesExpire(); }
-  const base::Time& ExpiryDate() const { return expiry_date_; }
-  bool IsSecure() const { return secure_; }
-  bool IsHttpOnly() const { return httponly_; }
-
-  bool IsExpired(const base::Time& current) {
-    return has_expires_ && current >= expiry_date_;
-  }
-
-  // Are the cookies considered equivalent in the eyes of the RFC.
-  // This says that the domain and path should string match identically.
-  bool IsEquivalent(const CanonicalCookie& ecc) const {
-    // It seems like it would make sense to take secure and httponly into
-    // account, but the RFC doesn't specify this.
-    return name_ == ecc.Name() && path_ == ecc.Path();
-  }
-
-  void SetLastAccessDate(const base::Time& date) {
-    last_access_date_ = date;
-  }
-
-  bool IsOnPath(const std::string& url_path) const;
-
-  std::string DebugString() const;
- private:
-  std::string name_;
-  std::string value_;
-  std::string path_;
-  base::Time creation_date_;
-  base::Time last_access_date_;
-  base::Time expiry_date_;
-  bool has_expires_;
-  bool secure_;
-  bool httponly_;
-};
-
 typedef base::RefCountedThreadSafe<CookieMonster::PersistentCookieStore>
     RefcountedPersistentCookieStore;
 
diff --git a/net/base/cookie_monster_perftest.cc b/net/base/cookie_monster_perftest.cc
index 9e76ac8..3d1e7a7 100644
--- a/net/base/cookie_monster_perftest.cc
+++ b/net/base/cookie_monster_perftest.cc
@@ -40,7 +40,7 @@
 static const GURL kUrlGoogle("http://www.google.izzle");
 
 TEST(CookieMonsterTest, TestAddCookiesOnSingleHost) {
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   std::vector<std::string> cookies;
   for (int i = 0; i < kNumCookies; i++) {
     cookies.push_back(StringPrintf("a%03d=b", i));
@@ -67,7 +67,7 @@
 }
 
 TEST(CookieMonsterTest, TestAddCookieOnManyHosts) {
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   std::string cookie(kCookieLine);
   std::vector<GURL> gurls;  // just wanna have ffffuunnn
   for (int i = 0; i < kNumCookies; ++i) {
@@ -93,3 +93,71 @@
   cm->DeleteAll(false);
   timer3.Done();
 }
+
+TEST(CookieMonsterTest, TestDomainTree) {
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
+  std::string cookie(kCookieLine);
+  std::string domain_base("top.com");
+
+  std::vector<GURL> gurl_list;
+
+  // Create a balanced binary tree of domains on which the cookie is set.
+  for (int i1=0; i1 < 2; i1++) {
+    std::string domain_base_1((i1 ? "a." : "b.") + domain_base);
+    gurl_list.push_back(GURL(StringPrintf("http://%s",
+                                          domain_base_1.c_str())));
+    for (int i2=0; i2 < 2; i2++) {
+      std::string domain_base_2((i1 ? "a." : "b.") + domain_base_1);
+      gurl_list.push_back(GURL(StringPrintf("http://%s",
+                                            domain_base_2.c_str())));
+      for (int i3=0; i3 < 2; i3++) {
+        std::string domain_base_3((i1 ? "a." : "b.") + domain_base_2);
+        gurl_list.push_back(GURL(StringPrintf("http://%s",
+                                              domain_base_3.c_str())));
+        for (int i4=0; i4 < 2; i4++) {
+          gurl_list.push_back(GURL(StringPrintf("http://%s.%s",
+                                                i4 ? "a" : "b",
+                                                domain_base_3.c_str())));
+        }
+      }
+    }
+  }
+
+  for (std::vector<GURL>::const_iterator it = gurl_list.begin();
+       it != gurl_list.end(); it++) {
+    EXPECT_TRUE(cm->SetCookie(*it, cookie));
+  }
+
+  GURL probe_gurl("http://b.a.b.a.top.com/");
+  PerfTimeLogger timer("Cookie_monster_query_domain_tree");
+  for (int i = 0; i < kNumCookies; i++) {
+    cm->GetCookies(probe_gurl);
+  }
+  timer.Done();
+
+  cm->DeleteAll(false);
+  gurl_list.clear();
+
+  // Create a line of 32 domain cookies such that all cookies stored
+  // by effective TLD+1 will apply to probe GURL.
+  // (TLD + 1 is the level above .com/org/net/etc, e.g. "top.com"
+  // or "google.com".  "Effective" is added to include sites like
+  // bbc.co.uk, where the effetive TLD+1 is more than one level
+  // below the top level.)
+  gurl_list.push_back(GURL("http://a.top.com"));
+  gurl_list.push_back(GURL("http://b.a.top.com"));
+  gurl_list.push_back(GURL("http://a.b.a.top.com"));
+  gurl_list.push_back(GURL("http://b.a.b.a.top.com"));
+
+  for (int i = 0; i < 8; i++) {
+    for (std::vector<GURL>::const_iterator it = gurl_list.begin();
+         it != gurl_list.end(); it++) {
+      EXPECT_TRUE(cm->SetCookie(*it, StringPrintf("a%03d=b", i)));
+    }
+  }
+  PerfTimeLogger timer2("Cookie_monster_query_domain_line");
+  for (int i = 0; i < kNumCookies; i++) {
+    cm->GetCookies(probe_gurl);
+  }
+  timer2.Done();
+}
diff --git a/net/base/cookie_monster_unittest.cc b/net/base/cookie_monster_unittest.cc
index cdbf869..c61a782 100644
--- a/net/base/cookie_monster_unittest.cc
+++ b/net/base/cookie_monster_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/basictypes.h"
 #include "base/platform_thread.h"
 #include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
 #include "base/string_util.h"
 #include "base/time.h"
 #include "googleurl/src/gurl.h"
@@ -19,10 +20,233 @@
 using base::TimeDelta;
 
 namespace {
-  class ParsedCookieTest : public testing::Test { };
-  class CookieMonsterTest : public testing::Test { };
+
+class ParsedCookieTest : public testing::Test { };
+class CookieMonsterTest : public testing::Test { };
+
+// Describes a call to one of the 3 functions of PersistentCookieStore.
+struct CookieStoreCommand {
+  enum Type {
+    ADD,
+    UPDATE_ACCESS_TIME,
+    REMOVE,
+  };
+
+  CookieStoreCommand(Type type,
+                     const std::string& key,
+                     const net::CookieMonster::CanonicalCookie& cookie)
+      : type(type), key(key), cookie(cookie) {}
+
+  Type type;
+  std::string key;  // Only applicable to the ADD command.
+  net::CookieMonster::CanonicalCookie cookie;
+};
+
+// Implementation of PersistentCookieStore that captures the
+// received commands and saves them to a list.
+// The result of calls to Load() can be configured using SetLoadExpectation().
+class MockPersistentCookieStore
+    : public net::CookieMonster::PersistentCookieStore {
+ public:
+  typedef std::vector<CookieStoreCommand> CommandList;
+
+  MockPersistentCookieStore() : load_return_value_(true) {
+  }
+
+  virtual bool Load(
+      std::vector<net::CookieMonster::KeyedCanonicalCookie>* out_cookies) {
+    bool ok = load_return_value_;
+    if (ok)
+      *out_cookies = load_result_;
+    return ok;
+  }
+
+  virtual void AddCookie(const std::string& key,
+                         const net::CookieMonster::CanonicalCookie& cookie) {
+    commands_.push_back(
+        CookieStoreCommand(CookieStoreCommand::ADD, key, cookie));
+  }
+
+  virtual void UpdateCookieAccessTime(
+      const net::CookieMonster::CanonicalCookie& cookie) {
+    commands_.push_back(CookieStoreCommand(
+        CookieStoreCommand::UPDATE_ACCESS_TIME, std::string(), cookie));
+  }
+
+  virtual void DeleteCookie(
+      const net::CookieMonster::CanonicalCookie& cookie) {
+    commands_.push_back(
+        CookieStoreCommand(CookieStoreCommand::REMOVE, std::string(), cookie));
+  }
+
+  void SetLoadExpectation(
+      bool return_value,
+      const std::vector<net::CookieMonster::KeyedCanonicalCookie>& result) {
+    load_return_value_ = return_value;
+    load_result_ = result;
+  }
+
+  const CommandList& commands() const {
+    return commands_;
+  }
+
+ private:
+  CommandList commands_;
+
+  // Deferred result to use when Load() is called.
+  bool load_return_value_;
+  std::vector<net::CookieMonster::KeyedCanonicalCookie> load_result_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockPersistentCookieStore);
+};
+
+// Mock for CookieMonster::Delegate
+class MockCookieMonsterDelegate : public net::CookieMonster::Delegate {
+ public:
+  typedef std::pair<net::CookieMonster::CanonicalCookie, bool>
+      CookieNotification;
+
+  MockCookieMonsterDelegate() {}
+
+  virtual void OnCookieChanged(
+      const net::CookieMonster::CanonicalCookie& cookie,
+      bool removed) {
+    CookieNotification notification(cookie, removed);
+    changes_.push_back(notification);
+  }
+
+  const std::vector<CookieNotification>& changes() const { return changes_; }
+
+  void reset() { changes_.clear(); }
+
+ private:
+  virtual ~MockCookieMonsterDelegate() {}
+
+  std::vector<CookieNotification> changes_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockCookieMonsterDelegate);
+};
+
+// Helper to build a list of KeyedCanonicalCookies.
+void AddKeyedCookieToList(
+    const std::string& key,
+    const std::string& cookie_line,
+    const Time& creation_time,
+    std::vector<net::CookieMonster::KeyedCanonicalCookie>* out_list) {
+
+  // Parse the cookie line.
+  net::CookieMonster::ParsedCookie pc(cookie_line);
+  EXPECT_TRUE(pc.IsValid());
+
+  // This helper is simplistic in interpreting a parsed cookie, in order to
+  // avoid duplicated CookieMonster's CanonPath() and CanonExpiration()
+  // functions. Would be nice to export them, and re-use here.
+  EXPECT_FALSE(pc.HasMaxAge());
+  EXPECT_TRUE(pc.HasPath());
+  Time cookie_expires = pc.HasExpires() ?
+      net::CookieMonster::ParseCookieTime(pc.Expires()) : Time();
+  std::string cookie_path = pc.Path();
+
+  scoped_ptr<net::CookieMonster::CanonicalCookie> cookie(
+      new net::CookieMonster::CanonicalCookie(
+          pc.Name(), pc.Value(), key, cookie_path,
+          pc.IsSecure(), pc.IsHttpOnly(),
+          creation_time, creation_time,
+          !cookie_expires.is_null(),
+          cookie_expires));
+
+  out_list->push_back(
+      net::CookieMonster::KeyedCanonicalCookie(
+          key, cookie.release()));
 }
 
+// Helper for DeleteAllForHost test; repopulates CM with same layout
+// each time.
+const char* kTopLevelDomainPlus1 = "http://www.harvard.edu";
+const char* kTopLevelDomainPlus2 = "http://www.math.harvard.edu";
+const char* kTopLevelDomainPlus2Secure = "https://www.math.harvard.edu";
+const char* kTopLevelDomainPlus3 =
+    "http://www.bourbaki.math.harvard.edu";
+const char* kOtherDomain = "http://www.mit.edu";
+
+void PopulateCmForDeleteAllForHost(scoped_refptr<net::CookieMonster> cm) {
+  GURL url_top_level_domain_plus_1(kTopLevelDomainPlus1);
+  GURL url_top_level_domain_plus_2(kTopLevelDomainPlus2);
+  GURL url_top_level_domain_plus_2_secure(kTopLevelDomainPlus2Secure);
+  GURL url_top_level_domain_plus_3(kTopLevelDomainPlus3);
+  GURL url_other(kOtherDomain);
+
+  cm->DeleteAll(true);
+
+  // Static population for probe:
+  //    * Three levels of domain cookie (.b.a, .c.b.a, .d.c.b.a)
+  //    * Three levels of host cookie (w.b.a, w.c.b.a, w.d.c.b.a)
+  //    * http_only cookie (w.c.b.a)
+  //    * Two secure cookies (.c.b.a, w.c.b.a)
+  //    * Two domain path cookies (.c.b.a/dir1, .c.b.a/dir1/dir2)
+  //    * Two host path cookies (w.c.b.a/dir1, w.c.b.a/dir1/dir2)
+
+  // Domain cookies
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_1,
+                                       "dom_1", "X", ".harvard.edu", "/",
+                                       base::Time(), false, false));
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_2,
+                                       "dom_2", "X", ".math.harvard.edu", "/",
+                                       base::Time(), false, false));
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_3,
+                                       "dom_3", "X",
+                                       ".bourbaki.math.harvard.edu", "/",
+                                       base::Time(), false, false));
+
+  // Host cookies
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_1,
+                                       "host_1", "X", "", "/",
+                                       base::Time(), false, false));
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_2,
+                                       "host_2", "X", "", "/",
+                                       base::Time(), false, false));
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_3,
+                                       "host_3", "X", "", "/",
+                                       base::Time(), false, false));
+
+  // Http_only cookie
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_2,
+                                       "httpo_check", "X", "", "/",
+                                       base::Time(), false, true));
+
+  // Secure cookies
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_2_secure,
+                                       "sec_dom", "X", ".math.harvard.edu",
+                                       "/", base::Time(), true, false));
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_2_secure,
+                                       "sec_host", "X", "", "/",
+                                       base::Time(), true, false));
+
+  // Domain path cookies
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_2,
+                                       "dom_path_1", "X",
+                                       ".math.harvard.edu", "/dir1",
+                                       base::Time(), false, false));
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_2,
+                                       "dom_path_2", "X",
+                                       ".math.harvard.edu", "/dir1/dir2",
+                                       base::Time(), false, false));
+
+  // Host path cookies
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_2,
+                                       "host_path_1", "X",
+                                       "", "/dir1",
+                                       base::Time(), false, false));
+  EXPECT_TRUE(cm->SetCookieWithDetails(url_top_level_domain_plus_2,
+                                       "host_path_2", "X",
+                                       "", "/dir1/dir2",
+                                       base::Time(), false, false));
+
+  EXPECT_EQ(13U, cm->GetAllCookies().size());
+}
+
+}  // namespace
+
 
 TEST(ParsedCookieTest, TestBasic) {
   net::CookieMonster::ParsedCookie pc("a=b");
@@ -237,6 +461,33 @@
   EXPECT_EQ("BB", pc3.Value());
 }
 
+TEST(ParsedCookieTest, ParseTokensAndValues) {
+  EXPECT_EQ("hello",
+            net::CookieMonster::ParsedCookie::ParseTokenString(
+                "hello\nworld"));
+  EXPECT_EQ("fs!!@",
+            net::CookieMonster::ParsedCookie::ParseTokenString(
+                "fs!!@;helloworld"));
+  EXPECT_EQ("hello world\tgood",
+            net::CookieMonster::ParsedCookie::ParseTokenString(
+                "hello world\tgood\rbye"));
+  EXPECT_EQ("A",
+            net::CookieMonster::ParsedCookie::ParseTokenString(
+                "A=B=C;D=E"));
+  EXPECT_EQ("hello",
+            net::CookieMonster::ParsedCookie::ParseValueString(
+                "hello\nworld"));
+  EXPECT_EQ("fs!!@",
+            net::CookieMonster::ParsedCookie::ParseValueString(
+                "fs!!@;helloworld"));
+  EXPECT_EQ("hello world\tgood",
+            net::CookieMonster::ParsedCookie::ParseValueString(
+                "hello world\tgood\rbye"));
+  EXPECT_EQ("A=B=C",
+            net::CookieMonster::ParsedCookie::ParseValueString(
+                "A=B=C;D=E"));
+}
+
 static const char kUrlGoogle[] = "http://www.google.izzle";
 static const char kUrlGoogleSecure[] = "https://www.google.izzle";
 static const char kUrlFtp[] = "ftp://ftp.google.izzle/";
@@ -246,7 +497,9 @@
 TEST(CookieMonsterTest, DomainTest) {
   GURL url_google(kUrlGoogle);
 
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<MockPersistentCookieStore> store(
+      new MockPersistentCookieStore);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, NULL));
   EXPECT_TRUE(cm->SetCookie(url_google, "A=B"));
   EXPECT_EQ("A=B", cm->GetCookies(url_google));
   EXPECT_TRUE(cm->SetCookie(url_google, "C=D; domain=.google.izzle"));
@@ -272,23 +525,33 @@
   EXPECT_EQ("C=D; E=F; G=H",
             cm->GetCookies(GURL("http://bla.www.google.izzle")));
   EXPECT_EQ("A=B; C=D; E=F; G=H", cm->GetCookies(url_google));
+
+  // Nothing was persisted to the backing store.
+  EXPECT_EQ(0u, store->commands().size());
 }
 
 // FireFox recognizes domains containing trailing periods as valid.
 // IE and Safari do not. Assert the expected policy here.
 TEST(CookieMonsterTest, DomainWithTrailingDotTest) {
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<MockPersistentCookieStore> store(
+      new MockPersistentCookieStore);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, NULL));
   GURL url_google("http://www.google.com");
 
   EXPECT_FALSE(cm->SetCookie(url_google, "a=1; domain=.www.google.com."));
   EXPECT_FALSE(cm->SetCookie(url_google, "b=2; domain=.www.google.com.."));
   EXPECT_EQ("", cm->GetCookies(url_google));
+
+  // Nothing was persisted to the backing store.
+  EXPECT_EQ(0u, store->commands().size());
 }
 
 // Test that cookies can bet set on higher level domains.
 // http://b/issue?id=896491
 TEST(CookieMonsterTest, ValidSubdomainTest) {
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<MockPersistentCookieStore> store(
+      new MockPersistentCookieStore);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, NULL));
   GURL url_abcd("http://a.b.c.d.com");
   GURL url_bcd("http://b.c.d.com");
   GURL url_cd("http://c.d.com");
@@ -309,6 +572,9 @@
   EXPECT_TRUE(cm->SetCookie(url_bcd, "X=cd; domain=.c.d.com"));
   EXPECT_EQ("b=2; c=3; d=4; X=bcd; X=cd", cm->GetCookies(url_bcd));
   EXPECT_EQ("c=3; d=4; X=cd", cm->GetCookies(url_cd));
+
+  // Nothing was persisted to the backing store.
+  EXPECT_EQ(0u, store->commands().size());
 }
 
 // Test that setting a cookie which specifies an invalid domain has
@@ -317,7 +583,10 @@
 // http://b/issue?id=896472
 TEST(CookieMonsterTest, InvalidDomainTest) {
   {
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<MockPersistentCookieStore> store(
+        new MockPersistentCookieStore);
+
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, NULL));
     GURL url_foobar("http://foo.bar.com");
 
     // More specific sub-domain than allowed.
@@ -348,13 +617,16 @@
     EXPECT_FALSE(cm->SetCookie(url_foobar, "o=15; domain=.foo.bar.com#sup"));
 
     EXPECT_EQ("", cm->GetCookies(url_foobar));
+
+    // Nothing was persisted to the backing store.
+    EXPECT_EQ(0u, store->commands().size());
   }
 
   {
     // Make sure the cookie code hasn't gotten its subdomain string handling
     // reversed, missed a suffix check, etc.  It's important here that the two
     // hosts below have the same domain + registry.
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     GURL url_foocom("http://foo.com.com");
     EXPECT_FALSE(cm->SetCookie(url_foocom, "a=1; domain=.foo.com.com.com"));
     EXPECT_EQ("", cm->GetCookies(url_foocom));
@@ -366,7 +638,7 @@
 // http://b/issue?id=889898
 TEST(CookieMonsterTest, DomainWithoutLeadingDotTest) {
   {  // The omission of dot results in setting a domain cookie.
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     GURL url_hosted("http://manage.hosted.filefront.com");
     GURL url_filefront("http://www.filefront.com");
     EXPECT_TRUE(cm->SetCookie(url_hosted, "sawAd=1; domain=filefront.com"));
@@ -375,7 +647,7 @@
   }
 
   {  // Even when the domains match exactly, don't consider it host cookie.
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     GURL url("http://www.google.com");
     EXPECT_TRUE(cm->SetCookie(url, "a=1; domain=www.google.com"));
     EXPECT_EQ("a=1", cm->GetCookies(url));
@@ -387,7 +659,7 @@
 // Test that the domain specified in cookie string is treated case-insensitive
 // http://b/issue?id=896475.
 TEST(CookieMonsterTest, CaseInsensitiveDomainTest) {
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   GURL url_google("http://www.google.com");
   EXPECT_TRUE(cm->SetCookie(url_google, "a=1; domain=.GOOGLE.COM"));
   EXPECT_TRUE(cm->SetCookie(url_google, "b=2; domain=.wWw.gOOgLE.coM"));
@@ -397,13 +669,13 @@
 TEST(CookieMonsterTest, TestIpAddress) {
   GURL url_ip("http://1.2.3.4/weee");
   {
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     EXPECT_TRUE(cm->SetCookie(url_ip, kValidCookieLine));
     EXPECT_EQ("A=B", cm->GetCookies(url_ip));
   }
 
   {  // IP addresses should not be able to set domain cookies.
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     EXPECT_FALSE(cm->SetCookie(url_ip, "b=2; domain=.1.2.3.4"));
     EXPECT_FALSE(cm->SetCookie(url_ip, "c=3; domain=.3.4"));
     EXPECT_EQ("", cm->GetCookies(url_ip));
@@ -419,7 +691,7 @@
 // Test host cookies, and setting of cookies on TLD.
 TEST(CookieMonsterTest, TestNonDottedAndTLD) {
   {
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     GURL url("http://com/");
     // Allow setting on "com", (but only as a host cookie).
     EXPECT_TRUE(cm->SetCookie(url, "a=1"));
@@ -433,7 +705,7 @@
   }
 
   {  // http://com. should be treated the same as http://com.
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     GURL url("http://com./index.html");
     EXPECT_TRUE(cm->SetCookie(url, "a=1"));
     EXPECT_EQ("a=1", cm->GetCookies(url));
@@ -441,7 +713,7 @@
   }
 
   {  // Should not be able to set host cookie from a subdomain.
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     GURL url("http://a.b");
     EXPECT_FALSE(cm->SetCookie(url, "a=1; domain=.b"));
     EXPECT_FALSE(cm->SetCookie(url, "b=2; domain=b"));
@@ -449,7 +721,7 @@
   }
 
   {  // Same test as above, but explicitly on a known TLD (com).
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     GURL url("http://google.com");
     EXPECT_FALSE(cm->SetCookie(url, "a=1; domain=.com"));
     EXPECT_FALSE(cm->SetCookie(url, "b=2; domain=com"));
@@ -457,7 +729,7 @@
   }
 
   {  // Make sure can't set cookie on TLD which is dotted.
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     GURL url("http://google.co.uk");
     EXPECT_FALSE(cm->SetCookie(url, "a=1; domain=.co.uk"));
     EXPECT_FALSE(cm->SetCookie(url, "b=2; domain=.uk"));
@@ -467,7 +739,7 @@
   }
 
   {  // Intranet URLs should only be able to set host cookies.
-    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+    scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
     GURL url("http://b");
     EXPECT_TRUE(cm->SetCookie(url, "a=1"));
     EXPECT_FALSE(cm->SetCookie(url, "b=2; domain=.b"));
@@ -479,7 +751,7 @@
 // Test reading/writing cookies when the domain ends with a period,
 // as in "www.google.com."
 TEST(CookieMonsterTest, TestHostEndsWithDot) {
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   GURL url("http://www.google.com");
   GURL url_with_dot("http://www.google.com.");
   EXPECT_TRUE(cm->SetCookie(url, "a=1"));
@@ -499,19 +771,19 @@
 }
 
 TEST(CookieMonsterTest, InvalidScheme) {
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   EXPECT_FALSE(cm->SetCookie(GURL(kUrlFtp), kValidCookieLine));
 }
 
 TEST(CookieMonsterTest, InvalidScheme_Read) {
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   EXPECT_TRUE(cm->SetCookie(GURL(kUrlGoogle), kValidDomainCookieLine));
   EXPECT_EQ("", cm->GetCookies(GURL(kUrlFtp)));
 }
 
 TEST(CookieMonsterTest, PathTest) {
   std::string url("http://www.google.izzle");
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   EXPECT_TRUE(cm->SetCookie(GURL(url), "A=B; path=/wee"));
   EXPECT_EQ("A=B", cm->GetCookies(GURL(url + "/wee")));
   EXPECT_EQ("A=B", cm->GetCookies(GURL(url + "/wee/")));
@@ -528,7 +800,7 @@
 
 TEST(CookieMonsterTest, HttpOnlyTest) {
   GURL url_google(kUrlGoogle);
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   net::CookieOptions options;
   options.set_include_httponly();
 
@@ -562,6 +834,11 @@
   const time_t epoch;
 };
 
+struct DomainIsHostOnlyCase {
+  const char* str;
+  const bool is_host_only;
+};
+
 }  // namespace
 
 TEST(CookieMonsterTest, TestCookieDateParsing) {
@@ -648,9 +925,24 @@
   }
 }
 
+TEST(CookieMonsterTest, TestDomainIsHostOnly) {
+  const DomainIsHostOnlyCase tests[] = {
+    { "",               true },
+    { "www.google.com", true },
+    { ".google.com",    false }
+  };
+
+  for (size_t i = 0; i < arraysize(tests); ++i) {
+    EXPECT_EQ(tests[i].is_host_only,
+              net::CookieMonster::DomainIsHostOnly(tests[i].str));
+  }
+}
+
 TEST(CookieMonsterTest, TestCookieDeletion) {
   GURL url_google(kUrlGoogle);
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<MockPersistentCookieStore> store(
+      new MockPersistentCookieStore);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, NULL));
 
   // Create a session cookie.
   EXPECT_TRUE(cm->SetCookie(url_google, kValidCookieLine));
@@ -673,38 +965,53 @@
   EXPECT_TRUE(cm->SetCookie(url_google,
                            std::string(kValidCookieLine) +
                            "; expires=Mon, 18-Apr-22 22:50:13 GMT"));
+  ASSERT_EQ(1u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[0].type);
+
   EXPECT_EQ("A=B", cm->GetCookies(url_google));
   // Delete it via Max-Age.
   EXPECT_TRUE(cm->SetCookie(url_google,
                            std::string(kValidCookieLine) + "; max-age=0"));
+  ASSERT_EQ(2u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[1].type);
   EXPECT_EQ("", cm->GetCookies(url_google));
 
   // Create a persistent cookie.
   EXPECT_TRUE(cm->SetCookie(url_google,
                            std::string(kValidCookieLine) +
                            "; expires=Mon, 18-Apr-22 22:50:13 GMT"));
+  ASSERT_EQ(3u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[2].type);
   EXPECT_EQ("A=B", cm->GetCookies(url_google));
   // Delete it via Expires.
   EXPECT_TRUE(cm->SetCookie(url_google,
                            std::string(kValidCookieLine) +
                            "; expires=Mon, 18-Apr-1977 22:50:13 GMT"));
+  ASSERT_EQ(4u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[3].type);
   EXPECT_EQ("", cm->GetCookies(url_google));
 
   // Create a persistent cookie.
   EXPECT_TRUE(cm->SetCookie(url_google,
                            std::string(kValidCookieLine) +
                            "; expires=Mon, 18-Apr-22 22:50:13 GMT"));
+  ASSERT_EQ(5u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[4].type);
   EXPECT_EQ("A=B", cm->GetCookies(url_google));
   // Delete it via Expires, with a unix epoch of 0.
   EXPECT_TRUE(cm->SetCookie(url_google,
                            std::string(kValidCookieLine) +
                            "; expires=Thu, 1-Jan-1970 00:00:00 GMT"));
+  ASSERT_EQ(6u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[5].type);
   EXPECT_EQ("", cm->GetCookies(url_google));
 }
 
 TEST(CookieMonsterTest, TestCookieDeleteAll) {
   GURL url_google(kUrlGoogle);
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<MockPersistentCookieStore> store(
+      new MockPersistentCookieStore);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, NULL));
   net::CookieOptions options;
   options.set_include_httponly();
 
@@ -716,11 +1023,26 @@
 
   EXPECT_EQ(2, cm->DeleteAll(false));
   EXPECT_EQ("", cm->GetCookiesWithOptions(url_google, options));
+
+  EXPECT_EQ(0u, store->commands().size());
+
+  // Create a persistent cookie.
+  EXPECT_TRUE(cm->SetCookie(url_google,
+                            std::string(kValidCookieLine) +
+                            "; expires=Mon, 18-Apr-22 22:50:13 GMT"));
+  ASSERT_EQ(1u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[0].type);
+
+  EXPECT_EQ(1, cm->DeleteAll(true));  // sync_to_store = true.
+  ASSERT_EQ(2u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[1].type);
+
+  EXPECT_EQ("", cm->GetCookiesWithOptions(url_google, options));
 }
 
 TEST(CookieMonsterTest, TestCookieDeleteAllCreatedAfterTimestamp) {
   GURL url_google(kUrlGoogle);
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   Time now = Time::Now();
 
   // Nothing has been added so nothing should be deleted.
@@ -748,7 +1070,7 @@
 
 TEST(CookieMonsterTest, TestCookieDeleteAllCreatedBetweenTimestamps) {
   GURL url_google(kUrlGoogle);
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   Time now = Time::Now();
 
   // Nothing has been added so nothing should be deleted.
@@ -791,7 +1113,7 @@
 TEST(CookieMonsterTest, TestSecure) {
   GURL url_google(kUrlGoogle);
   GURL url_google_secure(kUrlGoogleSecure);
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
 
   EXPECT_TRUE(cm->SetCookie(url_google, "A=B"));
   EXPECT_EQ("A=B", cm->GetCookies(url_google));
@@ -814,7 +1136,7 @@
 
 static Time GetFirstCookieAccessDate(net::CookieMonster* cm) {
   const net::CookieMonster::CookieList all_cookies(cm->GetAllCookies());
-  return all_cookies.front().second.LastAccessDate();
+  return all_cookies.front().LastAccessDate();
 }
 
 static const int kLastAccessThresholdMilliseconds = 200;
@@ -822,7 +1144,7 @@
 TEST(CookieMonsterTest, TestLastAccess) {
   GURL url_google(kUrlGoogle);
   scoped_refptr<net::CookieMonster> cm(
-      new net::CookieMonster(kLastAccessThresholdMilliseconds));
+      new net::CookieMonster(NULL, NULL, kLastAccessThresholdMilliseconds));
 
   EXPECT_TRUE(cm->SetCookie(url_google, "A=B"));
   const Time last_access_date(GetFirstCookieAccessDate(cm));
@@ -850,7 +1172,7 @@
 
 TEST(CookieMonsterTest, TestHostGarbageCollection) {
   GURL url_google(kUrlGoogle);
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   // Add a bunch of cookies on a single host, should purge them.
   for (int i = 0; i < 101; i++) {
     std::string cookie = StringPrintf("a%03d=b", i);
@@ -865,7 +1187,7 @@
 
 TEST(CookieMonsterTest, TestTotalGarbageCollection) {
   scoped_refptr<net::CookieMonster> cm(
-      new net::CookieMonster(kLastAccessThresholdMilliseconds));
+      new net::CookieMonster(NULL, NULL, kLastAccessThresholdMilliseconds));
 
   // Add a bunch of cookies on a bunch of host, some should get purged.
   const GURL sticky_cookie("http://a0000.izzle");
@@ -900,7 +1222,7 @@
 TEST(CookieMonsterTest, NetUtilCookieTest) {
   const GURL test_url("http://mojo.jojo.google.izzle/");
 
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
 
   EXPECT_TRUE(cm->SetCookie(test_url, "foo=bar"));
   std::string value = cm->GetCookies(test_url);
@@ -922,15 +1244,15 @@
   net::CookieMonster::CookieList cookies = cm->GetAllCookies();
   for (net::CookieMonster::CookieList::iterator it = cookies.begin();
        it != cookies.end(); ++it)
-    if (it->first == domain && it->second.Name() == name)
-      return cm->DeleteCookie(domain, it->second, false);
+    if (it->Domain() == domain && it->Name() == name)
+      return cm->DeleteCookie(domain, *it, false);
   return false;
 }
 
 TEST(CookieMonsterTest, TestDeleteSingleCookie) {
   GURL url_google(kUrlGoogle);
 
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
 
   EXPECT_TRUE(cm->SetCookie(url_google, "A=B"));
   EXPECT_TRUE(cm->SetCookie(url_google, "C=D"));
@@ -945,8 +1267,8 @@
 }
 
 TEST(CookieMonsterTest, SetCookieableSchemes) {
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
-  scoped_refptr<net::CookieMonster> cm_foo(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
+  scoped_refptr<net::CookieMonster> cm_foo(new net::CookieMonster(NULL, NULL));
 
   // Only cm_foo should allow foo:// cookies.
   const char* kSchemes[] = {"foo"};
@@ -962,12 +1284,11 @@
 }
 
 TEST(CookieMonsterTest, GetAllCookiesForURL) {
-
   GURL url_google(kUrlGoogle);
   GURL url_google_secure(kUrlGoogleSecure);
 
   scoped_refptr<net::CookieMonster> cm(
-      new net::CookieMonster(kLastAccessThresholdMilliseconds));
+      new net::CookieMonster(NULL, NULL, kLastAccessThresholdMilliseconds));
 
   // Create an httponly cookie.
   net::CookieOptions options;
@@ -980,49 +1301,96 @@
   EXPECT_TRUE(cm->SetCookieWithOptions(url_google_secure,
                                        "E=F; domain=.google.izzle; secure",
                                        options));
+
   const Time last_access_date(GetFirstCookieAccessDate(cm));
 
   PlatformThread::Sleep(kLastAccessThresholdMilliseconds + 20);
 
-  // Check raw cookies.
-  net::CookieMonster::CookieList raw_cookies =
+  // Check cookies for url.
+  net::CookieMonster::CookieList cookies =
       cm->GetAllCookiesForURL(url_google);
-  net::CookieMonster::CookieList::iterator it = raw_cookies.begin();
+  net::CookieMonster::CookieList::iterator it = cookies.begin();
 
-  ASSERT_TRUE(it != raw_cookies.end());
-  EXPECT_EQ("www.google.izzle", it->first);
-  EXPECT_EQ("A", it->second.Name());
+  ASSERT_TRUE(it != cookies.end());
+  EXPECT_EQ("www.google.izzle", it->Domain());
+  EXPECT_EQ("A", it->Name());
 
-  ASSERT_TRUE(++it != raw_cookies.end());
-  EXPECT_EQ(".google.izzle", it->first);
-  EXPECT_EQ("C", it->second.Name());
+  ASSERT_TRUE(++it != cookies.end());
+  EXPECT_EQ(".google.izzle", it->Domain());
+  EXPECT_EQ("C", it->Name());
 
-  ASSERT_TRUE(++it == raw_cookies.end());
+  ASSERT_TRUE(++it == cookies.end());
 
   // Test secure cookies.
-  raw_cookies = cm->GetAllCookiesForURL(url_google_secure);
-  it = raw_cookies.begin();
+  cookies = cm->GetAllCookiesForURL(url_google_secure);
+  it = cookies.begin();
 
-  ASSERT_TRUE(it != raw_cookies.end());
-  EXPECT_EQ("www.google.izzle", it->first);
-  EXPECT_EQ("A", it->second.Name());
+  ASSERT_TRUE(it != cookies.end());
+  EXPECT_EQ("www.google.izzle", it->Domain());
+  EXPECT_EQ("A", it->Name());
 
-  ASSERT_TRUE(++it != raw_cookies.end());
-  EXPECT_EQ(".google.izzle", it->first);
-  EXPECT_EQ("C", it->second.Name());
+  ASSERT_TRUE(++it != cookies.end());
+  EXPECT_EQ(".google.izzle", it->Domain());
+  EXPECT_EQ("C", it->Name());
 
-  ASSERT_TRUE(++it != raw_cookies.end());
-  EXPECT_EQ(".google.izzle", it->first);
-  EXPECT_EQ("E", it->second.Name());
+  ASSERT_TRUE(++it != cookies.end());
+  EXPECT_EQ(".google.izzle", it->Domain());
+  EXPECT_EQ("E", it->Name());
 
-  ASSERT_TRUE(++it == raw_cookies.end());
+  ASSERT_TRUE(++it == cookies.end());
 
   // Reading after a short wait should not update the access date.
-  EXPECT_TRUE (last_access_date == GetFirstCookieAccessDate(cm));
+  EXPECT_TRUE(last_access_date == GetFirstCookieAccessDate(cm));
+}
+
+TEST(CookieMonsterTest, GetAllCookiesForURLPathMatching) {
+  GURL url_google(kUrlGoogle);
+  GURL url_google_foo("http://www.google.izzle/foo");
+  GURL url_google_bar("http://www.google.izzle/bar");
+
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
+  net::CookieOptions options;
+
+  EXPECT_TRUE(cm->SetCookieWithOptions(url_google_foo,
+                                       "A=B; path=/foo;",
+                                       options));
+  EXPECT_TRUE(cm->SetCookieWithOptions(url_google_bar,
+                                       "C=D; path=/bar;",
+                                       options));
+  EXPECT_TRUE(cm->SetCookieWithOptions(url_google,
+                                       "E=F;",
+                                       options));
+
+  net::CookieMonster::CookieList cookies =
+      cm->GetAllCookiesForURL(url_google_foo);
+  net::CookieMonster::CookieList::iterator it = cookies.begin();
+
+  ASSERT_TRUE(it != cookies.end());
+  EXPECT_EQ("A", it->Name());
+  EXPECT_EQ("/foo", it->Path());
+
+  ASSERT_TRUE(++it != cookies.end());
+  EXPECT_EQ("E", it->Name());
+  EXPECT_EQ("/", it->Path());
+
+  ASSERT_TRUE(++it == cookies.end());
+
+  cookies = cm->GetAllCookiesForURL(url_google_bar);
+  it = cookies.begin();
+
+  ASSERT_TRUE(it != cookies.end());
+  EXPECT_EQ("C", it->Name());
+  EXPECT_EQ("/bar", it->Path());
+
+  ASSERT_TRUE(++it != cookies.end());
+  EXPECT_EQ("E", it->Name());
+  EXPECT_EQ("/", it->Path());
+
+  ASSERT_TRUE(++it == cookies.end());
 }
 
 TEST(CookieMonsterTest, DeleteCookieByName) {
-  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
   GURL url_google(kUrlGoogle);
 
   EXPECT_TRUE(cm->SetCookie(url_google, "A=A1; path=/"));
@@ -1039,9 +1407,375 @@
   EXPECT_EQ(expected_size, cookies.size());
   for (net::CookieMonster::CookieList::iterator it = cookies.begin();
        it != cookies.end(); ++it) {
-    EXPECT_NE("A1", it->second.Value());
-    EXPECT_NE("A2", it->second.Value());
+    EXPECT_NE("A1", it->Value());
+    EXPECT_NE("A2", it->Value());
   }
 }
 
-// TODO test overwrite cookie
+// Test that overwriting persistent cookies deletes the old one from the
+// backing store.
+TEST(CookieMonsterTest, OverwritePersistentCookie) {
+  GURL url_google("http://www.google.com/");
+  GURL url_chromium("http://chromium.org");
+  scoped_refptr<MockPersistentCookieStore> store(
+      new MockPersistentCookieStore);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, NULL));
+
+  // Insert a cookie "a" for path "/path1"
+  EXPECT_TRUE(
+      cm->SetCookie(url_google, "a=val1; path=/path1; "
+                                "expires=Mon, 18-Apr-22 22:50:13 GMT"));
+  ASSERT_EQ(1u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[0].type);
+
+  // Insert a cookie "b" for path "/path1"
+  EXPECT_TRUE(
+      cm->SetCookie(url_google, "b=val1; path=/path1; "
+                                "expires=Mon, 18-Apr-22 22:50:14 GMT"));
+  ASSERT_EQ(2u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[1].type);
+
+  // Insert a cookie "b" for path "/path1", that is httponly. This should
+  // overwrite the non-http-only version.
+  net::CookieOptions allow_httponly;
+  allow_httponly.set_include_httponly();
+  EXPECT_TRUE(
+    cm->SetCookieWithOptions(url_google,
+                             "b=val2; path=/path1; httponly; "
+                             "expires=Mon, 18-Apr-22 22:50:14 GMT",
+                             allow_httponly));
+  ASSERT_EQ(4u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[2].type);
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[3].type);
+
+  // Insert a cookie "a" for path "/path1". This should overwrite.
+  EXPECT_TRUE(cm->SetCookie(url_google,
+                            "a=val33; path=/path1; "
+                            "expires=Mon, 18-Apr-22 22:50:14 GMT"));
+  ASSERT_EQ(6u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[4].type);
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[5].type);
+
+  // Insert a cookie "a" for path "/path2". This should NOT overwrite
+  // cookie "a", since the path is different.
+  EXPECT_TRUE(cm->SetCookie(url_google,
+                            "a=val9; path=/path2; "
+                            "expires=Mon, 18-Apr-22 22:50:14 GMT"));
+  ASSERT_EQ(7u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[6].type);
+
+  // Insert a cookie "a" for path "/path1", but this time for "chromium.org".
+  // Although the name and path match, the hostnames do not, so shouldn't
+  // overwrite.
+  EXPECT_TRUE(cm->SetCookie(url_chromium,
+                            "a=val99; path=/path1; "
+                            "expires=Mon, 18-Apr-22 22:50:14 GMT"));
+  ASSERT_EQ(8u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[7].type);
+
+  EXPECT_EQ("a=val33", cm->GetCookies(GURL("http://www.google.com/path1")));
+  EXPECT_EQ("a=val9", cm->GetCookies(GURL("http://www.google.com/path2")));
+  EXPECT_EQ("a=val99", cm->GetCookies(GURL("http://chromium.org/path1")));
+}
+
+// Tests importing from a persistent cookie store that contains duplicate
+// equivalent cookies. This situation should be handled by removing the
+// duplicate cookie (both from the in-memory cache, and from the backing store).
+//
+// This is a regression test for: http://crbug.com/17855.
+TEST(CookieMonsterTest, DontImportDuplicateCookies) {
+  GURL url_google("http://www.google.com/");
+
+  scoped_refptr<MockPersistentCookieStore> store(
+      new MockPersistentCookieStore);
+
+  // We will fill some initial cookies into the PersistentCookieStore,
+  // to simulate a database with 4 duplicates.
+  std::vector<net::CookieMonster::KeyedCanonicalCookie> initial_cookies;
+
+  // Insert 4 cookies with name "X" on path "/", with varying creation
+  // dates. We expect only the most recent one to be preserved following
+  // the import.
+
+  AddKeyedCookieToList("www.google.com",
+                       "X=1; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
+                       Time::Now() + TimeDelta::FromDays(3),
+                       &initial_cookies);
+
+  AddKeyedCookieToList("www.google.com",
+                       "X=2; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
+                       Time::Now() + TimeDelta::FromDays(1),
+                       &initial_cookies);
+
+  // ===> This one is the WINNER (biggest creation time).  <====
+  AddKeyedCookieToList("www.google.com",
+                       "X=3; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
+                       Time::Now() + TimeDelta::FromDays(4),
+                       &initial_cookies);
+
+  AddKeyedCookieToList("www.google.com",
+                       "X=4; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
+                       Time::Now(),
+                       &initial_cookies);
+
+  // Insert 2 cookies with name "X" on path "/2", with varying creation
+  // dates. We expect only the most recent one to be preserved the import.
+
+  // ===> This one is the WINNER (biggest creation time).  <====
+  AddKeyedCookieToList("www.google.com",
+                       "X=a1; path=/2; expires=Mon, 18-Apr-22 22:50:14 GMT",
+                       Time::Now() + TimeDelta::FromDays(9),
+                       &initial_cookies);
+
+  AddKeyedCookieToList("www.google.com",
+                       "X=a2; path=/2; expires=Mon, 18-Apr-22 22:50:14 GMT",
+                       Time::Now() + TimeDelta::FromDays(1),
+                       &initial_cookies);
+
+  // Insert 1 cookie with name "Y" on path "/".
+  AddKeyedCookieToList("www.google.com",
+                       "Y=a; path=/; expires=Mon, 18-Apr-22 22:50:14 GMT",
+                       Time::Now() + TimeDelta::FromDays(9),
+                       &initial_cookies);
+
+  // Inject our initial cookies into the mock PersistentCookieStore.
+  store->SetLoadExpectation(true, initial_cookies);
+
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, NULL));
+
+  // Verify that duplicates were not imported for path "/".
+  // (If this had failed, GetCookies() would have also returned X=1, X=2, X=4).
+  EXPECT_EQ("X=3; Y=a", cm->GetCookies(GURL("http://www.google.com/")));
+
+  // Verify that same-named cookie on a different path ("/x2") didn't get
+  // messed up.
+  EXPECT_EQ("X=a1; X=3; Y=a",
+            cm->GetCookies(GURL("http://www.google.com/2/x")));
+
+  // Verify that the PersistentCookieStore was told to kill its 4 duplicates.
+  ASSERT_EQ(4u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[0].type);
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[1].type);
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[2].type);
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[3].type);
+}
+
+TEST(CookieMonsterTest, Delegate) {
+  GURL url_google(kUrlGoogle);
+
+  scoped_refptr<MockPersistentCookieStore> store(
+      new MockPersistentCookieStore);
+  scoped_refptr<MockCookieMonsterDelegate> delegate(
+      new MockCookieMonsterDelegate);
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(store, delegate));
+
+  EXPECT_TRUE(cm->SetCookie(url_google, "A=B"));
+  EXPECT_TRUE(cm->SetCookie(url_google, "C=D"));
+  EXPECT_TRUE(cm->SetCookie(url_google, "E=F"));
+  EXPECT_EQ("A=B; C=D; E=F", cm->GetCookies(url_google));
+  ASSERT_EQ(3u, delegate->changes().size());
+  EXPECT_EQ(false, delegate->changes()[0].second);
+  EXPECT_EQ(url_google.host(), delegate->changes()[0].first.Domain());
+  EXPECT_EQ("A", delegate->changes()[0].first.Name());
+  EXPECT_EQ("B", delegate->changes()[0].first.Value());
+  EXPECT_EQ(url_google.host(), delegate->changes()[1].first.Domain());
+  EXPECT_EQ(false, delegate->changes()[1].second);
+  EXPECT_EQ("C", delegate->changes()[1].first.Name());
+  EXPECT_EQ("D", delegate->changes()[1].first.Value());
+  EXPECT_EQ(url_google.host(), delegate->changes()[2].first.Domain());
+  EXPECT_EQ(false, delegate->changes()[2].second);
+  EXPECT_EQ("E", delegate->changes()[2].first.Name());
+  EXPECT_EQ("F", delegate->changes()[2].first.Value());
+  delegate->reset();
+
+  EXPECT_TRUE(FindAndDeleteCookie(cm, url_google.host(), "C"));
+  EXPECT_EQ("A=B; E=F", cm->GetCookies(url_google));
+  ASSERT_EQ(1u, delegate->changes().size());
+  EXPECT_EQ(url_google.host(), delegate->changes()[0].first.Domain());
+  EXPECT_EQ(true, delegate->changes()[0].second);
+  EXPECT_EQ("C", delegate->changes()[0].first.Name());
+  EXPECT_EQ("D", delegate->changes()[0].first.Value());
+  delegate->reset();
+
+  EXPECT_FALSE(FindAndDeleteCookie(cm, "random.host", "E"));
+  EXPECT_EQ("A=B; E=F", cm->GetCookies(url_google));
+  EXPECT_EQ(0u, delegate->changes().size());
+
+  // Insert a cookie "a" for path "/path1"
+  EXPECT_TRUE(
+      cm->SetCookie(url_google, "a=val1; path=/path1; "
+                                "expires=Mon, 18-Apr-22 22:50:13 GMT"));
+  ASSERT_EQ(1u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[0].type);
+  ASSERT_EQ(1u, delegate->changes().size());
+  EXPECT_EQ(false, delegate->changes()[0].second);
+  EXPECT_EQ(url_google.host(), delegate->changes()[0].first.Domain());
+  EXPECT_EQ("a", delegate->changes()[0].first.Name());
+  EXPECT_EQ("val1", delegate->changes()[0].first.Value());
+  delegate->reset();
+
+  // Insert a cookie "a" for path "/path1", that is httponly. This should
+  // overwrite the non-http-only version.
+  net::CookieOptions allow_httponly;
+  allow_httponly.set_include_httponly();
+  EXPECT_TRUE(
+    cm->SetCookieWithOptions(url_google,
+                             "a=val2; path=/path1; httponly; "
+                             "expires=Mon, 18-Apr-22 22:50:14 GMT",
+                             allow_httponly));
+  ASSERT_EQ(3u, store->commands().size());
+  EXPECT_EQ(CookieStoreCommand::REMOVE, store->commands()[1].type);
+  EXPECT_EQ(CookieStoreCommand::ADD, store->commands()[2].type);
+  ASSERT_EQ(2u, delegate->changes().size());
+  EXPECT_EQ(url_google.host(), delegate->changes()[0].first.Domain());
+  EXPECT_EQ(true, delegate->changes()[0].second);
+  EXPECT_EQ("a", delegate->changes()[0].first.Name());
+  EXPECT_EQ("val1", delegate->changes()[0].first.Value());
+  EXPECT_EQ(url_google.host(), delegate->changes()[1].first.Domain());
+  EXPECT_EQ(false, delegate->changes()[1].second);
+  EXPECT_EQ("a", delegate->changes()[1].first.Name());
+  EXPECT_EQ("val2", delegate->changes()[1].first.Value());
+  delegate->reset();
+}
+
+TEST(CookieMonsterTest, SetCookieWithDetails) {
+  GURL url_google(kUrlGoogle);
+  GURL url_google_foo("http://www.google.izzle/foo");
+  GURL url_google_bar("http://www.google.izzle/bar");
+  GURL url_google_secure(kUrlGoogleSecure);
+
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
+
+  EXPECT_TRUE(cm->SetCookieWithDetails(
+      url_google_foo, "A", "B", std::string(), "/foo", base::Time(),
+      false, false));
+  EXPECT_TRUE(cm->SetCookieWithDetails(
+      url_google_bar, "C", "D", "google.izzle", "/bar", base::Time(),
+      false, true));
+  EXPECT_TRUE(cm->SetCookieWithDetails(
+      url_google, "E", "F", std::string(), std::string(), base::Time(),
+      true, false));
+
+  // Test that malformed attributes fail to set the cookie.
+  EXPECT_FALSE(cm->SetCookieWithDetails(
+      url_google_foo, " A", "B", std::string(), "/foo", base::Time(),
+      false, false));
+  EXPECT_FALSE(cm->SetCookieWithDetails(
+      url_google_foo, "A;", "B", std::string(), "/foo", base::Time(),
+      false, false));
+  EXPECT_FALSE(cm->SetCookieWithDetails(
+      url_google_foo, "A=", "B", std::string(), "/foo", base::Time(),
+      false, false));
+  EXPECT_FALSE(cm->SetCookieWithDetails(
+      url_google_foo, "A", "B", "google.ozzzzzzle", "foo", base::Time(),
+      false, false));
+  EXPECT_FALSE(cm->SetCookieWithDetails(
+      url_google_foo, "A=", "B", std::string(), "foo", base::Time(),
+      false, false));
+
+  net::CookieMonster::CookieList cookies =
+      cm->GetAllCookiesForURL(url_google_foo);
+  net::CookieMonster::CookieList::iterator it = cookies.begin();
+
+  ASSERT_TRUE(it != cookies.end());
+  EXPECT_EQ("A", it->Name());
+  EXPECT_EQ("B", it->Value());
+  EXPECT_EQ("www.google.izzle", it->Domain());
+  EXPECT_EQ("/foo", it->Path());
+  EXPECT_FALSE(it->DoesExpire());
+  EXPECT_FALSE(it->IsSecure());
+  EXPECT_FALSE(it->IsHttpOnly());
+
+  ASSERT_TRUE(++it == cookies.end());
+
+  cookies = cm->GetAllCookiesForURL(url_google_bar);
+  it = cookies.begin();
+
+  ASSERT_TRUE(it != cookies.end());
+  EXPECT_EQ("C", it->Name());
+  EXPECT_EQ("D", it->Value());
+  EXPECT_EQ(".google.izzle", it->Domain());
+  EXPECT_EQ("/bar", it->Path());
+  EXPECT_FALSE(it->IsSecure());
+  EXPECT_TRUE(it->IsHttpOnly());
+
+  ASSERT_TRUE(++it == cookies.end());
+
+  cookies = cm->GetAllCookiesForURL(url_google_secure);
+  it = cookies.begin();
+
+  ASSERT_TRUE(it != cookies.end());
+  EXPECT_EQ("E", it->Name());
+  EXPECT_EQ("F", it->Value());
+  EXPECT_EQ("/", it->Path());
+  EXPECT_EQ("www.google.izzle", it->Domain());
+  EXPECT_TRUE(it->IsSecure());
+  EXPECT_FALSE(it->IsHttpOnly());
+
+  ASSERT_TRUE(++it == cookies.end());
+}
+
+
+
+TEST(CookieMonsterTest, DeleteAllForHost) {
+  scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL));
+
+  // Test probes:
+  //    * Non-secure URL, mid-level (http://w.c.b.a)
+  //    * Secure URL, mid-level (https://w.c.b.a)
+  //    * URL with path, mid-level (https:/w.c.b.a/dir1/xx)
+  // All three tests should nuke only the midlevel host cookie,
+  // the http_only cookie, the host secure cookie, and the two host
+  // path cookies.  http_only, secure, and paths are ignored by
+  // this call, and domain cookies arent touched.
+  PopulateCmForDeleteAllForHost(cm);
+  EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus3)));
+  EXPECT_EQ("dom_1=X; dom_2=X; host_2=X; sec_dom=X; sec_host=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus2Secure)));
+  EXPECT_EQ("dom_1=X; host_1=X", cm->GetCookies(GURL(kTopLevelDomainPlus1)));
+  EXPECT_EQ("dom_path_2=X; host_path_2=X; dom_path_1=X; host_path_1=X; "
+            "dom_1=X; dom_2=X; host_2=X; sec_dom=X; sec_host=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus2Secure +
+                                std::string("/dir1/dir2/xxx"))));
+
+  EXPECT_EQ(5, cm->DeleteAllForHost(GURL(kTopLevelDomainPlus2)));
+  EXPECT_EQ(8U, cm->GetAllCookies().size());
+
+  EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus3)));
+  EXPECT_EQ("dom_1=X; dom_2=X; sec_dom=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus2Secure)));
+  EXPECT_EQ("dom_1=X; host_1=X", cm->GetCookies(GURL(kTopLevelDomainPlus1)));
+  EXPECT_EQ("dom_path_2=X; dom_path_1=X; dom_1=X; dom_2=X; sec_dom=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus2Secure +
+                                std::string("/dir1/dir2/xxx"))));
+
+  PopulateCmForDeleteAllForHost(cm);
+  EXPECT_EQ(5, cm->DeleteAllForHost(GURL(kTopLevelDomainPlus2Secure)));
+  EXPECT_EQ(8U, cm->GetAllCookies().size());
+
+  EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus3)));
+  EXPECT_EQ("dom_1=X; dom_2=X; sec_dom=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus2Secure)));
+  EXPECT_EQ("dom_1=X; host_1=X", cm->GetCookies(GURL(kTopLevelDomainPlus1)));
+  EXPECT_EQ("dom_path_2=X; dom_path_1=X; dom_1=X; dom_2=X; sec_dom=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus2Secure +
+                                std::string("/dir1/dir2/xxx"))));
+
+  PopulateCmForDeleteAllForHost(cm);
+  EXPECT_EQ(5, cm->DeleteAllForHost(GURL(kTopLevelDomainPlus2Secure +
+                                         std::string("/dir1/xxx"))));
+  EXPECT_EQ(8U, cm->GetAllCookies().size());
+
+  EXPECT_EQ("dom_1=X; dom_2=X; dom_3=X; host_3=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus3)));
+  EXPECT_EQ("dom_1=X; dom_2=X; sec_dom=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus2Secure)));
+  EXPECT_EQ("dom_1=X; host_1=X", cm->GetCookies(GURL(kTopLevelDomainPlus1)));
+  EXPECT_EQ("dom_path_2=X; dom_path_1=X; dom_1=X; dom_2=X; sec_dom=X",
+            cm->GetCookies(GURL(kTopLevelDomainPlus2Secure +
+                                std::string("/dir1/dir2/xxx"))));
+
+}
diff --git a/net/base/cookie_options.h b/net/base/cookie_options.h
index e9301fe..9995a05 100644
--- a/net/base/cookie_options.h
+++ b/net/base/cookie_options.h
@@ -14,12 +14,22 @@
   // Default is to exclude httponly, which means:
   // - reading operations will not return httponly cookies.
   // - writing operations will not write httponly cookies.
-  CookieOptions() : exclude_httponly_(true) {}
+  CookieOptions()
+      : exclude_httponly_(true),
+        force_session_(false) {
+  }
+
   void set_exclude_httponly() { exclude_httponly_ = true; }
   void set_include_httponly() { exclude_httponly_ = false; }
   bool exclude_httponly() const { return exclude_httponly_; }
+
+  // Forces a cookie to be saved as a session cookie.
+  void set_force_session() { force_session_ = true; }
+  bool force_session() const { return force_session_; }
+
  private:
   bool exclude_httponly_;
+  bool force_session_;
 };
 }  // namespace net
 
diff --git a/net/base/cookie_policy.h b/net/base/cookie_policy.h
index d2df2f5..5e57c90 100644
--- a/net/base/cookie_policy.h
+++ b/net/base/cookie_policy.h
@@ -5,18 +5,30 @@
 #ifndef NET_BASE_COOKIE_POLICY_H_
 #define NET_BASE_COOKIE_POLICY_H_
 
+#include <string>
+
 #include "net/base/completion_callback.h"
 
 class GURL;
 
 namespace net {
 
+// Alternative success codes for CookiePolicy::Can{Get,Set}Cookie(s).
+enum {
+  OK_FOR_SESSION_ONLY = 1,  // The cookie may be set but not persisted.
+};
+
 class CookiePolicy {
  public:
-  // Determines if the URL's cookies may be read.  Returns OK if allowed to
-  // read cookies for the given URL.  Returns ERR_IO_PENDING to indicate that
-  // the completion callback will be notified (asynchronously and on the
-  // current thread) of the final result.  Note: The completion callback must
+  // Determines if the URL's cookies may be read.
+  //
+  // Returns:
+  //   OK                   -  if allowed to read cookies
+  //   ERR_ACCESS_DENIED    -  if not allowed to read cookies
+  //   ERR_IO_PENDING       -  if the result will be determined asynchronously
+  //
+  // If the return value is ERR_IO_PENDING, then the given callback will be
+  // notified once the final result is determined.  Note: The callback must
   // remain valid until notified.
   virtual int CanGetCookies(const GURL& url,
                             const GURL& first_party_for_cookies,
@@ -27,6 +39,19 @@
   // the completion callback will be notified (asynchronously and on the
   // current thread) of the final result.  Note: The completion callback must
   // remain valid until notified.
+
+  // Determines if the URL's cookies may be written.
+  //
+  // Returns:
+  //   OK                   -  if allowed to write cookies
+  //   OK_FOR_SESSION_ONLY  -  if allowed to write cookies, but forces them to
+  //                           be stored as session cookies
+  //   ERR_ACCESS_DENIED    -  if not allowed to write cookies
+  //   ERR_IO_PENDING       -  if the result will be determined asynchronously
+  //
+  // If the return value is ERR_IO_PENDING, then the given callback will be
+  // notified once the final result is determined.  Note: The callback must
+  // remain valid until notified.
   virtual int CanSetCookie(const GURL& url,
                            const GURL& first_party_for_cookies,
                            const std::string& cookie_line,
diff --git a/net/base/dir_header.html b/net/base/dir_header.html
index 0383147..ad4ec0a 100644
--- a/net/base/dir_header.html
+++ b/net/base/dir_header.html
@@ -1,5 +1,7 @@
 <!DOCTYPE html>
+
 <html>
+
 <head>
 
 <script>
@@ -15,9 +17,13 @@
   var row = document.createElement("tr");
   var file_cell = document.createElement("td");
   var link = document.createElement("a");
+
+  link.className = isdir ? "icon dir" : "icon file";
+
   if (name == "..") {
     link.href = root + "..";
     link.innerText = document.getElementById("parentDirText").innerText;
+    link.className = "icon up";
     size = "";
     date_modified = "";
   } else {
@@ -54,29 +60,84 @@
 
 function onListingParsingError() {
   var box = document.getElementById("listingParsingErrorBox");
-  box.innerHTML = box.innerHTML.replace("LOCATION", document.location + "?raw");
+  box.innerHTML = box.innerHTML.replace("LOCATION", encodeURI(document.location)
+      + "?raw");
   box.style.display = "";
 }
 </script>
 
-<title id="title"></title>
 <style>
-  h1 { white-space: nowrap; }
-  td.detailsColumn { text-align: right; padding-left: 30px; white-space: nowrap; }
-  #listingParsingErrorBox { border: 1px solid black; background: #fae691; padding: 10px; display: none }
+
+  h1 {
+    border-bottom: 1px solid #c0c0c0;
+    margin-bottom: 10px;
+    padding-bottom: 10px;
+    white-space: nowrap;
+  }
+
+  table {
+    border-collapse: collapse;
+  }
+
+  tr.header {
+    font-weight: bold;
+  }
+
+  td.detailsColumn {
+    padding-left: 2em;
+    text-align: right;
+    white-space: nowrap;
+  }
+
+  a.icon {
+    padding-left: 1.5em;
+    text-decoration: none;
+  }
+
+  a.icon:hover {
+    text-decoration: underline;
+  }
+
+  a.file {
+    background : url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABHUlEQVR42o2RMW7DIBiF3498iHRJD5JKHurL+CRVBp+i2T16tTynF2gO0KSb5ZrBBl4HHDBuK/WXACH4eO9/CAAAbdvijzLGNE1TVZXfZuHg6XCAQESAZXbOKaXO57eiKG6ft9PrKQIkCQqFoIiQFBGlFIB5nvM8t9aOX2Nd18oDzjnPgCDpn/BH4zh2XZdlWVmWiUK4IgCBoFMUz9eP6zRN75cLgEQhcmTQIbl72O0f9865qLAAsURAAgKBJKEtgLXWvyjLuFsThCSstb8rBCaAQhDYWgIZ7myM+TUBjDHrHlZcbMYYk34cN0YSLcgS+wL0fe9TXDMbY33fR2AYBvyQ8L0Gk8MwREBrTfKe4TpTzwhArXWi8HI84h/1DfwI5mhxJamFAAAAAElFTkSuQmCC ") left top no-repeat;
+  }
+
+  a.dir {
+    background : url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAd5JREFUeNqMU79rFUEQ/vbuodFEEkzAImBpkUabFP4ldpaJhZXYm/RiZWsv/hkWFglBUyTIgyAIIfgIRjHv3r39MePM7N3LcbxAFvZ2b2bn22/mm3XMjF+HL3YW7q28YSIw8mBKoBihhhgCsoORot9d3/ywg3YowMXwNde/PzGnk2vn6PitrT+/PGeNaecg4+qNY3D43vy16A5wDDd4Aqg/ngmrjl/GoN0U5V1QquHQG3q+TPDVhVwyBffcmQGJmSVfyZk7R3SngI4JKfwDJ2+05zIg8gbiereTZRHhJ5KCMOwDFLjhoBTn2g0ghagfKeIYJDPFyibJVBtTREwq60SpYvh5++PpwatHsxSm9QRLSQpEVSd7/TYJUb49TX7gztpjjEffnoVw66+Ytovs14Yp7HaKmUXeX9rKUoMoLNW3srqI5fWn8JejrVkK0QcrkFLOgS39yoKUQe292WJ1guUHG8K2o8K00oO1BTvXoW4yasclUTgZYJY9aFNfAThX5CZRmczAV52oAPoupHhWRIUUAOoyUIlYVaAa/VbLbyiZUiyFbjQFNwiZQSGl4IDy9sO5Wrty0QLKhdZPxmgGcDo8ejn+c/6eiK9poz15Kw7Dr/vN/z6W7q++091/AQYA5mZ8GYJ9K0AAAAAASUVORK5CYII= ") left top no-repeat;
+  }
+
+  a.up {
+    background : url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU0toU0EUPfPysx/tTxuDH9SCWhUDooIbd7oRUUTMouqi2iIoCO6lceHWhegy4EJFinWjrlQUpVm0IIoFpVDEIthm0dpikpf3ZuZ6Z94nrXhhMjM3c8895977BBHB2PznK8WPtDgyWH5q77cPH8PpdXuhpQT4ifR9u5sfJb1bmw6VivahATDrxcRZ2njfoaMv+2j7mLDn93MPiNRMvGbL18L9IpF8h9/TN+EYkMffSiOXJ5+hkD+PdqcLpICWHOHc2CC+LEyA/K+cKQMnlQHJX8wqYG3MAJy88Wa4OLDvEqAEOpJd0LxHIMdHBziowSwVlF8D6QaicK01krw/JynwcKoEwZczewroTvZirlKJs5CqQ5CG8pb57FnJUA0LYCXMX5fibd+p8LWDDemcPZbzQyjvH+Ki1TlIciElA7ghwLKV4kRZstt2sANWRjYTAGzuP2hXZFpJ/GsxgGJ0ox1aoFWsDXyyxqCs26+ydmagFN/rRjymJ1898bzGzmQE0HCZpmk5A0RFIv8Pn0WYPsiu6t/Rsj6PauVTwffTSzGAGZhUG2F06hEc9ibS7OPMNp6ErYFlKavo7MkhmTqCxZ/jwzGA9Hx82H2BZSw1NTN9Gx8ycHkajU/7M+jInsDC7DiaEmo1bNl1AMr9ASFgqVu9MCTIzoGUimXVAnnaN0PdBBDCCYbEtMk6wkpQwIG0sn0PQIUF4GsTwLSIFKNqF6DVrQq+IWVrQDxAYQC/1SsYOI4pOxKZrfifiUSbDUisif7XlpGIPufXd/uvdvZm760M0no1FZcnrzUdjw7au3vu/BVgAFLXeuTxhTXVAAAAAElFTkSuQmCC ") left top no-repeat;
+  }
+
+  #listingParsingErrorBox {
+    border: 1px solid black;
+    background: #fae691;
+    padding: 10px;
+    display: none;
+  }
 </style>
+
+<title id="title"></title>
+
 </head>
+
 <body>
+
 <div id="listingParsingErrorBox" i18n-values=".innerHTML:listingParsingErrorBoxText"></div>
+
 <span id="parentDirText" style="display:none" i18n-content="parentDirText"></span>
+
 <h1 id="header" i18n-content="header"></h1>
-<hr/>
+
 <table id="table">
-<tr style="font-weight: bold">
- <td i18n-content="headerName"></td>
- <td class="detailsColumn" i18n-content="headerSize"></td>
- <td class="detailsColumn" i18n-content="headerDateModified"></td>
-</tr>
+  <tr class="header">
+    <td i18n-content="headerName"></td>
+    <td class="detailsColumn" i18n-content="headerSize"></td>
+    <td class="detailsColumn" i18n-content="headerDateModified"></td>
+  </tr>
 </table>
+
 </body>
+
 </html>
diff --git a/net/base/directory_lister.cc b/net/base/directory_lister.cc
index ef3130b..ab45e2f 100644
--- a/net/base/directory_lister.cc
+++ b/net/base/directory_lister.cc
@@ -41,18 +41,21 @@
 // comparison function on the filenames for sorting in the user's locale.
 static bool CompareFindInfo(const file_util::FileEnumerator::FindInfo& a,
                             const file_util::FileEnumerator::FindInfo& b) {
+  // Parent directory before all else.
+  if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(a)))
+    return true;
+  if (file_util::IsDotDot(file_util::FileEnumerator::GetFilename(b)))
+    return false;
+
+  // Directories before regular files.
   bool a_is_directory = file_util::FileEnumerator::IsDirectory(a);
   bool b_is_directory = file_util::FileEnumerator::IsDirectory(b);
   if (a_is_directory != b_is_directory)
     return a_is_directory;
 
-#if defined(OS_WIN)
-  return file_util::LocaleAwareCompareFilenames(FilePath(a.cFileName),
-                                                FilePath(b.cFileName));
-#elif defined(OS_POSIX)
-  return file_util::LocaleAwareCompareFilenames(FilePath(a.filename),
-                                                FilePath(b.filename));
-#endif
+  return file_util::LocaleAwareCompareFilenames(
+      file_util::FileEnumerator::GetFilename(a),
+      file_util::FileEnumerator::GetFilename(b));
 }
 
 DirectoryLister::DirectoryLister(const FilePath& dir,
diff --git a/net/base/directory_lister_unittest.cc b/net/base/directory_lister_unittest.cc
index 85e2b64..f3cd33b 100644
--- a/net/base/directory_lister_unittest.cc
+++ b/net/base/directory_lister_unittest.cc
@@ -35,17 +35,13 @@
             !file_util::FileEnumerator::IsDirectory(file_list_[current])) {
           continue;
         }
+        EXPECT_FALSE(file_util::IsDotDot(
+            file_util::FileEnumerator::GetFilename(file_list_[current])));
         EXPECT_EQ(file_util::FileEnumerator::IsDirectory(file_list_[previous]),
                   file_util::FileEnumerator::IsDirectory(file_list_[current]));
-#if defined(OS_WIN)
         EXPECT_TRUE(file_util::LocaleAwareCompareFilenames(
-                        FilePath(file_list_[previous].cFileName),
-                        FilePath(file_list_[current].cFileName)));
-#elif defined(OS_POSIX)
-        EXPECT_TRUE(file_util::LocaleAwareCompareFilenames(
-                        FilePath(file_list_[previous].filename),
-                        FilePath(file_list_[current].filename)));
-#endif
+            file_util::FileEnumerator::GetFilename(file_list_[previous]),
+            file_util::FileEnumerator::GetFilename(file_list_[current])));
       }
     }
   }
diff --git a/net/base/dns_util.cc b/net/base/dns_util.cc
index db5f606..87d1866 100644
--- a/net/base/dns_util.cc
+++ b/net/base/dns_util.cc
@@ -70,4 +70,12 @@
   return true;
 }
 
+std::string TrimEndingDot(const std::string& host) {
+  std::string host_trimmed = host;
+  size_t len = host_trimmed.length();
+  if (len > 1 && host_trimmed[len - 1] == '.')
+    host_trimmed.erase(len - 1);
+  return host_trimmed;
+}
+
 }  // namespace net
diff --git a/net/base/dns_util.h b/net/base/dns_util.h
index 8eb98f2..f0d2051 100644
--- a/net/base/dns_util.h
+++ b/net/base/dns_util.h
@@ -20,6 +20,9 @@
 // characters as given in RFC 3490, 4.1, 3(a)
 bool IsSTD3ASCIIValidCharacter(char c);
 
+// Returns the hostname by trimming the ending dot, if one exists.
+std::string TrimEndingDot(const std::string& host);
+
 }  // namespace net
 
 #endif  // NET_BASE_DNS_UTIL_H_
diff --git a/net/base/effective_tld_names.cc b/net/base/effective_tld_names.cc
index 4a4b325..3618949 100644
--- a/net/base/effective_tld_names.cc
+++ b/net/base/effective_tld_names.cc
@@ -1,5 +1,5 @@
 /* C++ code produced by gperf version 3.0.3 */
-/* Command-line: gperf -a -L C++ -C -c -o -t -k '*' -NFindDomain -D -m 2 effective_tld_names.gperf  */
+/* Command-line: gperf -a -L C++ -C -c -o -t -k '*' -NFindDomain -D -m 5 effective_tld_names.gperf  */
 
 #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
       && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
@@ -42,12 +42,12 @@
   int type;  // 1: exception, 2: wildcard
 };
 
-#define TOTAL_KEYWORDS 3637
+#define TOTAL_KEYWORDS 3698
 #define MIN_WORD_LENGTH 2
 #define MAX_WORD_LENGTH 43
-#define MIN_HASH_VALUE 3
-#define MAX_HASH_VALUE 54846
-/* maximum key range = 54844, duplicates = 0 */
+#define MIN_HASH_VALUE 10
+#define MAX_HASH_VALUE 63219
+/* maximum key range = 63210, duplicates = 0 */
 
 class Perfect_Hash
 {
@@ -62,35 +62,35 @@
 {
   static const unsigned short asso_values[] =
     {
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847,     2,    18,    10,  9511,     3,
-         23,     0,     0,     0,     1,     1,     0,  9795,    61,     0,
-          0, 54847,     2, 54847,     3,  3905,     0, 54847,     0, 54847,
-      54847, 54847,     0,    15,    14,    12,    11,    10,     9,     5,
-          8,     7,     6, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847,  1028,  6669,   713,
-          4,    14,  5079,  1392,  4218,  1087,  5578,  2938,  3327,   524,
-         96,     0,  1333,  1976,    64,   146,   154,   351,   443,  4591,
-        818,    25,   595,  3198,    22,  8347,     2,  1449,     0,     3,
-          1,  6029, 11607,  1285,   661,    13,   117,  6398,  1807, 13010,
-       1479, 12724,  2783, 13030,  1602,     4,    59,   102, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847, 54847,
-      54847
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220,     5,     8, 63220,     4,     3,
+          4,     3,     5,     3,     4,     3,     3,     3,  3068,    20,
+      63220,     4,  4795,     4, 63220,     4, 63220,     4, 63220,     3,
+      63220, 63220, 63220,    19,    18,     9,    17,    16,    15,     8,
+         14,    11,    10, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220,   506, 15138,  8462,
+         11,   115, 12215,  3362,  1446,   939,  5031,  5868,    30,   365,
+        270,     5,   137,   643,    97,    14,    26,   289,  1951,  3657,
+       8038,   831,     6,   653,     3, 13576,     4,  1099,    49,    48,
+          5,    38,  2900, 10663,  1564,   130, 13960,  3727,  3920, 13502,
+       4843, 16568,  1644,  9521,  1597,     7,    22,    12, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220, 63220,
+      63220
     };
   register int hval = len;
 
@@ -205,19 +205,19 @@
         hval += asso_values[(unsigned char)str[7]];
       /*FALLTHROUGH*/
       case 7:
-        hval += asso_values[(unsigned char)str[6]+1];
+        hval += asso_values[(unsigned char)str[6]];
       /*FALLTHROUGH*/
       case 6:
-        hval += asso_values[(unsigned char)str[5]+19];
+        hval += asso_values[(unsigned char)str[5]+16];
       /*FALLTHROUGH*/
       case 5:
-        hval += asso_values[(unsigned char)str[4]+2];
+        hval += asso_values[(unsigned char)str[4]];
       /*FALLTHROUGH*/
       case 4:
-        hval += asso_values[(unsigned char)str[3]+4];
+        hval += asso_values[(unsigned char)str[3]+3];
       /*FALLTHROUGH*/
       case 3:
-        hval += asso_values[(unsigned char)str[2]+12];
+        hval += asso_values[(unsigned char)str[2]+13];
       /*FALLTHROUGH*/
       case 2:
         hval += asso_values[(unsigned char)str[1]];
@@ -234,1674 +234,2058 @@
 {
   static const struct DomainRule wordlist[] =
     {
-#line 1519 "effective_tld_names.gperf"
-      {"io", 0},
-#line 1180 "effective_tld_names.gperf"
-      {"gov", 0},
-#line 1107 "effective_tld_names.gperf"
-      {"gd", 0},
-#line 1441 "effective_tld_names.gperf"
-      {"id", 2},
-#line 815 "effective_tld_names.gperf"
-      {"edu", 0},
-#line 2235 "effective_tld_names.gperf"
-      {"no", 0},
-#line 1112 "effective_tld_names.gperf"
-      {"ge", 0},
-#line 1449 "effective_tld_names.gperf"
-      {"ie", 0},
-#line 909 "effective_tld_names.gperf"
-      {"ee", 0},
-#line 538 "effective_tld_names.gperf"
-      {"co", 0},
-#line 1336 "effective_tld_names.gperf"
-      {"gy", 0},
-#line 460 "effective_tld_names.gperf"
-      {"cd", 0},
-#line 2090 "effective_tld_names.gperf"
-      {"ne", 0},
-#line 2107 "effective_tld_names.gperf"
-      {"net", 0},
-#line 1242 "effective_tld_names.gperf"
-      {"gov.mo", 0},
-#line 1240 "effective_tld_names.gperf"
-      {"gov.mk", 0},
-#line 1241 "effective_tld_names.gperf"
-      {"gov.mn", 0},
-#line 1190 "effective_tld_names.gperf"
-      {"gov.bm", 0},
-#line 1191 "effective_tld_names.gperf"
-      {"gov.bo", 0},
-#line 869 "effective_tld_names.gperf"
-      {"edu.mo", 0},
-#line 867 "effective_tld_names.gperf"
-      {"edu.mk", 0},
-#line 868 "effective_tld_names.gperf"
-      {"edu.mn", 0},
-#line 824 "effective_tld_names.gperf"
-      {"edu.bm", 0},
-#line 825 "effective_tld_names.gperf"
-      {"edu.bo", 0},
-#line 1198 "effective_tld_names.gperf"
-      {"gov.cm", 0},
-#line 1200 "effective_tld_names.gperf"
-      {"gov.co", 0},
-#line 1199 "effective_tld_names.gperf"
-      {"gov.cn", 0},
-#line 740 "effective_tld_names.gperf"
-      {"cy", 2},
-#line 588 "effective_tld_names.gperf"
-      {"com", 0},
-#line 831 "effective_tld_names.gperf"
-      {"edu.co", 0},
-#line 830 "effective_tld_names.gperf"
-      {"edu.cn", 0},
-#line 2161 "effective_tld_names.gperf"
-      {"net.mo", 0},
-#line 2160 "effective_tld_names.gperf"
-      {"net.mk", 0},
-#line 1189 "effective_tld_names.gperf"
-      {"gov.bf", 0},
-#line 2118 "effective_tld_names.gperf"
-      {"net.bm", 0},
-#line 2119 "effective_tld_names.gperf"
-      {"net.bo", 0},
-#line 823 "effective_tld_names.gperf"
-      {"edu.bi", 0},
-#line 1281 "effective_tld_names.gperf"
-      {"gr", 0},
-#line 1522 "effective_tld_names.gperf"
-      {"ir", 0},
-#line 945 "effective_tld_names.gperf"
-      {"er", 2},
-#line 1422 "effective_tld_names.gperf"
-      {"hr", 0},
-#line 2125 "effective_tld_names.gperf"
-      {"net.co", 0},
-#line 2124 "effective_tld_names.gperf"
-      {"net.cn", 0},
-#line 829 "effective_tld_names.gperf"
-      {"edu.ci", 0},
-#line 3617 "effective_tld_names.gperf"
-      {"ye", 2},
-#line 646 "effective_tld_names.gperf"
-      {"com.mo", 0},
-#line 645 "effective_tld_names.gperf"
-      {"com.mk", 0},
-#line 2273 "effective_tld_names.gperf"
-      {"nr", 0},
-#line 601 "effective_tld_names.gperf"
-      {"com.bm", 0},
-#line 602 "effective_tld_names.gperf"
-      {"com.bo", 0},
-#line 2241 "effective_tld_names.gperf"
-      {"nom.co", 0},
-#line 720 "effective_tld_names.gperf"
-      {"cr", 0},
-#line 609 "effective_tld_names.gperf"
-      {"com.co", 0},
-#line 2123 "effective_tld_names.gperf"
-      {"net.ci", 0},
-#line 608 "effective_tld_names.gperf"
-      {"com.cn", 0},
-#line 1250 "effective_tld_names.gperf"
-      {"gov.pk", 0},
-#line 1252 "effective_tld_names.gperf"
-      {"gov.pn", 0},
-#line 1146 "effective_tld_names.gperf"
-      {"gn", 0},
-#line 1460 "effective_tld_names.gperf"
-      {"in", 0},
-#line 1501 "effective_tld_names.gperf"
-      {"int", 0},
-#line 1402 "effective_tld_names.gperf"
-      {"hn", 0},
-#line 600 "effective_tld_names.gperf"
-      {"com.bi", 0},
-#line 879 "effective_tld_names.gperf"
-      {"edu.pk", 0},
-#line 881 "effective_tld_names.gperf"
-      {"edu.pn", 0},
-#line 1255 "effective_tld_names.gperf"
-      {"gov.pt", 0},
-#line 607 "effective_tld_names.gperf"
-      {"com.ci", 0},
-#line 884 "effective_tld_names.gperf"
-      {"edu.pt", 0},
-#line 1170 "effective_tld_names.gperf"
-      {"gop.pk", 0},
-#line 532 "effective_tld_names.gperf"
-      {"cn", 0},
-#line 2172 "effective_tld_names.gperf"
-      {"net.pk", 0},
-#line 2174 "effective_tld_names.gperf"
-      {"net.pn", 0},
-#line 877 "effective_tld_names.gperf"
-      {"edu.pf", 0},
-#line 1232 "effective_tld_names.gperf"
-      {"gov.lk", 0},
-#line 1158 "effective_tld_names.gperf"
-      {"gob.bo", 0},
-#line 1503 "effective_tld_names.gperf"
-      {"int.bo", 0},
-#line 2177 "effective_tld_names.gperf"
-      {"net.pt", 0},
-#line 577 "effective_tld_names.gperf"
-      {"co.uz", 0},
-#line 861 "effective_tld_names.gperf"
-      {"edu.lk", 0},
-#line 1234 "effective_tld_names.gperf"
-      {"gov.lt", 0},
-#line 1505 "effective_tld_names.gperf"
-      {"int.co", 0},
-#line 659 "effective_tld_names.gperf"
-      {"com.pk", 0},
-#line 1244 "effective_tld_names.gperf"
-      {"gov.mu", 0},
-#line 1300 "effective_tld_names.gperf"
-      {"gs", 0},
-#line 1527 "effective_tld_names.gperf"
-      {"is", 0},
-#line 948 "effective_tld_names.gperf"
-      {"es", 0},
-#line 663 "effective_tld_names.gperf"
-      {"com.pt", 0},
-#line 2154 "effective_tld_names.gperf"
-      {"net.lk", 0},
-#line 1324 "effective_tld_names.gperf"
-      {"gt", 2},
-#line 1534 "effective_tld_names.gperf"
-      {"it", 0},
-#line 954 "effective_tld_names.gperf"
-      {"et", 2},
-#line 1424 "effective_tld_names.gperf"
-      {"ht", 0},
-#line 1201 "effective_tld_names.gperf"
-      {"gov.cu", 0},
-#line 1504 "effective_tld_names.gperf"
-      {"int.ci", 0},
-#line 657 "effective_tld_names.gperf"
-      {"com.pf", 0},
-#line 832 "effective_tld_names.gperf"
-      {"edu.cu", 0},
-#line 2162 "effective_tld_names.gperf"
-      {"net.mu", 0},
-#line 640 "effective_tld_names.gperf"
-      {"com.lk", 0},
-#line 1237 "effective_tld_names.gperf"
-      {"gov.ma", 0},
-#line 2126 "effective_tld_names.gperf"
-      {"net.cu", 0},
-#line 1187 "effective_tld_names.gperf"
-      {"gov.ba", 0},
-#line 1165 "effective_tld_names.gperf"
-      {"gob.pk", 0},
-#line 1478 "effective_tld_names.gperf"
-      {"inf.mk", 0},
-#line 647 "effective_tld_names.gperf"
-      {"com.mu", 0},
-#line 821 "effective_tld_names.gperf"
-      {"edu.ba", 0},
-#line 1510 "effective_tld_names.gperf"
-      {"int.pt", 0},
-#line 610 "effective_tld_names.gperf"
-      {"com.cu", 0},
-#line 2158 "effective_tld_names.gperf"
-      {"net.ma", 0},
-#line 1298 "effective_tld_names.gperf"
-      {"grp.lk", 0},
-#line 2116 "effective_tld_names.gperf"
-      {"net.ba", 0},
-#line 1152 "effective_tld_names.gperf"
-      {"go.kr", 0},
-#line 2250 "effective_tld_names.gperf"
-      {"nom.ro", 0},
-#line 1508 "effective_tld_names.gperf"
-      {"int.lk", 0},
-#line 1151 "effective_tld_names.gperf"
-      {"go.jp", 0},
-#line 665 "effective_tld_names.gperf"
-      {"com.ro", 0},
-#line 813 "effective_tld_names.gperf"
-      {"ed.jp", 0},
-#line 597 "effective_tld_names.gperf"
-      {"com.ba", 0},
-#line 557 "effective_tld_names.gperf"
-      {"co.kr", 0},
-#line 1153 "effective_tld_names.gperf"
-      {"go.pw", 0},
-#line 2092 "effective_tld_names.gperf"
-      {"ne.kr", 0},
-#line 556 "effective_tld_names.gperf"
-      {"co.jp", 0},
-#line 814 "effective_tld_names.gperf"
-      {"ed.pw", 0},
-#line 2091 "effective_tld_names.gperf"
-      {"ne.jp", 0},
-#line 875 "effective_tld_names.gperf"
-      {"edu.pa", 0},
-#line 566 "effective_tld_names.gperf"
-      {"co.pw", 0},
-#line 2093 "effective_tld_names.gperf"
-      {"ne.pw", 0},
-#line 2169 "effective_tld_names.gperf"
-      {"net.pa", 0},
-#line 1229 "effective_tld_names.gperf"
-      {"gov.la", 0},
-#line 2246 "effective_tld_names.gperf"
-      {"nom.pa", 0},
-#line 858 "effective_tld_names.gperf"
-      {"edu.la", 0},
-#line 1283 "effective_tld_names.gperf"
-      {"gr.jp", 0},
-#line 655 "effective_tld_names.gperf"
-      {"com.pa", 0},
-#line 1257 "effective_tld_names.gperf"
-      {"gov.ru", 0},
-#line 2151 "effective_tld_names.gperf"
-      {"net.la", 0},
-#line 886 "effective_tld_names.gperf"
-      {"edu.ru", 0},
-#line 1479 "effective_tld_names.gperf"
-      {"info", 0},
-#line 2268 "effective_tld_names.gperf"
-      {"nov.ru", 0},
-#line 1477 "effective_tld_names.gperf"
-      {"inf.cu", 0},
-#line 637 "effective_tld_names.gperf"
-      {"com.la", 0},
-#line 2178 "effective_tld_names.gperf"
-      {"net.ru", 0},
-#line 2350 "effective_tld_names.gperf"
-      {"org", 0},
-#line 1163 "effective_tld_names.gperf"
-      {"gob.pa", 0},
-#line 666 "effective_tld_names.gperf"
-      {"com.ru", 0},
-#line 1325 "effective_tld_names.gperf"
-      {"gu", 2},
-#line 960 "effective_tld_names.gperf"
-      {"eu", 0},
-#line 1425 "effective_tld_names.gperf"
-      {"hu", 0},
-#line 2414 "effective_tld_names.gperf"
-      {"org.mo", 0},
-#line 2412 "effective_tld_names.gperf"
-      {"org.mk", 0},
-#line 2413 "effective_tld_names.gperf"
-      {"org.mn", 0},
-#line 2362 "effective_tld_names.gperf"
-      {"org.bm", 0},
-#line 2363 "effective_tld_names.gperf"
-      {"org.bo", 0},
-#line 2286 "effective_tld_names.gperf"
-      {"nu", 0},
-#line 949 "effective_tld_names.gperf"
-      {"es.kr", 0},
-#line 1423 "effective_tld_names.gperf"
-      {"hs.kr", 0},
-#line 2370 "effective_tld_names.gperf"
-      {"org.co", 0},
-#line 2369 "effective_tld_names.gperf"
-      {"org.cn", 0},
-#line 732 "effective_tld_names.gperf"
-      {"cu", 0},
-#line 1507 "effective_tld_names.gperf"
-      {"int.la", 0},
-#line 2361 "effective_tld_names.gperf"
-      {"org.bi", 0},
-#line 1188 "effective_tld_names.gperf"
-      {"gov.bb", 0},
-#line 2368 "effective_tld_names.gperf"
-      {"org.ci", 0},
-#line 822 "effective_tld_names.gperf"
-      {"edu.bb", 0},
-#line 1265 "effective_tld_names.gperf"
-      {"gov.st", 0},
-#line 1511 "effective_tld_names.gperf"
-      {"int.ru", 0},
-#line 2339 "effective_tld_names.gperf"
-      {"or.kr", 0},
-#line 894 "effective_tld_names.gperf"
-      {"edu.st", 0},
-#line 2338 "effective_tld_names.gperf"
-      {"or.jp", 0},
-#line 2117 "effective_tld_names.gperf"
-      {"net.bb", 0},
-#line 3626 "effective_tld_names.gperf"
-      {"yu", 2},
-#line 2186 "effective_tld_names.gperf"
-      {"net.st", 0},
-#line 2426 "effective_tld_names.gperf"
-      {"org.pk", 0},
-#line 2428 "effective_tld_names.gperf"
-      {"org.pn", 0},
-#line 2342 "effective_tld_names.gperf"
-      {"or.pw", 0},
-#line 598 "effective_tld_names.gperf"
-      {"com.bb", 0},
-#line 2431 "effective_tld_names.gperf"
-      {"org.pt", 0},
-#line 674 "effective_tld_names.gperf"
-      {"com.st", 0},
-#line 2424 "effective_tld_names.gperf"
-      {"org.pf", 0},
-#line 1445 "effective_tld_names.gperf"
-      {"id.us", 0},
-#line 2404 "effective_tld_names.gperf"
-      {"org.lk", 0},
-#line 2089 "effective_tld_names.gperf"
-      {"nd.us", 0},
-#line 576 "effective_tld_names.gperf"
-      {"co.us", 0},
-#line 737 "effective_tld_names.gperf"
-      {"cv", 0},
-#line 2096 "effective_tld_names.gperf"
-      {"ne.us", 0},
-#line 2415 "effective_tld_names.gperf"
-      {"org.mu", 0},
-#line 1270 "effective_tld_names.gperf"
-      {"gov.to", 0},
-#line 1269 "effective_tld_names.gperf"
-      {"gov.tn", 0},
-#line 1230 "effective_tld_names.gperf"
-      {"gov.lb", 0},
-#line 2294 "effective_tld_names.gperf"
-      {"ny.us", 0},
-#line 897 "effective_tld_names.gperf"
-      {"edu.to", 0},
-#line 859 "effective_tld_names.gperf"
-      {"edu.lb", 0},
-#line 1271 "effective_tld_names.gperf"
-      {"gov.tt", 0},
-#line 2371 "effective_tld_names.gperf"
-      {"org.cu", 0},
-#line 1498 "effective_tld_names.gperf"
-      {"ing.pa", 0},
-#line 898 "effective_tld_names.gperf"
-      {"edu.tt", 0},
-#line 2191 "effective_tld_names.gperf"
-      {"net.to", 0},
-#line 2190 "effective_tld_names.gperf"
-      {"net.tn", 0},
-#line 2152 "effective_tld_names.gperf"
-      {"net.lb", 0},
-#line 2409 "effective_tld_names.gperf"
-      {"org.ma", 0},
-#line 2432 "effective_tld_names.gperf"
-      {"org.ro", 0},
-#line 2192 "effective_tld_names.gperf"
-      {"net.tt", 0},
-#line 2359 "effective_tld_names.gperf"
-      {"org.ba", 0},
-#line 678 "effective_tld_names.gperf"
-      {"com.to", 0},
-#line 677 "effective_tld_names.gperf"
-      {"com.tn", 0},
-#line 638 "effective_tld_names.gperf"
-      {"com.lb", 0},
-#line 1144 "effective_tld_names.gperf"
-      {"gm", 0},
-#line 1455 "effective_tld_names.gperf"
-      {"im", 0},
-#line 1400 "effective_tld_names.gperf"
-      {"hm", 0},
-#line 679 "effective_tld_names.gperf"
-      {"com.tt", 0},
-#line 1259 "effective_tld_names.gperf"
-      {"gov.sa", 0},
-#line 1466 "effective_tld_names.gperf"
-      {"in.us", 0},
-#line 888 "effective_tld_names.gperf"
-      {"edu.sa", 0},
-#line 542 "effective_tld_names.gperf"
-      {"co.at", 0},
-#line 530 "effective_tld_names.gperf"
-      {"cm", 0},
-#line 1225 "effective_tld_names.gperf"
-      {"gov.km", 0},
-#line 568 "effective_tld_names.gperf"
-      {"co.rw", 0},
-#line 1226 "effective_tld_names.gperf"
-      {"gov.kn", 0},
-#line 2180 "effective_tld_names.gperf"
-      {"net.sa", 0},
-#line 854 "effective_tld_names.gperf"
-      {"edu.km", 0},
-#line 855 "effective_tld_names.gperf"
-      {"edu.kn", 0},
-#line 1231 "effective_tld_names.gperf"
-      {"gov.lc", 0},
-#line 2422 "effective_tld_names.gperf"
-      {"org.pa", 0},
-#line 1224 "effective_tld_names.gperf"
-      {"gov.ki", 0},
-#line 860 "effective_tld_names.gperf"
-      {"edu.lc", 0},
-#line 668 "effective_tld_names.gperf"
-      {"com.sa", 0},
-#line 1514 "effective_tld_names.gperf"
-      {"int.tt", 0},
-#line 2148 "effective_tld_names.gperf"
-      {"net.kn", 0},
-#line 853 "effective_tld_names.gperf"
-      {"edu.ki", 0},
-#line 2244 "effective_tld_names.gperf"
-      {"nom.km", 0},
-#line 2153 "effective_tld_names.gperf"
-      {"net.lc", 0},
-#line 156 "effective_tld_names.gperf"
-      {"ao", 0},
-#line 634 "effective_tld_names.gperf"
-      {"com.km", 0},
-#line 68 "effective_tld_names.gperf"
-      {"ad", 0},
-#line 2147 "effective_tld_names.gperf"
-      {"net.ki", 0},
-#line 2401 "effective_tld_names.gperf"
-      {"org.la", 0},
-#line 2298 "effective_tld_names.gperf"
-      {"nz", 2},
-#line 74 "effective_tld_names.gperf"
-      {"ae", 0},
-#line 551 "effective_tld_names.gperf"
-      {"co.im", 0},
-#line 639 "effective_tld_names.gperf"
-      {"com.lc", 0},
-#line 731 "effective_tld_names.gperf"
-      {"ct.us", 0},
-#line 743 "effective_tld_names.gperf"
-      {"cz", 0},
-#line 633 "effective_tld_names.gperf"
-      {"com.ki", 0},
-#line 2346 "effective_tld_names.gperf"
-      {"or.us", 0},
-#line 1239 "effective_tld_names.gperf"
-      {"gov.mg", 0},
-#line 2434 "effective_tld_names.gperf"
-      {"org.ru", 0},
-#line 3645 "effective_tld_names.gperf"
-      {"zm", 2},
-#line 866 "effective_tld_names.gperf"
-      {"edu.mg", 0},
-#line 2321 "effective_tld_names.gperf"
-      {"om", 2},
-#line 166 "effective_tld_names.gperf"
-      {"ar", 2},
-#line 2245 "effective_tld_names.gperf"
-      {"nom.mg", 0},
-#line 1969 "effective_tld_names.gperf"
-      {"mo", 0},
-#line 1864 "effective_tld_names.gperf"
-      {"md", 0},
-#line 644 "effective_tld_names.gperf"
-      {"com.mg", 0},
-#line 1867 "effective_tld_names.gperf"
-      {"me", 0},
-#line 2032 "effective_tld_names.gperf"
-      {"my", 0},
-#line 1169 "effective_tld_names.gperf"
-      {"gon.pk", 0},
-#line 145 "effective_tld_names.gperf"
-      {"an", 0},
-#line 1193 "effective_tld_names.gperf"
-      {"gov.bs", 0},
-#line 180 "effective_tld_names.gperf"
-      {"arpa", 0},
-#line 827 "effective_tld_names.gperf"
-      {"edu.bs", 0},
-#line 2333 "effective_tld_names.gperf"
-      {"or.at", 0},
-#line 2360 "effective_tld_names.gperf"
-      {"org.bb", 0},
-#line 1277 "effective_tld_names.gperf"
-      {"gov.ws", 0},
-#line 808 "effective_tld_names.gperf"
-      {"ec", 0},
-#line 2442 "effective_tld_names.gperf"
-      {"org.st", 0},
-#line 2121 "effective_tld_names.gperf"
-      {"net.bs", 0},
-#line 903 "effective_tld_names.gperf"
-      {"edu.ws", 0},
-#line 2002 "effective_tld_names.gperf"
-      {"mr", 0},
-#line 2087 "effective_tld_names.gperf"
-      {"nc", 0},
-#line 1260 "effective_tld_names.gperf"
-      {"gov.sb", 0},
-#line 457 "effective_tld_names.gperf"
-      {"cc", 0},
-#line 889 "effective_tld_names.gperf"
-      {"edu.sb", 0},
-#line 604 "effective_tld_names.gperf"
-      {"com.bs", 0},
-#line 2199 "effective_tld_names.gperf"
-      {"net.ws", 0},
-#line 197 "effective_tld_names.gperf"
-      {"as", 0},
-#line 1442 "effective_tld_names.gperf"
-      {"id.ir", 0},
-#line 228 "effective_tld_names.gperf"
-      {"at", 0},
-#line 819 "effective_tld_names.gperf"
-      {"edu.an", 0},
-#line 1254 "effective_tld_names.gperf"
-      {"gov.ps", 0},
-#line 2181 "effective_tld_names.gperf"
-      {"net.sb", 0},
-#line 1966 "effective_tld_names.gperf"
-      {"mn", 0},
-#line 77 "effective_tld_names.gperf"
-      {"aero", 0},
-#line 687 "effective_tld_names.gperf"
-      {"com.ws", 0},
-#line 883 "effective_tld_names.gperf"
-      {"edu.ps", 0},
-#line 553 "effective_tld_names.gperf"
-      {"co.ir", 0},
-#line 1183 "effective_tld_names.gperf"
-      {"gov.af", 0},
-#line 2114 "effective_tld_names.gperf"
-      {"net.an", 0},
-#line 817 "effective_tld_names.gperf"
-      {"edu.af", 0},
-#line 669 "effective_tld_names.gperf"
-      {"com.sb", 0},
-#line 2176 "effective_tld_names.gperf"
-      {"net.ps", 0},
-#line 2112 "effective_tld_names.gperf"
-      {"net.ai", 0},
-#line 594 "effective_tld_names.gperf"
-      {"com.an", 0},
-#line 1326 "effective_tld_names.gperf"
-      {"gu.us", 0},
-#line 2110 "effective_tld_names.gperf"
-      {"net.af", 0},
-#line 2447 "effective_tld_names.gperf"
-      {"org.to", 0},
-#line 662 "effective_tld_names.gperf"
-      {"com.ps", 0},
-#line 2446 "effective_tld_names.gperf"
-      {"org.tn", 0},
-#line 2402 "effective_tld_names.gperf"
-      {"org.lb", 0},
-#line 2005 "effective_tld_names.gperf"
-      {"ms", 0},
-#line 592 "effective_tld_names.gperf"
-      {"com.ai", 0},
-#line 2448 "effective_tld_names.gperf"
-      {"org.tt", 0},
-#line 590 "effective_tld_names.gperf"
-      {"com.af", 0},
-#line 2010 "effective_tld_names.gperf"
-      {"mt", 2},
-#line 1261 "effective_tld_names.gperf"
-      {"gov.sc", 0},
-#line 69 "effective_tld_names.gperf"
-      {"ad.jp", 0},
-#line 890 "effective_tld_names.gperf"
-      {"edu.sc", 0},
-#line 739 "effective_tld_names.gperf"
-      {"cx", 0},
-#line 1256 "effective_tld_names.gperf"
-      {"gov.rs", 0},
-#line 1238 "effective_tld_names.gperf"
-      {"gov.me", 0},
-#line 2182 "effective_tld_names.gperf"
-      {"net.sc", 0},
-#line 1276 "effective_tld_names.gperf"
-      {"gov.vn", 0},
-#line 885 "effective_tld_names.gperf"
-      {"edu.rs", 0},
-#line 865 "effective_tld_names.gperf"
-      {"edu.me", 0},
-#line 902 "effective_tld_names.gperf"
-      {"edu.vn", 0},
-#line 2435 "effective_tld_names.gperf"
-      {"org.sa", 0},
-#line 564 "effective_tld_names.gperf"
-      {"co.na", 0},
-#line 670 "effective_tld_names.gperf"
-      {"com.sc", 0},
-#line 2159 "effective_tld_names.gperf"
-      {"net.me", 0},
-#line 425 "effective_tld_names.gperf"
-      {"c.la", 0},
-#line 2198 "effective_tld_names.gperf"
-      {"net.vn", 0},
-#line 2397 "effective_tld_names.gperf"
-      {"org.km", 0},
-#line 567 "effective_tld_names.gperf"
-      {"co.rs", 0},
-#line 2398 "effective_tld_names.gperf"
-      {"org.kn", 0},
-#line 2403 "effective_tld_names.gperf"
-      {"org.lc", 0},
-#line 2197 "effective_tld_names.gperf"
-      {"net.vi", 0},
-#line 686 "effective_tld_names.gperf"
-      {"com.vn", 0},
-#line 2292 "effective_tld_names.gperf"
-      {"nv.us", 0},
-#line 1465 "effective_tld_names.gperf"
-      {"in.ua", 0},
-#line 2396 "effective_tld_names.gperf"
-      {"org.ki", 0},
-#line 1156 "effective_tld_names.gperf"
-      {"go.tz", 0},
-#line 685 "effective_tld_names.gperf"
-      {"com.vi", 0},
-#line 876 "effective_tld_names.gperf"
-      {"edu.pe", 0},
-#line 535 "effective_tld_names.gperf"
-      {"cn.ua", 0},
-#line 2303 "effective_tld_names.gperf"
-      {"od.ua", 0},
-#line 574 "effective_tld_names.gperf"
-      {"co.tz", 0},
-#line 2094 "effective_tld_names.gperf"
-      {"ne.tz", 0},
-#line 2170 "effective_tld_names.gperf"
-      {"net.pe", 0},
-#line 1462 "effective_tld_names.gperf"
-      {"in.na", 0},
-#line 1515 "effective_tld_names.gperf"
-      {"int.vn", 0},
-#line 233 "effective_tld_names.gperf"
-      {"au", 2},
-#line 2247 "effective_tld_names.gperf"
-      {"nom.pe", 0},
-#line 2411 "effective_tld_names.gperf"
-      {"org.mg", 0},
-#line 1463 "effective_tld_names.gperf"
-      {"in.rs", 0},
-#line 656 "effective_tld_names.gperf"
-      {"com.pe", 0},
-#line 1332 "effective_tld_names.gperf"
-      {"gv.at", 0},
-#line 1263 "effective_tld_names.gperf"
-      {"gov.sg", 0},
-#line 2233 "effective_tld_names.gperf"
-      {"nm.us", 0},
-#line 892 "effective_tld_names.gperf"
-      {"edu.sg", 0},
-#line 2184 "effective_tld_names.gperf"
-      {"net.sg", 0},
-#line 1164 "effective_tld_names.gperf"
-      {"gob.pe", 0},
-#line 2013 "effective_tld_names.gperf"
-      {"mu", 0},
-#line 2365 "effective_tld_names.gperf"
-      {"org.bs", 0},
-#line 672 "effective_tld_names.gperf"
-      {"com.sg", 0},
-#line 2341 "effective_tld_names.gperf"
-      {"or.na", 0},
-#line 2007 "effective_tld_names.gperf"
-      {"ms.kr", 0},
-#line 1090 "effective_tld_names.gperf"
-      {"ga", 0},
-#line 1150 "effective_tld_names.gperf"
-      {"go.it", 0},
-#line 2249 "effective_tld_names.gperf"
-      {"nom.re", 0},
-#line 2455 "effective_tld_names.gperf"
-      {"org.ws", 0},
-#line 2037 "effective_tld_names.gperf"
-      {"na", 0},
-#line 2237 "effective_tld_names.gperf"
-      {"no.it", 0},
-#line 1113 "effective_tld_names.gperf"
-      {"ge.it", 0},
-#line 664 "effective_tld_names.gperf"
-      {"com.re", 0},
-#line 427 "effective_tld_names.gperf"
-      {"ca", 0},
-#line 451 "effective_tld_names.gperf"
-      {"cat", 0},
-#line 554 "effective_tld_names.gperf"
-      {"co.it", 0},
-#line 2436 "effective_tld_names.gperf"
-      {"org.sb", 0},
-#line 3649 "effective_tld_names.gperf"
-      {"zt.ua", 0},
-#line 461 "effective_tld_names.gperf"
-      {"ce.it", 0},
-#line 2357 "effective_tld_names.gperf"
-      {"org.an", 0},
-#line 2430 "effective_tld_names.gperf"
-      {"org.ps", 0},
-#line 2344 "effective_tld_names.gperf"
-      {"or.tz", 0},
-#line 1126 "effective_tld_names.gperf"
-      {"gi", 0},
-#line 2355 "effective_tld_names.gperf"
-      {"org.ai", 0},
-#line 2353 "effective_tld_names.gperf"
-      {"org.af", 0},
-#line 1282 "effective_tld_names.gperf"
-      {"gr.it", 0},
-#line 169 "effective_tld_names.gperf"
-      {"ar.us", 0},
-#line 2216 "effective_tld_names.gperf"
-      {"ni", 2},
-#line 1973 "effective_tld_names.gperf"
-      {"mo.us", 0},
-#line 2028 "effective_tld_names.gperf"
-      {"mv", 2},
-#line 1866 "effective_tld_names.gperf"
-      {"md.us", 0},
-#line 202 "effective_tld_names.gperf"
-      {"asia", 0},
-#line 495 "effective_tld_names.gperf"
-      {"ci", 0},
-#line 2406 "effective_tld_names.gperf"
-      {"org.ls", 0},
-#line 1869 "effective_tld_names.gperf"
-      {"me.us", 0},
-#line 721 "effective_tld_names.gperf"
-      {"cr.it", 0},
-#line 1211 "effective_tld_names.gperf"
-      {"gov.gn", 0},
-#line 130 "effective_tld_names.gperf"
-      {"am", 0},
-#line 841 "effective_tld_names.gperf"
-      {"edu.gn", 0},
-#line 928 "effective_tld_names.gperf"
-      {"en.it", 0},
-#line 3630 "effective_tld_names.gperf"
-      {"za", 2},
-#line 1210 "effective_tld_names.gperf"
-      {"gov.gi", 0},
-#line 2132 "effective_tld_names.gperf"
-      {"net.gn", 0},
-#line 840 "effective_tld_names.gperf"
-      {"edu.gi", 0},
-#line 1223 "effective_tld_names.gperf"
-      {"gov.kg", 0},
-#line 2437 "effective_tld_names.gperf"
-      {"org.sc", 0},
-#line 534 "effective_tld_names.gperf"
-      {"cn.it", 0},
-#line 852 "effective_tld_names.gperf"
-      {"edu.kg", 0},
-#line 620 "effective_tld_names.gperf"
-      {"com.gn", 0},
-#line 2088 "effective_tld_names.gperf"
-      {"nc.us", 0},
-#line 2433 "effective_tld_names.gperf"
-      {"org.rs", 0},
-#line 2410 "effective_tld_names.gperf"
-      {"org.me", 0},
-#line 2146 "effective_tld_names.gperf"
-      {"net.kg", 0},
-#line 2454 "effective_tld_names.gperf"
-      {"org.vn", 0},
-#line 1157 "effective_tld_names.gperf"
-      {"go.ug", 0},
-#line 1528 "effective_tld_names.gperf"
-      {"is.it", 0},
-#line 198 "effective_tld_names.gperf"
-      {"as.us", 0},
-#line 619 "effective_tld_names.gperf"
-      {"com.gi", 0},
-#line 1181 "effective_tld_names.gperf"
-      {"gov.ac", 0},
-#line 1965 "effective_tld_names.gperf"
-      {"mm", 2},
-#line 540 "effective_tld_names.gperf"
-      {"co.ag", 0},
-#line 256 "effective_tld_names.gperf"
-      {"az", 0},
-#line 816 "effective_tld_names.gperf"
-      {"edu.ac", 0},
-#line 632 "effective_tld_names.gperf"
-      {"com.kg", 0},
-#line 2453 "effective_tld_names.gperf"
-      {"org.vi", 0},
-#line 1968 "effective_tld_names.gperf"
-      {"mn.us", 0},
-#line 575 "effective_tld_names.gperf"
-      {"co.ug", 0},
-#line 729 "effective_tld_names.gperf"
-      {"cs.it", 0},
-#line 2095 "effective_tld_names.gperf"
-      {"ne.ug", 0},
-#line 730 "effective_tld_names.gperf"
-      {"ct.it", 0},
-#line 2108 "effective_tld_names.gperf"
-      {"net.ac", 0},
-#line 2337 "effective_tld_names.gperf"
-      {"or.it", 0},
-#line 2295 "effective_tld_names.gperf"
-      {"nyc.museum", 0},
-#line 589 "effective_tld_names.gperf"
-      {"com.ac", 0},
-#line 2423 "effective_tld_names.gperf"
-      {"org.pe", 0},
-#line 2008 "effective_tld_names.gperf"
-      {"ms.us", 0},
-#line 2012 "effective_tld_names.gperf"
-      {"mt.us", 0},
-#line 2034 "effective_tld_names.gperf"
-      {"mz", 2},
-#line 738 "effective_tld_names.gperf"
-      {"cv.ua", 0},
-#line 1975 "effective_tld_names.gperf"
-      {"mobi", 0},
-#line 1275 "effective_tld_names.gperf"
-      {"gov.vc", 0},
-#line 901 "effective_tld_names.gperf"
-      {"edu.vc", 0},
-#line 2440 "effective_tld_names.gperf"
-      {"org.sg", 0},
-#line 801 "effective_tld_names.gperf"
-      {"e-burg.ru", 0},
-#line 32 "effective_tld_names.gperf"
-      {"ac", 0},
-#line 1813 "effective_tld_names.gperf"
-      {"ly", 0},
-#line 1243 "effective_tld_names.gperf"
-      {"gov.mr", 0},
-#line 2196 "effective_tld_names.gperf"
-      {"net.vc", 0},
-#line 1192 "effective_tld_names.gperf"
-      {"gov.br", 0},
-#line 1089 "effective_tld_names.gperf"
-      {"g12.br", 0},
-#line 826 "effective_tld_names.gperf"
-      {"edu.br", 0},
-#line 2263 "effective_tld_names.gperf"
-      {"not.br", 0},
-#line 1279 "effective_tld_names.gperf"
-      {"gp", 0},
-#line 684 "effective_tld_names.gperf"
-      {"com.vc", 0},
-#line 2120 "effective_tld_names.gperf"
-      {"net.br", 0},
-#line 2272 "effective_tld_names.gperf"
-      {"np", 2},
-#line 573 "effective_tld_names.gperf"
-      {"co.tt", 0},
-#line 1790 "effective_tld_names.gperf"
-      {"lr", 0},
-#line 2240 "effective_tld_names.gperf"
-      {"nom.br", 0},
-#line 2345 "effective_tld_names.gperf"
-      {"or.ug", 0},
-#line 2111 "effective_tld_names.gperf"
-      {"net.ag", 0},
-#line 603 "effective_tld_names.gperf"
-      {"com.br", 0},
-#line 2239 "effective_tld_names.gperf"
-      {"nom.ag", 0},
-#line 1862 "effective_tld_names.gperf"
-      {"mc", 0},
-#line 1253 "effective_tld_names.gperf"
-      {"gov.pr", 0},
-#line 591 "effective_tld_names.gperf"
-      {"com.ag", 0},
-#line 882 "effective_tld_names.gperf"
-      {"edu.pr", 0},
-#line 1123 "effective_tld_names.gperf"
-      {"gg", 0},
-#line 910 "effective_tld_names.gperf"
-      {"eg", 2},
-#line 2288 "effective_tld_names.gperf"
-      {"nu.it", 0},
-#line 1775 "effective_tld_names.gperf"
-      {"local", 0},
-#line 1185 "effective_tld_names.gperf"
-      {"gov.as", 0},
-#line 2175 "effective_tld_names.gperf"
-      {"net.pr", 0},
-#line 2210 "effective_tld_names.gperf"
-      {"ng", 0},
-#line 1233 "effective_tld_names.gperf"
-      {"gov.lr", 0},
-#line 2166 "effective_tld_names.gperf"
-      {"net.nf", 0},
-#line 254 "effective_tld_names.gperf"
-      {"ax", 0},
-#line 466 "effective_tld_names.gperf"
-      {"cg", 0},
-#line 862 "effective_tld_names.gperf"
-      {"edu.lr", 0},
-#line 661 "effective_tld_names.gperf"
-      {"com.pr", 0},
-#line 1405 "effective_tld_names.gperf"
-      {"hof.no", 0},
-#line 1791 "effective_tld_names.gperf"
-      {"ls", 0},
-#line 652 "effective_tld_names.gperf"
-      {"com.nf", 0},
-#line 558 "effective_tld_names.gperf"
-      {"co.lc", 0},
-#line 537 "effective_tld_names.gperf"
-      {"cnt.br", 0},
-#line 2155 "effective_tld_names.gperf"
-      {"net.lr", 0},
-#line 1792 "effective_tld_names.gperf"
-      {"lt", 0},
-#line 2381 "effective_tld_names.gperf"
-      {"org.gn", 0},
-#line 1041 "effective_tld_names.gperf"
-      {"fo", 0},
-#line 641 "effective_tld_names.gperf"
-      {"com.lr", 0},
-#line 2380 "effective_tld_names.gperf"
-      {"org.gi", 0},
-#line 1091 "effective_tld_names.gperf"
-      {"ga.us", 0},
-#line 1437 "effective_tld_names.gperf"
-      {"ia.us", 0},
-#line 955 "effective_tld_names.gperf"
-      {"etc.br", 0},
-#line 2395 "effective_tld_names.gperf"
-      {"org.kg", 0},
-#line 1476 "effective_tld_names.gperf"
-      {"inf.br", 0},
-#line 2030 "effective_tld_names.gperf"
-      {"mx", 0},
-#line 2876 "effective_tld_names.gperf"
-      {"sd", 0},
-#line 2285 "effective_tld_names.gperf"
-      {"ntr.br", 0},
-#line 950 "effective_tld_names.gperf"
-      {"esp.br", 0},
-#line 430 "effective_tld_names.gperf"
-      {"ca.us", 0},
-#line 1197 "effective_tld_names.gperf"
-      {"gov.cl", 0},
-#line 539 "effective_tld_names.gperf"
-      {"co.ae", 0},
-#line 2879 "effective_tld_names.gperf"
-      {"se", 0},
-#line 3065 "effective_tld_names.gperf"
-      {"sy", 0},
-#line 1246 "effective_tld_names.gperf"
-      {"gov.my", 0},
-#line 2351 "effective_tld_names.gperf"
-      {"org.ac", 0},
-#line 1194 "effective_tld_names.gperf"
-      {"gov.by", 0},
-#line 1056 "effective_tld_names.gperf"
-      {"fr", 0},
-#line 2068 "effective_tld_names.gperf"
-      {"nat.tn", 0},
-#line 872 "effective_tld_names.gperf"
-      {"edu.my", 0},
-#line 2439 "effective_tld_names.gperf"
-      {"org.se", 0},
-#line 952 "effective_tld_names.gperf"
-      {"est.pr", 0},
-#line 44 "effective_tld_names.gperf"
-      {"ac.kr", 0},
-#line 1383 "effective_tld_names.gperf"
-      {"hi.us", 0},
-#line 43 "effective_tld_names.gperf"
-      {"ac.jp", 0},
-#line 2165 "effective_tld_names.gperf"
-      {"net.my", 0},
-#line 1251 "effective_tld_names.gperf"
-      {"gov.pl", 0},
-#line 1174 "effective_tld_names.gperf"
-      {"gos.pk", 0},
-#line 2993 "effective_tld_names.gperf"
-      {"sr", 0},
-#line 880 "effective_tld_names.gperf"
-      {"edu.pl", 0},
-#line 650 "effective_tld_names.gperf"
-      {"com.my", 0},
-#line 1456 "effective_tld_names.gperf"
-      {"im.it", 0},
-#line 605 "effective_tld_names.gperf"
-      {"com.by", 0},
-#line 1182 "effective_tld_names.gperf"
-      {"gov.ae", 0},
-#line 931 "effective_tld_names.gperf"
-      {"eng.br", 0},
-#line 651 "effective_tld_names.gperf"
-      {"com.na", 0},
-#line 2173 "effective_tld_names.gperf"
-      {"net.pl", 0},
-#line 75 "effective_tld_names.gperf"
-      {"ae.org", 0},
-#line 565 "effective_tld_names.gperf"
-      {"co.pn", 0},
-#line 555 "effective_tld_names.gperf"
-      {"co.je", 0},
-#line 2949 "effective_tld_names.gperf"
-      {"sn", 0},
-#line 458 "effective_tld_names.gperf"
-      {"cc.na", 0},
-#line 2248 "effective_tld_names.gperf"
-      {"nom.pl", 0},
-#line 536 "effective_tld_names.gperf"
-      {"cng.br", 0},
-#line 2109 "effective_tld_names.gperf"
-      {"net.ae", 0},
-#line 1159 "effective_tld_names.gperf"
-      {"gob.cl", 0},
-#line 660 "effective_tld_names.gperf"
-      {"com.pl", 0},
-#line 2269 "effective_tld_names.gperf"
-      {"novara.it", 0},
-#line 1236 "effective_tld_names.gperf"
-      {"gov.ly", 0},
-#line 2958 "effective_tld_names.gperf"
-      {"soc.lk", 0},
-#line 1524 "effective_tld_names.gperf"
-      {"irc.pl", 0},
-#line 1149 "effective_tld_names.gperf"
-      {"go.cr", 0},
-#line 864 "effective_tld_names.gperf"
-      {"edu.ly", 0},
-#line 1288 "effective_tld_names.gperf"
-      {"granvin.no", 0},
-#line 2452 "effective_tld_names.gperf"
-      {"org.vc", 0},
-#line 812 "effective_tld_names.gperf"
-      {"ed.cr", 0},
-#line 157 "effective_tld_names.gperf"
-      {"ao.it", 0},
-#line 2157 "effective_tld_names.gperf"
-      {"net.ly", 0},
-#line 257 "effective_tld_names.gperf"
-      {"az.us", 0},
 #line 547 "effective_tld_names.gperf"
-      {"co.cr", 0},
-#line 2999 "effective_tld_names.gperf"
-      {"st", 0},
-#line 1797 "effective_tld_names.gperf"
-      {"lu", 0},
-#line 2364 "effective_tld_names.gperf"
-      {"org.br", 0},
-#line 744 "effective_tld_names.gperf"
-      {"cz.it", 0},
-#line 643 "effective_tld_names.gperf"
-      {"com.ly", 0},
-#line 2354 "effective_tld_names.gperf"
-      {"org.ag", 0},
-#line 1446 "effective_tld_names.gperf"
-      {"idrett.no", 0},
-#line 51 "effective_tld_names.gperf"
-      {"ac.pr", 0},
-#line 97 "effective_tld_names.gperf"
-      {"ai", 0},
-#line 1818 "effective_tld_names.gperf"
-      {"ma", 0},
-#line 168 "effective_tld_names.gperf"
-      {"ar.it", 0},
-#line 1972 "effective_tld_names.gperf"
-      {"mo.it", 0},
-#line 2429 "effective_tld_names.gperf"
-      {"org.pr", 0},
-#line 1868 "effective_tld_names.gperf"
-      {"me.it", 0},
-#line 1208 "effective_tld_names.gperf"
-      {"gov.gg", 0},
-#line 1323 "effective_tld_names.gperf"
-      {"gsm.pl", 0},
-#line 146 "effective_tld_names.gperf"
-      {"an.it", 0},
-#line 1811 "effective_tld_names.gperf"
-      {"lv", 0},
-#line 2405 "effective_tld_names.gperf"
-      {"org.lr", 0},
-#line 2131 "effective_tld_names.gperf"
-      {"net.gg", 0},
-#line 908 "effective_tld_names.gperf"
-      {"edunet.tn", 0},
-#line 3401 "effective_tld_names.gperf"
-      {"ws", 0},
-#line 3377 "effective_tld_names.gperf"
-      {"web.co", 0},
-#line 559 "effective_tld_names.gperf"
-      {"co.ls", 0},
-#line 229 "effective_tld_names.gperf"
-      {"at.it", 0},
+      {"co", 0},
+#line 755 "effective_tld_names.gperf"
+      {"cz", 0},
+#line 1547 "effective_tld_names.gperf"
+      {"io", 0},
+#line 158 "effective_tld_names.gperf"
+      {"ao", 0},
+#line 261 "effective_tld_names.gperf"
+      {"az", 0},
+#line 469 "effective_tld_names.gperf"
+      {"cd", 0},
+#line 597 "effective_tld_names.gperf"
+      {"com", 0},
+#line 1464 "effective_tld_names.gperf"
+      {"id", 2},
+#line 69 "effective_tld_names.gperf"
+      {"ad", 0},
+#line 965 "effective_tld_names.gperf"
+      {"es", 0},
+#line 1556 "effective_tld_names.gperf"
+      {"is", 0},
+#line 200 "effective_tld_names.gperf"
+      {"as", 0},
+#line 827 "effective_tld_names.gperf"
+      {"edu", 0},
 #line 715 "effective_tld_names.gperf"
-      {"council.aero", 0},
-#line 1967 "effective_tld_names.gperf"
-      {"mn.it", 0},
-#line 2336 "effective_tld_names.gperf"
-      {"or.cr", 0},
-#line 3380 "effective_tld_names.gperf"
-      {"web.pk", 0},
-#line 957 "effective_tld_names.gperf"
-      {"eti.br", 0},
-#line 1264 "effective_tld_names.gperf"
-      {"gov.sl", 0},
-#line 34 "effective_tld_names.gperf"
-      {"ac.at", 0},
-#line 3040 "effective_tld_names.gperf"
-      {"su", 0},
-#line 2418 "effective_tld_names.gperf"
-      {"org.my", 0},
-#line 1245 "effective_tld_names.gperf"
-      {"gov.mw", 0},
-#line 893 "effective_tld_names.gperf"
-      {"edu.sl", 0},
-#line 3378 "effective_tld_names.gperf"
-      {"web.lk", 0},
-#line 2006 "effective_tld_names.gperf"
-      {"ms.it", 0},
-#line 54 "effective_tld_names.gperf"
-      {"ac.rw", 0},
-#line 2419 "effective_tld_names.gperf"
-      {"org.na", 0},
-#line 870 "effective_tld_names.gperf"
-      {"edu.mw", 0},
-#line 2011 "effective_tld_names.gperf"
-      {"mt.it", 0},
-#line 185 "effective_tld_names.gperf"
-      {"art.museum", 0},
-#line 2185 "effective_tld_names.gperf"
-      {"net.sl", 0},
-#line 1266 "effective_tld_names.gperf"
-      {"gov.sy", 0},
-#line 2163 "effective_tld_names.gperf"
-      {"net.mw", 0},
-#line 1205 "effective_tld_names.gperf"
-      {"gov.ec", 0},
-#line 895 "effective_tld_names.gperf"
-      {"edu.sy", 0},
-#line 2427 "effective_tld_names.gperf"
-      {"org.pl", 0},
-#line 835 "effective_tld_names.gperf"
-      {"edu.ec", 0},
-#line 673 "effective_tld_names.gperf"
+      {"coop", 0},
+#line 971 "effective_tld_names.gperf"
+      {"et", 2},
+#line 1563 "effective_tld_names.gperf"
+      {"it", 0},
+#line 233 "effective_tld_names.gperf"
+      {"at", 0},
+#line 533 "effective_tld_names.gperf"
+      {"cl", 0},
+#line 1475 "effective_tld_names.gperf"
+      {"il", 2},
+#line 115 "effective_tld_names.gperf"
+      {"al", 0},
+#line 684 "effective_tld_names.gperf"
       {"com.sl", 0},
-#line 648 "effective_tld_names.gperf"
-      {"com.mw", 0},
-#line 2187 "effective_tld_names.gperf"
-      {"net.sy", 0},
-#line 2352 "effective_tld_names.gperf"
-      {"org.ae", 0},
-#line 2129 "effective_tld_names.gperf"
-      {"net.ec", 0},
-#line 429 "effective_tld_names.gperf"
-      {"ca.na", 0},
-#line 40 "effective_tld_names.gperf"
-      {"ac.im", 0},
-#line 1470 "effective_tld_names.gperf"
-      {"ind.tn", 0},
-#line 675 "effective_tld_names.gperf"
-      {"com.sy", 0},
-#line 613 "effective_tld_names.gperf"
-      {"com.ec", 0},
-#line 72 "effective_tld_names.gperf"
-      {"adv.br", 0},
-#line 1268 "effective_tld_names.gperf"
-      {"gov.tl", 0},
-#line 3054 "effective_tld_names.gperf"
-      {"sv", 2},
-#line 2878 "effective_tld_names.gperf"
-      {"sd.us", 0},
-#line 2408 "effective_tld_names.gperf"
-      {"org.ly", 0},
-#line 1509 "effective_tld_names.gperf"
-      {"int.mw", 0},
-#line 3039 "effective_tld_names.gperf"
-      {"stv.ru", 0},
-#line 1207 "effective_tld_names.gperf"
-      {"gov.ge", 0},
-#line 1458 "effective_tld_names.gperf"
-      {"imb.br", 0},
-#line 70 "effective_tld_names.gperf"
-      {"adm.br", 0},
-#line 838 "effective_tld_names.gperf"
-      {"edu.ge", 0},
-#line 1352 "effective_tld_names.gperf"
-      {"hamaroy.no", 0},
-#line 552 "effective_tld_names.gperf"
-      {"co.in", 0},
-#line 1247 "effective_tld_names.gperf"
-      {"gov.ng", 0},
-#line 2130 "effective_tld_names.gperf"
-      {"net.ge", 0},
-#line 873 "effective_tld_names.gperf"
-      {"edu.ng", 0},
-#line 1037 "effective_tld_names.gperf"
-      {"fm", 0},
-#line 182 "effective_tld_names.gperf"
-      {"art.br", 0},
-#line 1280 "effective_tld_names.gperf"
-      {"gq", 0},
-#line 1521 "effective_tld_names.gperf"
-      {"iq", 0},
-#line 617 "effective_tld_names.gperf"
-      {"com.ge", 0},
-#line 2167 "effective_tld_names.gperf"
-      {"net.ng", 0},
-#line 85 "effective_tld_names.gperf"
-      {"ag", 0},
-#line 1258 "effective_tld_names.gperf"
-      {"gov.rw", 0},
-#line 2000 "effective_tld_names.gperf"
-      {"mp", 0},
-#line 887 "effective_tld_names.gperf"
-      {"edu.rw", 0},
-#line 1731 "effective_tld_names.gperf"
-      {"lc", 0},
-#line 653 "effective_tld_names.gperf"
-      {"com.ng", 0},
-#line 2946 "effective_tld_names.gperf"
-      {"sm", 0},
-#line 2179 "effective_tld_names.gperf"
-      {"net.rw", 0},
-#line 938 "effective_tld_names.gperf"
-      {"ens.tn", 0},
-#line 1227 "effective_tld_names.gperf"
-      {"gov.ky", 0},
-#line 667 "effective_tld_names.gperf"
-      {"com.rw", 0},
-#line 2378 "effective_tld_names.gperf"
-      {"org.gg", 0},
-#line 856 "effective_tld_names.gperf"
-      {"edu.ky", 0},
-#line 42 "effective_tld_names.gperf"
-      {"ac.ir", 0},
-#line 1899 "effective_tld_names.gperf"
-      {"mg", 0},
-#line 2149 "effective_tld_names.gperf"
-      {"net.ky", 0},
-#line 810 "effective_tld_names.gperf"
-      {"ed.ao", 0},
-#line 3405 "effective_tld_names.gperf"
-      {"wy.us", 0},
-#line 1109 "effective_tld_names.gperf"
-      {"gda.pl", 0},
-#line 247 "effective_tld_names.gperf"
-      {"av.it", 0},
-#line 2038 "effective_tld_names.gperf"
-      {"na.it", 0},
-#line 3069 "effective_tld_names.gperf"
-      {"sz", 0},
-#line 541 "effective_tld_names.gperf"
+#line 685 "effective_tld_names.gperf"
+      {"com.sn", 0},
+#line 1596 "effective_tld_names.gperf"
+      {"jo", 0},
+#line 908 "effective_tld_names.gperf"
+      {"edu.sl", 0},
+#line 909 "effective_tld_names.gperf"
+      {"edu.sn", 0},
+#line 550 "effective_tld_names.gperf"
       {"co.ao", 0},
-#line 635 "effective_tld_names.gperf"
-      {"com.ky", 0},
-#line 428 "effective_tld_names.gperf"
-      {"ca.it", 0},
-#line 1512 "effective_tld_names.gperf"
-      {"int.rw", 0},
-#line 563 "effective_tld_names.gperf"
-      {"co.mw", 0},
-#line 837 "effective_tld_names.gperf"
-      {"edu.es", 0},
-#line 3633 "effective_tld_names.gperf"
-      {"za.org", 0},
-#line 2242 "effective_tld_names.gperf"
-      {"nom.es", 0},
-#line 615 "effective_tld_names.gperf"
-      {"com.es", 0},
-#line 1819 "effective_tld_names.gperf"
-      {"ma.us", 0},
-#line 2441 "effective_tld_names.gperf"
-      {"org.sl", 0},
-#line 2048 "effective_tld_names.gperf"
-      {"name", 0},
-#line 2416 "effective_tld_names.gperf"
-      {"org.mw", 0},
-#line 2366 "effective_tld_names.gperf"
-      {"org.bw", 0},
-#line 52 "effective_tld_names.gperf"
-      {"ac.rs", 0},
-#line 1160 "effective_tld_names.gperf"
-      {"gob.es", 0},
-#line 1168 "effective_tld_names.gperf"
-      {"gol.no", 0},
-#line 1408 "effective_tld_names.gperf"
-      {"hol.no", 0},
-#line 2443 "effective_tld_names.gperf"
-      {"org.sy", 0},
-#line 1903 "effective_tld_names.gperf"
-      {"mi.us", 0},
-#line 2374 "effective_tld_names.gperf"
-      {"org.ec", 0},
-#line 1184 "effective_tld_names.gperf"
-      {"gov.al", 0},
-#line 2845 "effective_tld_names.gperf"
-      {"sc", 0},
-#line 818 "effective_tld_names.gperf"
-      {"edu.al", 0},
-#line 186 "effective_tld_names.gperf"
-      {"art.pl", 0},
-#line 1535 "effective_tld_names.gperf"
-      {"it.ao", 0},
-#line 59 "effective_tld_names.gperf"
-      {"ac.tz", 0},
-#line 2113 "effective_tld_names.gperf"
-      {"net.al", 0},
-#line 1443 "effective_tld_names.gperf"
-      {"id.lv", 0},
-#line 2470 "effective_tld_names.gperf"
-      {"osteroy.no", 0},
-#line 593 "effective_tld_names.gperf"
-      {"com.al", 0},
-#line 3648 "effective_tld_names.gperf"
-      {"zp.ua", 0},
-#line 1206 "effective_tld_names.gperf"
-      {"gov.ee", 0},
-#line 50 "effective_tld_names.gperf"
-      {"ac.pa", 0},
-#line 2884 "effective_tld_names.gperf"
-      {"sec.ps", 0},
-#line 836 "effective_tld_names.gperf"
-      {"edu.ee", 0},
-#line 1876 "effective_tld_names.gperf"
-      {"med.pa", 0},
-#line 2377 "effective_tld_names.gperf"
-      {"org.ge", 0},
-#line 2283 "effective_tld_names.gperf"
-      {"nt.no", 0},
-#line 1376 "effective_tld_names.gperf"
-      {"hemnes.no", 0},
-#line 1273 "effective_tld_names.gperf"
-      {"gov.tw", 0},
-#line 2420 "effective_tld_names.gperf"
-      {"org.ng", 0},
-#line 1448 "effective_tld_names.gperf"
-      {"idv.tw", 0},
-#line 614 "effective_tld_names.gperf"
-      {"com.ee", 0},
-#line 899 "effective_tld_names.gperf"
-      {"edu.tw", 0},
-#line 962 "effective_tld_names.gperf"
-      {"eu.int", 0},
-#line 1895 "effective_tld_names.gperf"
-      {"meraker.no", 0},
-#line 2194 "effective_tld_names.gperf"
-      {"net.tw", 0},
-#line 1706 "effective_tld_names.gperf"
-      {"la", 0},
-#line 231 "effective_tld_names.gperf"
-      {"atm.pl", 0},
-#line 1773 "effective_tld_names.gperf"
-      {"lo.it", 0},
-#line 2031 "effective_tld_names.gperf"
-      {"mx.na", 0},
 #line 681 "effective_tld_names.gperf"
-      {"com.tw", 0},
-#line 2275 "effective_tld_names.gperf"
-      {"ns.ca", 0},
-#line 1733 "effective_tld_names.gperf"
-      {"le.it", 0},
-#line 1416 "effective_tld_names.gperf"
-      {"horten.no", 0},
-#line 2280 "effective_tld_names.gperf"
-      {"nt.ca", 0},
-#line 143 "effective_tld_names.gperf"
-      {"amursk.ru", 0},
-#line 2399 "effective_tld_names.gperf"
-      {"org.ky", 0},
-#line 1755 "effective_tld_names.gperf"
-      {"li", 0},
-#line 2325 "effective_tld_names.gperf"
-      {"on.ca", 0},
-#line 1212 "effective_tld_names.gperf"
-      {"gov.gr", 0},
-#line 1863 "effective_tld_names.gperf"
-      {"mc.it", 0},
-#line 2376 "effective_tld_names.gperf"
-      {"org.es", 0},
-#line 843 "effective_tld_names.gperf"
-      {"edu.gr", 0},
-#line 2847 "effective_tld_names.gperf"
-      {"sc.kr", 0},
-#line 2134 "effective_tld_names.gperf"
-      {"net.gr", 0},
-#line 622 "effective_tld_names.gperf"
-      {"com.gr", 0},
-#line 1536 "effective_tld_names.gperf"
-      {"its.me", 0},
-#line 497 "effective_tld_names.gperf"
-      {"cim.br", 0},
-#line 1793 "effective_tld_names.gperf"
-      {"lt.it", 0},
-#line 2855 "effective_tld_names.gperf"
-      {"sch.lk", 0},
-#line 60 "effective_tld_names.gperf"
-      {"ac.ug", 0},
-#line 3403 "effective_tld_names.gperf"
-      {"wv.us", 0},
-#line 984 "effective_tld_names.gperf"
-      {"fe.it", 0},
-#line 1331 "effective_tld_names.gperf"
-      {"gv.ao", 0},
-#line 2800 "effective_tld_names.gperf"
-      {"sa", 0},
-#line 2957 "effective_tld_names.gperf"
-      {"so.it", 0},
-#line 2356 "effective_tld_names.gperf"
-      {"org.al", 0},
-#line 49 "effective_tld_names.gperf"
-      {"ac.ng", 0},
-#line 2287 "effective_tld_names.gperf"
-      {"nu.ca", 0},
-#line 2955 "effective_tld_names.gperf"
-      {"snz.ru", 0},
-#line 997 "effective_tld_names.gperf"
-      {"fi", 0},
-#line 1879 "effective_tld_names.gperf"
-      {"med.sa", 0},
-#line 1057 "effective_tld_names.gperf"
-      {"fr.it", 0},
-#line 1088 "effective_tld_names.gperf"
-      {"g.se", 0},
-#line 1436 "effective_tld_names.gperf"
-      {"i.se", 0},
-#line 803 "effective_tld_names.gperf"
-      {"e.se", 0},
-#line 1342 "effective_tld_names.gperf"
-      {"h.se", 0},
-#line 3407 "effective_tld_names.gperf"
-      {"x.se", 0},
-#line 2375 "effective_tld_names.gperf"
-      {"org.ee", 0},
-#line 2910 "effective_tld_names.gperf"
-      {"si", 0},
-#line 1913 "effective_tld_names.gperf"
-      {"mil", 0},
-#line 2036 "effective_tld_names.gperf"
-      {"n.se", 0},
-#line 162 "effective_tld_names.gperf"
-      {"aq", 0},
-#line 2995 "effective_tld_names.gperf"
-      {"sr.it", 0},
-#line 426 "effective_tld_names.gperf"
-      {"c.se", 0},
-#line 2063 "effective_tld_names.gperf"
-      {"napoli.it", 0},
-#line 3402 "effective_tld_names.gperf"
-      {"ws.na", 0},
-#line 595 "effective_tld_names.gperf"
-      {"com.aw", 0},
-#line 973 "effective_tld_names.gperf"
-      {"fam.pk", 0},
-#line 1918 "effective_tld_names.gperf"
-      {"mil.bo", 0},
-#line 2450 "effective_tld_names.gperf"
-      {"org.tw", 0},
-#line 1922 "effective_tld_names.gperf"
-      {"mil.co", 0},
-#line 1921 "effective_tld_names.gperf"
-      {"mil.cn", 0},
-#line 3610 "effective_tld_names.gperf"
-      {"y.se", 0},
-#line 2135 "effective_tld_names.gperf"
-      {"net.gy", 0},
-#line 809 "effective_tld_names.gperf"
-      {"ecn.br", 0},
-#line 1401 "effective_tld_names.gperf"
-      {"hm.no", 0},
-#line 2849 "effective_tld_names.gperf"
-      {"sc.us", 0},
-#line 2001 "effective_tld_names.gperf"
-      {"mq", 0},
-#line 623 "effective_tld_names.gperf"
-      {"com.gy", 0},
-#line 1248 "effective_tld_names.gperf"
-      {"gov.nr", 0},
-#line 2284 "effective_tld_names.gperf"
-      {"nt.ro", 0},
-#line 874 "effective_tld_names.gperf"
-      {"edu.nr", 0},
-#line 2997 "effective_tld_names.gperf"
-      {"ss.it", 0},
-#line 3629 "effective_tld_names.gperf"
-      {"z.se", 0},
-#line 3632 "effective_tld_names.gperf"
-      {"za.net", 0},
-#line 1798 "effective_tld_names.gperf"
-      {"lu.it", 0},
-#line 1780 "effective_tld_names.gperf"
-      {"lom.no", 0},
-#line 2168 "effective_tld_names.gperf"
-      {"net.nr", 0},
-#line 2300 "effective_tld_names.gperf"
-      {"o.se", 0},
-#line 2978 "effective_tld_names.gperf"
-      {"sortland.no", 0},
-#line 654 "effective_tld_names.gperf"
-      {"com.nr", 0},
-#line 2383 "effective_tld_names.gperf"
-      {"org.gr", 0},
-#line 210 "effective_tld_names.gperf"
-      {"ass.km", 0},
-#line 1278 "effective_tld_names.gperf"
-      {"government.aero", 0},
-#line 1468 "effective_tld_names.gperf"
-      {"ind.br", 0},
-#line 1917 "effective_tld_names.gperf"
-      {"mil.ba", 0},
-#line 1708 "effective_tld_names.gperf"
-      {"la.us", 0},
-#line 1054 "effective_tld_names.gperf"
-      {"fot.br", 0},
+      {"com.sc", 0},
+#line 568 "effective_tld_names.gperf"
+      {"co.ls", 0},
+#line 689 "effective_tld_names.gperf"
+      {"com.tn", 0},
+#line 688 "effective_tld_names.gperf"
+      {"com.tj", 0},
+#line 822 "effective_tld_names.gperf"
+      {"ed.ao", 0},
+#line 905 "effective_tld_names.gperf"
+      {"edu.sc", 0},
+#line 650 "effective_tld_names.gperf"
+      {"com.lr", 0},
+#line 912 "effective_tld_names.gperf"
+      {"edu.tj", 0},
 #line 1124 "effective_tld_names.gperf"
-      {"ggf.br", 0},
-#line 560 "effective_tld_names.gperf"
-      {"co.ma", 0},
-#line 33 "effective_tld_names.gperf"
-      {"ac.ae", 0},
-#line 1901 "effective_tld_names.gperf"
-      {"mi.it", 0},
-#line 1108 "effective_tld_names.gperf"
-      {"gd.cn", 0},
-#line 1368 "effective_tld_names.gperf"
-      {"he.cn", 0},
-#line 3264 "effective_tld_names.gperf"
-      {"uy", 2},
-#line 1712 "effective_tld_names.gperf"
-      {"lahppi.no", 0},
-#line 990 "effective_tld_names.gperf"
-      {"fet.no", 0},
-#line 2003 "effective_tld_names.gperf"
-      {"mr.no", 0},
-#line 2692 "effective_tld_names.gperf"
-      {"qa", 2},
-#line 440 "effective_tld_names.gperf"
-      {"can.museum", 0},
-#line 2471 "effective_tld_names.gperf"
-      {"ostre-toten.no", 0},
-#line 1308 "effective_tld_names.gperf"
-      {"gs.jan-mayen.no", 0},
-#line 2996 "effective_tld_names.gperf"
-      {"srv.br", 0},
-#line 1941 "effective_tld_names.gperf"
-      {"mil.ru", 0},
-#line 2898 "effective_tld_names.gperf"
-      {"sg", 0},
-#line 103 "effective_tld_names.gperf"
-      {"air.museum", 0},
-#line 2857 "effective_tld_names.gperf"
-      {"sch.sa", 0},
-#line 1106 "effective_tld_names.gperf"
-      {"gc.ca", 0},
-#line 1403 "effective_tld_names.gperf"
-      {"hn.cn", 0},
-#line 1796 "effective_tld_names.gperf"
-      {"ltd.lk", 0},
-#line 1753 "effective_tld_names.gperf"
-      {"lg.jp", 0},
-#line 1075 "effective_tld_names.gperf"
-      {"fst.br", 0},
-#line 38 "effective_tld_names.gperf"
-      {"ac.cr", 0},
-#line 3243 "effective_tld_names.gperf"
-      {"us", 0},
+      {"gd", 0},
+#line 682 "effective_tld_names.gperf"
+      {"com.sd", 0},
+#line 875 "effective_tld_names.gperf"
+      {"edu.lr", 0},
+#line 1323 "effective_tld_names.gperf"
+      {"gs", 0},
+#line 648 "effective_tld_names.gperf"
+      {"com.lc", 0},
+#line 906 "effective_tld_names.gperf"
+      {"edu.sd", 0},
+#line 551 "effective_tld_names.gperf"
+      {"co.at", 0},
+#line 1564 "effective_tld_names.gperf"
+      {"it.ao", 0},
+#line 873 "effective_tld_names.gperf"
+      {"edu.lc", 0},
+#line 1447 "effective_tld_names.gperf"
+      {"ht", 0},
+#line 1347 "effective_tld_names.gperf"
+      {"gt", 2},
+#line 1154 "effective_tld_names.gperf"
+      {"gl", 0},
+#line 563 "effective_tld_names.gperf"
+      {"co.it", 0},
+#line 756 "effective_tld_names.gperf"
+      {"cz.it", 0},
+#line 159 "effective_tld_names.gperf"
+      {"ao.it", 0},
+#line 1200 "effective_tld_names.gperf"
+      {"gov", 0},
+#line 741 "effective_tld_names.gperf"
+      {"cs.it", 0},
+#line 1557 "effective_tld_names.gperf"
+      {"is.it", 0},
+#line 732 "effective_tld_names.gperf"
+      {"cr", 0},
+#line 962 "effective_tld_names.gperf"
+      {"er", 2},
+#line 1550 "effective_tld_names.gperf"
+      {"ir", 0},
+#line 168 "effective_tld_names.gperf"
+      {"ar", 2},
+#line 742 "effective_tld_names.gperf"
+      {"ct.it", 0},
+#line 234 "effective_tld_names.gperf"
+      {"at.it", 0},
+#line 534 "effective_tld_names.gperf"
+      {"cl.it", 0},
+#line 116 "effective_tld_names.gperf"
+      {"al.it", 0},
+#line 1288 "effective_tld_names.gperf"
+      {"gov.sl", 0},
+#line 127 "effective_tld_names.gperf"
+      {"alta.no", 0},
+#line 925 "effective_tld_names.gperf"
+      {"ee", 0},
+#line 1472 "effective_tld_names.gperf"
+      {"ie", 0},
+#line 75 "effective_tld_names.gperf"
+      {"ae", 0},
+#line 1285 "effective_tld_names.gperf"
+      {"gov.sc", 0},
+#line 1292 "effective_tld_names.gperf"
+      {"gov.tl", 0},
+#line 1293 "effective_tld_names.gperf"
+      {"gov.tn", 0},
+#line 1291 "effective_tld_names.gperf"
+      {"gov.tj", 0},
+#line 680 "effective_tld_names.gperf"
+      {"com.sb", 0},
+#line 1167 "effective_tld_names.gperf"
+      {"go.it", 0},
+#line 1254 "effective_tld_names.gperf"
+      {"gov.lr", 0},
+#line 2272 "effective_tld_names.gperf"
+      {"no", 0},
+#line 2336 "effective_tld_names.gperf"
+      {"nz", 2},
+#line 3678 "effective_tld_names.gperf"
+      {"ye", 2},
+#line 904 "effective_tld_names.gperf"
+      {"edu.sb", 0},
+#line 1286 "effective_tld_names.gperf"
+      {"gov.sd", 0},
+#line 1252 "effective_tld_names.gperf"
+      {"gov.lc", 0},
+#line 1445 "effective_tld_names.gperf"
+      {"hr", 0},
 #line 1304 "effective_tld_names.gperf"
-      {"gs.cn", 0},
-#line 1396 "effective_tld_names.gperf"
-      {"hk", 0},
-#line 3620 "effective_tld_names.gperf"
-      {"yn.cn", 0},
-#line 3055 "effective_tld_names.gperf"
-      {"sv.it", 0},
-#line 932 "effective_tld_names.gperf"
-      {"eng.pro", 0},
-#line 1943 "effective_tld_names.gperf"
-      {"mil.st", 0},
-#line 161 "effective_tld_names.gperf"
-      {"ap.it", 0},
-#line 522 "effective_tld_names.gperf"
-      {"ck", 2},
-#line 439 "effective_tld_names.gperf"
-      {"can.br", 0},
-#line 1215 "effective_tld_names.gperf"
-      {"gov.im", 0},
-#line 1216 "effective_tld_names.gperf"
-      {"gov.in", 0},
-#line 2421 "effective_tld_names.gperf"
-      {"org.nr", 0},
-#line 847 "effective_tld_names.gperf"
-      {"edu.in", 0},
-#line 1220 "effective_tld_names.gperf"
-      {"gov.it", 0},
-#line 1118 "effective_tld_names.gperf"
-      {"genova.it", 0},
+      {"gr", 0},
+#line 647 "effective_tld_names.gperf"
+      {"com.lb", 0},
+#line 622 "effective_tld_names.gperf"
+      {"com.ec", 0},
+#line 1579 "effective_tld_names.gperf"
+      {"je", 0},
+#line 872 "effective_tld_names.gperf"
+      {"edu.lb", 0},
+#line 848 "effective_tld_names.gperf"
+      {"edu.ec", 0},
+#line 548 "effective_tld_names.gperf"
+      {"co.ae", 0},
+#line 562 "effective_tld_names.gperf"
+      {"co.ir", 0},
+#line 2265 "effective_tld_names.gperf"
+      {"nl", 0},
+#line 671 "effective_tld_names.gperf"
+      {"com.pl", 0},
+#line 900 "effective_tld_names.gperf"
+      {"edu.rs", 0},
+#line 672 "effective_tld_names.gperf"
+      {"com.pr", 0},
+#line 1129 "effective_tld_names.gperf"
+      {"ge", 0},
+#line 1465 "effective_tld_names.gperf"
+      {"id.ir", 0},
+#line 895 "effective_tld_names.gperf"
+      {"edu.pl", 0},
+#line 896 "effective_tld_names.gperf"
+      {"edu.pn", 0},
+#line 897 "effective_tld_names.gperf"
+      {"edu.pr", 0},
+#line 624 "effective_tld_names.gperf"
+      {"com.es", 0},
+#line 1608 "effective_tld_names.gperf"
+      {"jp", 0},
+#line 189 "effective_tld_names.gperf"
+      {"art.sn", 0},
+#line 692 "effective_tld_names.gperf"
+      {"com.tw", 0},
+#line 733 "effective_tld_names.gperf"
+      {"cr.it", 0},
 #line 850 "effective_tld_names.gperf"
-      {"edu.it", 0},
-#line 2139 "effective_tld_names.gperf"
-      {"net.im", 0},
-#line 2140 "effective_tld_names.gperf"
+      {"edu.es", 0},
+#line 170 "effective_tld_names.gperf"
+      {"ar.it", 0},
+#line 915 "effective_tld_names.gperf"
+      {"edu.tw", 0},
+#line 236 "effective_tld_names.gperf"
+      {"atm.pl", 0},
+#line 1302 "effective_tld_names.gperf"
+      {"gp", 0},
+#line 673 "effective_tld_names.gperf"
+      {"com.ps", 0},
+#line 470 "effective_tld_names.gperf"
+      {"ce.it", 0},
+#line 898 "effective_tld_names.gperf"
+      {"edu.ps", 0},
+#line 1284 "effective_tld_names.gperf"
+      {"gov.sb", 0},
+#line 220 "effective_tld_names.gperf"
+      {"asso.dz", 0},
+#line 2274 "effective_tld_names.gperf"
+      {"no.it", 0},
+#line 969 "effective_tld_names.gperf"
+      {"est.pr", 0},
+#line 1346 "effective_tld_names.gperf"
+      {"gsm.pl", 0},
+#line 1471 "effective_tld_names.gperf"
+      {"idv.tw", 0},
+#line 1177 "effective_tld_names.gperf"
+      {"gob.es", 0},
+#line 163 "effective_tld_names.gperf"
+      {"ap.it", 0},
+#line 1305 "effective_tld_names.gperf"
+      {"gr.it", 0},
+#line 1251 "effective_tld_names.gperf"
+      {"gov.lb", 0},
+#line 2310 "effective_tld_names.gperf"
+      {"nr", 0},
+#line 1226 "effective_tld_names.gperf"
+      {"gov.ec", 0},
+#line 716 "effective_tld_names.gperf"
+      {"coop.br", 0},
+#line 1280 "effective_tld_names.gperf"
+      {"gov.rs", 0},
+#line 1275 "effective_tld_names.gperf"
+      {"gov.pl", 0},
+#line 1276 "effective_tld_names.gperf"
+      {"gov.pn", 0},
+#line 1277 "effective_tld_names.gperf"
+      {"gov.pr", 0},
+#line 1130 "effective_tld_names.gperf"
+      {"ge.it", 0},
+#line 2124 "effective_tld_names.gperf"
+      {"ne", 0},
+#line 678 "effective_tld_names.gperf"
+      {"com.rw", 0},
+#line 902 "effective_tld_names.gperf"
+      {"edu.rw", 0},
+#line 1296 "effective_tld_names.gperf"
+      {"gov.tw", 0},
+#line 2309 "effective_tld_names.gperf"
+      {"np", 2},
+#line 541 "effective_tld_names.gperf"
+      {"cn", 0},
+#line 1278 "effective_tld_names.gperf"
+      {"gov.ps", 0},
+#line 1484 "effective_tld_names.gperf"
+      {"in", 0},
+#line 147 "effective_tld_names.gperf"
+      {"an", 0},
+#line 570 "effective_tld_names.gperf"
+      {"co.me", 0},
+#line 2285 "effective_tld_names.gperf"
+      {"nom.pl", 0},
+#line 744 "effective_tld_names.gperf"
+      {"cu", 0},
+#line 977 "effective_tld_names.gperf"
+      {"eu", 0},
+#line 2141 "effective_tld_names.gperf"
+      {"net", 0},
+#line 238 "effective_tld_names.gperf"
+      {"au", 2},
+#line 665 "effective_tld_names.gperf"
+      {"com.nr", 0},
+#line 188 "effective_tld_names.gperf"
+      {"art.pl", 0},
+#line 227 "effective_tld_names.gperf"
+      {"asso.re", 0},
+#line 2279 "effective_tld_names.gperf"
+      {"nom.es", 0},
+#line 889 "effective_tld_names.gperf"
+      {"edu.nr", 0},
+#line 3687 "effective_tld_names.gperf"
+      {"yu", 2},
+#line 574 "effective_tld_names.gperf"
+      {"co.pn", 0},
+#line 2222 "effective_tld_names.gperf"
+      {"net.sl", 0},
+#line 1425 "effective_tld_names.gperf"
+      {"hn", 0},
+#line 1163 "effective_tld_names.gperf"
+      {"gn", 0},
+#line 1528 "effective_tld_names.gperf"
+      {"int", 0},
+#line 1282 "effective_tld_names.gperf"
+      {"gov.rw", 0},
+#line 2219 "effective_tld_names.gperf"
+      {"net.sc", 0},
+#line 2227 "effective_tld_names.gperf"
+      {"net.tn", 0},
+#line 561 "effective_tld_names.gperf"
+      {"co.in", 0},
+#line 2226 "effective_tld_names.gperf"
+      {"net.tj", 0},
+#line 576 "effective_tld_names.gperf"
+      {"co.rs", 0},
+#line 2190 "effective_tld_names.gperf"
+      {"net.lr", 0},
+#line 1448 "effective_tld_names.gperf"
+      {"hu", 0},
+#line 1348 "effective_tld_names.gperf"
+      {"gu", 2},
+#line 2220 "effective_tld_names.gperf"
+      {"net.sd", 0},
+#line 2188 "effective_tld_names.gperf"
+      {"net.lc", 0},
+#line 543 "effective_tld_names.gperf"
+      {"cn.it", 0},
+#line 945 "effective_tld_names.gperf"
+      {"en.it", 0},
+#line 148 "effective_tld_names.gperf"
+      {"an.it", 0},
+#line 955 "effective_tld_names.gperf"
+      {"ens.tn", 0},
+#line 1541 "effective_tld_names.gperf"
+      {"int.tj", 0},
+#line 539 "effective_tld_names.gperf"
+      {"cm", 0},
+#line 1479 "effective_tld_names.gperf"
+      {"im", 0},
+#line 132 "effective_tld_names.gperf"
+      {"am", 0},
+#line 1272 "effective_tld_names.gperf"
+      {"gov.nr", 0},
+#line 3706 "effective_tld_names.gperf"
+      {"zm", 2},
+#line 1552 "effective_tld_names.gperf"
+      {"irc.pl", 0},
+#line 1503 "effective_tld_names.gperf"
+      {"info", 0},
+#line 655 "effective_tld_names.gperf"
+      {"com.ml", 0},
+#line 881 "effective_tld_names.gperf"
+      {"edu.ml", 0},
+#line 882 "effective_tld_names.gperf"
+      {"edu.mn", 0},
+#line 566 "effective_tld_names.gperf"
+      {"co.kr", 0},
+#line 1595 "effective_tld_names.gperf"
+      {"jm", 2},
+#line 675 "effective_tld_names.gperf"
+      {"com.re", 0},
+#line 966 "effective_tld_names.gperf"
+      {"es.kr", 0},
+#line 1520 "effective_tld_names.gperf"
+      {"info.ro", 0},
+#line 2218 "effective_tld_names.gperf"
+      {"net.sb", 0},
+#line 1423 "effective_tld_names.gperf"
+      {"hm", 0},
+#line 1161 "effective_tld_names.gperf"
+      {"gm", 0},
+#line 1507 "effective_tld_names.gperf"
+      {"info.co", 0},
+#line 2324 "effective_tld_names.gperf"
+      {"nu", 0},
+#line 623 "effective_tld_names.gperf"
+      {"com.ee", 0},
+#line 560 "effective_tld_names.gperf"
+      {"co.im", 0},
+#line 2187 "effective_tld_names.gperf"
+      {"net.lb", 0},
+#line 2164 "effective_tld_names.gperf"
+      {"net.ec", 0},
+#line 849 "effective_tld_names.gperf"
+      {"edu.ee", 0},
+#line 2210 "effective_tld_names.gperf"
+      {"net.pl", 0},
+#line 2211 "effective_tld_names.gperf"
+      {"net.pn", 0},
+#line 2212 "effective_tld_names.gperf"
+      {"net.pr", 0},
+#line 1169 "effective_tld_names.gperf"
+      {"go.kr", 0},
+#line 667 "effective_tld_names.gperf"
+      {"com.pe", 0},
+#line 1521 "effective_tld_names.gperf"
+      {"info.sd", 0},
+#line 1480 "effective_tld_names.gperf"
+      {"im.it", 0},
+#line 1446 "effective_tld_names.gperf"
+      {"hs.kr", 0},
+#line 891 "effective_tld_names.gperf"
+      {"edu.pe", 0},
+#line 949 "effective_tld_names.gperf"
+      {"eng.pro", 0},
+#line 2230 "effective_tld_names.gperf"
+      {"net.tw", 0},
+#line 571 "effective_tld_names.gperf"
+      {"co.mu", 0},
+#line 1262 "effective_tld_names.gperf"
+      {"gov.ml", 0},
+#line 1263 "effective_tld_names.gperf"
+      {"gov.mn", 0},
+#line 1265 "effective_tld_names.gperf"
+      {"gov.mr", 0},
+#line 2322 "effective_tld_names.gperf"
+      {"nt.ro", 0},
+#line 2213 "effective_tld_names.gperf"
+      {"net.ps", 0},
+#line 1126 "effective_tld_names.gperf"
+      {"gda.pl", 0},
+#line 1518 "effective_tld_names.gperf"
+      {"info.pl", 0},
+#line 2317 "effective_tld_names.gperf"
+      {"nt.au", 0},
+#line 125 "effective_tld_names.gperf"
+      {"algard.no", 0},
+#line 1181 "effective_tld_names.gperf"
+      {"gob.pe", 0},
+#line 1337 "effective_tld_names.gperf"
+      {"gs.oslo.no", 0},
+#line 2326 "effective_tld_names.gperf"
+      {"nu.it", 0},
+#line 1516 "effective_tld_names.gperf"
+      {"info.nr", 0},
+#line 1227 "effective_tld_names.gperf"
+      {"gov.ee", 0},
+#line 436 "effective_tld_names.gperf"
+      {"ca", 0},
+#line 564 "effective_tld_names.gperf"
+      {"co.je", 0},
+#line 659 "effective_tld_names.gperf"
+      {"com.mw", 0},
+#line 1270 "effective_tld_names.gperf"
+      {"gov.nc.tr", 0},
+#line 3691 "effective_tld_names.gperf"
+      {"za", 2},
+#line 885 "effective_tld_names.gperf"
+      {"edu.mw", 0},
+#line 2216 "effective_tld_names.gperf"
+      {"net.rw", 0},
+#line 602 "effective_tld_names.gperf"
+      {"com.al", 0},
+#line 603 "effective_tld_names.gperf"
+      {"com.an", 0},
+#line 250 "effective_tld_names.gperf"
+      {"auto.pl", 0},
+#line 565 "effective_tld_names.gperf"
+      {"co.jp", 0},
+#line 2286 "effective_tld_names.gperf"
+      {"nom.re", 0},
+#line 830 "effective_tld_names.gperf"
+      {"edu.al", 0},
+#line 831 "effective_tld_names.gperf"
+      {"edu.an", 0},
+#line 825 "effective_tld_names.gperf"
+      {"ed.jp", 0},
+#line 598 "effective_tld_names.gperf"
+      {"com.ac", 0},
+#line 70 "effective_tld_names.gperf"
+      {"ad.jp", 0},
+#line 1519 "effective_tld_names.gperf"
+      {"info.pr", 0},
+#line 828 "effective_tld_names.gperf"
+      {"edu.ac", 0},
+#line 1107 "effective_tld_names.gperf"
+      {"ga", 0},
+#line 1540 "effective_tld_names.gperf"
+      {"int.rw", 0},
+#line 460 "effective_tld_names.gperf"
+      {"cat", 0},
+#line 730 "effective_tld_names.gperf"
+      {"cpa.pro", 0},
+#line 2284 "effective_tld_names.gperf"
+      {"nom.pe", 0},
+#line 2205 "effective_tld_names.gperf"
+      {"net.nr", 0},
+#line 1168 "effective_tld_names.gperf"
+      {"go.jp", 0},
+#line 437 "effective_tld_names.gperf"
+      {"ca.it", 0},
+#line 1268 "effective_tld_names.gperf"
+      {"gov.mw", 0},
+#line 1487 "effective_tld_names.gperf"
+      {"in.rs", 0},
+#line 133 "effective_tld_names.gperf"
+      {"am.br", 0},
+#line 1204 "effective_tld_names.gperf"
+      {"gov.al", 0},
+#line 1201 "effective_tld_names.gperf"
+      {"gov.ac", 0},
+#line 1558 "effective_tld_names.gperf"
+      {"isa.us", 0},
+#line 2126 "effective_tld_names.gperf"
+      {"ne.kr", 0},
+#line 2070 "effective_tld_names.gperf"
+      {"na", 0},
+#line 1205 "effective_tld_names.gperf"
+      {"gov.as", 0},
+#line 1549 "effective_tld_names.gperf"
+      {"iq", 0},
+#line 164 "effective_tld_names.gperf"
+      {"aq", 0},
+#line 552 "effective_tld_names.gperf"
+      {"co.ba", 0},
+#line 604 "effective_tld_names.gperf"
+      {"com.aw", 0},
+#line 372 "effective_tld_names.gperf"
+      {"bo", 0},
+#line 431 "effective_tld_names.gperf"
+      {"bz", 0},
+#line 302 "effective_tld_names.gperf"
+      {"bd", 2},
+#line 414 "effective_tld_names.gperf"
+      {"bs", 0},
+#line 2196 "effective_tld_names.gperf"
+      {"net.ml", 0},
+#line 1306 "effective_tld_names.gperf"
+      {"gr.jp", 0},
+#line 569 "effective_tld_names.gperf"
+      {"co.ma", 0},
+#line 679 "effective_tld_names.gperf"
+      {"com.sa", 0},
+#line 416 "effective_tld_names.gperf"
+      {"bt", 2},
+#line 2275 "effective_tld_names.gperf"
+      {"nom.ad", 0},
+#line 878 "effective_tld_names.gperf"
+      {"edu.me", 0},
+#line 903 "effective_tld_names.gperf"
+      {"edu.sa", 0},
+#line 1303 "effective_tld_names.gperf"
+      {"gq", 0},
+#line 646 "effective_tld_names.gperf"
+      {"com.la", 0},
+#line 871 "effective_tld_names.gperf"
+      {"edu.la", 0},
+#line 649 "effective_tld_names.gperf"
+      {"com.lk", 0},
+#line 117 "effective_tld_names.gperf"
+      {"al.no", 0},
+#line 874 "effective_tld_names.gperf"
+      {"edu.lk", 0},
+#line 2071 "effective_tld_names.gperf"
+      {"na.it", 0},
+#line 2102 "effective_tld_names.gperf"
+      {"nat.tn", 0},
+#line 2207 "effective_tld_names.gperf"
+      {"net.pe", 0},
+#line 165 "effective_tld_names.gperf"
+      {"aq.it", 0},
+#line 373 "effective_tld_names.gperf"
+      {"bo.it", 0},
+#line 432 "effective_tld_names.gperf"
+      {"bz.it", 0},
+#line 1565 "effective_tld_names.gperf"
+      {"its.me", 0},
+#line 415 "effective_tld_names.gperf"
+      {"bs.it", 0},
+#line 1422 "effective_tld_names.gperf"
+      {"hl.no", 0},
+#line 390 "effective_tld_names.gperf"
+      {"br", 0},
+#line 1259 "effective_tld_names.gperf"
+      {"gov.me", 0},
+#line 1283 "effective_tld_names.gperf"
+      {"gov.sa", 0},
+#line 365 "effective_tld_names.gperf"
+      {"bl.it", 0},
+#line 304 "effective_tld_names.gperf"
+      {"be", 0},
+#line 2125 "effective_tld_names.gperf"
+      {"ne.jp", 0},
+#line 1250 "effective_tld_names.gperf"
+      {"gov.la", 0},
+#line 1253 "effective_tld_names.gperf"
+      {"gov.lk", 0},
+#line 181 "effective_tld_names.gperf"
+      {"arna.no", 0},
+#line 2200 "effective_tld_names.gperf"
+      {"net.mw", 0},
+#line 666 "effective_tld_names.gperf"
+      {"com.pa", 0},
+#line 890 "effective_tld_names.gperf"
+      {"edu.pa", 0},
+#line 2147 "effective_tld_names.gperf"
+      {"net.al", 0},
+#line 2148 "effective_tld_names.gperf"
+      {"net.an", 0},
+#line 670 "effective_tld_names.gperf"
+      {"com.pk", 0},
+#line 894 "effective_tld_names.gperf"
+      {"edu.pk", 0},
+#line 2142 "effective_tld_names.gperf"
+      {"net.ac", 0},
+#line 1537 "effective_tld_names.gperf"
+      {"int.mw", 0},
+#line 2321 "effective_tld_names.gperf"
+      {"nt.no", 0},
+#line 392 "effective_tld_names.gperf"
+      {"br.it", 0},
+#line 2267 "effective_tld_names.gperf"
+      {"nl.no", 0},
+#line 757 "effective_tld_names.gperf"
+      {"czeladz.pl", 0},
+#line 752 "effective_tld_names.gperf"
+      {"cy", 2},
+#line 1180 "effective_tld_names.gperf"
+      {"gob.pa", 0},
+#line 434 "effective_tld_names.gperf"
+      {"c.la", 0},
+#line 1182 "effective_tld_names.gperf"
+      {"gob.pk", 0},
+#line 1467 "effective_tld_names.gperf"
+      {"id.ly", 0},
+#line 1359 "effective_tld_names.gperf"
+      {"gy", 0},
+#line 1274 "effective_tld_names.gperf"
+      {"gov.pk", 0},
+#line 1202 "effective_tld_names.gperf"
+      {"gov.ae", 0},
+#line 1191 "effective_tld_names.gperf"
+      {"gos.pk", 0},
+#line 1512 "effective_tld_names.gperf"
+      {"info.la", 0},
+#line 1514 "effective_tld_names.gperf"
+      {"info.na", 0},
+#line 370 "effective_tld_names.gperf"
+      {"bn", 2},
+#line 2283 "effective_tld_names.gperf"
+      {"nom.pa", 0},
+#line 662 "effective_tld_names.gperf"
+      {"com.na", 0},
+#line 504 "effective_tld_names.gperf"
+      {"ci", 0},
+#line 99 "effective_tld_names.gperf"
+      {"ai", 0},
+#line 693 "effective_tld_names.gperf"
+      {"com.ua", 0},
+#line 2194 "effective_tld_names.gperf"
+      {"net.me", 0},
+#line 1494 "effective_tld_names.gperf"
+      {"ind.tn", 0},
+#line 2217 "effective_tld_names.gperf"
+      {"net.sa", 0},
+#line 916 "effective_tld_names.gperf"
+      {"edu.ua", 0},
+#line 230 "effective_tld_names.gperf"
+      {"asti.it", 0},
+#line 860 "effective_tld_names.gperf"
+      {"edu.in", 0},
+#line 144 "effective_tld_names.gperf"
+      {"amur.ru", 0},
+#line 2186 "effective_tld_names.gperf"
+      {"net.la", 0},
+#line 702 "effective_tld_names.gperf"
+      {"como.it", 0},
+#line 2189 "effective_tld_names.gperf"
+      {"net.lk", 0},
+#line 1143 "effective_tld_names.gperf"
+      {"gi", 0},
+#line 2303 "effective_tld_names.gperf"
+      {"notodden.no", 0},
+#line 639 "effective_tld_names.gperf"
+      {"com.is", 0},
+#line 371 "effective_tld_names.gperf"
+      {"bn.it", 0},
+#line 1534 "effective_tld_names.gperf"
+      {"int.la", 0},
+#line 862 "effective_tld_names.gperf"
+      {"edu.is", 0},
+#line 638 "effective_tld_names.gperf"
+      {"com.iq", 0},
+#line 1535 "effective_tld_names.gperf"
+      {"int.lk", 0},
+#line 861 "effective_tld_names.gperf"
+      {"edu.iq", 0},
+#line 368 "effective_tld_names.gperf"
+      {"bm", 0},
+#line 1561 "effective_tld_names.gperf"
+      {"isla.pr", 0},
+#line 537 "effective_tld_names.gperf"
+      {"club.aero", 0},
+#line 1297 "effective_tld_names.gperf"
+      {"gov.ua", 0},
+#line 654 "effective_tld_names.gperf"
+      {"com.mk", 0},
+#line 1237 "effective_tld_names.gperf"
+      {"gov.in", 0},
+#line 1239 "effective_tld_names.gperf"
+      {"gov.ir", 0},
+#line 880 "effective_tld_names.gperf"
+      {"edu.mk", 0},
+#line 1505 "effective_tld_names.gperf"
+      {"info.az", 0},
+#line 718 "effective_tld_names.gperf"
+      {"coop.km", 0},
+#line 1504 "effective_tld_names.gperf"
+      {"info.at", 0},
+#line 2252 "effective_tld_names.gperf"
+      {"ni", 2},
+#line 1240 "effective_tld_names.gperf"
+      {"gov.is", 0},
+#line 738 "effective_tld_names.gperf"
+      {"crew.aero", 0},
+#line 2206 "effective_tld_names.gperf"
+      {"net.pa", 0},
+#line 1308 "effective_tld_names.gperf"
+      {"gran.no", 0},
+#line 1160 "effective_tld_names.gperf"
+      {"gloppen.no", 0},
+#line 1424 "effective_tld_names.gperf"
+      {"hm.no", 0},
+#line 553 "effective_tld_names.gperf"
+      {"co.bi", 0},
+#line 1238 "effective_tld_names.gperf"
+      {"gov.iq", 0},
+#line 1525 "effective_tld_names.gperf"
+      {"ing.pa", 0},
+#line 2209 "effective_tld_names.gperf"
+      {"net.pk", 0},
+#line 2143 "effective_tld_names.gperf"
+      {"net.ae", 0},
+#line 1058 "effective_tld_names.gperf"
+      {"fo", 0},
+#line 2137 "effective_tld_names.gperf"
+      {"nesodden.no", 0},
+#line 1368 "effective_tld_names.gperf"
+      {"habmer.no", 0},
+#line 1258 "effective_tld_names.gperf"
+      {"gov.ma", 0},
+#line 2335 "effective_tld_names.gperf"
+      {"nysa.pl", 0},
+#line 1261 "effective_tld_names.gperf"
+      {"gov.mk", 0},
+#line 2314 "effective_tld_names.gperf"
+      {"nsn.us", 0},
+#line 219 "effective_tld_names.gperf"
+      {"asso.ci", 0},
+#line 690 "effective_tld_names.gperf"
+      {"com.to", 0},
+#line 1587 "effective_tld_names.gperf"
+      {"jet.uk", 1},
+#line 913 "effective_tld_names.gperf"
+      {"edu.to", 0},
+#line 2268 "effective_tld_names.gperf"
+      {"nls.uk", 1},
+#line 265 "effective_tld_names.gperf"
+      {"ba", 0},
+#line 573 "effective_tld_names.gperf"
+      {"co.na", 0},
+#line 28 "effective_tld_names.gperf"
+      {"aa.no", 0},
+#line 1073 "effective_tld_names.gperf"
+      {"fr", 0},
+#line 224 "effective_tld_names.gperf"
+      {"asso.km", 0},
+#line 204 "effective_tld_names.gperf"
+      {"aseral.no", 0},
+#line 1199 "effective_tld_names.gperf"
+      {"gouv.sn", 0},
+#line 1307 "effective_tld_names.gperf"
+      {"grajewo.pl", 0},
+#line 676 "effective_tld_names.gperf"
+      {"com.ro", 0},
+#line 1294 "effective_tld_names.gperf"
+      {"gov.to", 0},
+#line 1367 "effective_tld_names.gperf"
+      {"ha.no", 0},
+#line 2231 "effective_tld_names.gperf"
+      {"net.ua", 0},
+#line 266 "effective_tld_names.gperf"
+      {"ba.it", 0},
+#line 2175 "effective_tld_names.gperf"
       {"net.in", 0},
-#line 1855 "effective_tld_names.gperf"
-      {"mat.br", 0},
-#line 628 "effective_tld_names.gperf"
-      {"com.io", 0},
-#line 86 "effective_tld_names.gperf"
-      {"ag.it", 0},
-#line 2881 "effective_tld_names.gperf"
-      {"se.net", 0},
-#line 1732 "effective_tld_names.gperf"
-      {"lc.it", 0},
-#line 1946 "effective_tld_names.gperf"
-      {"mil.to", 0},
-#line 1824 "effective_tld_names.gperf"
-      {"magazine.aero", 0},
-#line 1980 "effective_tld_names.gperf"
-      {"modalen.no", 0},
+#line 2177 "effective_tld_names.gperf"
+      {"net.ir", 0},
+#line 2256 "effective_tld_names.gperf"
+      {"nic.tj", 0},
+#line 141 "effective_tld_names.gperf"
+      {"amli.no", 0},
+#line 1399 "effective_tld_names.gperf"
+      {"hemnes.no", 0},
+#line 1074 "effective_tld_names.gperf"
+      {"fr.it", 0},
+#line 2178 "effective_tld_names.gperf"
+      {"net.is", 0},
+#line 2176 "effective_tld_names.gperf"
+      {"net.iq", 0},
+#line 1001 "effective_tld_names.gperf"
+      {"fe.it", 0},
+#line 1533 "effective_tld_names.gperf"
+      {"int.is", 0},
+#line 2193 "effective_tld_names.gperf"
+      {"net.ma", 0},
+#line 1548 "effective_tld_names.gperf"
+      {"ip6.arpa", 0},
+#line 1502 "effective_tld_names.gperf"
+      {"inf.mk", 0},
+#line 2195 "effective_tld_names.gperf"
+      {"net.mk", 0},
+#line 78 "effective_tld_names.gperf"
+      {"aero", 0},
+#line 1235 "effective_tld_names.gperf"
+      {"gov.ie", 0},
+#line 2287 "effective_tld_names.gperf"
+      {"nom.ro", 0},
+#line 1081 "effective_tld_names.gperf"
+      {"frei.no", 0},
+#line 601 "effective_tld_names.gperf"
+      {"com.ai", 0},
+#line 1119 "effective_tld_names.gperf"
+      {"gaular.no", 0},
+#line 816 "effective_tld_names.gperf"
+      {"e164.arpa", 0},
+#line 376 "effective_tld_names.gperf"
+      {"bodo.no", 0},
+#line 2228 "effective_tld_names.gperf"
+      {"net.to", 0},
+#line 2073 "effective_tld_names.gperf"
+      {"nacion.ar", 1},
+#line 1428 "effective_tld_names.gperf"
+      {"hof.no", 0},
+#line 1486 "effective_tld_names.gperf"
+      {"in.na", 0},
+#line 476 "effective_tld_names.gperf"
+      {"ch", 0},
+#line 1054 "effective_tld_names.gperf"
+      {"fm", 0},
+#line 1101 "effective_tld_names.gperf"
+      {"fusa.no", 0},
+#line 634 "effective_tld_names.gperf"
+      {"com.hn", 0},
+#line 635 "effective_tld_names.gperf"
+      {"com.hr", 0},
+#line 858 "effective_tld_names.gperf"
+      {"edu.hn", 0},
+#line 426 "effective_tld_names.gperf"
+      {"by", 0},
+#line 656 "effective_tld_names.gperf"
+      {"com.mo", 0},
+#line 883 "effective_tld_names.gperf"
+      {"edu.mo", 0},
+#line 1142 "effective_tld_names.gperf"
+      {"gh", 0},
+#line 1186 "effective_tld_names.gperf"
+      {"gon.pk", 0},
+#line 435 "effective_tld_names.gperf"
+      {"c.se", 0},
+#line 815 "effective_tld_names.gperf"
+      {"e.se", 0},
+#line 1459 "effective_tld_names.gperf"
+      {"i.se", 0},
+#line 27 "effective_tld_names.gperf"
+      {"a.se", 0},
+#line 3462 "effective_tld_names.gperf"
+      {"x.se", 0},
+#line 1178 "effective_tld_names.gperf"
+      {"gob.hn", 0},
+#line 3690 "effective_tld_names.gperf"
+      {"z.se", 0},
+#line 3671 "effective_tld_names.gperf"
+      {"y.se", 0},
+#line 477 "effective_tld_names.gperf"
+      {"ch.it", 0},
+#line 2271 "effective_tld_names.gperf"
+      {"nnov.ru", 0},
+#line 1365 "effective_tld_names.gperf"
+      {"h.se", 0},
+#line 1105 "effective_tld_names.gperf"
+      {"g.se", 0},
+#line 1264 "effective_tld_names.gperf"
+      {"gov.mo", 0},
+#line 2000 "effective_tld_names.gperf"
+      {"mo", 0},
+#line 2067 "effective_tld_names.gperf"
+      {"mz", 2},
+#line 1893 "effective_tld_names.gperf"
+      {"md", 0},
+#line 2036 "effective_tld_names.gperf"
+      {"ms", 0},
+#line 1615 "effective_tld_names.gperf"
+      {"jur.pro", 0},
+#line 2041 "effective_tld_names.gperf"
+      {"mt", 2},
+#line 329 "effective_tld_names.gperf"
+      {"bi", 0},
+#line 1995 "effective_tld_names.gperf"
+      {"ml", 0},
+#line 2006 "effective_tld_names.gperf"
+      {"mobi", 0},
+#line 3456 "effective_tld_names.gperf"
+      {"ws", 0},
+#line 417 "effective_tld_names.gperf"
+      {"bu.no", 0},
+#line 651 "effective_tld_names.gperf"
+      {"com.lv", 0},
+#line 1322 "effective_tld_names.gperf"
+      {"grue.no", 0},
+#line 876 "effective_tld_names.gperf"
+      {"edu.lv", 0},
+#line 951 "effective_tld_names.gperf"
+      {"engine.aero", 0},
+#line 2069 "effective_tld_names.gperf"
+      {"n.se", 0},
+#line 2146 "effective_tld_names.gperf"
+      {"net.ai", 0},
+#line 2003 "effective_tld_names.gperf"
+      {"mo.it", 0},
+#line 3319 "effective_tld_names.gperf"
+      {"uz", 0},
+#line 2037 "effective_tld_names.gperf"
+      {"ms.it", 0},
+#line 3296 "effective_tld_names.gperf"
+      {"us", 0},
+#line 2033 "effective_tld_names.gperf"
+      {"mr", 0},
+#line 2042 "effective_tld_names.gperf"
+      {"mt.it", 0},
+#line 330 "effective_tld_names.gperf"
+      {"bi.it", 0},
+#line 2288 "effective_tld_names.gperf"
+      {"nome.pt", 0},
+#line 1896 "effective_tld_names.gperf"
+      {"me", 0},
+#line 438 "effective_tld_names.gperf"
+      {"ca.na", 0},
+#line 440 "effective_tld_names.gperf"
+      {"caa.aero", 0},
+#line 1256 "effective_tld_names.gperf"
+      {"gov.lv", 0},
+#line 1055 "effective_tld_names.gperf"
+      {"fm.br", 0},
+#line 2031 "effective_tld_names.gperf"
+      {"mp", 0},
+#line 346 "effective_tld_names.gperf"
+      {"biz", 0},
+#line 2253 "effective_tld_names.gperf"
+      {"nic.ar", 1},
+#line 145 "effective_tld_names.gperf"
+      {"amursk.ru", 0},
+#line 3269 "effective_tld_names.gperf"
+      {"ud.it", 0},
+#line 101 "effective_tld_names.gperf"
+      {"aid.pl", 0},
+#line 2172 "effective_tld_names.gperf"
+      {"net.hn", 0},
+#line 3436 "effective_tld_names.gperf"
+      {"web.tj", 0},
+#line 1897 "effective_tld_names.gperf"
+      {"me.it", 0},
+#line 357 "effective_tld_names.gperf"
+      {"biz.tj", 0},
+#line 2197 "effective_tld_names.gperf"
+      {"net.mo", 0},
+#line 1133 "effective_tld_names.gperf"
+      {"gen.in", 0},
+#line 587 "effective_tld_names.gperf"
+      {"co.vi", 0},
+#line 954 "effective_tld_names.gperf"
+      {"enna.it", 0},
+#line 178 "effective_tld_names.gperf"
+      {"arendal.no", 0},
+#line 1127 "effective_tld_names.gperf"
+      {"gdansk.pl", 0},
+#line 737 "effective_tld_names.gperf"
+      {"cremona.it", 0},
+#line 1997 "effective_tld_names.gperf"
+      {"mn", 0},
+#line 952 "effective_tld_names.gperf"
+      {"engineer.aero", 0},
+#line 970 "effective_tld_names.gperf"
+      {"estate.museum", 0},
+#line 2044 "effective_tld_names.gperf"
+      {"mu", 0},
+#line 1193 "effective_tld_names.gperf"
+      {"gouv.ci", 0},
+#line 482 "effective_tld_names.gperf"
+      {"chel.ru", 0},
+#line 1493 "effective_tld_names.gperf"
+      {"ind.in", 0},
+#line 355 "effective_tld_names.gperf"
+      {"biz.pl", 0},
+#line 356 "effective_tld_names.gperf"
+      {"biz.pr", 0},
+#line 404 "effective_tld_names.gperf"
+      {"broker.aero", 0},
+#line 285 "effective_tld_names.gperf"
+      {"bamble.no", 0},
+#line 2191 "effective_tld_names.gperf"
+      {"net.lv", 0},
+#line 2098 "effective_tld_names.gperf"
+      {"nara.jp", 2},
+#line 2306 "effective_tld_names.gperf"
+      {"novara.it", 0},
+#line 1998 "effective_tld_names.gperf"
+      {"mn.it", 0},
+#line 1196 "effective_tld_names.gperf"
+      {"gouv.km", 0},
+#line 1996 "effective_tld_names.gperf"
+      {"mm", 2},
+#line 658 "effective_tld_names.gperf"
+      {"com.mv", 0},
+#line 749 "effective_tld_names.gperf"
+      {"cv", 0},
+#line 884 "effective_tld_names.gperf"
+      {"edu.mv", 0},
+#line 2038 "effective_tld_names.gperf"
+      {"ms.kr", 0},
+#line 697 "effective_tld_names.gperf"
+      {"com.vn", 0},
+#line 918 "effective_tld_names.gperf"
+      {"edu.vn", 0},
+#line 205 "effective_tld_names.gperf"
+      {"asia", 0},
+#line 695 "effective_tld_names.gperf"
+      {"com.vc", 0},
+#line 579 "effective_tld_names.gperf"
+      {"co.sz", 0},
+#line 1511 "effective_tld_names.gperf"
+      {"info.ki", 0},
+#line 917 "effective_tld_names.gperf"
+      {"edu.vc", 0},
+#line 1466 "effective_tld_names.gperf"
+      {"id.lv", 0},
+#line 1597 "effective_tld_names.gperf"
+      {"jobs", 0},
+#line 353 "effective_tld_names.gperf"
+      {"biz.nr", 0},
+#line 578 "effective_tld_names.gperf"
+      {"co.st", 0},
 #line 1087 "effective_tld_names.gperf"
-      {"g.bg", 0},
-#line 1434 "effective_tld_names.gperf"
-      {"i.bg", 0},
-#line 802 "effective_tld_names.gperf"
+      {"froland.no", 0},
+#line 1605 "effective_tld_names.gperf"
+      {"journal.aero", 0},
+#line 2133 "effective_tld_names.gperf"
+      {"nel.uk", 1},
+#line 1267 "effective_tld_names.gperf"
+      {"gov.mv", 0},
+#line 252 "effective_tld_names.gperf"
+      {"av.it", 0},
+#line 407 "effective_tld_names.gperf"
+      {"brumunddal.no", 0},
+#line 1014 "effective_tld_names.gperf"
+      {"fi", 0},
+#line 1354 "effective_tld_names.gperf"
+      {"gv.ao", 0},
+#line 1299 "effective_tld_names.gperf"
+      {"gov.vn", 0},
+#line 637 "effective_tld_names.gperf"
+      {"com.io", 0},
+#line 1355 "effective_tld_names.gperf"
+      {"gv.at", 0},
+#line 1298 "effective_tld_names.gperf"
+      {"gov.vc", 0},
+#line 1847 "effective_tld_names.gperf"
+      {"ma", 0},
+#line 2081 "effective_tld_names.gperf"
+      {"name", 0},
+#line 328 "effective_tld_names.gperf"
+      {"bh", 0},
+#line 668 "effective_tld_names.gperf"
+      {"com.pf", 0},
+#line 2084 "effective_tld_names.gperf"
+      {"name.jo", 0},
+#line 892 "effective_tld_names.gperf"
+      {"edu.pf", 0},
+#line 1016 "effective_tld_names.gperf"
+      {"fi.it", 0},
+#line 633 "effective_tld_names.gperf"
+      {"com.hk", 0},
+#line 98 "effective_tld_names.gperf"
+      {"ah.no", 0},
+#line 198 "effective_tld_names.gperf"
+      {"arts.ro", 0},
+#line 857 "effective_tld_names.gperf"
+      {"edu.hk", 0},
+#line 2008 "effective_tld_names.gperf"
+      {"mobi.na", 0},
+#line 195 "effective_tld_names.gperf"
+      {"arts.co", 0},
+#line 1056 "effective_tld_names.gperf"
+      {"fm.no", 0},
+#line 3267 "effective_tld_names.gperf"
+      {"ua", 0},
+#line 975 "effective_tld_names.gperf"
+      {"etne.no", 0},
+#line 447 "effective_tld_names.gperf"
+      {"campobasso.it", 0},
+#line 2255 "effective_tld_names.gperf"
+      {"nic.in", 0},
+#line 264 "effective_tld_names.gperf"
+      {"b.se", 0},
+#line 1470 "effective_tld_names.gperf"
+      {"idv.hk", 0},
+#line 29 "effective_tld_names.gperf"
+      {"aarborte.no", 0},
+#line 1002 "effective_tld_names.gperf"
+      {"fed.us", 0},
+#line 1234 "effective_tld_names.gperf"
+      {"gov.hk", 0},
+#line 2032 "effective_tld_names.gperf"
+      {"mq", 0},
+#line 243 "effective_tld_names.gperf"
+      {"aurland.no", 0},
+#line 352 "effective_tld_names.gperf"
+      {"biz.mw", 0},
+#line 221 "effective_tld_names.gperf"
+      {"asso.fr", 0},
+#line 2199 "effective_tld_names.gperf"
+      {"net.mv", 0},
+#line 1524 "effective_tld_names.gperf"
+      {"info.vn", 0},
+#line 1070 "effective_tld_names.gperf"
+      {"fosnes.no", 0},
+#line 663 "effective_tld_names.gperf"
+      {"com.nf", 0},
+#line 2089 "effective_tld_names.gperf"
+      {"name.pr", 0},
+#line 2234 "effective_tld_names.gperf"
+      {"net.vn", 0},
+#line 1536 "effective_tld_names.gperf"
+      {"int.mv", 0},
+#line 1431 "effective_tld_names.gperf"
+      {"hol.no", 0},
+#line 1185 "effective_tld_names.gperf"
+      {"gol.no", 0},
+#line 2232 "effective_tld_names.gperf"
+      {"net.vc", 0},
+#line 211 "effective_tld_names.gperf"
+      {"asn.lv", 0},
+#line 953 "effective_tld_names.gperf"
+      {"england.museum", 0},
+#line 1543 "effective_tld_names.gperf"
+      {"int.vn", 0},
+#line 210 "effective_tld_names.gperf"
+      {"asmatart.museum", 0},
+#line 428 "effective_tld_names.gperf"
+      {"bygland.no", 0},
+#line 2034 "effective_tld_names.gperf"
+      {"mr.no", 0},
+#line 167 "effective_tld_names.gperf"
+      {"aquila.it", 0},
+#line 3437 "effective_tld_names.gperf"
+      {"wegrow.pl", 0},
+#line 1432 "effective_tld_names.gperf"
+      {"hole.no", 0},
+#line 1603 "effective_tld_names.gperf"
+      {"jorpeland.no", 0},
+#line 1093 "effective_tld_names.gperf"
+      {"fuel.aero", 0},
+#line 1909 "effective_tld_names.gperf"
+      {"med.sd", 0},
+#line 1433 "effective_tld_names.gperf"
+      {"holmestrand.no", 0},
+#line 2065 "effective_tld_names.gperf"
+      {"my", 0},
+#line 2171 "effective_tld_names.gperf"
+      {"net.hk", 0},
+#line 216 "effective_tld_names.gperf"
+      {"assisi.museum", 0},
+#line 149 "effective_tld_names.gperf"
+      {"ancona.it", 0},
+#line 3433 "effective_tld_names.gperf"
+      {"web.lk", 0},
+#line 990 "effective_tld_names.gperf"
+      {"fam.pk", 0},
+#line 1190 "effective_tld_names.gperf"
+      {"gorlice.pl", 0},
+#line 286 "effective_tld_names.gperf"
+      {"bar.pro", 0},
+#line 3417 "effective_tld_names.gperf"
+      {"wa.au", 0},
+#line 2026 "effective_tld_names.gperf"
+      {"moskenes.no", 0},
+#line 374 "effective_tld_names.gperf"
+      {"bo.nordland.no", 0},
+#line 1901 "effective_tld_names.gperf"
+      {"med.ec", 0},
+#line 1906 "effective_tld_names.gperf"
+      {"med.pl", 0},
+#line 3317 "effective_tld_names.gperf"
+      {"uy", 2},
+#line 1907 "effective_tld_names.gperf"
+      {"med.pro", 0},
+#line 599 "effective_tld_names.gperf"
+      {"com.af", 0},
+#line 829 "effective_tld_names.gperf"
+      {"edu.af", 0},
+#line 1588 "effective_tld_names.gperf"
+      {"jevnaker.no", 0},
+#line 3435 "effective_tld_names.gperf"
+      {"web.pk", 0},
+#line 354 "effective_tld_names.gperf"
+      {"biz.pk", 0},
+#line 2203 "effective_tld_names.gperf"
+      {"net.nf", 0},
+#line 1017 "effective_tld_names.gperf"
+      {"fie.ee", 0},
+#line 1203 "effective_tld_names.gperf"
+      {"gov.af", 0},
+#line 83 "effective_tld_names.gperf"
+      {"aerodrome.aero", 0},
+#line 3286 "effective_tld_names.gperf"
+      {"unbi.ba", 0},
+#line 2251 "effective_tld_names.gperf"
+      {"nhs.uk", 1},
+#line 1444 "effective_tld_names.gperf"
+      {"hoylandet.no", 0},
+#line 1930 "effective_tld_names.gperf"
+      {"mi.it", 0},
+#line 3292 "effective_tld_names.gperf"
+      {"unsa.ba", 0},
+#line 989 "effective_tld_names.gperf"
+      {"f.se", 0},
+#line 2088 "effective_tld_names.gperf"
+      {"name.na", 0},
+#line 1007 "effective_tld_names.gperf"
+      {"fet.no", 0},
+#line 1098 "effective_tld_names.gperf"
+      {"fuoisku.no", 0},
+#line 124 "effective_tld_names.gperf"
+      {"alesund.no", 0},
+#line 1454 "effective_tld_names.gperf"
+      {"hvaler.no", 0},
+#line 950 "effective_tld_names.gperf"
+      {"engerdal.no", 0},
+#line 1455 "effective_tld_names.gperf"
+      {"hyllestad.no", 0},
+#line 2104 "effective_tld_names.gperf"
+      {"national.museum", 0},
+#line 1024 "effective_tld_names.gperf"
+      {"fin.tn", 0},
+#line 172 "effective_tld_names.gperf"
+      {"arboretum.museum", 0},
+#line 1902 "effective_tld_names.gperf"
+      {"med.ee", 0},
+#line 2082 "effective_tld_names.gperf"
+      {"name.az", 0},
+#line 303 "effective_tld_names.gperf"
+      {"bd.se", 0},
+#line 2144 "effective_tld_names.gperf"
+      {"net.af", 0},
+#line 1042 "effective_tld_names.gperf"
+      {"fla.no", 0},
+#line 3314 "effective_tld_names.gperf"
+      {"utsira.no", 0},
+#line 3457 "effective_tld_names.gperf"
+      {"ws.na", 0},
+#line 403 "effective_tld_names.gperf"
+      {"broadcast.museum", 0},
+#line 736 "effective_tld_names.gperf"
+      {"creation.museum", 0},
+#line 696 "effective_tld_names.gperf"
+      {"com.vi", 0},
+#line 239 "effective_tld_names.gperf"
+      {"audnedaln.no", 0},
+#line 1023 "effective_tld_names.gperf"
+      {"fin.ec", 0},
+#line 734 "effective_tld_names.gperf"
+      {"crafts.museum", 0},
+#line 3298 "effective_tld_names.gperf"
+      {"us.na", 0},
+#line 109 "effective_tld_names.gperf"
+      {"airport.aero", 0},
+#line 2025 "effective_tld_names.gperf"
+      {"mosjoen.no", 0},
+#line 242 "effective_tld_names.gperf"
+      {"aure.no", 0},
+#line 1526 "effective_tld_names.gperf"
+      {"ingatlan.hu", 0},
+#line 1568 "effective_tld_names.gperf"
+      {"iveland.no", 0},
+#line 314 "effective_tld_names.gperf"
+      {"belluno.it", 0},
+#line 1375 "effective_tld_names.gperf"
+      {"hamaroy.no", 0},
+#line 1013 "effective_tld_names.gperf"
+      {"fhv.se", 0},
+#line 1732 "effective_tld_names.gperf"
+      {"kz", 0},
+#line 1496 "effective_tld_names.gperf"
+      {"indian.museum", 0},
+#line 1061 "effective_tld_names.gperf"
+      {"folldal.no", 0},
+#line 1940 "effective_tld_names.gperf"
+      {"mielec.pl", 0},
+#line 2095 "effective_tld_names.gperf"
+      {"nannestad.no", 0},
+#line 2093 "effective_tld_names.gperf"
+      {"namsos.no", 0},
+#line 686 "effective_tld_names.gperf"
+      {"com.st", 0},
+#line 910 "effective_tld_names.gperf"
+      {"edu.st", 0},
+#line 691 "effective_tld_names.gperf"
+      {"com.tt", 0},
+#line 1194 "effective_tld_names.gperf"
+      {"gouv.fr", 0},
+#line 914 "effective_tld_names.gperf"
+      {"edu.tt", 0},
+#line 930 "effective_tld_names.gperf"
+      {"eid.no", 0},
+#line 721 "effective_tld_names.gperf"
+      {"coop.tt", 0},
+#line 408 "effective_tld_names.gperf"
+      {"brunel.museum", 0},
+#line 1580 "effective_tld_names.gperf"
+      {"jefferson.museum", 0},
+#line 1908 "effective_tld_names.gperf"
+      {"med.sa", 0},
+#line 1559 "effective_tld_names.gperf"
+      {"isernia.it", 0},
+#line 1695 "effective_tld_names.gperf"
+      {"kr", 0},
+#line 1395 "effective_tld_names.gperf"
+      {"hellas.museum", 0},
+#line 1607 "effective_tld_names.gperf"
+      {"journalist.aero", 0},
+#line 1929 "effective_tld_names.gperf"
+      {"mh", 0},
+#line 1289 "effective_tld_names.gperf"
+      {"gov.st", 0},
+#line 1647 "effective_tld_names.gperf"
+      {"ke", 2},
+#line 334 "effective_tld_names.gperf"
+      {"bielawa.pl", 0},
+#line 1295 "effective_tld_names.gperf"
+      {"gov.tt", 0},
+#line 232 "effective_tld_names.gperf"
+      {"astronomy.museum", 0},
+#line 1255 "effective_tld_names.gperf"
+      {"gov.lt", 0},
+#line 1012 "effective_tld_names.gperf"
+      {"fhsk.se", 0},
+#line 1621 "effective_tld_names.gperf"
+      {"k12.ec", 0},
+#line 674 "effective_tld_names.gperf"
+      {"com.pt", 0},
+#line 899 "effective_tld_names.gperf"
+      {"edu.pt", 0},
+#line 324 "effective_tld_names.gperf"
+      {"beskidy.pl", 0},
+#line 1846 "effective_tld_names.gperf"
+      {"m.se", 0},
+#line 1697 "effective_tld_names.gperf"
+      {"kr.it", 0},
+#line 2233 "effective_tld_names.gperf"
+      {"net.vi", 0},
+#line 1400 "effective_tld_names.gperf"
+      {"hemsedal.no", 0},
+#line 714 "effective_tld_names.gperf"
+      {"convent.museum", 0},
+#line 3416 "effective_tld_names.gperf"
+      {"w.se", 0},
+#line 2011 "effective_tld_names.gperf"
+      {"modalen.no", 0},
+#line 976 "effective_tld_names.gperf"
+      {"etnedal.no", 0},
+#line 1919 "effective_tld_names.gperf"
+      {"meland.no", 0},
+#line 1905 "effective_tld_names.gperf"
+      {"med.pa", 0},
+#line 3442 "effective_tld_names.gperf"
+      {"wielun.pl", 0},
+#line 310 "effective_tld_names.gperf"
+      {"beiarn.no", 0},
+#line 1279 "effective_tld_names.gperf"
+      {"gov.pt", 0},
+#line 981 "effective_tld_names.gperf"
+      {"evenes.no", 0},
+#line 411 "effective_tld_names.gperf"
+      {"bruxelles.museum", 0},
+#line 3266 "effective_tld_names.gperf"
+      {"u.se", 0},
+#line 461 "effective_tld_names.gperf"
+      {"catania.it", 0},
+#line 289 "effective_tld_names.gperf"
+      {"bari.it", 0},
+#line 1301 "effective_tld_names.gperf"
+      {"government.aero", 0},
+#line 480 "effective_tld_names.gperf"
+      {"charter.aero", 0},
+#line 1675 "effective_tld_names.gperf"
+      {"kn", 0},
+#line 2223 "effective_tld_names.gperf"
+      {"net.st", 0},
+#line 2229 "effective_tld_names.gperf"
+      {"net.tt", 0},
+#line 530 "effective_tld_names.gperf"
+      {"civilwar.museum", 0},
+#line 1684 "effective_tld_names.gperf"
+      {"komi.ru", 0},
+#line 1035 "effective_tld_names.gperf"
+      {"fitjar.no", 0},
+#line 1542 "effective_tld_names.gperf"
+      {"int.tt", 0},
+#line 1672 "effective_tld_names.gperf"
+      {"km", 0},
+#line 1114 "effective_tld_names.gperf"
+      {"gamvik.no", 0},
+#line 450 "effective_tld_names.gperf"
+      {"canada.museum", 0},
+#line 1158 "effective_tld_names.gperf"
+      {"gliwice.pl", 0},
+#line 1034 "effective_tld_names.gperf"
+      {"firm.ro", 0},
+#line 2005 "effective_tld_names.gperf"
+      {"moareke.no", 0},
+#line 1030 "effective_tld_names.gperf"
+      {"firm.co", 0},
+#line 1386 "effective_tld_names.gperf"
+      {"hasvik.no", 0},
+#line 1523 "effective_tld_names.gperf"
+      {"info.tt", 0},
+#line 1942 "effective_tld_names.gperf"
+      {"mil", 0},
+#line 2214 "effective_tld_names.gperf"
+      {"net.pt", 0},
+#line 1331 "effective_tld_names.gperf"
+      {"gs.jan-mayen.no", 0},
+#line 335 "effective_tld_names.gperf"
+      {"biella.it", 0},
+#line 475 "effective_tld_names.gperf"
+      {"cg", 0},
+#line 926 "effective_tld_names.gperf"
+      {"eg", 2},
+#line 1538 "effective_tld_names.gperf"
+      {"int.pt", 0},
+#line 87 "effective_tld_names.gperf"
+      {"ag", 0},
+#line 1976 "effective_tld_names.gperf"
+      {"mil.tj", 0},
+#line 629 "effective_tld_names.gperf"
+      {"com.gn", 0},
+#line 631 "effective_tld_names.gperf"
+      {"com.gr", 0},
+#line 854 "effective_tld_names.gperf"
+      {"edu.gn", 0},
+#line 856 "effective_tld_names.gperf"
+      {"edu.gr", 0},
+#line 549 "effective_tld_names.gperf"
+      {"co.ag", 0},
+#line 1140 "effective_tld_names.gperf"
+      {"gg", 0},
+#line 935 "effective_tld_names.gperf"
+      {"eigersund.no", 0},
+#line 1317 "effective_tld_names.gperf"
+      {"grosseto.it", 0},
+#line 1575 "effective_tld_names.gperf"
+      {"jamison.museum", 0},
+#line 1497 "effective_tld_names.gperf"
+      {"indiana.museum", 0},
+#line 630 "effective_tld_names.gperf"
+      {"com.gp", 0},
+#line 1852 "effective_tld_names.gperf"
+      {"magadan.ru", 0},
+#line 1350 "effective_tld_names.gperf"
+      {"guernsey.museum", 0},
+#line 855 "effective_tld_names.gperf"
+      {"edu.gp", 0},
+#line 88 "effective_tld_names.gperf"
+      {"ag.it", 0},
+#line 1232 "effective_tld_names.gperf"
+      {"gov.gn", 0},
+#line 1233 "effective_tld_names.gperf"
+      {"gov.gr", 0},
+#line 1953 "effective_tld_names.gperf"
+      {"mil.ec", 0},
+#line 2116 "effective_tld_names.gperf"
+      {"naustdal.no", 0},
+#line 1971 "effective_tld_names.gperf"
+      {"mil.pl", 0},
+#line 708 "effective_tld_names.gperf"
+      {"consulado.st", 0},
+#line 1120 "effective_tld_names.gperf"
+      {"gausdal.no", 0},
+#line 2246 "effective_tld_names.gperf"
+      {"ng", 0},
+#line 1978 "effective_tld_names.gperf"
+      {"mil.tw", 0},
+#line 344 "effective_tld_names.gperf"
+      {"birkenes.no", 0},
+#line 1871 "effective_tld_names.gperf"
+      {"mari.ru", 0},
+#line 1189 "effective_tld_names.gperf"
+      {"gorizia.it", 0},
+#line 1546 "effective_tld_names.gperf"
+      {"intl.tn", 0},
+#line 2061 "effective_tld_names.gperf"
+      {"mv", 0},
+#line 979 "effective_tld_names.gperf"
+      {"eu.int", 0},
+#line 1600 "effective_tld_names.gperf"
+      {"jolster.no", 0},
+#line 940 "effective_tld_names.gperf"
+      {"elvendrell.museum", 0},
+#line 1369 "effective_tld_names.gperf"
+      {"hadsel.no", 0},
+#line 3295 "effective_tld_names.gperf"
+      {"urn.arpa", 0},
+#line 705 "effective_tld_names.gperf"
+      {"conf.lv", 0},
+#line 1973 "effective_tld_names.gperf"
+      {"mil.rw", 0},
+#line 1522 "effective_tld_names.gperf"
+      {"info.tn", 0},
+#line 1437 "effective_tld_names.gperf"
+      {"hornindal.no", 0},
+#line 2139 "effective_tld_names.gperf"
+      {"nesseby.no", 0},
+#line 2028 "effective_tld_names.gperf"
+      {"moss.no", 0},
+#line 2249 "effective_tld_names.gperf"
+      {"ngo.pl", 0},
+#line 351 "effective_tld_names.gperf"
+      {"biz.mv", 0},
+#line 3306 "effective_tld_names.gperf"
+      {"usgarden.museum", 0},
+#line 258 "effective_tld_names.gperf"
+      {"aw", 0},
+#line 2167 "effective_tld_names.gperf"
+      {"net.gn", 0},
+#line 2169 "effective_tld_names.gperf"
+      {"net.gr", 0},
+#line 3711 "effective_tld_names.gperf"
+      {"zw", 2},
+#line 626 "effective_tld_names.gperf"
+      {"com.ge", 0},
+#line 592 "effective_tld_names.gperf"
+      {"collection.museum", 0},
+#line 1918 "effective_tld_names.gperf"
+      {"meeres.museum", 0},
+#line 851 "effective_tld_names.gperf"
+      {"edu.ge", 0},
+#line 359 "effective_tld_names.gperf"
+      {"biz.vn", 0},
+#line 683 "effective_tld_names.gperf"
+      {"com.sg", 0},
+#line 583 "effective_tld_names.gperf"
+      {"co.tz", 0},
+#line 907 "effective_tld_names.gperf"
+      {"edu.sg", 0},
+#line 701 "effective_tld_names.gperf"
+      {"community.museum", 0},
+#line 575 "effective_tld_names.gperf"
+      {"co.pw", 0},
+#line 166 "effective_tld_names.gperf"
+      {"aquarium.museum", 0},
+#line 1356 "effective_tld_names.gperf"
+      {"gw", 0},
+#line 457 "effective_tld_names.gperf"
+      {"casino.hu", 0},
+#line 826 "effective_tld_names.gperf"
+      {"ed.pw", 0},
+#line 2168 "effective_tld_names.gperf"
+      {"net.gp", 0},
+#line 582 "effective_tld_names.gperf"
+      {"co.tt", 0},
+#line 698 "effective_tld_names.gperf"
+      {"com.ws", 0},
+#line 1576 "effective_tld_names.gperf"
+      {"jan-mayen.no", 0},
+#line 919 "effective_tld_names.gperf"
+      {"edu.ws", 0},
+#line 2134 "effective_tld_names.gperf"
+      {"nes.akershus.no", 0},
+#line 1729 "effective_tld_names.gperf"
+      {"ky", 0},
+#line 3301 "effective_tld_names.gperf"
+      {"usarts.museum", 0},
+#line 1173 "effective_tld_names.gperf"
+      {"go.tz", 0},
+#line 2718 "effective_tld_names.gperf"
+      {"ps", 0},
+#line 1686 "effective_tld_names.gperf"
+      {"kommune.no", 0},
+#line 1170 "effective_tld_names.gperf"
+      {"go.pw", 0},
+#line 1228 "effective_tld_names.gperf"
+      {"gov.ge", 0},
+#line 2722 "effective_tld_names.gperf"
+      {"pt", 0},
+#line 331 "effective_tld_names.gperf"
+      {"bialowieza.pl", 0},
+#line 739 "effective_tld_names.gperf"
+      {"crimea.ua", 0},
+#line 2594 "effective_tld_names.gperf"
+      {"pl", 0},
+#line 687 "effective_tld_names.gperf"
+      {"com.sy", 0},
+#line 1969 "effective_tld_names.gperf"
+      {"mil.pe", 0},
+#line 911 "effective_tld_names.gperf"
+      {"edu.sy", 0},
+#line 1287 "effective_tld_names.gperf"
+      {"gov.sg", 0},
+#line 652 "effective_tld_names.gperf"
+      {"com.ly", 0},
+#line 728 "effective_tld_names.gperf"
+      {"countryestate.museum", 0},
+#line 877 "effective_tld_names.gperf"
+      {"edu.ly", 0},
+#line 1300 "effective_tld_names.gperf"
+      {"gov.ws", 0},
+#line 1110 "effective_tld_names.gperf"
+      {"gallery.museum", 0},
+#line 554 "effective_tld_names.gperf"
+      {"co.bw", 0},
+#line 1008 "effective_tld_names.gperf"
+      {"fetsund.no", 0},
+#line 2606 "effective_tld_names.gperf"
+      {"po.it", 0},
+#line 2736 "effective_tld_names.gperf"
+      {"pz.it", 0},
+#line 2555 "effective_tld_names.gperf"
+      {"pd.it", 0},
+#line 2631 "effective_tld_names.gperf"
+      {"pr", 0},
+#line 572 "effective_tld_names.gperf"
+      {"co.mw", 0},
+#line 2701 "effective_tld_names.gperf"
+      {"pro", 0},
+#line 2723 "effective_tld_names.gperf"
+      {"pt.it", 0},
+#line 1661 "effective_tld_names.gperf"
+      {"ki", 0},
+#line 1290 "effective_tld_names.gperf"
+      {"gov.sy", 0},
+#line 2556 "effective_tld_names.gperf"
+      {"pe", 0},
+#line 108 "effective_tld_names.gperf"
+      {"airline.aero", 0},
+#line 1875 "effective_tld_names.gperf"
+      {"marker.no", 0},
+#line 1945 "effective_tld_names.gperf"
+      {"mil.al", 0},
+#line 1701 "effective_tld_names.gperf"
+      {"krakow.pl", 0},
+#line 1257 "effective_tld_names.gperf"
+      {"gov.ly", 0},
+#line 1943 "effective_tld_names.gperf"
+      {"mil.ac", 0},
+#line 863 "effective_tld_names.gperf"
+      {"edu.it", 0},
+#line 405 "effective_tld_names.gperf"
+      {"bronnoy.no", 0},
+#line 1611 "effective_tld_names.gperf"
+      {"judaica.museum", 0},
+#line 2632 "effective_tld_names.gperf"
+      {"pr.it", 0},
+#line 2627 "effective_tld_names.gperf"
+      {"pp.az", 0},
+#line 1044 "effective_tld_names.gperf"
+      {"flanders.museum", 0},
+#line 1164 "effective_tld_names.gperf"
+      {"gniezno.pl", 0},
+#line 2558 "effective_tld_names.gperf"
+      {"pe.it", 0},
+#line 1870 "effective_tld_names.gperf"
+      {"mari-el.ru", 0},
+#line 2128 "effective_tld_names.gperf"
+      {"ne.tz", 0},
+#line 2092 "effective_tld_names.gperf"
+      {"name.vn", 0},
+#line 1241 "effective_tld_names.gperf"
+      {"gov.it", 0},
+#line 2127 "effective_tld_names.gperf"
+      {"ne.pw", 0},
+#line 2602 "effective_tld_names.gperf"
+      {"plo.ps", 0},
+#line 137 "effective_tld_names.gperf"
+      {"american.museum", 0},
+#line 3434 "effective_tld_names.gperf"
+      {"web.nf", 0},
+#line 664 "effective_tld_names.gperf"
+      {"com.ng", 0},
+#line 2165 "effective_tld_names.gperf"
+      {"net.ge", 0},
+#line 621 "effective_tld_names.gperf"
+      {"com.dz", 0},
+#line 888 "effective_tld_names.gperf"
+      {"edu.ng", 0},
+#line 847 "effective_tld_names.gperf"
+      {"edu.dz", 0},
+#line 2704 "effective_tld_names.gperf"
+      {"pro.ec", 0},
+#line 1011 "effective_tld_names.gperf"
+      {"fhs.no", 0},
+#line 2221 "effective_tld_names.gperf"
+      {"net.sg", 0},
+#line 222 "effective_tld_names.gperf"
+      {"asso.gp", 0},
+#line 2708 "effective_tld_names.gperf"
+      {"pro.pr", 0},
+#line 577 "effective_tld_names.gperf"
+      {"co.rw", 0},
+#line 924 "effective_tld_names.gperf"
+      {"edunet.tn", 0},
+#line 2235 "effective_tld_names.gperf"
+      {"net.ws", 0},
+#line 76 "effective_tld_names.gperf"
+      {"ae.org", 0},
+#line 2603 "effective_tld_names.gperf"
+      {"pn", 0},
+#line 1451 "effective_tld_names.gperf"
+      {"humanities.museum", 0},
+#line 943 "effective_tld_names.gperf"
+      {"embroidery.museum", 0},
+#line 326 "effective_tld_names.gperf"
+      {"bg", 0},
+#line 1271 "effective_tld_names.gperf"
+      {"gov.ng", 0},
+#line 1225 "effective_tld_names.gperf"
+      {"gov.dz", 0},
+#line 1128 "effective_tld_names.gperf"
+      {"gdynia.pl", 0},
+#line 2261 "effective_tld_names.gperf"
+      {"nissedal.no", 0},
+#line 2224 "effective_tld_names.gperf"
+      {"net.sy", 0},
+#line 653 "effective_tld_names.gperf"
+      {"com.mg", 0},
+#line 958 "effective_tld_names.gperf"
+      {"environment.museum", 0},
+#line 879 "effective_tld_names.gperf"
+      {"edu.mg", 0},
+#line 142 "effective_tld_names.gperf"
+      {"amot.no", 0},
+#line 2192 "effective_tld_names.gperf"
+      {"net.ly", 0},
+#line 1606 "effective_tld_names.gperf"
+      {"journalism.museum", 0},
+#line 1853 "effective_tld_names.gperf"
+      {"magazine.aero", 0},
+#line 2604 "effective_tld_names.gperf"
+      {"pn.it", 0},
+#line 185 "effective_tld_names.gperf"
+      {"art.dz", 0},
+#line 327 "effective_tld_names.gperf"
+      {"bg.it", 0},
+#line 2725 "effective_tld_names.gperf"
+      {"pu.it", 0},
+#line 1623 "effective_tld_names.gperf"
+      {"kafjord.no", 0},
+#line 1090 "effective_tld_names.gperf"
+      {"frosta.no", 0},
+#line 661 "effective_tld_names.gperf"
+      {"com.my", 0},
+#line 2727 "effective_tld_names.gperf"
+      {"publ.pt", 0},
+#line 1855 "effective_tld_names.gperf"
+      {"mail.pl", 0},
+#line 887 "effective_tld_names.gperf"
+      {"edu.my", 0},
+#line 1260 "effective_tld_names.gperf"
+      {"gov.mg", 0},
+#line 1944 "effective_tld_names.gperf"
+      {"mil.ae", 0},
+#line 442 "effective_tld_names.gperf"
+      {"cagliari.it", 0},
+#line 455 "effective_tld_names.gperf"
+      {"casadelamoneda.museum", 0},
+#line 231 "effective_tld_names.gperf"
+      {"astrakhan.ru", 0},
+#line 1309 "effective_tld_names.gperf"
+      {"grandrapids.museum", 0},
+#line 1941 "effective_tld_names.gperf"
+      {"mielno.pl", 0},
+#line 1872 "effective_tld_names.gperf"
+      {"marine.ru", 0},
+#line 299 "effective_tld_names.gperf"
+      {"bauern.museum", 0},
+#line 2282 "effective_tld_names.gperf"
+      {"nom.mg", 0},
+#line 253 "effective_tld_names.gperf"
+      {"avellino.it", 0},
+#line 3450 "effective_tld_names.gperf"
+      {"wolomin.pl", 0},
+#line 600 "effective_tld_names.gperf"
+      {"com.ag", 0},
+#line 2247 "effective_tld_names.gperf"
+      {"ngo.lk", 0},
+#line 1269 "effective_tld_names.gperf"
+      {"gov.my", 0},
+#line 3693 "effective_tld_names.gperf"
+      {"za.net", 0},
+#line 628 "effective_tld_names.gperf"
+      {"com.gi", 0},
+#line 853 "effective_tld_names.gperf"
+      {"edu.gi", 0},
+#line 2204 "effective_tld_names.gperf"
+      {"net.ng", 0},
+#line 694 "effective_tld_names.gperf"
+      {"com.uz", 0},
+#line 2163 "effective_tld_names.gperf"
+      {"net.dz", 0},
+#line 2559 "effective_tld_names.gperf"
+      {"pe.kr", 0},
+#line 2529 "effective_tld_names.gperf"
+      {"pa", 0},
+#line 131 "effective_tld_names.gperf"
+      {"alvdal.no", 0},
+#line 80 "effective_tld_names.gperf"
+      {"aero.tt", 0},
+#line 322 "effective_tld_names.gperf"
+      {"berlin.museum", 0},
+#line 1957 "effective_tld_names.gperf"
+      {"mil.in", 0},
+#line 2316 "effective_tld_names.gperf"
+      {"nsw.edu.au", 0},
+#line 275 "effective_tld_names.gperf"
+      {"baikal.ru", 0},
+#line 1231 "effective_tld_names.gperf"
+      {"gov.gi", 0},
+#line 1099 "effective_tld_names.gperf"
+      {"fuossko.no", 0},
+#line 425 "effective_tld_names.gperf"
+      {"bw", 0},
+#line 2276 "effective_tld_names.gperf"
+      {"nom.ag", 0},
+#line 2531 "effective_tld_names.gperf"
+      {"pa.it", 0},
+#line 3685 "effective_tld_names.gperf"
+      {"yosemite.museum", 0},
+#line 1958 "effective_tld_names.gperf"
+      {"mil.iq", 0},
+#line 2029 "effective_tld_names.gperf"
+      {"mosvik.no", 0},
+#line 723 "effective_tld_names.gperf"
+      {"corporation.museum", 0},
+#line 1653 "effective_tld_names.gperf"
+      {"kh", 2},
+#line 1052 "effective_tld_names.gperf"
+      {"florida.museum", 0},
+#line 1383 "effective_tld_names.gperf"
+      {"hareid.no", 0},
+#line 636 "effective_tld_names.gperf"
+      {"com.ht", 0},
+#line 859 "effective_tld_names.gperf"
+      {"edu.ht", 0},
+#line 1032 "effective_tld_names.gperf"
+      {"firm.in", 0},
+#line 937 "effective_tld_names.gperf"
+      {"elblag.pl", 0},
+#line 2202 "effective_tld_names.gperf"
+      {"net.my", 0},
+#line 3694 "effective_tld_names.gperf"
+      {"za.org", 0},
+#line 1718 "effective_tld_names.gperf"
+      {"kuzbass.ru", 0},
+#line 1311 "effective_tld_names.gperf"
+      {"granvin.no", 0},
+#line 1620 "effective_tld_names.gperf"
+      {"k.se", 0},
+#line 2738 "effective_tld_names.gperf"
+      {"qa", 2},
+#line 996 "effective_tld_names.gperf"
+      {"farmers.museum", 0},
+#line 3626 "effective_tld_names.gperf"
+      {"xn--snes-poa.no", 0},
+#line 605 "effective_tld_names.gperf"
+      {"com.az", 0},
+#line 2101 "effective_tld_names.gperf"
+      {"narvik.no", 0},
+#line 138 "effective_tld_names.gperf"
+      {"americana.museum", 0},
+#line 832 "effective_tld_names.gperf"
+      {"edu.az", 0},
+#line 2145 "effective_tld_names.gperf"
+      {"net.ag", 0},
+#line 2628 "effective_tld_names.gperf"
+      {"pp.ru", 0},
+#line 1977 "effective_tld_names.gperf"
+      {"mil.to", 0},
+#line 406 "effective_tld_names.gperf"
+      {"bronnoysund.no", 0},
+#line 709 "effective_tld_names.gperf"
+      {"consultant.aero", 0},
+#line 980 "effective_tld_names.gperf"
+      {"evenassi.no", 0},
+#line 1135 "effective_tld_names.gperf"
+      {"genova.it", 0},
+#line 409 "effective_tld_names.gperf"
+      {"brussel.museum", 0},
+#line 186 "effective_tld_names.gperf"
+      {"art.ht", 0},
+#line 410 "effective_tld_names.gperf"
+      {"brussels.museum", 0},
+#line 1676 "effective_tld_names.gperf"
+      {"kobe.jp", 2},
+#line 1206 "effective_tld_names.gperf"
+      {"gov.az", 0},
+#line 1009 "effective_tld_names.gperf"
+      {"fg.it", 0},
+#line 2009 "effective_tld_names.gperf"
+      {"mobi.tt", 0},
+#line 214 "effective_tld_names.gperf"
+      {"assassination.museum", 0},
+#line 1498 "effective_tld_names.gperf"
+      {"indianapolis.museum", 0},
+#line 2734 "effective_tld_names.gperf"
+      {"py", 2},
+#line 960 "effective_tld_names.gperf"
+      {"epilepsy.museum", 0},
+#line 1198 "effective_tld_names.gperf"
+      {"gouv.rw", 0},
+#line 1384 "effective_tld_names.gperf"
+      {"harstad.no", 0},
+#line 140 "effective_tld_names.gperf"
+      {"americanart.museum", 0},
+#line 3606 "effective_tld_names.gperf"
+      {"xn--rst-0na.no", 0},
+#line 1006 "effective_tld_names.gperf"
+      {"ferrara.it", 0},
+#line 3677 "effective_tld_names.gperf"
+      {"yaroslavl.ru", 0},
+#line 1010 "effective_tld_names.gperf"
+      {"fh.se", 0},
+#line 2173 "effective_tld_names.gperf"
+      {"net.ht", 0},
+#line 2240 "effective_tld_names.gperf"
+      {"newport.museum", 0},
+#line 358 "effective_tld_names.gperf"
+      {"biz.tt", 0},
+#line 96 "effective_tld_names.gperf"
+      {"agro.pl", 0},
+#line 320 "effective_tld_names.gperf"
+      {"berkeley.museum", 0},
+#line 1027 "effective_tld_names.gperf"
+      {"finland.museum", 0},
+#line 453 "effective_tld_names.gperf"
+      {"carrier.museum", 0},
+#line 1495 "effective_tld_names.gperf"
+      {"inderoy.no", 0},
+#line 2726 "effective_tld_names.gperf"
+      {"pub.sa", 0},
+#line 1923 "effective_tld_names.gperf"
+      {"memorial.museum", 0},
+#line 1968 "effective_tld_names.gperf"
+      {"mil.no", 0},
+#line 3594 "effective_tld_names.gperf"
+      {"xn--rde-ula.no", 0},
+#line 2149 "effective_tld_names.gperf"
+      {"net.az", 0},
+#line 2584 "effective_tld_names.gperf"
+      {"pi.it", 0},
+#line 2707 "effective_tld_names.gperf"
+      {"pro.na", 0},
+#line 1485 "effective_tld_names.gperf"
+      {"in-addr.arpa", 0},
+#line 1529 "effective_tld_names.gperf"
+      {"int.az", 0},
+#line 594 "effective_tld_names.gperf"
+      {"coloradoplateau.museum", 0},
+#line 1956 "effective_tld_names.gperf"
+      {"mil.hn", 0},
+#line 3627 "effective_tld_names.gperf"
+      {"xn--snsa-roa.no", 0},
+#line 1567 "effective_tld_names.gperf"
+      {"ivanovo.ru", 0},
+#line 3119 "effective_tld_names.gperf"
+      {"sz", 0},
+#line 3557 "effective_tld_names.gperf"
+      {"xn--lrdal-sra.no", 0},
+#line 2924 "effective_tld_names.gperf"
+      {"sd", 0},
+#line 3294 "effective_tld_names.gperf"
+      {"uri.arpa", 0},
+#line 1601 "effective_tld_names.gperf"
+      {"jondal.no", 0},
+#line 740 "effective_tld_names.gperf"
+      {"crotone.it", 0},
+#line 3644 "effective_tld_names.gperf"
+      {"xn--troms-zua.no", 0},
+#line 3048 "effective_tld_names.gperf"
+      {"st", 0},
+#line 2989 "effective_tld_names.gperf"
+      {"sl", 0},
+#line 703 "effective_tld_names.gperf"
+      {"computer.museum", 0},
+#line 90 "effective_tld_names.gperf"
+      {"agents.aero", 0},
+#line 2713 "effective_tld_names.gperf"
+      {"prof.pr", 0},
+#line 1928 "effective_tld_names.gperf"
+      {"mg", 0},
+#line 3006 "effective_tld_names.gperf"
+      {"so.it", 0},
+#line 1964 "effective_tld_names.gperf"
+      {"mil.lv", 0},
+#line 3046 "effective_tld_names.gperf"
+      {"ss.it", 0},
+#line 1598 "effective_tld_names.gperf"
+      {"jobs.tt", 0},
+#line 3042 "effective_tld_names.gperf"
+      {"sr", 0},
+#line 3590 "effective_tld_names.gperf"
+      {"xn--p1ai", 0},
+#line 3593 "effective_tld_names.gperf"
+      {"xn--rdal-poa.no", 0},
+#line 2927 "effective_tld_names.gperf"
+      {"se", 0},
+#line 1066 "effective_tld_names.gperf"
+      {"forsand.no", 0},
+#line 3620 "effective_tld_names.gperf"
+      {"xn--slat-5na.no", 0},
+#line 3273 "effective_tld_names.gperf"
+      {"ug", 0},
+#line 3289 "effective_tld_names.gperf"
+      {"univ.sn", 0},
+#line 3044 "effective_tld_names.gperf"
+      {"sr.it", 0},
+#line 2091 "effective_tld_names.gperf"
+      {"name.tt", 0},
+#line 3029 "effective_tld_names.gperf"
+      {"sos.pl", 0},
+#line 433 "effective_tld_names.gperf"
+      {"c.bg", 0},
+#line 814 "effective_tld_names.gperf"
       {"e.bg", 0},
-#line 1341 "effective_tld_names.gperf"
-      {"h.bg", 0},
-#line 3406 "effective_tld_names.gperf"
+#line 1457 "effective_tld_names.gperf"
+      {"i.bg", 0},
+#line 26 "effective_tld_names.gperf"
+      {"a.bg", 0},
+#line 3461 "effective_tld_names.gperf"
       {"x.bg", 0},
 #line 21 "effective_tld_names.gperf"
       {"6.bg", 0},
+#line 16 "effective_tld_names.gperf"
+      {"2.bg", 0},
 #line 25 "effective_tld_names.gperf"
       {"9.bg", 0},
 #line 24 "effective_tld_names.gperf"
       {"8.bg", 0},
+#line 3689 "effective_tld_names.gperf"
+      {"z.bg", 0},
+#line 3034 "effective_tld_names.gperf"
+      {"sp.it", 0},
 #line 23 "effective_tld_names.gperf"
       {"7.bg", 0},
 #line 20 "effective_tld_names.gperf"
@@ -1910,8247 +2294,8025 @@
       {"4.bg", 0},
 #line 18 "effective_tld_names.gperf"
       {"3.bg", 0},
-#line 16 "effective_tld_names.gperf"
-      {"2.bg", 0},
-#line 2035 "effective_tld_names.gperf"
-      {"n.bg", 0},
 #line 15 "effective_tld_names.gperf"
       {"1.bg", 0},
 #line 14 "effective_tld_names.gperf"
       {"0.bg", 0},
-#line 3365 "effective_tld_names.gperf"
-      {"wa.us", 0},
-#line 424 "effective_tld_names.gperf"
-      {"c.bg", 0},
-#line 3379 "effective_tld_names.gperf"
-      {"web.nf", 0},
-#line 3219 "effective_tld_names.gperf"
-      {"udm.ru", 0},
-#line 3609 "effective_tld_names.gperf"
-      {"y.bg", 0},
-#line 1931 "effective_tld_names.gperf"
-      {"mil.km", 0},
-#line 2217 "effective_tld_names.gperf"
-      {"nic.ar", 1},
-#line 3386 "effective_tld_names.gperf"
-      {"wi.us", 0},
-#line 1979 "effective_tld_names.gperf"
-      {"mod.gi", 0},
-#line 238 "effective_tld_names.gperf"
-      {"aurland.no", 0},
-#line 1344 "effective_tld_names.gperf"
-      {"ha.no", 0},
-#line 27 "effective_tld_names.gperf"
-      {"a.se", 0},
-#line 3628 "effective_tld_names.gperf"
-      {"z.bg", 0},
-#line 2299 "effective_tld_names.gperf"
-      {"o.bg", 0},
-#line 1935 "effective_tld_names.gperf"
-      {"mil.mg", 0},
-#line 983 "effective_tld_names.gperf"
-      {"fc.it", 0},
-#line 363 "effective_tld_names.gperf"
-      {"bo", 0},
-#line 296 "effective_tld_names.gperf"
-      {"bd", 2},
-#line 2987 "effective_tld_names.gperf"
-      {"spb.ru", 0},
-#line 298 "effective_tld_names.gperf"
-      {"be", 0},
-#line 148 "effective_tld_names.gperf"
-      {"and.museum", 0},
-#line 1817 "effective_tld_names.gperf"
-      {"m.se", 0},
-#line 417 "effective_tld_names.gperf"
-      {"by", 0},
-#line 41 "effective_tld_names.gperf"
-      {"ac.in", 0},
-#line 702 "effective_tld_names.gperf"
-      {"control.aero", 0},
-#line 381 "effective_tld_names.gperf"
-      {"br", 0},
-#line 599 "effective_tld_names.gperf"
-      {"com.bh", 0},
-#line 431 "effective_tld_names.gperf"
-      {"caa.aero", 0},
-#line 2818 "effective_tld_names.gperf"
-      {"samara.ru", 0},
-#line 1249 "effective_tld_names.gperf"
-      {"gov.ph", 0},
-#line 2388 "effective_tld_names.gperf"
-      {"org.im", 0},
-#line 361 "effective_tld_names.gperf"
-      {"bn", 2},
-#line 2389 "effective_tld_names.gperf"
-      {"org.in", 0},
-#line 878 "effective_tld_names.gperf"
-      {"edu.ph", 0},
-#line 61 "effective_tld_names.gperf"
-      {"ac.vn", 0},
-#line 89 "effective_tld_names.gperf"
-      {"agr.br", 0},
-#line 2171 "effective_tld_names.gperf"
-      {"net.ph", 0},
-#line 1871 "effective_tld_names.gperf"
-      {"med.br", 0},
-#line 2232 "effective_tld_names.gperf"
-      {"nm.cn", 0},
-#line 1137 "effective_tld_names.gperf"
-      {"gl", 0},
-#line 1451 "effective_tld_names.gperf"
-      {"il", 2},
-#line 658 "effective_tld_names.gperf"
-      {"com.ph", 0},
-#line 2228 "effective_tld_names.gperf"
-      {"nl", 0},
-#line 405 "effective_tld_names.gperf"
-      {"bs", 0},
-#line 150 "effective_tld_names.gperf"
-      {"andebu.no", 0},
-#line 524 "effective_tld_names.gperf"
-      {"cl", 0},
-#line 407 "effective_tld_names.gperf"
-      {"bt", 2},
-#line 133 "effective_tld_names.gperf"
-      {"ambulance.aero", 0},
-#line 953 "effective_tld_names.gperf"
-      {"estate.museum", 0},
-#line 1222 "effective_tld_names.gperf"
-      {"gov.jo", 0},
-#line 851 "effective_tld_names.gperf"
-      {"edu.jo", 0},
-#line 2848 "effective_tld_names.gperf"
-      {"sc.ug", 0},
-#line 1435 "effective_tld_names.gperf"
-      {"i.ph", 0},
-#line 48 "effective_tld_names.gperf"
-      {"ac.mw", 0},
-#line 704 "effective_tld_names.gperf"
-      {"coop", 0},
-#line 3258 "effective_tld_names.gperf"
-      {"ut.us", 0},
-#line 3266 "effective_tld_names.gperf"
-      {"uz", 0},
-#line 2145 "effective_tld_names.gperf"
-      {"net.jo", 0},
-#line 1340 "effective_tld_names.gperf"
-      {"gz.cn", 0},
-#line 3608 "effective_tld_names.gperf"
-      {"xz.cn", 0},
-#line 1471 "effective_tld_names.gperf"
-      {"inderoy.no", 0},
-#line 2951 "effective_tld_names.gperf"
-      {"snaase.no", 0},
-#line 631 "effective_tld_names.gperf"
-      {"com.jo", 0},
-#line 1756 "effective_tld_names.gperf"
-      {"li.it", 0},
-#line 1746 "effective_tld_names.gperf"
-      {"lel.br", 0},
-#line 2236 "effective_tld_names.gperf"
-      {"no.com", 0},
-#line 1971 "effective_tld_names.gperf"
-      {"mo.cn", 0},
-#line 561 "effective_tld_names.gperf"
-      {"co.me", 0},
-#line 1938 "effective_tld_names.gperf"
-      {"mil.pe", 0},
-#line 1754 "effective_tld_names.gperf"
-      {"lg.ua", 0},
-#line 2312 "effective_tld_names.gperf"
-      {"ok.us", 0},
-#line 941 "effective_tld_names.gperf"
-      {"environment.museum", 0},
-#line 2805 "effective_tld_names.gperf"
-      {"sa.it", 0},
-#line 1877 "effective_tld_names.gperf"
-      {"med.pl", 0},
-#line 533 "effective_tld_names.gperf"
-      {"cn.com", 0},
-#line 2309 "effective_tld_names.gperf"
-      {"og.ao", 0},
-#line 999 "effective_tld_names.gperf"
-      {"fi.it", 0},
-#line 1116 "effective_tld_names.gperf"
-      {"gen.in", 0},
-#line 147 "effective_tld_names.gperf"
-      {"ancona.it", 0},
-#line 2911 "effective_tld_names.gperf"
-      {"si.it", 0},
-#line 1962 "effective_tld_names.gperf"
-      {"mk", 0},
-#line 163 "effective_tld_names.gperf"
-      {"aq.it", 0},
-#line 1007 "effective_tld_names.gperf"
-      {"fin.tn", 0},
-#line 1335 "effective_tld_names.gperf"
-      {"gx.cn", 0},
-#line 1314 "effective_tld_names.gperf"
-      {"gs.oslo.no", 0},
-#line 1875 "effective_tld_names.gperf"
-      {"med.ly", 0},
-#line 2425 "effective_tld_names.gperf"
-      {"org.ph", 0},
-#line 207 "effective_tld_names.gperf"
-      {"asmatart.museum", 0},
-#line 578 "effective_tld_names.gperf"
-      {"co.vi", 0},
-#line 2293 "effective_tld_names.gperf"
-      {"nx.cn", 0},
-#line 1219 "effective_tld_names.gperf"
-      {"gov.is", 0},
-#line 849 "effective_tld_names.gperf"
-      {"edu.is", 0},
-#line 1421 "effective_tld_names.gperf"
-      {"hoylandet.no", 0},
-#line 26 "effective_tld_names.gperf"
-      {"a.bg", 0},
-#line 2143 "effective_tld_names.gperf"
-      {"net.is", 0},
-#line 2886 "effective_tld_names.gperf"
-      {"sel.no", 0},
-#line 630 "effective_tld_names.gperf"
-      {"com.is", 0},
-#line 2327 "effective_tld_names.gperf"
-      {"ontario.museum", 0},
-#line 2394 "effective_tld_names.gperf"
-      {"org.jo", 0},
-#line 2188 "effective_tld_names.gperf"
-      {"net.th", 0},
-#line 1930 "effective_tld_names.gperf"
-      {"mil.kg", 0},
-#line 1727 "effective_tld_names.gperf"
-      {"lavagis.no", 0},
-#line 1816 "effective_tld_names.gperf"
-      {"m.bg", 0},
-#line 359 "effective_tld_names.gperf"
-      {"bm", 0},
-#line 1506 "effective_tld_names.gperf"
-      {"int.is", 0},
-#line 28 "effective_tld_names.gperf"
-      {"aa.no", 0},
-#line 1347 "effective_tld_names.gperf"
-      {"hagebostad.no", 0},
-#line 3000 "effective_tld_names.gperf"
-      {"st.no", 0},
-#line 1500 "effective_tld_names.gperf"
-      {"insurance.aero", 0},
-#line 587 "effective_tld_names.gperf"
-      {"columbus.museum", 0},
-#line 1914 "effective_tld_names.gperf"
-      {"mil.ac", 0},
-#line 3068 "effective_tld_names.gperf"
-      {"syzran.ru", 0},
-#line 523 "effective_tld_names.gperf"
-      {"ck.ua", 0},
-#line 1452 "effective_tld_names.gperf"
-      {"il.us", 0},
-#line 3245 "effective_tld_names.gperf"
-      {"us.na", 0},
-#line 2020 "effective_tld_names.gperf"
-      {"mus.br", 0},
-#line 914 "effective_tld_names.gperf"
-      {"eid.no", 0},
-#line 1214 "effective_tld_names.gperf"
-      {"gov.ie", 0},
-#line 961 "effective_tld_names.gperf"
-      {"eu.com", 0},
-#line 1426 "effective_tld_names.gperf"
-      {"hu.com", 0},
-#line 422 "effective_tld_names.gperf"
-      {"bz", 0},
-#line 975 "effective_tld_names.gperf"
-      {"far.br", 0},
-#line 919 "effective_tld_names.gperf"
-      {"eigersund.no", 0},
-#line 3215 "effective_tld_names.gperf"
-      {"ua", 0},
-#line 3217 "effective_tld_names.gperf"
-      {"ud.it", 0},
-#line 1343 "effective_tld_names.gperf"
-      {"ha.cn", 0},
-#line 3370 "effective_tld_names.gperf"
-      {"war.museum", 0},
-#line 2960 "effective_tld_names.gperf"
-      {"software.aero", 0},
-#line 1705 "effective_tld_names.gperf"
-      {"l.se", 0},
-#line 2985 "effective_tld_names.gperf"
-      {"sp.it", 0},
-#line 304 "effective_tld_names.gperf"
-      {"beiarn.no", 0},
-#line 1948 "effective_tld_names.gperf"
-      {"mil.vc", 0},
-#line 1872 "effective_tld_names.gperf"
-      {"med.ec", 0},
-#line 992 "effective_tld_names.gperf"
-      {"fg.it", 0},
-#line 1382 "effective_tld_names.gperf"
-      {"hi.cn", 0},
-#line 1919 "effective_tld_names.gperf"
-      {"mil.br", 0},
-#line 1736 "effective_tld_names.gperf"
-      {"lebesby.no", 0},
-#line 2850 "effective_tld_names.gperf"
-      {"sch.ae", 0},
-#line 1795 "effective_tld_names.gperf"
-      {"ltd.gi", 0},
-#line 2227 "effective_tld_names.gperf"
-      {"nkz.ru", 0},
-#line 177 "effective_tld_names.gperf"
-      {"arezzo.it", 0},
-#line 113 "effective_tld_names.gperf"
-      {"al", 0},
-#line 1937 "effective_tld_names.gperf"
-      {"mil.no", 0},
-#line 100 "effective_tld_names.gperf"
-      {"aip.ee", 0},
-#line 717 "effective_tld_names.gperf"
-      {"county.museum", 0},
-#line 2856 "effective_tld_names.gperf"
-      {"sch.ly", 0},
-#line 2392 "effective_tld_names.gperf"
-      {"org.is", 0},
-#line 2078 "effective_tld_names.gperf"
-      {"nature.museum", 0},
-#line 109 "effective_tld_names.gperf"
-      {"ak.us", 0},
-#line 1964 "effective_tld_names.gperf"
-      {"ml", 2},
-#line 972 "effective_tld_names.gperf"
-      {"f.se", 0},
-#line 1841 "effective_tld_names.gperf"
-      {"mari-el.ru", 0},
-#line 2799 "effective_tld_names.gperf"
-      {"s.se", 0},
-#line 934 "effective_tld_names.gperf"
-      {"engine.aero", 0},
-#line 2851 "effective_tld_names.gperf"
-      {"sch.gg", 0},
-#line 45 "effective_tld_names.gperf"
-      {"ac.ma", 0},
-#line 2218 "effective_tld_names.gperf"
-      {"nic.im", 0},
-#line 2219 "effective_tld_names.gperf"
-      {"nic.in", 0},
-#line 1936 "effective_tld_names.gperf"
-      {"mil.my", 0},
-#line 2332 "effective_tld_names.gperf"
-      {"oppegard.no", 0},
-#line 1920 "effective_tld_names.gperf"
-      {"mil.by", 0},
-#line 1039 "effective_tld_names.gperf"
-      {"fm.no", 0},
-#line 2698 "effective_tld_names.gperf"
-      {"qsl.br", 0},
-#line 167 "effective_tld_names.gperf"
-      {"ar.com", 0},
-#line 37 "effective_tld_names.gperf"
-      {"ac.cn", 0},
-#line 1940 "effective_tld_names.gperf"
-      {"mil.pl", 0},
-#line 548 "effective_tld_names.gperf"
-      {"co.gg", 0},
-#line 2802 "effective_tld_names.gperf"
-      {"sa.cr", 0},
-#line 1915 "effective_tld_names.gperf"
-      {"mil.ae", 0},
-#line 940 "effective_tld_names.gperf"
-      {"entomology.museum", 0},
-#line 1542 "effective_tld_names.gperf"
-      {"iz.hr", 0},
-#line 998 "effective_tld_names.gperf"
-      {"fi.cr", 0},
-#line 3362 "effective_tld_names.gperf"
-      {"w.se", 0},
-#line 134 "effective_tld_names.gperf"
-      {"ambulance.museum", 0},
-#line 1772 "effective_tld_names.gperf"
-      {"ln.cn", 0},
-#line 1221 "effective_tld_names.gperf"
-      {"gov.je", 0},
-#line 3221 "effective_tld_names.gperf"
-      {"ug", 0},
-#line 1040 "effective_tld_names.gperf"
-      {"fnd.br", 0},
-#line 2144 "effective_tld_names.gperf"
-      {"net.je", 0},
-#line 79 "effective_tld_names.gperf"
-      {"aerobatic.aero", 0},
-#line 2085 "effective_tld_names.gperf"
-      {"navuotna.no", 0},
-#line 1155 "effective_tld_names.gperf"
-      {"go.tj", 0},
-#line 3056 "effective_tld_names.gperf"
-      {"svalbard.no", 0},
-#line 1821 "effective_tld_names.gperf"
-      {"mad.museum", 0},
-#line 1125 "effective_tld_names.gperf"
-      {"gh", 0},
-#line 1771 "effective_tld_names.gperf"
-      {"lk", 0},
-#line 260 "effective_tld_names.gperf"
-      {"ba", 0},
-#line 364 "effective_tld_names.gperf"
-      {"bo.it", 0},
-#line 572 "effective_tld_names.gperf"
-      {"co.tj", 0},
-#line 3004 "effective_tld_names.gperf"
-      {"stange.no", 0},
-#line 467 "effective_tld_names.gperf"
-      {"ch", 0},
-#line 1873 "effective_tld_names.gperf"
-      {"med.ee", 0},
-#line 936 "effective_tld_names.gperf"
-      {"england.museum", 0},
-#line 1218 "effective_tld_names.gperf"
-      {"gov.ir", 0},
-#line 1404 "effective_tld_names.gperf"
-      {"hobol.no", 0},
-#line 2877 "effective_tld_names.gperf"
-      {"sd.cn", 0},
-#line 2142 "effective_tld_names.gperf"
-      {"net.ir", 0},
-#line 1310 "effective_tld_names.gperf"
-      {"gs.nl.no", 0},
-#line 323 "effective_tld_names.gperf"
-      {"bi", 0},
-#line 1430 "effective_tld_names.gperf"
-      {"hurum.no", 0},
-#line 383 "effective_tld_names.gperf"
-      {"br.it", 0},
-#line 2916 "effective_tld_names.gperf"
-      {"siljan.no", 0},
-#line 1209 "effective_tld_names.gperf"
-      {"gov.gh", 0},
-#line 2460 "effective_tld_names.gperf"
-      {"orskog.no", 0},
-#line 839 "effective_tld_names.gperf"
-      {"edu.gh", 0},
-#line 2349 "effective_tld_names.gperf"
-      {"orenburg.ru", 0},
-#line 362 "effective_tld_names.gperf"
-      {"bn.it", 0},
-#line 712 "effective_tld_names.gperf"
-      {"corvette.museum", 0},
-#line 1704 "effective_tld_names.gperf"
-      {"l.bg", 0},
-#line 1856 "effective_tld_names.gperf"
-      {"matera.it", 0},
-#line 618 "effective_tld_names.gperf"
-      {"com.gh", 0},
-#line 1313 "effective_tld_names.gperf"
-      {"gs.ol.no", 0},
-#line 116 "effective_tld_names.gperf"
-      {"al.us", 0},
-#line 2950 "effective_tld_names.gperf"
-      {"sn.cn", 0},
-#line 406 "effective_tld_names.gperf"
-      {"bs.it", 0},
-#line 525 "effective_tld_names.gperf"
-      {"cl.it", 0},
-#line 1023 "effective_tld_names.gperf"
-      {"fk", 2},
-#line 1148 "effective_tld_names.gperf"
-      {"go.ci", 0},
-#line 1469 "effective_tld_names.gperf"
-      {"ind.in", 0},
-#line 811 "effective_tld_names.gperf"
-      {"ed.ci", 0},
-#line 1963 "effective_tld_names.gperf"
-      {"mk.ua", 0},
-#line 1932 "effective_tld_names.gperf"
-      {"mil.kr", 0},
-#line 911 "effective_tld_names.gperf"
-      {"egersund.no", 0},
-#line 546 "effective_tld_names.gperf"
-      {"co.ci", 0},
-#line 2921 "effective_tld_names.gperf"
-      {"sk", 0},
-#line 1944 "effective_tld_names.gperf"
-      {"mil.sy", 0},
-#line 1923 "effective_tld_names.gperf"
-      {"mil.ec", 0},
-#line 1794 "effective_tld_names.gperf"
-      {"ltd.co.im", 0},
-#line 2980 "effective_tld_names.gperf"
-      {"sos.pl", 0},
-#line 1102 "effective_tld_names.gperf"
-      {"gaular.no", 0},
-#line 1846 "effective_tld_names.gperf"
-      {"marker.no", 0},
-#line 1924 "effective_tld_names.gperf"
-      {"mil.ge", 0},
-#line 2393 "effective_tld_names.gperf"
-      {"org.je", 0},
-#line 971 "effective_tld_names.gperf"
-      {"f.bg", 0},
-#line 2798 "effective_tld_names.gperf"
-      {"s.bg", 0},
-#line 62 "effective_tld_names.gperf"
-      {"aca.pro", 0},
-#line 226 "effective_tld_names.gperf"
-      {"astrakhan.ru", 0},
-#line 1942 "effective_tld_names.gperf"
-      {"mil.rw", 0},
-#line 99 "effective_tld_names.gperf"
-      {"aid.pl", 0},
-#line 3631 "effective_tld_names.gperf"
-      {"za.com", 0},
-#line 2335 "effective_tld_names.gperf"
-      {"or.ci", 0},
-#line 2391 "effective_tld_names.gperf"
-      {"org.ir", 0},
-#line 336 "effective_tld_names.gperf"
-      {"bir.ru", 0},
-#line 718 "effective_tld_names.gperf"
-      {"cpa.pro", 0},
-#line 320 "effective_tld_names.gperf"
-      {"bg", 0},
-#line 1333 "effective_tld_names.gperf"
-      {"gw", 0},
-#line 1444 "effective_tld_names.gperf"
-      {"id.ly", 0},
-#line 1472 "effective_tld_names.gperf"
-      {"indian.museum", 0},
-#line 1539 "effective_tld_names.gperf"
-      {"iveland.no", 0},
-#line 165 "effective_tld_names.gperf"
-      {"aquila.it", 0},
-#line 1311 "effective_tld_names.gperf"
-      {"gs.nt.no", 0},
-#line 970 "effective_tld_names.gperf"
-      {"express.aero", 0},
-#line 1196 "effective_tld_names.gperf"
-      {"gov.cd", 0},
-#line 2253 "effective_tld_names.gperf"
-      {"nord-fron.no", 0},
-#line 2306 "effective_tld_names.gperf"
-      {"odo.br", 0},
-#line 2379 "effective_tld_names.gperf"
-      {"org.gh", 0},
-#line 2977 "effective_tld_names.gperf"
-      {"sorreisa.no", 0},
-#line 3246 "effective_tld_names.gperf"
-      {"usa.museum", 0},
-#line 964 "effective_tld_names.gperf"
-      {"evenes.no", 0},
-#line 2040 "effective_tld_names.gperf"
-      {"nacion.ar", 1},
-#line 3361 "effective_tld_names.gperf"
-      {"w.bg", 0},
-#line 1315 "effective_tld_names.gperf"
-      {"gs.rl.no", 0},
-#line 907 "effective_tld_names.gperf"
-      {"educator.aero", 0},
-#line 2214 "effective_tld_names.gperf"
-      {"nh.us", 0},
-#line 1167 "effective_tld_names.gperf"
-      {"gok.pk", 0},
-#line 2693 "effective_tld_names.gperf"
-      {"qc.ca", 0},
-#line 2067 "effective_tld_names.gperf"
-      {"narvik.no", 0},
-#line 3650 "effective_tld_names.gperf"
-      {"zw", 2},
-#line 570 "effective_tld_names.gperf"
-      {"co.sz", 0},
-#line 1097 "effective_tld_names.gperf"
-      {"gamvik.no", 0},
-#line 1757 "effective_tld_names.gperf"
-      {"lib.ee", 0},
-#line 1845 "effective_tld_names.gperf"
-      {"maritimo.museum", 0},
-#line 1878 "effective_tld_names.gperf"
-      {"med.pro", 0},
-#line 2211 "effective_tld_names.gperf"
-      {"ngo.lk", 0},
-#line 1844 "effective_tld_names.gperf"
-      {"maritime.museum", 0},
-#line 46 "effective_tld_names.gperf"
-      {"ac.me", 0},
-#line 1018 "effective_tld_names.gperf"
-      {"fitjar.no", 0},
-#line 1274 "effective_tld_names.gperf"
-      {"gov.ua", 0},
-#line 2310 "effective_tld_names.gperf"
-      {"oh.us", 0},
-#line 900 "effective_tld_names.gperf"
-      {"edu.ua", 0},
-#line 1787 "effective_tld_names.gperf"
-      {"louvre.museum", 0},
-#line 719 "effective_tld_names.gperf"
-      {"cq.cn", 0},
-#line 1843 "effective_tld_names.gperf"
-      {"marine.ru", 0},
-#line 2195 "effective_tld_names.gperf"
-      {"net.ua", 0},
-#line 2940 "effective_tld_names.gperf"
-      {"sl", 0},
-#line 682 "effective_tld_names.gperf"
-      {"com.ua", 0},
-#line 423 "effective_tld_names.gperf"
-      {"bz.it", 0},
-#line 1101 "effective_tld_names.gperf"
-      {"gateway.museum", 0},
-#line 454 "effective_tld_names.gperf"
-      {"catering.aero", 0},
-#line 1947 "effective_tld_names.gperf"
-      {"mil.tw", 0},
-#line 1301 "effective_tld_names.gperf"
-      {"gs.aa.no", 0},
-#line 2691 "effective_tld_names.gperf"
-      {"q.bg", 0},
-#line 499 "effective_tld_names.gperf"
-      {"cinema.museum", 0},
-#line 1900 "effective_tld_names.gperf"
-      {"mh", 0},
-#line 3644 "effective_tld_names.gperf"
-      {"zlg.br", 0},
-#line 703 "effective_tld_names.gperf"
-      {"convent.museum", 0},
-#line 925 "effective_tld_names.gperf"
-      {"elverum.no", 0},
-#line 3624 "effective_tld_names.gperf"
-      {"yosemite.museum", 0},
-#line 2880 "effective_tld_names.gperf"
-      {"se.com", 0},
-#line 246 "effective_tld_names.gperf"
-      {"automotive.museum", 0},
-#line 1319 "effective_tld_names.gperf"
-      {"gs.tm.no", 0},
-#line 340 "effective_tld_names.gperf"
-      {"biz", 0},
-#line 1789 "effective_tld_names.gperf"
-      {"loyalist.museum", 0},
-#line 114 "effective_tld_names.gperf"
-      {"al.it", 0},
-#line 1359 "effective_tld_names.gperf"
-      {"haram.no", 0},
-#line 1262 "effective_tld_names.gperf"
-      {"gov.sd", 0},
-#line 1996 "effective_tld_names.gperf"
-      {"mosreg.ru", 0},
-#line 891 "effective_tld_names.gperf"
-      {"edu.sd", 0},
-#line 2846 "effective_tld_names.gperf"
-      {"sc.cn", 0},
-#line 2267 "effective_tld_names.gperf"
-      {"notteroy.no", 0},
-#line 1006 "effective_tld_names.gperf"
-      {"fin.ec", 0},
-#line 2183 "effective_tld_names.gperf"
-      {"net.sd", 0},
-#line 968 "effective_tld_names.gperf"
-      {"exhibition.museum", 0},
-#line 671 "effective_tld_names.gperf"
-      {"com.sd", 0},
-#line 213 "effective_tld_names.gperf"
-      {"assisi.museum", 0},
-#line 345 "effective_tld_names.gperf"
-      {"biz.pk", 0},
-#line 170 "effective_tld_names.gperf"
-      {"arboretum.museum", 0},
-#line 2276 "effective_tld_names.gperf"
-      {"nsk.ru", 0},
-#line 1865 "effective_tld_names.gperf"
-      {"md.ci", 0},
-#line 2261 "effective_tld_names.gperf"
-      {"norilsk.ru", 0},
-#line 1122 "effective_tld_names.gperf"
-      {"gf", 0},
-#line 1805 "effective_tld_names.gperf"
-      {"lunner.no", 0},
-#line 3064 "effective_tld_names.gperf"
-      {"sx.cn", 0},
-#line 1320 "effective_tld_names.gperf"
-      {"gs.tr.no", 0},
-#line 2208 "effective_tld_names.gperf"
-      {"nf", 0},
-#line 1431 "effective_tld_names.gperf"
-      {"hvaler.no", 0},
-#line 2451 "effective_tld_names.gperf"
-      {"org.ua", 0},
-#line 465 "effective_tld_names.gperf"
-      {"cf", 0},
-#line 1203 "effective_tld_names.gperf"
-      {"gov.dm", 0},
-#line 833 "effective_tld_names.gperf"
-      {"edu.dm", 0},
-#line 569 "effective_tld_names.gperf"
-      {"co.st", 0},
-#line 1994 "effective_tld_names.gperf"
-      {"mosjoen.no", 0},
-#line 926 "effective_tld_names.gperf"
-      {"embroidery.museum", 0},
-#line 1053 "effective_tld_names.gperf"
-      {"fosnes.no", 0},
-#line 2127 "effective_tld_names.gperf"
-      {"net.dm", 0},
-#line 1351 "effective_tld_names.gperf"
-      {"hamar.no", 0},
-#line 1517 "effective_tld_names.gperf"
-      {"interactive.museum", 0},
-#line 611 "effective_tld_names.gperf"
-      {"com.dm", 0},
-#line 2919 "effective_tld_names.gperf"
-      {"siracusa.it", 0},
-#line 1887 "effective_tld_names.gperf"
-      {"medical.museum", 0},
-#line 3619 "effective_tld_names.gperf"
-      {"yk.ca", 0},
-#line 1983 "effective_tld_names.gperf"
-      {"modern.museum", 0},
-#line 1950 "effective_tld_names.gperf"
-      {"milano.it", 0},
-#line 253 "effective_tld_names.gperf"
-      {"aw", 0},
-#line 1407 "effective_tld_names.gperf"
-      {"hokksund.no", 0},
-#line 545 "effective_tld_names.gperf"
-      {"co.bw", 0},
-#line 1024 "effective_tld_names.gperf"
-      {"fl.us", 0},
-#line 963 "effective_tld_names.gperf"
-      {"evenassi.no", 0},
-#line 250 "effective_tld_names.gperf"
-      {"aviation.museum", 0},
-#line 2029 "effective_tld_names.gperf"
-      {"mw", 0},
-#line 1060 "effective_tld_names.gperf"
-      {"frankfurt.museum", 0},
-#line 261 "effective_tld_names.gperf"
-      {"ba.it", 0},
-#line 2061 "effective_tld_names.gperf"
-      {"nannestad.no", 0},
-#line 232 "effective_tld_names.gperf"
-      {"ato.br", 0},
-#line 468 "effective_tld_names.gperf"
-      {"ch.it", 0},
-#line 2101 "effective_tld_names.gperf"
-      {"nes.buskerud.no", 0},
-#line 1770 "effective_tld_names.gperf"
-      {"livorno.it", 0},
-#line 2990 "effective_tld_names.gperf"
-      {"spy.museum", 0},
-#line 1428 "effective_tld_names.gperf"
-      {"humanities.museum", 0},
-#line 93 "effective_tld_names.gperf"
-      {"agrinet.tn", 0},
-#line 2438 "effective_tld_names.gperf"
-      {"org.sd", 0},
-#line 324 "effective_tld_names.gperf"
-      {"bi.it", 0},
-#line 2975 "effective_tld_names.gperf"
-      {"sor-varanger.no", 0},
-#line 3261 "effective_tld_names.gperf"
-      {"utsira.no", 0},
-#line 3214 "effective_tld_names.gperf"
-      {"u.se", 0},
-#line 727 "effective_tld_names.gperf"
-      {"crimea.ua", 0},
-#line 76 "effective_tld_names.gperf"
-      {"aejrie.no", 0},
-#line 2238 "effective_tld_names.gperf"
-      {"nom.ad", 0},
-#line 2924 "effective_tld_names.gperf"
-      {"skanland.no", 0},
-#line 2843 "effective_tld_names.gperf"
-      {"savona.it", 0},
-#line 2954 "effective_tld_names.gperf"
-      {"snoasa.no", 0},
-#line 1375 "effective_tld_names.gperf"
-      {"hemne.no", 0},
-#line 2979 "effective_tld_names.gperf"
-      {"sorum.no", 0},
-#line 1321 "effective_tld_names.gperf"
-      {"gs.va.no", 0},
-#line 349 "effective_tld_names.gperf"
-      {"biz.tt", 0},
-#line 2372 "effective_tld_names.gperf"
-      {"org.dm", 0},
-#line 1399 "effective_tld_names.gperf"
-      {"hl.no", 0},
-#line 201 "effective_tld_names.gperf"
-      {"aseral.no", 0},
-#line 2230 "effective_tld_names.gperf"
-      {"nl.no", 0},
-#line 2894 "effective_tld_names.gperf"
-      {"settlers.museum", 0},
-#line 2099 "effective_tld_names.gperf"
-      {"nel.uk", 1},
-#line 1810 "effective_tld_names.gperf"
-      {"luzern.museum", 0},
-#line 1372 "effective_tld_names.gperf"
-      {"hellas.museum", 0},
-#line 2084 "effective_tld_names.gperf"
-      {"navigation.aero", 0},
-#line 2229 "effective_tld_names.gperf"
-      {"nl.ca", 0},
-#line 58 "effective_tld_names.gperf"
-      {"ac.tj", 0},
-#line 342 "effective_tld_names.gperf"
-      {"biz.ki", 0},
-#line 3250 "effective_tld_names.gperf"
-      {"usculture.museum", 0},
-#line 2893 "effective_tld_names.gperf"
-      {"settlement.museum", 0},
-#line 1927 "effective_tld_names.gperf"
-      {"mil.in", 0},
-#line 199 "effective_tld_names.gperf"
-      {"ascoli-piceno.it", 0},
-#line 1747 "effective_tld_names.gperf"
-      {"lenvik.no", 0},
-#line 2316 "effective_tld_names.gperf"
-      {"ol.no", 0},
-#line 3015 "effective_tld_names.gperf"
-      {"stavern.no", 0},
-#line 419 "effective_tld_names.gperf"
-      {"bygland.no", 0},
-#line 2243 "effective_tld_names.gperf"
-      {"nom.fr", 0},
-#line 616 "effective_tld_names.gperf"
-      {"com.fr", 0},
-#line 1726 "effective_tld_names.gperf"
-      {"latina.it", 0},
-#line 1213 "effective_tld_names.gperf"
-      {"gov.hk", 0},
-#line 1447 "effective_tld_names.gperf"
-      {"idv.hk", 0},
-#line 844 "effective_tld_names.gperf"
-      {"edu.hk", 0},
-#line 845 "effective_tld_names.gperf"
-      {"edu.hn", 0},
-#line 321 "effective_tld_names.gperf"
-      {"bg.it", 0},
-#line 1110 "effective_tld_names.gperf"
-      {"gdansk.pl", 0},
-#line 846 "effective_tld_names.gperf"
-      {"edu.ht", 0},
-#line 3614 "effective_tld_names.gperf"
-      {"yamal.ru", 0},
-#line 2136 "effective_tld_names.gperf"
-      {"net.hk", 0},
-#line 2137 "effective_tld_names.gperf"
-      {"net.hn", 0},
-#line 2326 "effective_tld_names.gperf"
-      {"online.museum", 0},
-#line 39 "effective_tld_names.gperf"
-      {"ac.gn", 0},
-#line 2138 "effective_tld_names.gperf"
-      {"net.ht", 0},
-#line 408 "effective_tld_names.gperf"
-      {"bu.no", 0},
-#line 624 "effective_tld_names.gperf"
-      {"com.hk", 0},
-#line 625 "effective_tld_names.gperf"
-      {"com.hn", 0},
-#line 627 "effective_tld_names.gperf"
-      {"com.ht", 0},
-#line 164 "effective_tld_names.gperf"
-      {"aquarium.museum", 0},
-#line 83 "effective_tld_names.gperf"
-      {"af", 0},
-#line 91 "effective_tld_names.gperf"
-      {"agriculture.museum", 0},
-#line 2009 "effective_tld_names.gperf"
-      {"msk.ru", 0},
-#line 2899 "effective_tld_names.gperf"
-      {"sh", 0},
-#line 2854 "effective_tld_names.gperf"
-      {"sch.jo", 0},
-#line 36 "effective_tld_names.gperf"
-      {"ac.ci", 0},
-#line 1161 "effective_tld_names.gperf"
-      {"gob.hn", 0},
-#line 2062 "effective_tld_names.gperf"
-      {"naples.it", 0},
-#line 3224 "effective_tld_names.gperf"
-      {"uk", 2},
-#line 1397 "effective_tld_names.gperf"
-      {"hk.cn", 0},
-#line 1820 "effective_tld_names.gperf"
-      {"macerata.it", 0},
-#line 976 "effective_tld_names.gperf"
-      {"fareast.ru", 0},
-#line 248 "effective_tld_names.gperf"
-      {"avellino.it", 0},
-#line 3002 "effective_tld_names.gperf"
-      {"stalbans.museum", 0},
-#line 1432 "effective_tld_names.gperf"
-      {"hyllestad.no", 0},
-#line 259 "effective_tld_names.gperf"
-      {"b.se", 0},
-#line 1998 "effective_tld_names.gperf"
-      {"mosvik.no", 0},
-#line 350 "effective_tld_names.gperf"
-      {"biz.vn", 0},
-#line 1951 "effective_tld_names.gperf"
-      {"military.museum", 0},
-#line 2889 "effective_tld_names.gperf"
-      {"seljord.no", 0},
-#line 188 "effective_tld_names.gperf"
-      {"artcenter.museum", 0},
-#line 1982 "effective_tld_names.gperf"
-      {"modena.it", 0},
-#line 3213 "effective_tld_names.gperf"
-      {"u.bg", 0},
-#line 92 "effective_tld_names.gperf"
-      {"agrigento.it", 0},
-#line 1939 "effective_tld_names.gperf"
-      {"mil.ph", 0},
-#line 716 "effective_tld_names.gperf"
-      {"countryestate.museum", 0},
-#line 543 "effective_tld_names.gperf"
-      {"co.ba", 0},
-#line 1896 "effective_tld_names.gperf"
-      {"mesaverde.museum", 0},
-#line 1450 "effective_tld_names.gperf"
-      {"if.ua", 0},
-#line 1345 "effective_tld_names.gperf"
-      {"habmer.no", 0},
-#line 724 "effective_tld_names.gperf"
-      {"creation.museum", 0},
-#line 1929 "effective_tld_names.gperf"
-      {"mil.jo", 0},
-#line 2270 "effective_tld_names.gperf"
-      {"novosibirsk.ru", 0},
-#line 1984 "effective_tld_names.gperf"
-      {"modum.no", 0},
-#line 2384 "effective_tld_names.gperf"
-      {"org.hk", 0},
-#line 2385 "effective_tld_names.gperf"
-      {"org.hn", 0},
-#line 2801 "effective_tld_names.gperf"
-      {"sa.com", 0},
-#line 2959 "effective_tld_names.gperf"
-      {"society.museum", 0},
-#line 2386 "effective_tld_names.gperf"
-      {"org.ht", 0},
-#line 1529 "effective_tld_names.gperf"
-      {"isa.us", 0},
-#line 2694 "effective_tld_names.gperf"
-      {"qc.com", 0},
-#line 1287 "effective_tld_names.gperf"
-      {"grane.no", 0},
-#line 2928 "effective_tld_names.gperf"
-      {"ski.museum", 0},
-#line 1724 "effective_tld_names.gperf"
-      {"larvik.no", 0},
-#line 2033 "effective_tld_names.gperf"
-      {"mytis.ru", 0},
-#line 1953 "effective_tld_names.gperf"
-      {"mincom.tn", 0},
-#line 56 "effective_tld_names.gperf"
-      {"ac.sz", 0},
-#line 2220 "effective_tld_names.gperf"
-      {"niepce.museum", 0},
-#line 174 "effective_tld_names.gperf"
-      {"ardal.no", 0},
-#line 2308 "effective_tld_names.gperf"
-      {"off.ai", 0},
-#line 115 "effective_tld_names.gperf"
-      {"al.no", 0},
-#line 1567 "effective_tld_names.gperf"
-      {"jo", 0},
-#line 2226 "effective_tld_names.gperf"
-      {"nj.us", 0},
-#line 2277 "effective_tld_names.gperf"
-      {"nsn.us", 0},
-#line 1550 "effective_tld_names.gperf"
-      {"je", 0},
-#line 2387 "effective_tld_names.gperf"
-      {"org.hu", 0},
-#line 463 "effective_tld_names.gperf"
-      {"center.museum", 0},
-#line 104 "effective_tld_names.gperf"
-      {"aircraft.aero", 0},
-#line 1370 "effective_tld_names.gperf"
-      {"health.vn", 0},
-#line 295 "effective_tld_names.gperf"
-      {"bc.ca", 0},
-#line 1783 "effective_tld_names.gperf"
-      {"loppa.no", 0},
-#line 153 "effective_tld_names.gperf"
-      {"anthro.museum", 0},
-#line 2462 "effective_tld_names.gperf"
-      {"oryol.ru", 0},
-#line 1398 "effective_tld_names.gperf"
-      {"hl.cn", 0},
-#line 1363 "effective_tld_names.gperf"
-      {"hasvik.no", 0},
-#line 2929 "effective_tld_names.gperf"
-      {"ski.no", 0},
-#line 922 "effective_tld_names.gperf"
-      {"elburg.museum", 0},
-#line 2213 "effective_tld_names.gperf"
-      {"ngo.pl", 0},
-#line 526 "effective_tld_names.gperf"
-      {"clinton.museum", 0},
-#line 1833 "effective_tld_names.gperf"
-      {"malvik.no", 0},
-#line 1393 "effective_tld_names.gperf"
-      {"hitra.no", 0},
-#line 3019 "effective_tld_names.gperf"
-      {"steigen.no", 0},
-#line 449 "effective_tld_names.gperf"
-      {"castle.museum", 0},
-#line 200 "effective_tld_names.gperf"
-      {"ascolipiceno.it", 0},
-#line 2102 "effective_tld_names.gperf"
-      {"nesna.no", 0},
-#line 1954 "effective_tld_names.gperf"
-      {"miners.museum", 0},
-#line 413 "effective_tld_names.gperf"
-      {"bus.museum", 0},
-#line 2967 "effective_tld_names.gperf"
-      {"somna.no", 0},
-#line 519 "effective_tld_names.gperf"
-      {"civilisation.museum", 0},
-#line 549 "effective_tld_names.gperf"
-      {"co.gy", 0},
-#line 3059 "effective_tld_names.gperf"
-      {"svizzera.museum", 0},
-#line 3265 "effective_tld_names.gperf"
-      {"uy.com", 0},
-#line 1309 "effective_tld_names.gperf"
-      {"gs.mr.no", 0},
-#line 258 "effective_tld_names.gperf"
-      {"b.bg", 0},
-#line 88 "effective_tld_names.gperf"
-      {"agents.aero", 0},
-#line 2944 "effective_tld_names.gperf"
-      {"slg.br", 0},
-#line 184 "effective_tld_names.gperf"
-      {"art.ht", 0},
-#line 2836 "effective_tld_names.gperf"
-      {"sarpsborg.no", 0},
-#line 3025 "effective_tld_names.gperf"
-      {"stokke.no", 0},
-#line 82 "effective_tld_names.gperf"
-      {"aeroport.fr", 0},
-#line 2328 "effective_tld_names.gperf"
-      {"openair.museum", 0},
-#line 347 "effective_tld_names.gperf"
-      {"biz.pr", 0},
-#line 1413 "effective_tld_names.gperf"
-      {"honefoss.no", 0},
-#line 1815 "effective_tld_names.gperf"
-      {"lyngen.no", 0},
-#line 1827 "effective_tld_names.gperf"
-      {"maintenance.aero", 0},
-#line 1361 "effective_tld_names.gperf"
-      {"harstad.no", 0},
-#line 3244 "effective_tld_names.gperf"
-      {"us.com", 0},
-#line 1520 "effective_tld_names.gperf"
-      {"ip6.arpa", 0},
-#line 360 "effective_tld_names.gperf"
-      {"bmd.br", 0},
-#line 1737 "effective_tld_names.gperf"
-      {"lebork.pl", 0},
-#line 2992 "effective_tld_names.gperf"
-      {"square.museum", 0},
-#line 1743 "effective_tld_names.gperf"
-      {"leirvik.no", 0},
-#line 450 "effective_tld_names.gperf"
-      {"castres.museum", 0},
-#line 2943 "effective_tld_names.gperf"
-      {"sld.pa", 0},
-#line 53 "effective_tld_names.gperf"
-      {"ac.ru", 0},
-#line 2513 "effective_tld_names.gperf"
-      {"pe", 0},
-#line 131 "effective_tld_names.gperf"
-      {"am.br", 0},
-#line 2688 "effective_tld_names.gperf"
-      {"py", 2},
-#line 1415 "effective_tld_names.gperf"
-      {"horology.museum", 0},
-#line 1081 "effective_tld_names.gperf"
-      {"fuoisku.no", 0},
-#line 1154 "effective_tld_names.gperf"
-      {"go.th", 0},
-#line 2587 "effective_tld_names.gperf"
-      {"pr", 0},
-#line 571 "effective_tld_names.gperf"
-      {"co.th", 0},
-#line 346 "effective_tld_names.gperf"
-      {"biz.pl", 0},
-#line 2559 "effective_tld_names.gperf"
-      {"pn", 0},
-#line 3247 "effective_tld_names.gperf"
-      {"usantiques.museum", 0},
-#line 1297 "effective_tld_names.gperf"
-      {"grozny.ru", 0},
-#line 2953 "effective_tld_names.gperf"
-      {"snillfjord.no", 0},
-#line 2853 "effective_tld_names.gperf"
-      {"sch.je", 0},
-#line 1410 "effective_tld_names.gperf"
-      {"holmestrand.no", 0},
-#line 1235 "effective_tld_names.gperf"
-      {"gov.lv", 0},
-#line 863 "effective_tld_names.gperf"
-      {"edu.lv", 0},
-#line 974 "effective_tld_names.gperf"
-      {"family.museum", 0},
-#line 1417 "effective_tld_names.gperf"
-      {"hotel.hu", 0},
-#line 2672 "effective_tld_names.gperf"
-      {"ps", 0},
-#line 2156 "effective_tld_names.gperf"
-      {"net.lv", 0},
-#line 2083 "effective_tld_names.gperf"
-      {"naval.museum", 0},
-#line 2676 "effective_tld_names.gperf"
-      {"pt", 0},
-#line 1566 "effective_tld_names.gperf"
-      {"jm", 2},
-#line 1464 "effective_tld_names.gperf"
-      {"in.th", 0},
-#line 642 "effective_tld_names.gperf"
-      {"com.lv", 0},
-#line 1289 "effective_tld_names.gperf"
-      {"gratangen.no", 0},
-#line 107 "effective_tld_names.gperf"
-      {"airport.aero", 0},
-#line 2922 "effective_tld_names.gperf"
-      {"sk.ca", 0},
-#line 2852 "effective_tld_names.gperf"
-      {"sch.ir", 0},
-#line 459 "effective_tld_names.gperf"
-      {"cci.fr", 0},
-#line 1882 "effective_tld_names.gperf"
-      {"medecin.km", 0},
-#line 2081 "effective_tld_names.gperf"
-      {"naumburg.museum", 0},
-#line 2861 "effective_tld_names.gperf"
-      {"schokoladen.museum", 0},
-#line 2516 "effective_tld_names.gperf"
-      {"pe.kr", 0},
-#line 2343 "effective_tld_names.gperf"
-      {"or.th", 0},
-#line 562 "effective_tld_names.gperf"
-      {"co.mu", 0},
-#line 1025 "effective_tld_names.gperf"
-      {"fla.no", 0},
-#line 209 "effective_tld_names.gperf"
-      {"asnes.no", 0},
-#line 2518 "effective_tld_names.gperf"
-      {"per.la", 0},
-#line 1365 "effective_tld_names.gperf"
-      {"haugesund.no", 0},
-#line 1807 "effective_tld_names.gperf"
-      {"luster.no", 0},
-#line 382 "effective_tld_names.gperf"
-      {"br.com", 0},
-#line 2806 "effective_tld_names.gperf"
-      {"safety.aero", 0},
-#line 927 "effective_tld_names.gperf"
-      {"emergency.aero", 0},
-#line 1058 "effective_tld_names.gperf"
-      {"frana.no", 0},
-#line 1955 "effective_tld_names.gperf"
-      {"mining.museum", 0},
-#line 2315 "effective_tld_names.gperf"
-      {"oksnes.no", 0},
-#line 1000 "effective_tld_names.gperf"
-      {"fie.ee", 0},
-#line 1768 "effective_tld_names.gperf"
-      {"living.museum", 0},
-#line 1011 "effective_tld_names.gperf"
-      {"finnoy.no", 0},
-#line 2076 "effective_tld_names.gperf"
-      {"naturalsciences.museum", 0},
-#line 343 "effective_tld_names.gperf"
-      {"biz.mw", 0},
-#line 741 "effective_tld_names.gperf"
-      {"cyber.museum", 0},
-#line 1741 "effective_tld_names.gperf"
-      {"leikanger.no", 0},
-#line 2340 "effective_tld_names.gperf"
-      {"or.mu", 0},
-#line 1052 "effective_tld_names.gperf"
-      {"forum.hu", 0},
-#line 87 "effective_tld_names.gperf"
-      {"agdenes.no", 0},
-#line 2407 "effective_tld_names.gperf"
-      {"org.lv", 0},
-#line 1429 "effective_tld_names.gperf"
-      {"hurdal.no", 0},
-#line 986 "effective_tld_names.gperf"
-      {"federation.aero", 0},
-#line 996 "effective_tld_names.gperf"
-      {"fhv.se", 0},
-#line 2200 "effective_tld_names.gperf"
-      {"neues.museum", 0},
-#line 1272 "effective_tld_names.gperf"
-      {"gov.tv", 0},
-#line 531 "effective_tld_names.gperf"
-      {"cmw.ru", 0},
-#line 1925 "effective_tld_names.gperf"
-      {"mil.gh", 0},
-#line 2021 "effective_tld_names.gperf"
-      {"museet.museum", 0},
-#line 1049 "effective_tld_names.gperf"
-      {"forsand.no", 0},
-#line 2193 "effective_tld_names.gperf"
-      {"net.tv", 0},
-#line 1070 "effective_tld_names.gperf"
-      {"froland.no", 0},
-#line 301 "effective_tld_names.gperf"
-      {"beauxarts.museum", 0},
-#line 2589 "effective_tld_names.gperf"
-      {"pr.us", 0},
-#line 680 "effective_tld_names.gperf"
-      {"com.tv", 0},
-#line 96 "effective_tld_names.gperf"
-      {"ah.no", 0},
-#line 2942 "effective_tld_names.gperf"
-      {"slattum.no", 0},
-#line 2817 "effective_tld_names.gperf"
-      {"salzburg.museum", 0},
-#line 3248 "effective_tld_names.gperf"
-      {"usarts.museum", 0},
-#line 626 "effective_tld_names.gperf"
-      {"com.hr", 0},
-#line 281 "effective_tld_names.gperf"
-      {"bar.pro", 0},
-#line 2972 "effective_tld_names.gperf"
-      {"sor-aurdal.no", 0},
-#line 2858 "effective_tld_names.gperf"
-      {"sch.uk", 2},
-#line 1880 "effective_tld_names.gperf"
-      {"med.sd", 0},
-#line 462 "effective_tld_names.gperf"
-      {"celtic.museum", 0},
-#line 227 "effective_tld_names.gperf"
-      {"astronomy.museum", 0},
-#line 2456 "effective_tld_names.gperf"
-      {"oristano.it", 0},
-#line 2962 "effective_tld_names.gperf"
-      {"sogne.no", 0},
-#line 1063 "effective_tld_names.gperf"
-      {"freemasonry.museum", 0},
-#line 249 "effective_tld_names.gperf"
-      {"averoy.no", 0},
-#line 1019 "effective_tld_names.gperf"
-      {"fj", 2},
-#line 445 "effective_tld_names.gperf"
-      {"cartoonart.museum", 0},
-#line 2459 "effective_tld_names.gperf"
-      {"orland.no", 0},
-#line 1759 "effective_tld_names.gperf"
-      {"lierne.no", 0},
-#line 2863 "effective_tld_names.gperf"
-      {"school.na", 0},
-#line 2461 "effective_tld_names.gperf"
-      {"orsta.no", 0},
-#line 3052 "effective_tld_names.gperf"
-      {"surrey.museum", 0},
-#line 1889 "effective_tld_names.gperf"
-      {"meeres.museum", 0},
-#line 1543 "effective_tld_names.gperf"
-      {"izhevsk.ru", 0},
-#line 1010 "effective_tld_names.gperf"
-      {"finland.museum", 0},
-#line 456 "effective_tld_names.gperf"
-      {"cbg.ru", 0},
-#line 357 "effective_tld_names.gperf"
-      {"bl.uk", 1},
-#line 2860 "effective_tld_names.gperf"
-      {"schoenbrunn.museum", 0},
-#line 842 "effective_tld_names.gperf"
-      {"edu.gp", 0},
-#line 2814 "effective_tld_names.gperf"
-      {"salerno.it", 0},
-#line 2133 "effective_tld_names.gperf"
-      {"net.gp", 0},
-#line 270 "effective_tld_names.gperf"
-      {"baikal.ru", 0},
-#line 621 "effective_tld_names.gperf"
-      {"com.gp", 0},
-#line 2449 "effective_tld_names.gperf"
-      {"org.tv", 0},
-#line 1533 "effective_tld_names.gperf"
-      {"isleofman.museum", 0},
-#line 2019 "effective_tld_names.gperf"
-      {"murmansk.ru", 0},
-#line 3226 "effective_tld_names.gperf"
-      {"uk.net", 0},
-#line 2209 "effective_tld_names.gperf"
-      {"nf.ca", 0},
-#line 1038 "effective_tld_names.gperf"
-      {"fm.br", 0},
-#line 3395 "effective_tld_names.gperf"
-      {"wolomin.pl", 0},
-#line 1083 "effective_tld_names.gperf"
-      {"furniture.museum", 0},
-#line 149 "effective_tld_names.gperf"
-      {"andasuolo.no", 0},
-#line 3227 "effective_tld_names.gperf"
-      {"ulan-ude.ru", 0},
-#line 2274 "effective_tld_names.gperf"
-      {"nrw.museum", 0},
-#line 2307 "effective_tld_names.gperf"
-      {"of.no", 0},
-#line 1379 "effective_tld_names.gperf"
-      {"heritage.museum", 0},
-#line 582 "effective_tld_names.gperf"
-      {"coldwar.museum", 0},
-#line 3230 "effective_tld_names.gperf"
-      {"ulm.museum", 0},
-#line 372 "effective_tld_names.gperf"
-      {"bolzano.it", 0},
-#line 3254 "effective_tld_names.gperf"
-      {"ushistory.museum", 0},
-#line 444 "effective_tld_names.gperf"
-      {"carrier.museum", 0},
-#line 1573 "effective_tld_names.gperf"
-      {"jor.br", 0},
-#line 55 "effective_tld_names.gperf"
-      {"ac.se", 0},
-#line 1548 "effective_tld_names.gperf"
-      {"jar.ru", 0},
-#line 1579 "effective_tld_names.gperf"
-      {"jp", 0},
-#line 1358 "effective_tld_names.gperf"
-      {"hapmir.no", 0},
-#line 2680 "effective_tld_names.gperf"
-      {"pub.sa", 0},
-#line 2077 "effective_tld_names.gperf"
-      {"naturbruksgymn.se", 0},
-#line 2520 "effective_tld_names.gperf"
-      {"per.sg", 0},
-#line 482 "effective_tld_names.gperf"
-      {"chieti.it", 0},
-#line 293 "effective_tld_names.gperf"
-      {"bauern.museum", 0},
-#line 322 "effective_tld_names.gperf"
-      {"bh", 0},
-#line 2511 "effective_tld_names.gperf"
-      {"pc.pl", 0},
-#line 2486 "effective_tld_names.gperf"
-      {"pa", 0},
-#line 2562 "effective_tld_names.gperf"
-      {"po.it", 0},
-#line 2512 "effective_tld_names.gperf"
-      {"pd.it", 0},
-#line 2515 "effective_tld_names.gperf"
-      {"pe.it", 0},
-#line 1307 "effective_tld_names.gperf"
-      {"gs.hm.no", 0},
-#line 1369 "effective_tld_names.gperf"
-      {"health.museum", 0},
-#line 1385 "effective_tld_names.gperf"
-      {"histoire.museum", 0},
-#line 1749 "effective_tld_names.gperf"
-      {"lesja.no", 0},
-#line 2382 "effective_tld_names.gperf"
-      {"org.gp", 0},
-#line 583 "effective_tld_names.gperf"
-      {"collection.museum", 0},
-#line 2588 "effective_tld_names.gperf"
-      {"pr.it", 0},
-#line 1328 "effective_tld_names.gperf"
-      {"gulen.no", 0},
-#line 1414 "effective_tld_names.gperf"
-      {"hornindal.no", 0},
-#line 90 "effective_tld_names.gperf"
-      {"agrar.hu", 0},
-#line 2678 "effective_tld_names.gperf"
-      {"ptz.ru", 0},
-#line 1823 "effective_tld_names.gperf"
-      {"magadan.ru", 0},
-#line 2560 "effective_tld_names.gperf"
-      {"pn.it", 0},
-#line 71 "effective_tld_names.gperf"
-      {"adult.ht", 0},
-#line 1355 "effective_tld_names.gperf"
-      {"hammerfest.no", 0},
-#line 1306 "effective_tld_names.gperf"
-      {"gs.hl.no", 0},
-#line 1890 "effective_tld_names.gperf"
-      {"meland.no", 0},
-#line 356 "effective_tld_names.gperf"
-      {"bl.it", 0},
-#line 2961 "effective_tld_names.gperf"
-      {"sogndal.no", 0},
-#line 1348 "effective_tld_names.gperf"
-      {"halden.no", 0},
-#line 1147 "effective_tld_names.gperf"
-      {"gniezno.pl", 0},
-#line 2677 "effective_tld_names.gperf"
-      {"pt.it", 0},
-#line 2106 "effective_tld_names.gperf"
-      {"nesset.no", 0},
-#line 344 "effective_tld_names.gperf"
-      {"biz.nr", 0},
-#line 95 "effective_tld_names.gperf"
-      {"ah.cn", 0},
-#line 1065 "effective_tld_names.gperf"
-      {"freiburg.museum", 0},
-#line 1099 "effective_tld_names.gperf"
-      {"gangwon.kr", 0},
-#line 1786 "effective_tld_names.gperf"
-      {"loten.no", 0},
-#line 1874 "effective_tld_names.gperf"
-      {"med.ht", 0},
-#line 313 "effective_tld_names.gperf"
-      {"bergen.no", 0},
-#line 1143 "effective_tld_names.gperf"
-      {"gloppen.no", 0},
-#line 3034 "effective_tld_names.gperf"
-      {"strand.no", 0},
-#line 2812 "effective_tld_names.gperf"
-      {"salat.no", 0},
-#line 331 "effective_tld_names.gperf"
-      {"bievat.no", 0},
-#line 3066 "effective_tld_names.gperf"
-      {"sydney.museum", 0},
-#line 2888 "effective_tld_names.gperf"
-      {"selje.no", 0},
-#line 959 "effective_tld_names.gperf"
-      {"etnedal.no", 0},
-#line 455 "effective_tld_names.gperf"
-      {"cb.it", 0},
-#line 2882 "effective_tld_names.gperf"
-      {"seaport.museum", 0},
-#line 1774 "effective_tld_names.gperf"
-      {"loabat.no", 0},
-#line 2819 "effective_tld_names.gperf"
-      {"samnanger.no", 0},
-#line 471 "effective_tld_names.gperf"
-      {"charter.aero", 0},
-#line 57 "effective_tld_names.gperf"
-      {"ac.th", 0},
-#line 2679 "effective_tld_names.gperf"
-      {"pu.it", 0},
-#line 416 "effective_tld_names.gperf"
-      {"bw", 0},
-#line 2529 "effective_tld_names.gperf"
-      {"pg", 2},
-#line 1748 "effective_tld_names.gperf"
-      {"lerdal.no", 0},
-#line 2519 "effective_tld_names.gperf"
-      {"per.nf", 0},
-#line 2480 "effective_tld_names.gperf"
-      {"oxford.museum", 0},
-#line 2583 "effective_tld_names.gperf"
-      {"pp.az", 0},
-#line 1131 "effective_tld_names.gperf"
-      {"giske.no", 0},
-#line 1096 "effective_tld_names.gperf"
-      {"games.hu", 0},
-#line 35 "effective_tld_names.gperf"
-      {"ac.be", 0},
-#line 1467 "effective_tld_names.gperf"
-      {"incheon.kr", 0},
-#line 2862 "effective_tld_names.gperf"
-      {"school.museum", 0},
-#line 132 "effective_tld_names.gperf"
-      {"amber.museum", 0},
-#line 3013 "effective_tld_names.gperf"
-      {"station.museum", 0},
-#line 208 "effective_tld_names.gperf"
-      {"asn.lv", 0},
-#line 2673 "effective_tld_names.gperf"
-      {"psc.br", 0},
-#line 2489 "effective_tld_names.gperf"
-      {"pa.us", 0},
-#line 158 "effective_tld_names.gperf"
-      {"aomori.jp", 2},
-#line 2685 "effective_tld_names.gperf"
-      {"pv.it", 0},
-#line 3014 "effective_tld_names.gperf"
-      {"stavanger.no", 0},
-#line 2212 "effective_tld_names.gperf"
-      {"ngo.ph", 0},
-#line 274 "effective_tld_names.gperf"
-      {"balestrand.no", 0},
-#line 2281 "effective_tld_names.gperf"
-      {"nt.edu.au", 0},
-#line 700 "effective_tld_names.gperf"
-      {"contemporary.museum", 0},
-#line 47 "effective_tld_names.gperf"
-      {"ac.mu", 0},
-#line 309 "effective_tld_names.gperf"
-      {"benevento.it", 0},
-#line 1730 "effective_tld_names.gperf"
-      {"lb", 0},
-#line 379 "effective_tld_names.gperf"
-      {"botany.museum", 0},
-#line 2331 "effective_tld_names.gperf"
-      {"oppdal.no", 0},
-#line 1891 "effective_tld_names.gperf"
-      {"meldal.no", 0},
-#line 1999 "effective_tld_names.gperf"
-      {"motorcycle.museum", 0},
-#line 2835 "effective_tld_names.gperf"
-      {"saratov.ru", 0},
-#line 550 "effective_tld_names.gperf"
-      {"co.hu", 0},
-#line 127 "effective_tld_names.gperf"
-      {"alto-adige.it", 0},
-#line 2690 "effective_tld_names.gperf"
-      {"pz.it", 0},
-#line 2923 "effective_tld_names.gperf"
-      {"skanit.no", 0},
-#line 985 "effective_tld_names.gperf"
-      {"fed.us", 0},
-#line 1457 "effective_tld_names.gperf"
-      {"imageandsound.museum", 0},
-#line 241 "effective_tld_names.gperf"
-      {"austin.museum", 0},
-#line 1580 "effective_tld_names.gperf"
-      {"jpn.com", 0},
-#line 399 "effective_tld_names.gperf"
-      {"brunel.museum", 0},
-#line 242 "effective_tld_names.gperf"
-      {"australia.museum", 0},
-#line 544 "effective_tld_names.gperf"
-      {"co.bi", 0},
-#line 1710 "effective_tld_names.gperf"
-      {"labor.museum", 0},
-#line 2831 "effective_tld_names.gperf"
-      {"santacruz.museum", 0},
-#line 433 "effective_tld_names.gperf"
-      {"cagliari.it", 0},
-#line 1356 "effective_tld_names.gperf"
-      {"handson.museum", 0},
-#line 2510 "effective_tld_names.gperf"
-      {"pc.it", 0},
-#line 2022 "effective_tld_names.gperf"
-      {"museum", 0},
-#line 2844 "effective_tld_names.gperf"
-      {"sb", 0},
-#line 287 "effective_tld_names.gperf"
-      {"barum.no", 0},
-#line 1092 "effective_tld_names.gperf"
-      {"gaivuotna.no", 0},
-#line 2483 "effective_tld_names.gperf"
-      {"oystre-slidre.no", 0},
-#line 1926 "effective_tld_names.gperf"
-      {"mil.hn", 0},
-#line 723 "effective_tld_names.gperf"
-      {"cranbrook.museum", 0},
-#line 987 "effective_tld_names.gperf"
-      {"fedje.no", 0},
-#line 1105 "effective_tld_names.gperf"
-      {"gb.net", 0},
-#line 1902 "effective_tld_names.gperf"
-      {"mi.th", 0},
-#line 2674 "effective_tld_names.gperf"
-      {"psi.br", 0},
-#line 967 "effective_tld_names.gperf"
-      {"exeter.museum", 0},
-#line 1974 "effective_tld_names.gperf"
-      {"moareke.no", 0},
-#line 3241 "effective_tld_names.gperf"
-      {"uri.arpa", 0},
-#line 2097 "effective_tld_names.gperf"
-      {"nebraska.museum", 0},
-#line 1848 "effective_tld_names.gperf"
-      {"marnardal.no", 0},
-#line 2023 "effective_tld_names.gperf"
-      {"museum.no", 0},
-#line 2334 "effective_tld_names.gperf"
-      {"or.bi", 0},
-#line 319 "effective_tld_names.gperf"
-      {"bf", 0},
-#line 2065 "effective_tld_names.gperf"
-      {"naroy.no", 0},
-#line 1047 "effective_tld_names.gperf"
-      {"forli-cesena.it", 0},
-#line 1881 "effective_tld_names.gperf"
-      {"medecin.fr", 0},
-#line 1293 "effective_tld_names.gperf"
-      {"grong.no", 0},
-#line 191 "effective_tld_names.gperf"
-      {"artgallery.museum", 0},
-#line 2256 "effective_tld_names.gperf"
-      {"nordkapp.no", 0},
-#line 2592 "effective_tld_names.gperf"
-      {"prd.km", 0},
-#line 786 "effective_tld_names.gperf"
-      {"do", 2},
-#line 1767 "effective_tld_names.gperf"
-      {"lipetsk.ru", 0},
-#line 2966 "effective_tld_names.gperf"
-      {"solund.no", 0},
-#line 759 "effective_tld_names.gperf"
-      {"de", 0},
-#line 3048 "effective_tld_names.gperf"
-      {"sunndal.no", 0},
-#line 3408 "effective_tld_names.gperf"
-      {"xj.cn", 0},
-#line 1061 "effective_tld_names.gperf"
-      {"franziskaner.museum", 0},
-#line 1093 "effective_tld_names.gperf"
-      {"gallery.museum", 0},
-#line 2027 "effective_tld_names.gperf"
-      {"music.museum", 0},
-#line 2875 "effective_tld_names.gperf"
-      {"scotland.museum", 0},
-#line 2059 "effective_tld_names.gperf"
-      {"namsos.no", 0},
-#line 67 "effective_tld_names.gperf"
-      {"act.gov.au", 0},
-#line 2593 "effective_tld_names.gperf"
-      {"prd.mg", 0},
-#line 2488 "effective_tld_names.gperf"
-      {"pa.it", 0},
-#line 2024 "effective_tld_names.gperf"
-      {"museum.tt", 0},
-#line 3643 "effective_tld_names.gperf"
-      {"zj.cn", 0},
-#line 3242 "effective_tld_names.gperf"
-      {"urn.arpa", 0},
-#line 994 "effective_tld_names.gperf"
-      {"fhs.no", 0},
-#line 1312 "effective_tld_names.gperf"
-      {"gs.of.no", 0},
-#line 2900 "effective_tld_names.gperf"
-      {"sh.cn", 0},
-#line 956 "effective_tld_names.gperf"
-      {"ethnology.museum", 0},
-#line 1420 "effective_tld_names.gperf"
-      {"hoyanger.no", 0},
-#line 3383 "effective_tld_names.gperf"
-      {"western.museum", 0},
-#line 1172 "effective_tld_names.gperf"
-      {"gorizia.it", 0},
-#line 1008 "effective_tld_names.gperf"
-      {"fineart.museum", 0},
-#line 1750 "effective_tld_names.gperf"
-      {"levanger.no", 0},
-#line 2540 "effective_tld_names.gperf"
-      {"pi.it", 0},
-#line 1802 "effective_tld_names.gperf"
-      {"lugansk.ua", 0},
-#line 982 "effective_tld_names.gperf"
-      {"fauske.no", 0},
-#line 2105 "effective_tld_names.gperf"
-      {"nesseby.no", 0},
-#line 2973 "effective_tld_names.gperf"
-      {"sor-fron.no", 0},
-#line 434 "effective_tld_names.gperf"
-      {"cahcesuolo.no", 0},
-#line 1418 "effective_tld_names.gperf"
-      {"hotel.lk", 0},
-#line 1100 "effective_tld_names.gperf"
-      {"garden.museum", 0},
-#line 396 "effective_tld_names.gperf"
-      {"bronnoy.no", 0},
-#line 176 "effective_tld_names.gperf"
-      {"arendal.no", 0},
-#line 2514 "effective_tld_names.gperf"
-      {"pe.ca", 0},
-#line 2305 "effective_tld_names.gperf"
-      {"odessa.ua", 0},
-#line 1884 "effective_tld_names.gperf"
-      {"media.hu", 0},
-#line 3020 "effective_tld_names.gperf"
-      {"steinkjer.no", 0},
-#line 1547 "effective_tld_names.gperf"
-      {"jan-mayen.no", 0},
-#line 1885 "effective_tld_names.gperf"
-      {"media.museum", 0},
-#line 155 "effective_tld_names.gperf"
-      {"antiques.museum", 0},
-#line 3016 "effective_tld_names.gperf"
-      {"stavropol.ru", 0},
-#line 1009 "effective_tld_names.gperf"
-      {"finearts.museum", 0},
-#line 1339 "effective_tld_names.gperf"
-      {"gyeongnam.kr", 0},
-#line 244 "effective_tld_names.gperf"
-      {"author.aero", 0},
-#line 2897 "effective_tld_names.gperf"
-      {"sf.no", 0},
-#line 1764 "effective_tld_names.gperf"
-      {"lindas.no", 0},
-#line 951 "effective_tld_names.gperf"
-      {"essex.museum", 0},
-#line 3006 "effective_tld_names.gperf"
-      {"stargard.pl", 0},
-#line 1859 "effective_tld_names.gperf"
-      {"mazury.pl", 0},
-#line 2017 "effective_tld_names.gperf"
-      {"muncie.museum", 0},
-#line 312 "effective_tld_names.gperf"
-      {"bergbau.museum", 0},
-#line 3037 "effective_tld_names.gperf"
-      {"student.aero", 0},
-#line 2254 "effective_tld_names.gperf"
-      {"nord-odal.no", 0},
-#line 1893 "effective_tld_names.gperf"
-      {"meloy.no", 0},
-#line 402 "effective_tld_names.gperf"
-      {"bruxelles.museum", 0},
-#line 1051 "effective_tld_names.gperf"
-      {"fortworth.museum", 0},
-#line 2686 "effective_tld_names.gperf"
-      {"pvt.ge", 0},
-#line 351 "effective_tld_names.gperf"
-      {"bj", 0},
-#line 1835 "effective_tld_names.gperf"
-      {"mandal.no", 0},
-#line 335 "effective_tld_names.gperf"
-      {"bio.br", 0},
-#line 761 "effective_tld_names.gperf"
-      {"de.us", 0},
-#line 1722 "effective_tld_names.gperf"
-      {"lardal.no", 0},
-#line 2695 "effective_tld_names.gperf"
-      {"qh.cn", 0},
-#line 1267 "effective_tld_names.gperf"
-      {"gov.tj", 0},
-#line 2530 "effective_tld_names.gperf"
-      {"pg.it", 0},
-#line 441 "effective_tld_names.gperf"
-      {"canada.museum", 0},
-#line 896 "effective_tld_names.gperf"
-      {"edu.tj", 0},
-#line 2047 "effective_tld_names.gperf"
-      {"namdalseid.no", 0},
-#line 2189 "effective_tld_names.gperf"
-      {"net.tj", 0},
-#line 2086 "effective_tld_names.gperf"
-      {"nb.ca", 0},
-#line 1995 "effective_tld_names.gperf"
-      {"moskenes.no", 0},
-#line 676 "effective_tld_names.gperf"
-      {"com.tj", 0},
-#line 782 "effective_tld_names.gperf"
-      {"dm", 0},
-#line 1042 "effective_tld_names.gperf"
-      {"foggia.it", 0},
-#line 1806 "effective_tld_names.gperf"
-      {"luroy.no", 0},
-#line 1475 "effective_tld_names.gperf"
-      {"indianmarket.museum", 0},
-#line 1513 "effective_tld_names.gperf"
-      {"int.tj", 0},
-#line 1956 "effective_tld_names.gperf"
-      {"minnesota.museum", 0},
-#line 1354 "effective_tld_names.gperf"
-      {"hammarfeasta.no", 0},
-#line 1322 "effective_tld_names.gperf"
-      {"gs.vf.no", 0},
-#line 1291 "effective_tld_names.gperf"
-      {"greta.fr", 0},
-#line 123 "effective_tld_names.gperf"
-      {"algard.no", 0},
-#line 800 "effective_tld_names.gperf"
-      {"dz", 0},
-#line 448 "effective_tld_names.gperf"
-      {"casino.hu", 0},
-#line 805 "effective_tld_names.gperf"
-      {"eastafrica.museum", 0},
-#line 2485 "effective_tld_names.gperf"
-      {"p.se", 0},
-#line 1581 "effective_tld_names.gperf"
-      {"js.cn", 0},
-#line 1454 "effective_tld_names.gperf"
-      {"illustration.museum", 0},
-#line 2347 "effective_tld_names.gperf"
-      {"oregon.museum", 0},
-#line 3017 "effective_tld_names.gperf"
-      {"steam.museum", 0},
-#line 1378 "effective_tld_names.gperf"
-      {"herad.no", 0},
-#line 944 "effective_tld_names.gperf"
-      {"equipment.aero", 0},
-#line 2649 "effective_tld_names.gperf"
-      {"pri.ee", 0},
-#line 2920 "effective_tld_names.gperf"
-      {"sirdal.no", 0},
-#line 2969 "effective_tld_names.gperf"
-      {"sondrio.it", 0},
-#line 476 "effective_tld_names.gperf"
-      {"cherkassy.ua", 0},
-#line 2066 "effective_tld_names.gperf"
-      {"narviika.no", 0},
-#line 1544 "effective_tld_names.gperf"
-      {"j.bg", 0},
-#line 1934 "effective_tld_names.gperf"
-      {"mil.lv", 0},
-#line 1586 "effective_tld_names.gperf"
-      {"jur.pro", 0},
-#line 1411 "effective_tld_names.gperf"
-      {"holtalen.no", 0},
-#line 329 "effective_tld_names.gperf"
-      {"biella.it", 0},
-#line 378 "effective_tld_names.gperf"
-      {"botanicgarden.museum", 0},
-#line 1111 "effective_tld_names.gperf"
-      {"gdynia.pl", 0},
-#line 3044 "effective_tld_names.gperf"
-      {"suldal.no", 0},
-#line 2445 "effective_tld_names.gperf"
-      {"org.tj", 0},
-#line 1587 "effective_tld_names.gperf"
-      {"jus.br", 0},
-#line 1317 "effective_tld_names.gperf"
-      {"gs.st.no", 0},
-#line 1391 "effective_tld_names.gperf"
-      {"history.museum", 0},
-#line 3225 "effective_tld_names.gperf"
-      {"uk.com", 0},
-#line 66 "effective_tld_names.gperf"
-      {"act.edu.au", 0},
-#line 397 "effective_tld_names.gperf"
-      {"bronnoysund.no", 0},
-#line 701 "effective_tld_names.gperf"
-      {"contemporaryart.museum", 0},
-#line 2586 "effective_tld_names.gperf"
-      {"ppg.br", 0},
-#line 935 "effective_tld_names.gperf"
-      {"engineer.aero", 0},
-#line 1723 "effective_tld_names.gperf"
-      {"larsson.museum", 0},
-#line 2915 "effective_tld_names.gperf"
-      {"sigdal.no", 0},
-#line 1003 "effective_tld_names.gperf"
-      {"filatelia.museum", 0},
-#line 1784 "effective_tld_names.gperf"
-      {"lorenskog.no", 0},
-#line 1993 "effective_tld_names.gperf"
-      {"moscow.museum", 0},
-#line 722 "effective_tld_names.gperf"
-      {"crafts.museum", 0},
-#line 979 "effective_tld_names.gperf"
-      {"farmers.museum", 0},
-#line 2650 "effective_tld_names.gperf"
-      {"principe.st", 0},
-#line 1050 "effective_tld_names.gperf"
-      {"fortmissoula.museum", 0},
-#line 783 "effective_tld_names.gperf"
-      {"dn.ua", 0},
-#line 297 "effective_tld_names.gperf"
-      {"bd.se", 0},
-#line 793 "effective_tld_names.gperf"
-      {"dr.na", 0},
-#line 520 "effective_tld_names.gperf"
-      {"civilization.museum", 0},
-#line 151 "effective_tld_names.gperf"
-      {"andoy.no", 0},
-#line 2933 "effective_tld_names.gperf"
-      {"skjak.no", 0},
-#line 1769 "effective_tld_names.gperf"
-      {"livinghistory.museum", 0},
-#line 2549 "effective_tld_names.gperf"
-      {"pk", 0},
-#line 2016 "effective_tld_names.gperf"
-      {"mulhouse.museum", 0},
-#line 2883 "effective_tld_names.gperf"
-      {"sebastopol.ua", 0},
-#line 3634 "effective_tld_names.gperf"
-      {"zachpomor.pl", 0},
-#line 375 "effective_tld_names.gperf"
-      {"boston.museum", 0},
-#line 2252 "effective_tld_names.gperf"
-      {"nord-aurdal.no", 0},
-#line 2231 "effective_tld_names.gperf"
-      {"nls.uk", 1},
-#line 272 "effective_tld_names.gperf"
-      {"balat.no", 0},
-#line 2577 "effective_tld_names.gperf"
-      {"portland.museum", 0},
-#line 316 "effective_tld_names.gperf"
-      {"berlin.museum", 0},
-#line 3260 "effective_tld_names.gperf"
-      {"utazas.hu", 0},
-#line 2323 "effective_tld_names.gperf"
-      {"omasvuotna.no", 0},
-#line 30 "effective_tld_names.gperf"
-      {"ab.ca", 0},
-#line 2484 "effective_tld_names.gperf"
-      {"p.bg", 0},
-#line 923 "effective_tld_names.gperf"
-      {"elk.pl", 0},
-#line 1367 "effective_tld_names.gperf"
-      {"hb.cn", 0},
-#line 2041 "effective_tld_names.gperf"
-      {"nagano.jp", 2},
-#line 311 "effective_tld_names.gperf"
-      {"bergamo.it", 0},
-#line 1044 "effective_tld_names.gperf"
-      {"folldal.no", 0},
-#line 300 "effective_tld_names.gperf"
-      {"beardu.no", 0},
-#line 916 "effective_tld_names.gperf"
-      {"eidsberg.no", 0},
-#line 1860 "effective_tld_names.gperf"
-      {"mb.ca", 0},
-#line 757 "effective_tld_names.gperf"
-      {"dc.us", 0},
-#line 2467 "effective_tld_names.gperf"
-      {"oskol.ru", 0},
-#line 3058 "effective_tld_names.gperf"
-      {"svelvik.no", 0},
-#line 1765 "effective_tld_names.gperf"
-      {"lindesnes.no", 0},
-#line 2074 "effective_tld_names.gperf"
-      {"naturalhistory.museum", 0},
-#line 758 "effective_tld_names.gperf"
-      {"ddr.museum", 0},
-#line 1292 "effective_tld_names.gperf"
-      {"grimstad.no", 0},
-#line 1525 "effective_tld_names.gperf"
-      {"irkutsk.ru", 0},
-#line 1728 "effective_tld_names.gperf"
-      {"lavangen.no", 0},
-#line 141 "effective_tld_names.gperf"
-      {"amsterdam.museum", 0},
-#line 2913 "effective_tld_names.gperf"
-      {"siellak.no", 0},
-#line 1556 "effective_tld_names.gperf"
-      {"jerusalem.museum", 0},
-#line 1576 "effective_tld_names.gperf"
-      {"journal.aero", 0},
-#line 1045 "effective_tld_names.gperf"
-      {"force.museum", 0},
-#line 1588 "effective_tld_names.gperf"
-      {"jx.cn", 0},
-#line 2018 "effective_tld_names.gperf"
-      {"muosat.no", 0},
-#line 1825 "effective_tld_names.gperf"
-      {"magnitka.ru", 0},
-#line 1119 "effective_tld_names.gperf"
-      {"geology.museum", 0},
-#line 2656 "effective_tld_names.gperf"
-      {"pro", 0},
-#line 2808 "effective_tld_names.gperf"
-      {"saintlouis.museum", 0},
-#line 2465 "effective_tld_names.gperf"
-      {"osaka.jp", 2},
-#line 500 "effective_tld_names.gperf"
-      {"circus.museum", 0},
-#line 437 "effective_tld_names.gperf"
-      {"cambridge.museum", 0},
-#line 1305 "effective_tld_names.gperf"
-      {"gs.fm.no", 0},
-#line 1302 "effective_tld_names.gperf"
-      {"gs.ah.no", 0},
-#line 1763 "effective_tld_names.gperf"
-      {"lincoln.museum", 0},
-#line 2550 "effective_tld_names.gperf"
-      {"pl", 0},
-#line 1171 "effective_tld_names.gperf"
-      {"gorge.museum", 0},
-#line 3021 "effective_tld_names.gperf"
-      {"stjohn.museum", 0},
-#line 3647 "effective_tld_names.gperf"
-      {"zoology.museum", 0},
-#line 905 "effective_tld_names.gperf"
-      {"education.museum", 0},
-#line 768 "effective_tld_names.gperf"
-      {"dep.no", 0},
-#line 1459 "effective_tld_names.gperf"
-      {"imperia.it", 0},
-#line 2825 "effective_tld_names.gperf"
-      {"sandnes.no", 0},
-#line 1114 "effective_tld_names.gperf"
-      {"geelvinck.museum", 0},
-#line 3398 "effective_tld_names.gperf"
-      {"workshop.museum", 0},
-#line 1360 "effective_tld_names.gperf"
-      {"hareid.no", 0},
-#line 586 "effective_tld_names.gperf"
-      {"columbia.museum", 0},
-#line 3010 "effective_tld_names.gperf"
-      {"state.museum", 0},
-#line 204 "effective_tld_names.gperf"
-      {"askim.no", 0},
-#line 1066 "effective_tld_names.gperf"
-      {"freight.aero", 0},
-#line 2257 "effective_tld_names.gperf"
-      {"nordre-land.no", 0},
-#line 3050 "effective_tld_names.gperf"
-      {"surgut.ru", 0},
-#line 2813 "effective_tld_names.gperf"
-      {"salem.museum", 0},
-#line 1020 "effective_tld_names.gperf"
-      {"fj.cn", 0},
-#line 432 "effective_tld_names.gperf"
-      {"cadaques.museum", 0},
-#line 1495 "effective_tld_names.gperf"
-      {"info.tn", 0},
-#line 494 "effective_tld_names.gperf"
-      {"chuvashia.ru", 0},
-#line 3237 "effective_tld_names.gperf"
-      {"university.museum", 0},
-#line 294 "effective_tld_names.gperf"
-      {"bb", 0},
-#line 1716 "effective_tld_names.gperf"
-      {"lancashire.museum", 0},
-#line 3042 "effective_tld_names.gperf"
-      {"suisse.museum", 0},
-#line 1779 "effective_tld_names.gperf"
-      {"logistics.aero", 0},
-#line 1546 "effective_tld_names.gperf"
-      {"jamison.museum", 0},
-#line 1801 "effective_tld_names.gperf"
-      {"lucerne.museum", 0},
-#line 1284 "effective_tld_names.gperf"
-      {"grajewo.pl", 0},
-#line 1346 "effective_tld_names.gperf"
-      {"hadsel.no", 0},
-#line 404 "effective_tld_names.gperf"
-      {"bryne.no", 0},
-#line 1059 "effective_tld_names.gperf"
-      {"francaise.museum", 0},
-#line 1489 "effective_tld_names.gperf"
-      {"info.nr", 0},
-#line 3071 "effective_tld_names.gperf"
-      {"szczytno.pl", 0},
-#line 63 "effective_tld_names.gperf"
-      {"academy.museum", 0},
-#line 2060 "effective_tld_names.gperf"
-      {"namsskogan.no", 0},
-#line 189 "effective_tld_names.gperf"
-      {"artdeco.museum", 0},
-#line 2945 "effective_tld_names.gperf"
-      {"slupsk.pl", 0},
-#line 206 "effective_tld_names.gperf"
-      {"askvoll.no", 0},
-#line 1782 "effective_tld_names.gperf"
-      {"london.museum", 0},
-#line 1073 "effective_tld_names.gperf"
-      {"frosta.no", 0},
-#line 2815 "effective_tld_names.gperf"
-      {"saltdal.no", 0},
-#line 3220 "effective_tld_names.gperf"
-      {"udmurtia.ru", 0},
-#line 175 "effective_tld_names.gperf"
-      {"aremark.no", 0},
-#line 111 "effective_tld_names.gperf"
-      {"aknoluokta.no", 0},
-#line 1830 "effective_tld_names.gperf"
-      {"mallorca.museum", 0},
-#line 691 "effective_tld_names.gperf"
-      {"como.it", 0},
-#line 1349 "effective_tld_names.gperf"
-      {"halloffame.museum", 0},
-#line 1104 "effective_tld_names.gperf"
-      {"gb.com", 0},
-#line 2255 "effective_tld_names.gperf"
-      {"norddal.no", 0},
-#line 2499 "effective_tld_names.gperf"
-      {"panama.museum", 0},
-#line 81 "effective_tld_names.gperf"
-      {"aerodrome.aero", 0},
-#line 1440 "effective_tld_names.gperf"
-      {"icnet.uk", 1},
-#line 2663 "effective_tld_names.gperf"
-      {"pro.tt", 0},
-#line 2495 "effective_tld_names.gperf"
-      {"palana.ru", 0},
-#line 1892 "effective_tld_names.gperf"
-      {"melhus.no", 0},
-#line 1496 "effective_tld_names.gperf"
-      {"info.tt", 0},
-#line 3376 "effective_tld_names.gperf"
-      {"waw.pl", 0},
-#line 2925 "effective_tld_names.gperf"
-      {"skaun.no", 0},
-#line 1178 "effective_tld_names.gperf"
-      {"gouv.km", 0},
-#line 1055 "effective_tld_names.gperf"
-      {"foundation.museum", 0},
-#line 966 "effective_tld_names.gperf"
-      {"exchange.aero", 0},
-#line 2803 "effective_tld_names.gperf"
-      {"sa.edu.au", 0},
-#line 498 "effective_tld_names.gperf"
-      {"cincinnati.museum", 0},
-#line 395 "effective_tld_names.gperf"
-      {"broker.aero", 0},
-#line 1762 "effective_tld_names.gperf"
-      {"limanowa.pl", 0},
-#line 2291 "effective_tld_names.gperf"
-      {"nuremberg.museum", 0},
-#line 3368 "effective_tld_names.gperf"
-      {"wales.museum", 0},
-#line 1176 "effective_tld_names.gperf"
-      {"gouv.fr", 0},
-#line 1531 "effective_tld_names.gperf"
-      {"ishikawa.jp", 2},
-#line 2215 "effective_tld_names.gperf"
-      {"nhs.uk", 1},
-#line 1371 "effective_tld_names.gperf"
-      {"heimatunduhren.museum", 0},
-#line 3057 "effective_tld_names.gperf"
-      {"sveio.no", 0},
-#line 181 "effective_tld_names.gperf"
-      {"arq.br", 0},
-#line 403 "effective_tld_names.gperf"
-      {"bryansk.ru", 0},
-#line 2482 "effective_tld_names.gperf"
-      {"oygarden.no", 0},
-#line 452 "effective_tld_names.gperf"
-      {"catania.it", 0},
-#line 280 "effective_tld_names.gperf"
-      {"bamble.no", 0},
-#line 1711 "effective_tld_names.gperf"
-      {"labour.museum", 0},
-#line 1778 "effective_tld_names.gperf"
-      {"lodingen.no", 0},
-#line 3363 "effective_tld_names.gperf"
-      {"wa.edu.au", 0},
-#line 2912 "effective_tld_names.gperf"
-      {"sibenik.museum", 0},
-#line 1852 "effective_tld_names.gperf"
-      {"masoy.no", 0},
-#line 129 "effective_tld_names.gperf"
-      {"alvdal.no", 0},
-#line 2025 "effective_tld_names.gperf"
-      {"museumcenter.museum", 0},
-#line 373 "effective_tld_names.gperf"
-      {"bomlo.no", 0},
-#line 29 "effective_tld_names.gperf"
-      {"aarborte.no", 0},
-#line 80 "effective_tld_names.gperf"
-      {"aeroclub.aero", 0},
-#line 792 "effective_tld_names.gperf"
-      {"dp.ua", 0},
-#line 203 "effective_tld_names.gperf"
-      {"asker.no", 0},
-#line 585 "effective_tld_names.gperf"
-      {"coloradoplateau.museum", 0},
-#line 1494 "effective_tld_names.gperf"
-      {"info.sd", 0},
-#line 2015 "effective_tld_names.gperf"
-      {"muenster.museum", 0},
-#line 742 "effective_tld_names.gperf"
-      {"cymru.museum", 0},
-#line 2664 "effective_tld_names.gperf"
-      {"pro.vn", 0},
-#line 3638 "effective_tld_names.gperf"
-      {"zarow.pl", 0},
-#line 2551 "effective_tld_names.gperf"
-      {"pl.ua", 0},
-#line 3381 "effective_tld_names.gperf"
-      {"web.tj", 0},
-#line 488 "effective_tld_names.gperf"
-      {"chita.ru", 0},
-#line 472 "effective_tld_names.gperf"
-      {"chattanooga.museum", 0},
-#line 2965 "effective_tld_names.gperf"
-      {"sologne.museum", 0},
-#line 481 "effective_tld_names.gperf"
-      {"chicago.museum", 0},
-#line 2811 "effective_tld_names.gperf"
-      {"salangen.no", 0},
-#line 937 "effective_tld_names.gperf"
-      {"enna.it", 0},
-#line 2322 "effective_tld_names.gperf"
-      {"omaha.museum", 0},
-#line 236 "effective_tld_names.gperf"
-      {"aukra.no", 0},
-#line 1166 "effective_tld_names.gperf"
-      {"gobiernoelectronico.ar", 1},
-#line 2259 "effective_tld_names.gperf"
-      {"nore-og-uvdal.no", 0},
-#line 2531 "effective_tld_names.gperf"
-      {"ph", 0},
-#line 2976 "effective_tld_names.gperf"
-      {"sorfold.no", 0},
-#line 78 "effective_tld_names.gperf"
-      {"aero.tt", 0},
-#line 1294 "effective_tld_names.gperf"
-      {"grosseto.it", 0},
-#line 1482 "effective_tld_names.gperf"
-      {"info.ec", 0},
-#line 2477 "effective_tld_names.gperf"
-      {"other.nf", 0},
-#line 1558 "effective_tld_names.gperf"
-      {"jet.uk", 1},
-#line 3382 "effective_tld_names.gperf"
-      {"wegrow.pl", 0},
-#line 112 "effective_tld_names.gperf"
-      {"akrehamn.no", 0},
-#line 3612 "effective_tld_names.gperf"
-      {"yamagata.jp", 2},
-#line 31 "effective_tld_names.gperf"
-      {"abo.pa", 0},
-#line 1568 "effective_tld_names.gperf"
-      {"jobs", 0},
-#line 442 "effective_tld_names.gperf"
-      {"capebreton.museum", 0},
-#line 1814 "effective_tld_names.gperf"
-      {"lyngdal.no", 0},
-#line 3030 "effective_tld_names.gperf"
-      {"store.ro", 0},
-#line 921 "effective_tld_names.gperf"
-      {"elblag.pl", 0},
-#line 1128 "effective_tld_names.gperf"
-      {"giessen.museum", 0},
-#line 991 "effective_tld_names.gperf"
-      {"fetsund.no", 0},
-#line 3636 "effective_tld_names.gperf"
-      {"zakopane.pl", 0},
-#line 3384 "effective_tld_names.gperf"
-      {"westfalen.museum", 0},
 #line 1145 "effective_tld_names.gperf"
-      {"gmina.pl", 0},
-#line 376 "effective_tld_names.gperf"
-      {"botanical.museum", 0},
-#line 3238 "effective_tld_names.gperf"
-      {"unjarga.no", 0},
-#line 3031 "effective_tld_names.gperf"
-      {"store.st", 0},
-#line 2827 "effective_tld_names.gperf"
-      {"sandoy.no", 0},
-#line 748 "effective_tld_names.gperf"
-      {"d.se", 0},
-#line 269 "effective_tld_names.gperf"
-      {"baidar.no", 0},
-#line 435 "effective_tld_names.gperf"
-      {"california.museum", 0},
-#line 1987 "effective_tld_names.gperf"
-      {"money.museum", 0},
-#line 1518 "effective_tld_names.gperf"
-      {"intl.tn", 0},
-#line 2202 "effective_tld_names.gperf"
-      {"newjersey.museum", 0},
-#line 965 "effective_tld_names.gperf"
-      {"evje-og-hornnes.no", 0},
-#line 1046 "effective_tld_names.gperf"
-      {"forde.no", 0},
-#line 2658 "effective_tld_names.gperf"
-      {"pro.br", 0},
-#line 353 "effective_tld_names.gperf"
-      {"bjarkoy.no", 0},
-#line 2687 "effective_tld_names.gperf"
-      {"pw", 0},
-#line 3641 "effective_tld_names.gperf"
-      {"zgrad.ru", 0},
-#line 1985 "effective_tld_names.gperf"
-      {"molde.no", 0},
-#line 2970 "effective_tld_names.gperf"
-      {"songdalen.no", 0},
-#line 1578 "effective_tld_names.gperf"
-      {"journalist.aero", 0},
-#line 2494 "effective_tld_names.gperf"
-      {"palace.museum", 0},
-#line 491 "effective_tld_names.gperf"
-      {"chukotka.ru", 0},
-#line 338 "effective_tld_names.gperf"
-      {"birkenes.no", 0},
-#line 2662 "effective_tld_names.gperf"
-      {"pro.pr", 0},
-#line 3252 "effective_tld_names.gperf"
-      {"usenet.pl", 0},
-#line 1729 "effective_tld_names.gperf"
-      {"law.pro", 0},
-#line 392 "effective_tld_names.gperf"
-      {"british.museum", 0},
-#line 3622 "effective_tld_names.gperf"
-      {"york.museum", 0},
-#line 1094 "effective_tld_names.gperf"
-      {"galsa.no", 0},
-#line 1350 "effective_tld_names.gperf"
-      {"halsa.no", 0},
-#line 518 "effective_tld_names.gperf"
-      {"civilaviation.aero", 0},
-#line 1912 "effective_tld_names.gperf"
-      {"mielno.pl", 0},
-#line 3232 "effective_tld_names.gperf"
-      {"ulvik.no", 0},
-#line 1858 "effective_tld_names.gperf"
-      {"mazowsze.pl", 0},
-#line 1959 "effective_tld_names.gperf"
-      {"miyagi.jp", 2},
-#line 1034 "effective_tld_names.gperf"
-      {"florence.it", 0},
-#line 2930 "effective_tld_names.gperf"
-      {"skien.no", 0},
-#line 2476 "effective_tld_names.gperf"
-      {"otago.museum", 0},
-#line 1337 "effective_tld_names.gperf"
-      {"gyeongbuk.kr", 0},
-#line 2039 "effective_tld_names.gperf"
-      {"naamesjevuemie.no", 0},
-#line 1717 "effective_tld_names.gperf"
-      {"landes.museum", 0},
-#line 1117 "effective_tld_names.gperf"
-      {"genoa.it", 0},
-#line 390 "effective_tld_names.gperf"
-      {"bristol.museum", 0},
-#line 84 "effective_tld_names.gperf"
-      {"afjord.no", 0},
-#line 939 "effective_tld_names.gperf"
-      {"entertainment.aero", 0},
-#line 2492 "effective_tld_names.gperf"
-      {"padova.it", 0},
-#line 3028 "effective_tld_names.gperf"
-      {"stordal.no", 0},
-#line 225 "effective_tld_names.gperf"
-      {"asti.it", 0},
-#line 2947 "effective_tld_names.gperf"
-      {"smola.no", 0},
-#line 1978 "effective_tld_names.gperf"
-      {"mobi.tt", 0},
-#line 2661 "effective_tld_names.gperf"
-      {"pro.na", 0},
-#line 365 "effective_tld_names.gperf"
-      {"bo.nordland.no", 0},
-#line 1538 "effective_tld_names.gperf"
-      {"ivanovo.ru", 0},
-#line 3389 "effective_tld_names.gperf"
-      {"wildlife.museum", 0},
-#line 3430 "effective_tld_names.gperf"
-      {"xn--bod-2na.no", 0},
-#line 2479 "effective_tld_names.gperf"
-      {"ovre-eiker.no", 0},
-#line 3623 "effective_tld_names.gperf"
-      {"yorkshire.museum", 0},
-#line 904 "effective_tld_names.gperf"
-      {"educ.ar", 1},
-#line 1488 "effective_tld_names.gperf"
-      {"info.nf", 0},
-#line 1781 "effective_tld_names.gperf"
-      {"lomza.pl", 0},
-#line 1028 "effective_tld_names.gperf"
-      {"flatanger.no", 0},
-#line 190 "effective_tld_names.gperf"
-      {"arteducation.museum", 0},
-#line 2458 "effective_tld_names.gperf"
-      {"orkdal.no", 0},
-#line 762 "effective_tld_names.gperf"
-      {"deatnu.no", 0},
-#line 1473 "effective_tld_names.gperf"
-      {"indiana.museum", 0},
-#line 126 "effective_tld_names.gperf"
-      {"altai.ru", 0},
-#line 780 "effective_tld_names.gperf"
-      {"dk", 0},
-#line 389 "effective_tld_names.gperf"
-      {"brindisi.it", 0},
-#line 187 "effective_tld_names.gperf"
-      {"artanddesign.museum", 0},
-#line 1847 "effective_tld_names.gperf"
-      {"marketplace.aero", 0},
-#line 2971 "effective_tld_names.gperf"
-      {"sopot.pl", 0},
-#line 2576 "effective_tld_names.gperf"
-      {"portal.museum", 0},
-#line 2952 "effective_tld_names.gperf"
-      {"snasa.no", 0},
-#line 930 "effective_tld_names.gperf"
-      {"enebakk.no", 0},
-#line 2557 "effective_tld_names.gperf"
-      {"plc.ly", 0},
-#line 2840 "effective_tld_names.gperf"
-      {"sauda.no", 0},
-#line 334 "effective_tld_names.gperf"
-      {"bindal.no", 0},
-#line 1945 "effective_tld_names.gperf"
-      {"mil.tj", 0},
-#line 1419 "effective_tld_names.gperf"
-      {"house.museum", 0},
-#line 2556 "effective_tld_names.gperf"
-      {"plc.co.im", 0},
-#line 1082 "effective_tld_names.gperf"
-      {"fuossko.no", 0},
-#line 747 "effective_tld_names.gperf"
-      {"d.bg", 0},
-#line 1412 "effective_tld_names.gperf"
-      {"homebuilt.aero", 0},
-#line 1715 "effective_tld_names.gperf"
-      {"lanbib.se", 0},
-#line 2075 "effective_tld_names.gperf"
-      {"naturalhistorymuseum.museum", 0},
-#line 105 "effective_tld_names.gperf"
-      {"airguard.museum", 0},
-#line 2948 "effective_tld_names.gperf"
-      {"smolensk.ru", 0},
-#line 1540 "effective_tld_names.gperf"
-      {"ivgu.no", 0},
-#line 765 "effective_tld_names.gperf"
-      {"delaware.museum", 0},
-#line 252 "effective_tld_names.gperf"
-      {"avoues.fr", 0},
-#line 2528 "effective_tld_names.gperf"
-      {"pf", 0},
-#line 2544 "effective_tld_names.gperf"
-      {"pilots.museum", 0},
-#line 1395 "effective_tld_names.gperf"
-      {"hjelmeland.no", 0},
-#line 400 "effective_tld_names.gperf"
-      {"brussel.museum", 0},
-#line 1103 "effective_tld_names.gperf"
-      {"gausdal.no", 0},
-#line 2320 "effective_tld_names.gperf"
-      {"olsztyn.pl", 0},
-#line 1285 "effective_tld_names.gperf"
-      {"gran.no", 0},
-#line 251 "effective_tld_names.gperf"
-      {"avocat.fr", 0},
-#line 223 "effective_tld_names.gperf"
-      {"association.aero", 0},
-#line 2986 "effective_tld_names.gperf"
-      {"space.museum", 0},
-#line 2659 "effective_tld_names.gperf"
-      {"pro.ec", 0},
-#line 230 "effective_tld_names.gperf"
-      {"atlanta.museum", 0},
-#line 275 "effective_tld_names.gperf"
-      {"ballangen.no", 0},
-#line 3026 "effective_tld_names.gperf"
-      {"stor-elvdal.no", 0},
-#line 352 "effective_tld_names.gperf"
-      {"bj.cn", 0},
-#line 3249 "effective_tld_names.gperf"
-      {"uscountryestate.museum", 0},
-#line 1911 "effective_tld_names.gperf"
-      {"mielec.pl", 0},
-#line 3251 "effective_tld_names.gperf"
-      {"usdecorativearts.museum", 0},
-#line 1290 "effective_tld_names.gperf"
-      {"graz.museum", 0},
-#line 2982 "effective_tld_names.gperf"
-      {"soundandvision.museum", 0},
-#line 1709 "effective_tld_names.gperf"
-      {"laakesvuemie.no", 0},
-#line 1481 "effective_tld_names.gperf"
-      {"info.co", 0},
-#line 1545 "effective_tld_names.gperf"
-      {"jamal.ru", 0},
-#line 915 "effective_tld_names.gperf"
-      {"eidfjord.no", 0},
-#line 277 "effective_tld_names.gperf"
-      {"balsan.it", 0},
-#line 2043 "effective_tld_names.gperf"
-      {"nagoya.jp", 2},
-#line 1991 "effective_tld_names.gperf"
-      {"monza.it", 0},
-#line 1618 "effective_tld_names.gperf"
-      {"ke", 2},
-#line 3409 "effective_tld_names.gperf"
-      {"xn--55qx5d.cn", 0},
-#line 1700 "effective_tld_names.gperf"
-      {"ky", 0},
-#line 401 "effective_tld_names.gperf"
-      {"brussels.museum", 0},
-#line 120 "effective_tld_names.gperf"
-      {"alaska.museum", 0},
-#line 754 "effective_tld_names.gperf"
-      {"database.museum", 0},
-#line 1666 "effective_tld_names.gperf"
-      {"kr", 0},
-#line 1021 "effective_tld_names.gperf"
-      {"fjaler.no", 0},
-#line 2832 "effective_tld_names.gperf"
-      {"santafe.museum", 0},
-#line 447 "effective_tld_names.gperf"
-      {"caserta.it", 0},
-#line 1986 "effective_tld_names.gperf"
-      {"moma.museum", 0},
-#line 1646 "effective_tld_names.gperf"
-      {"kn", 0},
-#line 993 "effective_tld_names.gperf"
-      {"fh.se", 0},
-#line 942 "effective_tld_names.gperf"
-      {"environmentalconservation.museum", 0},
-#line 3073 "effective_tld_names.gperf"
-      {"szkola.pl", 0},
-#line 1836 "effective_tld_names.gperf"
-      {"mansion.museum", 0},
-#line 2204 "effective_tld_names.gperf"
-      {"newport.museum", 0},
-#line 438 "effective_tld_names.gperf"
-      {"campobasso.it", 0},
-#line 760 "effective_tld_names.gperf"
-      {"de.com", 0},
-#line 981 "effective_tld_names.gperf"
-      {"farsund.no", 0},
-#line 918 "effective_tld_names.gperf"
-      {"eidsvoll.no", 0},
-#line 2262 "effective_tld_names.gperf"
-      {"north.museum", 0},
-#line 1327 "effective_tld_names.gperf"
-      {"guernsey.museum", 0},
-#line 1329 "effective_tld_names.gperf"
-      {"gunma.jp", 2},
-#line 2892 "effective_tld_names.gperf"
-      {"services.aero", 0},
-#line 1988 "effective_tld_names.gperf"
-      {"monmouth.museum", 0},
-#line 3635 "effective_tld_names.gperf"
-      {"zagan.pl", 0},
-#line 989 "effective_tld_names.gperf"
-      {"ferrara.it", 0},
-#line 794 "effective_tld_names.gperf"
-      {"drammen.no", 0},
-#line 3625 "effective_tld_names.gperf"
-      {"youth.museum", 0},
-#line 221 "effective_tld_names.gperf"
-      {"asso.mc", 0},
-#line 1530 "effective_tld_names.gperf"
-      {"isernia.it", 0},
-#line 2968 "effective_tld_names.gperf"
-      {"sondre-land.no", 0},
-#line 2823 "effective_tld_names.gperf"
-      {"sandefjord.no", 0},
-#line 917 "effective_tld_names.gperf"
-      {"eidskog.no", 0},
-#line 692 "effective_tld_names.gperf"
-      {"computer.museum", 0},
-#line 436 "effective_tld_names.gperf"
-      {"caltanissetta.it", 0},
-#line 1837 "effective_tld_names.gperf"
-      {"mansions.museum", 0},
-#line 3640 "effective_tld_names.gperf"
-      {"zgorzelec.pl", 0},
-#line 1381 "effective_tld_names.gperf"
-      {"heroy.nordland.no", 0},
-#line 1523 "effective_tld_names.gperf"
-      {"iraq.museum", 0},
-#line 220 "effective_tld_names.gperf"
-      {"asso.km", 0},
-#line 1132 "effective_tld_names.gperf"
-      {"gjemnes.no", 0},
-#line 1366 "effective_tld_names.gperf"
-      {"hawaii.museum", 0},
-#line 3552 "effective_tld_names.gperf"
-      {"xn--sandy-yua.no", 0},
-#line 3424 "effective_tld_names.gperf"
-      {"xn--bidr-5nac.no", 0},
-#line 2457 "effective_tld_names.gperf"
-      {"orkanger.no", 0},
-#line 2874 "effective_tld_names.gperf"
-      {"scientist.aero", 0},
-#line 3216 "effective_tld_names.gperf"
-      {"uba.ar", 1},
-#line 912 "effective_tld_names.gperf"
-      {"egyptian.museum", 0},
-#line 1299 "effective_tld_names.gperf"
-      {"grue.no", 0},
-#line 306 "effective_tld_names.gperf"
-      {"belgorod.ru", 0},
-#line 3400 "effective_tld_names.gperf"
-      {"wroclaw.pl", 0},
-#line 2963 "effective_tld_names.gperf"
-      {"sokndal.no", 0},
-#line 2318 "effective_tld_names.gperf"
-      {"olecko.pl", 0},
-#line 217 "effective_tld_names.gperf"
-      {"asso.fr", 0},
-#line 2057 "effective_tld_names.gperf"
-      {"name.tt", 0},
-#line 1831 "effective_tld_names.gperf"
-      {"malopolska.pl", 0},
-#line 2271 "effective_tld_names.gperf"
-      {"nowaruda.pl", 0},
-#line 276 "effective_tld_names.gperf"
-      {"ballooning.aero", 0},
-#line 1701 "effective_tld_names.gperf"
-      {"ky.us", 0},
-#line 2469 "effective_tld_names.gperf"
-      {"osoyro.no", 0},
-#line 2468 "effective_tld_names.gperf"
-      {"oslo.no", 0},
-#line 2258 "effective_tld_names.gperf"
-      {"nordreisa.no", 0},
-#line 2329 "effective_tld_names.gperf"
-      {"opoczno.pl", 0},
-#line 2497 "effective_tld_names.gperf"
-      {"palermo.it", 0},
-#line 1559 "effective_tld_names.gperf"
-      {"jevnaker.no", 0},
-#line 235 "effective_tld_names.gperf"
-      {"augustow.pl", 0},
-#line 2864 "effective_tld_names.gperf"
-      {"schweiz.museum", 0},
-#line 2891 "effective_tld_names.gperf"
-      {"seoul.kr", 0},
-#line 179 "effective_tld_names.gperf"
-      {"arna.no", 0},
-#line 2517 "effective_tld_names.gperf"
-      {"penza.ru", 0},
-#line 1643 "effective_tld_names.gperf"
-      {"km", 0},
-#line 1808 "effective_tld_names.gperf"
-      {"lutsk.ua", 0},
-#line 1904 "effective_tld_names.gperf"
-      {"miasta.pl", 0},
-#line 1565 "effective_tld_names.gperf"
-      {"jl.cn", 0},
-#line 279 "effective_tld_names.gperf"
-      {"baltimore.museum", 0},
-#line 492 "effective_tld_names.gperf"
-      {"chungbuk.kr", 0},
-#line 302 "effective_tld_names.gperf"
-      {"bedzin.pl", 0},
-#line 106 "effective_tld_names.gperf"
-      {"airline.aero", 0},
-#line 804 "effective_tld_names.gperf"
-      {"e164.arpa", 0},
-#line 1577 "effective_tld_names.gperf"
-      {"journalism.museum", 0},
-#line 1679 "effective_tld_names.gperf"
-      {"ks.us", 0},
-#line 1703 "effective_tld_names.gperf"
-      {"kz", 0},
-#line 521 "effective_tld_names.gperf"
-      {"civilwar.museum", 0},
-#line 3001 "effective_tld_names.gperf"
-      {"stadt.museum", 0},
-#line 1179 "effective_tld_names.gperf"
-      {"gouv.rw", 0},
-#line 108 "effective_tld_names.gperf"
-      {"airtraffic.aero", 0},
-#line 806 "effective_tld_names.gperf"
-      {"eastcoast.museum", 0},
-#line 1849 "effective_tld_names.gperf"
-      {"maryland.museum", 0},
-#line 2989 "effective_tld_names.gperf"
-      {"sport.hu", 0},
-#line 290 "effective_tld_names.gperf"
-      {"bashkiria.ru", 0},
-#line 289 "effective_tld_names.gperf"
-      {"basel.museum", 0},
-#line 2053 "effective_tld_names.gperf"
-      {"name.my", 0},
-#line 1406 "effective_tld_names.gperf"
-      {"hokkaido.jp", 2},
-#line 267 "effective_tld_names.gperf"
-      {"bahccavuotna.no", 0},
+      {"giessen.museum", 0},
+#line 3670 "effective_tld_names.gperf"
+      {"y.bg", 0},
+#line 1573 "effective_tld_names.gperf"
+      {"j.bg", 0},
+#line 529 "effective_tld_names.gperf"
+      {"civilization.museum", 0},
 #line 2304 "effective_tld_names.gperf"
-      {"odda.no", 0},
-#line 3038 "effective_tld_names.gperf"
-      {"stuttgart.museum", 0},
-#line 224 "effective_tld_names.gperf"
-      {"association.museum", 0},
-#line 65 "effective_tld_names.gperf"
-      {"accident-prevention.aero", 0},
-#line 2100 "effective_tld_names.gperf"
-      {"nes.akershus.no", 0},
-#line 387 "effective_tld_names.gperf"
-      {"bremanger.no", 0},
-#line 3371 "effective_tld_names.gperf"
+      {"notteroy.no", 0},
+#line 528 "effective_tld_names.gperf"
+      {"civilisation.museum", 0},
+#line 1364 "effective_tld_names.gperf"
+      {"h.bg", 0},
+#line 1104 "effective_tld_names.gperf"
+      {"g.bg", 0},
+#line 3426 "effective_tld_names.gperf"
       {"warmia.pl", 0},
-#line 1834 "effective_tld_names.gperf"
-      {"manchester.museum", 0},
-#line 1908 "effective_tld_names.gperf"
-      {"midsund.no", 0},
-#line 3404 "effective_tld_names.gperf"
-      {"www.ro", 0},
-#line 1338 "effective_tld_names.gperf"
-      {"gyeonggi.kr", 0},
-#line 2926 "effective_tld_names.gperf"
-      {"skedsmo.no", 0},
-#line 1136 "effective_tld_names.gperf"
-      {"gjovik.no", 0},
-#line 2866 "effective_tld_names.gperf"
-      {"science.museum", 0},
-#line 728 "effective_tld_names.gperf"
-      {"crotone.it", 0},
-#line 1851 "effective_tld_names.gperf"
-      {"masfjorden.no", 0},
-#line 1493 "effective_tld_names.gperf"
-      {"info.ro", 0},
-#line 3255 "effective_tld_names.gperf"
-      {"ushuaia.museum", 0},
-#line 1886 "effective_tld_names.gperf"
-      {"media.pl", 0},
-#line 1026 "effective_tld_names.gperf"
-      {"flakstad.no", 0},
-#line 1669 "effective_tld_names.gperf"
-      {"kr.ua", 0},
-#line 3267 "effective_tld_names.gperf"
-      {"uzhgorod.ua", 0},
-#line 1593 "effective_tld_names.gperf"
-      {"k12.vi", 0},
-#line 1516 "effective_tld_names.gperf"
-      {"intelligence.museum", 0},
-#line 2683 "effective_tld_names.gperf"
-      {"pubol.museum", 0},
-#line 1095 "effective_tld_names.gperf"
-      {"game.tw", 0},
-#line 327 "effective_tld_names.gperf"
-      {"bible.museum", 0},
-#line 1776 "effective_tld_names.gperf"
-      {"localhistory.museum", 0},
-#line 2098 "effective_tld_names.gperf"
-      {"nedre-eiker.no", 0},
-#line 3539 "effective_tld_names.gperf"
-      {"xn--risa-5na.no", 0},
-#line 1067 "effective_tld_names.gperf"
-      {"fribourg.museum", 0},
-#line 2582 "effective_tld_names.gperf"
-      {"poznan.pl", 0},
-#line 2464 "effective_tld_names.gperf"
-      {"os.hordaland.no", 0},
-#line 770 "effective_tld_names.gperf"
-      {"design.aero", 0},
-#line 1002 "effective_tld_names.gperf"
-      {"figueres.museum", 0},
-#line 3532 "effective_tld_names.gperf"
-      {"xn--rady-ira.no", 0},
-#line 1678 "effective_tld_names.gperf"
-      {"ks.ua", 0},
-#line 308 "effective_tld_names.gperf"
-      {"belluno.it", 0},
-#line 943 "effective_tld_names.gperf"
-      {"epilepsy.museum", 0},
-#line 2872 "effective_tld_names.gperf"
-      {"sciences.museum", 0},
-#line 421 "effective_tld_names.gperf"
-      {"bytom.pl", 0},
-#line 205 "effective_tld_names.gperf"
-      {"askoy.no", 0},
-#line 3506 "effective_tld_names.gperf"
-      {"xn--lury-ira.no", 0},
-#line 1115 "effective_tld_names.gperf"
-      {"gemological.museum", 0},
-#line 3540 "effective_tld_names.gperf"
-      {"xn--risr-ira.no", 0},
-#line 3397 "effective_tld_names.gperf"
-      {"works.aero", 0},
-#line 1714 "effective_tld_names.gperf"
-      {"lakas.hu", 0},
-#line 2046 "effective_tld_names.gperf"
-      {"nalchik.ru", 0},
-#line 3567 "effective_tld_names.gperf"
-      {"xn--snsa-roa.no", 0},
-#line 1062 "effective_tld_names.gperf"
-      {"fredrikstad.no", 0},
-#line 958 "effective_tld_names.gperf"
-      {"etne.no", 0},
-#line 1632 "effective_tld_names.gperf"
-      {"ki", 0},
-#line 1668 "effective_tld_names.gperf"
-      {"kr.it", 0},
-#line 288 "effective_tld_names.gperf"
-      {"baseball.museum", 0},
-#line 3499 "effective_tld_names.gperf"
-      {"xn--linds-pra.no", 0},
-#line 3133 "effective_tld_names.gperf"
-      {"to", 0},
-#line 3090 "effective_tld_names.gperf"
-      {"td", 0},
-#line 1195 "effective_tld_names.gperf"
-      {"gov.bz", 0},
-#line 3536 "effective_tld_names.gperf"
-      {"xn--rennesy-v1a.no", 0},
-#line 1409 "effective_tld_names.gperf"
-      {"hole.no", 0},
-#line 3007 "effective_tld_names.gperf"
-      {"starnberg.museum", 0},
-#line 828 "effective_tld_names.gperf"
-      {"edu.bz", 0},
-#line 2122 "effective_tld_names.gperf"
-      {"net.bz", 0},
-#line 3155 "effective_tld_names.gperf"
-      {"tr", 2},
-#line 337 "effective_tld_names.gperf"
-      {"birdart.museum", 0},
-#line 1870 "effective_tld_names.gperf"
-      {"mecon.ar", 1},
-#line 606 "effective_tld_names.gperf"
-      {"com.bz", 0},
-#line 1799 "effective_tld_names.gperf"
-      {"lubin.pl", 0},
-#line 3477 "effective_tld_names.gperf"
-      {"xn--karmy-yua.no", 0},
-#line 142 "effective_tld_names.gperf"
-      {"amur.ru", 0},
-#line 3130 "effective_tld_names.gperf"
-      {"tn", 0},
-#line 332 "effective_tld_names.gperf"
-      {"bilbao.museum", 0},
-#line 1981 "effective_tld_names.gperf"
-      {"modelling.aero", 0},
-#line 2584 "effective_tld_names.gperf"
-      {"pp.ru", 0},
-#line 2566 "effective_tld_names.gperf"
-      {"pol.ht", 0},
-#line 154 "effective_tld_names.gperf"
-      {"anthropology.museum", 0},
-#line 1690 "effective_tld_names.gperf"
-      {"kv.ua", 0},
-#line 3257 "effective_tld_names.gperf"
-      {"ustka.pl", 0},
-#line 282 "effective_tld_names.gperf"
-      {"barcelona.museum", 0},
-#line 980 "effective_tld_names.gperf"
-      {"farmstead.museum", 0},
-#line 1828 "effective_tld_names.gperf"
-      {"malatvuopmi.no", 0},
-#line 579 "effective_tld_names.gperf"
-      {"coal.museum", 0},
-#line 3187 "effective_tld_names.gperf"
-      {"tt", 0},
-#line 3018 "effective_tld_names.gperf"
-      {"steiermark.museum", 0},
-#line 237 "effective_tld_names.gperf"
-      {"aure.no", 0},
-#line 1069 "effective_tld_names.gperf"
-      {"frogn.no", 0},
-#line 1822 "effective_tld_names.gperf"
-      {"madrid.museum", 0},
-#line 1316 "effective_tld_names.gperf"
-      {"gs.sf.no", 0},
-#line 1644 "effective_tld_names.gperf"
-      {"km.ua", 0},
-#line 2282 "effective_tld_names.gperf"
-      {"nt.gov.au", 0},
-#line 2289 "effective_tld_names.gperf"
-      {"nuernberg.museum", 0},
-#line 1894 "effective_tld_names.gperf"
-      {"memorial.museum", 0},
-#line 3412 "effective_tld_names.gperf"
-      {"xn--andy-ira.no", 0},
-#line 2265 "effective_tld_names.gperf"
-      {"notaires.km", 0},
-#line 1571 "effective_tld_names.gperf"
-      {"jolster.no", 0},
-#line 2699 "effective_tld_names.gperf"
-      {"quebec.museum", 0},
-#line 2829 "effective_tld_names.gperf"
-      {"sanok.pl", 0},
-#line 707 "effective_tld_names.gperf"
-      {"coop.km", 0},
-#line 1853 "effective_tld_names.gperf"
-      {"massa-carrara.it", 0},
-#line 1622 "effective_tld_names.gperf"
-      {"kg", 0},
-#line 384 "effective_tld_names.gperf"
-      {"brand.se", 0},
-#line 2766 "effective_tld_names.gperf"
-      {"ro", 0},
-#line 2223 "effective_tld_names.gperf"
-      {"nikolaev.ua", 0},
-#line 2720 "effective_tld_names.gperf"
-      {"re", 0},
-#line 1777 "effective_tld_names.gperf"
-      {"lodi.it", 0},
-#line 3429 "effective_tld_names.gperf"
-      {"xn--bmlo-gra.no", 0},
-#line 1033 "effective_tld_names.gperf"
-      {"flora.no", 0},
-#line 3396 "effective_tld_names.gperf"
-      {"workinggroup.aero", 0},
-#line 3294 "effective_tld_names.gperf"
-      {"ve", 2},
-#line 871 "effective_tld_names.gperf"
-      {"edu.mx", 0},
-#line 3140 "effective_tld_names.gperf"
-      {"tom.ru", 0},
-#line 1202 "effective_tld_names.gperf"
-      {"gov.cx", 0},
-#line 1713 "effective_tld_names.gperf"
-      {"lajolla.museum", 0},
-#line 2042 "effective_tld_names.gperf"
-      {"nagasaki.jp", 2},
-#line 2164 "effective_tld_names.gperf"
-      {"net.mx", 0},
-#line 2725 "effective_tld_names.gperf"
-      {"rec.co", 0},
-#line 2367 "effective_tld_names.gperf"
-      {"org.bz", 0},
-#line 649 "effective_tld_names.gperf"
-      {"com.mx", 0},
-#line 772 "effective_tld_names.gperf"
-      {"detroit.museum", 0},
-#line 314 "effective_tld_names.gperf"
-      {"berkeley.museum", 0},
-#line 3486 "effective_tld_names.gperf"
-      {"xn--ksnes-uua.no", 0},
-#line 1910 "effective_tld_names.gperf"
-      {"mie.jp", 2},
-#line 1572 "effective_tld_names.gperf"
-      {"jondal.no", 0},
-#line 3344 "effective_tld_names.gperf"
-      {"vn", 0},
-#line 415 "effective_tld_names.gperf"
-      {"bushey.museum", 0},
-#line 775 "effective_tld_names.gperf"
-      {"dinosaur.museum", 0},
-#line 1162 "effective_tld_names.gperf"
-      {"gob.mx", 0},
-#line 2466 "effective_tld_names.gperf"
-      {"osen.no", 0},
-#line 2785 "effective_tld_names.gperf"
-      {"rs", 0},
-#line 2964 "effective_tld_names.gperf"
-      {"sola.no", 0},
-#line 3195 "effective_tld_names.gperf"
-      {"tv", 0},
-#line 1480 "effective_tld_names.gperf"
-      {"info.az", 0},
-#line 370 "effective_tld_names.gperf"
-      {"bologna.it", 0},
-#line 211 "effective_tld_names.gperf"
-      {"assassination.museum", 0},
-#line 778 "effective_tld_names.gperf"
-      {"divttasvuotna.no", 0},
-#line 2727 "effective_tld_names.gperf"
-      {"rec.ro", 0},
-#line 1623 "effective_tld_names.gperf"
-      {"kg.kr", 0},
-#line 159 "effective_tld_names.gperf"
-      {"aosta.it", 0},
-#line 3041 "effective_tld_names.gperf"
-      {"suedtirol.it", 0},
-#line 2722 "effective_tld_names.gperf"
-      {"re.kr", 0},
-#line 3119 "effective_tld_names.gperf"
-      {"tm", 0},
-#line 709 "effective_tld_names.gperf"
-      {"coop.tt", 0},
-#line 3132 "effective_tld_names.gperf"
-      {"tn.us", 0},
-#line 1228 "effective_tld_names.gperf"
-      {"gov.kz", 0},
-#line 3611 "effective_tld_names.gperf"
-      {"yakutia.ru", 0},
-#line 857 "effective_tld_names.gperf"
-      {"edu.kz", 0},
-#line 2279 "effective_tld_names.gperf"
-      {"nsw.gov.au", 0},
-#line 291 "effective_tld_names.gperf"
-      {"baths.museum", 0},
-#line 2150 "effective_tld_names.gperf"
-      {"net.kz", 0},
-#line 1839 "effective_tld_names.gperf"
-      {"manx.museum", 0},
-#line 3122 "effective_tld_names.gperf"
-      {"tm.km", 0},
-#line 139 "effective_tld_names.gperf"
-      {"amli.no", 0},
-#line 3391 "effective_tld_names.gperf"
-      {"windmill.museum", 0},
-#line 3212 "effective_tld_names.gperf"
-      {"tz", 0},
-#line 636 "effective_tld_names.gperf"
-      {"com.kz", 0},
-#line 2591 "effective_tld_names.gperf"
-      {"prd.fr", 0},
-#line 2324 "effective_tld_names.gperf"
-      {"omsk.ru", 0},
-#line 2554 "effective_tld_names.gperf"
-      {"plants.museum", 0},
-#line 771 "effective_tld_names.gperf"
-      {"design.museum", 0},
-#line 3012 "effective_tld_names.gperf"
-      {"stathelle.no", 0},
-#line 2787 "effective_tld_names.gperf"
-      {"ru", 0},
-#line 2417 "effective_tld_names.gperf"
-      {"org.mx", 0},
-#line 1976 "effective_tld_names.gperf"
-      {"mobi.gp", 0},
-#line 1483 "effective_tld_names.gperf"
-      {"info.ht", 0},
-#line 1121 "effective_tld_names.gperf"
-      {"georgia.museum", 0},
-#line 1958 "effective_tld_names.gperf"
-      {"missoula.museum", 0},
-#line 243 "effective_tld_names.gperf"
-      {"austrheim.no", 0},
-#line 2014 "effective_tld_names.gperf"
-      {"muenchen.museum", 0},
-#line 3358 "effective_tld_names.gperf"
-      {"vu", 0},
-#line 1800 "effective_tld_names.gperf"
-      {"lucca.it", 0},
-#line 705 "effective_tld_names.gperf"
-      {"coop.br", 0},
-#line 711 "effective_tld_names.gperf"
-      {"corporation.museum", 0},
-#line 2481 "effective_tld_names.gperf"
-      {"oyer.no", 0},
-#line 2444 "effective_tld_names.gperf"
-      {"org.sz", 0},
-#line 3088 "effective_tld_names.gperf"
-      {"tc", 0},
-#line 1842 "effective_tld_names.gperf"
-      {"mari.ru", 0},
-#line 1484 "effective_tld_names.gperf"
-      {"info.hu", 0},
-#line 1186 "effective_tld_names.gperf"
-      {"gov.az", 0},
-#line 1761 "effective_tld_names.gperf"
-      {"lillesand.no", 0},
-#line 3554 "effective_tld_names.gperf"
-      {"xn--sgne-gra.no", 0},
-#line 377 "effective_tld_names.gperf"
-      {"botanicalgarden.museum", 0},
-#line 820 "effective_tld_names.gperf"
-      {"edu.az", 0},
-#line 1592 "effective_tld_names.gperf"
-      {"k12.ec", 0},
-#line 2869 "effective_tld_names.gperf"
-      {"sciencecenter.museum", 0},
-#line 2682 "effective_tld_names.gperf"
-      {"public.museum", 0},
-#line 2115 "effective_tld_names.gperf"
-      {"net.az", 0},
-#line 101 "effective_tld_names.gperf"
-      {"air-surveillance.aero", 0},
-#line 1027 "effective_tld_names.gperf"
-      {"flanders.museum", 0},
-#line 1499 "effective_tld_names.gperf"
-      {"ingatlan.hu", 0},
-#line 596 "effective_tld_names.gperf"
-      {"com.az", 0},
-#line 3043 "effective_tld_names.gperf"
-      {"sula.no", 0},
-#line 2839 "effective_tld_names.gperf"
-      {"satx.museum", 0},
-#line 172 "effective_tld_names.gperf"
-      {"archaeology.museum", 0},
-#line 3092 "effective_tld_names.gperf"
-      {"te.ua", 0},
-#line 212 "effective_tld_names.gperf"
-      {"assedic.fr", 0},
-#line 1949 "effective_tld_names.gperf"
-      {"milan.it", 0},
-#line 3094 "effective_tld_names.gperf"
-      {"tel", 0},
-#line 1502 "effective_tld_names.gperf"
-      {"int.az", 0},
-#line 3126 "effective_tld_names.gperf"
-      {"tm.pl", 0},
-#line 144 "effective_tld_names.gperf"
-      {"amusement.aero", 0},
-#line 3507 "effective_tld_names.gperf"
-      {"xn--mely-ira.no", 0},
-#line 2697 "effective_tld_names.gperf"
-      {"qld.gov.au", 0},
-#line 446 "effective_tld_names.gperf"
-      {"casadelamoneda.museum", 0},
-#line 2765 "effective_tld_names.gperf"
-      {"rnu.tn", 0},
-#line 2828 "effective_tld_names.gperf"
-      {"sanfrancisco.museum", 0},
-#line 110 "effective_tld_names.gperf"
-      {"akita.jp", 2},
-#line 2400 "effective_tld_names.gperf"
-      {"org.kz", 0},
-#line 464 "effective_tld_names.gperf"
-      {"certification.aero", 0},
-#line 3357 "effective_tld_names.gperf"
-      {"vt.us", 0},
-#line 789 "effective_tld_names.gperf"
-      {"donna.no", 0},
-#line 2870 "effective_tld_names.gperf"
-      {"sciencecenters.museum", 0},
-#line 414 "effective_tld_names.gperf"
-      {"busan.kr", 0},
-#line 1177 "effective_tld_names.gperf"
-      {"gouv.ht", 0},
-#line 410 "effective_tld_names.gperf"
-      {"building.museum", 0},
-#line 736 "effective_tld_names.gperf"
-      {"cuneo.it", 0},
-#line 2558 "effective_tld_names.gperf"
-      {"plo.ps", 0},
-#line 2502 "effective_tld_names.gperf"
-      {"paris.museum", 0},
-#line 2534 "effective_tld_names.gperf"
-      {"pharmacy.museum", 0},
-#line 708 "effective_tld_names.gperf"
-      {"coop.mw", 0},
-#line 2478 "effective_tld_names.gperf"
-      {"overhalla.no", 0},
-#line 1738 "effective_tld_names.gperf"
-      {"lecce.it", 0},
-#line 3291 "effective_tld_names.gperf"
-      {"vc", 0},
-#line 3433 "effective_tld_names.gperf"
-      {"xn--brum-voa.no", 0},
-#line 3134 "effective_tld_names.gperf"
-      {"to.it", 0},
-#line 2684 "effective_tld_names.gperf"
-      {"pulawy.pl", 0},
-#line 348 "effective_tld_names.gperf"
-      {"biz.tj", 0},
-#line 3091 "effective_tld_names.gperf"
-      {"te.it", 0},
-#line 2585 "effective_tld_names.gperf"
-      {"pp.se", 0},
-#line 2491 "effective_tld_names.gperf"
-      {"paderborn.museum", 0},
-#line 1303 "effective_tld_names.gperf"
-      {"gs.bu.no", 0},
-#line 2358 "effective_tld_names.gperf"
-      {"org.az", 0},
-#line 1048 "effective_tld_names.gperf"
-      {"forlicesena.it", 0},
-#line 3156 "effective_tld_names.gperf"
-      {"tr.it", 0},
-#line 1030 "effective_tld_names.gperf"
-      {"flesberg.no", 0},
-#line 3009 "effective_tld_names.gperf"
-      {"stat.no", 0},
-#line 3131 "effective_tld_names.gperf"
-      {"tn.it", 0},
-#line 527 "effective_tld_names.gperf"
-      {"clock.museum", 0},
-#line 496 "effective_tld_names.gperf"
-      {"cieszyn.pl", 0},
-#line 1645 "effective_tld_names.gperf"
-      {"kms.ru", 0},
-#line 2668 "effective_tld_names.gperf"
-      {"project.museum", 0},
-#line 2463 "effective_tld_names.gperf"
-      {"os.hedmark.no", 0},
-#line 3563 "effective_tld_names.gperf"
-      {"xn--smna-gra.no", 0},
-#line 160 "effective_tld_names.gperf"
-      {"aoste.it", 0},
-#line 714 "effective_tld_names.gperf"
-      {"costume.museum", 0},
-#line 3184 "effective_tld_names.gperf"
-      {"ts.it", 0},
-#line 2833 "effective_tld_names.gperf"
-      {"saotome.st", 0},
-#line 1883 "effective_tld_names.gperf"
-      {"media.aero", 0},
-#line 1555 "effective_tld_names.gperf"
-      {"jeonnam.kr", 0},
-#line 3616 "effective_tld_names.gperf"
-      {"yaroslavl.ru", 0},
-#line 779 "effective_tld_names.gperf"
-      {"dj", 0},
-#line 3345 "effective_tld_names.gperf"
-      {"vn.ua", 0},
-#line 3372 "effective_tld_names.gperf"
-      {"warszawa.pl", 0},
-#line 3497 "effective_tld_names.gperf"
-      {"xn--lgrd-poac.no", 0},
-#line 725 "effective_tld_names.gperf"
-      {"cremona.it", 0},
-#line 1766 "effective_tld_names.gperf"
-      {"linz.museum", 0},
-#line 2890 "effective_tld_names.gperf"
-      {"sendai.jp", 2},
-#line 1439 "effective_tld_names.gperf"
-      {"ibestad.no", 0},
-#line 1752 "effective_tld_names.gperf"
-      {"lezajsk.pl", 0},
-#line 2934 "effective_tld_names.gperf"
-      {"skjervoy.no", 0},
-#line 3204 "effective_tld_names.gperf"
-      {"tx.us", 0},
-#line 1734 "effective_tld_names.gperf"
-      {"leangaviika.no", 0},
-#line 3355 "effective_tld_names.gperf"
-      {"vrn.ru", 0},
-#line 2004 "effective_tld_names.gperf"
-      {"mragowo.pl", 0},
-#line 3062 "effective_tld_names.gperf"
-      {"swiebodzin.pl", 0},
-#line 3199 "effective_tld_names.gperf"
-      {"tv.na", 0},
-#line 1296 "effective_tld_names.gperf"
-      {"group.aero", 0},
-#line 3097 "effective_tld_names.gperf"
-      {"teramo.it", 0},
-#line 698 "effective_tld_names.gperf"
-      {"consultant.aero", 0},
-#line 264 "effective_tld_names.gperf"
-      {"badajoz.museum", 0},
-#line 2767 "effective_tld_names.gperf"
-      {"ro.it", 0},
-#line 2278 "effective_tld_names.gperf"
-      {"nsw.edu.au", 0},
-#line 763 "effective_tld_names.gperf"
-      {"decorativearts.museum", 0},
-#line 1015 "effective_tld_names.gperf"
-      {"firm.in", 0},
-#line 2721 "effective_tld_names.gperf"
-      {"re.it", 0},
-#line 3269 "effective_tld_names.gperf"
-      {"va", 0},
-#line 1380 "effective_tld_names.gperf"
-      {"heroy.more-og-romsdal.no", 0},
-#line 785 "effective_tld_names.gperf"
-      {"dni.us", 0},
-#line 3295 "effective_tld_names.gperf"
-      {"ve.it", 0},
-#line 477 "effective_tld_names.gperf"
-      {"chernigov.ua", 0},
-#line 791 "effective_tld_names.gperf"
-      {"dovre.no", 0},
-#line 906 "effective_tld_names.gperf"
-      {"educational.museum", 0},
-#line 1084 "effective_tld_names.gperf"
-      {"fusa.no", 0},
-#line 3104 "effective_tld_names.gperf"
-      {"tg", 0},
-#line 3320 "effective_tld_names.gperf"
-      {"vi", 0},
-#line 2574 "effective_tld_names.gperf"
-      {"porsgrunn.no", 0},
-#line 3354 "effective_tld_names.gperf"
-      {"vr.it", 0},
-#line 2490 "effective_tld_names.gperf"
-      {"pacific.museum", 0},
-#line 2761 "effective_tld_names.gperf"
-      {"rn.it", 0},
-#line 580 "effective_tld_names.gperf"
-      {"coastaldefence.museum", 0},
-#line 3256 "effective_tld_names.gperf"
-      {"uslivinghistory.museum", 0},
-#line 3585 "effective_tld_names.gperf"
-      {"xn--tysvr-vra.no", 0},
-#line 3235 "effective_tld_names.gperf"
-      {"undersea.museum", 0},
-#line 1591 "effective_tld_names.gperf"
-      {"k.se", 0},
-#line 1318 "effective_tld_names.gperf"
-      {"gs.svalbard.no", 0},
-#line 1139 "effective_tld_names.gperf"
-      {"glass.museum", 0},
-#line 3198 "effective_tld_names.gperf"
-      {"tv.it", 0},
-#line 2834 "effective_tld_names.gperf"
-      {"sapporo.jp", 2},
-#line 3356 "effective_tld_names.gperf"
-      {"vt.it", 0},
-#line 3218 "effective_tld_names.gperf"
-      {"udine.it", 0},
-#line 2792 "effective_tld_names.gperf"
-      {"rv.ua", 0},
-#line 3027 "effective_tld_names.gperf"
-      {"stord.no", 0},
-#line 3070 "effective_tld_names.gperf"
-      {"szczecin.pl", 0},
-#line 317 "effective_tld_names.gperf"
-      {"bern.museum", 0},
-#line 2260 "effective_tld_names.gperf"
-      {"norfolk.museum", 0},
-#line 1698 "effective_tld_names.gperf"
-      {"kvitsoy.no", 0},
-#line 3369 "effective_tld_names.gperf"
-      {"wallonie.museum", 0},
-#line 1036 "effective_tld_names.gperf"
-      {"floro.no", 0},
-#line 3432 "effective_tld_names.gperf"
-      {"xn--brnnysund-m8ac.no", 0},
-#line 1739 "effective_tld_names.gperf"
-      {"lecco.it", 0},
-#line 690 "effective_tld_names.gperf"
-      {"community.museum", 0},
-#line 478 "effective_tld_names.gperf"
-      {"chernovtsy.ua", 0},
-#line 3306 "effective_tld_names.gperf"
-      {"verran.no", 0},
-#line 2696 "effective_tld_names.gperf"
-      {"qld.edu.au", 0},
-#line 1526 "effective_tld_names.gperf"
-      {"iron.museum", 0},
-#line 1120 "effective_tld_names.gperf"
-      {"geometre-expert.fr", 0},
-#line 398 "effective_tld_names.gperf"
-      {"brumunddal.no", 0},
-#line 2724 "effective_tld_names.gperf"
-      {"rec.br", 0},
-#line 1022 "effective_tld_names.gperf"
-      {"fjell.no", 0},
-#line 3313 "effective_tld_names.gperf"
-      {"vet.br", 0},
-#line 1127 "effective_tld_names.gperf"
-      {"giehtavuoatna.no", 0},
-#line 3035 "effective_tld_names.gperf"
-      {"stranda.no", 0},
-#line 769 "effective_tld_names.gperf"
-      {"depot.museum", 0},
-#line 2917 "effective_tld_names.gperf"
-      {"silk.museum", 0},
-#line 2553 "effective_tld_names.gperf"
-      {"plantation.museum", 0},
-#line 271 "effective_tld_names.gperf"
-      {"bajddar.no", 0},
-#line 3189 "effective_tld_names.gperf"
-      {"tur.br", 0},
-#line 2104 "effective_tld_names.gperf"
-      {"nesoddtangen.no", 0},
-#line 2726 "effective_tld_names.gperf"
-      {"rec.nf", 0},
-#line 2885 "effective_tld_names.gperf"
-      {"sejny.pl", 0},
-#line 3318 "effective_tld_names.gperf"
-      {"vg", 0},
-#line 218 "effective_tld_names.gperf"
-      {"asso.gp", 0},
-#line 121 "effective_tld_names.gperf"
-      {"alessandria.it", 0},
-#line 420 "effective_tld_names.gperf"
-      {"bykle.no", 0},
-#line 1173 "effective_tld_names.gperf"
-      {"gorlice.pl", 0},
-#line 3272 "effective_tld_names.gperf"
-      {"va.us", 0},
-#line 2901 "effective_tld_names.gperf"
-      {"shell.museum", 0},
-#line 1692 "effective_tld_names.gperf"
-      {"kvalsund.no", 0},
-#line 3359 "effective_tld_names.gperf"
-      {"vv.it", 0},
-#line 3642 "effective_tld_names.gperf"
-      {"zhitomir.ua", 0},
-#line 2936 "effective_tld_names.gperf"
-      {"skoczow.pl", 0},
-#line 2748 "effective_tld_names.gperf"
-      {"ri.us", 0},
-#line 1537 "effective_tld_names.gperf"
-      {"ivano-frankivsk.ua", 0},
-#line 3322 "effective_tld_names.gperf"
-      {"vi.us", 0},
-#line 2708 "effective_tld_names.gperf"
-      {"raholt.no", 0},
-#line 2760 "effective_tld_names.gperf"
-      {"rm.it", 0},
-#line 1217 "effective_tld_names.gperf"
-      {"gov.iq", 0},
-#line 1960 "effective_tld_names.gperf"
-      {"miyazaki.jp", 2},
-#line 3613 "effective_tld_names.gperf"
-      {"yamaguchi.jp", 2},
-#line 848 "effective_tld_names.gperf"
-      {"edu.iq", 0},
-#line 3129 "effective_tld_names.gperf"
-      {"tmp.br", 0},
-#line 2141 "effective_tld_names.gperf"
-      {"net.iq", 0},
-#line 629 "effective_tld_names.gperf"
-      {"com.iq", 0},
-#line 2056 "effective_tld_names.gperf"
-      {"name.tj", 0},
-#line 2896 "effective_tld_names.gperf"
-      {"sex.pl", 0},
-#line 284 "effective_tld_names.gperf"
-      {"bari.it", 0},
-#line 2264 "effective_tld_names.gperf"
-      {"notaires.fr", 0},
-#line 3558 "effective_tld_names.gperf"
-      {"xn--sknit-yqa.no", 0},
-#line 1832 "effective_tld_names.gperf"
-      {"malselv.no", 0},
-#line 3089 "effective_tld_names.gperf"
-      {"tcm.museum", 0},
-#line 1590 "effective_tld_names.gperf"
-      {"k.bg", 0},
-#line 798 "effective_tld_names.gperf"
-      {"durham.museum", 0},
-#line 386 "effective_tld_names.gperf"
-      {"brasil.museum", 0},
-#line 1735 "effective_tld_names.gperf"
-      {"leasing.aero", 0},
-#line 2794 "effective_tld_names.gperf"
-      {"ryazan.ru", 0},
-#line 3033 "effective_tld_names.gperf"
-      {"stpetersburg.museum", 0},
-#line 1373 "effective_tld_names.gperf"
-      {"helsinki.museum", 0},
-#line 2571 "effective_tld_names.gperf"
-      {"pordenone.it", 0},
-#line 266 "effective_tld_names.gperf"
-      {"bahcavuotna.no", 0},
-#line 2762 "effective_tld_names.gperf"
-      {"rnd.ru", 0},
-#line 2719 "effective_tld_names.gperf"
-      {"rc.it", 0},
-#line 1745 "effective_tld_names.gperf"
-      {"leksvik.no", 0},
-#line 3292 "effective_tld_names.gperf"
-      {"vc.it", 0},
-#line 1031 "effective_tld_names.gperf"
-      {"flight.aero", 0},
-#line 3076 "effective_tld_names.gperf"
-      {"ta.it", 0},
-#line 125 "effective_tld_names.gperf"
-      {"alta.no", 0},
-#line 73 "effective_tld_names.gperf"
-      {"adygeya.ru", 0},
-#line 3223 "effective_tld_names.gperf"
-      {"uhren.museum", 0},
-#line 753 "effective_tld_names.gperf"
-      {"dallas.museum", 0},
-#line 374 "effective_tld_names.gperf"
-      {"bonn.museum", 0},
-#line 2206 "effective_tld_names.gperf"
-      {"newspaper.museum", 0},
-#line 1064 "effective_tld_names.gperf"
-      {"frei.no", 0},
-#line 2348 "effective_tld_names.gperf"
-      {"oregontrail.museum", 0},
-#line 265 "effective_tld_names.gperf"
-      {"baghdad.museum", 0},
-#line 3516 "effective_tld_names.gperf"
-      {"xn--mot-tla.no", 0},
-#line 3157 "effective_tld_names.gperf"
-      {"tr.no", 0},
-#line 1952 "effective_tld_names.gperf"
-      {"mill.museum", 0},
-#line 2390 "effective_tld_names.gperf"
-      {"org.iq", 0},
-#line 735 "effective_tld_names.gperf"
-      {"culture.museum", 0},
-#line 3253 "effective_tld_names.gperf"
-      {"usgarden.museum", 0},
-#line 1072 "effective_tld_names.gperf"
-      {"frosinone.it", 0},
-#line 2914 "effective_tld_names.gperf"
-      {"siena.it", 0},
-#line 2500 "effective_tld_names.gperf"
-      {"parachuting.aero", 0},
-#line 3162 "effective_tld_names.gperf"
-      {"tranby.no", 0},
-#line 1990 "effective_tld_names.gperf"
-      {"montreal.museum", 0},
-#line 2052 "effective_tld_names.gperf"
-      {"name.mk", 0},
-#line 3615 "effective_tld_names.gperf"
-      {"yamanashi.jp", 2},
-#line 2050 "effective_tld_names.gperf"
-      {"name.hr", 0},
-#line 2049 "effective_tld_names.gperf"
-      {"name.az", 0},
-#line 2764 "effective_tld_names.gperf"
-      {"rns.tn", 0},
-#line 1607 "effective_tld_names.gperf"
-      {"karlsoy.no", 0},
-#line 988 "effective_tld_names.gperf"
-      {"fermo.it", 0},
-#line 3639 "effective_tld_names.gperf"
-      {"zgora.pl", 0},
-#line 2702 "effective_tld_names.gperf"
-      {"ra.it", 0},
-#line 3154 "effective_tld_names.gperf"
-      {"tp.it", 0},
-#line 3270 "effective_tld_names.gperf"
-      {"va.it", 0},
-#line 1667 "effective_tld_names.gperf"
-      {"kr.com", 0},
-#line 196 "effective_tld_names.gperf"
-      {"artsandcrafts.museum", 0},
-#line 3574 "effective_tld_names.gperf"
-      {"xn--srum-gra.no", 0},
-#line 2509 "effective_tld_names.gperf"
-      {"pb.ao", 0},
-#line 3637 "effective_tld_names.gperf"
-      {"zaporizhzhe.ua", 0},
-#line 2266 "effective_tld_names.gperf"
-      {"notodden.no", 0},
-#line 2747 "effective_tld_names.gperf"
-      {"ri.it", 0},
-#line 2234 "effective_tld_names.gperf"
-      {"nnov.ru", 0},
-#line 3321 "effective_tld_names.gperf"
-      {"vi.it", 0},
-#line 1599 "effective_tld_names.gperf"
-      {"kaluga.ru", 0},
-#line 1134 "effective_tld_names.gperf"
-      {"gjerstad.no", 0},
-#line 1785 "effective_tld_names.gperf"
-      {"losangeles.museum", 0},
-#line 122 "effective_tld_names.gperf"
-      {"alesund.no", 0},
-#line 3036 "effective_tld_names.gperf"
-      {"stryn.no", 0},
-#line 173 "effective_tld_names.gperf"
-      {"architecture.museum", 0},
-#line 1029 "effective_tld_names.gperf"
-      {"flekkefjord.no", 0},
-#line 2505 "effective_tld_names.gperf"
-      {"parti.se", 0},
-#line 3387 "effective_tld_names.gperf"
-      {"wielun.pl", 0},
-#line 2804 "effective_tld_names.gperf"
-      {"sa.gov.au", 0},
-#line 2660 "effective_tld_names.gperf"
-      {"pro.ht", 0},
-#line 3075 "effective_tld_names.gperf"
-      {"t.se", 0},
-#line 1080 "effective_tld_names.gperf"
-      {"fundacio.museum", 0},
-#line 3123 "effective_tld_names.gperf"
-      {"tm.mc", 0},
-#line 2841 "effective_tld_names.gperf"
-      {"sauherad.no", 0},
-#line 699 "effective_tld_names.gperf"
-      {"consulting.aero", 0},
-#line 3556 "effective_tld_names.gperf"
-      {"xn--skjervy-v1a.no", 0},
-#line 2073 "effective_tld_names.gperf"
-      {"nativeamerican.museum", 0},
-#line 3125 "effective_tld_names.gperf"
-      {"tm.no", 0},
-#line 286 "effective_tld_names.gperf"
-      {"barlettaandriatrani.it", 0},
-#line 285 "effective_tld_names.gperf"
-      {"barletta-andria-trani.it", 0},
-#line 3236 "effective_tld_names.gperf"
-      {"union.aero", 0},
-#line 2735 "effective_tld_names.gperf"
-      {"rel.pl", 0},
-#line 118 "effective_tld_names.gperf"
-      {"alaheadju.no", 0},
-#line 795 "effective_tld_names.gperf"
-      {"drangedal.no", 0},
-#line 3364 "effective_tld_names.gperf"
-      {"wa.gov.au", 0},
-#line 411 "effective_tld_names.gperf"
-      {"burghof.museum", 0},
-#line 219 "effective_tld_names.gperf"
-      {"asso.ht", 0},
-#line 1485 "effective_tld_names.gperf"
-      {"info.ki", 0},
-#line 1016 "effective_tld_names.gperf"
-      {"firm.nf", 0},
-#line 2918 "effective_tld_names.gperf"
-      {"simbirsk.ru", 0},
-#line 3544 "effective_tld_names.gperf"
-      {"xn--rros-gra.no", 0},
-#line 2746 "effective_tld_names.gperf"
-      {"rg.it", 0},
-#line 3169 "effective_tld_names.gperf"
-      {"trd.br", 0},
-#line 268 "effective_tld_names.gperf"
-      {"bahn.museum", 0},
-#line 1133 "effective_tld_names.gperf"
-      {"gjerdrum.no", 0},
-#line 977 "effective_tld_names.gperf"
-      {"farm.museum", 0},
-#line 1909 "effective_tld_names.gperf"
-      {"midtre-gauldal.no", 0},
-#line 2070 "effective_tld_names.gperf"
-      {"national.museum", 0},
-#line 2474 "effective_tld_names.gperf"
-      {"ostrowiec.pl", 0},
-#line 2524 "effective_tld_names.gperf"
-      {"perugia.it", 0},
-#line 2868 "effective_tld_names.gperf"
-      {"scienceandindustry.museum", 0},
-#line 1961 "effective_tld_names.gperf"
-      {"mjondalen.no", 0},
-#line 3360 "effective_tld_names.gperf"
-      {"vyatka.ru", 0},
-#line 1541 "effective_tld_names.gperf"
-      {"iwate.jp", 2},
-#line 367 "effective_tld_names.gperf"
-      {"bodo.no", 0},
-#line 2503 "effective_tld_names.gperf"
-      {"parliament.uk", 1},
-#line 307 "effective_tld_names.gperf"
-      {"bellevue.museum", 0},
-#line 262 "effective_tld_names.gperf"
-      {"babia-gora.pl", 0},
-#line 2701 "effective_tld_names.gperf"
-      {"r.se", 0},
-#line 3067 "effective_tld_names.gperf"
-      {"sykkylven.no", 0},
-#line 2826 "effective_tld_names.gperf"
-      {"sandnessjoen.no", 0},
-#line 3060 "effective_tld_names.gperf"
-      {"sweden.museum", 0},
-#line 1665 "effective_tld_names.gperf"
-      {"kostroma.ru", 0},
-#line 2935 "effective_tld_names.gperf"
-      {"sklep.pl", 0},
-#line 1803 "effective_tld_names.gperf"
-      {"lukow.pl", 0},
-#line 493 "effective_tld_names.gperf"
-      {"chungnam.kr", 0},
-#line 2290 "effective_tld_names.gperf"
-      {"nuoro.it", 0},
-#line 135 "effective_tld_names.gperf"
-      {"american.museum", 0},
-#line 799 "effective_tld_names.gperf"
-      {"dyroy.no", 0},
-#line 2902 "effective_tld_names.gperf"
-      {"sherbrooke.museum", 0},
-#line 1758 "effective_tld_names.gperf"
-      {"lier.no", 0},
-#line 1907 "effective_tld_names.gperf"
-      {"midatlantic.museum", 0},
-#line 3029 "effective_tld_names.gperf"
-      {"store.nf", 0},
-#line 3117 "effective_tld_names.gperf"
-      {"tk", 0},
-#line 2988 "effective_tld_names.gperf"
-      {"spjelkavik.no", 0},
-#line 3127 "effective_tld_names.gperf"
-      {"tm.ro", 0},
-#line 3584 "effective_tld_names.gperf"
-      {"xn--troms-zua.no", 0},
-#line 2938 "effective_tld_names.gperf"
-      {"skole.museum", 0},
-#line 2927 "effective_tld_names.gperf"
-      {"skedsmokorset.no", 0},
-#line 710 "effective_tld_names.gperf"
-      {"copenhagen.museum", 0},
-#line 3074 "effective_tld_names.gperf"
-      {"t.bg", 0},
-#line 1554 "effective_tld_names.gperf"
-      {"jeonbuk.kr", 0},
-#line 3444 "effective_tld_names.gperf"
-      {"xn--finny-yua.no", 0},
-#line 3473 "effective_tld_names.gperf"
-      {"xn--io0a7i.cn", 0},
-#line 2783 "effective_tld_names.gperf"
-      {"royken.no", 0},
-#line 1013 "effective_tld_names.gperf"
-      {"firm.co", 0},
-#line 64 "effective_tld_names.gperf"
-      {"accident-investigation.aero", 0},
-#line 1624 "effective_tld_names.gperf"
-      {"kh", 2},
-#line 1933 "effective_tld_names.gperf"
-      {"mil.kz", 0},
-#line 3144 "effective_tld_names.gperf"
-      {"torino.it", 0},
-#line 1604 "effective_tld_names.gperf"
-      {"karate.museum", 0},
-#line 788 "effective_tld_names.gperf"
-      {"donetsk.ua", 0},
-#line 750 "effective_tld_names.gperf"
-      {"daejeon.kr", 0},
-#line 2781 "effective_tld_names.gperf"
-      {"rovigo.it", 0},
-#line 2887 "effective_tld_names.gperf"
-      {"selbu.no", 0},
-#line 3603 "effective_tld_names.gperf"
-      {"xn--yer-zna.no", 0},
-#line 695 "effective_tld_names.gperf"
-      {"conference.aero", 0},
-#line 1574 "effective_tld_names.gperf"
-      {"jorpeland.no", 0},
-#line 380 "effective_tld_names.gperf"
-      {"bozen.it", 0},
-#line 3305 "effective_tld_names.gperf"
-      {"verona.it", 0},
-#line 3005 "effective_tld_names.gperf"
-      {"starachowice.pl", 0},
-#line 766 "effective_tld_names.gperf"
-      {"delmenhorst.museum", 0},
-#line 3262 "effective_tld_names.gperf"
-      {"uvic.museum", 0},
-#line 2706 "effective_tld_names.gperf"
-      {"ragusa.it", 0},
-#line 119 "effective_tld_names.gperf"
-      {"aland.fi", 0},
-#line 1130 "effective_tld_names.gperf"
-      {"gildeskal.no", 0},
-#line 140 "effective_tld_names.gperf"
-      {"amot.no", 0},
-#line 2737 "effective_tld_names.gperf"
-      {"rennebu.no", 0},
-#line 1492 "effective_tld_names.gperf"
-      {"info.pr", 0},
-#line 2646 "effective_tld_names.gperf"
-      {"presse.ci", 0},
-#line 1916 "effective_tld_names.gperf"
-      {"mil.az", 0},
-#line 1685 "effective_tld_names.gperf"
-      {"kurgan.ru", 0},
-#line 3478 "effective_tld_names.gperf"
-      {"xn--kfjord-iua.no", 0},
-#line 3118 "effective_tld_names.gperf"
-      {"tl", 0},
-#line 2700 "effective_tld_names.gperf"
-      {"r.bg", 0},
-#line 1850 "effective_tld_names.gperf"
-      {"marylhurst.museum", 0},
-#line 3268 "effective_tld_names.gperf"
-      {"v.bg", 0},
-#line 2937 "effective_tld_names.gperf"
-      {"skodje.no", 0},
-#line 913 "effective_tld_names.gperf"
-      {"ehime.jp", 2},
-#line 1631 "effective_tld_names.gperf"
-      {"khv.ru", 0},
-#line 2572 "effective_tld_names.gperf"
-      {"porsanger.no", 0},
-#line 98 "effective_tld_names.gperf"
-      {"aichi.jp", 2},
-#line 1175 "effective_tld_names.gperf"
-      {"gouv.ci", 0},
-#line 2745 "effective_tld_names.gperf"
-      {"retina.ar", 1},
-#line 2671 "effective_tld_names.gperf"
-      {"przeworsk.pl", 0},
-#line 194 "effective_tld_names.gperf"
-      {"arts.nf", 0},
-#line 3271 "effective_tld_names.gperf"
-      {"va.no", 0},
-#line 1552 "effective_tld_names.gperf"
-      {"jeju.kr", 0},
-#line 470 "effective_tld_names.gperf"
-      {"championship.aero", 0},
-#line 2740 "effective_tld_names.gperf"
-      {"res.aero", 0},
-#line 1742 "effective_tld_names.gperf"
-      {"leirfjord.no", 0},
-#line 1140 "effective_tld_names.gperf"
-      {"gliding.aero", 0},
-#line 3618 "effective_tld_names.gperf"
-      {"yekaterinburg.ru", 0},
-#line 1594 "effective_tld_names.gperf"
-      {"kafjord.no", 0},
-#line 933 "effective_tld_names.gperf"
-      {"engerdal.no", 0},
-#line 1699 "effective_tld_names.gperf"
-      {"kw", 2},
-#line 3231 "effective_tld_names.gperf"
-      {"ulsan.kr", 0},
-#line 1497 "effective_tld_names.gperf"
-      {"info.vn", 0},
-#line 2689 "effective_tld_names.gperf"
-      {"pyatigorsk.ru", 0},
-#line 1740 "effective_tld_names.gperf"
-      {"legnica.pl", 0},
-#line 787 "effective_tld_names.gperf"
-      {"dolls.museum", 0},
-#line 193 "effective_tld_names.gperf"
-      {"arts.museum", 0},
-#line 3490 "effective_tld_names.gperf"
-      {"xn--l-1fa.no", 0},
-#line 3300 "effective_tld_names.gperf"
-      {"venice.it", 0},
-#line 1085 "effective_tld_names.gperf"
-      {"fylkesbibl.no", 0},
-#line 2541 "effective_tld_names.gperf"
-      {"piacenza.it", 0},
-#line 1330 "effective_tld_names.gperf"
-      {"guovdageaidnu.no", 0},
-#line 3504 "effective_tld_names.gperf"
-      {"xn--lt-liac.no", 0},
-#line 1001 "effective_tld_names.gperf"
-      {"field.museum", 0},
-#line 1433 "effective_tld_names.gperf"
-      {"hyogo.jp", 2},
-#line 475 "effective_tld_names.gperf"
-      {"chelyabinsk.ru", 0},
-#line 1005 "effective_tld_names.gperf"
-      {"film.museum", 0},
-#line 3045 "effective_tld_names.gperf"
-      {"suli.hu", 0},
-#line 3298 "effective_tld_names.gperf"
-      {"vegarshei.no", 0},
-#line 929 "effective_tld_names.gperf"
-      {"encyclopedic.museum", 0},
-#line 2555 "effective_tld_names.gperf"
-      {"plaza.museum", 0},
-#line 3124 "effective_tld_names.gperf"
-      {"tm.mg", 0},
-#line 1491 "effective_tld_names.gperf"
-      {"info.pl", 0},
-#line 696 "effective_tld_names.gperf"
-      {"congresodelalengua3.ar", 1},
-#line 1357 "effective_tld_names.gperf"
-      {"hanggliding.aero", 0},
-#line 3316 "effective_tld_names.gperf"
-      {"vevelstad.no", 0},
-#line 2573 "effective_tld_names.gperf"
-      {"porsangu.no", 0},
-#line 3209 "effective_tld_names.gperf"
-      {"tysnes.no", 0},
-#line 1575 "effective_tld_names.gperf"
-      {"joshkar-ola.ru", 0},
-#line 706 "effective_tld_names.gperf"
-      {"coop.ht", 0},
-#line 1487 "effective_tld_names.gperf"
-      {"info.na", 0},
-#line 3086 "effective_tld_names.gperf"
-      {"tatarstan.ru", 0},
-#line 2569 "effective_tld_names.gperf"
-      {"pomorskie.pl", 0},
-#line 3550 "effective_tld_names.gperf"
-      {"xn--s-1fa.no", 0},
-#line 2675 "effective_tld_names.gperf"
-      {"pskov.ru", 0},
-#line 3145 "effective_tld_names.gperf"
-      {"torino.museum", 0},
-#line 1888 "effective_tld_names.gperf"
-      {"medizinhistorisches.museum", 0},
-#line 474 "effective_tld_names.gperf"
-      {"cheltenham.museum", 0},
-#line 3172 "effective_tld_names.gperf"
-      {"trento.it", 0},
 #line 192 "effective_tld_names.gperf"
-      {"arts.co", 0},
-#line 214 "effective_tld_names.gperf"
-      {"assn.lk", 0},
-#line 1606 "effective_tld_names.gperf"
-      {"karikatur.museum", 0},
-#line 1551 "effective_tld_names.gperf"
-      {"jefferson.museum", 0},
-#line 3211 "effective_tld_names.gperf"
-      {"tyumen.ru", 0},
-#line 3171 "effective_tld_names.gperf"
-      {"trentino.it", 0},
-#line 2533 "effective_tld_names.gperf"
-      {"pharmaciens.km", 0},
-#line 2837 "effective_tld_names.gperf"
-      {"saskatchewan.museum", 0},
-#line 2772 "effective_tld_names.gperf"
-      {"rollag.no", 0},
-#line 2330 "effective_tld_names.gperf"
-      {"opole.pl", 0},
-#line 2103 "effective_tld_names.gperf"
-      {"nesodden.no", 0},
-#line 443 "effective_tld_names.gperf"
-      {"cargo.aero", 0},
-#line 1625 "effective_tld_names.gperf"
-      {"kh.ua", 0},
-#line 1017 "effective_tld_names.gperf"
-      {"firm.ro", 0},
-#line 136 "effective_tld_names.gperf"
-      {"americana.museum", 0},
-#line 1532 "effective_tld_names.gperf"
-      {"isla.pr", 0},
-#line 366 "effective_tld_names.gperf"
-      {"bo.telemark.no", 0},
-#line 2496 "effective_tld_names.gperf"
-      {"paleo.museum", 0},
-#line 2991 "effective_tld_names.gperf"
-      {"spydeberg.no", 0},
-#line 3008 "effective_tld_names.gperf"
-      {"starostwo.gov.pl", 0},
-#line 3166 "effective_tld_names.gperf"
-      {"travel", 0},
-#line 1744 "effective_tld_names.gperf"
-      {"leka.no", 0},
-#line 3593 "effective_tld_names.gperf"
-      {"xn--vestvgy-ixa6o.no", 0},
-#line 3385 "effective_tld_names.gperf"
-      {"whaling.museum", 0},
-#line 3606 "effective_tld_names.gperf"
-      {"xn--zf0ao64a.tw", 0},
-#line 2788 "effective_tld_names.gperf"
-      {"ru.com", 0},
-#line 2221 "effective_tld_names.gperf"
-      {"nieruchomosci.pl", 0},
-#line 746 "effective_tld_names.gperf"
-      {"czest.pl", 0},
-#line 3311 "effective_tld_names.gperf"
-      {"vestre-toten.no", 0},
-#line 764 "effective_tld_names.gperf"
-      {"defense.tn", 0},
-#line 138 "effective_tld_names.gperf"
-      {"americanart.museum", 0},
-#line 1563 "effective_tld_names.gperf"
-      {"jfk.museum", 0},
-#line 3436 "effective_tld_names.gperf"
-      {"xn--comunicaes-v6a2o.museum", 0},
-#line 3210 "effective_tld_names.gperf"
-      {"tysvar.no", 0},
-#line 2575 "effective_tld_names.gperf"
-      {"port.fr", 0},
-#line 3308 "effective_tld_names.gperf"
-      {"vestby.no", 0},
-#line 3548 "effective_tld_names.gperf"
-      {"xn--ryken-vua.no", 0},
-#line 3414 "effective_tld_names.gperf"
-      {"xn--asky-ira.no", 0},
-#line 3515 "effective_tld_names.gperf"
-      {"xn--mosjen-eya.no", 0},
-#line 3106 "effective_tld_names.gperf"
-      {"th", 0},
-#line 3163 "effective_tld_names.gperf"
-      {"tranoy.no", 0},
-#line 283 "effective_tld_names.gperf"
-      {"bardu.no", 0},
-#line 355 "effective_tld_names.gperf"
-      {"bjugn.no", 0},
-#line 1453 "effective_tld_names.gperf"
-      {"ilawa.pl", 0},
-#line 310 "effective_tld_names.gperf"
-      {"berg.no", 0},
-#line 2984 "effective_tld_names.gperf"
-      {"southwest.museum", 0},
-#line 3502 "effective_tld_names.gperf"
-      {"xn--lrdal-sra.no", 0},
-#line 2251 "effective_tld_names.gperf"
-      {"nome.pt", 0},
-#line 1392 "effective_tld_names.gperf"
-      {"historyofscience.museum", 0},
-#line 1760 "effective_tld_names.gperf"
-      {"lillehammer.no", 0},
-#line 2548 "effective_tld_names.gperf"
-      {"pittsburgh.museum", 0},
-#line 3024 "effective_tld_names.gperf"
-      {"stockholm.museum", 0},
-#line 3168 "effective_tld_names.gperf"
-      {"travel.tt", 0},
-#line 1560 "effective_tld_names.gperf"
-      {"jewelry.museum", 0},
-#line 3293 "effective_tld_names.gperf"
-      {"vdonsk.ru", 0},
-#line 3022 "effective_tld_names.gperf"
-      {"stjordal.no", 0},
-#line 2317 "effective_tld_names.gperf"
-      {"olawa.pl", 0},
-#line 3573 "effective_tld_names.gperf"
-      {"xn--srreisa-q1a.no", 0},
-#line 3417 "effective_tld_names.gperf"
-      {"xn--b-5ga.nordland.no", 0},
+      {"artdeco.museum", 0},
+#line 1072 "effective_tld_names.gperf"
+      {"foundation.museum", 0},
 #line 2998 "effective_tld_names.gperf"
-      {"sshn.se", 0},
-#line 745 "effective_tld_names.gperf"
-      {"czeladz.pl", 0},
+      {"sn", 0},
+#line 993 "effective_tld_names.gperf"
+      {"fareast.ru", 0},
+#line 3090 "effective_tld_names.gperf"
+      {"su", 0},
+#line 580 "effective_tld_names.gperf"
+      {"co.th", 0},
+#line 347 "effective_tld_names.gperf"
+      {"biz.at", 0},
+#line 313 "effective_tld_names.gperf"
+      {"bellevue.museum", 0},
+#line 3431 "effective_tld_names.gperf"
+      {"waw.pl", 0},
+#line 1371 "effective_tld_names.gperf"
+      {"halden.no", 0},
+#line 1877 "effective_tld_names.gperf"
+      {"marnardal.no", 0},
+#line 1065 "effective_tld_names.gperf"
+      {"forlicesena.it", 0},
+#line 2575 "effective_tld_names.gperf"
+      {"ph", 0},
+#line 2068 "effective_tld_names.gperf"
+      {"n.bg", 0},
+#line 3624 "effective_tld_names.gperf"
+      {"xn--snase-nra.no", 0},
+#line 1171 "effective_tld_names.gperf"
+      {"go.th", 0},
+#line 2626 "effective_tld_names.gperf"
+      {"poznan.pl", 0},
+#line 336 "effective_tld_names.gperf"
+      {"bieszczady.pl", 0},
+#line 2995 "effective_tld_names.gperf"
+      {"sm", 0},
+#line 2014 "effective_tld_names.gperf"
+      {"modern.museum", 0},
+#line 1439 "effective_tld_names.gperf"
+      {"horten.no", 0},
+#line 3613 "effective_tld_names.gperf"
+      {"xn--seral-lra.no", 0},
+#line 2062 "effective_tld_names.gperf"
+      {"mw", 0},
+#line 1638 "effective_tld_names.gperf"
+      {"karpacz.pl", 0},
+#line 2528 "effective_tld_names.gperf"
+      {"p.se", 0},
+#line 998 "effective_tld_names.gperf"
+      {"farsund.no", 0},
+#line 1427 "effective_tld_names.gperf"
+      {"hobol.no", 0},
+#line 492 "effective_tld_names.gperf"
+      {"children.museum", 0},
+#line 1966 "effective_tld_names.gperf"
+      {"mil.mv", 0},
+#line 2138 "effective_tld_names.gperf"
+      {"nesoddtangen.no", 0},
 #line 2932 "effective_tld_names.gperf"
-      {"skiptvet.no", 0},
-#line 273 "effective_tld_names.gperf"
-      {"bale.museum", 0},
-#line 1928 "effective_tld_names.gperf"
-      {"mil.iq", 0},
-#line 3150 "effective_tld_names.gperf"
-      {"tourism.tn", 0},
-#line 3496 "effective_tld_names.gperf"
-      {"xn--lesund-hua.no", 0},
-#line 245 "effective_tld_names.gperf"
-      {"auto.pl", 0},
-#line 1474 "effective_tld_names.gperf"
-      {"indianapolis.museum", 0},
-#line 2738 "effective_tld_names.gperf"
-      {"rennesoy.no", 0},
-#line 688 "effective_tld_names.gperf"
-      {"communication.museum", 0},
-#line 3072 "effective_tld_names.gperf"
-      {"szex.hu", 0},
-#line 3319 "effective_tld_names.gperf"
-      {"vgs.no", 0},
-#line 2824 "effective_tld_names.gperf"
-      {"sandiego.museum", 0},
-#line 3562 "effective_tld_names.gperf"
-      {"xn--smla-hra.no", 0},
-#line 3229 "effective_tld_names.gperf"
-      {"ullensvang.no", 0},
-#line 1898 "effective_tld_names.gperf"
-      {"metro.tokyo.jp", 1},
-#line 2751 "effective_tld_names.gperf"
-      {"rimini.it", 0},
-#line 3160 "effective_tld_names.gperf"
-      {"trainer.aero", 0},
-#line 3545 "effective_tld_names.gperf"
-      {"xn--rskog-uua.no", 0},
-#line 2770 "effective_tld_names.gperf"
-      {"rockart.museum", 0},
-#line 1804 "effective_tld_names.gperf"
-      {"lund.no", 0},
-#line 1905 "effective_tld_names.gperf"
-      {"michigan.museum", 0},
-#line 3202 "effective_tld_names.gperf"
-      {"tw", 0},
-#line 3051 "effective_tld_names.gperf"
-      {"surnadal.no", 0},
-#line 3346 "effective_tld_names.gperf"
-      {"voagat.no", 0},
-#line 2523 "effective_tld_names.gperf"
-      {"perso.tn", 0},
-#line 255 "effective_tld_names.gperf"
-      {"axis.museum", 0},
-#line 689 "effective_tld_names.gperf"
-      {"communications.museum", 0},
-#line 3394 "effective_tld_names.gperf"
-      {"wodzislaw.pl", 0},
-#line 807 "effective_tld_names.gperf"
-      {"ebiz.tw", 0},
-#line 195 "effective_tld_names.gperf"
-      {"arts.ro", 0},
-#line 683 "effective_tld_names.gperf"
-      {"com.uz", 0},
-#line 2838 "effective_tld_names.gperf"
-      {"sassari.it", 0},
-#line 2778 "effective_tld_names.gperf"
-      {"roros.no", 0},
-#line 2651 "effective_tld_names.gperf"
-      {"priv.at", 0},
-#line 3557 "effective_tld_names.gperf"
-      {"xn--skjk-soa.no", 0},
-#line 3301 "effective_tld_names.gperf"
-      {"vennesla.no", 0},
-#line 333 "effective_tld_names.gperf"
-      {"bill.museum", 0},
-#line 469 "effective_tld_names.gperf"
-      {"chambagri.fr", 0},
-#line 2741 "effective_tld_names.gperf"
-      {"res.in", 0},
-#line 2645 "effective_tld_names.gperf"
-      {"press.se", 0},
-#line 1977 "effective_tld_names.gperf"
-      {"mobi.na", 0},
-#line 318 "effective_tld_names.gperf"
-      {"beskidy.pl", 0},
-#line 3373 "effective_tld_names.gperf"
-      {"washingtondc.museum", 0},
-#line 3047 "effective_tld_names.gperf"
-      {"sund.no", 0},
-#line 3185 "effective_tld_names.gperf"
-      {"tsaritsyn.ru", 0},
-#line 3422 "effective_tld_names.gperf"
-      {"xn--bhcavuotna-s4a.no", 0},
-#line 796 "effective_tld_names.gperf"
-      {"drobak.no", 0},
-#line 2956 "effective_tld_names.gperf"
-      {"so.gov.pl", 0},
-#line 1612 "effective_tld_names.gperf"
-      {"katowice.pl", 0},
-#line 1077 "effective_tld_names.gperf"
-      {"fukui.jp", 2},
-#line 1997 "effective_tld_names.gperf"
-      {"moss.no", 0},
-#line 2297 "effective_tld_names.gperf"
-      {"nysa.pl", 0},
-#line 3287 "effective_tld_names.gperf"
-      {"varese.it", 0},
-#line 1014 "effective_tld_names.gperf"
-      {"firm.ht", 0},
-#line 2525 "effective_tld_names.gperf"
-      {"pesaro-urbino.it", 0},
-#line 693 "effective_tld_names.gperf"
-      {"computerhistory.museum", 0},
-#line 1897 "effective_tld_names.gperf"
-      {"messina.it", 0},
-#line 2994 "effective_tld_names.gperf"
-      {"sr.gov.pl", 0},
-#line 2475 "effective_tld_names.gperf"
-      {"ostrowwlkp.pl", 0},
-#line 2793 "effective_tld_names.gperf"
-      {"rw", 0},
-#line 1652 "effective_tld_names.gperf"
-      {"koenig.ru", 0},
-#line 1086 "effective_tld_names.gperf"
+      {"sec.ps", 0},
+#line 493 "effective_tld_names.gperf"
+      {"childrens.museum", 0},
+#line 1722 "effective_tld_names.gperf"
+      {"kvam.no", 0},
+#line 1885 "effective_tld_names.gperf"
+      {"matera.it", 0},
+#line 1336 "effective_tld_names.gperf"
+      {"gs.ol.no", 0},
+#line 1979 "effective_tld_names.gperf"
+      {"mil.vc", 0},
+#line 218 "effective_tld_names.gperf"
+      {"asso.bj", 0},
+#line 1103 "effective_tld_names.gperf"
       {"fyresdal.no", 0},
-#line 128 "effective_tld_names.gperf"
+#line 2847 "effective_tld_names.gperf"
+      {"sa", 0},
+#line 396 "effective_tld_names.gperf"
+      {"bremanger.no", 0},
+#line 395 "effective_tld_names.gperf"
+      {"brasil.museum", 0},
+#line 3630 "effective_tld_names.gperf"
+      {"xn--sr-odal-q1a.no", 0},
+#line 2564 "effective_tld_names.gperf"
+      {"perm.ru", 0},
+#line 1043 "effective_tld_names.gperf"
+      {"flakstad.no", 0},
+#line 1332 "effective_tld_names.gperf"
+      {"gs.mr.no", 0},
+#line 95 "effective_tld_names.gperf"
+      {"agrinet.tn", 0},
+#line 1488 "effective_tld_names.gperf"
+      {"in.th", 0},
+#line 1553 "effective_tld_names.gperf"
+      {"iris.arpa", 0},
+#line 3302 "effective_tld_names.gperf"
+      {"uscountryestate.museum", 0},
+#line 2007 "effective_tld_names.gperf"
+      {"mobi.gp", 0},
+#line 1242 "effective_tld_names.gperf"
+      {"gov.je", 0},
+#line 1851 "effective_tld_names.gperf"
+      {"madrid.museum", 0},
+#line 2853 "effective_tld_names.gperf"
+      {"sa.it", 0},
+#line 1159 "effective_tld_names.gperf"
+      {"glogow.pl", 0},
+#line 282 "effective_tld_names.gperf"
+      {"balsan.it", 0},
+#line 3424 "effective_tld_names.gperf"
+      {"wallonie.museum", 0},
+#line 1089 "effective_tld_names.gperf"
+      {"frosinone.it", 0},
+#line 1338 "effective_tld_names.gperf"
+      {"gs.rl.no", 0},
+#line 535 "effective_tld_names.gperf"
+      {"clinton.museum", 0},
+#line 1586 "effective_tld_names.gperf"
+      {"jessheim.no", 0},
+#line 508 "effective_tld_names.gperf"
+      {"cinema.museum", 0},
+#line 3571 "effective_tld_names.gperf"
+      {"xn--mli-tla.no", 0},
+#line 3049 "effective_tld_names.gperf"
+      {"st.no", 0},
+#line 2855 "effective_tld_names.gperf"
+      {"saga.jp", 2},
+#line 3554 "effective_tld_names.gperf"
+      {"xn--linds-pra.no", 0},
+#line 123 "effective_tld_names.gperf"
+      {"alessandria.it", 0},
+#line 2140 "effective_tld_names.gperf"
+      {"nesset.no", 0},
+#line 2013 "effective_tld_names.gperf"
+      {"modena.it", 0},
+#line 1326 "effective_tld_names.gperf"
+      {"gs.bu.no", 0},
+#line 2292 "effective_tld_names.gperf"
+      {"norddal.no", 0},
+#line 1933 "effective_tld_names.gperf"
+      {"miasta.pl", 0},
+#line 1878 "effective_tld_names.gperf"
+      {"maryland.museum", 0},
+#line 77 "effective_tld_names.gperf"
+      {"aejrie.no", 0},
+#line 3305 "effective_tld_names.gperf"
+      {"usenet.pl", 0},
+#line 2589 "effective_tld_names.gperf"
+      {"pisa.it", 0},
+#line 1452 "effective_tld_names.gperf"
+      {"hurdal.no", 0},
+#line 338 "effective_tld_names.gperf"
+      {"bilbao.museum", 0},
+#line 2586 "effective_tld_names.gperf"
+      {"pila.pl", 0},
+#line 1862 "effective_tld_names.gperf"
+      {"malvik.no", 0},
+#line 2179 "effective_tld_names.gperf"
+      {"net.je", 0},
+#line 2561 "effective_tld_names.gperf"
+      {"per.la", 0},
+#line 3601 "effective_tld_names.gperf"
+      {"xn--rland-uua.no", 0},
+#line 3664 "effective_tld_names.gperf"
+      {"xn--yer-zna.no", 0},
+#line 3628 "effective_tld_names.gperf"
+      {"xn--sr-aurdal-l8a.no", 0},
+#line 1925 "effective_tld_names.gperf"
+      {"mesaverde.museum", 0},
+#line 2848 "effective_tld_names.gperf"
+      {"sa.au", 0},
+#line 3115 "effective_tld_names.gperf"
+      {"sy", 0},
+#line 3007 "effective_tld_names.gperf"
+      {"soc.lk", 0},
+#line 1324 "effective_tld_names.gperf"
+      {"gs.aa.no", 0},
+#line 360 "effective_tld_names.gperf"
+      {"bj", 0},
+#line 1937 "effective_tld_names.gperf"
+      {"midsund.no", 0},
+#line 1981 "effective_tld_names.gperf"
+      {"milano.it", 0},
+#line 1622 "effective_tld_names.gperf"
+      {"k12.vi", 0},
+#line 263 "effective_tld_names.gperf"
+      {"b.bg", 0},
+#line 3575 "effective_tld_names.gperf"
+      {"xn--mot-tla.no", 0},
+#line 3013 "effective_tld_names.gperf"
+      {"sola.no", 0},
+#line 1434 "effective_tld_names.gperf"
+      {"holtalen.no", 0},
+#line 2994 "effective_tld_names.gperf"
+      {"slupsk.pl", 0},
+#line 3568 "effective_tld_names.gperf"
+      {"xn--mjndalen-64a.no", 0},
+#line 2262 "effective_tld_names.gperf"
+      {"nittedal.no", 0},
+#line 1374 "effective_tld_names.gperf"
+      {"hamar.no", 0},
+#line 1665 "effective_tld_names.gperf"
+      {"kirkenes.no", 0},
+#line 382 "effective_tld_names.gperf"
+      {"bomlo.no", 0},
+#line 2731 "effective_tld_names.gperf"
+      {"pv.it", 0},
+#line 3675 "effective_tld_names.gperf"
+      {"yamal.ru", 0},
+#line 2706 "effective_tld_names.gperf"
+      {"pro.mv", 0},
+#line 1599 "effective_tld_names.gperf"
+      {"jogasz.hu", 0},
+#line 1469 "effective_tld_names.gperf"
+      {"idrett.no", 0},
+#line 1574 "effective_tld_names.gperf"
+      {"jamal.ru", 0},
+#line 2958 "effective_tld_names.gperf"
+      {"si", 0},
+#line 1477 "effective_tld_names.gperf"
+      {"ilawa.pl", 0},
+#line 1376 "effective_tld_names.gperf"
+      {"hamburg.museum", 0},
+#line 2710 "effective_tld_names.gperf"
+      {"pro.vn", 0},
+#line 3696 "effective_tld_names.gperf"
+      {"zagan.pl", 0},
+#line 2714 "effective_tld_names.gperf"
+      {"project.museum", 0},
+#line 2715 "effective_tld_names.gperf"
+      {"promocion.ar", 1},
+#line 1109 "effective_tld_names.gperf"
+      {"gaivuotna.no", 0},
+#line 1334 "effective_tld_names.gperf"
+      {"gs.nt.no", 0},
+#line 384 "effective_tld_names.gperf"
+      {"boston.museum", 0},
+#line 1333 "effective_tld_names.gperf"
+      {"gs.nl.no", 0},
+#line 2743 "effective_tld_names.gperf"
+      {"qld.edu.au", 0},
+#line 267 "effective_tld_names.gperf"
+      {"babia-gora.pl", 0},
+#line 2959 "effective_tld_names.gperf"
+      {"si.it", 0},
+#line 176 "effective_tld_names.gperf"
+      {"ardal.no", 0},
+#line 729 "effective_tld_names.gperf"
+      {"county.museum", 0},
+#line 531 "effective_tld_names.gperf"
+      {"ck", 2},
+#line 3599 "effective_tld_names.gperf"
+      {"xn--risa-5na.no", 0},
+#line 1874 "effective_tld_names.gperf"
+      {"maritimo.museum", 0},
+#line 2080 "effective_tld_names.gperf"
+      {"namdalseid.no", 0},
+#line 558 "effective_tld_names.gperf"
+      {"co.gy", 0},
+#line 868 "effective_tld_names.gperf"
+      {"edu.kn", 0},
+#line 1849 "effective_tld_names.gperf"
+      {"macerata.it", 0},
+#line 483 "effective_tld_names.gperf"
+      {"cheltenham.museum", 0},
+#line 1419 "effective_tld_names.gperf"
+      {"hk", 0},
+#line 367 "effective_tld_names.gperf"
+      {"blog.br", 0},
+#line 1987 "effective_tld_names.gperf"
+      {"minnesota.museum", 0},
+#line 3300 "effective_tld_names.gperf"
+      {"usantiques.museum", 0},
+#line 2629 "effective_tld_names.gperf"
+      {"pp.se", 0},
+#line 3047 "effective_tld_names.gperf"
+      {"sshn.se", 0},
+#line 1315 "effective_tld_names.gperf"
+      {"grimstad.no", 0},
+#line 1247 "effective_tld_names.gperf"
+      {"gov.kn", 0},
+#line 3708 "effective_tld_names.gperf"
+      {"zoology.museum", 0},
+#line 3000 "effective_tld_names.gperf"
+      {"snaase.no", 0},
+#line 458 "effective_tld_names.gperf"
+      {"castle.museum", 0},
+#line 1873 "effective_tld_names.gperf"
+      {"maritime.museum", 0},
+#line 3093 "effective_tld_names.gperf"
+      {"sula.no", 0},
+#line 1571 "effective_tld_names.gperf"
+      {"iz.hr", 0},
+#line 1398 "effective_tld_names.gperf"
+      {"hemne.no", 0},
+#line 472 "effective_tld_names.gperf"
+      {"center.museum", 0},
+#line 2746 "effective_tld_names.gperf"
+      {"quebec.museum", 0},
+#line 235 "effective_tld_names.gperf"
+      {"atlanta.museum", 0},
+#line 1003 "effective_tld_names.gperf"
+      {"federation.aero", 0},
+#line 1192 "effective_tld_names.gperf"
+      {"gouv.bj", 0},
+#line 245 "effective_tld_names.gperf"
+      {"austevoll.no", 0},
+#line 3555 "effective_tld_names.gperf"
+      {"xn--lns-qla.museum", 0},
+#line 2238 "effective_tld_names.gperf"
+      {"newjersey.museum", 0},
+#line 1443 "effective_tld_names.gperf"
+      {"hoyanger.no", 0},
+#line 459 "effective_tld_names.gperf"
+      {"castres.museum", 0},
+#line 3003 "effective_tld_names.gperf"
+      {"snoasa.no", 0},
+#line 2027 "effective_tld_names.gperf"
+      {"mosreg.ru", 0},
+#line 1904 "effective_tld_names.gperf"
+      {"med.ly", 0},
+#line 2294 "effective_tld_names.gperf"
+      {"nordre-land.no", 0},
+#line 1926 "effective_tld_names.gperf"
+      {"messina.it", 0},
+#line 130 "effective_tld_names.gperf"
       {"altoadige.it", 0},
-#line 2526 "effective_tld_names.gperf"
-      {"pesarourbino.it", 0},
-#line 2648 "effective_tld_names.gperf"
-      {"presse.km", 0},
-#line 1074 "effective_tld_names.gperf"
-      {"froya.no", 0},
-#line 2728 "effective_tld_names.gperf"
-      {"recreation.aero", 0},
-#line 3061 "effective_tld_names.gperf"
-      {"swidnica.pl", 0},
-#line 3081 "effective_tld_names.gperf"
-      {"taranto.it", 0},
-#line 3309 "effective_tld_names.gperf"
-      {"vestnes.no", 0},
-#line 1589 "effective_tld_names.gperf"
-      {"k-uralsk.ru", 0},
-#line 2506 "effective_tld_names.gperf"
-      {"pasadena.museum", 0},
-#line 3186 "effective_tld_names.gperf"
-      {"tsk.ru", 0},
-#line 2981 "effective_tld_names.gperf"
-      {"sosnowiec.pl", 0},
-#line 3011 "effective_tld_names.gperf"
-      {"stateofdelaware.museum", 0},
-#line 3259 "effective_tld_names.gperf"
-      {"utah.museum", 0},
-#line 3505 "effective_tld_names.gperf"
-      {"xn--lten-gra.no", 0},
-#line 3455 "effective_tld_names.gperf"
-      {"xn--gls-elac.no", 0},
+#line 398 "effective_tld_names.gperf"
+      {"brindisi.it", 0},
+#line 1136 "effective_tld_names.gperf"
+      {"geology.museum", 0},
+#line 503 "effective_tld_names.gperf"
+      {"chuvashia.ru", 0},
+#line 1020 "effective_tld_names.gperf"
+      {"filatelia.museum", 0},
+#line 1036 "effective_tld_names.gperf"
+      {"fj", 2},
+#line 1025 "effective_tld_names.gperf"
+      {"fineart.museum", 0},
+#line 3077 "effective_tld_names.gperf"
+      {"stordal.no", 0},
+#line 988 "effective_tld_names.gperf"
+      {"f.bg", 0},
+#line 1026 "effective_tld_names.gperf"
+      {"finearts.museum", 0},
+#line 640 "effective_tld_names.gperf"
+      {"com.jo", 0},
+#line 348 "effective_tld_names.gperf"
+      {"biz.az", 0},
+#line 864 "effective_tld_names.gperf"
+      {"edu.jo", 0},
+#line 2608 "effective_tld_names.gperf"
+      {"podlasie.pl", 0},
 #line 388 "effective_tld_names.gperf"
-      {"brescia.it", 0},
-#line 3103 "effective_tld_names.gperf"
-      {"tf", 0},
-#line 1694 "effective_tld_names.gperf"
-      {"kvanangen.no", 0},
-#line 1680 "effective_tld_names.gperf"
-      {"kuban.ru", 0},
-#line 3388 "effective_tld_names.gperf"
-      {"wiki.br", 0},
-#line 3591 "effective_tld_names.gperf"
-      {"xn--vard-jra.no", 0},
-#line 777 "effective_tld_names.gperf"
-      {"divtasvuodna.no", 0},
-#line 3590 "effective_tld_names.gperf"
-      {"xn--vads-jra.no", 0},
-#line 1204 "effective_tld_names.gperf"
-      {"gov.dz", 0},
-#line 834 "effective_tld_names.gperf"
-      {"edu.dz", 0},
-#line 2055 "effective_tld_names.gperf"
-      {"name.pr", 0},
-#line 2128 "effective_tld_names.gperf"
-      {"net.dz", 0},
-#line 2473 "effective_tld_names.gperf"
-      {"ostroleka.pl", 0},
-#line 215 "effective_tld_names.gperf"
-      {"asso.ci", 0},
-#line 784 "effective_tld_names.gperf"
-      {"dnepropetrovsk.ua", 0},
-#line 612 "effective_tld_names.gperf"
-      {"com.dz", 0},
-#line 2903 "effective_tld_names.gperf"
-      {"shiga.jp", 2},
-#line 3513 "effective_tld_names.gperf"
+      {"botany.museum", 0},
+#line 2183 "effective_tld_names.gperf"
+      {"net.kn", 0},
+#line 1149 "effective_tld_names.gperf"
+      {"gjemnes.no", 0},
+#line 2694 "effective_tld_names.gperf"
+      {"pri.ee", 0},
+#line 559 "effective_tld_names.gperf"
+      {"co.hu", 0},
+#line 2598 "effective_tld_names.gperf"
+      {"plants.museum", 0},
+#line 1985 "effective_tld_names.gperf"
+      {"miners.museum", 0},
+#line 2327 "effective_tld_names.gperf"
+      {"nuernberg.museum", 0},
+#line 3638 "effective_tld_names.gperf"
+      {"xn--tjme-hra.no", 0},
+#line 337 "effective_tld_names.gperf"
+      {"bievat.no", 0},
+#line 3287 "effective_tld_names.gperf"
+      {"undersea.museum", 0},
+#line 128 "effective_tld_names.gperf"
+      {"altai.ru", 0},
+#line 160 "effective_tld_names.gperf"
+      {"aomori.jp", 2},
+#line 1243 "effective_tld_names.gperf"
+      {"gov.jo", 0},
+#line 3600 "effective_tld_names.gperf"
+      {"xn--risr-ira.no", 0},
+#line 129 "effective_tld_names.gperf"
+      {"alto-adige.it", 0},
+#line 1310 "effective_tld_names.gperf"
+      {"grane.no", 0},
+#line 2001 "effective_tld_names.gperf"
+      {"mo-i-rana.no", 0},
+#line 1974 "effective_tld_names.gperf"
+      {"mil.st", 0},
+#line 1651 "effective_tld_names.gperf"
+      {"kg", 0},
+#line 713 "effective_tld_names.gperf"
+      {"control.aero", 0},
+#line 1633 "effective_tld_names.gperf"
+      {"karate.museum", 0},
+#line 2947 "effective_tld_names.gperf"
+      {"sh", 0},
+#line 1887 "effective_tld_names.gperf"
+      {"mazowsze.pl", 0},
+#line 2992 "effective_tld_names.gperf"
+      {"sld.pa", 0},
+#line 1517 "effective_tld_names.gperf"
+      {"info.pk", 0},
+#line 89 "effective_tld_names.gperf"
+      {"agdenes.no", 0},
+#line 1645 "effective_tld_names.gperf"
+      {"kazimierz-dolny.pl", 0},
+#line 72 "effective_tld_names.gperf"
+      {"adult.ht", 0},
+#line 2846 "effective_tld_names.gperf"
+      {"s.se", 0},
+#line 462 "effective_tld_names.gperf"
+      {"catanzaro.it", 0},
+#line 1401 "effective_tld_names.gperf"
+      {"herad.no", 0},
+#line 254 "effective_tld_names.gperf"
+      {"averoy.no", 0},
+#line 1049 "effective_tld_names.gperf"
+      {"flog.br", 0},
+#line 479 "effective_tld_names.gperf"
+      {"championship.aero", 0},
+#line 2538 "effective_tld_names.gperf"
+      {"palana.ru", 0},
+#line 315 "effective_tld_names.gperf"
+      {"benevento.it", 0},
+#line 251 "effective_tld_names.gperf"
+      {"automotive.museum", 0},
+#line 2956 "effective_tld_names.gperf"
+      {"shop.pl", 0},
+#line 246 "effective_tld_names.gperf"
+      {"austin.museum", 0},
+#line 340 "effective_tld_names.gperf"
+      {"bindal.no", 0},
+#line 2010 "effective_tld_names.gperf"
+      {"mod.gi", 0},
+#line 1982 "effective_tld_names.gperf"
+      {"military.museum", 0},
+#line 1080 "effective_tld_names.gperf"
+      {"freemasonry.museum", 0},
+#line 1150 "effective_tld_names.gperf"
+      {"gjerdrum.no", 0},
+#line 2180 "effective_tld_names.gperf"
+      {"net.jo", 0},
+#line 2728 "effective_tld_names.gperf"
+      {"public.museum", 0},
+#line 379 "effective_tld_names.gperf"
+      {"bologna.it", 0},
+#line 1328 "effective_tld_names.gperf"
+      {"gs.fm.no", 0},
+#line 2937 "effective_tld_names.gperf"
+      {"seljord.no", 0},
+#line 2289 "effective_tld_names.gperf"
+      {"nord-aurdal.no", 0},
+#line 2328 "effective_tld_names.gperf"
+      {"nuoro.it", 0},
+#line 936 "effective_tld_names.gperf"
+      {"eisenbahn.museum", 0},
+#line 456 "effective_tld_names.gperf"
+      {"caserta.it", 0},
+#line 3010 "effective_tld_names.gperf"
+      {"sogndal.no", 0},
+#line 3633 "effective_tld_names.gperf"
+      {"xn--srreisa-q1a.no", 0},
+#line 1344 "effective_tld_names.gperf"
+      {"gs.va.no", 0},
+#line 1148 "effective_tld_names.gperf"
+      {"giske.no", 0},
+#line 1053 "effective_tld_names.gperf"
+      {"floro.no", 0},
+#line 446 "effective_tld_names.gperf"
+      {"cambridge.museum", 0},
+#line 2866 "effective_tld_names.gperf"
+      {"samara.ru", 0},
+#line 255 "effective_tld_names.gperf"
+      {"aviation.museum", 0},
+#line 1417 "effective_tld_names.gperf"
+      {"hjartdal.no", 0},
+#line 2136 "effective_tld_names.gperf"
+      {"nesna.no", 0},
+#line 1857 "effective_tld_names.gperf"
+      {"malatvuopmi.no", 0},
+#line 143 "effective_tld_names.gperf"
+      {"amsterdam.museum", 0},
+#line 2291 "effective_tld_names.gperf"
+      {"nord-odal.no", 0},
+#line 3467 "effective_tld_names.gperf"
+      {"xn--andy-ira.no", 0},
+#line 280 "effective_tld_names.gperf"
+      {"ballangen.no", 0},
+#line 724 "effective_tld_names.gperf"
+      {"corvette.museum", 0},
+#line 1728 "effective_tld_names.gperf"
+      {"kw", 2},
+#line 3084 "effective_tld_names.gperf"
+      {"strand.no", 0},
+#line 2693 "effective_tld_names.gperf"
+      {"presse.ml", 0},
+#line 1117 "effective_tld_names.gperf"
+      {"garden.museum", 0},
+#line 217 "effective_tld_names.gperf"
+      {"assn.lk", 0},
+#line 393 "effective_tld_names.gperf"
+      {"brand.se", 0},
+#line 1388 "effective_tld_names.gperf"
+      {"haugesund.no", 0},
+#line 995 "effective_tld_names.gperf"
+      {"farmequipment.museum", 0},
+#line 1845 "effective_tld_names.gperf"
+      {"m.bg", 0},
+#line 1138 "effective_tld_names.gperf"
+      {"georgia.museum", 0},
+#line 1325 "effective_tld_names.gperf"
+      {"gs.ah.no", 0},
+#line 973 "effective_tld_names.gperf"
+      {"ethnology.museum", 0},
+#line 2699 "effective_tld_names.gperf"
+      {"priv.no", 0},
+#line 190 "effective_tld_names.gperf"
+      {"artanddesign.museum", 0},
+#line 3415 "effective_tld_names.gperf"
+      {"w.bg", 0},
+#line 747 "effective_tld_names.gperf"
+      {"culture.museum", 0},
+#line 3064 "effective_tld_names.gperf"
+      {"stavern.no", 0},
+#line 1652 "effective_tld_names.gperf"
+      {"kg.kr", 0},
+#line 1920 "effective_tld_names.gperf"
+      {"meldal.no", 0},
+#line 1903 "effective_tld_names.gperf"
+      {"med.ht", 0},
+#line 491 "effective_tld_names.gperf"
+      {"chieti.it", 0},
+#line 3265 "effective_tld_names.gperf"
+      {"u.bg", 0},
+#line 1389 "effective_tld_names.gperf"
+      {"hawaii.museum", 0},
+#line 2118 "effective_tld_names.gperf"
+      {"navigation.aero", 0},
+#line 2700 "effective_tld_names.gperf"
+      {"priv.pl", 0},
+#line 3562 "effective_tld_names.gperf"
+      {"xn--mely-ira.no", 0},
+#line 642 "effective_tld_names.gperf"
+      {"com.ki", 0},
+#line 538 "effective_tld_names.gperf"
+      {"club.tw", 0},
+#line 866 "effective_tld_names.gperf"
+      {"edu.ki", 0},
+#line 2295 "effective_tld_names.gperf"
+      {"nordreisa.no", 0},
+#line 2930 "effective_tld_names.gperf"
+      {"seaport.museum", 0},
+#line 1636 "effective_tld_names.gperf"
+      {"karlsoy.no", 0},
+#line 139 "effective_tld_names.gperf"
+      {"americanantiques.museum", 0},
+#line 2709 "effective_tld_names.gperf"
+      {"pro.tt", 0},
+#line 3592 "effective_tld_names.gperf"
+      {"xn--rady-ira.no", 0},
+#line 1091 "effective_tld_names.gperf"
+      {"froya.no", 0},
+#line 3104 "effective_tld_names.gperf"
+      {"sv", 2},
+#line 3551 "effective_tld_names.gperf"
+      {"xn--lesund-hua.no", 0},
+#line 1245 "effective_tld_names.gperf"
+      {"gov.ki", 0},
+#line 3023 "effective_tld_names.gperf"
+      {"sor-odal.no", 0},
+#line 1038 "effective_tld_names.gperf"
+      {"fjaler.no", 0},
+#line 1589 "effective_tld_names.gperf"
+      {"jewelry.museum", 0},
+#line 2549 "effective_tld_names.gperf"
+      {"pasadena.museum", 0},
+#line 487 "effective_tld_names.gperf"
+      {"chernovtsy.ua", 0},
+#line 1562 "effective_tld_names.gperf"
+      {"isleofman.museum", 0},
+#line 991 "effective_tld_names.gperf"
+      {"family.museum", 0},
+#line 3105 "effective_tld_names.gperf"
+      {"sv.it", 0},
+#line 1864 "effective_tld_names.gperf"
+      {"mandal.no", 0},
+#line 1402 "effective_tld_names.gperf"
+      {"heritage.museum", 0},
+#line 1416 "effective_tld_names.gperf"
+      {"hitra.no", 0},
+#line 3641 "effective_tld_names.gperf"
+      {"xn--trany-yua.no", 0},
+#line 316 "effective_tld_names.gperf"
+      {"berg.no", 0},
+#line 153 "effective_tld_names.gperf"
+      {"andoy.no", 0},
+#line 1658 "effective_tld_names.gperf"
+      {"kherson.ua", 0},
+#line 934 "effective_tld_names.gperf"
+      {"eidsvoll.no", 0},
+#line 1418 "effective_tld_names.gperf"
+      {"hjelmeland.no", 0},
+#line 2117 "effective_tld_names.gperf"
+      {"naval.museum", 0},
+#line 249 "effective_tld_names.gperf"
+      {"author.aero", 0},
+#line 430 "effective_tld_names.gperf"
+      {"bytom.pl", 0},
+#line 1040 "effective_tld_names.gperf"
+      {"fk", 2},
+#line 963 "effective_tld_names.gperf"
+      {"erotica.hu", 0},
+#line 1414 "effective_tld_names.gperf"
+      {"history.museum", 0},
+#line 1954 "effective_tld_names.gperf"
+      {"mil.ge", 0},
+#line 2182 "effective_tld_names.gperf"
+      {"net.ki", 0},
+#line 1050 "effective_tld_names.gperf"
+      {"flora.no", 0},
+#line 3118 "effective_tld_names.gperf"
+      {"syzran.ru", 0},
+#line 177 "effective_tld_names.gperf"
+      {"aremark.no", 0},
+#line 471 "effective_tld_names.gperf"
+      {"celtic.museum", 0},
+#line 1988 "effective_tld_names.gperf"
+      {"missile.museum", 0},
+#line 3612 "effective_tld_names.gperf"
+      {"xn--sandy-yua.no", 0},
+#line 758 "effective_tld_names.gperf"
+      {"czest.pl", 0},
+#line 312 "effective_tld_names.gperf"
+      {"belgorod.ru", 0},
+#line 527 "effective_tld_names.gperf"
+      {"civilaviation.aero", 0},
+#line 745 "effective_tld_names.gperf"
+      {"cultural.museum", 0},
+#line 3085 "effective_tld_names.gperf"
+      {"stranda.no", 0},
+#line 1975 "effective_tld_names.gperf"
+      {"mil.sy", 0},
+#line 2573 "effective_tld_names.gperf"
+      {"pg", 2},
+#line 277 "effective_tld_names.gperf"
+      {"balat.no", 0},
+#line 1382 "effective_tld_names.gperf"
+      {"haram.no", 0},
+#line 632 "effective_tld_names.gperf"
+      {"com.gy", 0},
+#line 2588 "effective_tld_names.gperf"
+      {"pilots.museum", 0},
+#line 523 "effective_tld_names.gperf"
+      {"city.sapporo.jp", 1},
+#line 1229 "effective_tld_names.gperf"
+      {"gov.gg", 0},
+#line 1340 "effective_tld_names.gperf"
+      {"gs.st.no", 0},
+#line 1671 "effective_tld_names.gperf"
+      {"klodzko.pl", 0},
+#line 3576 "effective_tld_names.gperf"
+      {"xn--msy-ula0h.no", 0},
+#line 2934 "effective_tld_names.gperf"
+      {"sel.no", 0},
+#line 2574 "effective_tld_names.gperf"
+      {"pg.it", 0},
+#line 2562 "effective_tld_names.gperf"
+      {"per.nf", 0},
+#line 2957 "effective_tld_names.gperf"
+      {"show.aero", 0},
+#line 505 "effective_tld_names.gperf"
+      {"cieszyn.pl", 0},
+#line 2046 "effective_tld_names.gperf"
+      {"muenster.museum", 0},
+#line 725 "effective_tld_names.gperf"
+      {"cosenza.it", 0},
+#line 1134 "effective_tld_names.gperf"
+      {"genoa.it", 0},
+#line 3025 "effective_tld_names.gperf"
+      {"sorfold.no", 0},
+#line 1408 "effective_tld_names.gperf"
+      {"histoire.museum", 0},
+#line 212 "effective_tld_names.gperf"
+      {"asnes.no", 0},
+#line 1861 "effective_tld_names.gperf"
+      {"malselv.no", 0},
+#line 2290 "effective_tld_names.gperf"
+      {"nord-fron.no", 0},
+#line 2074 "effective_tld_names.gperf"
+      {"nagano.jp", 2},
+#line 399 "effective_tld_names.gperf"
+      {"bristol.museum", 0},
+#line 3438 "effective_tld_names.gperf"
+      {"western.museum", 0},
+#line 274 "effective_tld_names.gperf"
+      {"baidar.no", 0},
+#line 3307 "effective_tld_names.gperf"
+      {"ushistory.museum", 0},
+#line 2035 "effective_tld_names.gperf"
+      {"mragowo.pl", 0},
+#line 2696 "effective_tld_names.gperf"
+      {"priv.at", 0},
+#line 1656 "effective_tld_names.gperf"
+      {"khakassia.ru", 0},
+#line 421 "effective_tld_names.gperf"
+      {"buryatia.ru", 0},
+#line 463 "effective_tld_names.gperf"
+      {"catering.aero", 0},
+#line 319 "effective_tld_names.gperf"
+      {"bergen.no", 0},
+#line 2166 "effective_tld_names.gperf"
+      {"net.gg", 0},
+#line 3041 "effective_tld_names.gperf"
+      {"square.museum", 0},
+#line 1612 "effective_tld_names.gperf"
+      {"judygarland.museum", 0},
+#line 1028 "effective_tld_names.gperf"
+      {"finnoy.no", 0},
+#line 2542 "effective_tld_names.gperf"
+      {"panama.museum", 0},
+#line 1965 "effective_tld_names.gperf"
+      {"mil.mg", 0},
+#line 2891 "effective_tld_names.gperf"
+      {"savona.it", 0},
+#line 2733 "effective_tld_names.gperf"
+      {"pw", 0},
+#line 1883 "effective_tld_names.gperf"
+      {"massacarrara.it", 0},
+#line 2170 "effective_tld_names.gperf"
+      {"net.gy", 0},
+#line 3098 "effective_tld_names.gperf"
+      {"sunndal.no", 0},
+#line 1379 "effective_tld_names.gperf"
+      {"handson.museum", 0},
+#line 1004 "effective_tld_names.gperf"
+      {"fedje.no", 0},
+#line 2076 "effective_tld_names.gperf"
+      {"nagoya.jp", 2},
+#line 1993 "effective_tld_names.gperf"
+      {"mk", 0},
+#line 1967 "effective_tld_names.gperf"
+      {"mil.my", 0},
+#line 3310 "effective_tld_names.gperf"
+      {"ustka.pl", 0},
+#line 2049 "effective_tld_names.gperf"
+      {"muosat.no", 0},
+#line 3276 "effective_tld_names.gperf"
+      {"uk", 2},
+#line 2302 "effective_tld_names.gperf"
+      {"notaires.km", 0},
+#line 279 "effective_tld_names.gperf"
+      {"balestrand.no", 0},
+#line 3618 "effective_tld_names.gperf"
+      {"xn--sknit-yqa.no", 0},
+#line 1650 "effective_tld_names.gperf"
+      {"ketrzyn.pl", 0},
+#line 957 "effective_tld_names.gperf"
+      {"entomology.museum", 0},
+#line 2320 "effective_tld_names.gperf"
+      {"nt.gov.au", 0},
+#line 1899 "effective_tld_names.gperf"
+      {"mecon.ar", 1},
+#line 1692 "effective_tld_names.gperf"
+      {"konyvelo.hu", 0},
+#line 3293 "effective_tld_names.gperf"
+      {"upow.gov.pl", 0},
+#line 2298 "effective_tld_names.gperf"
+      {"norilsk.ru", 0},
+#line 964 "effective_tld_names.gperf"
+      {"erotika.hu", 0},
+#line 1353 "effective_tld_names.gperf"
+      {"guovdageaidnu.no", 0},
+#line 1931 "effective_tld_names.gperf"
+      {"mi.th", 0},
+#line 309 "effective_tld_names.gperf"
+      {"beeldengeluid.museum", 0},
+#line 2601 "effective_tld_names.gperf"
+      {"plc.ly", 0},
+#line 754 "effective_tld_names.gperf"
+      {"cymru.museum", 0},
+#line 2066 "effective_tld_names.gperf"
+      {"mytis.ru", 0},
+#line 247 "effective_tld_names.gperf"
+      {"australia.museum", 0},
+#line 268 "effective_tld_names.gperf"
+      {"badaddja.no", 0},
+#line 2242 "effective_tld_names.gperf"
+      {"newspaper.museum", 0},
+#line 1939 "effective_tld_names.gperf"
+      {"mie.jp", 2},
+#line 1075 "effective_tld_names.gperf"
+      {"frana.no", 0},
+#line 2094 "effective_tld_names.gperf"
+      {"namsskogan.no", 0},
+#line 1627 "effective_tld_names.gperf"
+      {"kalmykia.ru", 0},
+#line 517 "effective_tld_names.gperf"
+      {"city.kyoto.jp", 1},
+#line 321 "effective_tld_names.gperf"
+      {"berlevag.no", 0},
+#line 1634 "effective_tld_names.gperf"
+      {"karelia.ru", 0},
+#line 3604 "effective_tld_names.gperf"
+      {"xn--rros-gra.no", 0},
+#line 2099 "effective_tld_names.gperf"
+      {"naroy.no", 0},
+#line 3065 "effective_tld_names.gperf"
+      {"stavropol.ru", 0},
+#line 1946 "effective_tld_names.gperf"
+      {"mil.az", 0},
+#line 2965 "effective_tld_names.gperf"
+      {"siljan.no", 0},
+#line 1153 "effective_tld_names.gperf"
+      {"gjovik.no", 0},
+#line 947 "effective_tld_names.gperf"
+      {"enebakk.no", 0},
+#line 3320 "effective_tld_names.gperf"
+      {"uzhgorod.ua", 0},
+#line 2052 "effective_tld_names.gperf"
+      {"museet.museum", 0},
+#line 1915 "effective_tld_names.gperf"
+      {"media.pl", 0},
+#line 997 "effective_tld_names.gperf"
+      {"farmstead.museum", 0},
+#line 3572 "effective_tld_names.gperf"
       {"xn--mlselv-iua.no", 0},
 #line 1619 "effective_tld_names.gperf"
+      {"k.bg", 0},
+#line 2236 "effective_tld_names.gperf"
+      {"neues.museum", 0},
+#line 333 "effective_tld_names.gperf"
+      {"bible.museum", 0},
+#line 1351 "effective_tld_names.gperf"
+      {"gulen.no", 0},
+#line 3026 "effective_tld_names.gperf"
+      {"sorreisa.no", 0},
+#line 3596 "effective_tld_names.gperf"
+      {"xn--rennesy-v1a.no", 0},
+#line 956 "effective_tld_names.gperf"
+      {"entertainment.aero", 0},
+#line 1578 "effective_tld_names.gperf"
+      {"jaworzno.pl", 0},
+#line 1648 "effective_tld_names.gperf"
       {"kemerovo.ru", 0},
-#line 1142 "effective_tld_names.gperf"
-      {"glogow.pl", 0},
-#line 1486 "effective_tld_names.gperf"
-      {"info.la", 0},
-#line 755 "effective_tld_names.gperf"
-      {"davvenjarga.no", 0},
-#line 3566 "effective_tld_names.gperf"
-      {"xn--snes-poa.no", 0},
-#line 3410 "effective_tld_names.gperf"
-      {"xn--55qx5d.hk", 0},
-#line 326 "effective_tld_names.gperf"
-      {"bialystok.pl", 0},
-#line 371 "effective_tld_names.gperf"
-      {"bolt.hu", 0},
-#line 2790 "effective_tld_names.gperf"
-      {"ruovat.no", 0},
-#line 2809 "effective_tld_names.gperf"
-      {"saitama.jp", 2},
-#line 790 "effective_tld_names.gperf"
-      {"donostia.museum", 0},
-#line 3161 "effective_tld_names.gperf"
-      {"trana.no", 0},
-#line 2816 "effective_tld_names.gperf"
-      {"salvadordali.museum", 0},
-#line 3555 "effective_tld_names.gperf"
-      {"xn--skierv-uta.no", 0},
-#line 2225 "effective_tld_names.gperf"
-      {"nittedal.no", 0},
-#line 1135 "effective_tld_names.gperf"
-      {"gjesdal.no", 0},
-#line 3448 "effective_tld_names.gperf"
-      {"xn--frde-gra.no", 0},
-#line 2058 "effective_tld_names.gperf"
-      {"name.vn", 0},
-#line 1637 "effective_tld_names.gperf"
-      {"kirov.ru", 0},
-#line 1561 "effective_tld_names.gperf"
-      {"jewish.museum", 0},
-#line 1334 "effective_tld_names.gperf"
-      {"gwangju.kr", 0},
-#line 1906 "effective_tld_names.gperf"
-      {"microlight.aero", 0},
-#line 3206 "effective_tld_names.gperf"
-      {"tydal.no", 0},
-#line 2532 "effective_tld_names.gperf"
-      {"pharmacien.fr", 0},
-#line 3431 "effective_tld_names.gperf"
-      {"xn--brnny-wuac.no", 0},
-#line 2871 "effective_tld_names.gperf"
-      {"sciencehistory.museum", 0},
-#line 2522 "effective_tld_names.gperf"
-      {"perso.ht", 0},
-#line 2045 "effective_tld_names.gperf"
-      {"naklo.pl", 0},
-#line 2373 "effective_tld_names.gperf"
-      {"org.dz", 0},
-#line 3063 "effective_tld_names.gperf"
-      {"swinoujscie.pl", 0},
-#line 1353 "effective_tld_names.gperf"
-      {"hamburg.museum", 0},
-#line 2974 "effective_tld_names.gperf"
-      {"sor-odal.no", 0},
-#line 751 "effective_tld_names.gperf"
-      {"dagestan.ru", 0},
-#line 1608 "effective_tld_names.gperf"
-      {"karmoy.no", 0},
-#line 767 "effective_tld_names.gperf"
-      {"denmark.museum", 0},
-#line 1286 "effective_tld_names.gperf"
-      {"grandrapids.museum", 0},
-#line 94 "effective_tld_names.gperf"
-      {"agro.pl", 0},
-#line 409 "effective_tld_names.gperf"
-      {"budejju.no", 0},
-#line 222 "effective_tld_names.gperf"
-      {"asso.re", 0},
-#line 2538 "effective_tld_names.gperf"
-      {"phoenix.museum", 0},
-#line 3393 "effective_tld_names.gperf"
-      {"wloclawek.pl", 0},
-#line 920 "effective_tld_names.gperf"
-      {"eisenbahn.museum", 0},
-#line 3053 "effective_tld_names.gperf"
-      {"suwalki.pl", 0},
-#line 697 "effective_tld_names.gperf"
-      {"consulado.st", 0},
-#line 1621 "effective_tld_names.gperf"
-      {"ketrzyn.pl", 0},
-#line 2654 "effective_tld_names.gperf"
-      {"priv.no", 0},
-#line 3284 "effective_tld_names.gperf"
-      {"vantaa.museum", 0},
-#line 947 "effective_tld_names.gperf"
-      {"erotika.hu", 0},
-#line 1670 "effective_tld_names.gperf"
-      {"kraanghke.no", 0},
-#line 3113 "effective_tld_names.gperf"
-      {"tj", 0},
-#line 2508 "effective_tld_names.gperf"
-      {"pavia.it", 0},
-#line 3338 "effective_tld_names.gperf"
-      {"viterbo.it", 0},
-#line 3508 "effective_tld_names.gperf"
-      {"xn--merker-kua.no", 0},
-#line 2054 "effective_tld_names.gperf"
-      {"name.na", 0},
-#line 2581 "effective_tld_names.gperf"
-      {"powiat.pl", 0},
-#line 3547 "effective_tld_names.gperf"
-      {"xn--rsta-fra.no", 0},
-#line 3564 "effective_tld_names.gperf"
-      {"xn--snase-nra.no", 0},
-#line 3583 "effective_tld_names.gperf"
-      {"xn--trna-woa.no", 0},
-#line 3582 "effective_tld_names.gperf"
-      {"xn--trgstad-r1a.no", 0},
-#line 341 "effective_tld_names.gperf"
-      {"biz.az", 0},
-#line 3453 "effective_tld_names.gperf"
-      {"xn--givuotna-8ya.no", 0},
-#line 2643 "effective_tld_names.gperf"
-      {"press.ma", 0},
-#line 3446 "effective_tld_names.gperf"
-      {"xn--fl-zia.no", 0},
-#line 3179 "effective_tld_names.gperf"
-      {"tromso.no", 0},
-#line 3500 "effective_tld_names.gperf"
-      {"xn--lns-qla.museum", 0},
-#line 2710 "effective_tld_names.gperf"
-      {"railway.museum", 0},
-#line 2759 "effective_tld_names.gperf"
-      {"rl.no", 0},
-#line 3197 "effective_tld_names.gperf"
-      {"tv.br", 0},
-#line 1295 "effective_tld_names.gperf"
-      {"groundhandling.aero", 0},
-#line 1004 "effective_tld_names.gperf"
-      {"film.hu", 0},
-#line 183 "effective_tld_names.gperf"
-      {"art.dz", 0},
-#line 2859 "effective_tld_names.gperf"
-      {"schlesisches.museum", 0},
-#line 1719 "effective_tld_names.gperf"
-      {"lans.museum", 0},
-#line 3601 "effective_tld_names.gperf"
-      {"xn--vry-yla5g.no", 0},
-#line 234 "effective_tld_names.gperf"
-      {"audnedaln.no", 0},
-#line 3531 "effective_tld_names.gperf"
-      {"xn--porsgu-sta26f.no", 0},
-#line 480 "effective_tld_names.gperf"
-      {"chiba.jp", 2},
-#line 3178 "effective_tld_names.gperf"
-      {"tromsa.no", 0},
-#line 3621 "effective_tld_names.gperf"
-      {"yokohama.jp", 2},
-#line 3390 "effective_tld_names.gperf"
-      {"williamsburg.museum", 0},
-#line 3435 "effective_tld_names.gperf"
-      {"xn--ciqpn.hk", 0},
-#line 3440 "effective_tld_names.gperf"
-      {"xn--dnna-gra.no", 0},
-#line 2472 "effective_tld_names.gperf"
-      {"ostroda.pl", 0},
-#line 2079 "effective_tld_names.gperf"
-      {"naturhistorisches.museum", 0},
-#line 124 "effective_tld_names.gperf"
-      {"alstahaug.no", 0},
-#line 1068 "effective_tld_names.gperf"
-      {"frog.museum", 0},
-#line 3366 "effective_tld_names.gperf"
-      {"wakayama.jp", 2},
-#line 3479 "effective_tld_names.gperf"
-      {"xn--klbu-woa.no", 0},
-#line 2810 "effective_tld_names.gperf"
-      {"sakhalin.ru", 0},
-#line 368 "effective_tld_names.gperf"
-      {"bokn.no", 0},
-#line 3288 "effective_tld_names.gperf"
-      {"varggat.no", 0},
-#line 2222 "effective_tld_names.gperf"
-      {"niigata.jp", 2},
-#line 946 "effective_tld_names.gperf"
-      {"erotica.hu", 0},
-#line 2301 "effective_tld_names.gperf"
-      {"oceanographic.museum", 0},
-#line 3096 "effective_tld_names.gperf"
-      {"television.museum", 0},
-#line 2644 "effective_tld_names.gperf"
-      {"press.museum", 0},
-#line 529 "effective_tld_names.gperf"
-      {"club.tw", 0},
-#line 3146 "effective_tld_names.gperf"
-      {"torsken.no", 0},
-#line 473 "effective_tld_names.gperf"
-      {"chel.ru", 0},
-#line 1840 "effective_tld_names.gperf"
-      {"marburg.museum", 0},
-#line 2895 "effective_tld_names.gperf"
-      {"sex.hu", 0},
-#line 486 "effective_tld_names.gperf"
-      {"chiropractic.museum", 0},
-#line 3392 "effective_tld_names.gperf"
-      {"wlocl.pl", 0},
-#line 2545 "effective_tld_names.gperf"
-      {"pisa.it", 0},
-#line 117 "effective_tld_names.gperf"
-      {"alabama.museum", 0},
-#line 1384 "effective_tld_names.gperf"
-      {"hiroshima.jp", 2},
-#line 2578 "effective_tld_names.gperf"
-      {"portlligat.museum", 0},
-#line 1829 "effective_tld_names.gperf"
-      {"malbork.pl", 0},
-#line 2080 "effective_tld_names.gperf"
-      {"natuurwetenschappen.museum", 0},
-#line 3483 "effective_tld_names.gperf"
-      {"xn--krdsherad-m8a.no", 0},
-#line 3575 "effective_tld_names.gperf"
-      {"xn--stjrdal-s1a.no", 0},
-#line 3442 "effective_tld_names.gperf"
-      {"xn--dyry-ira.no", 0},
-#line 3577 "effective_tld_names.gperf"
-      {"xn--stre-toten-zcb.no", 0},
-#line 1812 "effective_tld_names.gperf"
-      {"lviv.ua", 0},
-#line 797 "effective_tld_names.gperf"
-      {"dudinka.ru", 0},
-#line 1695 "effective_tld_names.gperf"
-      {"kvinesdal.no", 0},
-#line 2786 "effective_tld_names.gperf"
-      {"rs.ba", 0},
-#line 3522 "effective_tld_names.gperf"
-      {"xn--nry-yla5g.no", 0},
-#line 2784 "effective_tld_names.gperf"
-      {"royrvik.no", 0},
-#line 3120 "effective_tld_names.gperf"
-      {"tm.fr", 0},
-#line 3570 "effective_tld_names.gperf"
-      {"xn--sr-odal-q1a.no", 0},
-#line 1615 "effective_tld_names.gperf"
-      {"kazan.ru", 0},
-#line 1854 "effective_tld_names.gperf"
-      {"massacarrara.it", 0},
-#line 239 "effective_tld_names.gperf"
-      {"aurskog-holand.no", 0},
-#line 924 "effective_tld_names.gperf"
-      {"elvendrell.museum", 0},
-#line 2842 "effective_tld_names.gperf"
-      {"savannahga.museum", 0},
-#line 305 "effective_tld_names.gperf"
-      {"belau.pw", 0},
-#line 240 "effective_tld_names.gperf"
-      {"austevoll.no", 0},
-#line 1989 "effective_tld_names.gperf"
-      {"monticello.museum", 0},
-#line 2493 "effective_tld_names.gperf"
-      {"padua.it", 0},
-#line 1362 "effective_tld_names.gperf"
-      {"harvestcelebration.museum", 0},
-#line 3143 "effective_tld_names.gperf"
-      {"topology.museum", 0},
-#line 1490 "effective_tld_names.gperf"
-      {"info.pk", 0},
-#line 2527 "effective_tld_names.gperf"
-      {"pescara.it", 0},
-#line 1957 "effective_tld_names.gperf"
-      {"missile.museum", 0},
-#line 2931 "effective_tld_names.gperf"
-      {"skierva.no", 0},
-#line 1386 "effective_tld_names.gperf"
-      {"historical.museum", 0},
-#line 1389 "effective_tld_names.gperf"
-      {"historisch.museum", 0},
-#line 3472 "effective_tld_names.gperf"
-      {"xn--indery-fya.no", 0},
-#line 3529 "effective_tld_names.gperf"
-      {"xn--ostery-fya.no", 0},
-#line 2867 "effective_tld_names.gperf"
-      {"scienceandhistory.museum", 0},
-#line 1671 "effective_tld_names.gperf"
-      {"kragero.no", 0},
-#line 2501 "effective_tld_names.gperf"
-      {"paragliding.aero", 0},
-#line 1681 "effective_tld_names.gperf"
-      {"kumamoto.jp", 2},
-#line 3530 "effective_tld_names.gperf"
-      {"xn--osyro-wua.no", 0},
-#line 3128 "effective_tld_names.gperf"
-      {"tm.se", 0},
-#line 418 "effective_tld_names.gperf"
-      {"bydgoszcz.pl", 0},
-#line 263 "effective_tld_names.gperf"
-      {"badaddja.no", 0},
-#line 3233 "effective_tld_names.gperf"
-      {"um.gov.pl", 0},
-#line 2647 "effective_tld_names.gperf"
-      {"presse.fr", 0},
-#line 3032 "effective_tld_names.gperf"
-      {"storfjord.no", 0},
-#line 1390 "effective_tld_names.gperf"
-      {"historisches.museum", 0},
-#line 3375 "effective_tld_names.gperf"
-      {"watchandclock.museum", 0},
-#line 3491 "effective_tld_names.gperf"
-      {"xn--laheadju-7ya.no", 0},
-#line 483 "effective_tld_names.gperf"
-      {"children.museum", 0},
-#line 1838 "effective_tld_names.gperf"
-      {"mantova.it", 0},
-#line 2820 "effective_tld_names.gperf"
-      {"sande.more-og-romsdal.no", 0},
-#line 3607 "effective_tld_names.gperf"
-      {"xn--zf0avx.hk", 0},
-#line 2590 "effective_tld_names.gperf"
-      {"prato.it", 0},
-#line 1636 "effective_tld_names.gperf"
-      {"kirkenes.no", 0},
-#line 1138 "effective_tld_names.gperf"
-      {"glas.museum", 0},
-#line 1826 "effective_tld_names.gperf"
-      {"mail.pl", 0},
-#line 3207 "effective_tld_names.gperf"
-      {"tynset.no", 0},
-#line 2734 "effective_tld_names.gperf"
-      {"rel.ht", 0},
-#line 3276 "effective_tld_names.gperf"
-      {"vagan.no", 0},
-#line 3324 "effective_tld_names.gperf"
-      {"vibovalentia.it", 0},
-#line 152 "effective_tld_names.gperf"
-      {"annefrank.museum", 0},
-#line 3304 "effective_tld_names.gperf"
-      {"verdal.no", 0},
-#line 2201 "effective_tld_names.gperf"
-      {"newhampshire.museum", 0},
-#line 1071 "effective_tld_names.gperf"
-      {"from.hr", 0},
-#line 484 "effective_tld_names.gperf"
-      {"childrens.museum", 0},
-#line 2543 "effective_tld_names.gperf"
-      {"pilot.aero", 0},
-#line 3523 "effective_tld_names.gperf"
-      {"xn--nttery-byae.no", 0},
-#line 2314 "effective_tld_names.gperf"
-      {"okinawa.jp", 2},
-#line 2521 "effective_tld_names.gperf"
-      {"perm.ru", 0},
-#line 216 "effective_tld_names.gperf"
-      {"asso.dz", 0},
-#line 2941 "effective_tld_names.gperf"
-      {"slask.pl", 0},
-#line 1689 "effective_tld_names.gperf"
-      {"kuzbass.ru", 0},
-#line 171 "effective_tld_names.gperf"
-      {"archaeological.museum", 0},
-#line 412 "effective_tld_names.gperf"
-      {"buryatia.ru", 0},
-#line 2546 "effective_tld_names.gperf"
-      {"pistoia.it", 0},
-#line 1043 "effective_tld_names.gperf"
-      {"folkebibl.no", 0},
-#line 1582 "effective_tld_names.gperf"
-      {"judaica.museum", 0},
-#line 1388 "effective_tld_names.gperf"
-      {"historichouses.museum", 0},
-#line 2757 "effective_tld_names.gperf"
-      {"risor.no", 0},
-#line 2504 "effective_tld_names.gperf"
-      {"parma.it", 0},
-#line 1809 "effective_tld_names.gperf"
-      {"luxembourg.museum", 0},
-#line 3560 "effective_tld_names.gperf"
-      {"xn--slat-5na.no", 0},
-#line 3646 "effective_tld_names.gperf"
-      {"zoological.museum", 0},
-#line 3538 "effective_tld_names.gperf"
-      {"xn--rholt-mra.no", 0},
-#line 354 "effective_tld_names.gperf"
-      {"bjerkreim.no", 0},
-#line 3228 "effective_tld_names.gperf"
-      {"ullensaker.no", 0},
-#line 733 "effective_tld_names.gperf"
-      {"cultural.museum", 0},
-#line 756 "effective_tld_names.gperf"
-      {"davvesiida.no", 0},
-#line 3191 "effective_tld_names.gperf"
-      {"turen.tn", 0},
-#line 1595 "effective_tld_names.gperf"
-      {"kagawa.jp", 2},
-#line 2026 "effective_tld_names.gperf"
-      {"museumvereniging.museum", 0},
-#line 1076 "effective_tld_names.gperf"
-      {"fuel.aero", 0},
-#line 3569 "effective_tld_names.gperf"
-      {"xn--sr-fron-q1a.no", 0},
-#line 3471 "effective_tld_names.gperf"
-      {"xn--hylandet-54a.no", 0},
-#line 2753 "effective_tld_names.gperf"
-      {"ringebu.no", 0},
-#line 3581 "effective_tld_names.gperf"
-      {"xn--trany-yua.no", 0},
-#line 3579 "effective_tld_names.gperf"
-      {"xn--tn0ag.hk", 0},
-#line 3460 "effective_tld_names.gperf"
-      {"xn--hbmer-xqa.no", 0},
-#line 2296 "effective_tld_names.gperf"
-      {"nyny.museum", 0},
-#line 3315 "effective_tld_names.gperf"
-      {"veterinaire.km", 0},
-#line 1629 "effective_tld_names.gperf"
-      {"kherson.ua", 0},
-#line 3510 "effective_tld_names.gperf"
-      {"xn--mk0axi.hk", 0},
-#line 1141 "effective_tld_names.gperf"
-      {"gliwice.pl", 0},
-#line 3234 "effective_tld_names.gperf"
-      {"unbi.ba", 0},
-#line 3329 "effective_tld_names.gperf"
-      {"vik.no", 0},
-#line 726 "effective_tld_names.gperf"
-      {"crew.aero", 0},
-#line 3514 "effective_tld_names.gperf"
-      {"xn--moreke-jua.no", 0},
-#line 3203 "effective_tld_names.gperf"
-      {"tw.cn", 0},
-#line 3553 "effective_tld_names.gperf"
-      {"xn--seral-lra.no", 0},
-#line 3541 "effective_tld_names.gperf"
-      {"xn--rland-uua.no", 0},
-#line 3049 "effective_tld_names.gperf"
-      {"surgeonshall.museum", 0},
-#line 3303 "effective_tld_names.gperf"
-      {"vercelli.it", 0},
-#line 1970 "effective_tld_names.gperf"
-      {"mo-i-rana.no", 0},
-#line 3317 "effective_tld_names.gperf"
-      {"vf.no", 0},
-#line 3222 "effective_tld_names.gperf"
-      {"ug.gov.pl", 0},
-#line 330 "effective_tld_names.gperf"
-      {"bieszczady.pl", 0},
-#line 3594 "effective_tld_names.gperf"
-      {"xn--vg-yiab.no", 0},
-#line 3196 "effective_tld_names.gperf"
-      {"tv.bo", 0},
-#line 3273 "effective_tld_names.gperf"
-      {"vaapste.no", 0},
-#line 1992 "effective_tld_names.gperf"
-      {"mordovia.ru", 0},
-#line 2044 "effective_tld_names.gperf"
-      {"nakhodka.ru", 0},
-#line 299 "effective_tld_names.gperf"
-      {"bearalvahki.no", 0},
-#line 178 "effective_tld_names.gperf"
-      {"arkhangelsk.ru", 0},
-#line 1638 "effective_tld_names.gperf"
-      {"kirovograd.ua", 0},
-#line 2302 "effective_tld_names.gperf"
-      {"oceanographique.museum", 0},
-#line 3142 "effective_tld_names.gperf"
-      {"tonsberg.no", 0},
-#line 2782 "effective_tld_names.gperf"
-      {"rovno.ua", 0},
-#line 1627 "effective_tld_names.gperf"
-      {"khakassia.ru", 0},
-#line 137 "effective_tld_names.gperf"
-      {"americanantiques.museum", 0},
-#line 315 "effective_tld_names.gperf"
-      {"berlevag.no", 0},
-#line 303 "effective_tld_names.gperf"
-      {"beeldengeluid.museum", 0},
-#line 3418 "effective_tld_names.gperf"
-      {"xn--b-5ga.telemark.no", 0},
-#line 22 "effective_tld_names.gperf"
-      {"6bone.pl", 0},
-#line 2652 "effective_tld_names.gperf"
-      {"priv.hu", 0},
-#line 1569 "effective_tld_names.gperf"
-      {"jobs.tt", 0},
-#line 2716 "effective_tld_names.gperf"
-      {"rauma.no", 0},
-#line 3312 "effective_tld_names.gperf"
-      {"vestvagoy.no", 0},
-#line 2865 "effective_tld_names.gperf"
-      {"science-fiction.museum", 0},
-#line 1672 "effective_tld_names.gperf"
-      {"krakow.pl", 0},
-#line 581 "effective_tld_names.gperf"
-      {"cody.museum", 0},
-#line 2564 "effective_tld_names.gperf"
-      {"podlasie.pl", 0},
-#line 3290 "effective_tld_names.gperf"
-      {"vb.it", 0},
-#line 1377 "effective_tld_names.gperf"
-      {"hemsedal.no", 0},
-#line 2904 "effective_tld_names.gperf"
-      {"shimane.jp", 2},
-#line 2769 "effective_tld_names.gperf"
-      {"rochester.museum", 0},
-#line 3177 "effective_tld_names.gperf"
-      {"trolley.museum", 0},
-#line 2082 "effective_tld_names.gperf"
-      {"naustdal.no", 0},
-#line 2795 "effective_tld_names.gperf"
-      {"rybnik.pl", 0},
-#line 3480 "effective_tld_names.gperf"
-      {"xn--koluokta-7ya57h.no", 0},
-#line 2669 "effective_tld_names.gperf"
-      {"promocion.ar", 1},
 #line 1438 "effective_tld_names.gperf"
-      {"ibaraki.jp", 2},
-#line 3239 "effective_tld_names.gperf"
-      {"unsa.ba", 0},
-#line 734 "effective_tld_names.gperf"
-      {"culturalcenter.museum", 0},
-#line 2064 "effective_tld_names.gperf"
-      {"nara.jp", 2},
-#line 394 "effective_tld_names.gperf"
-      {"broadcast.museum", 0},
-#line 1032 "effective_tld_names.gperf"
-      {"flog.br", 0},
-#line 339 "effective_tld_names.gperf"
-      {"birthplace.museum", 0},
-#line 3174 "effective_tld_names.gperf"
-      {"trieste.it", 0},
-#line 3492 "effective_tld_names.gperf"
-      {"xn--langevg-jxa.no", 0},
-#line 1605 "effective_tld_names.gperf"
-      {"karelia.ru", 0},
-#line 3348 "effective_tld_names.gperf"
-      {"volgograd.ru", 0},
-#line 3349 "effective_tld_names.gperf"
-      {"volkenkunde.museum", 0},
-#line 1564 "effective_tld_names.gperf"
-      {"jgora.pl", 0},
-#line 3282 "effective_tld_names.gperf"
-      {"valley.museum", 0},
-#line 1129 "effective_tld_names.gperf"
-      {"gifu.jp", 2},
-#line 3423 "effective_tld_names.gperf"
-      {"xn--bhccavuotna-k7a.no", 0},
-#line 3165 "effective_tld_names.gperf"
-      {"trapani.it", 0},
-#line 489 "effective_tld_names.gperf"
-      {"chocolate.museum", 0},
-#line 1641 "effective_tld_names.gperf"
-      {"klepp.no", 0},
-#line 2311 "effective_tld_names.gperf"
-      {"oita.jp", 2},
-#line 3310 "effective_tld_names.gperf"
-      {"vestre-slidre.no", 0},
-#line 3114 "effective_tld_names.gperf"
-      {"tj.cn", 0},
-#line 2873 "effective_tld_names.gperf"
-      {"sciencesnaturelles.museum", 0},
-#line 3139 "effective_tld_names.gperf"
-      {"tolga.no", 0},
-#line 3152 "effective_tld_names.gperf"
-      {"toyama.jp", 2},
-#line 3542 "effective_tld_names.gperf"
-      {"xn--rlingen-mxa.no", 0},
-#line 2752 "effective_tld_names.gperf"
-      {"rindal.no", 0},
-#line 1861 "effective_tld_names.gperf"
-      {"mbone.pl", 0},
-#line 3595 "effective_tld_names.gperf"
-      {"xn--vgan-qoa.no", 0},
-#line 3326 "effective_tld_names.gperf"
-      {"vic.gov.au", 0},
-#line 3121 "effective_tld_names.gperf"
-      {"tm.hu", 0},
-#line 3093 "effective_tld_names.gperf"
-      {"technology.museum", 0},
-#line 3589 "effective_tld_names.gperf"
-      {"xn--unjrga-rta.no", 0},
-#line 2780 "effective_tld_names.gperf"
-      {"rotorcraft.aero", 0},
-#line 3289 "effective_tld_names.gperf"
-      {"varoy.no", 0},
-#line 3158 "effective_tld_names.gperf"
-      {"trader.aero", 0},
-#line 3587 "effective_tld_names.gperf"
-      {"xn--uc0atv.tw", 0},
-#line 969 "effective_tld_names.gperf"
-      {"experts-comptables.fr", 0},
-#line 2051 "effective_tld_names.gperf"
-      {"name.jo", 0},
-#line 1387 "effective_tld_names.gperf"
-      {"historicalsociety.museum", 0},
-#line 1654 "effective_tld_names.gperf"
-      {"komforb.se", 0},
-#line 1857 "effective_tld_names.gperf"
-      {"matta-varjjat.no", 0},
-#line 2498 "effective_tld_names.gperf"
-      {"palmsprings.museum", 0},
-#line 2796 "effective_tld_names.gperf"
-      {"rygge.no", 0},
-#line 3167 "effective_tld_names.gperf"
-      {"travel.pl", 0},
-#line 328 "effective_tld_names.gperf"
-      {"bielawa.pl", 0},
-#line 1686 "effective_tld_names.gperf"
-      {"kursk.ru", 0},
-#line 3281 "effective_tld_names.gperf"
-      {"valle.no", 0},
-#line 3498 "effective_tld_names.gperf"
-      {"xn--lhppi-xqa.no", 0},
-#line 3149 "effective_tld_names.gperf"
-      {"tourism.pl", 0},
-#line 3079 "effective_tld_names.gperf"
-      {"tananger.no", 0},
-#line 3173 "effective_tld_names.gperf"
-      {"treviso.it", 0},
-#line 3512 "effective_tld_names.gperf"
-      {"xn--mli-tla.no", 0},
-#line 1697 "effective_tld_names.gperf"
-      {"kviteseid.no", 0},
-#line 2666 "effective_tld_names.gperf"
-      {"production.aero", 0},
-#line 2771 "effective_tld_names.gperf"
-      {"rodoy.no", 0},
-#line 1651 "effective_tld_names.gperf"
-      {"koeln.museum", 0},
-#line 2755 "effective_tld_names.gperf"
-      {"ringsaker.no", 0},
-#line 3314 "effective_tld_names.gperf"
-      {"veterinaire.fr", 0},
-#line 2670 "effective_tld_names.gperf"
-      {"pruszkow.pl", 0},
-#line 1394 "effective_tld_names.gperf"
-      {"hjartdal.no", 0},
-#line 3458 "effective_tld_names.gperf"
-      {"xn--h-2fa.no", 0},
-#line 752 "effective_tld_names.gperf"
-      {"dali.museum", 0},
-#line 3600 "effective_tld_names.gperf"
-      {"xn--vrggt-xqad.no", 0},
-#line 694 "effective_tld_names.gperf"
-      {"conf.lv", 0},
-#line 1691 "effective_tld_names.gperf"
-      {"kvafjord.no", 0},
-#line 3559 "effective_tld_names.gperf"
-      {"xn--sknland-fxa.no", 0},
-#line 3580 "effective_tld_names.gperf"
-      {"xn--tnsberg-q1a.no", 0},
-#line 1718 "effective_tld_names.gperf"
-      {"langevag.no", 0},
-#line 1613 "effective_tld_names.gperf"
-      {"kautokeino.no", 0},
-#line 3467 "effective_tld_names.gperf"
-      {"xn--hobl-ira.no", 0},
-#line 3546 "effective_tld_names.gperf"
-      {"xn--rst-0na.no", 0},
-#line 325 "effective_tld_names.gperf"
-      {"bialowieza.pl", 0},
-#line 1609 "effective_tld_names.gperf"
-      {"karpacz.pl", 0},
-#line 3568 "effective_tld_names.gperf"
-      {"xn--sr-aurdal-l8a.no", 0},
-#line 2905 "effective_tld_names.gperf"
-      {"shizuoka.jp", 2},
-#line 3325 "effective_tld_names.gperf"
-      {"vic.edu.au", 0},
-#line 2567 "effective_tld_names.gperf"
-      {"polkowice.pl", 0},
-#line 1078 "effective_tld_names.gperf"
-      {"fukuoka.jp", 2},
-#line 2653 "effective_tld_names.gperf"
-      {"priv.me", 0},
-#line 3427 "effective_tld_names.gperf"
-      {"xn--bjddar-pta.no", 0},
-#line 3503 "effective_tld_names.gperf"
-      {"xn--lrenskog-54a.no", 0},
-#line 2906 "effective_tld_names.gperf"
-      {"shop.ht", 0},
-#line 3476 "effective_tld_names.gperf"
-      {"xn--jrpeland-54a.no", 0},
-#line 3111 "effective_tld_names.gperf"
-      {"tingvoll.no", 0},
-#line 3474 "effective_tld_names.gperf"
-      {"xn--io0a7i.hk", 0},
-#line 3336 "effective_tld_names.gperf"
-      {"virtual.museum", 0},
-#line 485 "effective_tld_names.gperf"
-      {"childrensgarden.museum", 0},
-#line 3449 "effective_tld_names.gperf"
-      {"xn--frna-woa.no", 0},
-#line 2712 "effective_tld_names.gperf"
-      {"rakkestad.no", 0},
-#line 2907 "effective_tld_names.gperf"
-      {"shop.hu", 0},
-#line 2776 "effective_tld_names.gperf"
-      {"romsa.no", 0},
-#line 3141 "effective_tld_names.gperf"
-      {"tomsk.ru", 0},
-#line 3107 "effective_tld_names.gperf"
-      {"theater.museum", 0},
-#line 3493 "effective_tld_names.gperf"
-      {"xn--lcvr32d.hk", 0},
-#line 3085 "effective_tld_names.gperf"
-      {"tas.gov.au", 0},
-#line 3399 "effective_tld_names.gperf"
-      {"wroc.pl", 0},
-#line 3602 "effective_tld_names.gperf"
-      {"xn--wcvs22d.hk", 0},
-#line 2552 "effective_tld_names.gperf"
-      {"planetarium.museum", 0},
-#line 3277 "effective_tld_names.gperf"
-      {"vagsoy.no", 0},
-#line 995 "effective_tld_names.gperf"
-      {"fhsk.se", 0},
-#line 3543 "effective_tld_names.gperf"
-      {"xn--rmskog-bya.no", 0},
-#line 1684 "effective_tld_names.gperf"
-      {"kunstunddesign.museum", 0},
-#line 490 "effective_tld_names.gperf"
-      {"christiansburg.museum", 0},
-#line 3481 "effective_tld_names.gperf"
-      {"xn--krager-gya.no", 0},
-#line 1098 "effective_tld_names.gperf"
-      {"gangaviika.no", 0},
-#line 2561 "effective_tld_names.gperf"
-      {"po.gov.pl", 0},
-#line 2830 "effective_tld_names.gperf"
-      {"santabarbara.museum", 0},
-#line 1751 "effective_tld_names.gperf"
-      {"lewismiller.museum", 0},
-#line 3450 "effective_tld_names.gperf"
-      {"xn--frya-hra.no", 0},
-#line 2777 "effective_tld_names.gperf"
-      {"romskog.no", 0},
+      {"horology.museum", 0},
+#line 928 "effective_tld_names.gperf"
+      {"egyptian.museum", 0},
+#line 2597 "effective_tld_names.gperf"
+      {"plantation.museum", 0},
+#line 751 "effective_tld_names.gperf"
+      {"cx", 0},
+#line 259 "effective_tld_names.gperf"
+      {"ax", 0},
+#line 343 "effective_tld_names.gperf"
+      {"birdart.museum", 0},
 #line 1584 "effective_tld_names.gperf"
-      {"juedisches.museum", 0},
-#line 1659 "effective_tld_names.gperf"
-      {"kongsberg.no", 0},
-#line 2807 "effective_tld_names.gperf"
-      {"saga.jp", 2},
-#line 385 "effective_tld_names.gperf"
-      {"brandywinevalley.museum", 0},
-#line 2983 "effective_tld_names.gperf"
-      {"southcarolina.museum", 0},
-#line 2713 "effective_tld_names.gperf"
-      {"ralingen.no", 0},
-#line 504 "effective_tld_names.gperf"
-      {"city.hu", 0},
-#line 3136 "effective_tld_names.gperf"
-      {"tokke.no", 0},
+      {"jeonnam.kr", 0},
+#line 524 "effective_tld_names.gperf"
+      {"city.sendai.jp", 1},
+#line 586 "effective_tld_names.gperf"
+      {"co.uz", 0},
+#line 585 "effective_tld_names.gperf"
+      {"co.us", 0},
+#line 3290 "effective_tld_names.gperf"
+      {"university.museum", 0},
+#line 262 "effective_tld_names.gperf"
+      {"az.us", 0},
+#line 1468 "effective_tld_names.gperf"
+      {"id.us", 0},
+#line 201 "effective_tld_names.gperf"
+      {"as.us", 0},
+#line 717 "effective_tld_names.gperf"
+      {"coop.ht", 0},
+#line 1152 "effective_tld_names.gperf"
+      {"gjesdal.no", 0},
+#line 743 "effective_tld_names.gperf"
+      {"ct.us", 0},
+#line 1476 "effective_tld_names.gperf"
+      {"il.us", 0},
+#line 118 "effective_tld_names.gperf"
+      {"al.us", 0},
+#line 1440 "effective_tld_names.gperf"
+      {"hotel.hu", 0},
+#line 1881 "effective_tld_names.gperf"
+      {"masoy.no", 0},
+#line 445 "effective_tld_names.gperf"
+      {"caltanissetta.it", 0},
+#line 413 "effective_tld_names.gperf"
+      {"bryne.no", 0},
+#line 2614 "effective_tld_names.gperf"
+      {"pomorze.pl", 0},
+#line 134 "effective_tld_names.gperf"
+      {"amber.museum", 0},
+#line 748 "effective_tld_names.gperf"
+      {"cuneo.it", 0},
+#line 171 "effective_tld_names.gperf"
+      {"ar.us", 0},
 #line 2705 "effective_tld_names.gperf"
-      {"radoy.no", 0},
-#line 358 "effective_tld_names.gperf"
-      {"blog.br", 0},
+      {"pro.ht", 0},
+#line 669 "effective_tld_names.gperf"
+      {"com.ph", 0},
+#line 1582 "effective_tld_names.gperf"
+      {"jelenia-gora.pl", 0},
+#line 893 "effective_tld_names.gperf"
+      {"edu.ph", 0},
+#line 2946 "effective_tld_names.gperf"
+      {"sg", 0},
+#line 3642 "effective_tld_names.gperf"
+      {"xn--trgstad-r1a.no", 0},
+#line 2123 "effective_tld_names.gperf"
+      {"nd.us", 0},
+#line 939 "effective_tld_names.gperf"
+      {"elk.pl", 0},
+#line 1005 "effective_tld_names.gperf"
+      {"fermo.it", 0},
+#line 194 "effective_tld_names.gperf"
+      {"artgallery.museum", 0},
+#line 3101 "effective_tld_names.gperf"
+      {"surnadal.no", 0},
+#line 3615 "effective_tld_names.gperf"
+      {"xn--skierv-uta.no", 0},
+#line 223 "effective_tld_names.gperf"
+      {"asso.ht", 0},
+#line 710 "effective_tld_names.gperf"
+      {"consulting.aero", 0},
+#line 704 "effective_tld_names.gperf"
+      {"computerhistory.museum", 0},
+#line 1670 "effective_tld_names.gperf"
+      {"klepp.no", 0},
+#line 2021 "effective_tld_names.gperf"
+      {"montreal.museum", 0},
+#line 2702 "effective_tld_names.gperf"
+      {"pro.az", 0},
+#line 389 "effective_tld_names.gperf"
+      {"bozen.it", 0},
+#line 1273 "effective_tld_names.gperf"
+      {"gov.ph", 0},
+#line 3075 "effective_tld_names.gperf"
+      {"stor-elvdal.no", 0},
+#line 3634 "effective_tld_names.gperf"
+      {"xn--srum-gra.no", 0},
+#line 2130 "effective_tld_names.gperf"
+      {"ne.us", 0},
+#line 1097 "effective_tld_names.gperf"
+      {"fundacio.museum", 0},
+#line 2534 "effective_tld_names.gperf"
+      {"paderborn.museum", 0},
+#line 1490 "effective_tld_names.gperf"
+      {"in.us", 0},
 #line 17 "effective_tld_names.gperf"
       {"2000.hu", 0},
-#line 3347 "effective_tld_names.gperf"
-      {"volda.no", 0},
-#line 3299 "effective_tld_names.gperf"
-      {"venezia.it", 0},
-#line 2789 "effective_tld_names.gperf"
-      {"rubtsovsk.ru", 0},
-#line 3333 "effective_tld_names.gperf"
-      {"vindafjord.no", 0},
-#line 3337 "effective_tld_names.gperf"
-      {"virtuel.museum", 0},
-#line 3428 "effective_tld_names.gperf"
-      {"xn--blt-elab.no", 0},
-#line 1620 "effective_tld_names.gperf"
-      {"kepno.pl", 0},
-#line 292 "effective_tld_names.gperf"
-      {"batsfjord.no", 0},
-#line 3475 "effective_tld_names.gperf"
-      {"xn--jlster-bya.no", 0},
-#line 2742 "effective_tld_names.gperf"
-      {"research.aero", 0},
-#line 2207 "effective_tld_names.gperf"
-      {"newyork.museum", 0},
-#line 2773 "effective_tld_names.gperf"
-      {"roma.it", 0},
-#line 3296 "effective_tld_names.gperf"
-      {"vefsn.no", 0},
-#line 3083 "effective_tld_names.gperf"
-      {"tarnobrzeg.pl", 0},
-#line 1682 "effective_tld_names.gperf"
-      {"kunst.museum", 0},
-#line 2563 "effective_tld_names.gperf"
-      {"podhale.pl", 0},
-#line 1721 "effective_tld_names.gperf"
-      {"laquila.it", 0},
-#line 3604 "effective_tld_names.gperf"
-      {"xn--ygarden-p1a.no", 0},
-#line 978 "effective_tld_names.gperf"
-      {"farmequipment.museum", 0},
-#line 3525 "effective_tld_names.gperf"
-      {"xn--od0alg.cn", 0},
-#line 3084 "effective_tld_names.gperf"
-      {"tas.edu.au", 0},
-#line 3200 "effective_tld_names.gperf"
-      {"tvedestrand.no", 0},
-#line 3330 "effective_tld_names.gperf"
-      {"viking.museum", 0},
-#line 453 "effective_tld_names.gperf"
-      {"catanzaro.it", 0},
-#line 2657 "effective_tld_names.gperf"
-      {"pro.az", 0},
-#line 1687 "effective_tld_names.gperf"
-      {"kustanai.ru", 0},
-#line 1553 "effective_tld_names.gperf"
-      {"jelenia-gora.pl", 0},
-#line 1653 "effective_tld_names.gperf"
-      {"kolobrzeg.pl", 0},
-#line 3627 "effective_tld_names.gperf"
-      {"yuzhno-sakhalinsk.ru", 0},
-#line 3183 "effective_tld_names.gperf"
-      {"trysil.no", 0},
-#line 2723 "effective_tld_names.gperf"
-      {"realestate.pl", 0},
-#line 3351 "effective_tld_names.gperf"
-      {"voronezh.ru", 0},
-#line 1461 "effective_tld_names.gperf"
-      {"in-addr.arpa", 0},
-#line 2707 "effective_tld_names.gperf"
-      {"rahkkeravju.no", 0},
-#line 1688 "effective_tld_names.gperf"
-      {"kutno.pl", 0},
-#line 3561 "effective_tld_names.gperf"
-      {"xn--slt-elab.no", 0},
-#line 3511 "effective_tld_names.gperf"
-      {"xn--mlatvuopmi-s4a.no", 0},
-#line 2568 "effective_tld_names.gperf"
-      {"poltava.ua", 0},
-#line 3181 "effective_tld_names.gperf"
-      {"trust.museum", 0},
-#line 2224 "effective_tld_names.gperf"
-      {"nissedal.no", 0},
-#line 528 "effective_tld_names.gperf"
-      {"club.aero", 0},
-#line 3164 "effective_tld_names.gperf"
-      {"transport.museum", 0},
-#line 1601 "effective_tld_names.gperf"
-      {"kanagawa.jp", 2},
-#line 1661 "effective_tld_names.gperf"
-      {"konin.pl", 0},
-#line 1374 "effective_tld_names.gperf"
-      {"hembygdsforbund.museum", 0},
-#line 1696 "effective_tld_names.gperf"
-      {"kvinnherad.no", 0},
-#line 1585 "effective_tld_names.gperf"
-      {"juif.museum", 0},
-#line 1628 "effective_tld_names.gperf"
-      {"kharkov.ua", 0},
-#line 1673 "effective_tld_names.gperf"
-      {"krasnoyarsk.ru", 0},
-#line 3208 "effective_tld_names.gperf"
-      {"tysfjord.no", 0},
-#line 3331 "effective_tld_names.gperf"
-      {"vikna.no", 0},
-#line 2537 "effective_tld_names.gperf"
-      {"philately.museum", 0},
-#line 1655 "effective_tld_names.gperf"
-      {"komi.ru", 0},
-#line 1657 "effective_tld_names.gperf"
-      {"kommune.no", 0},
-#line 3462 "effective_tld_names.gperf"
-      {"xn--hery-ira.nordland.no", 0},
-#line 3263 "effective_tld_names.gperf"
-      {"uw.gov.pl", 0},
-#line 2763 "effective_tld_names.gperf"
-      {"rnrt.tn", 0},
-#line 2072 "effective_tld_names.gperf"
-      {"nationalheritage.museum", 0},
-#line 487 "effective_tld_names.gperf"
-      {"chirurgiens-dentistes.fr", 0},
-#line 2487 "effective_tld_names.gperf"
-      {"pa.gov.pl", 0},
-#line 2655 "effective_tld_names.gperf"
-      {"priv.pl", 0},
-#line 2071 "effective_tld_names.gperf"
-      {"nationalfirearms.museum", 0},
-#line 1648 "effective_tld_names.gperf"
-      {"kobierzyce.pl", 0},
-#line 3421 "effective_tld_names.gperf"
-      {"xn--berlevg-jxa.no", 0},
-#line 2711 "effective_tld_names.gperf"
-      {"raisa.no", 0},
-#line 3416 "effective_tld_names.gperf"
-      {"xn--avery-yua.no", 0},
-#line 2743 "effective_tld_names.gperf"
-      {"research.museum", 0},
-#line 278 "effective_tld_names.gperf"
-      {"balsfjord.no", 0},
-#line 3586 "effective_tld_names.gperf"
-      {"xn--uc0atv.hk", 0},
-#line 3447 "effective_tld_names.gperf"
-      {"xn--flor-jra.no", 0},
-#line 2205 "effective_tld_names.gperf"
-      {"news.hu", 0},
-#line 1583 "effective_tld_names.gperf"
-      {"judygarland.museum", 0},
-#line 1570 "effective_tld_names.gperf"
-      {"jogasz.hu", 0},
-#line 3457 "effective_tld_names.gperf"
-      {"xn--gmqw5a.hk", 0},
-#line 3328 "effective_tld_names.gperf"
-      {"video.hu", 0},
-#line 3509 "effective_tld_names.gperf"
-      {"xn--mjndalen-64a.no", 0},
-#line 3190 "effective_tld_names.gperf"
-      {"turek.pl", 0},
-#line 2665 "effective_tld_names.gperf"
-      {"prochowice.pl", 0},
-#line 3549 "effective_tld_names.gperf"
-      {"xn--ryrvik-bya.no", 0},
-#line 2775 "effective_tld_names.gperf"
-      {"rome.it", 0},
-#line 1012 "effective_tld_names.gperf"
-      {"firenze.it", 0},
-#line 1079 "effective_tld_names.gperf"
-      {"fukushima.jp", 2},
-#line 1427 "effective_tld_names.gperf"
-      {"huissier-justice.fr", 0},
-#line 2642 "effective_tld_names.gperf"
-      {"press.aero", 0},
-#line 2774 "effective_tld_names.gperf"
-      {"roma.museum", 0},
-#line 3297 "effective_tld_names.gperf"
-      {"vega.no", 0},
-#line 2681 "effective_tld_names.gperf"
-      {"publ.pt", 0},
-#line 3572 "effective_tld_names.gperf"
-      {"xn--srfold-bya.no", 0},
-#line 1617 "effective_tld_names.gperf"
-      {"kchr.ru", 0},
-#line 1707 "effective_tld_names.gperf"
-      {"la-spezia.it", 0},
-#line 3286 "effective_tld_names.gperf"
-      {"vardo.no", 0},
-#line 3588 "effective_tld_names.gperf"
-      {"xn--uc0ay4a.hk", 0},
-#line 3578 "effective_tld_names.gperf"
-      {"xn--tjme-hra.no", 0},
-#line 2733 "effective_tld_names.gperf"
-      {"reklam.hu", 0},
-#line 3519 "effective_tld_names.gperf"
-      {"xn--muost-0qa.no", 0},
-#line 1611 "effective_tld_names.gperf"
-      {"kaszuby.pl", 0},
-#line 3307 "effective_tld_names.gperf"
-      {"versailles.museum", 0},
-#line 3194 "effective_tld_names.gperf"
-      {"tuva.ru", 0},
-#line 3413 "effective_tld_names.gperf"
-      {"xn--aroport-bya.ci", 0},
-#line 2542 "effective_tld_names.gperf"
-      {"pila.pl", 0},
-#line 2717 "effective_tld_names.gperf"
-      {"ravenna.it", 0},
-#line 3528 "effective_tld_names.gperf"
-      {"xn--oppegrd-ixa.no", 0},
-#line 2791 "effective_tld_names.gperf"
-      {"russia.museum", 0},
-#line 2768 "effective_tld_names.gperf"
-      {"roan.no", 0},
-#line 2709 "effective_tld_names.gperf"
-      {"railroad.museum", 0},
-#line 1600 "effective_tld_names.gperf"
-      {"kamchatka.ru", 0},
-#line 713 "effective_tld_names.gperf"
-      {"cosenza.it", 0},
-#line 1035 "effective_tld_names.gperf"
-      {"florida.museum", 0},
-#line 3534 "effective_tld_names.gperf"
-      {"xn--rde-ula.no", 0},
-#line 1675 "effective_tld_names.gperf"
-      {"kristiansund.no", 0},
-#line 773 "effective_tld_names.gperf"
-      {"dgca.aero", 0},
-#line 3095 "effective_tld_names.gperf"
-      {"telekommunikation.museum", 0},
-#line 3192 "effective_tld_names.gperf"
-      {"turin.it", 0},
-#line 3332 "effective_tld_names.gperf"
-      {"village.museum", 0},
-#line 2640 "effective_tld_names.gperf"
-      {"preservation.museum", 0},
-#line 3484 "effective_tld_names.gperf"
-      {"xn--krehamn-dxa.no", 0},
-#line 1650 "effective_tld_names.gperf"
-      {"koebenhavn.museum", 0},
-#line 369 "effective_tld_names.gperf"
-      {"boleslawiec.pl", 0},
-#line 3551 "effective_tld_names.gperf"
-      {"xn--sandnessjen-ogb.no", 0},
-#line 1557 "effective_tld_names.gperf"
-      {"jessheim.no", 0},
-#line 391 "effective_tld_names.gperf"
-      {"british-library.uk", 1},
-#line 2704 "effective_tld_names.gperf"
-      {"radom.pl", 0},
-#line 2758 "effective_tld_names.gperf"
-      {"rissa.no", 0},
-#line 393 "effective_tld_names.gperf"
-      {"britishcolumbia.museum", 0},
-#line 2744 "effective_tld_names.gperf"
-      {"resistance.museum", 0},
-#line 3494 "effective_tld_names.gperf"
-      {"xn--ldingen-q1a.no", 0},
-#line 1562 "effective_tld_names.gperf"
-      {"jewishart.museum", 0},
-#line 2547 "effective_tld_names.gperf"
-      {"pisz.pl", 0},
-#line 2565 "effective_tld_names.gperf"
-      {"pol.dz", 0},
-#line 3275 "effective_tld_names.gperf"
-      {"vaga.no", 0},
-#line 3148 "effective_tld_names.gperf"
-      {"touch.museum", 0},
-#line 3465 "effective_tld_names.gperf"
-      {"xn--hmmrfeasta-s4ac.no", 0},
-#line 2908 "effective_tld_names.gperf"
-      {"shop.pl", 0},
-#line 3188 "effective_tld_names.gperf"
-      {"tula.ru", 0},
-#line 3078 "effective_tld_names.gperf"
-      {"tana.no", 0},
-#line 1674 "effective_tld_names.gperf"
-      {"kristiansand.no", 0},
-#line 1788 "effective_tld_names.gperf"
-      {"lowicz.pl", 0},
-#line 3571 "effective_tld_names.gperf"
-      {"xn--sr-varanger-ggb.no", 0},
-#line 2736 "effective_tld_names.gperf"
-      {"rendalen.no", 0},
-#line 3599 "effective_tld_names.gperf"
-      {"xn--vre-eiker-k8a.no", 0},
-#line 3112 "effective_tld_names.gperf"
-      {"tinn.no", 0},
-#line 3080 "effective_tld_names.gperf"
-      {"tank.museum", 0},
-#line 3098 "effective_tld_names.gperf"
-      {"terni.it", 0},
-#line 1693 "effective_tld_names.gperf"
-      {"kvam.no", 0},
-#line 2714 "effective_tld_names.gperf"
-      {"rana.no", 0},
-#line 3182 "effective_tld_names.gperf"
-      {"trustee.museum", 0},
-#line 1598 "effective_tld_names.gperf"
-      {"kalmykia.ru", 0},
-#line 2797 "effective_tld_names.gperf"
-      {"rzeszow.pl", 0},
-#line 1603 "effective_tld_names.gperf"
-      {"karasjok.no", 0},
-#line 3109 "effective_tld_names.gperf"
-      {"time.no", 0},
-#line 3468 "effective_tld_names.gperf"
-      {"xn--holtlen-hxa.no", 0},
-#line 2507 "effective_tld_names.gperf"
-      {"passenger-association.aero", 0},
-#line 1702 "effective_tld_names.gperf"
-      {"kyoto.jp", 2},
-#line 3279 "effective_tld_names.gperf"
-      {"valer.hedmark.no", 0},
-#line 2319 "effective_tld_names.gperf"
-      {"olkusz.pl", 0},
-#line 3159 "effective_tld_names.gperf"
-      {"trading.aero", 0},
-#line 3274 "effective_tld_names.gperf"
-      {"vadso.no", 0},
-#line 2539 "effective_tld_names.gperf"
-      {"photography.museum", 0},
-#line 1725 "effective_tld_names.gperf"
-      {"laspezia.it", 0},
-#line 3108 "effective_tld_names.gperf"
-      {"time.museum", 0},
-#line 3487 "effective_tld_names.gperf"
-      {"xn--kvfjord-nxa.no", 0},
-#line 3374 "effective_tld_names.gperf"
-      {"watch-and-clock.museum", 0},
-#line 1649 "effective_tld_names.gperf"
-      {"kochi.jp", 2},
-#line 2821 "effective_tld_names.gperf"
-      {"sande.vestfold.no", 0},
-#line 3023 "effective_tld_names.gperf"
-      {"stjordalshalsen.no", 0},
-#line 3176 "effective_tld_names.gperf"
-      {"trogstad.no", 0},
-#line 2779 "effective_tld_names.gperf"
-      {"rost.no", 0},
-#line 749 "effective_tld_names.gperf"
-      {"daegu.kr", 0},
-#line 3334 "effective_tld_names.gperf"
-      {"vinnica.ua", 0},
-#line 2313 "effective_tld_names.gperf"
-      {"okayama.jp", 2},
-#line 3100 "effective_tld_names.gperf"
-      {"test.ru", 0},
-#line 2739 "effective_tld_names.gperf"
-      {"repbody.aero", 0},
-#line 3046 "effective_tld_names.gperf"
-      {"sumy.ua", 0},
-#line 2715 "effective_tld_names.gperf"
-      {"randaberg.no", 0},
-#line 508 "effective_tld_names.gperf"
-      {"city.kyoto.jp", 1},
-#line 3469 "effective_tld_names.gperf"
-      {"xn--hpmir-xqa.no", 0},
-#line 3302 "effective_tld_names.gperf"
-      {"verbania.it", 0},
-#line 1720 "effective_tld_names.gperf"
-      {"lapy.pl", 0},
-#line 3350 "effective_tld_names.gperf"
-      {"vologda.ru", 0},
-#line 1639 "effective_tld_names.gperf"
-      {"kitakyushu.jp", 2},
-#line 3201 "effective_tld_names.gperf"
-      {"tver.ru", 0},
-#line 1677 "effective_tld_names.gperf"
-      {"krokstadelva.no", 0},
-#line 3170 "effective_tld_names.gperf"
-      {"tree.museum", 0},
-#line 2909 "effective_tld_names.gperf"
-      {"show.aero", 0},
-#line 3441 "effective_tld_names.gperf"
-      {"xn--drbak-wua.no", 0},
-#line 3240 "effective_tld_names.gperf"
-      {"upow.gov.pl", 0},
-#line 3533 "effective_tld_names.gperf"
-      {"xn--rdal-poa.no", 0},
-#line 1662 "effective_tld_names.gperf"
-      {"konskowola.pl", 0},
-#line 3110 "effective_tld_names.gperf"
-      {"timekeeping.museum", 0},
-#line 1663 "effective_tld_names.gperf"
-      {"konyvelo.hu", 0},
-#line 2703 "effective_tld_names.gperf"
-      {"rade.no", 0},
-#line 2730 "effective_tld_names.gperf"
-      {"reggio-emilia.it", 0},
-#line 2939 "effective_tld_names.gperf"
-      {"skydiving.aero", 0},
-#line 102 "effective_tld_names.gperf"
-      {"air-traffic-control.aero", 0},
-#line 3003 "effective_tld_names.gperf"
-      {"stalowa-wola.pl", 0},
-#line 3077 "effective_tld_names.gperf"
-      {"tambov.ru", 0},
-#line 3175 "effective_tld_names.gperf"
-      {"troandin.no", 0},
-#line 1616 "effective_tld_names.gperf"
-      {"kazimierz-dolny.pl", 0},
-#line 776 "effective_tld_names.gperf"
-      {"discovery.museum", 0},
-#line 2579 "effective_tld_names.gperf"
-      {"posts-and-telecommunications.museum", 0},
-#line 1664 "effective_tld_names.gperf"
-      {"kopervik.no", 0},
-#line 3115 "effective_tld_names.gperf"
-      {"tjeldsund.no", 0},
-#line 2641 "effective_tld_names.gperf"
-      {"presidio.museum", 0},
-#line 3425 "effective_tld_names.gperf"
-      {"xn--bievt-0qa.no", 0},
-#line 3470 "effective_tld_names.gperf"
-      {"xn--hyanger-q1a.no", 0},
-#line 1630 "effective_tld_names.gperf"
-      {"khmelnitskiy.ua", 0},
-#line 1642 "effective_tld_names.gperf"
-      {"klodzko.pl", 0},
-#line 1634 "effective_tld_names.gperf"
-      {"kids.us", 0},
-#line 3445 "effective_tld_names.gperf"
-      {"xn--fjord-lra.no", 0},
-#line 3283 "effective_tld_names.gperf"
-      {"vang.no", 0},
-#line 3153 "effective_tld_names.gperf"
-      {"tozsde.hu", 0},
-#line 3205 "effective_tld_names.gperf"
-      {"tychy.pl", 0},
-#line 3419 "effective_tld_names.gperf"
-      {"xn--bdddj-mrabd.no", 0},
-#line 3335 "effective_tld_names.gperf"
-      {"virginia.museum", 0},
+#line 2225 "effective_tld_names.gperf"
+      {"net.th", 0},
+#line 1442 "effective_tld_names.gperf"
+      {"house.museum", 0},
 #line 1626 "effective_tld_names.gperf"
-      {"khabarovsk.ru", 0},
-#line 3116 "effective_tld_names.gperf"
-      {"tjome.no", 0},
-#line 3082 "effective_tld_names.gperf"
-      {"targi.pl", 0},
-#line 1676 "effective_tld_names.gperf"
-      {"krodsherad.no", 0},
-#line 2754 "effective_tld_names.gperf"
-      {"ringerike.no", 0},
-#line 3488 "effective_tld_names.gperf"
-      {"xn--kvitsy-fya.no", 0},
-#line 1640 "effective_tld_names.gperf"
-      {"klabu.no", 0},
-#line 3466 "effective_tld_names.gperf"
-      {"xn--hnefoss-q1a.no", 0},
-#line 3278 "effective_tld_names.gperf"
-      {"vaksdal.no", 0},
-#line 1660 "effective_tld_names.gperf"
-      {"kongsvinger.no", 0},
-#line 2570 "effective_tld_names.gperf"
-      {"pomorze.pl", 0},
-#line 3597 "effective_tld_names.gperf"
-      {"xn--vler-qoa.hedmark.no", 0},
-#line 3147 "effective_tld_names.gperf"
-      {"tottori.jp", 2},
-#line 3524 "effective_tld_names.gperf"
-      {"xn--nvuotna-hwa.no", 0},
-#line 3526 "effective_tld_names.gperf"
-      {"xn--od0alg.hk", 0},
-#line 2749 "effective_tld_names.gperf"
-      {"rieti.it", 0},
-#line 2580 "effective_tld_names.gperf"
-      {"potenza.it", 0},
-#line 3439 "effective_tld_names.gperf"
-      {"xn--davvenjrga-y4a.no", 0},
-#line 1633 "effective_tld_names.gperf"
-      {"kids.museum", 0},
-#line 3459 "effective_tld_names.gperf"
-      {"xn--h1aegh.museum", 0},
-#line 3596 "effective_tld_names.gperf"
-      {"xn--vgsy-qoa0j.no", 0},
-#line 3517 "effective_tld_names.gperf"
-      {"xn--msy-ula0h.no", 0},
-#line 3151 "effective_tld_names.gperf"
-      {"town.museum", 0},
-#line 2750 "effective_tld_names.gperf"
-      {"riik.ee", 0},
-#line 3352 "effective_tld_names.gperf"
-      {"voss.no", 0},
-#line 1658 "effective_tld_names.gperf"
-      {"komvux.se", 0},
-#line 3339 "effective_tld_names.gperf"
-      {"vlaanderen.museum", 0},
-#line 3452 "effective_tld_names.gperf"
-      {"xn--gildeskl-g0a.no", 0},
-#line 3101 "effective_tld_names.gperf"
-      {"texas.museum", 0},
-#line 3341 "effective_tld_names.gperf"
-      {"vladimir.ru", 0},
-#line 3105 "effective_tld_names.gperf"
-      {"tgory.pl", 0},
-#line 3280 "effective_tld_names.gperf"
-      {"valer.ostfold.no", 0},
-#line 3451 "effective_tld_names.gperf"
-      {"xn--ggaviika-8ya47h.no", 0},
-#line 1614 "effective_tld_names.gperf"
-      {"kawasaki.jp", 2},
-#line 1596 "effective_tld_names.gperf"
-      {"kagoshima.jp", 2},
-#line 2822 "effective_tld_names.gperf"
-      {"sande.xn--mre-og-romsdal-qqb.no", 0},
-#line 1364 "effective_tld_names.gperf"
-      {"hattfjelldal.no", 0},
-#line 2203 "effective_tld_names.gperf"
-      {"newmexico.museum", 0},
-#line 1549 "effective_tld_names.gperf"
-      {"jaworzno.pl", 0},
-#line 774 "effective_tld_names.gperf"
-      {"dielddanuorri.no", 0},
-#line 2667 "effective_tld_names.gperf"
-      {"prof.pr", 0},
-#line 1602 "effective_tld_names.gperf"
-      {"karasjohka.no", 0},
-#line 3193 "effective_tld_names.gperf"
-      {"turystyka.pl", 0},
-#line 512 "effective_tld_names.gperf"
-      {"city.osaka.jp", 1},
-#line 3099 "effective_tld_names.gperf"
-      {"ternopil.ua", 0},
-#line 3180 "effective_tld_names.gperf"
-      {"trondheim.no", 0},
-#line 2615 "effective_tld_names.gperf"
-      {"pref.kyoto.jp", 1},
-#line 3605 "effective_tld_names.gperf"
-      {"xn--ystre-slidre-ujb.no", 0},
-#line 1635 "effective_tld_names.gperf"
-      {"kiev.ua", 0},
-#line 3367 "effective_tld_names.gperf"
-      {"walbrzych.pl", 0},
-#line 3420 "effective_tld_names.gperf"
-      {"xn--bearalvhki-y4a.no", 0},
-#line 3464 "effective_tld_names.gperf"
-      {"xn--hgebostad-g3a.no", 0},
-#line 1597 "effective_tld_names.gperf"
       {"kalisz.pl", 0},
-#line 781 "effective_tld_names.gperf"
-      {"dlugoleka.pl", 0},
-#line 3565 "effective_tld_names.gperf"
-      {"xn--sndre-land-0cb.no", 0},
-#line 2718 "effective_tld_names.gperf"
-      {"rawa-maz.pl", 0},
-#line 3135 "effective_tld_names.gperf"
-      {"tochigi.jp", 2},
-#line 3434 "effective_tld_names.gperf"
-      {"xn--btsfjord-9za.no", 0},
-#line 2732 "effective_tld_names.gperf"
-      {"reggioemilia.it", 0},
-#line 2609 "effective_tld_names.gperf"
-      {"pref.iwate.jp", 1},
-#line 3343 "effective_tld_names.gperf"
-      {"vlog.br", 0},
-#line 1647 "effective_tld_names.gperf"
-      {"kobe.jp", 2},
-#line 3592 "effective_tld_names.gperf"
-      {"xn--vegrshei-c0a.no", 0},
-#line 3535 "effective_tld_names.gperf"
-      {"xn--rdy-0nab.no", 0},
-#line 3461 "effective_tld_names.gperf"
-      {"xn--hcesuolo-7ya35b.no", 0},
-#line 511 "effective_tld_names.gperf"
-      {"city.okayama.jp", 1},
-#line 3520 "effective_tld_names.gperf"
-      {"xn--mxtq1m.hk", 0},
-#line 3489 "effective_tld_names.gperf"
-      {"xn--kvnangen-k0a.no", 0},
-#line 1683 "effective_tld_names.gperf"
-      {"kunstsammlung.museum", 0},
-#line 2614 "effective_tld_names.gperf"
-      {"pref.kumamoto.jp", 1},
-#line 2603 "effective_tld_names.gperf"
-      {"pref.gunma.jp", 1},
-#line 3521 "effective_tld_names.gperf"
-      {"xn--nmesjevuemie-tcba.no", 0},
-#line 2634 "effective_tld_names.gperf"
-      {"pref.tottori.jp", 1},
+#line 1912 "effective_tld_names.gperf"
+      {"media.aero", 0},
+#line 3471 "effective_tld_names.gperf"
+      {"xn--avery-yua.no", 0},
+#line 1018 "effective_tld_names.gperf"
+      {"field.museum", 0},
+#line 1869 "effective_tld_names.gperf"
+      {"marburg.museum", 0},
 #line 3495 "effective_tld_names.gperf"
-      {"xn--leagaviika-52b.no", 0},
-#line 3518 "effective_tld_names.gperf"
-      {"xn--mtta-vrjjat-k7af.no", 0},
-#line 3527 "effective_tld_names.gperf"
-      {"xn--od0aq3b.hk", 0},
-#line 2596 "effective_tld_names.gperf"
-      {"pref.aomori.jp", 1},
-#line 502 "effective_tld_names.gperf"
-      {"city.fukuoka.jp", 1},
-#line 516 "effective_tld_names.gperf"
-      {"city.shizuoka.jp", 1},
-#line 3411 "effective_tld_names.gperf"
-      {"xn--9dbhblg6di.museum", 0},
-#line 515 "effective_tld_names.gperf"
-      {"city.sendai.jp", 1},
-#line 507 "effective_tld_names.gperf"
-      {"city.kobe.jp", 1},
-#line 2598 "effective_tld_names.gperf"
-      {"pref.ehime.jp", 1},
-#line 3454 "effective_tld_names.gperf"
-      {"xn--gjvik-wua.no", 0},
-#line 3426 "effective_tld_names.gperf"
-      {"xn--bjarky-fya.no", 0},
-#line 3323 "effective_tld_names.gperf"
-      {"vibo-valentia.it", 0},
-#line 2635 "effective_tld_names.gperf"
-      {"pref.toyama.jp", 1},
-#line 2731 "effective_tld_names.gperf"
-      {"reggiocalabria.it", 0},
-#line 2069 "effective_tld_names.gperf"
-      {"national-library-scotland.uk", 1},
-#line 3285 "effective_tld_names.gperf"
-      {"vanylven.no", 0},
-#line 3137 "effective_tld_names.gperf"
-      {"tokushima.jp", 2},
-#line 2630 "effective_tld_names.gperf"
-      {"pref.shimane.jp", 1},
-#line 509 "effective_tld_names.gperf"
-      {"city.nagoya.jp", 1},
-#line 3353 "effective_tld_names.gperf"
-      {"vossevangen.no", 0},
-#line 2729 "effective_tld_names.gperf"
-      {"reggio-calabria.it", 0},
-#line 2599 "effective_tld_names.gperf"
-      {"pref.fukui.jp", 1},
-#line 3342 "effective_tld_names.gperf"
-      {"vladivostok.ru", 0},
-#line 3576 "effective_tld_names.gperf"
-      {"xn--stjrdalshalsen-sqb.no", 0},
-#line 2626 "effective_tld_names.gperf"
-      {"pref.osaka.jp", 1},
-#line 2535 "effective_tld_names.gperf"
-      {"philadelphia.museum", 0},
-#line 2606 "effective_tld_names.gperf"
-      {"pref.hyogo.jp", 1},
-#line 2629 "effective_tld_names.gperf"
-      {"pref.shiga.jp", 1},
-#line 3138 "effective_tld_names.gperf"
-      {"tokyo.jp", 2},
-#line 514 "effective_tld_names.gperf"
-      {"city.sapporo.jp", 1},
-#line 1610 "effective_tld_names.gperf"
-      {"kartuzy.pl", 0},
-#line 2616 "effective_tld_names.gperf"
-      {"pref.mie.jp", 1},
-#line 3501 "effective_tld_names.gperf"
-      {"xn--loabt-0qa.no", 0},
-#line 3340 "effective_tld_names.gperf"
-      {"vladikavkaz.ru", 0},
-#line 510 "effective_tld_names.gperf"
-      {"city.niigata.jp", 1},
-#line 2595 "effective_tld_names.gperf"
-      {"pref.akita.jp", 1},
-#line 501 "effective_tld_names.gperf"
-      {"city.chiba.jp", 1},
-#line 479 "effective_tld_names.gperf"
-      {"chesapeakebay.museum", 0},
-#line 3415 "effective_tld_names.gperf"
-      {"xn--aurskog-hland-jnb.no", 0},
-#line 3102 "effective_tld_names.gperf"
-      {"textile.museum", 0},
-#line 513 "effective_tld_names.gperf"
-      {"city.saitama.jp", 1},
-#line 3327 "effective_tld_names.gperf"
-      {"vicenza.it", 0},
-#line 2623 "effective_tld_names.gperf"
-      {"pref.oita.jp", 1},
-#line 2607 "effective_tld_names.gperf"
-      {"pref.ibaraki.jp", 1},
-#line 2624 "effective_tld_names.gperf"
-      {"pref.okayama.jp", 1},
-#line 3463 "effective_tld_names.gperf"
-      {"xn--hery-ira.xn--mre-og-romsdal-qqb.no", 0},
-#line 2536 "effective_tld_names.gperf"
-      {"philadelphiaarea.museum", 0},
-#line 2613 "effective_tld_names.gperf"
-      {"pref.kochi.jp", 1},
-#line 584 "effective_tld_names.gperf"
-      {"colonialwilliamsburg.museum", 0},
-#line 2600 "effective_tld_names.gperf"
-      {"pref.fukuoka.jp", 1},
-#line 2631 "effective_tld_names.gperf"
-      {"pref.shizuoka.jp", 1},
-#line 2621 "effective_tld_names.gperf"
-      {"pref.nara.jp", 1},
-#line 3456 "effective_tld_names.gperf"
-      {"xn--gmq050i.hk", 0},
-#line 517 "effective_tld_names.gperf"
-      {"city.yokohama.jp", 1},
-#line 3437 "effective_tld_names.gperf"
-      {"xn--correios-e-telecomunicaes-ghc29a.museum", 0},
-#line 3438 "effective_tld_names.gperf"
-      {"xn--czrw28b.tw", 0},
-#line 2617 "effective_tld_names.gperf"
-      {"pref.miyagi.jp", 1},
-#line 2619 "effective_tld_names.gperf"
-      {"pref.nagano.jp", 1},
-#line 2627 "effective_tld_names.gperf"
-      {"pref.saga.jp", 1},
-#line 2632 "effective_tld_names.gperf"
-      {"pref.tochigi.jp", 1},
-#line 1656 "effective_tld_names.gperf"
-      {"kommunalforbund.se", 0},
-#line 3087 "effective_tld_names.gperf"
-      {"taxi.aero", 0},
-#line 506 "effective_tld_names.gperf"
-      {"city.kitakyushu.jp", 1},
-#line 2622 "effective_tld_names.gperf"
-      {"pref.niigata.jp", 1},
-#line 2597 "effective_tld_names.gperf"
-      {"pref.chiba.jp", 1},
-#line 2756 "effective_tld_names.gperf"
-      {"riodejaneiro.museum", 0},
-#line 2601 "effective_tld_names.gperf"
-      {"pref.fukushima.jp", 1},
-#line 2628 "effective_tld_names.gperf"
-      {"pref.saitama.jp", 1},
-#line 2625 "effective_tld_names.gperf"
-      {"pref.okinawa.jp", 1},
-#line 3537 "effective_tld_names.gperf"
-      {"xn--rhkkervju-01af.no", 0},
-#line 3598 "effective_tld_names.gperf"
-      {"xn--vler-qoa.xn--stfold-9xa.no", 0},
-#line 503 "effective_tld_names.gperf"
-      {"city.hiroshima.jp", 1},
-#line 2602 "effective_tld_names.gperf"
-      {"pref.gifu.jp", 1},
-#line 2633 "effective_tld_names.gperf"
-      {"pref.tokushima.jp", 1},
-#line 2594 "effective_tld_names.gperf"
-      {"pref.aichi.jp", 1},
-#line 2618 "effective_tld_names.gperf"
-      {"pref.miyazaki.jp", 1},
-#line 2605 "effective_tld_names.gperf"
-      {"pref.hokkaido.jp", 1},
-#line 505 "effective_tld_names.gperf"
-      {"city.kawasaki.jp", 1},
+      {"xn--dnna-gra.no", 0},
+#line 3695 "effective_tld_names.gperf"
+      {"zachpomor.pl", 0},
+#line 1349 "effective_tld_names.gperf"
+      {"gu.us", 0},
+#line 557 "effective_tld_names.gperf"
+      {"co.gg", 0},
+#line 350 "effective_tld_names.gperf"
+      {"biz.ki", 0},
+#line 1724 "effective_tld_names.gperf"
+      {"kvinesdal.no", 0},
+#line 1865 "effective_tld_names.gperf"
+      {"mansion.museum", 0},
+#line 1866 "effective_tld_names.gperf"
+      {"mansions.museum", 0},
+#line 2329 "effective_tld_names.gperf"
+      {"nuremberg.museum", 0},
+#line 1509 "effective_tld_names.gperf"
+      {"info.ht", 0},
+#line 466 "effective_tld_names.gperf"
+      {"cc", 0},
+#line 820 "effective_tld_names.gperf"
+      {"ec", 0},
+#line 32 "effective_tld_names.gperf"
+      {"ac", 0},
+#line 2020 "effective_tld_names.gperf"
+      {"monticello.museum", 0},
+#line 2208 "effective_tld_names.gperf"
+      {"net.ph", 0},
+#line 1162 "effective_tld_names.gperf"
+      {"gmina.pl", 0},
+#line 1404 "effective_tld_names.gperf"
+      {"heroy.nordland.no", 0},
+#line 617 "effective_tld_names.gperf"
+      {"com.cn", 0},
+#line 843 "effective_tld_names.gperf"
+      {"edu.cn", 0},
 #line 2637 "effective_tld_names.gperf"
-      {"pref.yamagata.jp", 1},
-#line 3482 "effective_tld_names.gperf"
-      {"xn--kranghke-b0a.no", 0},
-#line 2620 "effective_tld_names.gperf"
-      {"pref.nagasaki.jp", 1},
+      {"prd.mg", 0},
+#line 567 "effective_tld_names.gperf"
+      {"co.lc", 0},
+#line 2609 "effective_tld_names.gperf"
+      {"pol.dz", 0},
+#line 3452 "effective_tld_names.gperf"
+      {"works.aero", 0},
+#line 1922 "effective_tld_names.gperf"
+      {"meloy.no", 0},
+#line 34 "effective_tld_names.gperf"
+      {"ac.at", 0},
+#line 1176 "effective_tld_names.gperf"
+      {"gob.cl", 0},
+#line 2535 "effective_tld_names.gperf"
+      {"padova.it", 0},
+#line 1156 "effective_tld_names.gperf"
+      {"glass.museum", 0},
+#line 1113 "effective_tld_names.gperf"
+      {"games.hu", 0},
+#line 1218 "effective_tld_names.gperf"
+      {"gov.cl", 0},
+#line 1220 "effective_tld_names.gperf"
+      {"gov.cn", 0},
+#line 1986 "effective_tld_names.gperf"
+      {"mining.museum", 0},
+#line 2270 "effective_tld_names.gperf"
+      {"nm.us", 0},
+#line 1112 "effective_tld_names.gperf"
+      {"game.tw", 0},
+#line 439 "effective_tld_names.gperf"
+      {"ca.us", 0},
+#line 1460 "effective_tld_names.gperf"
+      {"ia.us", 0},
+#line 1217 "effective_tld_names.gperf"
+      {"gov.cd", 0},
+#line 2121 "effective_tld_names.gperf"
+      {"nc", 0},
+#line 51 "effective_tld_names.gperf"
+      {"ac.pr", 0},
+#line 1992 "effective_tld_names.gperf"
+      {"mjondalen.no", 0},
+#line 3710 "effective_tld_names.gperf"
+      {"zt.ua", 0},
+#line 33 "effective_tld_names.gperf"
+      {"ac.ae", 0},
+#line 42 "effective_tld_names.gperf"
+      {"ac.ir", 0},
+#line 2563 "effective_tld_names.gperf"
+      {"per.sg", 0},
+#line 1312 "effective_tld_names.gperf"
+      {"gratangen.no", 0},
+#line 753 "effective_tld_names.gperf"
+      {"cyber.museum", 0},
+#line 3643 "effective_tld_names.gperf"
+      {"xn--trna-woa.no", 0},
+#line 1108 "effective_tld_names.gperf"
+      {"ga.us", 0},
+#line 1118 "effective_tld_names.gperf"
+      {"gateway.museum", 0},
+#line 2929 "effective_tld_names.gperf"
+      {"se.net", 0},
+#line 1681 "effective_tld_names.gperf"
+      {"koenig.ru", 0},
+#line 226 "effective_tld_names.gperf"
+      {"asso.nc", 0},
+#line 418 "effective_tld_names.gperf"
+      {"budejju.no", 0},
+#line 1709 "effective_tld_names.gperf"
+      {"kuban.ru", 0},
+#line 1637 "effective_tld_names.gperf"
+      {"karmoy.no", 0},
+#line 1669 "effective_tld_names.gperf"
+      {"klabu.no", 0},
+#line 281 "effective_tld_names.gperf"
+      {"ballooning.aero", 0},
+#line 2940 "effective_tld_names.gperf"
+      {"services.aero", 0},
+#line 2862 "effective_tld_names.gperf"
+      {"salerno.it", 0},
+#line 1700 "effective_tld_names.gperf"
+      {"kragero.no", 0},
+#line 3709 "effective_tld_names.gperf"
+      {"zp.ua", 0},
+#line 581 "effective_tld_names.gperf"
+      {"co.tj", 0},
+#line 35 "effective_tld_names.gperf"
+      {"ac.be", 0},
+#line 1059 "effective_tld_names.gperf"
+      {"foggia.it", 0},
+#line 2132 "effective_tld_names.gperf"
+      {"nedre-eiker.no", 0},
+#line 1510 "effective_tld_names.gperf"
+      {"info.hu", 0},
+#line 454 "effective_tld_names.gperf"
+      {"cartoonart.museum", 0},
+#line 46 "effective_tld_names.gperf"
+      {"ac.me", 0},
+#line 1481 "effective_tld_names.gperf"
+      {"imageandsound.museum", 0},
+#line 1172 "effective_tld_names.gperf"
+      {"go.tj", 0},
+#line 2159 "effective_tld_names.gperf"
+      {"net.cn", 0},
+#line 3570 "effective_tld_names.gperf"
+      {"xn--mlatvuopmi-s4a.no", 0},
+#line 2527 "effective_tld_names.gperf"
+      {"p.bg", 0},
+#line 41 "effective_tld_names.gperf"
+      {"ac.in", 0},
+#line 52 "effective_tld_names.gperf"
+      {"ac.rs", 0},
+#line 3285 "effective_tld_names.gperf"
+      {"um.gov.pl", 0},
+#line 3058 "effective_tld_names.gperf"
+      {"stat.no", 0},
+#line 332 "effective_tld_names.gperf"
+      {"bialystok.pl", 0},
+#line 1690 "effective_tld_names.gperf"
+      {"konin.pl", 0},
+#line 419 "effective_tld_names.gperf"
+      {"building.museum", 0},
+#line 544 "effective_tld_names.gperf"
+      {"cn.ua", 0},
+#line 1489 "effective_tld_names.gperf"
+      {"in.ua", 0},
+#line 155 "effective_tld_names.gperf"
+      {"anthro.museum", 0},
+#line 1717 "effective_tld_names.gperf"
+      {"kutno.pl", 0},
+#line 44 "effective_tld_names.gperf"
+      {"ac.kr", 0},
+#line 1679 "effective_tld_names.gperf"
+      {"koebenhavn.museum", 0},
+#line 2931 "effective_tld_names.gperf"
+      {"sebastopol.ua", 0},
+#line 40 "effective_tld_names.gperf"
+      {"ac.im", 0},
+#line 1184 "effective_tld_names.gperf"
+      {"gok.pk", 0},
+#line 62 "effective_tld_names.gperf"
+      {"aca.pro", 0},
+#line 412 "effective_tld_names.gperf"
+      {"bryansk.ru", 0},
+#line 1343 "effective_tld_names.gperf"
+      {"gs.tr.no", 0},
+#line 2131 "effective_tld_names.gperf"
+      {"nebraska.museum", 0},
 #line 2610 "effective_tld_names.gperf"
-      {"pref.kagawa.jp", 1},
-#line 2636 "effective_tld_names.gperf"
-      {"pref.wakayama.jp", 1},
-#line 2608 "effective_tld_names.gperf"
-      {"pref.ishikawa.jp", 1},
-#line 2611 "effective_tld_names.gperf"
-      {"pref.kagoshima.jp", 1},
-#line 2612 "effective_tld_names.gperf"
-      {"pref.kanagawa.jp", 1},
-#line 2604 "effective_tld_names.gperf"
-      {"pref.hiroshima.jp", 1},
-#line 2639 "effective_tld_names.gperf"
-      {"pref.yamanashi.jp", 1},
-#line 2638 "effective_tld_names.gperf"
-      {"pref.yamaguchi.jp", 1},
+      {"pol.ht", 0},
+#line 47 "effective_tld_names.gperf"
+      {"ac.mu", 0},
+#line 1373 "effective_tld_names.gperf"
+      {"halsa.no", 0},
+#line 1111 "effective_tld_names.gperf"
+      {"galsa.no", 0},
+#line 295 "effective_tld_names.gperf"
+      {"basel.museum", 0},
+#line 1100 "effective_tld_names.gperf"
+      {"furniture.museum", 0},
+#line 2737 "effective_tld_names.gperf"
+      {"q.bg", 0},
+#line 1195 "effective_tld_names.gperf"
+      {"gouv.ht", 0},
+#line 43 "effective_tld_names.gperf"
+      {"ac.jp", 0},
+#line 1921 "effective_tld_names.gperf"
+      {"melhus.no", 0},
+#line 1644 "effective_tld_names.gperf"
+      {"kazan.ru", 0},
+#line 2012 "effective_tld_names.gperf"
+      {"modelling.aero", 0},
+#line 50 "effective_tld_names.gperf"
+      {"ac.pa", 0},
+#line 2332 "effective_tld_names.gperf"
+      {"ny.us", 0},
+#line 3676 "effective_tld_names.gperf"
+      {"yamanashi.jp", 2},
+#line 931 "effective_tld_names.gperf"
+      {"eidfjord.no", 0},
+#line 1406 "effective_tld_names.gperf"
+      {"hi.us", 0},
+#line 486 "effective_tld_names.gperf"
+      {"chernigov.ua", 0},
+#line 53 "effective_tld_names.gperf"
+      {"ac.ru", 0},
+#line 3517 "effective_tld_names.gperf"
+      {"xn--hery-ira.nordland.no", 0},
+#line 522 "effective_tld_names.gperf"
+      {"city.saitama.jp", 1},
+#line 1858 "effective_tld_names.gperf"
+      {"malbork.pl", 0},
+#line 3589 "effective_tld_names.gperf"
+      {"xn--osyro-wua.no", 0},
+#line 3440 "effective_tld_names.gperf"
+      {"whaling.museum", 0},
+#line 1694 "effective_tld_names.gperf"
+      {"kostroma.ru", 0},
+#line 1914 "effective_tld_names.gperf"
+      {"media.museum", 0},
+#line 3087 "effective_tld_names.gperf"
+      {"student.aero", 0},
+#line 45 "effective_tld_names.gperf"
+      {"ac.ma", 0},
+#line 3700 "effective_tld_names.gperf"
+      {"zgora.pl", 0},
+#line 1508 "effective_tld_names.gperf"
+      {"info.ec", 0},
+#line 1593 "effective_tld_names.gperf"
+      {"jgora.pl", 0},
+#line 3097 "effective_tld_names.gperf"
+      {"sund.no", 0},
+#line 270 "effective_tld_names.gperf"
+      {"baghdad.museum", 0},
+#line 1342 "effective_tld_names.gperf"
+      {"gs.tm.no", 0},
+#line 1151 "effective_tld_names.gperf"
+      {"gjerstad.no", 0},
+#line 1146 "effective_tld_names.gperf"
+      {"gifu.jp", 2},
+#line 1041 "effective_tld_names.gperf"
+      {"fl.us", 0},
+#line 3068 "effective_tld_names.gperf"
+      {"steigen.no", 0},
+#line 1962 "effective_tld_names.gperf"
+      {"mil.kr", 0},
+#line 209 "effective_tld_names.gperf"
+      {"askvoll.no", 0},
+#line 276 "effective_tld_names.gperf"
+      {"bajddar.no", 0},
+#line 1316 "effective_tld_names.gperf"
+      {"grong.no", 0},
+#line 2685 "effective_tld_names.gperf"
+      {"presidio.museum", 0},
+#line 1716 "effective_tld_names.gperf"
+      {"kustanai.ru", 0},
+#line 3446 "effective_tld_names.gperf"
+      {"windmill.museum", 0},
+#line 616 "effective_tld_names.gperf"
+      {"com.ci", 0},
+#line 842 "effective_tld_names.gperf"
+      {"edu.ci", 0},
+#line 2106 "effective_tld_names.gperf"
+      {"nationalheritage.museum", 0},
+#line 513 "effective_tld_names.gperf"
+      {"city.hu", 0},
+#line 1924 "effective_tld_names.gperf"
+      {"meraker.no", 0},
+#line 1642 "effective_tld_names.gperf"
+      {"kautokeino.no", 0},
+#line 3459 "effective_tld_names.gperf"
+      {"www.ro", 0},
+#line 377 "effective_tld_names.gperf"
+      {"bokn.no", 0},
+#line 1067 "effective_tld_names.gperf"
+      {"fortmissoula.museum", 0},
+#line 1980 "effective_tld_names.gperf"
+      {"milan.it", 0},
+#line 2732 "effective_tld_names.gperf"
+      {"pvt.ge", 0},
+#line 1913 "effective_tld_names.gperf"
+      {"media.hu", 0},
+#line 269 "effective_tld_names.gperf"
+      {"badajoz.museum", 0},
+#line 1704 "effective_tld_names.gperf"
+      {"kristiansund.no", 0},
+#line 94 "effective_tld_names.gperf"
+      {"agrigento.it", 0},
+#line 1045 "effective_tld_names.gperf"
+      {"flatanger.no", 0},
+#line 1867 "effective_tld_names.gperf"
+      {"mantova.it", 0},
+#line 2023 "effective_tld_names.gperf"
+      {"mordovia.ru", 0},
+#line 1935 "effective_tld_names.gperf"
+      {"microlight.aero", 0},
+#line 1959 "effective_tld_names.gperf"
+      {"mil.jo", 0},
+#line 2712 "effective_tld_names.gperf"
+      {"production.aero", 0},
+#line 1088 "effective_tld_names.gperf"
+      {"from.hr", 0},
+#line 3581 "effective_tld_names.gperf"
+      {"xn--nry-yla5g.no", 0},
+#line 2297 "effective_tld_names.gperf"
+      {"norfolk.museum", 0},
+#line 1680 "effective_tld_names.gperf"
+      {"koeln.museum", 0},
+#line 3419 "effective_tld_names.gperf"
+      {"wa.gov.au", 0},
+#line 641 "effective_tld_names.gperf"
+      {"com.kg", 0},
+#line 865 "effective_tld_names.gperf"
+      {"edu.kg", 0},
+#line 107 "effective_tld_names.gperf"
+      {"airguard.museum", 0},
+#line 3071 "effective_tld_names.gperf"
+      {"stjordal.no", 0},
+#line 618 "effective_tld_names.gperf"
+      {"com.co", 0},
+#line 2961 "effective_tld_names.gperf"
+      {"siedlce.pl", 0},
+#line 844 "effective_tld_names.gperf"
+      {"edu.co", 0},
+#line 61 "effective_tld_names.gperf"
+      {"ac.vn", 0},
+#line 2158 "effective_tld_names.gperf"
+      {"net.ci", 0},
+#line 2593 "effective_tld_names.gperf"
+      {"pk", 0},
+#line 162 "effective_tld_names.gperf"
+      {"aoste.it", 0},
+#line 1706 "effective_tld_names.gperf"
+      {"krokstadelva.no", 0},
+#line 2063 "effective_tld_names.gperf"
+      {"mx", 0},
+#line 644 "effective_tld_names.gperf"
+      {"com.ky", 0},
+#line 869 "effective_tld_names.gperf"
+      {"edu.ky", 0},
+#line 1531 "effective_tld_names.gperf"
+      {"int.ci", 0},
+#line 1244 "effective_tld_names.gperf"
+      {"gov.kg", 0},
+#line 3410 "effective_tld_names.gperf"
+      {"vt.it", 0},
+#line 1703 "effective_tld_names.gperf"
+      {"kristiansand.no", 0},
+#line 3347 "effective_tld_names.gperf"
+      {"ve", 2},
+#line 467 "effective_tld_names.gperf"
+      {"cc.na", 0},
+#line 1000 "effective_tld_names.gperf"
+      {"fc.it", 0},
+#line 2004 "effective_tld_names.gperf"
+      {"mo.us", 0},
+#line 494 "effective_tld_names.gperf"
+      {"childrensgarden.museum", 0},
+#line 1895 "effective_tld_names.gperf"
+      {"md.us", 0},
+#line 2250 "effective_tld_names.gperf"
+      {"nh.us", 0},
+#line 2039 "effective_tld_names.gperf"
+      {"ms.us", 0},
+#line 1221 "effective_tld_names.gperf"
+      {"gov.co", 0},
+#line 2043 "effective_tld_names.gperf"
+      {"mt.us", 0},
+#line 3702 "effective_tld_names.gperf"
+      {"zgrad.ru", 0},
+#line 2615 "effective_tld_names.gperf"
+      {"pordenone.it", 0},
+#line 1248 "effective_tld_names.gperf"
+      {"gov.ky", 0},
+#line 3350 "effective_tld_names.gperf"
+      {"vega.no", 0},
+#line 3408 "effective_tld_names.gperf"
+      {"vr.it", 0},
+#line 2278 "effective_tld_names.gperf"
+      {"nom.co", 0},
+#line 3348 "effective_tld_names.gperf"
+      {"ve.it", 0},
+#line 3053 "effective_tld_names.gperf"
+      {"stange.no", 0},
+#line 3055 "effective_tld_names.gperf"
+      {"stargard.pl", 0},
+#line 3311 "effective_tld_names.gperf"
+      {"ut.us", 0},
+#line 1898 "effective_tld_names.gperf"
+      {"me.us", 0},
+#line 3288 "effective_tld_names.gperf"
+      {"union.aero", 0},
+#line 67 "effective_tld_names.gperf"
+      {"act.edu.au", 0},
+#line 2886 "effective_tld_names.gperf"
+      {"sassari.it", 0},
+#line 3665 "effective_tld_names.gperf"
+      {"xn--ygarden-p1a.no", 0},
+#line 3398 "effective_tld_names.gperf"
+      {"vn", 0},
+#line 645 "effective_tld_names.gperf"
+      {"com.kz", 0},
+#line 3412 "effective_tld_names.gperf"
+      {"vu", 0},
+#line 870 "effective_tld_names.gperf"
+      {"edu.kz", 0},
+#line 3549 "effective_tld_names.gperf"
+      {"xn--ldingen-q1a.no", 0},
+#line 2181 "effective_tld_names.gperf"
+      {"net.kg", 0},
+#line 3647 "effective_tld_names.gperf"
+      {"xn--uc0atv.tw", 0},
+#line 3103 "effective_tld_names.gperf"
+      {"suwalki.pl", 0},
+#line 3497 "effective_tld_names.gperf"
+      {"xn--dyry-ira.no", 0},
+#line 2160 "effective_tld_names.gperf"
+      {"net.co", 0},
+#line 1029 "effective_tld_names.gperf"
+      {"firenze.it", 0},
+#line 2050 "effective_tld_names.gperf"
+      {"murmansk.ru", 0},
+#line 1710 "effective_tld_names.gperf"
+      {"kumamoto.jp", 2},
+#line 1249 "effective_tld_names.gperf"
+      {"gov.kz", 0},
+#line 2845 "effective_tld_names.gperf"
+      {"s.bg", 0},
+#line 1532 "effective_tld_names.gperf"
+      {"int.co", 0},
+#line 2184 "effective_tld_names.gperf"
+      {"net.ky", 0},
+#line 1499 "effective_tld_names.gperf"
+      {"indianmarket.museum", 0},
+#line 1394 "effective_tld_names.gperf"
+      {"heimatunduhren.museum", 0},
+#line 1999 "effective_tld_names.gperf"
+      {"mn.us", 0},
+#line 2883 "effective_tld_names.gperf"
+      {"saratov.ru", 0},
+#line 1392 "effective_tld_names.gperf"
+      {"health.museum", 0},
+#line 2964 "effective_tld_names.gperf"
+      {"sigdal.no", 0},
+#line 1880 "effective_tld_names.gperf"
+      {"masfjorden.no", 0},
+#line 161 "effective_tld_names.gperf"
+      {"aosta.it", 0},
+#line 1618 "effective_tld_names.gperf"
+      {"k-uralsk.ru", 0},
+#line 3697 "effective_tld_names.gperf"
+      {"zakopane.pl", 0},
+#line 1572 "effective_tld_names.gperf"
+      {"izhevsk.ru", 0},
+#line 999 "effective_tld_names.gperf"
+      {"fauske.no", 0},
+#line 485 "effective_tld_names.gperf"
+      {"cherkassy.ua", 0},
+#line 1891 "effective_tld_names.gperf"
+      {"mc", 0},
+#line 3322 "effective_tld_names.gperf"
+      {"va", 0},
+#line 2090 "effective_tld_names.gperf"
+      {"name.tj", 0},
+#line 3667 "effective_tld_names.gperf"
+      {"xn--zf0ao64a.tw", 0},
+#line 1569 "effective_tld_names.gperf"
+      {"ivgu.no", 0},
+#line 591 "effective_tld_names.gperf"
+      {"coldwar.museum", 0},
+#line 3328 "effective_tld_names.gperf"
+      {"vaga.no", 0},
+#line 2185 "effective_tld_names.gperf"
+      {"net.kz", 0},
+#line 1892 "effective_tld_names.gperf"
+      {"mc.it", 0},
+#line 3323 "effective_tld_names.gperf"
+      {"va.it", 0},
+#line 3081 "effective_tld_names.gperf"
+      {"store.st", 0},
+#line 3094 "effective_tld_names.gperf"
+      {"suldal.no", 0},
+#line 3270 "effective_tld_names.gperf"
+      {"udine.it", 0},
+#line 3291 "effective_tld_names.gperf"
+      {"unjarga.no", 0},
+#line 1848 "effective_tld_names.gperf"
+      {"ma.us", 0},
+#line 3427 "effective_tld_names.gperf"
+      {"warszawa.pl", 0},
+#line 2330 "effective_tld_names.gperf"
+      {"nv.us", 0},
+#line 3052 "effective_tld_names.gperf"
+      {"stalowa-wola.pl", 0},
+#line 3420 "effective_tld_names.gperf"
+      {"wa.us", 0},
+#line 3080 "effective_tld_names.gperf"
+      {"store.ro", 0},
+#line 122 "effective_tld_names.gperf"
+      {"alaska.museum", 0},
+#line 2873 "effective_tld_names.gperf"
+      {"sandnes.no", 0},
+#line 1714 "effective_tld_names.gperf"
+      {"kurgan.ru", 0},
+#line 2296 "effective_tld_names.gperf"
+      {"nore-og-uvdal.no", 0},
+#line 2083 "effective_tld_names.gperf"
+      {"name.hr", 0},
+#line 401 "effective_tld_names.gperf"
+      {"british.museum", 0},
+#line 3076 "effective_tld_names.gperf"
+      {"stord.no", 0},
+#line 2625 "effective_tld_names.gperf"
+      {"powiat.pl", 0},
+#line 3673 "effective_tld_names.gperf"
+      {"yamagata.jp", 2},
+#line 3531 "effective_tld_names.gperf"
+      {"xn--jrpeland-54a.no", 0},
+#line 2716 "effective_tld_names.gperf"
+      {"pruszkow.pl", 0},
+#line 3526 "effective_tld_names.gperf"
+      {"xn--hylandet-54a.no", 0},
+#line 1723 "effective_tld_names.gperf"
+      {"kvanangen.no", 0},
+#line 3008 "effective_tld_names.gperf"
+      {"society.museum", 0},
+#line 2884 "effective_tld_names.gperf"
+      {"sarpsborg.no", 0},
+#line 3699 "effective_tld_names.gperf"
+      {"zarow.pl", 0},
+#line 3063 "effective_tld_names.gperf"
+      {"stavanger.no", 0},
+#line 3017 "effective_tld_names.gperf"
+      {"sondre-land.no", 0},
+#line 2867 "effective_tld_names.gperf"
+      {"samnanger.no", 0},
+#line 2729 "effective_tld_names.gperf"
+      {"pubol.museum", 0},
+#line 2072 "effective_tld_names.gperf"
+      {"naamesjevuemie.no", 0},
+#line 1157 "effective_tld_names.gperf"
+      {"gliding.aero", 0},
+#line 56 "effective_tld_names.gperf"
+      {"ac.sz", 0},
+#line 2620 "effective_tld_names.gperf"
+      {"portal.museum", 0},
+#line 3373 "effective_tld_names.gperf"
+      {"vi", 0},
+#line 2243 "effective_tld_names.gperf"
+      {"newyork.museum", 0},
+#line 311 "effective_tld_names.gperf"
+      {"belau.pw", 0},
+#line 3460 "effective_tld_names.gperf"
+      {"wy.us", 0},
+#line 750 "effective_tld_names.gperf"
+      {"cv.ua", 0},
+#line 2605 "effective_tld_names.gperf"
+      {"po.gov.pl", 0},
+#line 3374 "effective_tld_names.gperf"
+      {"vi.it", 0},
+#line 2619 "effective_tld_names.gperf"
+      {"port.fr", 0},
+#line 2942 "effective_tld_names.gperf"
+      {"settlers.museum", 0},
+#line 55 "effective_tld_names.gperf"
+      {"ac.se", 0},
+#line 3091 "effective_tld_names.gperf"
+      {"suedtirol.it", 0},
+#line 2112 "effective_tld_names.gperf"
+      {"nature.museum", 0},
+#line 2551 "effective_tld_names.gperf"
+      {"pavia.it", 0},
+#line 1932 "effective_tld_names.gperf"
+      {"mi.us", 0},
+#line 2864 "effective_tld_names.gperf"
+      {"salvadordali.museum", 0},
+#line 3441 "effective_tld_names.gperf"
+      {"wi.us", 0},
+#line 3423 "effective_tld_names.gperf"
+      {"wales.museum", 0},
+#line 3281 "effective_tld_names.gperf"
+      {"ullensvang.no", 0},
+#line 1820 "effective_tld_names.gperf"
+      {"ls", 0},
+#line 2856 "effective_tld_names.gperf"
+      {"saintlouis.museum", 0},
+#line 1821 "effective_tld_names.gperf"
+      {"lt", 0},
+#line 2590 "effective_tld_names.gperf"
+      {"pistoia.it", 0},
+#line 3324 "effective_tld_names.gperf"
+      {"va.no", 0},
+#line 1632 "effective_tld_names.gperf"
+      {"karasjok.no", 0},
+#line 3011 "effective_tld_names.gperf"
+      {"sogne.no", 0},
+#line 2970 "effective_tld_names.gperf"
+      {"sk", 0},
+#line 3014 "effective_tld_names.gperf"
+      {"sologne.museum", 0},
+#line 727 "effective_tld_names.gperf"
+      {"council.aero", 0},
+#line 1393 "effective_tld_names.gperf"
+      {"health.vn", 0},
+#line 2018 "effective_tld_names.gperf"
+      {"money.museum", 0},
+#line 1802 "effective_tld_names.gperf"
+      {"lo.it", 0},
+#line 1063 "effective_tld_names.gperf"
+      {"forde.no", 0},
+#line 1819 "effective_tld_names.gperf"
+      {"lr", 0},
+#line 1609 "effective_tld_names.gperf"
+      {"jpn.com", 0},
+#line 1822 "effective_tld_names.gperf"
+      {"lt.it", 0},
+#line 2064 "effective_tld_names.gperf"
+      {"mx.na", 0},
+#line 3018 "effective_tld_names.gperf"
+      {"sondrio.it", 0},
+#line 677 "effective_tld_names.gperf"
+      {"com.ru", 0},
+#line 901 "effective_tld_names.gperf"
+      {"edu.ru", 0},
+#line 3092 "effective_tld_names.gperf"
+      {"suisse.museum", 0},
+#line 2859 "effective_tld_names.gperf"
+      {"salangen.no", 0},
+#line 2996 "effective_tld_names.gperf"
+      {"smola.no", 0},
 #line 3443 "effective_tld_names.gperf"
+      {"wiki.br", 0},
+#line 2863 "effective_tld_names.gperf"
+      {"saltdal.no", 0},
+#line 2689 "effective_tld_names.gperf"
+      {"press.se", 0},
+#line 3056 "effective_tld_names.gperf"
+      {"starnberg.museum", 0},
+#line 1860 "effective_tld_names.gperf"
+      {"malopolska.pl", 0},
+#line 1804 "effective_tld_names.gperf"
+      {"local", 0},
+#line 3027 "effective_tld_names.gperf"
+      {"sortland.no", 0},
+#line 1762 "effective_tld_names.gperf"
+      {"le.it", 0},
+#line 1281 "effective_tld_names.gperf"
+      {"gov.ru", 0},
+#line 2621 "effective_tld_names.gperf"
+      {"portland.museum", 0},
+#line 2624 "effective_tld_names.gperf"
+      {"potenza.it", 0},
+#line 3560 "effective_tld_names.gperf"
+      {"xn--lten-gra.no", 0},
+#line 2305 "effective_tld_names.gperf"
+      {"nov.ru", 0},
+#line 1826 "effective_tld_names.gperf"
+      {"lu", 0},
+#line 2016 "effective_tld_names.gperf"
+      {"molde.no", 0},
+#line 291 "effective_tld_names.gperf"
+      {"barlettaandriatrani.it", 0},
+#line 290 "effective_tld_names.gperf"
+      {"barletta-andria-trani.it", 0},
+#line 288 "effective_tld_names.gperf"
+      {"bardu.no", 0},
+#line 1708 "effective_tld_names.gperf"
+      {"ks.us", 0},
+#line 3061 "effective_tld_names.gperf"
+      {"stathelle.no", 0},
+#line 2100 "effective_tld_names.gperf"
+      {"narviika.no", 0},
+#line 1827 "effective_tld_names.gperf"
+      {"lu.it", 0},
+#line 2530 "effective_tld_names.gperf"
+      {"pa.gov.pl", 0},
+#line 3541 "effective_tld_names.gperf"
+      {"xn--ksnes-uua.no", 0},
+#line 2969 "effective_tld_names.gperf"
+      {"sirdal.no", 0},
+#line 657 "effective_tld_names.gperf"
+      {"com.mu", 0},
+#line 2607 "effective_tld_names.gperf"
+      {"podhale.pl", 0},
+#line 1329 "effective_tld_names.gperf"
+      {"gs.hl.no", 0},
+#line 2215 "effective_tld_names.gperf"
+      {"net.ru", 0},
+#line 2613 "effective_tld_names.gperf"
+      {"pomorskie.pl", 0},
+#line 3016 "effective_tld_names.gperf"
+      {"somna.no", 0},
+#line 1539 "effective_tld_names.gperf"
+      {"int.ru", 0},
+#line 1051 "effective_tld_names.gperf"
+      {"florence.it", 0},
+#line 596 "effective_tld_names.gperf"
+      {"columbus.museum", 0},
+#line 92 "effective_tld_names.gperf"
+      {"agrar.hu", 0},
+#line 1266 "effective_tld_names.gperf"
+      {"gov.mu", 0},
+#line 3280 "effective_tld_names.gperf"
+      {"ullensaker.no", 0},
+#line 2587 "effective_tld_names.gperf"
+      {"pilot.aero", 0},
+#line 2684 "effective_tld_names.gperf"
+      {"preservation.museum", 0},
+#line 2616 "effective_tld_names.gperf"
+      {"porsanger.no", 0},
+#line 2665 "effective_tld_names.gperf"
+      {"pref.nara.jp", 1},
+#line 1735 "effective_tld_names.gperf"
+      {"la", 0},
+#line 3661 "effective_tld_names.gperf"
+      {"xn--vry-yla5g.no", 0},
+#line 3655 "effective_tld_names.gperf"
+      {"xn--vgan-qoa.no", 0},
+#line 3278 "effective_tld_names.gperf"
+      {"uk.net", 0},
+#line 2941 "effective_tld_names.gperf"
+      {"settlement.museum", 0},
+#line 2617 "effective_tld_names.gperf"
+      {"porsangu.no", 0},
+#line 3346 "effective_tld_names.gperf"
+      {"vdonsk.ru", 0},
+#line 2935 "effective_tld_names.gperf"
+      {"selbu.no", 0},
+#line 2618 "effective_tld_names.gperf"
+      {"porsgrunn.no", 0},
+#line 1078 "effective_tld_names.gperf"
+      {"franziskaner.museum", 0},
+#line 3432 "effective_tld_names.gperf"
+      {"web.co", 0},
+#line 2860 "effective_tld_names.gperf"
+      {"salat.no", 0},
+#line 2854 "effective_tld_names.gperf"
+      {"safety.aero", 0},
+#line 3086 "effective_tld_names.gperf"
+      {"stryn.no", 0},
+#line 2198 "effective_tld_names.gperf"
+      {"net.mu", 0},
+#line 1039 "effective_tld_names.gperf"
+      {"fjell.no", 0},
+#line 2596 "effective_tld_names.gperf"
+      {"planetarium.museum", 0},
+#line 2936 "effective_tld_names.gperf"
+      {"selje.no", 0},
+#line 1021 "effective_tld_names.gperf"
+      {"film.hu", 0},
+#line 1031 "effective_tld_names.gperf"
+      {"firm.ht", 0},
+#line 74 "effective_tld_names.gperf"
+      {"adygeya.ru", 0},
+#line 3686 "effective_tld_names.gperf"
+      {"youth.museum", 0},
+#line 2257 "effective_tld_names.gperf"
+      {"niepce.museum", 0},
+#line 1330 "effective_tld_names.gperf"
+      {"gs.hm.no", 0},
+#line 518 "effective_tld_names.gperf"
+      {"city.nagoya.jp", 1},
+#line 500 "effective_tld_names.gperf"
+      {"chukotka.ru", 0},
+#line 2541 "effective_tld_names.gperf"
+      {"palmsprings.museum", 0},
+#line 2686 "effective_tld_names.gperf"
+      {"press.aero", 0},
+#line 627 "effective_tld_names.gperf"
+      {"com.gh", 0},
+#line 706 "effective_tld_names.gperf"
+      {"conference.aero", 0},
+#line 852 "effective_tld_names.gperf"
+      {"edu.gh", 0},
+#line 584 "effective_tld_names.gperf"
+      {"co.ug", 0},
+#line 114 "effective_tld_names.gperf"
+      {"akrehamn.no", 0},
+#line 3069 "effective_tld_names.gperf"
+      {"steinkjer.no", 0},
+#line 1144 "effective_tld_names.gperf"
+      {"giehtavuoatna.no", 0},
+#line 1174 "effective_tld_names.gperf"
+      {"go.ug", 0},
+#line 1707 "effective_tld_names.gperf"
+      {"ks.ua", 0},
+#line 1842 "effective_tld_names.gperf"
+      {"ly", 0},
+#line 1230 "effective_tld_names.gperf"
+      {"gov.gh", 0},
+#line 1970 "effective_tld_names.gperf"
+      {"mil.ph", 0},
+#line 3070 "effective_tld_names.gperf"
+      {"stjohn.museum", 0},
+#line 3406 "effective_tld_names.gperf"
+      {"voss.no", 0},
+#line 3413 "effective_tld_names.gperf"
+      {"vv.it", 0},
+#line 2687 "effective_tld_names.gperf"
+      {"press.ma", 0},
+#line 1698 "effective_tld_names.gperf"
+      {"kr.ua", 0},
+#line 2659 "effective_tld_names.gperf"
+      {"pref.kyoto.jp", 1},
+#line 1086 "effective_tld_names.gperf"
+      {"frogn.no", 0},
+#line 3066 "effective_tld_names.gperf"
+      {"steam.museum", 0},
+#line 3074 "effective_tld_names.gperf"
+      {"stokke.no", 0},
+#line 1784 "effective_tld_names.gperf"
+      {"li", 0},
+#line 1352 "effective_tld_names.gperf"
+      {"gunma.jp", 2},
+#line 2612 "effective_tld_names.gperf"
+      {"poltava.ua", 0},
+#line 2545 "effective_tld_names.gperf"
+      {"paris.museum", 0},
+#line 2997 "effective_tld_names.gperf"
+      {"smolensk.ru", 0},
+#line 3458 "effective_tld_names.gperf"
+      {"wv.us", 0},
+#line 3005 "effective_tld_names.gperf"
+      {"so.gov.pl", 0},
+#line 481 "effective_tld_names.gperf"
+      {"chattanooga.museum", 0},
+#line 2129 "effective_tld_names.gperf"
+      {"ne.ug", 0},
+#line 3614 "effective_tld_names.gperf"
+      {"xn--sgne-gra.no", 0},
+#line 2293 "effective_tld_names.gperf"
+      {"nordkapp.no", 0},
+#line 2248 "effective_tld_names.gperf"
+      {"ngo.ph", 0},
+#line 1785 "effective_tld_names.gperf"
+      {"li.it", 0},
+#line 3498 "effective_tld_names.gperf"
       {"xn--eveni-0qa01ga.no", 0},
+#line 3514 "effective_tld_names.gperf"
+      {"xn--h1aegh.museum", 0},
+#line 375 "effective_tld_names.gperf"
+      {"bo.telemark.no", 0},
+#line 3033 "effective_tld_names.gperf"
+      {"southwest.museum", 0},
+#line 2547 "effective_tld_names.gperf"
+      {"parma.it", 0},
+#line 3043 "effective_tld_names.gperf"
+      {"sr.gov.pl", 0},
+#line 1077 "effective_tld_names.gperf"
+      {"frankfurt.museum", 0},
+#line 1730 "effective_tld_names.gperf"
+      {"ky.us", 0},
+#line 1951 "effective_tld_names.gperf"
+      {"mil.cn", 0},
+#line 3274 "effective_tld_names.gperf"
+      {"ug.gov.pl", 0},
+#line 1019 "effective_tld_names.gperf"
+      {"figueres.museum", 0},
+#line 1766 "effective_tld_names.gperf"
+      {"lebork.pl", 0},
+#line 3102 "effective_tld_names.gperf"
+      {"surrey.museum", 0},
+#line 2744 "effective_tld_names.gperf"
+      {"qld.gov.au", 0},
+#line 3359 "effective_tld_names.gperf"
+      {"verran.no", 0},
+#line 1673 "effective_tld_names.gperf"
+      {"km.ua", 0},
+#line 3038 "effective_tld_names.gperf"
+      {"sport.hu", 0},
+#line 3379 "effective_tld_names.gperf"
+      {"vic.edu.au", 0},
+#line 3019 "effective_tld_names.gperf"
+      {"songdalen.no", 0},
+#line 2633 "effective_tld_names.gperf"
+      {"pr.us", 0},
+#line 3650 "effective_tld_names.gperf"
+      {"xn--vads-jra.no", 0},
+#line 3451 "effective_tld_names.gperf"
+      {"workinggroup.aero", 0},
+#line 3354 "effective_tld_names.gperf"
+      {"vennesla.no", 0},
+#line 1585 "effective_tld_names.gperf"
+      {"jerusalem.museum", 0},
+#line 151 "effective_tld_names.gperf"
+      {"andasuolo.no", 0},
+#line 228 "effective_tld_names.gperf"
+      {"association.aero", 0},
+#line 707 "effective_tld_names.gperf"
+      {"congresodelalengua3.ar", 1},
+#line 1726 "effective_tld_names.gperf"
+      {"kviteseid.no", 0},
+#line 187 "effective_tld_names.gperf"
+      {"art.museum", 0},
+#line 3072 "effective_tld_names.gperf"
+      {"stjordalshalsen.no", 0},
+#line 2566 "effective_tld_names.gperf"
+      {"perso.sn", 0},
+#line 3651 "effective_tld_names.gperf"
+      {"xn--vard-jra.no", 0},
+#line 2567 "effective_tld_names.gperf"
+      {"perso.tn", 0},
+#line 3574 "effective_tld_names.gperf"
+      {"xn--mosjen-eya.no", 0},
+#line 3107 "effective_tld_names.gperf"
+      {"sveio.no", 0},
+#line 1787 "effective_tld_names.gperf"
+      {"lier.no", 0},
+#line 3031 "effective_tld_names.gperf"
+      {"soundandvision.museum", 0},
+#line 1786 "effective_tld_names.gperf"
+      {"lib.ee", 0},
+#line 3645 "effective_tld_names.gperf"
+      {"xn--tysvr-vra.no", 0},
+#line 1825 "effective_tld_names.gperf"
+      {"ltd.lk", 0},
+#line 3369 "effective_tld_names.gperf"
+      {"vevelstad.no", 0},
+#line 203 "effective_tld_names.gperf"
+      {"ascolipiceno.it", 0},
+#line 2879 "effective_tld_names.gperf"
+      {"santacruz.museum", 0},
+#line 1147 "effective_tld_names.gperf"
+      {"gildeskal.no", 0},
+#line 202 "effective_tld_names.gperf"
+      {"ascoli-piceno.it", 0},
+#line 423 "effective_tld_names.gperf"
+      {"busan.kr", 0},
+#line 1809 "effective_tld_names.gperf"
+      {"lom.no", 0},
+#line 3684 "effective_tld_names.gperf"
+      {"yorkshire.museum", 0},
+#line 1711 "effective_tld_names.gperf"
+      {"kunst.museum", 0},
+#line 3316 "effective_tld_names.gperf"
+      {"uw.gov.pl", 0},
+#line 2640 "effective_tld_names.gperf"
+      {"pref.aomori.jp", 1},
+#line 1362 "effective_tld_names.gperf"
+      {"gyeongnam.kr", 0},
+#line 2688 "effective_tld_names.gperf"
+      {"press.museum", 0},
+#line 3275 "effective_tld_names.gperf"
+      {"uhren.museum", 0},
+#line 2889 "effective_tld_names.gperf"
+      {"sauherad.no", 0},
+#line 1396 "effective_tld_names.gperf"
+      {"helsinki.museum", 0},
+#line 59 "effective_tld_names.gperf"
+      {"ac.tz", 0},
+#line 424 "effective_tld_names.gperf"
+      {"bushey.museum", 0},
+#line 1734 "effective_tld_names.gperf"
+      {"l.se", 0},
+#line 452 "effective_tld_names.gperf"
+      {"cargo.aero", 0},
+#line 1666 "effective_tld_names.gperf"
+      {"kirov.ru", 0},
+#line 3393 "effective_tld_names.gperf"
+      {"vlaanderen.museum", 0},
+#line 3583 "effective_tld_names.gperf"
+      {"xn--nvuotna-hwa.no", 0},
+#line 813 "effective_tld_names.gperf"
+      {"e-burg.ru", 0},
+#line 1806 "effective_tld_names.gperf"
+      {"lodi.it", 0},
+#line 474 "effective_tld_names.gperf"
+      {"cf", 0},
+#line 85 "effective_tld_names.gperf"
+      {"af", 0},
+#line 1938 "effective_tld_names.gperf"
+      {"midtre-gauldal.no", 0},
+#line 3508 "effective_tld_names.gperf"
+      {"xn--givuotna-8ya.no", 0},
+#line 625 "effective_tld_names.gperf"
+      {"com.fr", 0},
+#line 595 "effective_tld_names.gperf"
+      {"columbia.museum", 0},
+#line 1646 "effective_tld_names.gperf"
+      {"kchr.ru", 0},
+#line 2554 "effective_tld_names.gperf"
+      {"pc.pl", 0},
+#line 3116 "effective_tld_names.gperf"
+      {"sydney.museum", 0},
+#line 1139 "effective_tld_names.gperf"
+      {"gf", 0},
+#line 2553 "effective_tld_names.gperf"
+      {"pc.it", 0},
+#line 2658 "effective_tld_names.gperf"
+      {"pref.kumamoto.jp", 1},
+#line 385 "effective_tld_names.gperf"
+      {"botanical.museum", 0},
+#line 1441 "effective_tld_names.gperf"
+      {"hotel.lk", 0},
+#line 1403 "effective_tld_names.gperf"
+      {"heroy.more-og-romsdal.no", 0},
+#line 48 "effective_tld_names.gperf"
+      {"ac.mw", 0},
+#line 306 "effective_tld_names.gperf"
+      {"beardu.no", 0},
+#line 2579 "effective_tld_names.gperf"
+      {"philadelphia.museum", 0},
+#line 1079 "effective_tld_names.gperf"
+      {"fredrikstad.no", 0},
+#line 2532 "effective_tld_names.gperf"
+      {"pa.us", 0},
+#line 502 "effective_tld_names.gperf"
+      {"chungnam.kr", 0},
+#line 2595 "effective_tld_names.gperf"
+      {"pl.ua", 0},
+#line 556 "effective_tld_names.gperf"
+      {"co.cr", 0},
+#line 2244 "effective_tld_names.gperf"
+      {"nf", 0},
+#line 824 "effective_tld_names.gperf"
+      {"ed.cr", 0},
+#line 2280 "effective_tld_names.gperf"
+      {"nom.fr", 0},
+#line 1166 "effective_tld_names.gperf"
+      {"go.cr", 0},
+#line 1577 "effective_tld_names.gperf"
+      {"jar.ru", 0},
+#line 2692 "effective_tld_names.gperf"
+      {"presse.km", 0},
+#line 2730 "effective_tld_names.gperf"
+      {"pulawy.pl", 0},
+#line 3271 "effective_tld_names.gperf"
+      {"udm.ru", 0},
+#line 3469 "effective_tld_names.gperf"
+      {"xn--asky-ira.no", 0},
+#line 54 "effective_tld_names.gperf"
+      {"ac.rw", 0},
+#line 519 "effective_tld_names.gperf"
+      {"city.niigata.jp", 1},
+#line 49 "effective_tld_names.gperf"
+      {"ac.ng", 0},
+#line 1712 "effective_tld_names.gperf"
+      {"kunstsammlung.museum", 0},
+#line 3669 "effective_tld_names.gperf"
+      {"xz.cn", 0},
+#line 1188 "effective_tld_names.gperf"
+      {"gorge.museum", 0},
+#line 120 "effective_tld_names.gperf"
+      {"alaheadju.no", 0},
+#line 1610 "effective_tld_names.gperf"
+      {"js.cn", 0},
+#line 3062 "effective_tld_names.gperf"
+      {"station.museum", 0},
+#line 1363 "effective_tld_names.gperf"
+      {"gz.cn", 0},
+#line 1688 "effective_tld_names.gperf"
+      {"kongsberg.no", 0},
+#line 1125 "effective_tld_names.gperf"
+      {"gd.cn", 0},
+#line 1327 "effective_tld_names.gperf"
+      {"gs.cn", 0},
+#line 1594 "effective_tld_names.gperf"
+      {"jl.cn", 0},
+#line 2299 "effective_tld_names.gperf"
+      {"north.museum", 0},
+#line 1421 "effective_tld_names.gperf"
+      {"hl.cn", 0},
+#line 1720 "effective_tld_names.gperf"
+      {"kvafjord.no", 0},
+#line 1590 "effective_tld_names.gperf"
+      {"jewish.museum", 0},
+#line 174 "effective_tld_names.gperf"
+      {"archaeology.museum", 0},
+#line 3351 "effective_tld_names.gperf"
+      {"vegarshei.no", 0},
+#line 3538 "effective_tld_names.gperf"
+      {"xn--krdsherad-m8a.no", 0},
+#line 1581 "effective_tld_names.gperf"
+      {"jeju.kr", 0},
+#line 1840 "effective_tld_names.gperf"
+      {"lv", 0},
+#line 1515 "effective_tld_names.gperf"
+      {"info.nf", 0},
+#line 3453 "effective_tld_names.gperf"
+      {"workshop.museum", 0},
+#line 2667 "effective_tld_names.gperf"
+      {"pref.oita.jp", 1},
+#line 1631 "effective_tld_names.gperf"
+      {"karasjohka.no", 0},
+#line 1834 "effective_tld_names.gperf"
+      {"lunner.no", 0},
+#line 1677 "effective_tld_names.gperf"
+      {"kobierzyce.pl", 0},
+#line 2949 "effective_tld_names.gperf"
+      {"shell.museum", 0},
+#line 229 "effective_tld_names.gperf"
+      {"association.museum", 0},
+#line 1391 "effective_tld_names.gperf"
+      {"he.cn", 0},
+#line 2119 "effective_tld_names.gperf"
+      {"navuotna.no", 0},
+#line 3284 "effective_tld_names.gperf"
+      {"ulvik.no", 0},
+#line 1727 "effective_tld_names.gperf"
+      {"kvitsoy.no", 0},
+#line 2962 "effective_tld_names.gperf"
+      {"siellak.no", 0},
+#line 2030 "effective_tld_names.gperf"
+      {"motorcycle.museum", 0},
+#line 3358 "effective_tld_names.gperf"
+      {"verona.it", 0},
+#line 1741 "effective_tld_names.gperf"
+      {"lahppi.no", 0},
+#line 150 "effective_tld_names.gperf"
+      {"and.museum", 0},
+#line 497 "effective_tld_names.gperf"
+      {"chita.ru", 0},
+#line 3701 "effective_tld_names.gperf"
+      {"zgorzelec.pl", 0},
+#line 2260 "effective_tld_names.gperf"
+      {"nikolaev.ua", 0},
+#line 2539 "effective_tld_names.gperf"
+      {"paleo.museum", 0},
+#line 3635 "effective_tld_names.gperf"
+      {"xn--stjrdal-s1a.no", 0},
+#line 3283 "effective_tld_names.gperf"
+      {"ulsan.kr", 0},
+#line 2852 "effective_tld_names.gperf"
+      {"sa.gov.au", 0},
+#line 3548 "effective_tld_names.gperf"
+      {"xn--lcvr32d.hk", 0},
+#line 2963 "effective_tld_names.gperf"
+      {"siena.it", 0},
+#line 3681 "effective_tld_names.gperf"
+      {"yn.cn", 0},
+#line 422 "effective_tld_names.gperf"
+      {"bus.museum", 0},
+#line 3598 "effective_tld_names.gperf"
+      {"xn--rholt-mra.no", 0},
+#line 1799 "effective_tld_names.gperf"
+      {"livorno.it", 0},
+#line 1426 "effective_tld_names.gperf"
+      {"hn.cn", 0},
+#line 1788 "effective_tld_names.gperf"
+      {"lierne.no", 0},
+#line 3648 "effective_tld_names.gperf"
+      {"xn--uc0ay4a.hk", 0},
+#line 305 "effective_tld_names.gperf"
+      {"bearalvahki.no", 0},
+#line 1960 "effective_tld_names.gperf"
+      {"mil.kg", 0},
+#line 325 "effective_tld_names.gperf"
+      {"bf", 0},
+#line 2973 "effective_tld_names.gperf"
+      {"skanland.no", 0},
+#line 1667 "effective_tld_names.gperf"
+      {"kirovograd.ua", 0},
+#line 3371 "effective_tld_names.gperf"
+      {"vg", 0},
+#line 2312 "effective_tld_names.gperf"
+      {"ns.ca", 0},
+#line 2318 "effective_tld_names.gperf"
+      {"nt.ca", 0},
+#line 1952 "effective_tld_names.gperf"
+      {"mil.co", 0},
+#line 2266 "effective_tld_names.gperf"
+      {"nl.ca", 0},
+#line 1654 "effective_tld_names.gperf"
+      {"kh.ua", 0},
+#line 3001 "effective_tld_names.gperf"
+      {"snasa.no", 0},
+#line 1635 "effective_tld_names.gperf"
+      {"karikatur.museum", 0},
+#line 2333 "effective_tld_names.gperf"
+      {"nyc.museum", 0},
+#line 1624 "effective_tld_names.gperf"
+      {"kagawa.jp", 2},
+#line 2926 "effective_tld_names.gperf"
+      {"sd.us", 0},
+#line 941 "effective_tld_names.gperf"
+      {"elverum.no", 0},
+#line 2672 "effective_tld_names.gperf"
+      {"pref.saitama.jp", 1},
+#line 1916 "effective_tld_names.gperf"
+      {"medical.museum", 0},
+#line 2890 "effective_tld_names.gperf"
+      {"savannahga.museum", 0},
+#line 3338 "effective_tld_names.gperf"
+      {"vanylven.no", 0},
+#line 2581 "effective_tld_names.gperf"
+      {"philately.museum", 0},
+#line 449 "effective_tld_names.gperf"
+      {"can.museum", 0},
+#line 920 "effective_tld_names.gperf"
+      {"educ.ar", 1},
+#line 2269 "effective_tld_names.gperf"
+      {"nm.cn", 0},
+#line 1740 "effective_tld_names.gperf"
+      {"labour.museum", 0},
+#line 699 "effective_tld_names.gperf"
+      {"communication.museum", 0},
+#line 1366 "effective_tld_names.gperf"
+      {"ha.cn", 0},
+#line 3546 "effective_tld_names.gperf"
+      {"xn--laheadju-7ya.no", 0},
+#line 700 "effective_tld_names.gperf"
+      {"communications.museum", 0},
+#line 3532 "effective_tld_names.gperf"
+      {"xn--karmy-yua.no", 0},
+#line 1769 "effective_tld_names.gperf"
+      {"legnica.pl", 0},
+#line 2319 "effective_tld_names.gperf"
+      {"nt.edu.au", 0},
+#line 1744 "effective_tld_names.gperf"
+      {"lanbib.se", 0},
+#line 1879 "effective_tld_names.gperf"
+      {"marylhurst.museum", 0},
+#line 1963 "effective_tld_names.gperf"
+      {"mil.kz", 0},
+#line 2967 "effective_tld_names.gperf"
+      {"simbirsk.ru", 0},
+#line 3392 "effective_tld_names.gperf"
+      {"viterbo.it", 0},
+#line 3405 "effective_tld_names.gperf"
+      {"voronezh.ru", 0},
+#line 1755 "effective_tld_names.gperf"
+      {"latina.it", 0},
+#line 731 "effective_tld_names.gperf"
+      {"cq.cn", 0},
+#line 2325 "effective_tld_names.gperf"
+      {"nu.ca", 0},
+#line 2565 "effective_tld_names.gperf"
+      {"perso.ht", 0},
+#line 2258 "effective_tld_names.gperf"
+      {"nieruchomosci.pl", 0},
+#line 555 "effective_tld_names.gperf"
+      {"co.ci", 0},
+#line 823 "effective_tld_names.gperf"
+      {"ed.ci", 0},
+#line 1048 "effective_tld_names.gperf"
+      {"flight.aero", 0},
+#line 2642 "effective_tld_names.gperf"
+      {"pref.ehime.jp", 1},
+#line 156 "effective_tld_names.gperf"
+      {"anthropology.museum", 0},
+#line 1591 "effective_tld_names.gperf"
+      {"jewishart.museum", 0},
+#line 1165 "effective_tld_names.gperf"
+      {"go.ci", 0},
+#line 2263 "effective_tld_names.gperf"
+      {"nj.us", 0},
+#line 157 "effective_tld_names.gperf"
+      {"antiques.museum", 0},
+#line 3308 "effective_tld_names.gperf"
+      {"ushuaia.museum", 0},
+#line 938 "effective_tld_names.gperf"
+      {"elburg.museum", 0},
+#line 3024 "effective_tld_names.gperf"
+      {"sor-varanger.no", 0},
+#line 2893 "effective_tld_names.gperf"
+      {"sc", 0},
+#line 2075 "effective_tld_names.gperf"
+      {"nagasaki.jp", 2},
+#line 297 "effective_tld_names.gperf"
+      {"baths.museum", 0},
+#line 1859 "effective_tld_names.gperf"
+      {"mallorca.museum", 0},
+#line 1816 "effective_tld_names.gperf"
+      {"louvre.museum", 0},
+#line 1854 "effective_tld_names.gperf"
+      {"magnitka.ru", 0},
+#line 984 "effective_tld_names.gperf"
+      {"exeter.museum", 0},
+#line 1639 "effective_tld_names.gperf"
+      {"kartuzy.pl", 0},
+#line 2875 "effective_tld_names.gperf"
+      {"sandoy.no", 0},
+#line 3067 "effective_tld_names.gperf"
+      {"steiermark.museum", 0},
+#line 1719 "effective_tld_names.gperf"
+      {"kv.ua", 0},
+#line 2241 "effective_tld_names.gperf"
+      {"news.hu", 0},
+#line 1990 "effective_tld_names.gperf"
+      {"miyagi.jp", 2},
+#line 342 "effective_tld_names.gperf"
+      {"bir.ru", 0},
+#line 1765 "effective_tld_names.gperf"
+      {"lebesby.no", 0},
+#line 3646 "effective_tld_names.gperf"
+      {"xn--uc0atv.hk", 0},
+#line 1378 "effective_tld_names.gperf"
+      {"hammerfest.no", 0},
+#line 1655 "effective_tld_names.gperf"
+      {"khabarovsk.ru", 0},
+#line 380 "effective_tld_names.gperf"
+      {"bolt.hu", 0},
+#line 1083 "effective_tld_names.gperf"
+      {"freight.aero", 0},
+#line 1405 "effective_tld_names.gperf"
+      {"hi.cn", 0},
+#line 3282 "effective_tld_names.gperf"
+      {"ulm.museum", 0},
+#line 2813 "effective_tld_names.gperf"
+      {"ro", 0},
+#line 2832 "effective_tld_names.gperf"
+      {"rs", 0},
+#line 1604 "effective_tld_names.gperf"
+      {"joshkar-ola.ru", 0},
+#line 2634 "effective_tld_names.gperf"
+      {"prato.it", 0},
+#line 2580 "effective_tld_names.gperf"
+      {"philadelphiaarea.museum", 0},
+#line 207 "effective_tld_names.gperf"
+      {"askim.no", 0},
+#line 3110 "effective_tld_names.gperf"
+      {"sweden.museum", 0},
+#line 3649 "effective_tld_names.gperf"
+      {"xn--unjrga-rta.no", 0},
+#line 397 "effective_tld_names.gperf"
+      {"brescia.it", 0},
+#line 798 "effective_tld_names.gperf"
+      {"do", 2},
+#line 812 "effective_tld_names.gperf"
+      {"dz", 0},
+#line 2814 "effective_tld_names.gperf"
+      {"ro.it", 0},
+#line 57 "effective_tld_names.gperf"
+      {"ac.th", 0},
+#line 2301 "effective_tld_names.gperf"
+      {"notaires.fr", 0},
+#line 620 "effective_tld_names.gperf"
+      {"com.dm", 0},
+#line 846 "effective_tld_names.gperf"
+      {"edu.dm", 0},
+#line 2767 "effective_tld_names.gperf"
+      {"re", 0},
+#line 1064 "effective_tld_names.gperf"
+      {"forli-cesena.it", 0},
+#line 929 "effective_tld_names.gperf"
+      {"ehime.jp", 2},
+#line 3326 "effective_tld_names.gperf"
+      {"vaapste.no", 0},
+#line 1068 "effective_tld_names.gperf"
+      {"fortworth.museum", 0},
+#line 2078 "effective_tld_names.gperf"
+      {"naklo.pl", 0},
+#line 363 "effective_tld_names.gperf"
+      {"bjerkreim.no", 0},
+#line 1790 "effective_tld_names.gperf"
+      {"lillesand.no", 0},
+#line 1224 "effective_tld_names.gperf"
+      {"gov.dm", 0},
+#line 2880 "effective_tld_names.gperf"
+      {"santafe.museum", 0},
+#line 771 "effective_tld_names.gperf"
+      {"de", 0},
+#line 182 "effective_tld_names.gperf"
+      {"arpa", 0},
+#line 2895 "effective_tld_names.gperf"
+      {"sc.kr", 0},
+#line 2768 "effective_tld_names.gperf"
+      {"re.it", 0},
+#line 2674 "effective_tld_names.gperf"
+      {"pref.shimane.jp", 1},
+#line 1076 "effective_tld_names.gperf"
+      {"francaise.museum", 0},
+#line 1491 "effective_tld_names.gperf"
+      {"incheon.kr", 0},
+#line 3299 "effective_tld_names.gperf"
+      {"usa.museum", 0},
+#line 3309 "effective_tld_names.gperf"
+      {"uslivinghistory.museum", 0},
+#line 3009 "effective_tld_names.gperf"
+      {"software.aero", 0},
+#line 3279 "effective_tld_names.gperf"
+      {"ulan-ude.ru", 0},
+#line 3032 "effective_tld_names.gperf"
+      {"southcarolina.museum", 0},
+#line 3355 "effective_tld_names.gperf"
+      {"verbania.it", 0},
+#line 443 "effective_tld_names.gperf"
+      {"cahcesuolo.no", 0},
+#line 2834 "effective_tld_names.gperf"
+      {"ru", 0},
+#line 39 "effective_tld_names.gperf"
+      {"ac.gn", 0},
+#line 2812 "effective_tld_names.gperf"
+      {"rnu.tn", 0},
+#line 3558 "effective_tld_names.gperf"
+      {"xn--lrenskog-54a.no", 0},
+#line 3123 "effective_tld_names.gperf"
+      {"szkola.pl", 0},
+#line 248 "effective_tld_names.gperf"
+      {"austrheim.no", 0},
+#line 1319 "effective_tld_names.gperf"
+      {"group.aero", 0},
+#line 2808 "effective_tld_names.gperf"
+      {"rn.it", 0},
+#line 3340 "effective_tld_names.gperf"
+      {"varese.it", 0},
+#line 2811 "effective_tld_names.gperf"
+      {"rns.tn", 0},
+#line 105 "effective_tld_names.gperf"
+      {"air.museum", 0},
+#line 2546 "effective_tld_names.gperf"
+      {"parliament.uk", 1},
+#line 2162 "effective_tld_names.gperf"
+      {"net.dm", 0},
+#line 65 "effective_tld_names.gperf"
+      {"accident-prevention.aero", 0},
+#line 3122 "effective_tld_names.gperf"
+      {"szex.hu", 0},
+#line 794 "effective_tld_names.gperf"
+      {"dm", 0},
+#line 2807 "effective_tld_names.gperf"
+      {"rm.it", 0},
+#line 111 "effective_tld_names.gperf"
+      {"ak.us", 0},
+#line 97 "effective_tld_names.gperf"
+      {"ah.cn", 0},
+#line 3040 "effective_tld_names.gperf"
+      {"spydeberg.no", 0},
+#line 489 "effective_tld_names.gperf"
+      {"chiba.jp", 2},
+#line 3454 "effective_tld_names.gperf"
+      {"wroc.pl", 0},
+#line 2769 "effective_tld_names.gperf"
+      {"re.kr", 0},
+#line 3388 "effective_tld_names.gperf"
+      {"vinnica.ua", 0},
+#line 540 "effective_tld_names.gperf"
+      {"cmw.ru", 0},
+#line 2978 "effective_tld_names.gperf"
+      {"ski.no", 0},
+#line 982 "effective_tld_names.gperf"
+      {"evje-og-hornnes.no", 0},
+#line 2678 "effective_tld_names.gperf"
+      {"pref.tottori.jp", 1},
+#line 441 "effective_tld_names.gperf"
+      {"cadaques.museum", 0},
+#line 3352 "effective_tld_names.gperf"
+      {"venezia.it", 0},
+#line 208 "effective_tld_names.gperf"
+      {"askoy.no", 0},
+#line 2002 "effective_tld_names.gperf"
+      {"mo.cn", 0},
+#line 1674 "effective_tld_names.gperf"
+      {"kms.ru", 0},
+#line 2390 "effective_tld_names.gperf"
+      {"org", 0},
+#line 1839 "effective_tld_names.gperf"
+      {"luzern.museum", 0},
+#line 2749 "effective_tld_names.gperf"
+      {"ra.it", 0},
+#line 1972 "effective_tld_names.gperf"
+      {"mil.ru", 0},
+#line 3605 "effective_tld_names.gperf"
+      {"xn--rskog-uua.no", 0},
+#line 2484 "effective_tld_names.gperf"
+      {"org.sl", 0},
+#line 2485 "effective_tld_names.gperf"
+      {"org.sn", 0},
+#line 2373 "effective_tld_names.gperf"
+      {"or.at", 0},
+#line 2480 "effective_tld_names.gperf"
+      {"org.sc", 0},
+#line 2490 "effective_tld_names.gperf"
+      {"org.tn", 0},
+#line 2489 "effective_tld_names.gperf"
+      {"org.tj", 0},
+#line 2446 "effective_tld_names.gperf"
+      {"org.lr", 0},
+#line 2481 "effective_tld_names.gperf"
+      {"org.sd", 0},
+#line 2444 "effective_tld_names.gperf"
+      {"org.lc", 0},
+#line 2377 "effective_tld_names.gperf"
+      {"or.it", 0},
+#line 1776 "effective_tld_names.gperf"
+      {"lenvik.no", 0},
+#line 933 "effective_tld_names.gperf"
+      {"eidskog.no", 0},
+#line 2447 "effective_tld_names.gperf"
+      {"org.ls", 0},
+#line 2833 "effective_tld_names.gperf"
+      {"rs.ba", 0},
+#line 3529 "effective_tld_names.gperf"
+      {"xn--io0a7i.hk", 0},
+#line 521 "effective_tld_names.gperf"
+      {"city.osaka.jp", 1},
+#line 191 "effective_tld_names.gperf"
+      {"artcenter.museum", 0},
+#line 2952 "effective_tld_names.gperf"
+      {"shimane.jp", 2},
+#line 2479 "effective_tld_names.gperf"
+      {"org.sb", 0},
+#line 2806 "effective_tld_names.gperf"
+      {"rl.no", 0},
+#line 241 "effective_tld_names.gperf"
+      {"aukra.no", 0},
+#line 3362 "effective_tld_names.gperf"
+      {"vestnes.no", 0},
+#line 1830 "effective_tld_names.gperf"
+      {"lucerne.museum", 0},
+#line 2443 "effective_tld_names.gperf"
+      {"org.lb", 0},
+#line 2415 "effective_tld_names.gperf"
+      {"org.ec", 0},
+#line 2315 "effective_tld_names.gperf"
+      {"nsw.au", 0},
+#line 1758 "effective_tld_names.gperf"
+      {"law.pro", 0},
+#line 2476 "effective_tld_names.gperf"
+      {"org.rs", 0},
+#line 2470 "effective_tld_names.gperf"
+      {"org.pl", 0},
+#line 2471 "effective_tld_names.gperf"
+      {"org.pn", 0},
+#line 2472 "effective_tld_names.gperf"
+      {"org.pr", 0},
+#line 2861 "effective_tld_names.gperf"
+      {"salem.museum", 0},
+#line 2417 "effective_tld_names.gperf"
+      {"org.es", 0},
+#line 1888 "effective_tld_names.gperf"
+      {"mazury.pl", 0},
+#line 2493 "effective_tld_names.gperf"
+      {"org.tw", 0},
+#line 1357 "effective_tld_names.gperf"
+      {"gwangju.kr", 0},
+#line 2905 "effective_tld_names.gperf"
+      {"sch.sa", 0},
+#line 2473 "effective_tld_names.gperf"
+      {"org.ps", 0},
+#line 2903 "effective_tld_names.gperf"
+      {"sch.lk", 0},
+#line 2888 "effective_tld_names.gperf"
+      {"sauda.no", 0},
+#line 3088 "effective_tld_names.gperf"
+      {"stuttgart.museum", 0},
+#line 3015 "effective_tld_names.gperf"
+      {"solund.no", 0},
+#line 3372 "effective_tld_names.gperf"
+      {"vgs.no", 0},
+#line 1702 "effective_tld_names.gperf"
+      {"krasnoyarsk.ru", 0},
+#line 2108 "effective_tld_names.gperf"
+      {"naturalhistory.museum", 0},
+#line 2360 "effective_tld_names.gperf"
+      {"om", 2},
+#line 3525 "effective_tld_names.gperf"
+      {"xn--hyanger-q1a.no", 0},
+#line 362 "effective_tld_names.gperf"
+      {"bjarkoy.no", 0},
+#line 3095 "effective_tld_names.gperf"
+      {"suli.hu", 0},
+#line 197 "effective_tld_names.gperf"
+      {"arts.nf", 0},
+#line 660 "effective_tld_names.gperf"
+      {"com.mx", 0},
+#line 886 "effective_tld_names.gperf"
+      {"edu.mx", 0},
+#line 2464 "effective_tld_names.gperf"
+      {"org.nr", 0},
+#line 1015 "effective_tld_names.gperf"
+      {"fi.cr", 0},
+#line 2954 "effective_tld_names.gperf"
+      {"shop.ht", 0},
+#line 1179 "effective_tld_names.gperf"
+      {"gob.mx", 0},
+#line 2482 "effective_tld_names.gperf"
+      {"org.se", 0},
+#line 2820 "effective_tld_names.gperf"
+      {"roma.it", 0},
+#line 2898 "effective_tld_names.gperf"
+      {"sch.ae", 0},
+#line 1321 "effective_tld_names.gperf"
+      {"grp.lk", 0},
+#line 1187 "effective_tld_names.gperf"
+      {"gop.pk", 0},
+#line 532 "effective_tld_names.gperf"
+      {"ck.ua", 0},
+#line 2815 "effective_tld_names.gperf"
+      {"roan.no", 0},
+#line 2379 "effective_tld_names.gperf"
+      {"or.kr", 0},
+#line 2454 "effective_tld_names.gperf"
+      {"org.ml", 0},
+#line 2455 "effective_tld_names.gperf"
+      {"org.mn", 0},
+#line 2671 "effective_tld_names.gperf"
+      {"pref.saga.jp", 1},
+#line 3364 "effective_tld_names.gperf"
+      {"vestre-toten.no", 0},
+#line 3002 "effective_tld_names.gperf"
+      {"snillfjord.no", 0},
+#line 2911 "effective_tld_names.gperf"
+      {"school.na", 0},
+#line 2416 "effective_tld_names.gperf"
+      {"org.ee", 0},
+#line 3012 "effective_tld_names.gperf"
+      {"sokndal.no", 0},
+#line 2380 "effective_tld_names.gperf"
+      {"or.mu", 0},
+#line 2308 "effective_tld_names.gperf"
+      {"nowaruda.pl", 0},
+#line 1197 "effective_tld_names.gperf"
+      {"gouv.ml", 0},
+#line 2466 "effective_tld_names.gperf"
+      {"org.pe", 0},
+#line 2794 "effective_tld_names.gperf"
+      {"ri.it", 0},
+#line 1850 "effective_tld_names.gperf"
+      {"mad.museum", 0},
+#line 2906 "effective_tld_names.gperf"
+      {"sch.uk", 2},
+#line 3272 "effective_tld_names.gperf"
+      {"udmurtia.ru", 0},
+#line 2900 "effective_tld_names.gperf"
+      {"sch.ir", 0},
+#line 1782 "effective_tld_names.gperf"
+      {"lg.jp", 0},
+#line 3321 "effective_tld_names.gperf"
+      {"v.bg", 0},
+#line 2787 "effective_tld_names.gperf"
+      {"res.aero", 0},
+#line 2378 "effective_tld_names.gperf"
+      {"or.jp", 0},
+#line 3639 "effective_tld_names.gperf"
+      {"xn--tn0ag.hk", 0},
+#line 2459 "effective_tld_names.gperf"
+      {"org.mw", 0},
+#line 2782 "effective_tld_names.gperf"
+      {"rel.pl", 0},
+#line 2396 "effective_tld_names.gperf"
+      {"org.al", 0},
+#line 2397 "effective_tld_names.gperf"
+      {"org.an", 0},
+#line 1818 "effective_tld_names.gperf"
+      {"loyalist.museum", 0},
+#line 2391 "effective_tld_names.gperf"
+      {"org.ac", 0},
+#line 366 "effective_tld_names.gperf"
+      {"bl.uk", 1},
+#line 1236 "effective_tld_names.gperf"
+      {"gov.im", 0},
+#line 3585 "effective_tld_names.gperf"
+      {"xn--od0alg.hk", 0},
+#line 2788 "effective_tld_names.gperf"
+      {"res.in", 0},
+#line 2201 "effective_tld_names.gperf"
+      {"net.mx", 0},
+#line 1657 "effective_tld_names.gperf"
+      {"kharkov.ua", 0},
+#line 2955 "effective_tld_names.gperf"
+      {"shop.hu", 0},
+#line 2342 "effective_tld_names.gperf"
+      {"odda.no", 0},
+#line 2724 "effective_tld_names.gperf"
+      {"ptz.ru", 0},
+#line 2355 "effective_tld_names.gperf"
+      {"ol.no", 0},
+#line 2611 "effective_tld_names.gperf"
+      {"polkowice.pl", 0},
+#line 3057 "effective_tld_names.gperf"
+      {"starostwo.gov.pl", 0},
+#line 206 "effective_tld_names.gperf"
+      {"asker.no", 0},
+#line 2761 "effective_tld_names.gperf"
+      {"rana.no", 0},
+#line 2981 "effective_tld_names.gperf"
+      {"skiptvet.no", 0},
+#line 2663 "effective_tld_names.gperf"
+      {"pref.nagano.jp", 1},
+#line 944 "effective_tld_names.gperf"
+      {"emergency.aero", 0},
+#line 429 "effective_tld_names.gperf"
+      {"bykle.no", 0},
+#line 3303 "effective_tld_names.gperf"
+      {"usculture.museum", 0},
+#line 2509 "effective_tld_names.gperf"
+      {"osen.no", 0},
+#line 1955 "effective_tld_names.gperf"
+      {"mil.gh", 0},
+#line 2451 "effective_tld_names.gperf"
+      {"org.me", 0},
+#line 2478 "effective_tld_names.gperf"
+      {"org.sa", 0},
+#line 1803 "effective_tld_names.gperf"
+      {"loabat.no", 0},
+#line 3404 "effective_tld_names.gperf"
+      {"vologda.ru", 0},
+#line 1894 "effective_tld_names.gperf"
+      {"md.ci", 0},
+#line 2442 "effective_tld_names.gperf"
+      {"org.la", 0},
+#line 3050 "effective_tld_names.gperf"
+      {"stadt.museum", 0},
+#line 2445 "effective_tld_names.gperf"
+      {"org.lk", 0},
+#line 2600 "effective_tld_names.gperf"
+      {"plc.co.im", 0},
+#line 3397 "effective_tld_names.gperf"
+      {"vlog.br", 0},
+#line 3616 "effective_tld_names.gperf"
+      {"xn--skjervy-v1a.no", 0},
+#line 2622 "effective_tld_names.gperf"
+      {"portlligat.museum", 0},
+#line 2174 "effective_tld_names.gperf"
+      {"net.im", 0},
+#line 805 "effective_tld_names.gperf"
+      {"dr.na", 0},
+#line 1936 "effective_tld_names.gperf"
+      {"midatlantic.museum", 0},
+#line 3100 "effective_tld_names.gperf"
+      {"surgut.ru", 0},
+#line 2548 "effective_tld_names.gperf"
+      {"parti.se", 0},
+#line 2465 "effective_tld_names.gperf"
+      {"org.pa", 0},
+#line 2469 "effective_tld_names.gperf"
+      {"org.pk", 0},
+#line 2392 "effective_tld_names.gperf"
+      {"org.ae", 0},
+#line 1377 "effective_tld_names.gperf"
+      {"hammarfeasta.no", 0},
+#line 3357 "effective_tld_names.gperf"
+      {"verdal.no", 0},
+#line 1429 "effective_tld_names.gperf"
+      {"hokkaido.jp", 2},
+#line 2770 "effective_tld_names.gperf"
+      {"realestate.pl", 0},
+#line 1453 "effective_tld_names.gperf"
+      {"hurum.no", 0},
+#line 2511 "effective_tld_names.gperf"
+      {"oslo.no", 0},
+#line 1699 "effective_tld_names.gperf"
+      {"kraanghke.no", 0},
+#line 102 "effective_tld_names.gperf"
+      {"aip.ee", 0},
+#line 2697 "effective_tld_names.gperf"
+      {"priv.hu", 0},
+#line 588 "effective_tld_names.gperf"
+      {"coal.museum", 0},
+#line 2311 "effective_tld_names.gperf"
+      {"nrw.museum", 0},
+#line 2363 "effective_tld_names.gperf"
+      {"omsk.ru", 0},
+#line 2774 "effective_tld_names.gperf"
+      {"rec.ro", 0},
+#line 3515 "effective_tld_names.gperf"
+      {"xn--hbmer-xqa.no", 0},
+#line 2544 "effective_tld_names.gperf"
+      {"paragliding.aero", 0},
+#line 1753 "effective_tld_names.gperf"
+      {"larvik.no", 0},
+#line 1833 "effective_tld_names.gperf"
+      {"lund.no", 0},
+#line 927 "effective_tld_names.gperf"
+      {"egersund.no", 0},
+#line 2462 "effective_tld_names.gperf"
+      {"org.na", 0},
+#line 84 "effective_tld_names.gperf"
+      {"aeroport.fr", 0},
+#line 2748 "effective_tld_names.gperf"
+      {"r.se", 0},
+#line 2494 "effective_tld_names.gperf"
+      {"org.ua", 0},
+#line 3425 "effective_tld_names.gperf"
+      {"war.museum", 0},
+#line 2515 "effective_tld_names.gperf"
+      {"ostroda.pl", 0},
+#line 2430 "effective_tld_names.gperf"
+      {"org.in", 0},
+#line 2432 "effective_tld_names.gperf"
+      {"org.ir", 0},
+#line 3418 "effective_tld_names.gperf"
+      {"wa.edu.au", 0},
+#line 2933 "effective_tld_names.gperf"
+      {"sejny.pl", 0},
+#line 1551 "effective_tld_names.gperf"
+      {"iraq.museum", 0},
+#line 2433 "effective_tld_names.gperf"
+      {"org.is", 0},
+#line 1836 "effective_tld_names.gperf"
+      {"luster.no", 0},
+#line 2431 "effective_tld_names.gperf"
+      {"org.iq", 0},
+#line 760 "effective_tld_names.gperf"
+      {"d.se", 0},
+#line 3707 "effective_tld_names.gperf"
+      {"zoological.museum", 0},
+#line 2450 "effective_tld_names.gperf"
+      {"org.ma", 0},
+#line 2453 "effective_tld_names.gperf"
+      {"org.mk", 0},
+#line 2350 "effective_tld_names.gperf"
+      {"oita.jp", 2},
+#line 240 "effective_tld_names.gperf"
+      {"augustow.pl", 0},
+#line 1478 "effective_tld_names.gperf"
+      {"illustration.museum", 0},
+#line 2374 "effective_tld_names.gperf"
+      {"or.bi", 0},
+#line 2592 "effective_tld_names.gperf"
+      {"pittsburgh.museum", 0},
+#line 2647 "effective_tld_names.gperf"
+      {"pref.gunma.jp", 1},
+#line 1660 "effective_tld_names.gperf"
+      {"khv.ru", 0},
+#line 3059 "effective_tld_names.gperf"
+      {"state.museum", 0},
+#line 2910 "effective_tld_names.gperf"
+      {"school.museum", 0},
+#line 612 "effective_tld_names.gperf"
+      {"com.br", 0},
+#line 2514 "effective_tld_names.gperf"
+      {"ostre-toten.no", 0},
+#line 2679 "effective_tld_names.gperf"
+      {"pref.toyama.jp", 1},
+#line 839 "effective_tld_names.gperf"
+      {"edu.br", 0},
+#line 71 "effective_tld_names.gperf"
+      {"adm.br", 0},
+#line 237 "effective_tld_names.gperf"
+      {"ato.br", 0},
+#line 2653 "effective_tld_names.gperf"
+      {"pref.iwate.jp", 1},
+#line 613 "effective_tld_names.gperf"
+      {"com.bs", 0},
+#line 2524 "effective_tld_names.gperf"
+      {"oyer.no", 0},
+#line 840 "effective_tld_names.gperf"
+      {"edu.bs", 0},
+#line 73 "effective_tld_names.gperf"
+      {"adv.br", 0},
+#line 1106 "effective_tld_names.gperf"
+      {"g12.br", 0},
+#line 2491 "effective_tld_names.gperf"
+      {"org.to", 0},
+#line 3705 "effective_tld_names.gperf"
+      {"zlg.br", 0},
+#line 464 "effective_tld_names.gperf"
+      {"cb.it", 0},
+#line 2792 "effective_tld_names.gperf"
+      {"retina.ar", 1},
+#line 2381 "effective_tld_names.gperf"
+      {"or.na", 0},
+#line 3363 "effective_tld_names.gperf"
+      {"vestre-slidre.no", 0},
+#line 808 "effective_tld_names.gperf"
+      {"drobak.no", 0},
+#line 1213 "effective_tld_names.gperf"
+      {"gov.br", 0},
+#line 1689 "effective_tld_names.gperf"
+      {"kongsvinger.no", 0},
+#line 607 "effective_tld_names.gperf"
+      {"com.bb", 0},
+#line 183 "effective_tld_names.gperf"
+      {"arq.br", 0},
+#line 1779 "effective_tld_names.gperf"
+      {"levanger.no", 0},
+#line 834 "effective_tld_names.gperf"
+      {"edu.bb", 0},
+#line 1659 "effective_tld_names.gperf"
+      {"khmelnitskiy.ua", 0},
+#line 1214 "effective_tld_names.gperf"
+      {"gov.bs", 0},
+#line 2537 "effective_tld_names.gperf"
+      {"palace.museum", 0},
+#line 1361 "effective_tld_names.gperf"
+      {"gyeonggi.kr", 0},
+#line 2475 "effective_tld_names.gperf"
+      {"org.ro", 0},
+#line 525 "effective_tld_names.gperf"
+      {"city.shizuoka.jp", 1},
+#line 2277 "effective_tld_names.gperf"
+      {"nom.br", 0},
+#line 806 "effective_tld_names.gperf"
+      {"drammen.no", 0},
+#line 184 "effective_tld_names.gperf"
+      {"art.br", 0},
+#line 394 "effective_tld_names.gperf"
+      {"brandywinevalley.museum", 0},
+#line 972 "effective_tld_names.gperf"
+      {"etc.br", 0},
+#line 590 "effective_tld_names.gperf"
+      {"cody.museum", 0},
+#line 2300 "effective_tld_names.gperf"
+      {"not.br", 0},
+#line 1208 "effective_tld_names.gperf"
+      {"gov.bb", 0},
+#line 1934 "effective_tld_names.gperf"
+      {"michigan.museum", 0},
+#line 1856 "effective_tld_names.gperf"
+      {"maintenance.aero", 0},
+#line 2096 "effective_tld_names.gperf"
+      {"naples.it", 0},
+#line 3030 "effective_tld_names.gperf"
+      {"sosnowiec.pl", 0},
+#line 3657 "effective_tld_names.gperf"
+      {"xn--vler-qoa.hedmark.no", 0},
+#line 3329 "effective_tld_names.gperf"
+      {"vagan.no", 0},
+#line 1555 "effective_tld_names.gperf"
+      {"iron.museum", 0},
+#line 2872 "effective_tld_names.gperf"
+      {"sandiego.museum", 0},
+#line 2024 "effective_tld_names.gperf"
+      {"moscow.museum", 0},
+#line 2395 "effective_tld_names.gperf"
+      {"org.ai", 0},
+#line 1705 "effective_tld_names.gperf"
+      {"krodsherad.no", 0},
+#line 1500 "effective_tld_names.gperf"
+      {"inf.br", 0},
+#line 2155 "effective_tld_names.gperf"
+      {"net.br", 0},
+#line 545 "effective_tld_names.gperf"
+      {"cng.br", 0},
+#line 948 "effective_tld_names.gperf"
+      {"eng.br", 0},
+#line 1545 "effective_tld_names.gperf"
+      {"interactive.museum", 0},
+#line 2338 "effective_tld_names.gperf"
+      {"o.se", 0},
+#line 2512 "effective_tld_names.gperf"
+      {"osoyro.no", 0},
+#line 546 "effective_tld_names.gperf"
+      {"cnt.br", 0},
+#line 2156 "effective_tld_names.gperf"
+      {"net.bs", 0},
+#line 2502 "effective_tld_names.gperf"
+      {"orland.no", 0},
+#line 2938 "effective_tld_names.gperf"
+      {"sendai.jp", 2},
+#line 1381 "effective_tld_names.gperf"
+      {"hapmir.no", 0},
+#line 1033 "effective_tld_names.gperf"
+      {"firm.nf", 0},
+#line 3376 "effective_tld_names.gperf"
+      {"vibo-valentia.it", 0},
+#line 1482 "effective_tld_names.gperf"
+      {"imb.br", 0},
+#line 1616 "effective_tld_names.gperf"
+      {"jus.br", 0},
+#line 1824 "effective_tld_names.gperf"
+      {"ltd.gi", 0},
+#line 2151 "effective_tld_names.gperf"
+      {"net.bb", 0},
+#line 2426 "effective_tld_names.gperf"
+      {"org.hn", 0},
+#line 961 "effective_tld_names.gperf"
+      {"equipment.aero", 0},
+#line 2456 "effective_tld_names.gperf"
+      {"org.mo", 0},
+#line 719 "effective_tld_names.gperf"
+      {"coop.mv", 0},
+#line 3528 "effective_tld_names.gperf"
+      {"xn--io0a7i.cn", 0},
+#line 2690 "effective_tld_names.gperf"
+      {"presse.ci", 0},
+#line 2525 "effective_tld_names.gperf"
+      {"oygarden.no", 0},
+#line 1506 "effective_tld_names.gperf"
+      {"info.bb", 0},
+#line 2960 "effective_tld_names.gperf"
+      {"sibenik.museum", 0},
+#line 2874 "effective_tld_names.gperf"
+      {"sandnessjoen.no", 0},
+#line 3360 "effective_tld_names.gperf"
+      {"versailles.museum", 0},
+#line 3089 "effective_tld_names.gperf"
+      {"stv.ru", 0},
+#line 2107 "effective_tld_names.gperf"
+      {"nativeamerican.museum", 0},
+#line 2448 "effective_tld_names.gperf"
+      {"org.lv", 0},
+#line 2015 "effective_tld_names.gperf"
+      {"modum.no", 0},
+#line 2784 "effective_tld_names.gperf"
+      {"rennebu.no", 0},
+#line 1733 "effective_tld_names.gperf"
+      {"l.bg", 0},
+#line 1756 "effective_tld_names.gperf"
+      {"lavagis.no", 0},
+#line 296 "effective_tld_names.gperf"
+      {"bashkiria.ru", 0},
+#line 2254 "effective_tld_names.gperf"
+      {"nic.im", 0},
+#line 3336 "effective_tld_names.gperf"
+      {"vang.no", 0},
+#line 3524 "effective_tld_names.gperf"
+      {"xn--hpmir-xqa.no", 0},
+#line 293 "effective_tld_names.gperf"
+      {"barum.no", 0},
+#line 3617 "effective_tld_names.gperf"
+      {"xn--skjk-soa.no", 0},
+#line 68 "effective_tld_names.gperf"
+      {"act.gov.au", 0},
+#line 2830 "effective_tld_names.gperf"
+      {"royken.no", 0},
+#line 3036 "effective_tld_names.gperf"
+      {"spb.ru", 0},
+#line 2972 "effective_tld_names.gperf"
+      {"skanit.no", 0},
+#line 2109 "effective_tld_names.gperf"
+      {"naturalhistorymuseum.museum", 0},
+#line 2944 "effective_tld_names.gperf"
+      {"sex.pl", 0},
+#line 2742 "effective_tld_names.gperf"
+      {"qld.au", 0},
+#line 498 "effective_tld_names.gperf"
+      {"chocolate.museum", 0},
+#line 818 "effective_tld_names.gperf"
+      {"eastcoast.museum", 0},
+#line 298 "effective_tld_names.gperf"
+      {"batsfjord.no", 0},
+#line 1436 "effective_tld_names.gperf"
+      {"honefoss.no", 0},
+#line 300 "effective_tld_names.gperf"
+      {"bb", 0},
+#line 968 "effective_tld_names.gperf"
+      {"essex.museum", 0},
+#line 606 "effective_tld_names.gperf"
+      {"com.ba", 0},
+#line 833 "effective_tld_names.gperf"
+      {"edu.ba", 0},
+#line 1757 "effective_tld_names.gperf"
+      {"lavangen.no", 0},
+#line 2822 "effective_tld_names.gperf"
+      {"rome.it", 0},
+#line 3108 "effective_tld_names.gperf"
+      {"svelvik.no", 0},
+#line 1882 "effective_tld_names.gperf"
+      {"massa-carrara.it", 0},
+#line 427 "effective_tld_names.gperf"
+      {"bydgoszcz.pl", 0},
+#line 179 "effective_tld_names.gperf"
+      {"arezzo.it", 0},
+#line 1380 "effective_tld_names.gperf"
+      {"hanggliding.aero", 0},
+#line 520 "effective_tld_names.gperf"
+      {"city.okayama.jp", 1},
+#line 3578 "effective_tld_names.gperf"
+      {"xn--muost-0qa.no", 0},
+#line 1207 "effective_tld_names.gperf"
+      {"gov.ba", 0},
+#line 776 "effective_tld_names.gperf"
+      {"defense.tn", 0},
+#line 1456 "effective_tld_names.gperf"
+      {"hyogo.jp", 2},
+#line 2817 "effective_tld_names.gperf"
+      {"rockart.museum", 0},
+#line 1513 "effective_tld_names.gperf"
+      {"info.mv", 0},
+#line 1738 "effective_tld_names.gperf"
+      {"laakesvuemie.no", 0},
+#line 31 "effective_tld_names.gperf"
+      {"abo.pa", 0},
+#line 2572 "effective_tld_names.gperf"
+      {"pf", 0},
+#line 1807 "effective_tld_names.gperf"
+      {"lodingen.no", 0},
+#line 1664 "effective_tld_names.gperf"
+      {"kiev.ua", 0},
+#line 2019 "effective_tld_names.gperf"
+      {"monmouth.museum", 0},
+#line 2773 "effective_tld_names.gperf"
+      {"rec.nf", 0},
+#line 609 "effective_tld_names.gperf"
+      {"com.bi", 0},
+#line 836 "effective_tld_names.gperf"
+      {"edu.bi", 0},
+#line 1994 "effective_tld_names.gperf"
+      {"mk.ua", 0},
+#line 2912 "effective_tld_names.gperf"
+      {"schweiz.museum", 0},
+#line 3004 "effective_tld_names.gperf"
+      {"snz.ru", 0},
+#line 2458 "effective_tld_names.gperf"
+      {"org.mv", 0},
+#line 2695 "effective_tld_names.gperf"
+      {"principe.st", 0},
+#line 3330 "effective_tld_names.gperf"
+      {"vagsoy.no", 0},
+#line 3377 "effective_tld_names.gperf"
+      {"vibovalentia.it", 0},
+#line 3584 "effective_tld_names.gperf"
+      {"xn--od0alg.cn", 0},
+#line 1777 "effective_tld_names.gperf"
+      {"lerdal.no", 0},
+#line 509 "effective_tld_names.gperf"
+      {"circus.museum", 0},
+#line 2497 "effective_tld_names.gperf"
+      {"org.vn", 0},
+#line 3334 "effective_tld_names.gperf"
+      {"valle.no", 0},
+#line 2495 "effective_tld_names.gperf"
+      {"org.vc", 0},
+#line 3683 "effective_tld_names.gperf"
+      {"york.museum", 0},
+#line 1318 "effective_tld_names.gperf"
+      {"groundhandling.aero", 0},
+#line 364 "effective_tld_names.gperf"
+      {"bjugn.no", 0},
+#line 1492 "effective_tld_names.gperf"
+      {"ind.br", 0},
+#line 2150 "effective_tld_names.gperf"
+      {"net.ba", 0},
+#line 506 "effective_tld_names.gperf"
+      {"cim.br", 0},
+#line 1409 "effective_tld_names.gperf"
+      {"historical.museum", 0},
+#line 387 "effective_tld_names.gperf"
+      {"botanicgarden.museum", 0},
+#line 308 "effective_tld_names.gperf"
+      {"bedzin.pl", 0},
+#line 2750 "effective_tld_names.gperf"
+      {"rade.no", 0},
+#line 797 "effective_tld_names.gperf"
+      {"dni.us", 0},
+#line 3510 "effective_tld_names.gperf"
+      {"xn--gls-elac.no", 0},
+#line 1811 "effective_tld_names.gperf"
+      {"london.museum", 0},
+#line 2097 "effective_tld_names.gperf"
+      {"napoli.it", 0},
+#line 1132 "effective_tld_names.gperf"
+      {"gemological.museum", 0},
+#line 746 "effective_tld_names.gperf"
+      {"culturalcenter.museum", 0},
+#line 3335 "effective_tld_names.gperf"
+      {"valley.museum", 0},
+#line 2467 "effective_tld_names.gperf"
+      {"org.pf", 0},
+#line 2425 "effective_tld_names.gperf"
+      {"org.hk", 0},
+#line 2785 "effective_tld_names.gperf"
+      {"rennesoy.no", 0},
+#line 1560 "effective_tld_names.gperf"
+      {"ishikawa.jp", 2},
+#line 1828 "effective_tld_names.gperf"
+      {"lubin.pl", 0},
+#line 3099 "effective_tld_names.gperf"
+      {"surgeonshall.museum", 0},
+#line 244 "effective_tld_names.gperf"
+      {"aurskog-holand.no", 0},
+#line 2017 "effective_tld_names.gperf"
+      {"moma.museum", 0},
+#line 611 "effective_tld_names.gperf"
+      {"com.bo", 0},
+#line 1613 "effective_tld_names.gperf"
+      {"juedisches.museum", 0},
+#line 838 "effective_tld_names.gperf"
+      {"edu.bo", 0},
+#line 2334 "effective_tld_names.gperf"
+      {"nyny.museum", 0},
+#line 1602 "effective_tld_names.gperf"
+      {"jor.br", 0},
+#line 3656 "effective_tld_names.gperf"
+      {"xn--vgsy-qoa0j.no", 0},
+#line 1071 "effective_tld_names.gperf"
+      {"fot.br", 0},
+#line 1175 "effective_tld_names.gperf"
+      {"gob.bo", 0},
+#line 3682 "effective_tld_names.gperf"
+      {"yokohama.jp", 2},
+#line 1092 "effective_tld_names.gperf"
+      {"fst.br", 0},
+#line 448 "effective_tld_names.gperf"
+      {"can.br", 0},
+#line 2717 "effective_tld_names.gperf"
+      {"przeworsk.pl", 0},
+#line 3096 "effective_tld_names.gperf"
+      {"sumy.ua", 0},
+#line 1212 "effective_tld_names.gperf"
+      {"gov.bo", 0},
+#line 3662 "effective_tld_names.gperf"
+      {"xn--wcvs22d.hk", 0},
+#line 2666 "effective_tld_names.gperf"
+      {"pref.niigata.jp", 1},
+#line 2673 "effective_tld_names.gperf"
+      {"pref.shiga.jp", 1},
+#line 784 "effective_tld_names.gperf"
+      {"detroit.museum", 0},
+#line 942 "effective_tld_names.gperf"
+      {"embaixada.st", 0},
+#line 809 "effective_tld_names.gperf"
+      {"dudinka.ru", 0},
+#line 1721 "effective_tld_names.gperf"
+      {"kvalsund.no", 0},
+#line 3610 "effective_tld_names.gperf"
+      {"xn--s-1fa.no", 0},
+#line 86 "effective_tld_names.gperf"
+      {"afjord.no", 0},
+#line 1814 "effective_tld_names.gperf"
+      {"losangeles.museum", 0},
+#line 3545 "effective_tld_names.gperf"
+      {"xn--l-1fa.no", 0},
+#line 2323 "effective_tld_names.gperf"
+      {"ntr.br", 0},
+#line 1751 "effective_tld_names.gperf"
+      {"lardal.no", 0},
+#line 1876 "effective_tld_names.gperf"
+      {"marketplace.aero", 0},
+#line 1793 "effective_tld_names.gperf"
+      {"lindas.no", 0},
+#line 1794 "effective_tld_names.gperf"
+      {"lindesnes.no", 0},
+#line 119 "effective_tld_names.gperf"
+      {"alabama.museum", 0},
+#line 3608 "effective_tld_names.gperf"
+      {"xn--ryken-vua.no", 0},
+#line 923 "effective_tld_names.gperf"
+      {"educator.aero", 0},
+#line 2087 "effective_tld_names.gperf"
+      {"name.my", 0},
+#line 3559 "effective_tld_names.gperf"
+      {"xn--lt-liac.no", 0},
+#line 1544 "effective_tld_names.gperf"
+      {"intelligence.museum", 0},
+#line 1800 "effective_tld_names.gperf"
+      {"lk", 0},
+#line 2154 "effective_tld_names.gperf"
+      {"net.bo", 0},
+#line 2393 "effective_tld_names.gperf"
+      {"org.af", 0},
+#line 3552 "effective_tld_names.gperf"
+      {"xn--lgrd-poac.no", 0},
+#line 2798 "effective_tld_names.gperf"
+      {"rimini.it", 0},
+#line 381 "effective_tld_names.gperf"
+      {"bolzano.it", 0},
+#line 2980 "effective_tld_names.gperf"
+      {"skierva.no", 0},
+#line 1530 "effective_tld_names.gperf"
+      {"int.bo", 0},
+#line 283 "effective_tld_names.gperf"
+      {"balsfjord.no", 0},
+#line 3185 "effective_tld_names.gperf"
+      {"to", 0},
+#line 3264 "effective_tld_names.gperf"
+      {"tz", 0},
+#line 3141 "effective_tld_names.gperf"
+      {"td", 0},
+#line 2557 "effective_tld_names.gperf"
+      {"pe.ca", 0},
+#line 3239 "effective_tld_names.gperf"
+      {"tt", 0},
+#line 3170 "effective_tld_names.gperf"
+      {"tl", 0},
+#line 3500 "effective_tld_names.gperf"
+      {"xn--fjord-lra.no", 0},
+#line 383 "effective_tld_names.gperf"
+      {"bonn.museum", 0},
+#line 2060 "effective_tld_names.gperf"
+      {"music.museum", 0},
+#line 386 "effective_tld_names.gperf"
+      {"botanicalgarden.museum", 0},
+#line 3186 "effective_tld_names.gperf"
+      {"to.it", 0},
+#line 3523 "effective_tld_names.gperf"
+      {"xn--holtlen-hxa.no", 0},
+#line 3236 "effective_tld_names.gperf"
+      {"ts.it", 0},
+#line 3207 "effective_tld_names.gperf"
+      {"tr", 2},
+#line 2122 "effective_tld_names.gperf"
+      {"nc.us", 0},
+#line 2366 "effective_tld_names.gperf"
+      {"ontario.museum", 0},
+#line 1628 "effective_tld_names.gperf"
+      {"kaluga.ru", 0},
+#line 2635 "effective_tld_names.gperf"
+      {"prd.fr", 0},
+#line 1991 "effective_tld_names.gperf"
+      {"miyazaki.jp", 2},
+#line 126 "effective_tld_names.gperf"
+      {"alstahaug.no", 0},
+#line 3208 "effective_tld_names.gperf"
+      {"tr.it", 0},
+#line 341 "effective_tld_names.gperf"
+      {"bio.br", 0},
+#line 2842 "effective_tld_names.gperf"
+      {"rybnik.pl", 0},
+#line 273 "effective_tld_names.gperf"
+      {"bahn.museum", 0},
+#line 3142 "effective_tld_names.gperf"
+      {"te.it", 0},
+#line 196 "effective_tld_names.gperf"
+      {"arts.museum", 0},
+#line 1778 "effective_tld_names.gperf"
+      {"lesja.no", 0},
+#line 1725 "effective_tld_names.gperf"
+      {"kvinnherad.no", 0},
+#line 3206 "effective_tld_names.gperf"
+      {"tp.it", 0},
+#line 2775 "effective_tld_names.gperf"
+      {"recreation.aero", 0},
+#line 1746 "effective_tld_names.gperf"
+      {"landes.museum", 0},
+#line 1641 "effective_tld_names.gperf"
+      {"katowice.pl", 0},
+#line 369 "effective_tld_names.gperf"
+      {"bmd.br", 0},
+#line 3561 "effective_tld_names.gperf"
+      {"xn--lury-ira.no", 0},
+#line 378 "effective_tld_names.gperf"
+      {"boleslawiec.pl", 0},
+#line 287 "effective_tld_names.gperf"
+      {"barcelona.museum", 0},
+#line 3182 "effective_tld_names.gperf"
+      {"tn", 0},
+#line 1115 "effective_tld_names.gperf"
+      {"gangaviika.no", 0},
+#line 79 "effective_tld_names.gperf"
+      {"aero.mv", 0},
+#line 1691 "effective_tld_names.gperf"
+      {"konskowola.pl", 0},
+#line 2871 "effective_tld_names.gperf"
+      {"sandefjord.no", 0},
+#line 2496 "effective_tld_names.gperf"
+      {"org.vi", 0},
+#line 2264 "effective_tld_names.gperf"
+      {"nkz.ru", 0},
+#line 3587 "effective_tld_names.gperf"
+      {"xn--oppegrd-ixa.no", 0},
+#line 3183 "effective_tld_names.gperf"
+      {"tn.it", 0},
+#line 765 "effective_tld_names.gperf"
+      {"dallas.museum", 0},
+#line 1742 "effective_tld_names.gperf"
+      {"lajolla.museum", 0},
+#line 3171 "effective_tld_names.gperf"
+      {"tm", 0},
+#line 2877 "effective_tld_names.gperf"
+      {"sanok.pl", 0},
+#line 3337 "effective_tld_names.gperf"
+      {"vantaa.museum", 0},
+#line 2793 "effective_tld_names.gperf"
+      {"rg.it", 0},
+#line 3349 "effective_tld_names.gperf"
+      {"vefsn.no", 0},
+#line 349 "effective_tld_names.gperf"
+      {"biz.bb", 0},
+#line 1155 "effective_tld_names.gperf"
+      {"glas.museum", 0},
+#line 2583 "effective_tld_names.gperf"
+      {"photography.museum", 0},
+#line 3178 "effective_tld_names.gperf"
+      {"tm.pl", 0},
+#line 2486 "effective_tld_names.gperf"
+      {"org.st", 0},
+#line 2492 "effective_tld_names.gperf"
+      {"org.tt", 0},
+#line 1412 "effective_tld_names.gperf"
+      {"historisch.museum", 0},
+#line 3386 "effective_tld_names.gperf"
+      {"village.museum", 0},
+#line 2051 "effective_tld_names.gperf"
+      {"mus.br", 0},
+#line 1797 "effective_tld_names.gperf"
+      {"living.museum", 0},
+#line 515 "effective_tld_names.gperf"
+      {"city.kitakyushu.jp", 1},
+#line 1752 "effective_tld_names.gperf"
+      {"larsson.museum", 0},
+#line 2105 "effective_tld_names.gperf"
+      {"nationalfirearms.museum", 0},
+#line 2987 "effective_tld_names.gperf"
+      {"skole.museum", 0},
+#line 2797 "effective_tld_names.gperf"
+      {"riik.ee", 0},
+#line 339 "effective_tld_names.gperf"
+      {"bill.museum", 0},
+#line 2365 "effective_tld_names.gperf"
+      {"online.museum", 0},
+#line 1663 "effective_tld_names.gperf"
+      {"kids.us", 0},
+#line 271 "effective_tld_names.gperf"
+      {"bahcavuotna.no", 0},
+#line 2474 "effective_tld_names.gperf"
+      {"org.pt", 0},
+#line 3342 "effective_tld_names.gperf"
+      {"varoy.no", 0},
+#line 974 "effective_tld_names.gperf"
+      {"eti.br", 0},
+#line 3607 "effective_tld_names.gperf"
+      {"xn--rsta-fra.no", 0},
+#line 3573 "effective_tld_names.gperf"
+      {"xn--moreke-jua.no", 0},
+#line 1069 "effective_tld_names.gperf"
+      {"forum.hu", 0},
+#line 3563 "effective_tld_names.gperf"
+      {"xn--merker-kua.no", 0},
+#line 2259 "effective_tld_names.gperf"
+      {"niigata.jp", 2},
+#line 3126 "effective_tld_names.gperf"
+      {"ta.it", 0},
+#line 786 "effective_tld_names.gperf"
+      {"dielddanuorri.no", 0},
+#line 1413 "effective_tld_names.gperf"
+      {"historisches.museum", 0},
+#line 2840 "effective_tld_names.gperf"
+      {"rw", 0},
+#line 1057 "effective_tld_names.gperf"
+      {"fnd.br", 0},
+#line 1989 "effective_tld_names.gperf"
+      {"missoula.museum", 0},
+#line 146 "effective_tld_names.gperf"
+      {"amusement.aero", 0},
+#line 323 "effective_tld_names.gperf"
+      {"bern.museum", 0},
+#line 58 "effective_tld_names.gperf"
+      {"ac.tj", 0},
+#line 1209 "effective_tld_names.gperf"
+      {"gov.bf", 0},
+#line 3246 "effective_tld_names.gperf"
+      {"tuva.ru", 0},
+#line 2053 "effective_tld_names.gperf"
+      {"museum", 0},
+#line 2582 "effective_tld_names.gperf"
+      {"phoenix.museum", 0},
+#line 1407 "effective_tld_names.gperf"
+      {"hiroshima.jp", 2},
+#line 2826 "effective_tld_names.gperf"
+      {"rost.no", 0},
+#line 1116 "effective_tld_names.gperf"
+      {"gangwon.kr", 0},
+#line 3179 "effective_tld_names.gperf"
+      {"tm.ro", 0},
+#line 2764 "effective_tld_names.gperf"
+      {"ravenna.it", 0},
+#line 959 "effective_tld_names.gperf"
+      {"environmentalconservation.museum", 0},
+#line 1884 "effective_tld_names.gperf"
+      {"mat.br", 0},
+#line 720 "effective_tld_names.gperf"
+      {"coop.mw", 0},
+#line 2057 "effective_tld_names.gperf"
+      {"museum.tt", 0},
+#line 3391 "effective_tld_names.gperf"
+      {"virtuel.museum", 0},
+#line 1715 "effective_tld_names.gperf"
+      {"kursk.ru", 0},
+#line 3501 "effective_tld_names.gperf"
+      {"xn--fl-zia.no", 0},
+#line 1791 "effective_tld_names.gperf"
+      {"limanowa.pl", 0},
+#line 3209 "effective_tld_names.gperf"
+      {"tr.no", 0},
+#line 2904 "effective_tld_names.gperf"
+      {"sch.ly", 0},
+#line 2348 "effective_tld_names.gperf"
+      {"og.ao", 0},
+#line 2925 "effective_tld_names.gperf"
+      {"sd.cn", 0},
+#line 1731 "effective_tld_names.gperf"
+      {"kyoto.jp", 2},
+#line 1770 "effective_tld_names.gperf"
+      {"leikanger.no", 0},
+#line 2048 "effective_tld_names.gperf"
+      {"muncie.museum", 0},
+#line 3629 "effective_tld_names.gperf"
+      {"xn--sr-fron-q1a.no", 0},
+#line 1630 "effective_tld_names.gperf"
+      {"kanagawa.jp", 2},
+#line 3117 "effective_tld_names.gperf"
+      {"sykkylven.no", 0},
+#line 1411 "effective_tld_names.gperf"
+      {"historichouses.museum", 0},
+#line 2422 "effective_tld_names.gperf"
+      {"org.gn", 0},
+#line 2424 "effective_tld_names.gperf"
+      {"org.gr", 0},
+#line 1335 "effective_tld_names.gperf"
+      {"gs.of.no", 0},
+#line 1900 "effective_tld_names.gperf"
+      {"med.br", 0},
+#line 2343 "effective_tld_names.gperf"
+      {"odessa.ua", 0},
+#line 2578 "effective_tld_names.gperf"
+      {"pharmacy.museum", 0},
+#line 1387 "effective_tld_names.gperf"
+      {"hattfjelldal.no", 0},
+#line 2423 "effective_tld_names.gperf"
+      {"org.gp", 0},
+#line 2056 "effective_tld_names.gperf"
+      {"museum.no", 0},
+#line 994 "effective_tld_names.gperf"
+      {"farm.museum", 0},
+#line 1815 "effective_tld_names.gperf"
+      {"loten.no", 0},
+#line 3145 "effective_tld_names.gperf"
+      {"tel", 0},
+#line 3463 "effective_tld_names.gperf"
+      {"xj.cn", 0},
+#line 3704 "effective_tld_names.gperf"
+      {"zj.cn", 0},
+#line 3268 "effective_tld_names.gperf"
+      {"uba.ar", 1},
+#line 1844 "effective_tld_names.gperf"
+      {"lyngen.no", 0},
+#line 3174 "effective_tld_names.gperf"
+      {"tm.km", 0},
+#line 3177 "effective_tld_names.gperf"
+      {"tm.no", 0},
+#line 1739 "effective_tld_names.gperf"
+      {"labor.museum", 0},
+#line 2755 "effective_tld_names.gperf"
+      {"raholt.no", 0},
+#line 3602 "effective_tld_names.gperf"
+      {"xn--rlingen-mxa.no", 0},
+#line 2999 "effective_tld_names.gperf"
+      {"sn.cn", 0},
+#line 3411 "effective_tld_names.gperf"
+      {"vt.us", 0},
+#line 2086 "effective_tld_names.gperf"
+      {"name.mv", 0},
+#line 3390 "effective_tld_names.gperf"
+      {"virtual.museum", 0},
+#line 1022 "effective_tld_names.gperf"
+      {"film.museum", 0},
+#line 1570 "effective_tld_names.gperf"
+      {"iwate.jp", 2},
+#line 3421 "effective_tld_names.gperf"
+      {"wakayama.jp", 2},
+#line 2850 "effective_tld_names.gperf"
+      {"sa.cr", 0},
+#line 1754 "effective_tld_names.gperf"
+      {"laspezia.it", 0},
+#line 501 "effective_tld_names.gperf"
+      {"chungbuk.kr", 0},
+#line 3327 "effective_tld_names.gperf"
+      {"vadso.no", 0},
+#line 3381 "effective_tld_names.gperf"
+      {"vicenza.it", 0},
+#line 2367 "effective_tld_names.gperf"
+      {"openair.museum", 0},
+#line 2945 "effective_tld_names.gperf"
+      {"sf.no", 0},
+#line 2418 "effective_tld_names.gperf"
+      {"org.ge", 0},
+#line 2384 "effective_tld_names.gperf"
+      {"or.tz", 0},
+#line 2382 "effective_tld_names.gperf"
+      {"or.pw", 0},
+#line 3230 "effective_tld_names.gperf"
+      {"tromsa.no", 0},
+#line 3128 "effective_tld_names.gperf"
+      {"tana.no", 0},
+#line 2483 "effective_tld_names.gperf"
+      {"org.sg", 0},
+#line 787 "effective_tld_names.gperf"
+      {"dinosaur.museum", 0},
+#line 2498 "effective_tld_names.gperf"
+      {"org.ws", 0},
+#line 2513 "effective_tld_names.gperf"
+      {"osteroy.no", 0},
+#line 3507 "effective_tld_names.gperf"
+      {"xn--gildeskl-g0a.no", 0},
+#line 3082 "effective_tld_names.gperf"
+      {"storfjord.no", 0},
+#line 768 "effective_tld_names.gperf"
+      {"davvesiida.no", 0},
+#line 1483 "effective_tld_names.gperf"
+      {"imperia.it", 0},
+#line 3313 "effective_tld_names.gperf"
+      {"utazas.hu", 0},
+#line 3513 "effective_tld_names.gperf"
+      {"xn--h-2fa.no", 0},
+#line 3380 "effective_tld_names.gperf"
+      {"vic.gov.au", 0},
+#line 778 "effective_tld_names.gperf"
+      {"delmenhorst.museum", 0},
+#line 2487 "effective_tld_names.gperf"
+      {"org.sy", 0},
+#line 2449 "effective_tld_names.gperf"
+      {"org.ly", 0},
+#line 992 "effective_tld_names.gperf"
+      {"far.br", 0},
+#line 2655 "effective_tld_names.gperf"
+      {"pref.kagoshima.jp", 1},
+#line 100 "effective_tld_names.gperf"
+      {"aichi.jp", 2},
+#line 2741 "effective_tld_names.gperf"
+      {"qh.cn", 0},
+#line 782 "effective_tld_names.gperf"
+      {"design.aero", 0},
+#line 1614 "effective_tld_names.gperf"
+      {"juif.museum", 0},
+#line 3218 "effective_tld_names.gperf"
+      {"travel", 0},
+#line 2990 "effective_tld_names.gperf"
+      {"slask.pl", 0},
+#line 3220 "effective_tld_names.gperf"
+      {"travel.tt", 0},
+#line 507 "effective_tld_names.gperf"
+      {"cincinnati.museum", 0},
+#line 444 "effective_tld_names.gperf"
+      {"california.museum", 0},
+#line 785 "effective_tld_names.gperf"
+      {"dgca.aero", 0},
+#line 1831 "effective_tld_names.gperf"
+      {"lugansk.ua", 0},
+#line 3344 "effective_tld_names.gperf"
+      {"vc", 0},
+#line 3422 "effective_tld_names.gperf"
+      {"walbrzych.pl", 0},
+#line 3172 "effective_tld_names.gperf"
+      {"tm.fr", 0},
+#line 1983 "effective_tld_names.gperf"
+      {"mill.museum", 0},
+#line 3240 "effective_tld_names.gperf"
+      {"tula.ru", 0},
+#line 1372 "effective_tld_names.gperf"
+      {"halloffame.museum", 0},
+#line 3158 "effective_tld_names.gperf"
+      {"th", 0},
+#line 2463 "effective_tld_names.gperf"
+      {"org.ng", 0},
+#line 2414 "effective_tld_names.gperf"
+      {"org.dz", 0},
+#line 3407 "effective_tld_names.gperf"
+      {"vossevangen.no", 0},
+#line 2488 "effective_tld_names.gperf"
+      {"org.sz", 0},
+#line 2639 "effective_tld_names.gperf"
+      {"pref.akita.jp", 1},
+#line 1868 "effective_tld_names.gperf"
+      {"manx.museum", 0},
+#line 1764 "effective_tld_names.gperf"
+      {"leasing.aero", 0},
+#line 2670 "effective_tld_names.gperf"
+      {"pref.osaka.jp", 1},
+#line 1835 "effective_tld_names.gperf"
+      {"luroy.no", 0},
+#line 3021 "effective_tld_names.gperf"
+      {"sor-aurdal.no", 0},
+#line 3263 "effective_tld_names.gperf"
+      {"tyumen.ru", 0},
+#line 3345 "effective_tld_names.gperf"
+      {"vc.it", 0},
+#line 3219 "effective_tld_names.gperf"
+      {"travel.pl", 0},
+#line 3125 "effective_tld_names.gperf"
+      {"t.se", 0},
+#line 2984 "effective_tld_names.gperf"
+      {"sklep.pl", 0},
+#line 278 "effective_tld_names.gperf"
+      {"bale.museum", 0},
+#line 3325 "effective_tld_names.gperf"
+      {"va.us", 0},
+#line 2571 "effective_tld_names.gperf"
+      {"pescara.it", 0},
+#line 2452 "effective_tld_names.gperf"
+      {"org.mg", 0},
+#line 1320 "effective_tld_names.gperf"
+      {"grozny.ru", 0},
+#line 1687 "effective_tld_names.gperf"
+      {"komvux.se", 0},
+#line 817 "effective_tld_names.gperf"
+      {"eastafrica.museum", 0},
+#line 1843 "effective_tld_names.gperf"
+      {"lyngdal.no", 0},
+#line 1462 "effective_tld_names.gperf"
+      {"ibestad.no", 0},
+#line 2461 "effective_tld_names.gperf"
+      {"org.my", 0},
+#line 361 "effective_tld_names.gperf"
+      {"bj.cn", 0},
+#line 1345 "effective_tld_names.gperf"
+      {"gs.vf.no", 0},
+#line 3231 "effective_tld_names.gperf"
+      {"tromso.no", 0},
+#line 2394 "effective_tld_names.gperf"
+      {"org.ag", 0},
+#line 2421 "effective_tld_names.gperf"
+      {"org.gi", 0},
+#line 2851 "effective_tld_names.gperf"
+      {"sa.edu.au", 0},
+#line 3536 "effective_tld_names.gperf"
+      {"xn--krager-gya.no", 0},
+#line 921 "effective_tld_names.gperf"
+      {"education.museum", 0},
+#line 3588 "effective_tld_names.gperf"
+      {"xn--ostery-fya.no", 0},
+#line 2837 "effective_tld_names.gperf"
+      {"ruovat.no", 0},
+#line 2503 "effective_tld_names.gperf"
+      {"orskog.no", 0},
+#line 2683 "effective_tld_names.gperf"
+      {"pref.yamanashi.jp", 1},
+#line 3533 "effective_tld_names.gperf"
+      {"xn--kfjord-iua.no", 0},
+#line 3399 "effective_tld_names.gperf"
+      {"vn.ua", 0},
+#line 3212 "effective_tld_names.gperf"
+      {"trainer.aero", 0},
+#line 3039 "effective_tld_names.gperf"
+      {"spy.museum", 0},
+#line 3135 "effective_tld_names.gperf"
+      {"tas.edu.au", 0},
+#line 3672 "effective_tld_names.gperf"
+      {"yakutia.ru", 0},
+#line 2591 "effective_tld_names.gperf"
+      {"pisz.pl", 0},
+#line 1863 "effective_tld_names.gperf"
+      {"manchester.museum", 0},
+#line 3201 "effective_tld_names.gperf"
+      {"tourism.pl", 0},
+#line 345 "effective_tld_names.gperf"
+      {"birthplace.museum", 0},
+#line 1420 "effective_tld_names.gperf"
+      {"hk.cn", 0},
+#line 3455 "effective_tld_names.gperf"
+      {"wroclaw.pl", 0},
+#line 1640 "effective_tld_names.gperf"
+      {"kaszuby.pl", 0},
+#line 2427 "effective_tld_names.gperf"
+      {"org.ht", 0},
+#line 1917 "effective_tld_names.gperf"
+      {"medizinhistorisches.museum", 0},
+#line 3137 "effective_tld_names.gperf"
+      {"tatarstan.ru", 0},
+#line 3227 "effective_tld_names.gperf"
+      {"troandin.no", 0},
+#line 1949 "effective_tld_names.gperf"
+      {"mil.br", 0},
+#line 2691 "effective_tld_names.gperf"
+      {"presse.fr", 0},
+#line 2398 "effective_tld_names.gperf"
+      {"org.az", 0},
+#line 3247 "effective_tld_names.gperf"
+      {"tv", 0},
+#line 3202 "effective_tld_names.gperf"
+      {"tourism.tn", 0},
+#line 3400 "effective_tld_names.gperf"
+      {"voagat.no", 0},
+#line 3261 "effective_tld_names.gperf"
+      {"tysnes.no", 0},
+#line 3375 "effective_tld_names.gperf"
+      {"vi.us", 0},
+#line 512 "effective_tld_names.gperf"
+      {"city.hiroshima.jp", 1},
+#line 2747 "effective_tld_names.gperf"
+      {"r.bg", 0},
+#line 2914 "effective_tld_names.gperf"
+      {"science.museum", 0},
+#line 2920 "effective_tld_names.gperf"
+      {"sciences.museum", 0},
+#line 1141 "effective_tld_names.gperf"
+      {"ggf.br", 0},
+#line 1625 "effective_tld_names.gperf"
+      {"kagoshima.jp", 2},
+#line 783 "effective_tld_names.gperf"
+      {"design.museum", 0},
+#line 3250 "effective_tld_names.gperf"
+      {"tv.it", 0},
+#line 2983 "effective_tld_names.gperf"
+      {"skjervoy.no", 0},
+#line 791 "effective_tld_names.gperf"
+      {"dj", 0},
+#line 3521 "effective_tld_names.gperf"
+      {"xn--hnefoss-q1a.no", 0},
+#line 526 "effective_tld_names.gperf"
+      {"city.yokohama.jp", 1},
+#line 759 "effective_tld_names.gperf"
+      {"d.bg", 0},
+#line 3680 "effective_tld_names.gperf"
+      {"yk.ca", 0},
+#line 1037 "effective_tld_names.gperf"
+      {"fj.cn", 0},
+#line 2568 "effective_tld_names.gperf"
+      {"perugia.it", 0},
+#line 3248 "effective_tld_names.gperf"
+      {"tv.bo", 0},
+#line 135 "effective_tld_names.gperf"
+      {"ambulance.aero", 0},
+#line 1527 "effective_tld_names.gperf"
+      {"insurance.aero", 0},
+#line 3022 "effective_tld_names.gperf"
+      {"sor-fron.no", 0},
+#line 1927 "effective_tld_names.gperf"
+      {"metro.tokyo.jp", 1},
+#line 3249 "effective_tld_names.gperf"
+      {"tv.br", 0},
+#line 3403 "effective_tld_names.gperf"
+      {"volkenkunde.museum", 0},
+#line 2585 "effective_tld_names.gperf"
+      {"piacenza.it", 0},
+#line 983 "effective_tld_names.gperf"
+      {"exchange.aero", 0},
+#line 2819 "effective_tld_names.gperf"
+      {"rollag.no", 0},
+#line 2948 "effective_tld_names.gperf"
+      {"sh.cn", 0},
+#line 2781 "effective_tld_names.gperf"
+      {"rel.ht", 0},
+#line 1060 "effective_tld_names.gperf"
+      {"folkebibl.no", 0},
+#line 2783 "effective_tld_names.gperf"
+      {"rendalen.no", 0},
+#line 3164 "effective_tld_names.gperf"
+      {"tinn.no", 0},
+#line 2838 "effective_tld_names.gperf"
+      {"russia.museum", 0},
+#line 514 "effective_tld_names.gperf"
+      {"city.kawasaki.jp", 1},
+#line 3447 "effective_tld_names.gperf"
+      {"wlocl.pl", 0},
+#line 1084 "effective_tld_names.gperf"
+      {"fribourg.museum", 0},
+#line 1773 "effective_tld_names.gperf"
+      {"leka.no", 0},
+#line 922 "effective_tld_names.gperf"
+      {"educational.museum", 0},
+#line 1122 "effective_tld_names.gperf"
+      {"gb.net", 0},
+#line 614 "effective_tld_names.gperf"
+      {"com.by", 0},
+#line 2552 "effective_tld_names.gperf"
+      {"pb.ao", 0},
+#line 121 "effective_tld_names.gperf"
+      {"aland.fi", 0},
+#line 2901 "effective_tld_names.gperf"
+      {"sch.je", 0},
+#line 3494 "effective_tld_names.gperf"
+      {"xn--davvenjrga-y4a.no", 0},
+#line 2313 "effective_tld_names.gperf"
+      {"nsk.ru", 0},
+#line 3698 "effective_tld_names.gperf"
+      {"zaporizhzhe.ua", 0},
+#line 1215 "effective_tld_names.gperf"
+      {"gov.by", 0},
+#line 1643 "effective_tld_names.gperf"
+      {"kawasaki.jp", 2},
+#line 2703 "effective_tld_names.gperf"
+      {"pro.br", 0},
+#line 3161 "effective_tld_names.gperf"
+      {"time.no", 0},
+#line 2337 "effective_tld_names.gperf"
+      {"o.bg", 0},
+#line 2721 "effective_tld_names.gperf"
+      {"pskov.ru", 0},
+#line 2828 "effective_tld_names.gperf"
+      {"rovigo.it", 0},
+#line 3180 "effective_tld_names.gperf"
+      {"tm.se", 0},
+#line 2719 "effective_tld_names.gperf"
+      {"psc.br", 0},
+#line 2630 "effective_tld_names.gperf"
+      {"ppg.br", 0},
+#line 1736 "effective_tld_names.gperf"
+      {"la-spezia.it", 0},
+#line 3304 "effective_tld_names.gperf"
+      {"usdecorativearts.museum", 0},
+#line 807 "effective_tld_names.gperf"
+      {"drangedal.no", 0},
+#line 615 "effective_tld_names.gperf"
+      {"com.bz", 0},
+#line 3262 "effective_tld_names.gperf"
+      {"tysvar.no", 0},
+#line 841 "effective_tld_names.gperf"
+      {"edu.bz", 0},
+#line 3382 "effective_tld_names.gperf"
+      {"video.hu", 0},
+#line 803 "effective_tld_names.gperf"
+      {"dovre.no", 0},
+#line 1131 "effective_tld_names.gperf"
+      {"geelvinck.museum", 0},
+#line 1370 "effective_tld_names.gperf"
+      {"hagebostad.no", 0},
+#line 1947 "effective_tld_names.gperf"
+      {"mil.ba", 0},
+#line 1760 "effective_tld_names.gperf"
+      {"lc", 0},
+#line 2756 "effective_tld_names.gperf"
+      {"railroad.museum", 0},
+#line 1683 "effective_tld_names.gperf"
+      {"komforb.se", 0},
+#line 619 "effective_tld_names.gperf"
+      {"com.cu", 0},
+#line 726 "effective_tld_names.gperf"
+      {"costume.museum", 0},
+#line 845 "effective_tld_names.gperf"
+      {"edu.cu", 0},
+#line 2675 "effective_tld_names.gperf"
+      {"pref.shizuoka.jp", 1},
+#line 1216 "effective_tld_names.gperf"
+      {"gov.bz", 0},
+#line 3127 "effective_tld_names.gperf"
+      {"tambov.ru", 0},
+#line 2383 "effective_tld_names.gperf"
+      {"or.th", 0},
+#line 2536 "effective_tld_names.gperf"
+      {"padua.it", 0},
+#line 2370 "effective_tld_names.gperf"
+      {"opole.pl", 0},
+#line 3527 "effective_tld_names.gperf"
+      {"xn--indery-fya.no", 0},
+#line 1761 "effective_tld_names.gperf"
+      {"lc.it", 0},
+#line 3253 "effective_tld_names.gperf"
+      {"tver.ru", 0},
+#line 1222 "effective_tld_names.gperf"
+      {"gov.cu", 0},
+#line 3216 "effective_tld_names.gperf"
+      {"transport.museum", 0},
+#line 3659 "effective_tld_names.gperf"
+      {"xn--vre-eiker-k8a.no", 0},
+#line 1737 "effective_tld_names.gperf"
+      {"la.us", 0},
+#line 2521 "effective_tld_names.gperf"
+      {"overhalla.no", 0},
+#line 2654 "effective_tld_names.gperf"
+      {"pref.kagawa.jp", 1},
+#line 2799 "effective_tld_names.gperf"
+      {"rindal.no", 0},
+#line 3505 "effective_tld_names.gperf"
+      {"xn--frya-hra.no", 0},
+#line 2680 "effective_tld_names.gperf"
+      {"pref.wakayama.jp", 1},
+#line 3229 "effective_tld_names.gperf"
+      {"trolley.museum", 0},
+#line 3401 "effective_tld_names.gperf"
+      {"volda.no", 0},
+#line 1339 "effective_tld_names.gperf"
+      {"gs.sf.no", 0},
+#line 307 "effective_tld_names.gperf"
+      {"beauxarts.museum", 0},
+#line 64 "effective_tld_names.gperf"
+      {"accident-investigation.aero", 0},
+#line 2157 "effective_tld_names.gperf"
+      {"net.bz", 0},
+#line 2923 "effective_tld_names.gperf"
+      {"scotland.museum", 0},
+#line 3217 "effective_tld_names.gperf"
+      {"trapani.it", 0},
+#line 3465 "effective_tld_names.gperf"
+      {"xn--55qx5d.hk", 0},
+#line 2643 "effective_tld_names.gperf"
+      {"pref.fukui.jp", 1},
+#line 136 "effective_tld_names.gperf"
+      {"ambulance.museum", 0},
+#line 2434 "effective_tld_names.gperf"
+      {"org.je", 0},
+#line 1501 "effective_tld_names.gperf"
+      {"inf.cu", 0},
+#line 2161 "effective_tld_names.gperf"
+      {"net.cu", 0},
+#line 3028 "effective_tld_names.gperf"
+      {"sorum.no", 0},
+#line 792 "effective_tld_names.gperf"
+      {"dk", 0},
+#line 2979 "effective_tld_names.gperf"
+      {"skien.no", 0},
+#line 1458 "effective_tld_names.gperf"
+      {"i.ph", 0},
+#line 643 "effective_tld_names.gperf"
+      {"com.km", 0},
+#line 2735 "effective_tld_names.gperf"
+      {"pyatigorsk.ru", 0},
+#line 867 "effective_tld_names.gperf"
+      {"edu.km", 0},
+#line 2760 "effective_tld_names.gperf"
+      {"ralingen.no", 0},
+#line 3621 "effective_tld_names.gperf"
+      {"xn--slt-elab.no", 0},
+#line 3224 "effective_tld_names.gperf"
+      {"trento.it", 0},
+#line 3223 "effective_tld_names.gperf"
+      {"trentino.it", 0},
+#line 213 "effective_tld_names.gperf"
+      {"ass.km", 0},
+#line 1246 "effective_tld_names.gperf"
+      {"gov.km", 0},
+#line 2054 "effective_tld_names.gperf"
+      {"museum.mv", 0},
+#line 3225 "effective_tld_names.gperf"
+      {"treviso.it", 0},
+#line 2681 "effective_tld_names.gperf"
+      {"pref.yamagata.jp", 1},
+#line 2868 "effective_tld_names.gperf"
+      {"sande.more-og-romsdal.no", 0},
+#line 3339 "effective_tld_names.gperf"
+      {"vardo.no", 0},
+#line 1948 "effective_tld_names.gperf"
+      {"mil.bo", 0},
+#line 1911 "effective_tld_names.gperf"
+      {"medecin.km", 0},
+#line 2281 "effective_tld_names.gperf"
+      {"nom.km", 0},
+#line 1430 "effective_tld_names.gperf"
+      {"hokksund.no", 0},
+#line 91 "effective_tld_names.gperf"
+      {"agr.br", 0},
+#line 1183 "effective_tld_names.gperf"
+      {"gobiernoelectronico.ar", 1},
+#line 763 "effective_tld_names.gperf"
+      {"dagestan.ru", 0},
+#line 473 "effective_tld_names.gperf"
+      {"certification.aero", 0},
+#line 2986 "effective_tld_names.gperf"
+      {"skodje.no", 0},
+#line 3251 "effective_tld_names.gperf"
+      {"tv.na", 0},
+#line 66 "effective_tld_names.gperf"
+      {"act.au", 0},
+#line 3499 "effective_tld_names.gperf"
+      {"xn--finny-yua.no", 0},
+#line 1886 "effective_tld_names.gperf"
+      {"matta-varjjat.no", 0},
+#line 2902 "effective_tld_names.gperf"
+      {"sch.jo", 0},
+#line 2763 "effective_tld_names.gperf"
+      {"rauma.no", 0},
+#line 2668 "effective_tld_names.gperf"
+      {"pref.okayama.jp", 1},
+#line 2356 "effective_tld_names.gperf"
+      {"olawa.pl", 0},
+#line 2804 "effective_tld_names.gperf"
+      {"risor.no", 0},
+#line 2829 "effective_tld_names.gperf"
+      {"rovno.ua", 0},
+#line 2825 "effective_tld_names.gperf"
+      {"roros.no", 0},
+#line 775 "effective_tld_names.gperf"
+      {"decorativearts.museum", 0},
+#line 1678 "effective_tld_names.gperf"
+      {"kochi.jp", 2},
+#line 3622 "effective_tld_names.gperf"
+      {"xn--smla-hra.no", 0},
+#line 3383 "effective_tld_names.gperf"
+      {"vik.no", 0},
+#line 2499 "effective_tld_names.gperf"
+      {"oristano.it", 0},
+#line 60 "effective_tld_names.gperf"
+      {"ac.ug", 0},
+#line 2745 "effective_tld_names.gperf"
+      {"qsl.br", 0},
+#line 3156 "effective_tld_names.gperf"
+      {"tg", 0},
+#line 294 "effective_tld_names.gperf"
+      {"baseball.museum", 0},
+#line 3503 "effective_tld_names.gperf"
+      {"xn--frde-gra.no", 0},
+#line 2439 "effective_tld_names.gperf"
+      {"org.kn", 0},
+#line 2892 "effective_tld_names.gperf"
+      {"sb", 0},
+#line 3159 "effective_tld_names.gperf"
+      {"theater.museum", 0},
+#line 946 "effective_tld_names.gperf"
+      {"encyclopedic.museum", 0},
+#line 2656 "effective_tld_names.gperf"
+      {"pref.kanagawa.jp", 1},
+#line 2993 "effective_tld_names.gperf"
+      {"slg.br", 0},
+#line 3353 "effective_tld_names.gperf"
+      {"venice.it", 0},
+#line 3196 "effective_tld_names.gperf"
+      {"torino.it", 0},
+#line 3402 "effective_tld_names.gperf"
+      {"volgograd.ru", 0},
+#line 2921 "effective_tld_names.gperf"
+      {"sciencesnaturelles.museum", 0},
+#line 2751 "effective_tld_names.gperf"
+      {"radom.pl", 0},
+#line 3120 "effective_tld_names.gperf"
+      {"szczecin.pl", 0},
+#line 3482 "effective_tld_names.gperf"
+      {"xn--bjddar-pta.no", 0},
+#line 3045 "effective_tld_names.gperf"
+      {"srv.br", 0},
+#line 3535 "effective_tld_names.gperf"
+      {"xn--koluokta-7ya57h.no", 0},
+#line 2991 "effective_tld_names.gperf"
+      {"slattum.no", 0},
+#line 802 "effective_tld_names.gperf"
+      {"donostia.museum", 0},
+#line 3439 "effective_tld_names.gperf"
+      {"westfalen.museum", 0},
+#line 2818 "effective_tld_names.gperf"
+      {"rodoy.no", 0},
+#line 2111 "effective_tld_names.gperf"
+      {"naturbruksgymn.se", 0},
+#line 2435 "effective_tld_names.gperf"
+      {"org.jo", 0},
+#line 3254 "effective_tld_names.gperf"
+      {"tw", 0},
+#line 3509 "effective_tld_names.gperf"
+      {"xn--gjvik-wua.no", 0},
+#line 3148 "effective_tld_names.gperf"
+      {"teramo.it", 0},
+#line 3368 "effective_tld_names.gperf"
+      {"veterinaire.km", 0},
+#line 3150 "effective_tld_names.gperf"
+      {"ternopil.ua", 0},
+#line 2698 "effective_tld_names.gperf"
+      {"priv.me", 0},
+#line 3037 "effective_tld_names.gperf"
+      {"spjelkavik.no", 0},
+#line 2040 "effective_tld_names.gperf"
+      {"msk.ru", 0},
+#line 2526 "effective_tld_names.gperf"
+      {"oystre-slidre.no", 0},
+#line 1798 "effective_tld_names.gperf"
+      {"livinghistory.museum", 0},
+#line 3365 "effective_tld_names.gperf"
+      {"vestvagoy.no", 0},
+#line 3389 "effective_tld_names.gperf"
+      {"virginia.museum", 0},
+#line 3176 "effective_tld_names.gperf"
+      {"tm.mg", 0},
+#line 801 "effective_tld_names.gperf"
+      {"donna.no", 0},
+#line 3341 "effective_tld_names.gperf"
+      {"varggat.no", 0},
+#line 2844 "effective_tld_names.gperf"
+      {"rzeszow.pl", 0},
+#line 777 "effective_tld_names.gperf"
+      {"delaware.museum", 0},
+#line 3234 "effective_tld_names.gperf"
+      {"trustee.museum", 0},
+#line 175 "effective_tld_names.gperf"
+      {"architecture.museum", 0},
+#line 3595 "effective_tld_names.gperf"
+      {"xn--rdy-0nab.no", 0},
+#line 3197 "effective_tld_names.gperf"
+      {"torino.museum", 0},
+#line 1617 "effective_tld_names.gperf"
+      {"jx.cn", 0},
+#line 1358 "effective_tld_names.gperf"
+      {"gx.cn", 0},
+#line 2899 "effective_tld_names.gperf"
+      {"sch.gg", 0},
+#line 2387 "effective_tld_names.gperf"
+      {"oregon.museum", 0},
+#line 2752 "effective_tld_names.gperf"
+      {"radoy.no", 0},
+#line 799 "effective_tld_names.gperf"
+      {"dolls.museum", 0},
+#line 2331 "effective_tld_names.gperf"
+      {"nx.cn", 0},
 #line 3485 "effective_tld_names.gperf"
-      {"xn--krjohka-hwab49j.no", 0}
+      {"xn--bod-2na.no", 0},
+#line 3151 "effective_tld_names.gperf"
+      {"test.ru", 0},
+#line 3205 "effective_tld_names.gperf"
+      {"tozsde.hu", 0},
+#line 1341 "effective_tld_names.gperf"
+      {"gs.svalbard.no", 0},
+#line 2887 "effective_tld_names.gperf"
+      {"satx.museum", 0},
+#line 1668 "effective_tld_names.gperf"
+      {"kitakyushu.jp", 2},
+#line 3054 "effective_tld_names.gperf"
+      {"starachowice.pl", 0},
+#line 1062 "effective_tld_names.gperf"
+      {"force.museum", 0},
+#line 3539 "effective_tld_names.gperf"
+      {"xn--krehamn-dxa.no", 0},
+#line 1473 "effective_tld_names.gperf"
+      {"if.ua", 0},
+#line 2437 "effective_tld_names.gperf"
+      {"org.ki", 0},
+#line 3464 "effective_tld_names.gperf"
+      {"xn--55qx5d.cn", 0},
+#line 38 "effective_tld_names.gperf"
+      {"ac.cr", 0},
+#line 2047 "effective_tld_names.gperf"
+      {"mulhouse.museum", 0},
+#line 3504 "effective_tld_names.gperf"
+      {"xn--frna-woa.no", 0},
+#line 3652 "effective_tld_names.gperf"
+      {"xn--vegrshei-c0a.no", 0},
+#line 2720 "effective_tld_names.gperf"
+      {"psi.br", 0},
+#line 22 "effective_tld_names.gperf"
+      {"6bone.pl", 0},
+#line 1474 "effective_tld_names.gperf"
+      {"iki.fi", 0},
+#line 1085 "effective_tld_names.gperf"
+      {"frog.museum", 0},
+#line 2800 "effective_tld_names.gperf"
+      {"ringebu.no", 0},
+#line 2505 "effective_tld_names.gperf"
+      {"oryol.ru", 0},
+#line 2045 "effective_tld_names.gperf"
+      {"muenchen.museum", 0},
+#line 2354 "effective_tld_names.gperf"
+      {"oksnes.no", 0},
+#line 511 "effective_tld_names.gperf"
+      {"city.fukuoka.jp", 1},
+#line 37 "effective_tld_names.gperf"
+      {"ac.cn", 0},
+#line 3331 "effective_tld_names.gperf"
+      {"vaksdal.no", 0},
+#line 2516 "effective_tld_names.gperf"
+      {"ostroleka.pl", 0},
+#line 3490 "effective_tld_names.gperf"
+      {"xn--ciqpn.hk", 0},
+#line 3409 "effective_tld_names.gperf"
+      {"vrn.ru", 0},
+#line 2823 "effective_tld_names.gperf"
+      {"romsa.no", 0},
+#line 1662 "effective_tld_names.gperf"
+      {"kids.museum", 0},
+#line 2419 "effective_tld_names.gperf"
+      {"org.gg", 0},
+#line 711 "effective_tld_names.gperf"
+      {"contemporary.museum", 0},
+#line 3210 "effective_tld_names.gperf"
+      {"trader.aero", 0},
+#line 2540 "effective_tld_names.gperf"
+      {"palermo.it", 0},
+#line 3449 "effective_tld_names.gperf"
+      {"wodzislaw.pl", 0},
+#line 3569 "effective_tld_names.gperf"
+      {"xn--mk0axi.hk", 0},
+#line 3414 "effective_tld_names.gperf"
+      {"vyatka.ru", 0},
+#line 490 "effective_tld_names.gperf"
+      {"chicago.museum", 0},
+#line 2055 "effective_tld_names.gperf"
+      {"museum.mw", 0},
+#line 1123 "effective_tld_names.gperf"
+      {"gc.ca", 0},
+#line 2803 "effective_tld_names.gperf"
+      {"riodejaneiro.museum", 0},
+#line 2077 "effective_tld_names.gperf"
+      {"nakhodka.ru", 0},
+#line 2816 "effective_tld_names.gperf"
+      {"rochester.museum", 0},
+#line 2858 "effective_tld_names.gperf"
+      {"sakhalin.ru", 0},
+#line 3444 "effective_tld_names.gperf"
+      {"wildlife.museum", 0},
+#line 2897 "effective_tld_names.gperf"
+      {"sc.us", 0},
+#line 3235 "effective_tld_names.gperf"
+      {"trysil.no", 0},
+#line 3591 "effective_tld_names.gperf"
+      {"xn--porsgu-sta26f.no", 0},
+#line 2507 "effective_tld_names.gperf"
+      {"os.hordaland.no", 0},
+#line 811 "effective_tld_names.gperf"
+      {"dyroy.no", 0},
+#line 1314 "effective_tld_names.gperf"
+      {"greta.fr", 0},
+#line 2085 "effective_tld_names.gperf"
+      {"name.mk", 0},
+#line 2966 "effective_tld_names.gperf"
+      {"silk.museum", 0},
+#line 1463 "effective_tld_names.gperf"
+      {"icnet.uk", 1},
+#line 3660 "effective_tld_names.gperf"
+      {"xn--vrggt-xqad.no", 0},
+#line 2657 "effective_tld_names.gperf"
+      {"pref.kochi.jp", 1},
+#line 3653 "effective_tld_names.gperf"
+      {"xn--vestvgy-ixa6o.no", 0},
+#line 2922 "effective_tld_names.gperf"
+      {"scientist.aero", 0},
+#line 3165 "effective_tld_names.gperf"
+      {"tj", 0},
+#line 3215 "effective_tld_names.gperf"
+      {"tranoy.no", 0},
+#line 3556 "effective_tld_names.gperf"
+      {"xn--loabt-0qa.no", 0},
+#line 3502 "effective_tld_names.gperf"
+      {"xn--flor-jra.no", 0},
+#line 3124 "effective_tld_names.gperf"
+      {"t.bg", 0},
+#line 1750 "effective_tld_names.gperf"
+      {"laquila.it", 0},
+#line 36 "effective_tld_names.gperf"
+      {"ac.ci", 0},
+#line 173 "effective_tld_names.gperf"
+      {"archaeological.museum", 0},
+#line 2810 "effective_tld_names.gperf"
+      {"rnrt.tn", 0},
+#line 2645 "effective_tld_names.gperf"
+      {"pref.fukushima.jp", 1},
+#line 2664 "effective_tld_names.gperf"
+      {"pref.nagasaki.jp", 1},
+#line 773 "effective_tld_names.gperf"
+      {"de.us", 0},
+#line 3378 "effective_tld_names.gperf"
+      {"vic.au", 0},
+#line 712 "effective_tld_names.gperf"
+      {"contemporaryart.museum", 0},
+#line 2916 "effective_tld_names.gperf"
+      {"scienceandindustry.museum", 0},
+#line 284 "effective_tld_names.gperf"
+      {"baltimore.museum", 0},
+#line 301 "effective_tld_names.gperf"
+      {"bc.ca", 0},
+#line 3488 "effective_tld_names.gperf"
+      {"xn--brum-voa.no", 0},
+#line 2919 "effective_tld_names.gperf"
+      {"sciencehistory.museum", 0},
+#line 112 "effective_tld_names.gperf"
+      {"akita.jp", 2},
+#line 1772 "effective_tld_names.gperf"
+      {"leirvik.no", 0},
+#line 317 "effective_tld_names.gperf"
+      {"bergamo.it", 0},
+#line 3623 "effective_tld_names.gperf"
+      {"xn--smna-gra.no", 0},
+#line 762 "effective_tld_names.gperf"
+      {"daejeon.kr", 0},
+#line 3396 "effective_tld_names.gperf"
+      {"vladivostok.ru", 0},
+#line 2805 "effective_tld_names.gperf"
+      {"rissa.no", 0},
+#line 1781 "effective_tld_names.gperf"
+      {"lezajsk.pl", 0},
+#line 2766 "effective_tld_names.gperf"
+      {"rc.it", 0},
+#line 3516 "effective_tld_names.gperf"
+      {"xn--hcesuolo-7ya35b.no", 0},
+#line 2022 "effective_tld_names.gperf"
+      {"monza.it", 0},
+#line 789 "effective_tld_names.gperf"
+      {"divtasvuodna.no", 0},
+#line 516 "effective_tld_names.gperf"
+      {"city.kobe.jp", 1},
+#line 3553 "effective_tld_names.gperf"
+      {"xn--lhppi-xqa.no", 0},
+#line 2386 "effective_tld_names.gperf"
+      {"or.us", 0},
+#line 1219 "effective_tld_names.gperf"
+      {"gov.cm", 0},
+#line 3129 "effective_tld_names.gperf"
+      {"tananger.no", 0},
+#line 1950 "effective_tld_names.gperf"
+      {"mil.by", 0},
+#line 1649 "effective_tld_names.gperf"
+      {"kepno.pl", 0},
+#line 1823 "effective_tld_names.gperf"
+      {"ltd.co.im", 0},
+#line 225 "effective_tld_names.gperf"
+      {"asso.mc", 0},
+#line 2079 "effective_tld_names.gperf"
+      {"nalchik.ru", 0},
+#line 790 "effective_tld_names.gperf"
+      {"divttasvuotna.no", 0},
+#line 2388 "effective_tld_names.gperf"
+      {"oregontrail.museum", 0},
+#line 3544 "effective_tld_names.gperf"
+      {"xn--kvnangen-k0a.no", 0},
+#line 1805 "effective_tld_names.gperf"
+      {"localhistory.museum", 0},
+#line 2468 "effective_tld_names.gperf"
+      {"org.ph", 0},
+#line 804 "effective_tld_names.gperf"
+      {"dp.ua", 0},
+#line 2650 "effective_tld_names.gperf"
+      {"pref.hyogo.jp", 1},
+#line 779 "effective_tld_names.gperf"
+      {"denmark.museum", 0},
+#line 1789 "effective_tld_names.gperf"
+      {"lillehammer.no", 0},
+#line 3131 "effective_tld_names.gperf"
+      {"taranto.it", 0},
+#line 1047 "effective_tld_names.gperf"
+      {"flesberg.no", 0},
+#line 3387 "effective_tld_names.gperf"
+      {"vindafjord.no", 0},
+#line 2550 "effective_tld_names.gperf"
+      {"passenger-association.aero", 0},
+#line 2677 "effective_tld_names.gperf"
+      {"pref.tokushima.jp", 1},
+#line 3370 "effective_tld_names.gperf"
+      {"vf.no", 0},
+#line 2648 "effective_tld_names.gperf"
+      {"pref.hiroshima.jp", 1},
+#line 795 "effective_tld_names.gperf"
+      {"dn.ua", 0},
+#line 800 "effective_tld_names.gperf"
+      {"donetsk.ua", 0},
+#line 1890 "effective_tld_names.gperf"
+      {"mbone.pl", 0},
+#line 3169 "effective_tld_names.gperf"
+      {"tk", 0},
+#line 3312 "effective_tld_names.gperf"
+      {"utah.museum", 0},
+#line 257 "effective_tld_names.gperf"
+      {"avoues.fr", 0},
+#line 2638 "effective_tld_names.gperf"
+      {"pref.aichi.jp", 1},
+#line 2762 "effective_tld_names.gperf"
+      {"randaberg.no", 0},
+#line 3703 "effective_tld_names.gperf"
+      {"zhitomir.ua", 0},
+#line 3237 "effective_tld_names.gperf"
+      {"tsaritsyn.ru", 0},
+#line 1554 "effective_tld_names.gperf"
+      {"irkutsk.ru", 0},
+#line 2795 "effective_tld_names.gperf"
+      {"ri.us", 0},
+#line 1223 "effective_tld_names.gperf"
+      {"gov.cx", 0},
+#line 2341 "effective_tld_names.gperf"
+      {"od.ua", 0},
+#line 2757 "effective_tld_names.gperf"
+      {"railway.museum", 0},
+#line 2410 "effective_tld_names.gperf"
+      {"org.cn", 0},
+#line 496 "effective_tld_names.gperf"
+      {"chirurgiens-dentistes.fr", 0},
+#line 3428 "effective_tld_names.gperf"
+      {"washingtondc.museum", 0},
+#line 3079 "effective_tld_names.gperf"
+      {"store.nf", 0},
+#line 1783 "effective_tld_names.gperf"
+      {"lg.ua", 0},
+#line 3167 "effective_tld_names.gperf"
+      {"tjeldsund.no", 0},
+#line 468 "effective_tld_names.gperf"
+      {"cci.fr", 0},
+#line 1682 "effective_tld_names.gperf"
+      {"kolobrzeg.pl", 0},
+#line 3051 "effective_tld_names.gperf"
+      {"stalbans.museum", 0},
+#line 3133 "effective_tld_names.gperf"
+      {"tarnobrzeg.pl", 0},
+#line 1747 "effective_tld_names.gperf"
+      {"langevag.no", 0},
+#line 154 "effective_tld_names.gperf"
+      {"annefrank.museum", 0},
+#line 2915 "effective_tld_names.gperf"
+      {"scienceandhistory.museum", 0},
+#line 932 "effective_tld_names.gperf"
+      {"eidsberg.no", 0},
+#line 3121 "effective_tld_names.gperf"
+      {"szczytno.pl", 0},
+#line 2881 "effective_tld_names.gperf"
+      {"saotome.st", 0},
+#line 1961 "effective_tld_names.gperf"
+      {"mil.km", 0},
+#line 2577 "effective_tld_names.gperf"
+      {"pharmaciens.km", 0},
+#line 987 "effective_tld_names.gperf"
+      {"express.aero", 0},
+#line 2570 "effective_tld_names.gperf"
+      {"pesarourbino.it", 0},
+#line 2569 "effective_tld_names.gperf"
+      {"pesaro-urbino.it", 0},
+#line 3480 "effective_tld_names.gperf"
+      {"xn--bievt-0qa.no", 0},
+#line 2669 "effective_tld_names.gperf"
+      {"pref.okinawa.jp", 1},
+#line 3522 "effective_tld_names.gperf"
+      {"xn--hobl-ira.no", 0},
+#line 1713 "effective_tld_names.gperf"
+      {"kunstunddesign.museum", 0},
+#line 1461 "effective_tld_names.gperf"
+      {"ibaraki.jp", 2},
+#line 1813 "effective_tld_names.gperf"
+      {"lorenskog.no", 0},
+#line 2909 "effective_tld_names.gperf"
+      {"schokoladen.museum", 0},
+#line 3173 "effective_tld_names.gperf"
+      {"tm.hu", 0},
+#line 3668 "effective_tld_names.gperf"
+      {"xn--zf0avx.hk", 0},
+#line 215 "effective_tld_names.gperf"
+      {"assedic.fr", 0},
+#line 3258 "effective_tld_names.gperf"
+      {"tydal.no", 0},
+#line 3213 "effective_tld_names.gperf"
+      {"trana.no", 0},
+#line 608 "effective_tld_names.gperf"
+      {"com.bh", 0},
+#line 835 "effective_tld_names.gperf"
+      {"edu.bh", 0},
+#line 3106 "effective_tld_names.gperf"
+      {"svalbard.no", 0},
+#line 1210 "effective_tld_names.gperf"
+      {"gov.bh", 0},
+#line 420 "effective_tld_names.gperf"
+      {"burghof.museum", 0},
+#line 2759 "effective_tld_names.gperf"
+      {"rakkestad.no", 0},
+#line 3259 "effective_tld_names.gperf"
+      {"tynset.no", 0},
+#line 2772 "effective_tld_names.gperf"
+      {"rec.co", 0},
+#line 3252 "effective_tld_names.gperf"
+      {"tvedestrand.no", 0},
+#line 2504 "effective_tld_names.gperf"
+      {"orsta.no", 0},
+#line 2409 "effective_tld_names.gperf"
+      {"org.ci", 0},
+#line 1801 "effective_tld_names.gperf"
+      {"ln.cn", 0},
+#line 2988 "effective_tld_names.gperf"
+      {"skydiving.aero", 0},
+#line 2971 "effective_tld_names.gperf"
+      {"sk.ca", 0},
+#line 2152 "effective_tld_names.gperf"
+      {"net.bh", 0},
+#line 2349 "effective_tld_names.gperf"
+      {"oh.us", 0},
+#line 2758 "effective_tld_names.gperf"
+      {"raisa.no", 0},
+#line 735 "effective_tld_names.gperf"
+      {"cranbrook.museum", 0},
+#line 3020 "effective_tld_names.gperf"
+      {"sopot.pl", 0},
+#line 3688 "effective_tld_names.gperf"
+      {"yuzhno-sakhalinsk.ru", 0},
+#line 3640 "effective_tld_names.gperf"
+      {"xn--tnsberg-q1a.no", 0},
+#line 2307 "effective_tld_names.gperf"
+      {"novosibirsk.ru", 0},
+#line 2436 "effective_tld_names.gperf"
+      {"org.kg", 0},
+#line 2411 "effective_tld_names.gperf"
+      {"org.co", 0},
+#line 2440 "effective_tld_names.gperf"
+      {"org.ky", 0},
+#line 2523 "effective_tld_names.gperf"
+      {"oxford.museum", 0},
+#line 495 "effective_tld_names.gperf"
+      {"chiropractic.museum", 0},
+#line 2753 "effective_tld_names.gperf"
+      {"ragusa.it", 0},
+#line 2951 "effective_tld_names.gperf"
+      {"shiga.jp", 2},
+#line 2560 "effective_tld_names.gperf"
+      {"penza.ru", 0},
+#line 292 "effective_tld_names.gperf"
+      {"barreau.bj", 0},
+#line 2939 "effective_tld_names.gperf"
+      {"seoul.kr", 0},
+#line 1808 "effective_tld_names.gperf"
+      {"logistics.aero", 0},
+#line 1095 "effective_tld_names.gperf"
+      {"fukuoka.jp", 2},
+#line 3035 "effective_tld_names.gperf"
+      {"space.museum", 0},
+#line 2652 "effective_tld_names.gperf"
+      {"pref.ishikawa.jp", 1},
+#line 3472 "effective_tld_names.gperf"
+      {"xn--b-5ga.nordland.no", 0},
+#line 2441 "effective_tld_names.gperf"
+      {"org.kz", 0},
+#line 3384 "effective_tld_names.gperf"
+      {"viking.museum", 0},
+#line 2636 "effective_tld_names.gperf"
+      {"prd.km", 0},
+#line 193 "effective_tld_names.gperf"
+      {"arteducation.museum", 0},
+#line 3567 "effective_tld_names.gperf"
+      {"xn--mgberp4a5d4ar", 0},
+#line 113 "effective_tld_names.gperf"
+      {"aknoluokta.no", 0},
+#line 3394 "effective_tld_names.gperf"
+      {"vladikavkaz.ru", 0},
+#line 2839 "effective_tld_names.gperf"
+      {"rv.ua", 0},
+#line 3244 "effective_tld_names.gperf"
+      {"turin.it", 0},
+#line 3111 "effective_tld_names.gperf"
+      {"swidnica.pl", 0},
+#line 3226 "effective_tld_names.gperf"
+      {"trieste.it", 0},
+#line 2882 "effective_tld_names.gperf"
+      {"sapporo.jp", 2},
+#line 2517 "effective_tld_names.gperf"
+      {"ostrowiec.pl", 0},
+#line 3506 "effective_tld_names.gperf"
+      {"xn--ggaviika-8ya47h.no", 0},
+#line 796 "effective_tld_names.gperf"
+      {"dnepropetrovsk.ua", 0},
+#line 510 "effective_tld_names.gperf"
+      {"city.chiba.jp", 1},
+#line 821 "effective_tld_names.gperf"
+      {"ecn.br", 0},
+#line 2831 "effective_tld_names.gperf"
+      {"royrvik.no", 0},
+#line 180 "effective_tld_names.gperf"
+      {"arkhangelsk.ru", 0},
+#line 2518 "effective_tld_names.gperf"
+      {"ostrowwlkp.pl", 0},
+#line 3204 "effective_tld_names.gperf"
+      {"toyama.jp", 2},
+#line 3448 "effective_tld_names.gperf"
+      {"wloclawek.pl", 0},
+#line 3136 "effective_tld_names.gperf"
+      {"tas.gov.au", 0},
+#line 2599 "effective_tld_names.gperf"
+      {"plaza.museum", 0},
+#line 536 "effective_tld_names.gperf"
+      {"clock.museum", 0},
+#line 2974 "effective_tld_names.gperf"
+      {"skaun.no", 0},
+#line 1137 "effective_tld_names.gperf"
+      {"geometre-expert.fr", 0},
+#line 1837 "effective_tld_names.gperf"
+      {"lutsk.ua", 0},
+#line 318 "effective_tld_names.gperf"
+      {"bergbau.museum", 0},
+#line 3147 "effective_tld_names.gperf"
+      {"television.museum", 0},
+#line 3582 "effective_tld_names.gperf"
+      {"xn--nttery-byae.no", 0},
+#line 2977 "effective_tld_names.gperf"
+      {"ski.museum", 0},
+#line 1629 "effective_tld_names.gperf"
+      {"kamchatka.ru", 0},
+#line 2843 "effective_tld_names.gperf"
+      {"rygge.no", 0},
+#line 2779 "effective_tld_names.gperf"
+      {"reggioemilia.it", 0},
+#line 2777 "effective_tld_names.gperf"
+      {"reggio-emilia.it", 0},
+#line 3109 "effective_tld_names.gperf"
+      {"svizzera.museum", 0},
+#line 2660 "effective_tld_names.gperf"
+      {"pref.mie.jp", 1},
+#line 3385 "effective_tld_names.gperf"
+      {"vikna.no", 0},
+#line 2896 "effective_tld_names.gperf"
+      {"sc.ug", 0},
+#line 3343 "effective_tld_names.gperf"
+      {"vb.it", 0},
+#line 3243 "effective_tld_names.gperf"
+      {"turen.tn", 0},
+#line 2644 "effective_tld_names.gperf"
+      {"pref.fukuoka.jp", 1},
+#line 3366 "effective_tld_names.gperf"
+      {"vet.br", 0},
+#line 1749 "effective_tld_names.gperf"
+      {"lapy.pl", 0},
+#line 2477 "effective_tld_names.gperf"
+      {"org.ru", 0},
+#line 767 "effective_tld_names.gperf"
+      {"davvenjarga.no", 0},
+#line 3233 "effective_tld_names.gperf"
+      {"trust.museum", 0},
+#line 2796 "effective_tld_names.gperf"
+      {"rieti.it", 0},
+#line 3184 "effective_tld_names.gperf"
+      {"tn.us", 0},
+#line 3260 "effective_tld_names.gperf"
+      {"tysfjord.no", 0},
+#line 2508 "effective_tld_names.gperf"
+      {"osaka.jp", 2},
+#line 2885 "effective_tld_names.gperf"
+      {"saskatchewan.museum", 0},
+#line 3356 "effective_tld_names.gperf"
+      {"vercelli.it", 0},
+#line 3139 "effective_tld_names.gperf"
+      {"tc", 0},
+#line 2245 "effective_tld_names.gperf"
+      {"nf.ca", 0},
+#line 2457 "effective_tld_names.gperf"
+      {"org.mu", 0},
+#line 2739 "effective_tld_names.gperf"
+      {"qc.ca", 0},
+#line 3149 "effective_tld_names.gperf"
+      {"terni.it", 0},
+#line 2809 "effective_tld_names.gperf"
+      {"rnd.ru", 0},
+#line 3143 "effective_tld_names.gperf"
+      {"te.ua", 0},
+#line 1046 "effective_tld_names.gperf"
+      {"flekkefjord.no", 0},
+#line 2982 "effective_tld_names.gperf"
+      {"skjak.no", 0},
+#line 793 "effective_tld_names.gperf"
+      {"dlugoleka.pl", 0},
+#line 3211 "effective_tld_names.gperf"
+      {"trading.aero", 0},
+#line 3114 "effective_tld_names.gperf"
+      {"sx.cn", 0},
+#line 199 "effective_tld_names.gperf"
+      {"artsandcrafts.museum", 0},
+#line 3530 "effective_tld_names.gperf"
+      {"xn--jlster-bya.no", 0},
+#line 3228 "effective_tld_names.gperf"
+      {"trogstad.no", 0},
+#line 2385 "effective_tld_names.gperf"
+      {"or.ug", 0},
+#line 2420 "effective_tld_names.gperf"
+      {"org.gh", 0},
+#line 2519 "effective_tld_names.gperf"
+      {"otago.museum", 0},
+#line 3175 "effective_tld_names.gperf"
+      {"tm.mc", 0},
+#line 93 "effective_tld_names.gperf"
+      {"agriculture.museum", 0},
+#line 1771 "effective_tld_names.gperf"
+      {"leirfjord.no", 0},
+#line 1910 "effective_tld_names.gperf"
+      {"medecin.fr", 0},
+#line 484 "effective_tld_names.gperf"
+      {"chelyabinsk.ru", 0},
+#line 3445 "effective_tld_names.gperf"
+      {"williamsburg.museum", 0},
+#line 589 "effective_tld_names.gperf"
+      {"coastaldefence.museum", 0},
+#line 2114 "effective_tld_names.gperf"
+      {"natuurwetenschappen.museum", 0},
+#line 1743 "effective_tld_names.gperf"
+      {"lakas.hu", 0},
+#line 1759 "effective_tld_names.gperf"
+      {"lb", 0},
+#line 2894 "effective_tld_names.gperf"
+      {"sc.cn", 0},
+#line 3566 "effective_tld_names.gperf"
+      {"xn--mgbaam7a8h", 0},
+#line 3332 "effective_tld_names.gperf"
+      {"valer.hedmark.no", 0},
+#line 169 "effective_tld_names.gperf"
+      {"ar.com", 0},
+#line 465 "effective_tld_names.gperf"
+      {"cbg.ru", 0},
+#line 82 "effective_tld_names.gperf"
+      {"aeroclub.aero", 0},
+#line 2273 "effective_tld_names.gperf"
+      {"no.com", 0},
+#line 103 "effective_tld_names.gperf"
+      {"air-surveillance.aero", 0},
+#line 3157 "effective_tld_names.gperf"
+      {"tgory.pl", 0},
+#line 3579 "effective_tld_names.gperf"
+      {"xn--mxtq1m.hk", 0},
+#line 2522 "effective_tld_names.gperf"
+      {"ovre-eiker.no", 0},
+#line 2824 "effective_tld_names.gperf"
+      {"romskog.no", 0},
+#line 542 "effective_tld_names.gperf"
+      {"cn.com", 0},
+#line 272 "effective_tld_names.gperf"
+      {"bahccavuotna.no", 0},
+#line 978 "effective_tld_names.gperf"
+      {"eu.com", 0},
+#line 1449 "effective_tld_names.gperf"
+      {"hu.com", 0},
+#line 1817 "effective_tld_names.gperf"
+      {"lowicz.pl", 0},
+#line 3619 "effective_tld_names.gperf"
+      {"xn--sknland-fxa.no", 0},
+#line 1763 "effective_tld_names.gperf"
+      {"leangaviika.no", 0},
+#line 2428 "effective_tld_names.gperf"
+      {"org.hu", 0},
+#line 2907 "effective_tld_names.gperf"
+      {"schlesisches.museum", 0},
+#line 1792 "effective_tld_names.gperf"
+      {"lincoln.museum", 0},
+#line 2501 "effective_tld_names.gperf"
+      {"orkdal.no", 0},
+#line 2789 "effective_tld_names.gperf"
+      {"research.aero", 0},
+#line 3367 "effective_tld_names.gperf"
+      {"veterinaire.fr", 0},
+#line 3692 "effective_tld_names.gperf"
+      {"za.com", 0},
+#line 3198 "effective_tld_names.gperf"
+      {"torsken.no", 0},
+#line 2376 "effective_tld_names.gperf"
+      {"or.cr", 0},
+#line 3199 "effective_tld_names.gperf"
+      {"tottori.jp", 2},
+#line 3666 "effective_tld_names.gperf"
+      {"xn--ystre-slidre-ujb.no", 0},
+#line 3543 "effective_tld_names.gperf"
+      {"xn--kvitsy-fya.no", 0},
+#line 1410 "effective_tld_names.gperf"
+      {"historicalsociety.museum", 0},
+#line 770 "effective_tld_names.gperf"
+      {"ddr.museum", 0},
+#line 2369 "effective_tld_names.gperf"
+      {"opoczno.pl", 0},
+#line 2500 "effective_tld_names.gperf"
+      {"orkanger.no", 0},
+#line 391 "effective_tld_names.gperf"
+      {"br.com", 0},
+#line 1841 "effective_tld_names.gperf"
+      {"lviv.ua", 0},
+#line 3163 "effective_tld_names.gperf"
+      {"tingvoll.no", 0},
+#line 260 "effective_tld_names.gperf"
+      {"axis.museum", 0},
+#line 3654 "effective_tld_names.gperf"
+      {"xn--vg-yiab.no", 0},
+#line 1693 "effective_tld_names.gperf"
+      {"kopervik.no", 0},
+#line 1775 "effective_tld_names.gperf"
+      {"lel.br", 0},
+#line 2836 "effective_tld_names.gperf"
+      {"rubtsovsk.ru", 0},
+#line 2346 "effective_tld_names.gperf"
+      {"of.no", 0},
+#line 3168 "effective_tld_names.gperf"
+      {"tjome.no", 0},
+#line 3547 "effective_tld_names.gperf"
+      {"xn--langevg-jxa.no", 0},
+#line 2364 "effective_tld_names.gperf"
+      {"on.ca", 0},
+#line 2790 "effective_tld_names.gperf"
+      {"research.museum", 0},
+#line 3586 "effective_tld_names.gperf"
+      {"xn--od0aq3b.hk", 0},
+#line 985 "effective_tld_names.gperf"
+      {"exhibition.museum", 0},
+#line 3232 "effective_tld_names.gperf"
+      {"trondheim.no", 0},
+#line 2506 "effective_tld_names.gperf"
+      {"os.hedmark.no", 0},
+#line 2345 "effective_tld_names.gperf"
+      {"of.by", 0},
+#line 2646 "effective_tld_names.gperf"
+      {"pref.gifu.jp", 1},
+#line 2802 "effective_tld_names.gperf"
+      {"ringsaker.no", 0},
+#line 2361 "effective_tld_names.gperf"
+      {"omaha.museum", 0},
+#line 2375 "effective_tld_names.gperf"
+      {"or.ci", 0},
+#line 81 "effective_tld_names.gperf"
+      {"aerobatic.aero", 0},
+#line 2876 "effective_tld_names.gperf"
+      {"sanfrancisco.museum", 0},
+#line 2237 "effective_tld_names.gperf"
+      {"newhampshire.museum", 0},
+#line 2510 "effective_tld_names.gperf"
+      {"oskol.ru", 0},
+#line 3192 "effective_tld_names.gperf"
+      {"tom.ru", 0},
+#line 2827 "effective_tld_names.gperf"
+      {"rotorcraft.aero", 0},
+#line 2533 "effective_tld_names.gperf"
+      {"pacific.museum", 0},
+#line 2791 "effective_tld_names.gperf"
+      {"resistance.museum", 0},
+#line 2801 "effective_tld_names.gperf"
+      {"ringerike.no", 0},
+#line 2543 "effective_tld_names.gperf"
+      {"parachuting.aero", 0},
+#line 3297 "effective_tld_names.gperf"
+      {"us.com", 0},
+#line 2968 "effective_tld_names.gperf"
+      {"siracusa.it", 0},
+#line 2347 "effective_tld_names.gperf"
+      {"off.ai", 0},
+#line 499 "effective_tld_names.gperf"
+      {"christiansburg.museum", 0},
+#line 2917 "effective_tld_names.gperf"
+      {"sciencecenter.museum", 0},
+#line 2918 "effective_tld_names.gperf"
+      {"sciencecenters.museum", 0},
+#line 2413 "effective_tld_names.gperf"
+      {"org.dm", 0},
+#line 1390 "effective_tld_names.gperf"
+      {"hb.cn", 0},
+#line 1832 "effective_tld_names.gperf"
+      {"lukow.pl", 0},
+#line 2943 "effective_tld_names.gperf"
+      {"sex.hu", 0},
+#line 2058 "effective_tld_names.gperf"
+      {"museumcenter.museum", 0},
+#line 3491 "effective_tld_names.gperf"
+      {"xn--comunicaes-v6a2o.museum", 0},
+#line 2985 "effective_tld_names.gperf"
+      {"skoczow.pl", 0},
+#line 30 "effective_tld_names.gperf"
+      {"ab.ca", 0},
+#line 2351 "effective_tld_names.gperf"
+      {"ok.us", 0},
+#line 106 "effective_tld_names.gperf"
+      {"aircraft.aero", 0},
+#line 2120 "effective_tld_names.gperf"
+      {"nb.ca", 0},
+#line 3191 "effective_tld_names.gperf"
+      {"tolga.no", 0},
+#line 2975 "effective_tld_names.gperf"
+      {"skedsmo.no", 0},
+#line 63 "effective_tld_names.gperf"
+      {"academy.museum", 0},
+#line 2641 "effective_tld_names.gperf"
+      {"pref.chiba.jp", 1},
+#line 2676 "effective_tld_names.gperf"
+      {"pref.tochigi.jp", 1},
+#line 2821 "effective_tld_names.gperf"
+      {"roma.museum", 0},
+#line 2239 "effective_tld_names.gperf"
+      {"newmexico.museum", 0},
+#line 3245 "effective_tld_names.gperf"
+      {"turystyka.pl", 0},
+#line 3318 "effective_tld_names.gperf"
+      {"uy.com", 0},
+#line 3134 "effective_tld_names.gperf"
+      {"tas.au", 0},
+#line 3625 "effective_tld_names.gperf"
+      {"xn--sndre-land-0cb.no", 0},
+#line 3152 "effective_tld_names.gperf"
+      {"test.tj", 0},
+#line 1094 "effective_tld_names.gperf"
+      {"fukui.jp", 2},
+#line 2460 "effective_tld_names.gperf"
+      {"org.mx", 0},
+#line 1748 "effective_tld_names.gperf"
+      {"lans.museum", 0},
+#line 3519 "effective_tld_names.gperf"
+      {"xn--hgebostad-g3a.no", 0},
+#line 152 "effective_tld_names.gperf"
+      {"andebu.no", 0},
+#line 2878 "effective_tld_names.gperf"
+      {"santabarbara.museum", 0},
+#line 488 "effective_tld_names.gperf"
+      {"chesapeakebay.museum", 0},
+#line 2429 "effective_tld_names.gperf"
+      {"org.im", 0},
+#line 1745 "effective_tld_names.gperf"
+      {"lancashire.museum", 0},
+#line 3138 "effective_tld_names.gperf"
+      {"taxi.aero", 0},
+#line 780 "effective_tld_names.gperf"
+      {"dep.no", 0},
+#line 1313 "effective_tld_names.gperf"
+      {"graz.museum", 0},
+#line 3060 "effective_tld_names.gperf"
+      {"stateofdelaware.museum", 0},
+#line 610 "effective_tld_names.gperf"
+      {"com.bm", 0},
+#line 1838 "effective_tld_names.gperf"
+      {"luxembourg.museum", 0},
+#line 837 "effective_tld_names.gperf"
+      {"edu.bm", 0},
+#line 967 "effective_tld_names.gperf"
+      {"esp.br", 0},
+#line 2353 "effective_tld_names.gperf"
+      {"okinawa.jp", 2},
+#line 3155 "effective_tld_names.gperf"
+      {"tf", 0},
+#line 1211 "effective_tld_names.gperf"
+      {"gov.bm", 0},
+#line 1696 "effective_tld_names.gperf"
+      {"kr.com", 0},
+#line 774 "effective_tld_names.gperf"
+      {"deatnu.no", 0},
+#line 2576 "effective_tld_names.gperf"
+      {"pharmacien.fr", 0},
+#line 2754 "effective_tld_names.gperf"
+      {"rahkkeravju.no", 0},
+#line 1768 "effective_tld_names.gperf"
+      {"lecco.it", 0},
+#line 2771 "effective_tld_names.gperf"
+      {"rec.br", 0},
+#line 2786 "effective_tld_names.gperf"
+      {"repbody.aero", 0},
+#line 3611 "effective_tld_names.gperf"
+      {"xn--sandnessjen-ogb.no", 0},
+#line 2869 "effective_tld_names.gperf"
+      {"sande.vestfold.no", 0},
+#line 3479 "effective_tld_names.gperf"
+      {"xn--bidr-5nac.no", 0},
+#line 1767 "effective_tld_names.gperf"
+      {"lecce.it", 0},
+#line 2153 "effective_tld_names.gperf"
+      {"net.bm", 0},
+#line 1435 "effective_tld_names.gperf"
+      {"homebuilt.aero", 0},
+#line 722 "effective_tld_names.gperf"
+      {"copenhagen.museum", 0},
+#line 2841 "effective_tld_names.gperf"
+      {"ryazan.ru", 0},
+#line 3679 "effective_tld_names.gperf"
+      {"yekaterinburg.ru", 0},
+#line 3132 "effective_tld_names.gperf"
+      {"targi.pl", 0},
+#line 3534 "effective_tld_names.gperf"
+      {"xn--klbu-woa.no", 0},
+#line 2344 "effective_tld_names.gperf"
+      {"odo.br", 0},
+#line 3395 "effective_tld_names.gperf"
+      {"vladimir.ru", 0},
+#line 3257 "effective_tld_names.gperf"
+      {"tychy.pl", 0},
+#line 3511 "effective_tld_names.gperf"
+      {"xn--gmq050i.hk", 0},
+#line 2405 "effective_tld_names.gperf"
+      {"org.br", 0},
+#line 1984 "effective_tld_names.gperf"
+      {"mincom.tn", 0},
+#line 2406 "effective_tld_names.gperf"
+      {"org.bs", 0},
+#line 2950 "effective_tld_names.gperf"
+      {"sherbrooke.museum", 0},
+#line 2400 "effective_tld_names.gperf"
+      {"org.bb", 0},
+#line 2110 "effective_tld_names.gperf"
+      {"naturalsciences.museum", 0},
+#line 2407 "effective_tld_names.gperf"
+      {"org.bw", 0},
+#line 593 "effective_tld_names.gperf"
+      {"colonialwilliamsburg.museum", 0},
+#line 1889 "effective_tld_names.gperf"
+      {"mb.ca", 0},
+#line 3112 "effective_tld_names.gperf"
+      {"swiebodzin.pl", 0},
+#line 1829 "effective_tld_names.gperf"
+      {"lucca.it", 0},
+#line 3193 "effective_tld_names.gperf"
+      {"tomsk.ru", 0},
+#line 764 "effective_tld_names.gperf"
+      {"dali.museum", 0},
+#line 3496 "effective_tld_names.gperf"
+      {"xn--drbak-wua.no", 0},
+#line 2682 "effective_tld_names.gperf"
+      {"pref.yamaguchi.jp", 1},
+#line 1774 "effective_tld_names.gperf"
+      {"leksvik.no", 0},
+#line 1810 "effective_tld_names.gperf"
+      {"lomza.pl", 0},
+#line 3144 "effective_tld_names.gperf"
+      {"technology.museum", 0},
+#line 3473 "effective_tld_names.gperf"
+      {"xn--b-5ga.telemark.no", 0},
+#line 3333 "effective_tld_names.gperf"
+      {"valer.ostfold.no", 0},
+#line 1385 "effective_tld_names.gperf"
+      {"harvestcelebration.museum", 0},
+#line 1812 "effective_tld_names.gperf"
+      {"loppa.no", 0},
+#line 2399 "effective_tld_names.gperf"
+      {"org.ba", 0},
+#line 256 "effective_tld_names.gperf"
+      {"avocat.fr", 0},
+#line 1780 "effective_tld_names.gperf"
+      {"lewismiller.museum", 0},
+#line 3468 "effective_tld_names.gperf"
+      {"xn--aroport-bya.ci", 0},
+#line 2402 "effective_tld_names.gperf"
+      {"org.bi", 0},
+#line 3188 "effective_tld_names.gperf"
+      {"tokke.no", 0},
+#line 3487 "effective_tld_names.gperf"
+      {"xn--brnnysund-m8ac.no", 0},
+#line 2059 "effective_tld_names.gperf"
+      {"museumvereniging.museum", 0},
+#line 769 "effective_tld_names.gperf"
+      {"dc.us", 0},
+#line 2661 "effective_tld_names.gperf"
+      {"pref.miyagi.jp", 1},
+#line 3242 "effective_tld_names.gperf"
+      {"turek.pl", 0},
+#line 2362 "effective_tld_names.gperf"
+      {"omasvuotna.no", 0},
+#line 761 "effective_tld_names.gperf"
+      {"daegu.kr", 0},
+#line 2404 "effective_tld_names.gperf"
+      {"org.bo", 0},
+#line 2113 "effective_tld_names.gperf"
+      {"naturhistorisches.museum", 0},
+#line 3636 "effective_tld_names.gperf"
+      {"xn--stjrdalshalsen-sqb.no", 0},
+#line 3674 "effective_tld_names.gperf"
+      {"yamaguchi.jp", 2},
+#line 2857 "effective_tld_names.gperf"
+      {"saitama.jp", 2},
+#line 2928 "effective_tld_names.gperf"
+      {"se.com", 0},
+#line 3637 "effective_tld_names.gperf"
+      {"xn--stre-toten-zcb.no", 0},
+#line 788 "effective_tld_names.gperf"
+      {"discovery.museum", 0},
+#line 2623 "effective_tld_names.gperf"
+      {"posts-and-telecommunications.museum", 0},
+#line 2849 "effective_tld_names.gperf"
+      {"sa.com", 0},
+#line 2765 "effective_tld_names.gperf"
+      {"rawa-maz.pl", 0},
+#line 2649 "effective_tld_names.gperf"
+      {"pref.hokkaido.jp", 1},
+#line 2908 "effective_tld_names.gperf"
+      {"schoenbrunn.museum", 0},
+#line 3162 "effective_tld_names.gperf"
+      {"timekeeping.museum", 0},
+#line 1566 "effective_tld_names.gperf"
+      {"ivano-frankivsk.ua", 0},
+#line 2115 "effective_tld_names.gperf"
+      {"naumburg.museum", 0},
+#line 766 "effective_tld_names.gperf"
+      {"database.museum", 0},
+#line 3475 "effective_tld_names.gperf"
+      {"xn--bearalvhki-y4a.no", 0},
+#line 2359 "effective_tld_names.gperf"
+      {"olsztyn.pl", 0},
+#line 2371 "effective_tld_names.gperf"
+      {"oppdal.no", 0},
+#line 2711 "effective_tld_names.gperf"
+      {"prochowice.pl", 0},
+#line 1082 "effective_tld_names.gperf"
+      {"freiburg.museum", 0},
+#line 1685 "effective_tld_names.gperf"
+      {"kommunalforbund.se", 0},
+#line 1592 "effective_tld_names.gperf"
+      {"jfk.museum", 0},
+#line 1360 "effective_tld_names.gperf"
+      {"gyeongbuk.kr", 0},
+#line 3632 "effective_tld_names.gperf"
+      {"xn--srfold-bya.no", 0},
+#line 3315 "effective_tld_names.gperf"
+      {"uvic.museum", 0},
+#line 3512 "effective_tld_names.gperf"
+      {"xn--gmqw5a.hk", 0},
+#line 3631 "effective_tld_names.gperf"
+      {"xn--sr-varanger-ggb.no", 0},
+#line 3221 "effective_tld_names.gperf"
+      {"trd.br", 0},
+#line 3361 "effective_tld_names.gperf"
+      {"vestby.no", 0},
+#line 1096 "effective_tld_names.gperf"
+      {"fukushima.jp", 2},
+#line 1450 "effective_tld_names.gperf"
+      {"huissier-justice.fr", 0},
+#line 451 "effective_tld_names.gperf"
+      {"capebreton.museum", 0},
+#line 400 "effective_tld_names.gperf"
+      {"british-library.uk", 1},
+#line 3130 "effective_tld_names.gperf"
+      {"tank.museum", 0},
+#line 3256 "effective_tld_names.gperf"
+      {"tx.us", 0},
+#line 3255 "effective_tld_names.gperf"
+      {"tw.cn", 0},
+#line 2135 "effective_tld_names.gperf"
+      {"nes.buskerud.no", 0},
+#line 3609 "effective_tld_names.gperf"
+      {"xn--ryrvik-bya.no", 0},
+#line 3073 "effective_tld_names.gperf"
+      {"stockholm.museum", 0},
+#line 3222 "effective_tld_names.gperf"
+      {"tree.museum", 0},
+#line 2340 "effective_tld_names.gperf"
+      {"oceanographique.museum", 0},
+#line 1796 "effective_tld_names.gperf"
+      {"lipetsk.ru", 0},
+#line 3486 "effective_tld_names.gperf"
+      {"xn--brnny-wuac.no", 0},
+#line 2520 "effective_tld_names.gperf"
+      {"other.nf", 0},
+#line 3241 "effective_tld_names.gperf"
+      {"tur.br", 0},
+#line 2408 "effective_tld_names.gperf"
+      {"org.bz", 0},
+#line 2412 "effective_tld_names.gperf"
+      {"org.cu", 0},
+#line 2662 "effective_tld_names.gperf"
+      {"pref.miyazaki.jp", 1},
+#line 3187 "effective_tld_names.gperf"
+      {"tochigi.jp", 2},
+#line 3277 "effective_tld_names.gperf"
+      {"uk.com", 0},
+#line 2357 "effective_tld_names.gperf"
+      {"olecko.pl", 0},
+#line 3154 "effective_tld_names.gperf"
+      {"textile.museum", 0},
+#line 2372 "effective_tld_names.gperf"
+      {"oppegard.no", 0},
+#line 2438 "effective_tld_names.gperf"
+      {"org.km", 0},
+#line 3153 "effective_tld_names.gperf"
+      {"texas.museum", 0},
+#line 3160 "effective_tld_names.gperf"
+      {"time.museum", 0},
+#line 781 "effective_tld_names.gperf"
+      {"depot.museum", 0},
+#line 3550 "effective_tld_names.gperf"
+      {"xn--leagaviika-52b.no", 0},
+#line 2651 "effective_tld_names.gperf"
+      {"pref.ibaraki.jp", 1},
+#line 3166 "effective_tld_names.gperf"
+      {"tj.cn", 0},
+#line 2358 "effective_tld_names.gperf"
+      {"olkusz.pl", 0},
+#line 2976 "effective_tld_names.gperf"
+      {"skedsmokorset.no", 0},
+#line 1397 "effective_tld_names.gperf"
+      {"hembygdsforbund.museum", 0},
+#line 478 "effective_tld_names.gperf"
+      {"chambagri.fr", 0},
+#line 2103 "effective_tld_names.gperf"
+      {"national-library-scotland.uk", 1},
+#line 3190 "effective_tld_names.gperf"
+      {"tokyo.jp", 2},
+#line 3483 "effective_tld_names.gperf"
+      {"xn--blt-elab.no", 0},
+#line 1102 "effective_tld_names.gperf"
+      {"fylkesbibl.no", 0},
+#line 2953 "effective_tld_names.gperf"
+      {"shizuoka.jp", 2},
+#line 3203 "effective_tld_names.gperf"
+      {"town.museum", 0},
+#line 3238 "effective_tld_names.gperf"
+      {"tsk.ru", 0},
+#line 3663 "effective_tld_names.gperf"
+      {"xn--wgbh1c", 0},
+#line 2368 "effective_tld_names.gperf"
+      {"operaunite.com", 0},
+#line 402 "effective_tld_names.gperf"
+      {"britishcolumbia.museum", 0},
+#line 3658 "effective_tld_names.gperf"
+      {"xn--vler-qoa.xn--stfold-9xa.no", 0},
+#line 3484 "effective_tld_names.gperf"
+      {"xn--bmlo-gra.no", 0},
+#line 810 "effective_tld_names.gperf"
+      {"durham.museum", 0},
+#line 3200 "effective_tld_names.gperf"
+      {"touch.museum", 0},
+#line 3113 "effective_tld_names.gperf"
+      {"swinoujscie.pl", 0},
+#line 3214 "effective_tld_names.gperf"
+      {"tranby.no", 0},
+#line 3537 "effective_tld_names.gperf"
+      {"xn--kranghke-b0a.no", 0},
+#line 3140 "effective_tld_names.gperf"
+      {"tcm.museum", 0},
+#line 3195 "effective_tld_names.gperf"
+      {"topology.museum", 0},
+#line 3146 "effective_tld_names.gperf"
+      {"telekommunikation.museum", 0},
+#line 819 "effective_tld_names.gperf"
+      {"ebiz.tw", 0},
+#line 3542 "effective_tld_names.gperf"
+      {"xn--kvfjord-nxa.no", 0},
+#line 2401 "effective_tld_names.gperf"
+      {"org.bh", 0},
+#line 3564 "effective_tld_names.gperf"
+      {"xn--mgba3a4f16a.ir", 0},
+#line 3565 "effective_tld_names.gperf"
+      {"xn--mgba3a4fra.ir", 0},
+#line 2780 "effective_tld_names.gperf"
+      {"reklam.hu", 0},
+#line 1415 "effective_tld_names.gperf"
+      {"historyofscience.museum", 0},
+#line 3518 "effective_tld_names.gperf"
+      {"xn--hery-ira.xn--mre-og-romsdal-qqb.no", 0},
+#line 2740 "effective_tld_names.gperf"
+      {"qc.com", 0},
+#line 3476 "effective_tld_names.gperf"
+      {"xn--berlevg-jxa.no", 0},
+#line 3493 "effective_tld_names.gperf"
+      {"xn--czrw28b.tw", 0},
+#line 3194 "effective_tld_names.gperf"
+      {"tonsberg.no", 0},
+#line 1583 "effective_tld_names.gperf"
+      {"jeonbuk.kr", 0},
+#line 3577 "effective_tld_names.gperf"
+      {"xn--mtta-vrjjat-k7af.no", 0},
+#line 3477 "effective_tld_names.gperf"
+      {"xn--bhcavuotna-s4a.no", 0},
+#line 772 "effective_tld_names.gperf"
+      {"de.com", 0},
+#line 3489 "effective_tld_names.gperf"
+      {"xn--btsfjord-9za.no", 0},
+#line 2835 "effective_tld_names.gperf"
+      {"ru.com", 0},
+#line 3481 "effective_tld_names.gperf"
+      {"xn--bjarky-fya.no", 0},
+#line 2352 "effective_tld_names.gperf"
+      {"okayama.jp", 2},
+#line 3474 "effective_tld_names.gperf"
+      {"xn--bdddj-mrabd.no", 0},
+#line 2339 "effective_tld_names.gperf"
+      {"oceanographic.museum", 0},
+#line 3078 "effective_tld_names.gperf"
+      {"store.bb", 0},
+#line 1795 "effective_tld_names.gperf"
+      {"linz.museum", 0},
+#line 3083 "effective_tld_names.gperf"
+      {"stpetersburg.museum", 0},
+#line 3429 "effective_tld_names.gperf"
+      {"watch-and-clock.museum", 0},
+#line 3466 "effective_tld_names.gperf"
+      {"xn--9dbhblg6di.museum", 0},
+#line 1121 "effective_tld_names.gperf"
+      {"gb.com", 0},
+#line 110 "effective_tld_names.gperf"
+      {"airtraffic.aero", 0},
+#line 3430 "effective_tld_names.gperf"
+      {"watchandclock.museum", 0},
+#line 2913 "effective_tld_names.gperf"
+      {"science-fiction.museum", 0},
+#line 3520 "effective_tld_names.gperf"
+      {"xn--hmmrfeasta-s4ac.no", 0},
+#line 2403 "effective_tld_names.gperf"
+      {"org.bm", 0},
+#line 3603 "effective_tld_names.gperf"
+      {"xn--rmskog-bya.no", 0},
+#line 3597 "effective_tld_names.gperf"
+      {"xn--rhkkervju-01af.no", 0},
+#line 2870 "effective_tld_names.gperf"
+      {"sande.xn--mre-og-romsdal-qqb.no", 0},
+#line 2389 "effective_tld_names.gperf"
+      {"orenburg.ru", 0},
+#line 3181 "effective_tld_names.gperf"
+      {"tmp.br", 0},
+#line 104 "effective_tld_names.gperf"
+      {"air-traffic-control.aero", 0},
+#line 3470 "effective_tld_names.gperf"
+      {"xn--aurskog-hland-jnb.no", 0},
+#line 3189 "effective_tld_names.gperf"
+      {"tokushima.jp", 2},
+#line 2778 "effective_tld_names.gperf"
+      {"reggiocalabria.it", 0},
+#line 2776 "effective_tld_names.gperf"
+      {"reggio-calabria.it", 0},
+#line 3540 "effective_tld_names.gperf"
+      {"xn--krjohka-hwab49j.no", 0},
+#line 3492 "effective_tld_names.gperf"
+      {"xn--correios-e-telecomunicaes-ghc29a.museum", 0},
+#line 2865 "effective_tld_names.gperf"
+      {"salzburg.museum", 0},
+#line 3580 "effective_tld_names.gperf"
+      {"xn--nmesjevuemie-tcba.no", 0},
+#line 3478 "effective_tld_names.gperf"
+      {"xn--bhccavuotna-k7a.no", 0},
+#line 986 "effective_tld_names.gperf"
+      {"experts-comptables.fr", 0}
     };
 
   static const short lookup[] =
     {
-        -1,   -1,   -1,    0,    1,   -1,    2,    3,
-        -1,   -1,   -1,   -1,    4,   -1,   -1,    5,
-         6,    7,    8,   -1,   -1,   -1,   -1,   -1,
-         9,   -1,   -1,   10,   11,   12,   13,   14,
-        15,   16,   17,   18,   -1,   -1,   -1,   19,
-        20,   21,   22,   23,   24,   25,   -1,   26,
-        -1,   27,   28,   -1,   -1,   29,   -1,   30,
-        -1,   31,   32,   33,   34,   35,   -1,   -1,
-        36,   -1,   37,   38,   39,   40,   -1,   41,
-        -1,   42,   43,   44,   -1,   45,   46,   47,
-        48,   49,   50,   -1,   -1,   -1,   -1,   -1,
-        51,   -1,   -1,   52,   53,   54,   -1,   -1,
-        55,   56,   57,   58,   59,   60,   61,   -1,
-        62,   63,   -1,   64,   -1,   -1,   -1,   -1,
-        65,   -1,   -1,   66,   -1,   67,   -1,   -1,
-        68,   -1,   69,   70,   -1,   -1,   -1,   71,
-        72,   -1,   73,   74,   -1,   75,   -1,   76,
-        77,   -1,   -1,   78,   -1,   79,   80,   -1,
-        -1,   -1,   -1,   81,   82,   83,   84,   -1,
-        -1,   85,   86,   -1,   87,   88,   89,   90,
-        -1,   91,   92,   -1,   -1,   93,   -1,   -1,
-        -1,   94,   -1,   -1,   -1,   95,   96,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        97,   -1,   -1,   98,   99,   -1,   -1,  100,
-       101,  102,   -1,   -1,  103,   -1,   -1,   -1,
-        -1,   -1,   -1,  104,   -1,   -1,   -1,  105,
-        -1,   -1,  106,   -1,   -1,  107,  108,   -1,
-        -1,   -1,   -1,   -1,  109,   -1,  110,   -1,
-       111,   -1,  112,   -1,   -1,   -1,   -1,  113,
-       114,   -1,  115,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  116,   -1,   -1,  117,   -1,  118,
-       119,   -1,   -1,  120,   -1,  121,   -1,   -1,
-       122,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  123,   -1,   -1,   -1,   -1,
-       124,   -1,  125,   -1,   -1,   -1,   -1,   -1,
-       126,   -1,   -1,   -1,   -1,  127,   -1,   -1,
-       128,   -1,  129,   -1,   -1,   -1,  130,   -1,
+        -1,   -1,    0,    1,    2,    3,    4,   -1,
+         5,    6,    7,    8,    9,   10,   11,   12,
+        -1,   -1,   -1,   -1,   -1,   13,   -1,   -1,
+        14,   15,   16,   17,   -1,   18,   19,   -1,
+        20,   21,   -1,   -1,   -1,   22,   23,   24,
+        -1,   25,   -1,   26,   27,   28,   -1,   29,
+        30,   31,   32,   -1,   -1,   33,   34,   35,
+        36,   37,   -1,   38,   -1,   39,   40,   -1,
+        41,   42,   -1,   -1,   43,   44,   -1,   -1,
+        -1,   45,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   46,   47,   -1,   48,   -1,   -1,   49,
+        -1,   -1,   50,   -1,   51,   -1,   52,   53,
+        54,   55,   -1,   -1,   -1,   -1,   56,   -1,
+        -1,   57,   58,   -1,   -1,   59,   60,   61,
+        -1,   62,   63,   64,   -1,   -1,   -1,   -1,
+        -1,   65,   66,   67,   -1,   68,   69,   70,
+        71,   72,   73,   74,   75,   76,   -1,   -1,
+        -1,   77,   -1,   78,   79,   -1,   80,   -1,
+        81,   -1,   -1,   82,   83,   -1,   84,   85,
+        86,   -1,   87,   88,   89,   90,   91,   -1,
+        92,   93,   94,   95,   -1,   -1,   -1,   -1,
+        96,   97,   98,   99,   -1,  100,  101,   -1,
+       102,  103,   -1,  104,  105,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  106,  107,
+        -1,   -1,   -1,   -1,  108,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  109,  110,   -1,   -1,
+       111,  112,  113,  114,   -1,  115,   -1,   -1,
+       116,   -1,   -1,  117,  118,  119,  120,   -1,
+        -1,   -1,  121,   -1,  122,   -1,   -1,   -1,
+        -1,  123,  124,  125,   -1,  126,   -1,  127,
+        -1,   -1,  128,   -1,   -1,   -1,   -1,   -1,
+       129,  130,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,  131,   -1,   -1,
-        -1,   -1,  132,   -1,   -1,  133,   -1,  134,
-        -1,   -1,  135,   -1,   -1,   -1,   -1,   -1,
-        -1,  136,   -1,   -1,   -1,   -1,  137,  138,
-        -1,   -1,  139,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  140,
-        -1,   -1,   -1,  141,   -1,   -1,   -1,   -1,
-        -1,  142,   -1,  143,  144,  145,  146,  147,
-       148,  149,   -1,   -1,   -1,   -1,  150,   -1,
-       151,  152,   -1,  153,   -1,  154,   -1,  155,
-       156,   -1,   -1,   -1,   -1,   -1,  157,   -1,
-        -1,  158,   -1,   -1,   -1,   -1,   -1,   -1,
-       159,  160,  161,   -1,   -1,  162,   -1,   -1,
-        -1,  163,  164,   -1,   -1,   -1,   -1,  165,
-        -1,   -1,   -1,  166,  167,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,  168,   -1,  169,  170,
-        -1,   -1,  171,   -1,   -1,   -1,   -1,  172,
-        -1,  173,   -1,   -1,   -1,   -1,   -1,   -1,
-       174,   -1,   -1,   -1,   -1,  175,   -1,  176,
-        -1,   -1,   -1,   -1,   -1,   -1,  177,   -1,
-        -1,   -1,   -1,  178,   -1,   -1,   -1,   -1,
-       179,   -1,   -1,  180,   -1,  181,   -1,   -1,
-        -1,  182,  183,   -1,  184,  185,   -1,   -1,
-       186,   -1,  187,   -1,   -1,  188,  189,  190,
-        -1,   -1,  191,   -1,   -1,   -1,  192,   -1,
-        -1,   -1,   -1,   -1,  193,   -1,  194,  195,
-        -1,   -1,   -1,   -1,   -1,   -1,  196,  197,
-       198,   -1,  199,   -1,   -1,   -1,   -1,   -1,
-       200,   -1,  201,  202,   -1,   -1,  203,  204,
-        -1,  205,   -1,   -1,  206,   -1,   -1,  207,
-        -1,   -1,   -1,  208,   -1,   -1,   -1,  209,
-        -1,  210,   -1,   -1,  211,   -1,   -1,   -1,
-        -1,   -1,  212,   -1,  213,  214,   -1,   -1,
-        -1,  215,  216,   -1,   -1,  217,   -1,   -1,
-        -1,  218,   -1,   -1,   -1,   -1,  219,   -1,
-       220,  221,   -1,   -1,   -1,  222,  223,  224,
-       225,   -1,   -1,   -1,   -1,   -1,   -1,  226,
-        -1,   -1,   -1,  227,   -1,  228,   -1,   -1,
-       229,  230,  231,   -1,   -1,   -1,  232,   -1,
-        -1,   -1,  233,  234,  235,   -1,   -1,  236,
-        -1,   -1,  237,  238,   -1,   -1,  239,  240,
-        -1,  241,   -1,  242,  243,   -1,   -1,   -1,
-        -1,  244,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  245,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  132,  133,  134,  135,   -1,
+        -1,   -1,   -1,   -1,   -1,  136,   -1,   -1,
+        -1,   -1,  137,   -1,   -1,   -1,  138,  139,
+       140,  141,  142,   -1,  143,  144,   -1,  145,
+       146,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  147,   -1,   -1,   -1,  148,   -1,  149,
+       150,  151,   -1,   -1,   -1,   -1,  152,   -1,
+       153,   -1,  154,   -1,  155,  156,  157,   -1,
+       158,  159,   -1,  160,  161,   -1,  162,   -1,
+        -1,   -1,  163,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  164,  165,   -1,  166,   -1,   -1,
+        -1,   -1,  167,   -1,  168,   -1,   -1,   -1,
+        -1,   -1,  169,   -1,  170,  171,   -1,   -1,
+       172,   -1,   -1,  173,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  174,   -1,  175,  176,
+        -1,   -1,   -1,   -1,   -1,  177,  178,   -1,
+       179,   -1,   -1,   -1,   -1,  180,   -1,   -1,
+        -1,  181,  182,  183,   -1,  184,   -1,  185,
+       186,   -1,   -1,   -1,  187,  188,   -1,   -1,
+        -1,   -1,   -1,  189,  190,  191,   -1,  192,
+        -1,  193,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  194,  195,  196,   -1,  197,   -1,
+        -1,  198,  199,  200,   -1,   -1,  201,  202,
+        -1,  203,  204,  205,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  206,  207,  208,
+        -1,   -1,   -1,  209,   -1,  210,   -1,  211,
+       212,  213,   -1,   -1,   -1,   -1,   -1,   -1,
+       214,   -1,   -1,   -1,   -1,   -1,  215,  216,
+        -1,   -1,   -1,   -1,  217,   -1,  218,   -1,
+        -1,  219,   -1,   -1,   -1,   -1,   -1,  220,
+        -1,  221,   -1,   -1,   -1,   -1,  222,  223,
+       224,   -1,   -1,   -1,  225,   -1,   -1,   -1,
+        -1,  226,   -1,   -1,  227,  228,  229,  230,
+       231,   -1,  232,  233,   -1,   -1,  234,  235,
+       236,   -1,   -1,  237,   -1,  238,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  239,   -1,  240,
+       241,   -1,   -1,   -1,   -1,   -1,   -1,  242,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  246,  247,  248,
-        -1,   -1,   -1,  249,   -1,   -1,   -1,  250,
+       243,  244,   -1,   -1,   -1,  245,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  246,   -1,
+        -1,   -1,   -1,   -1,  247,   -1,   -1,   -1,
+        -1,   -1,   -1,  248,   -1,   -1,   -1,   -1,
+       249,   -1,  250,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,  251,   -1,   -1,
+        -1,  252,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  253,  254,   -1,
+        -1,   -1,   -1,   -1,   -1,  255,   -1,   -1,
+        -1,   -1,  256,  257,   -1,   -1,  258,   -1,
+        -1,   -1,   -1,  259,  260,  261,   -1,   -1,
+        -1,   -1,  262,   -1,   -1,  263,  264,   -1,
+        -1,  265,   -1,   -1,  266,   -1,   -1,   -1,
+       267,  268,  269,  270,   -1,   -1,  271,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  272,   -1,
+       273,   -1,   -1,   -1,   -1,   -1,  274,   -1,
+        -1,   -1,  275,   -1,   -1,  276,   -1,   -1,
+       277,   -1,   -1,   -1,   -1,  278,   -1,   -1,
+        -1,   -1,   -1,  279,   -1,   -1,   -1,   -1,
+       280,   -1,  281,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  282,  283,   -1,   -1,  284,
+        -1,   -1,   -1,   -1,  285,   -1,   -1,  286,
+       287,   -1,   -1,  288,   -1,   -1,  289,   -1,
+        -1,   -1,   -1,   -1,  290,   -1,   -1,   -1,
+        -1,   -1,  291,   -1,  292,   -1,  293,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       252,   -1,  253,   -1,   -1,  254,   -1,  255,
-        -1,   -1,   -1,  256,   -1,   -1,   -1,  257,
-       258,   -1,   -1,   -1,   -1,   -1,   -1,  259,
-        -1,   -1,   -1,   -1,  260,  261,   -1,   -1,
-       262,  263,   -1,   -1,  264,   -1,   -1,  265,
-       266,   -1,   -1,   -1,  267,   -1,   -1,   -1,
-        -1,  268,   -1,   -1,  269,  270,  271,  272,
-        -1,   -1,   -1,   -1,   -1,  273,   -1,  274,
-        -1,   -1,  275,  276,   -1,   -1,  277,  278,
-        -1,  279,  280,  281,   -1,   -1,  282,   -1,
-       283,   -1,   -1,   -1,  284,   -1,   -1,   -1,
-       285,   -1,  286,   -1,   -1,  287,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  288,
-       289,  290,  291,   -1,   -1,   -1,   -1,   -1,
-       292,  293,  294,  295,   -1,   -1,   -1,   -1,
-        -1,  296,   -1,  297,  298,   -1,  299,   -1,
-        -1,  300,   -1,   -1,   -1,   -1,   -1,   -1,
-       301,  302,   -1,   -1,   -1,   -1,   -1,   -1,
-       303,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  304,   -1,   -1,  305,   -1,   -1,
-       306,   -1,  307,  308,   -1,  309,   -1,   -1,
-       310,   -1,   -1,  311,   -1,  312,   -1,   -1,
-        -1,  313,   -1,   -1,   -1,   -1,  314,   -1,
-        -1,   -1,  315,   -1,  316,  317,   -1,   -1,
-       318,   -1,  319,  320,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  321,
-       322,  323,  324,   -1,   -1,  325,  326,   -1,
-        -1,   -1,  327,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,  328,   -1,   -1,   -1,
-       329,   -1,  330,   -1,   -1,  331,   -1,   -1,
-       332,   -1,   -1,   -1,   -1,  333,   -1,   -1,
-        -1,   -1,  334,   -1,  335,   -1,   -1,   -1,
-        -1,   -1,   -1,  336,  337,  338,   -1,  339,
-        -1,   -1,   -1,   -1,   -1,  340,  341,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  342,   -1,
+       294,   -1,   -1,   -1,   -1,   -1,   -1,  295,
+        -1,   -1,   -1,   -1,   -1,  296,   -1,   -1,
+        -1,   -1,   -1,  297,   -1,   -1,   -1,   -1,
+        -1,  298,   -1,  299,  300,  301,   -1,   -1,
+        -1,   -1,   -1,  302,   -1,   -1,  303,   -1,
+        -1,   -1,   -1,  304,   -1,  305,   -1,  306,
+        -1,  307,  308,   -1,  309,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       343,   -1,   -1,  344,   -1,   -1,   -1,   -1,
-       345,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  311,  312,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  346,   -1,   -1,   -1,   -1,  347,
-        -1,   -1,   -1,   -1,   -1,   -1,  348,   -1,
-        -1,   -1,   -1,   -1,   -1,  349,  350,   -1,
-       351,   -1,   -1,  352,   -1,   -1,  353,   -1,
-        -1,  354,   -1,   -1,   -1,   -1,   -1,  355,
-        -1,   -1,  356,  357,   -1,   -1,  358,  359,
-       360,   -1,   -1,   -1,  361,  362,   -1,  363,
-        -1,   -1,  364,   -1,  365,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  366,   -1,   -1,
-       367,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  368,   -1,   -1,   -1,   -1,   -1,  369,
-        -1,  370,   -1,  371,   -1,   -1,  372,   -1,
-        -1,  373,   -1,   -1,   -1,  374,  375,  376,
-        -1,   -1,  377,  378,   -1,   -1,  379,  380,
-        -1,  381,   -1,   -1,   -1,  382,   -1,  383,
-       384,  385,   -1,   -1,   -1,   -1,   -1,   -1,
-       386,   -1,   -1,  387,  388,   -1,   -1,   -1,
-        -1,   -1,   -1,  389,   -1,   -1,   -1,   -1,
-        -1,   -1,  390,  391,   -1,  392,  393,  394,
-        -1,   -1,   -1,   -1,   -1,  395,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  396,   -1,
-       397,   -1,   -1,  398,   -1,   -1,  399,  400,
-        -1,  401,  402,   -1,  403,   -1,   -1,  404,
-        -1,  405,  406,  407,   -1,  408,   -1,   -1,
-       409,   -1,  410,  411,  412,   -1,   -1,  413,
-       414,  415,   -1,   -1,   -1,  416,   -1,   -1,
-        -1,  417,   -1,   -1,  418,   -1,  419,   -1,
+        -1,   -1,   -1,   -1,   -1,  313,   -1,   -1,
+        -1,   -1,  314,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  315,   -1,   -1,   -1,   -1,
+       316,   -1,   -1,   -1,   -1,   -1,  317,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  318,  319,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  420,
-       421,   -1,   -1,   -1,   -1,   -1,  422,   -1,
+        -1,   -1,   -1,   -1,   -1,  320,   -1,   -1,
+        -1,   -1,  321,   -1,   -1,   -1,   -1,   -1,
+       322,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       323,   -1,   -1,  324,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  325,  326,  327,   -1,  328,
+        -1,  329,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  330,   -1,  331,   -1,  332,  333,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  334,
+        -1,  335,   -1,   -1,   -1,   -1,  336,   -1,
+        -1,   -1,   -1,   -1,  337,   -1,   -1,   -1,
+       338,   -1,   -1,   -1,  339,  340,  341,   -1,
+        -1,   -1,  342,   -1,   -1,   -1,   -1,  343,
+       344,   -1,   -1,   -1,  345,   -1,  346,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  347,   -1,
+        -1,  348,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  349,   -1,   -1,  350,  351,   -1,  352,
+        -1,   -1,  353,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  354,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  355,  356,
+        -1,   -1,   -1,   -1,   -1,   -1,  357,   -1,
+       358,   -1,  359,  360,   -1,  361,  362,  363,
+       364,   -1,  365,   -1,  366,   -1,   -1,   -1,
+        -1,  367,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  368,  369,  370,  371,   -1,   -1,
+        -1,   -1,   -1,  372,   -1,   -1,   -1,  373,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  423,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  424,  425,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  426,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  374,  375,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  376,   -1,   -1,   -1,
+       377,   -1,  378,   -1,   -1,   -1,   -1,   -1,
+       379,  380,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  381,   -1,
+        -1,  382,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  383,  384,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  385,
+        -1,   -1,   -1,   -1,   -1,  386,   -1,   -1,
+       387,   -1,   -1,  388,   -1,   -1,   -1,   -1,
+        -1,   -1,  389,  390,   -1,   -1,   -1,   -1,
+        -1,   -1,  391,   -1,   -1,   -1,   -1,   -1,
+       392,   -1,   -1,   -1,   -1,  393,  394,  395,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       396,  397,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  398,   -1,  399,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  400,   -1,   -1,   -1,   -1,   -1,  401,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  402,  403,  404,
+        -1,   -1,   -1,   -1,  405,   -1,   -1,   -1,
+       406,  407,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  408,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  409,  410,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       411,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       412,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  413,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  414,   -1,
+        -1,   -1,   -1,  415,   -1,   -1,   -1,   -1,
+       416,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  417,   -1,   -1,
+        -1,  418,   -1,  419,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  420,   -1,   -1,   -1,   -1,   -1,
+       421,  422,  423,   -1,   -1,   -1,   -1,  424,
+        -1,   -1,   -1,   -1,   -1,   -1,  425,  426,
         -1,   -1,   -1,   -1,   -1,  427,   -1,   -1,
-        -1,   -1,   -1,  428,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  429,   -1,   -1,  430,   -1,
-        -1,   -1,   -1,   -1,  431,   -1,  432,   -1,
-       433,   -1,   -1,  434,   -1,  435,   -1,  436,
-        -1,   -1,   -1,  437,   -1,   -1,   -1,  438,
-        -1,   -1,   -1,  439,   -1,   -1,   -1,  440,
-        -1,  441,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  442,   -1,   -1,  443,  444,   -1,  445,
-        -1,   -1,   -1,   -1,  446,   -1,   -1,  447,
-        -1,   -1,   -1,   -1,  448,  449,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  450,
-       451,   -1,   -1,  452,   -1,   -1,   -1,   -1,
-       453,   -1,   -1,  454,   -1,   -1,   -1,   -1,
-        -1,   -1,  455,   -1,  456,  457,   -1,   -1,
-       458,   -1,   -1,   -1,  459,  460,   -1,  461,
-        -1,   -1,   -1,  462,   -1,   -1,  463,  464,
-       465,   -1,   -1,  466,   -1,   -1,   -1,   -1,
-        -1,  467,   -1,   -1,   -1,   -1,  468,   -1,
-        -1,  469,  470,  471,  472,  473,   -1,   -1,
-        -1,  474,   -1,   -1,   -1,   -1,  475,   -1,
-        -1,   -1,   -1,  476,   -1,   -1,   -1,   -1,
-        -1,  477,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  478,   -1,   -1,   -1,   -1,  479,  480,
-        -1,   -1,  481,  482,   -1,   -1,   -1,  483,
-        -1,  484,   -1,   -1,   -1,  485,   -1,  486,
-       487,   -1,   -1,   -1,  488,  489,  490,  491,
+        -1,  428,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  429,   -1,   -1,   -1,  430,  431,
+       432,  433,  434,   -1,   -1,   -1,  435,  436,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  492,   -1,   -1,  493,   -1,   -1,
-       494,  495,   -1,  496,  497,  498,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  499,  500,   -1,
-       501,   -1,   -1,   -1,  502,   -1,  503,  504,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  505,
-       506,  507,   -1,   -1,   -1,   -1,   -1,  508,
-        -1,   -1,   -1,  509,   -1,   -1,  510,  511,
-        -1,  512,  513,  514,   -1,   -1,   -1,   -1,
-        -1,  515,   -1,  516,   -1,   -1,   -1,  517,
-       518,  519,  520,   -1,  521,   -1,  522,   -1,
-        -1,   -1,   -1,  523,  524,  525,   -1,   -1,
-        -1,   -1,   -1,   -1,  526,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  527,  528,  529,
-        -1,   -1,   -1,   -1,  530,  531,   -1,   -1,
-       532,  533,  534,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,  535,   -1,   -1,  536,
-       537,   -1,  538,  539,   -1,   -1,  540,   -1,
-        -1,   -1,   -1,   -1,   -1,  541,   -1,   -1,
-        -1,   -1,  542,  543,   -1,   -1,   -1,   -1,
+        -1,  437,  438,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  439,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  440,  441,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  442,   -1,   -1,
+        -1,   -1,   -1,  443,  444,   -1,   -1,   -1,
+        -1,  445,   -1,   -1,  446,   -1,   -1,  447,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       448,   -1,  449,   -1,  450,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       451,   -1,   -1,   -1,   -1,  452,   -1,  453,
+        -1,  454,   -1,   -1,   -1,  455,   -1,  456,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  457,
+        -1,   -1,   -1,   -1,   -1,  458,   -1,  459,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  460,   -1,  461,   -1,   -1,   -1,
+        -1,   -1,   -1,  462,  463,   -1,   -1,  464,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  465,
+        -1,  466,   -1,   -1,   -1,  467,   -1,   -1,
+        -1,  468,   -1,  469,   -1,   -1,   -1,  470,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  471,
+        -1,   -1,   -1,   -1,   -1,  472,   -1,  473,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  474,  475,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  476,
+       477,  478,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       479,   -1,   -1,   -1,   -1,   -1,  480,   -1,
+       481,   -1,   -1,  482,   -1,   -1,  483,   -1,
+        -1,  484,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  485,   -1,   -1,   -1,   -1,
+        -1,   -1,  486,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  487,   -1,   -1,   -1,
+       488,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       489,   -1,   -1,   -1,  490,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  491,   -1,   -1,
+        -1,   -1,  492,   -1,   -1,   -1,   -1,  493,
+        -1,   -1,  494,   -1,  495,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  496,  497,
+        -1,  498,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  499,   -1,   -1,
+        -1,   -1,   -1,  500,   -1,   -1,   -1,   -1,
+       501,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       502,   -1,  503,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  504,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  505,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  506,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       507,   -1,   -1,   -1,  508,   -1,  509,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  510,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  511,   -1,   -1,   -1,   -1,   -1,
+       512,   -1,  513,   -1,  514,   -1,  515,   -1,
+        -1,  516,  517,   -1,   -1,  518,   -1,   -1,
+        -1,   -1,   -1,  519,   -1,   -1,  520,   -1,
+        -1,   -1,  521,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  522,   -1,   -1,   -1,   -1,  523,
+        -1,   -1,   -1,  524,   -1,   -1,  525,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  526,  527,
+       528,  529,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       530,   -1,   -1,   -1,   -1,  531,  532,   -1,
+        -1,   -1,  533,   -1,   -1,   -1,   -1,   -1,
+       534,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  535,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  536,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  537,
+        -1,   -1,  538,   -1,   -1,  539,   -1,  540,
+        -1,   -1,  541,   -1,   -1,  542,   -1,  543,
        544,  545,   -1,   -1,   -1,   -1,   -1,   -1,
+       546,  547,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  546,   -1,  547,   -1,   -1,   -1,
-        -1,   -1,   -1,  548,  549,   -1,  550,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  551,   -1,   -1,  552,   -1,   -1,   -1,
-       553,  554,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,  555,   -1,   -1,   -1,
-        -1,   -1,  556,   -1,   -1,   -1,   -1,   -1,
-        -1,  557,  558,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  559,   -1,   -1,  560,   -1,
-        -1,   -1,   -1,   -1,  561,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,  562,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  563,   -1,
-        -1,  564,   -1,   -1,   -1,   -1,  565,   -1,
-        -1,  566,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  567,
+       548,  549,   -1,   -1,  550,   -1,  551,   -1,
+       552,   -1,  553,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  554,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  568,   -1,   -1,   -1,   -1,  569,  570,
-       571,   -1,   -1,  572,   -1,  573,  574,  575,
-       576,   -1,  577,  578,   -1,  579,   -1,   -1,
-       580,   -1,   -1,   -1,  581,   -1,   -1,   -1,
-       582,   -1,   -1,   -1,  583,   -1,   -1,  584,
-        -1,  585,   -1,   -1,  586,  587,   -1,   -1,
-        -1,  588,   -1,   -1,  589,   -1,   -1,   -1,
-        -1,   -1,   -1,  590,   -1,   -1,  591,  592,
-        -1,   -1,   -1,  593,   -1,  594,   -1,   -1,
-        -1,   -1,  595,   -1,   -1,   -1,   -1,   -1,
-        -1,  596,  597,   -1,   -1,   -1,   -1,  598,
-        -1,   -1,   -1,   -1,   -1,   -1,  599,   -1,
-        -1,   -1,  600,   -1,  601,  602,   -1,   -1,
-        -1,   -1,   -1,  603,   -1,  604,  605,  606,
-        -1,   -1,   -1,  607,   -1,   -1,  608,  609,
-       610,  611,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  612,   -1,
-        -1,  613,   -1,   -1,   -1,   -1,  614,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  615,
-        -1,  616,  617,  618,   -1,  619,   -1,   -1,
-       620,   -1,   -1,   -1,   -1,  621,   -1,  622,
-        -1,   -1,   -1,   -1,  623,   -1,   -1,  624,
-       625,   -1,   -1,   -1,  626,  627,   -1,   -1,
+        -1,  555,   -1,   -1,   -1,   -1,   -1,   -1,
+       556,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  557,   -1,   -1,   -1,  558,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  628,   -1,   -1,   -1,   -1,  629,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  630,   -1,   -1,   -1,  631,  632,   -1,
-        -1,  633,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,  634,   -1,   -1,  635,
-        -1,   -1,   -1,  636,   -1,   -1,  637,   -1,
-        -1,   -1,   -1,   -1,   -1,  638,  639,  640,
-        -1,   -1,  641,   -1,  642,   -1,  643,  644,
-        -1,   -1,   -1,  645,   -1,   -1,   -1,  646,
-        -1,  647,   -1,  648,  649,   -1,   -1,   -1,
+        -1,   -1,  559,   -1,   -1,  560,   -1,  561,
+        -1,   -1,   -1,  562,   -1,   -1,   -1,   -1,
+        -1,  563,   -1,   -1,  564,   -1,   -1,   -1,
+        -1,  565,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  566,   -1,   -1,   -1,  567,  568,  569,
+        -1,   -1,   -1,  570,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  571,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  572,   -1,  573,
+        -1,  574,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       650,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  651,   -1,  652,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  575,   -1,   -1,   -1,   -1,  576,   -1,
+        -1,   -1,   -1,  577,   -1,   -1,   -1,   -1,
+        -1,   -1,  578,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  579,   -1,  580,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  581,
+        -1,   -1,   -1,  582,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  583,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  584,   -1,   -1,
+        -1,  585,  586,   -1,   -1,  587,   -1,   -1,
+        -1,  588,  589,  590,   -1,   -1,   -1,  591,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  592,   -1,  593,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,  653,  654,   -1,   -1,
-        -1,   -1,   -1,  655,   -1,   -1,   -1,  656,
-       657,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  658,   -1,   -1,  659,   -1,   -1,
-       660,   -1,  661,   -1,   -1,   -1,  662,  663,
-       664,   -1,  665,   -1,   -1,   -1,   -1,   -1,
-       666,  667,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  668,   -1,   -1,  669,   -1,
-        -1,   -1,  670,   -1,   -1,   -1,  671,   -1,
-       672,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  673,  674,
-       675,  676,   -1,   -1,   -1,   -1,   -1,  677,
-       678,   -1,   -1,   -1,   -1,   -1,   -1,  679,
-        -1,   -1,   -1,   -1,   -1,  680,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  681,  682,   -1,   -1,   -1,   -1,   -1,
-       683,   -1,   -1,   -1,  684,  685,  686,   -1,
-       687,   -1,  688,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  689,
-        -1,   -1,  690,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  691,  692,   -1,  693,   -1,
-        -1,   -1,  694,   -1,   -1,   -1,  695,   -1,
-        -1,   -1,  696,   -1,  697,  698,   -1,   -1,
-        -1,   -1,  699,   -1,   -1,   -1,   -1,   -1,
+       594,   -1,   -1,   -1,  595,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  596,
+        -1,   -1,   -1,   -1,   -1,  597,   -1,  598,
+       599,   -1,   -1,   -1,   -1,   -1,  600,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       700,   -1,   -1,   -1,   -1,   -1,   -1,  701,
+        -1,   -1,   -1,  601,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  702,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       703,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  704,   -1,   -1,   -1,   -1,  705,
-        -1,  706,  707,   -1,  708,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,  709,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  602,   -1,   -1,   -1,
+        -1,  603,   -1,  604,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       710,   -1,   -1,  711,  712,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  605,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  606,   -1,
+        -1,   -1,   -1,  607,   -1,   -1,   -1,  608,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       609,   -1,   -1,  610,   -1,   -1,   -1,   -1,
+       611,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       612,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  613,   -1,
+        -1,   -1,   -1,   -1,   -1,  614,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       713,  714,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  715,  716,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       717,   -1,   -1,  718,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  719,   -1,   -1,
-       720,   -1,   -1,   -1,   -1,   -1,  721,   -1,
-        -1,   -1,   -1,   -1,   -1,  722,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  723,
-       724,   -1,  725,   -1,   -1,   -1,  726,   -1,
-        -1,   -1,  727,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  728,  729,
-       730,  731,  732,   -1,   -1,   -1,  733,   -1,
-       734,  735,   -1,  736,   -1,  737,   -1,   -1,
-       738,   -1,   -1,   -1,  739,   -1,   -1,   -1,
-        -1,   -1,   -1,  740,   -1,   -1,   -1,  741,
-        -1,   -1,   -1,   -1,  742,  743,   -1,   -1,
+       615,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  616,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  617,   -1,   -1,   -1,   -1,  618,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  619,   -1,   -1,   -1,   -1,
+        -1,   -1,  620,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  621,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  622,   -1,  623,   -1,   -1,  624,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       625,   -1,   -1,  626,   -1,   -1,   -1,  627,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  628,   -1,   -1,   -1,  629,   -1,   -1,
+        -1,  630,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  631,   -1,   -1,   -1,
+       632,   -1,   -1,   -1,   -1,  633,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  634,
+        -1,   -1,   -1,   -1,   -1,  635,   -1,  636,
+       637,   -1,   -1,   -1,   -1,  638,   -1,   -1,
+       639,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       640,   -1,   -1,   -1,   -1,   -1,   -1,  641,
+       642,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  643,   -1,   -1,
+        -1,   -1,   -1,  644,   -1,   -1,   -1,  645,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  646,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  647,  648,  649,   -1,
+        -1,   -1,   -1,  650,   -1,   -1,   -1,   -1,
+       651,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  652,   -1,   -1,   -1,   -1,   -1,  653,
+        -1,   -1,   -1,   -1,   -1,  654,   -1,   -1,
+        -1,  655,   -1,  656,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  657,   -1,  658,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  659,   -1,
+       660,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  661,   -1,   -1,   -1,
+        -1,   -1,   -1,  662,   -1,   -1,   -1,  663,
+        -1,  664,   -1,   -1,   -1,  665,   -1,   -1,
+        -1,   -1,   -1,   -1,  666,   -1,   -1,  667,
+        -1,  668,   -1,   -1,   -1,  669,   -1,   -1,
+        -1,   -1,   -1,  670,  671,   -1,   -1,  672,
+       673,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  674,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  675,   -1,   -1,   -1,
+        -1,   -1,  676,   -1,  677,   -1,   -1,  678,
+        -1,   -1,   -1,   -1,   -1,   -1,  679,   -1,
+        -1,   -1,   -1,   -1,  680,   -1,   -1,   -1,
+        -1,   -1,   -1,  681,   -1,   -1,   -1,   -1,
+       682,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       683,  684,  685,   -1,   -1,  686,   -1,  687,
+        -1,   -1,   -1,  688,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  689,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  690,   -1,  691,   -1,   -1,   -1,
+        -1,   -1,  692,   -1,   -1,   -1,   -1,  693,
+        -1,   -1,   -1,   -1,   -1,   -1,  694,   -1,
+       695,  696,   -1,  697,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  698,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       699,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  700,   -1,   -1,   -1,
+        -1,  701,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  702,   -1,   -1,   -1,  703,   -1,
+        -1,   -1,  704,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  705,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  706,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  707,   -1,   -1,
+        -1,  708,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  709,   -1,   -1,   -1,   -1,
+       710,   -1,   -1,   -1,  711,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  712,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  713,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  714,   -1,  715,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  716,  717,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  718,
+       719,  720,  721,   -1,   -1,   -1,   -1,  722,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  723,  724,   -1,
+        -1,   -1,   -1,  725,  726,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  727,   -1,
+        -1,   -1,   -1,   -1,   -1,  728,   -1,  729,
+        -1,   -1,  730,   -1,   -1,   -1,  731,  732,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  733,  734,   -1,   -1,  735,   -1,
+       736,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  737,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  738,  739,   -1,   -1,   -1,
+       740,   -1,  741,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  742,   -1,   -1,  743,   -1,
        744,   -1,   -1,   -1,   -1,   -1,  745,   -1,
-        -1,   -1,  746,   -1,  747,   -1,   -1,   -1,
-        -1,  748,   -1,   -1,   -1,   -1,  749,   -1,
-        -1,   -1,   -1,   -1,  750,   -1,   -1,   -1,
-        -1,  751,  752,   -1,   -1,   -1,   -1,  753,
-        -1,   -1,  754,   -1,   -1,   -1,   -1,   -1,
-       755,   -1,   -1,   -1,  756,   -1,   -1,   -1,
-       757,   -1,  758,   -1,  759,   -1,   -1,   -1,
-        -1,   -1,  760,   -1,   -1,  761,   -1,   -1,
-        -1,  762,  763,  764,   -1,   -1,  765,   -1,
+        -1,   -1,   -1,  746,   -1,   -1,   -1,  747,
+        -1,   -1,   -1,   -1,  748,  749,   -1,   -1,
+        -1,  750,   -1,   -1,   -1,  751,   -1,  752,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  766,   -1,
+        -1,   -1,  753,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  754,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  755,  756,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  757,  758,  759,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  760,  761,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  762,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       767,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  768,
-       769,   -1,   -1,   -1,  770,   -1,   -1,   -1,
-        -1,  771,   -1,  772,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  773,
-        -1,   -1,   -1,   -1,   -1,   -1,  774,   -1,
-        -1,  775,   -1,   -1,  776,  777,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  778,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  763,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       779,   -1,  780,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  781,   -1,
-        -1,   -1,  782,   -1,   -1,   -1,   -1,  783,
-        -1,   -1,   -1,   -1,   -1,  784,  785,   -1,
-        -1,   -1,   -1,   -1,  786,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  787,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  788,   -1,
-        -1,   -1,  789,   -1,   -1,   -1,   -1,   -1,
-        -1,  790,   -1,   -1,   -1,  791,   -1,   -1,
-       792,   -1,   -1,   -1,  793,   -1,   -1,   -1,
-        -1,   -1,  794,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  795,   -1,   -1,   -1,  796,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  797,   -1,   -1,   -1,   -1,   -1,   -1,
-       798,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  799,   -1,   -1,   -1,   -1,
-        -1,  800,   -1,   -1,   -1,   -1,   -1,  801,
-        -1,   -1,  802,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  803,  804,   -1,   -1,  805,
-        -1,  806,  807,  808,   -1,   -1,   -1,   -1,
-       809,   -1,   -1,  810,   -1,   -1,  811,   -1,
-        -1,   -1,   -1,  812,   -1,  813,   -1,   -1,
-        -1,   -1,   -1,  814,   -1,  815,   -1,   -1,
-        -1,   -1,  816,   -1,   -1,  817,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  818,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  819,
-        -1,   -1,   -1,   -1,  820,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  821,   -1,  822,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  823,
-        -1,   -1,   -1,   -1,  824,   -1,  825,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       826,  827,  828,  829,  830,  831,  832,  833,
-       834,  835,  836,  837,  838,  839,  840,  841,
-       842,   -1,   -1,   -1,   -1,   -1,  843,   -1,
+        -1,   -1,   -1,   -1,  764,   -1,   -1,  765,
+        -1,  766,   -1,   -1,  767,  768,   -1,  769,
+        -1,   -1,  770,  771,  772,   -1,   -1,   -1,
+       773,   -1,   -1,   -1,   -1,   -1,  774,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  775,   -1,
+       776,   -1,   -1,   -1,  777,  778,   -1,   -1,
+       779,   -1,   -1,  780,  781,  782,   -1,  783,
+        -1,  784,   -1,   -1,  785,   -1,  786,   -1,
+       787,   -1,   -1,   -1,  788,   -1,   -1,   -1,
+        -1,  789,   -1,   -1,   -1,  790,   -1,   -1,
+        -1,   -1,   -1,  791,   -1,   -1,  792,  793,
+        -1,   -1,   -1,   -1,   -1,  794,  795,   -1,
+       796,   -1,   -1,  797,  798,   -1,  799,  800,
+        -1,   -1,   -1,   -1,  801,   -1,   -1,   -1,
+        -1,  802,  803,   -1,  804,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  805,   -1,   -1,   -1,
+        -1,  806,  807,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  808,   -1,   -1,   -1,
+        -1,  809,   -1,   -1,   -1,  810,   -1,   -1,
+        -1,   -1,  811,   -1,   -1,  812,  813,   -1,
+        -1,   -1,   -1,  814,   -1,   -1,   -1,   -1,
+        -1,   -1,  815,  816,   -1,   -1,  817,   -1,
+        -1,   -1,  818,   -1,   -1,   -1,   -1,   -1,
+        -1,  819,  820,   -1,  821,   -1,   -1,   -1,
+        -1,   -1,  822,  823,  824,   -1,   -1,   -1,
+       825,   -1,  826,   -1,   -1,   -1,   -1,  827,
+        -1,   -1,   -1,   -1,  828,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  844,   -1,   -1,   -1,   -1,
+        -1,  829,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  830,   -1,   -1,   -1,   -1,
+        -1,  831,  832,   -1,   -1,   -1,   -1,   -1,
+        -1,  833,   -1,   -1,  834,   -1,   -1,   -1,
+        -1,   -1,   -1,  835,   -1,   -1,   -1,  836,
+        -1,   -1,   -1,   -1,   -1,  837,   -1,   -1,
+        -1,  838,   -1,   -1,  839,  840,   -1,   -1,
+       841,   -1,  842,  843,   -1,   -1,  844,   -1,
+        -1,  845,  846,   -1,  847,   -1,   -1,   -1,
+       848,   -1,   -1,   -1,   -1,  849,   -1,   -1,
+        -1,   -1,   -1,   -1,  850,  851,   -1,  852,
+        -1,   -1,  853,  854,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  855,
+        -1,   -1,   -1,   -1,   -1,  856,  857,  858,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       845,   -1,   -1,  846,   -1,   -1,   -1,  847,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  848,
-        -1,   -1,   -1,  849,  850,   -1,   -1,  851,
-        -1,  852,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  859,   -1,   -1,   -1,  860,
+        -1,  861,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  862,   -1,   -1,   -1,
+       863,  864,   -1,   -1,   -1,   -1,   -1,   -1,
+       865,   -1,   -1,  866,   -1,  867,   -1,  868,
+        -1,   -1,   -1,  869,   -1,   -1,   -1,  870,
+        -1,   -1,   -1,  871,  872,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  853,   -1,   -1,   -1,   -1,  854,   -1,
+        -1,  873,   -1,   -1,   -1,   -1,  874,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  855,   -1,   -1,
+        -1,   -1,   -1,  875,   -1,   -1,   -1,   -1,
+       876,  877,   -1,  878,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  879,
+        -1,   -1,   -1,  880,  881,   -1,   -1,   -1,
+       882,  883,   -1,  884,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  856,   -1,
-        -1,   -1,   -1,  857,   -1,   -1,   -1,   -1,
-       858,   -1,   -1,   -1,  859,   -1,   -1,   -1,
-       860,   -1,   -1,   -1,   -1,   -1,  861,   -1,
-        -1,  862,   -1,  863,   -1,   -1,   -1,   -1,
-        -1,  864,   -1,   -1,   -1,   -1,   -1,  865,
+        -1,   -1,  885,   -1,   -1,   -1,  886,  887,
+       888,  889,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  890,   -1,
+        -1,  891,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  892,   -1,   -1,  893,   -1,   -1,   -1,
+        -1,   -1,   -1,  894,   -1,  895,   -1,   -1,
+        -1,   -1,  896,  897,   -1,   -1,   -1,   -1,
+        -1,  898,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  899,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       900,   -1,   -1,   -1,   -1,   -1,  901,   -1,
+        -1,   -1,   -1,   -1,   -1,  902,   -1,   -1,
+       903,  904,  905,  906,   -1,  907,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  866,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  908,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       867,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  868,   -1,
-        -1,   -1,  869,   -1,   -1,   -1,   -1,   -1,
-       870,   -1,   -1,   -1,  871,   -1,  872,   -1,
-       873,  874,   -1,   -1,  875,   -1,  876,   -1,
-        -1,   -1,   -1,  877,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  878,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  879,   -1,
-       880,  881,  882,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  883,   -1,   -1,   -1,  884,   -1,
-        -1,   -1,  885,  886,   -1,   -1,   -1,  887,
-        -1,   -1,  888,  889,  890,   -1,  891,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  892,   -1,
-        -1,   -1,  893,   -1,  894,   -1,   -1,  895,
-        -1,   -1,  896,  897,  898,   -1,   -1,   -1,
-       899,   -1,  900,   -1,   -1,   -1,  901,  902,
-        -1,   -1,   -1,   -1,   -1,  903,   -1,   -1,
-        -1,   -1,   -1,   -1,  904,  905,   -1,   -1,
+        -1,  909,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  910,   -1,
+        -1,   -1,  911,   -1,  912,   -1,  913,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  914,   -1,   -1,   -1,   -1,
+       915,  916,  917,   -1,   -1,   -1,   -1,  918,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  919,   -1,   -1,  920,   -1,   -1,
+        -1,   -1,   -1,   -1,  921,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  922,   -1,   -1,  923,
+        -1,   -1,   -1,   -1,  924,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  925,   -1,   -1,   -1,   -1,   -1,  926,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  927,  928,
+        -1,   -1,  929,  930,  931,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  932,   -1,   -1,  933,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,  934,   -1,   -1,   -1,
+        -1,   -1,  935,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  936,
+        -1,  937,  938,   -1,   -1,  939,   -1,  940,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  906,   -1,   -1,   -1,   -1,
+        -1,  941,   -1,  942,  943,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,  944,  945,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,  946,   -1,
+        -1,   -1,  947,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  948,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  949,   -1,   -1,   -1,   -1,   -1,
+        -1,  950,  951,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  952,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       907,   -1,   -1,   -1,  908,   -1,   -1,   -1,
-       909,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  910,   -1,   -1,
-        -1,  911,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  912,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       913,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,  914,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  915,   -1,
-        -1,  916,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  917,   -1,   -1,
-        -1,  918,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  919,   -1,   -1,   -1,   -1,
-       920,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  921,
-        -1,  922,   -1,   -1,  923,   -1,  924,   -1,
-        -1,  925,  926,   -1,  927,   -1,   -1,   -1,
-        -1,   -1,  928,   -1,  929,  930,  931,   -1,
-        -1,   -1,   -1,   -1,   -1,  932,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  933,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  934,   -1,   -1,
-        -1,   -1,   -1,  935,   -1,   -1,   -1,  936,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  937,  938,   -1,   -1,  939,   -1,
-        -1,   -1,   -1,   -1,  940,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  941,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,  942,  943,   -1,  944,   -1,   -1,
-        -1,   -1,   -1,   -1,  945,  946,   -1,   -1,
-        -1,  947,   -1,   -1,   -1,   -1,   -1,   -1,
-       948,   -1,   -1,  949,   -1,   -1,   -1,   -1,
-       950,   -1,   -1,   -1,  951,   -1,   -1,  952,
         -1,   -1,   -1,   -1,   -1,   -1,  953,   -1,
-        -1,   -1,   -1,   -1,  954,   -1,   -1,   -1,
-        -1,   -1,  955,   -1,  956,   -1,   -1,   -1,
-        -1,   -1,   -1,  957,   -1,   -1,   -1,   -1,
-        -1,  958,  959,   -1,  960,  961,   -1,   -1,
-        -1,   -1,   -1,  962,   -1,  963,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  964,   -1,
-        -1,   -1,   -1,   -1,   -1,  965,   -1,   -1,
-        -1,   -1,   -1,   -1,  966,   -1,  967,   -1,
-        -1,  968,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  969,   -1,   -1,
-        -1,   -1,   -1,  970,   -1,  971,   -1,   -1,
-        -1,  972,   -1,   -1,   -1,   -1,   -1,   -1,
-       973,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  954,   -1,   -1,   -1,   -1,   -1,   -1,
+       955,  956,   -1,   -1,   -1,   -1,   -1,   -1,
+       957,   -1,   -1,   -1,   -1,   -1,  958,  959,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       974,   -1,  975,   -1,   -1,   -1,   -1,   -1,
-        -1,  976,   -1,   -1,  977,   -1,   -1,   -1,
+        -1,  960,  961,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  978,
-        -1,   -1,  979,   -1,   -1,   -1,  980,   -1,
-       981,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-       982,   -1,   -1,   -1,  983,   -1,   -1,   -1,
-        -1,  984,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  985,
-       986,   -1,   -1,   -1,   -1,   -1,  987,   -1,
-        -1,   -1,   -1,  988,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,  989,   -1,   -1,   -1,  990,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  991,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,  992,
-        -1,   -1,  993,   -1,   -1,   -1,   -1,   -1,
+       962,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,  963,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,  994,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,  995,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,  996,   -1,   -1,   -1,   -1,   -1,  997,
-        -1,   -1,   -1,  998,   -1,   -1,  999,   -1,
-        -1,   -1, 1000, 1001,   -1,   -1, 1002, 1003,
+        -1,   -1,  964,   -1,  965,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1004,   -1,   -1,   -1,   -1, 1005,   -1,
-        -1,   -1,   -1, 1006,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1007,   -1,   -1,   -1,
-        -1, 1008,   -1,   -1,   -1,   -1,   -1, 1009,
-        -1,   -1,   -1,   -1,   -1,   -1, 1010,   -1,
-        -1,   -1,   -1, 1011,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1012,
-        -1,   -1,   -1,   -1, 1013,   -1,   -1,   -1,
+       966,   -1,   -1,   -1,   -1,   -1,  967,   -1,
+        -1,  968,  969,   -1,   -1,  970,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1014, 1015,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1016,   -1,   -1, 1017,
-        -1, 1018,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1019,   -1,   -1,   -1,
-        -1, 1020,   -1,   -1, 1021,   -1,   -1,   -1,
-        -1, 1022,   -1, 1023, 1024,   -1,   -1, 1025,
-        -1,   -1,   -1,   -1, 1026,   -1,   -1,   -1,
-        -1, 1027,   -1,   -1, 1028,   -1,   -1, 1029,
-        -1, 1030,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1031, 1032,   -1,   -1,   -1,   -1, 1033,
-        -1, 1034,   -1,   -1,   -1, 1035, 1036,   -1,
+        -1,   -1,   -1,   -1,  971,   -1,   -1,   -1,
+        -1,  972,   -1,   -1,  973,   -1,   -1,  974,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1037,   -1,   -1,   -1,   -1, 1038,
-      1039,   -1,   -1,   -1,   -1,   -1,   -1, 1040,
-        -1,   -1,   -1,   -1,   -1,   -1, 1041, 1042,
+        -1,   -1,  975,   -1,   -1,   -1,   -1,   -1,
+       976,   -1,  977,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1043,
-        -1,   -1,   -1, 1044, 1045,   -1,   -1,   -1,
-        -1,   -1,   -1, 1046, 1047,   -1,   -1, 1048,
-        -1,   -1,   -1,   -1, 1049,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1050,   -1,   -1,
-      1051,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       978,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1052, 1053,   -1, 1054,   -1, 1055,   -1,
+        -1,   -1,   -1,  979,   -1,  980,   -1,   -1,
+        -1,   -1,  981,   -1,   -1,   -1,  982,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1056,   -1,   -1,   -1,   -1, 1057,   -1,
-        -1,   -1,   -1,   -1,   -1, 1058,   -1,   -1,
-        -1,   -1,   -1,   -1, 1059,   -1,   -1,   -1,
-      1060,   -1, 1061, 1062, 1063,   -1,   -1,   -1,
-        -1,   -1, 1064,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1065, 1066,   -1,   -1,   -1,   -1,
-        -1, 1067,   -1,   -1,   -1,   -1, 1068,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1069,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+       983,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1070,   -1,
-        -1,   -1,   -1,   -1,   -1, 1071,   -1,   -1,
-        -1,   -1,   -1, 1072,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1073, 1074,   -1,   -1,
-        -1, 1075,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,  984,   -1,   -1,   -1,   -1,  985,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1076,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1077,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1078, 1079, 1080,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1081,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1082,   -1, 1083,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1084,   -1,   -1,   -1, 1085,
-      1086, 1087,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1088, 1089,   -1,   -1,   -1, 1090,
-      1091,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1092, 1093,   -1,   -1,   -1,
-        -1,   -1,   -1, 1094, 1095, 1096,   -1,   -1,
-        -1, 1097,   -1,   -1,   -1, 1098, 1099,   -1,
-        -1,   -1,   -1,   -1,   -1, 1100,   -1, 1101,
-        -1,   -1, 1102, 1103,   -1, 1104,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1105,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1106,   -1,
-        -1,   -1,   -1, 1107,   -1, 1108,   -1, 1109,
-        -1,   -1,   -1,   -1,   -1, 1110,   -1, 1111,
-        -1,   -1,   -1, 1112,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1113,
-      1114,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1115,   -1,   -1, 1116,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1117,   -1,
-        -1, 1118,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1119,
-        -1, 1120,   -1,   -1,   -1,   -1,   -1, 1121,
-        -1,   -1,   -1,   -1, 1122, 1123,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1124, 1125,   -1,   -1,   -1,   -1,   -1,   -1,
-      1126,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1127,   -1,   -1,
-        -1,   -1, 1128,   -1,   -1,   -1,   -1, 1129,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  986,
+        -1,   -1,   -1,  987,  988,   -1,   -1,   -1,
+       989,  990,   -1,  991,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,  992,   -1,  993,   -1,  994,
+        -1,   -1,   -1,  995,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1130,   -1,   -1, 1131,   -1, 1132,
+        -1,  996,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1133,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1134,   -1,
-        -1, 1135,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1136,   -1,   -1,
-        -1,   -1,   -1,   -1, 1137,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1138,   -1,   -1,   -1,
+        -1,   -1,  997,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,  998,
+       999, 1000,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1001, 1002,   -1, 1003,   -1, 1004, 1005,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1139,   -1,   -1,
-      1140,   -1,   -1,   -1,   -1, 1141,   -1,   -1,
-        -1,   -1, 1142,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1143,   -1,   -1,
-        -1, 1144,   -1, 1145,   -1,   -1,   -1,   -1,
-      1146,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1147,   -1, 1148,   -1,
-        -1,   -1,   -1,   -1, 1149,   -1,   -1, 1150,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1151,
-        -1,   -1,   -1, 1152,   -1,   -1, 1153,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1006,
+      1007,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1008,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1009,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1010,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1011,   -1, 1012,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1013,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1014,   -1,
+        -1,   -1,   -1, 1015, 1016, 1017, 1018, 1019,
+      1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027,
+      1028, 1029, 1030, 1031, 1032,   -1, 1033,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1034, 1035,
+        -1,   -1, 1036,   -1,   -1,   -1,   -1, 1037,
+      1038, 1039,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1040, 1041,   -1, 1042,   -1,   -1,   -1,
+        -1,   -1,   -1, 1043,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1044,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1045,   -1,
+      1046, 1047,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1048,   -1,   -1,   -1,   -1,
+      1049,   -1,   -1,   -1,   -1,   -1, 1050,   -1,
+        -1,   -1,   -1,   -1,   -1, 1051,   -1,   -1,
+      1052,   -1,   -1,   -1,   -1,   -1,   -1, 1053,
+        -1,   -1, 1054, 1055,   -1,   -1, 1056,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1057,
+        -1,   -1, 1058,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1059,   -1,   -1,   -1,   -1,   -1,
+        -1, 1060,   -1,   -1, 1061,   -1, 1062, 1063,
+        -1,   -1,   -1,   -1,   -1,   -1, 1064,   -1,
+        -1,   -1, 1065,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1066,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1067,
+        -1,   -1, 1068,   -1,   -1,   -1,   -1,   -1,
+      1069,   -1,   -1, 1070,   -1,   -1,   -1, 1071,
+        -1, 1072, 1073,   -1,   -1,   -1, 1074,   -1,
+      1075,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1076, 1077,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1078,   -1,   -1,   -1,   -1,   -1, 1079,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1080,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1081,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1082,
+        -1,   -1,   -1, 1083,   -1,   -1,   -1,   -1,
+        -1,   -1, 1084, 1085, 1086,   -1,   -1,   -1,
+        -1,   -1,   -1, 1087, 1088,   -1,   -1,   -1,
+      1089,   -1,   -1,   -1,   -1, 1090,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1091,   -1,   -1,
+        -1, 1092,   -1,   -1,   -1,   -1, 1093,   -1,
+      1094, 1095,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1096,   -1,
+        -1,   -1,   -1,   -1, 1097,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1098, 1099,   -1,   -1,   -1,   -1, 1100,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1101,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1102,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1103,   -1,
+      1104,   -1,   -1,   -1,   -1, 1105,   -1, 1106,
+      1107, 1108,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1109,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1110,   -1,   -1,
+        -1, 1111,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1112, 1113,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1114, 1115, 1116,
+        -1, 1117, 1118,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1119,   -1,   -1,   -1,   -1,   -1, 1120, 1121,
+      1122,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1123,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1124,   -1,   -1,   -1, 1125,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1126,   -1, 1127, 1128, 1129,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1130,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1131,   -1,
+      1132,   -1, 1133,   -1,   -1, 1134,   -1, 1135,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1136,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1137,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1138,
+      1139, 1140, 1141,   -1,   -1,   -1, 1142,   -1,
+      1143,   -1,   -1,   -1,   -1,   -1,   -1, 1144,
+        -1,   -1,   -1,   -1, 1145, 1146, 1147,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1148, 1149,   -1,   -1,   -1,
+      1150,   -1,   -1, 1151,   -1,   -1, 1152, 1153,
         -1,   -1,   -1, 1154,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1155,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1155,   -1, 1156,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1156,   -1, 1157,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1158, 1159,
+        -1,   -1, 1160,   -1,   -1,   -1,   -1, 1161,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1157,   -1, 1158,   -1,   -1,   -1,   -1,
-        -1,   -1, 1159,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1160,   -1,   -1,
-        -1,   -1, 1161,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1162,   -1,   -1,   -1,   -1,   -1, 1163,
-      1164,   -1,   -1,   -1, 1165,   -1, 1166, 1167,
-        -1,   -1,   -1,   -1,   -1, 1168,   -1, 1169,
-        -1,   -1,   -1,   -1,   -1, 1170,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1162, 1163,
+        -1, 1164,   -1, 1165,   -1,   -1,   -1,   -1,
+        -1, 1166,   -1,   -1,   -1,   -1,   -1, 1167,
+        -1,   -1,   -1,   -1,   -1,   -1, 1168,   -1,
+      1169,   -1,   -1,   -1,   -1,   -1,   -1, 1170,
         -1,   -1,   -1,   -1,   -1, 1171,   -1,   -1,
-        -1,   -1, 1172,   -1, 1173, 1174,   -1,   -1,
-        -1,   -1, 1175,   -1,   -1,   -1,   -1, 1176,
-        -1, 1177,   -1,   -1, 1178,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1179,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1180, 1181,   -1,
-      1182,   -1,   -1, 1183,   -1,   -1, 1184,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1185,   -1,   -1, 1186,
-      1187,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1172,   -1,   -1,
+        -1,   -1,   -1, 1173,   -1,   -1, 1174,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1175,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1188,   -1,   -1,   -1,   -1,   -1,
-        -1, 1189,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1176, 1177,
+        -1,   -1,   -1, 1178,   -1, 1179,   -1, 1180,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1190,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1191, 1192,
-        -1,   -1,   -1, 1193,   -1,   -1, 1194,   -1,
-        -1, 1195,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1196, 1197,   -1,   -1,   -1, 1198,   -1,
+        -1,   -1,   -1,   -1,   -1, 1181,   -1,   -1,
+        -1, 1182,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1183,   -1,   -1,
+      1184,   -1,   -1,   -1, 1185,   -1, 1186,   -1,
+        -1, 1187,   -1,   -1,   -1, 1188, 1189,   -1,
+      1190, 1191,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1199, 1200,   -1, 1201,   -1,   -1,
-        -1,   -1, 1202,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1192,   -1,   -1,
+        -1, 1193,   -1,   -1,   -1,   -1,   -1,   -1,
+      1194,   -1,   -1,   -1, 1195,   -1,   -1,   -1,
+        -1,   -1,   -1, 1196,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1197,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1203,   -1,
-        -1,   -1,   -1,   -1,   -1, 1204,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1205,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1206,   -1,   -1,
+        -1,   -1, 1198,   -1, 1199,   -1,   -1,   -1,
+        -1,   -1, 1200,   -1,   -1,   -1,   -1,   -1,
+      1201,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1202,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1203,
+        -1,   -1, 1204,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1205, 1206,   -1, 1207,   -1,
+        -1,   -1,   -1, 1208, 1209, 1210,   -1,   -1,
+        -1,   -1, 1211,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1212, 1213,   -1,   -1,   -1,
+        -1, 1214,   -1,   -1,   -1, 1215,   -1, 1216,
+        -1,   -1,   -1,   -1, 1217,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1218,   -1, 1219,   -1,
+        -1,   -1,   -1,   -1, 1220,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1221,   -1, 1222,   -1,
+      1223,   -1,   -1, 1224,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1225,
+        -1,   -1,   -1,   -1,   -1, 1226,   -1,   -1,
+        -1,   -1, 1227, 1228,   -1,   -1,   -1, 1229,
+        -1,   -1,   -1, 1230,   -1,   -1,   -1, 1231,
+        -1, 1232,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1233,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1234,   -1, 1235,   -1,
+        -1, 1236,   -1,   -1,   -1,   -1,   -1,   -1,
+      1237,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1207,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1208,   -1,
-        -1,   -1,   -1, 1209,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1238,
+        -1, 1239,   -1, 1240,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1241,   -1,   -1,   -1,   -1,
+        -1, 1242,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1243,   -1,
+        -1,   -1,   -1, 1244,   -1, 1245,   -1,   -1,
+      1246,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1210,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1247, 1248,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1211,   -1,   -1,   -1,   -1,   -1, 1212,
-        -1,   -1,   -1, 1213,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1214,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1215,   -1,   -1, 1216,
-        -1,   -1,   -1, 1217, 1218, 1219,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1220,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1221,   -1,   -1,   -1,
-        -1,   -1,   -1, 1222,   -1,   -1,   -1,   -1,
-        -1,   -1, 1223, 1224,   -1,   -1,   -1,   -1,
-      1225,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1226,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1227,
-        -1, 1228,   -1,   -1,   -1,   -1,   -1,   -1,
-      1229,   -1, 1230,   -1,   -1,   -1,   -1,   -1,
-      1231,   -1,   -1,   -1,   -1, 1232,   -1,   -1,
-      1233,   -1,   -1,   -1,   -1,   -1, 1234,   -1,
-        -1, 1235,   -1,   -1, 1236,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1237,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1238,   -1,   -1,   -1,   -1,
-      1239,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1240,   -1,   -1,   -1,   -1, 1241,
-        -1,   -1, 1242, 1243,   -1,   -1,   -1, 1244,
-      1245,   -1,   -1,   -1,   -1, 1246,   -1,   -1,
-      1247,   -1,   -1,   -1, 1248, 1249,   -1, 1250,
-        -1,   -1,   -1,   -1,   -1, 1251,   -1, 1252,
+        -1,   -1, 1249,   -1,   -1,   -1,   -1, 1250,
+        -1,   -1,   -1,   -1, 1251,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1252,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 1253,
-      1254, 1255,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1256,   -1,   -1,   -1,   -1,
-        -1,   -1, 1257,   -1, 1258,   -1,   -1,   -1,
+        -1, 1254,   -1,   -1,   -1, 1255,   -1,   -1,
+      1256,   -1, 1257,   -1,   -1,   -1,   -1,   -1,
+      1258,   -1,   -1,   -1,   -1,   -1, 1259,   -1,
+        -1,   -1,   -1,   -1,   -1, 1260,   -1,   -1,
+        -1,   -1,   -1, 1261,   -1,   -1, 1262,   -1,
+      1263,   -1,   -1,   -1,   -1, 1264,   -1,   -1,
+        -1, 1265,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1259, 1260, 1261,   -1,   -1,   -1, 1262,
-      1263,   -1, 1264,   -1,   -1, 1265,   -1,   -1,
+        -1, 1266,   -1,   -1, 1267,   -1,   -1,   -1,
+        -1,   -1, 1268,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1269,   -1,   -1,   -1,
+      1270,   -1, 1271,   -1,   -1,   -1, 1272,   -1,
+        -1, 1273, 1274,   -1, 1275,   -1,   -1,   -1,
+      1276,   -1,   -1, 1277,   -1, 1278,   -1, 1279,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1266,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1267, 1268, 1269, 1270,
-        -1, 1271,   -1,   -1,   -1, 1272,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1273,   -1,   -1,   -1,
-      1274,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1280, 1281,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1282,   -1,
+        -1,   -1,   -1,   -1, 1283,   -1, 1284,   -1,
+        -1,   -1, 1285, 1286,   -1,   -1,   -1,   -1,
+        -1,   -1, 1287,   -1,   -1,   -1,   -1, 1288,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1289,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1275,   -1,   -1,   -1,
-        -1, 1276,   -1,   -1,   -1,   -1, 1277,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1278,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1279,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1280,
-        -1,   -1,   -1, 1281,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1282,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1283,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1284,   -1, 1285,   -1, 1286,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1287,
+        -1,   -1,   -1, 1290,   -1,   -1,   -1,   -1,
+        -1, 1291,   -1,   -1,   -1,   -1, 1292,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1293,   -1,
+      1294,   -1, 1295,   -1, 1296,   -1, 1297,   -1,
+        -1,   -1, 1298,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1299, 1300,   -1,
+        -1, 1301,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1302,   -1,   -1,
+      1303,   -1,   -1,   -1,   -1, 1304, 1305,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1288,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1289,   -1,   -1,   -1,   -1,
-      1290,   -1,   -1,   -1, 1291,   -1,   -1,   -1,
-      1292, 1293,   -1,   -1,   -1,   -1, 1294,   -1,
-        -1, 1295,   -1, 1296,   -1,   -1,   -1,   -1,
-      1297,   -1,   -1, 1298,   -1, 1299,   -1,   -1,
-        -1, 1300,   -1,   -1,   -1, 1301,   -1,   -1,
-        -1,   -1,   -1, 1302,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1303,   -1,   -1,   -1,
-        -1,   -1,   -1, 1304,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1305,   -1,   -1,   -1,   -1,
+        -1, 1306,   -1,   -1,   -1, 1307,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1308,   -1,   -1,
+        -1,   -1,   -1,   -1, 1309, 1310,   -1, 1311,
+        -1,   -1,   -1,   -1, 1312,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1313,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1306,
+        -1,   -1, 1314,   -1,   -1, 1315,   -1,   -1,
+      1316,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1317, 1318,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1319,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1320,   -1,   -1,   -1,
+        -1,   -1, 1321,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1307,   -1,   -1,   -1, 1308,   -1,   -1, 1309,
-        -1, 1310,   -1, 1311,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1312,   -1,   -1,
-        -1,   -1,   -1, 1313,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1314,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1315,   -1,   -1,   -1,   -1,
-        -1, 1316,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1317,   -1,   -1,   -1,   -1, 1318,
+      1322,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1323,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1324,   -1, 1325,   -1,
+      1326,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1327,   -1,   -1,   -1,
+        -1, 1328,   -1,   -1,   -1,   -1,   -1,   -1,
+      1329,   -1,   -1,   -1,   -1, 1330,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1331,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1332,   -1,
+      1333,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1319,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1320,   -1,
-        -1, 1321,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1322, 1323, 1324,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1325,   -1,   -1,   -1,   -1, 1326,   -1,
-        -1,   -1,   -1,   -1,   -1, 1327,   -1, 1328,
-        -1,   -1, 1329,   -1, 1330, 1331, 1332,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1333,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1334,
-        -1,   -1, 1335,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1334,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1335,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1, 1336,   -1,   -1,   -1,   -1,   -1,
-      1337,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1338,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1339,   -1,   -1,   -1,   -1,
-        -1,   -1, 1340,   -1,   -1,   -1, 1341,   -1,
-        -1,   -1, 1342,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1343,   -1,
+        -1,   -1,   -1,   -1, 1337,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1338,   -1,   -1,
+        -1,   -1, 1339,   -1,   -1, 1340,   -1,   -1,
+      1341,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1342,   -1,   -1,
+        -1,   -1,   -1,   -1, 1343,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 1344,
-      1345,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1346,   -1,   -1,   -1,   -1,   -1, 1347,   -1,
+        -1,   -1,   -1, 1345, 1346,   -1,   -1,   -1,
+        -1, 1347,   -1,   -1,   -1, 1348,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1348,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1349,   -1,   -1,   -1,
-        -1, 1350,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1349, 1350,   -1,   -1,   -1,   -1,   -1,
+        -1, 1351,   -1,   -1,   -1, 1352, 1353,   -1,
+        -1, 1354,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1355,   -1, 1356,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1351,   -1,   -1, 1352,   -1,   -1, 1353,
-        -1,   -1,   -1,   -1, 1354,   -1,   -1, 1355,
-        -1,   -1,   -1,   -1,   -1, 1356,   -1,   -1,
-      1357, 1358,   -1,   -1,   -1,   -1,   -1,   -1,
-      1359, 1360, 1361,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1357,   -1,   -1,   -1,   -1, 1358,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1359,   -1,   -1,
+        -1,   -1,   -1,   -1, 1360,   -1, 1361,   -1,
+        -1,   -1, 1362,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1363,   -1,   -1,   -1,
+        -1,   -1,   -1, 1364,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1365,   -1, 1366,   -1,   -1,   -1,   -1,
+      1367,   -1,   -1,   -1, 1368, 1369,   -1,   -1,
+      1370,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1371,   -1,   -1,   -1,   -1,   -1,
+      1372,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1373,   -1,   -1,   -1,   -1, 1374,
+        -1,   -1, 1375, 1376,   -1,   -1,   -1, 1377,
+        -1,   -1, 1378, 1379,   -1,   -1, 1380,   -1,
+        -1,   -1,   -1,   -1, 1381,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1382,   -1,
+        -1, 1383,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1384,   -1,   -1, 1385,   -1,
+        -1,   -1,   -1, 1386,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1387,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1362,   -1,   -1,   -1,   -1,   -1, 1363,   -1,
-        -1,   -1, 1364,   -1,   -1,   -1,   -1,   -1,
-        -1, 1365,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1366,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1367,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1368,   -1,
-      1369,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1370,   -1,   -1,   -1,
-      1371,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1372,   -1,   -1,   -1,   -1,   -1, 1373,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1374,   -1,
-        -1, 1375,   -1,   -1,   -1,   -1,   -1,   -1,
-      1376, 1377,   -1,   -1, 1378,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1379,   -1, 1380,   -1,
-        -1, 1381,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1382,   -1,   -1,   -1, 1383, 1384,
-        -1,   -1, 1385, 1386,   -1,   -1,   -1, 1387,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1388,   -1,   -1,   -1,   -1,   -1,
-      1389,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1390,   -1, 1391,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1392,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1393,   -1,   -1, 1394, 1395,   -1,
-        -1,   -1,   -1,   -1,   -1, 1396,   -1,   -1,
-      1397,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1398,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1399,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1400,
+        -1,   -1, 1388, 1389,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1390,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1391,   -1, 1392,
+        -1,   -1, 1393,   -1,   -1,   -1,   -1, 1394,
+        -1,   -1,   -1,   -1, 1395,   -1,   -1,   -1,
+        -1,   -1,   -1, 1396,   -1, 1397,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1398,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1401,
-        -1,   -1,   -1,   -1,   -1, 1402, 1403, 1404,
-        -1, 1405,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1406,   -1,   -1,   -1,   -1,
+        -1,   -1, 1399,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1400,   -1,   -1, 1401,   -1,
+        -1,   -1,   -1, 1402,   -1, 1403,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1407, 1408,
-      1409,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1410,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1411,
-        -1,   -1,   -1,   -1,   -1,   -1, 1412,   -1,
-        -1,   -1,   -1,   -1,   -1, 1413,   -1,   -1,
+        -1,   -1,   -1, 1404,   -1,   -1,   -1,   -1,
+        -1,   -1, 1405, 1406,   -1,   -1,   -1, 1407,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1414,   -1,   -1,   -1,   -1, 1415, 1416,
-        -1,   -1,   -1,   -1, 1417,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1408,   -1,   -1,
+        -1,   -1,   -1, 1409,   -1,   -1,   -1, 1410,
+        -1,   -1, 1411,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1412, 1413,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1414,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1418,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1419,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1415,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1416,   -1,   -1,   -1,   -1, 1417,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1418,   -1,   -1,   -1,   -1, 1419,
         -1,   -1,   -1,   -1, 1420,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1421, 1422,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1423,   -1, 1424, 1425,   -1,   -1,   -1, 1426,
-        -1,   -1,   -1, 1427,   -1,   -1,   -1,   -1,
-      1428,   -1,   -1,   -1,   -1,   -1, 1429,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1430, 1431, 1432,   -1, 1433,   -1,   -1,   -1,
-      1434,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1435,   -1, 1436,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1437, 1438,   -1,   -1,   -1, 1439,   -1,   -1,
-        -1,   -1,   -1, 1440, 1441,   -1,   -1,   -1,
-        -1,   -1, 1442,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1443,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1444,   -1,   -1,   -1,   -1,
-        -1,   -1, 1445,   -1,   -1, 1446, 1447,   -1,
-        -1,   -1, 1448,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1421,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1449,
+        -1,   -1,   -1,   -1, 1422, 1423, 1424,   -1,
+        -1,   -1,   -1,   -1,   -1, 1425,   -1,   -1,
+        -1,   -1,   -1,   -1, 1426,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1427,   -1,   -1,   -1,
+        -1, 1428,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1429,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1450,   -1, 1451,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1452,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1430,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1431,   -1, 1432,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1433, 1434,   -1,   -1,
+        -1, 1435,   -1,   -1,   -1,   -1, 1436,   -1,
+        -1, 1437,   -1,   -1,   -1, 1438,   -1,   -1,
+        -1,   -1,   -1, 1439,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1440,   -1, 1441,   -1,
+        -1, 1442,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1443,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1444,   -1,   -1, 1445,   -1,   -1,
+      1446,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1447,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1448,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1449,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1450,   -1,
+        -1,   -1, 1451,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1452,   -1,   -1,
         -1,   -1,   -1,   -1, 1453,   -1,   -1,   -1,
-        -1, 1454,   -1,   -1, 1455,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1456,   -1,   -1,   -1,   -1, 1457,
-      1458,   -1,   -1,   -1,   -1,   -1, 1459,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1454,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1455,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1460, 1461,   -1,   -1, 1462,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1463, 1464,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1465, 1466, 1467,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1468,   -1,   -1,   -1,   -1, 1469,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1456,   -1,   -1,   -1,
+        -1,   -1, 1457,   -1,   -1,   -1,   -1,   -1,
+        -1, 1458,   -1, 1459,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1460,   -1,   -1,   -1,
+        -1, 1461,   -1,   -1,   -1, 1462,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1463,   -1,   -1,   -1,   -1,
+      1464,   -1,   -1,   -1,   -1,   -1,   -1, 1465,
+        -1,   -1,   -1,   -1,   -1, 1466,   -1,   -1,
+        -1, 1467,   -1, 1468,   -1,   -1, 1469,   -1,
         -1, 1470,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1471,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1472,   -1,
+        -1,   -1,   -1,   -1,   -1, 1473,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1474,   -1,   -1,
+      1475, 1476,   -1,   -1,   -1, 1477,   -1,   -1,
+        -1, 1478,   -1,   -1,   -1,   -1,   -1,   -1,
+      1479, 1480, 1481,   -1,   -1,   -1,   -1,   -1,
+      1482, 1483,   -1, 1484,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1485,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1486,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1487,   -1,
+      1488,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1489, 1490,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1471,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1491,   -1, 1492,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1472,   -1,   -1,   -1,   -1,   -1, 1473,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1474,   -1,   -1,   -1, 1475,   -1,   -1,   -1,
-        -1,   -1, 1476,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1477,   -1,   -1,   -1,
-        -1, 1478,   -1, 1479,   -1,   -1, 1480,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1481,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1482,   -1,   -1,
-      1483,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1484, 1485,   -1, 1486,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1487,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1488,   -1, 1489,
-        -1,   -1,   -1,   -1, 1490,   -1,   -1, 1491,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1492,   -1,   -1,   -1,   -1,
-      1493,   -1,   -1,   -1,   -1, 1494,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1495,   -1,   -1, 1496,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1497,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1498, 1499,   -1,   -1, 1500,
-        -1,   -1,   -1, 1501,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1502,   -1, 1503,
-      1504,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1505,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1493,   -1,
+        -1,   -1, 1494,   -1, 1495,   -1,   -1, 1496,
+        -1,   -1,   -1, 1497,   -1,   -1,   -1,   -1,
+        -1,   -1, 1498,   -1,   -1, 1499,   -1, 1500,
+        -1,   -1, 1501,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1502,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1503, 1504,   -1, 1505,
         -1,   -1,   -1,   -1,   -1,   -1, 1506,   -1,
-      1507,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1508,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1509,
-        -1,   -1,   -1,   -1,   -1,   -1, 1510,   -1,
-        -1, 1511,   -1,   -1, 1512,   -1,   -1,   -1,
-        -1, 1513,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1514,   -1, 1515,
-        -1,   -1,   -1,   -1, 1516,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1517, 1518,
-        -1,   -1, 1519,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1520,   -1,
+      1507,   -1,   -1,   -1,   -1, 1508, 1509,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1521,   -1,   -1,   -1,   -1,   -1,
-        -1, 1522,   -1,   -1, 1523,   -1,   -1,   -1,
-        -1, 1524,   -1, 1525,   -1,   -1,   -1,   -1,
-        -1, 1526,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1527,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1528,
-        -1, 1529, 1530,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1531,   -1,   -1,   -1,
+      1510,   -1,   -1,   -1, 1511,   -1,   -1,   -1,
+      1512,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1513,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1532,   -1,
+        -1,   -1, 1514,   -1,   -1,   -1,   -1, 1515,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1533,   -1,   -1,
-      1534,   -1,   -1,   -1,   -1,   -1,   -1, 1535,
-        -1, 1536,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1537,   -1,   -1,   -1,   -1,   -1,   -1,
-      1538,   -1,   -1,   -1,   -1,   -1,   -1, 1539,
-        -1,   -1,   -1,   -1, 1540, 1541,   -1,   -1,
-        -1, 1542,   -1,   -1,   -1,   -1, 1543,   -1,
+        -1,   -1,   -1,   -1,   -1, 1516,   -1,   -1,
+      1517,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1518,   -1,   -1,   -1, 1519,   -1,
+      1520,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1544,   -1,
+        -1, 1521, 1522,   -1,   -1,   -1,   -1,   -1,
+      1523,   -1,   -1, 1524,   -1,   -1,   -1, 1525,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1526,   -1,   -1,   -1,   -1, 1527,   -1, 1528,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1545,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1546,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1547,
-      1548,   -1,   -1,   -1, 1549,   -1,   -1,   -1,
-        -1, 1550,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1529,   -1,   -1,   -1, 1530,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1551,
+      1531, 1532,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1552,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1553, 1554,   -1,   -1, 1555,   -1, 1556,
-        -1,   -1, 1557,   -1,   -1,   -1,   -1, 1558,
-      1559,   -1,   -1, 1560,   -1,   -1, 1561,   -1,
-        -1,   -1,   -1,   -1, 1562,   -1,   -1,   -1,
-      1563,   -1, 1564,   -1,   -1,   -1,   -1,   -1,
+      1533,   -1,   -1,   -1,   -1, 1534,   -1,   -1,
+        -1, 1535,   -1, 1536, 1537,   -1, 1538,   -1,
+        -1,   -1, 1539,   -1,   -1, 1540,   -1,   -1,
+        -1,   -1,   -1,   -1, 1541, 1542,   -1,   -1,
+        -1, 1543,   -1,   -1,   -1,   -1,   -1, 1544,
+        -1,   -1, 1545,   -1, 1546,   -1,   -1, 1547,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1565,   -1,   -1,   -1, 1566,   -1,
-      1567,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1568,   -1,   -1,
+        -1, 1548,   -1,   -1, 1549,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1550,   -1,   -1, 1551,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1569,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1570,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1552,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1553, 1554,
+        -1,   -1,   -1,   -1,   -1,   -1, 1555, 1556,
+        -1,   -1,   -1, 1557, 1558,   -1,   -1,   -1,
+        -1,   -1,   -1, 1559,   -1,   -1,   -1,   -1,
+        -1,   -1, 1560,   -1, 1561, 1562,   -1,   -1,
+        -1,   -1, 1563,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1564, 1565,   -1, 1566,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1571, 1572, 1573,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1574,
+        -1,   -1,   -1, 1567, 1568, 1569,   -1,   -1,
+        -1, 1570, 1571, 1572,   -1,   -1,   -1,   -1,
+      1573,   -1,   -1,   -1,   -1,   -1,   -1, 1574,
+        -1,   -1, 1575,   -1, 1576,   -1,   -1, 1577,
+        -1,   -1, 1578,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1579,   -1, 1580,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1575,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1576,   -1,
-        -1, 1577,   -1,   -1,   -1,   -1, 1578,   -1,
-        -1,   -1,   -1, 1579,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1580,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1581,   -1,   -1,   -1, 1582,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1583, 1584, 1585,   -1, 1586,   -1,
+        -1, 1587,   -1, 1588,   -1, 1589,   -1, 1590,
+      1591,   -1, 1592,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1581,
-        -1,   -1,   -1,   -1,   -1, 1582,   -1,   -1,
-        -1,   -1, 1583,   -1,   -1,   -1,   -1, 1584,
+        -1, 1593,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1585,   -1,   -1,   -1,   -1, 1586,
-      1587, 1588,   -1,   -1, 1589,   -1,   -1,   -1,
-      1590,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1591,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1594,   -1,   -1, 1595,
+      1596, 1597,   -1,   -1,   -1,   -1,   -1, 1598,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1592,   -1,   -1,   -1,   -1,   -1,
-      1593, 1594,   -1,   -1,   -1,   -1, 1595,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1596, 1597,
-        -1,   -1,   -1, 1598,   -1,   -1,   -1,   -1,
-      1599,   -1, 1600,   -1,   -1,   -1,   -1,   -1,
+        -1, 1599,   -1,   -1, 1600,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1, 1601,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1602,   -1,   -1,
-        -1,   -1, 1603,   -1,   -1, 1604,   -1,   -1,
+        -1,   -1,   -1,   -1, 1602,   -1, 1603,   -1,
+        -1,   -1,   -1,   -1, 1604,   -1,   -1,   -1,
+      1605,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1606,   -1, 1607,   -1, 1608,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1609,
+        -1,   -1,   -1,   -1, 1610,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1611,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1605, 1606,   -1,   -1,   -1,   -1,   -1, 1607,
-        -1,   -1,   -1,   -1,   -1,   -1, 1608,   -1,
+        -1, 1612,   -1,   -1,   -1,   -1,   -1,   -1,
+      1613,   -1,   -1, 1614,   -1,   -1,   -1, 1615,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1609,   -1,   -1,   -1,   -1,   -1, 1610,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1611,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1612,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1613, 1614, 1615,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1616,   -1,
-        -1,   -1,   -1, 1617,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1618,   -1,   -1, 1619, 1620,   -1,   -1,
-        -1,   -1,   -1, 1621,   -1,   -1,   -1,   -1,
-        -1, 1622,   -1, 1623,   -1,   -1,   -1,   -1,
-        -1,   -1, 1624,   -1,   -1, 1625,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1626,   -1,   -1,   -1,   -1,   -1,
-        -1, 1627,   -1,   -1,   -1,   -1, 1628,   -1,
-        -1,   -1,   -1,   -1,   -1, 1629,   -1,   -1,
-        -1, 1630,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1631,   -1, 1632,   -1,   -1,   -1,   -1,
+        -1, 1616,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1617,   -1, 1618, 1619, 1620,
+      1621, 1622,   -1,   -1,   -1,   -1,   -1,   -1,
+      1623,   -1,   -1, 1624,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1633,   -1,   -1,   -1,   -1,
-        -1,   -1, 1634,   -1,   -1,   -1,   -1, 1635,
-        -1,   -1,   -1, 1636,   -1,   -1,   -1, 1637,
-        -1,   -1,   -1,   -1, 1638,   -1,   -1,   -1,
-      1639,   -1,   -1,   -1,   -1, 1640,   -1,   -1,
-        -1,   -1, 1641,   -1,   -1,   -1, 1642,   -1,
-        -1,   -1,   -1,   -1, 1643, 1644, 1645,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1646,   -1,
-        -1,   -1,   -1,   -1, 1647,   -1,   -1,   -1,
-      1648, 1649,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1650,   -1,   -1,
-        -1,   -1, 1651,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1652,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1653, 1654,   -1,   -1, 1655,   -1,   -1, 1656,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1657,
-        -1,   -1,   -1,   -1, 1658,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1659,   -1,   -1,   -1,   -1,   -1,
+      1625, 1626,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1627,   -1, 1628,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1629, 1630,
+        -1,   -1,   -1,   -1,   -1, 1631,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1660,   -1,   -1,   -1, 1661,   -1, 1662,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1663,
-      1664,   -1,   -1, 1665,   -1, 1666,   -1,   -1,
-      1667,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1668,   -1,   -1,   -1, 1669,   -1,
-      1670,   -1,   -1,   -1,   -1,   -1,   -1, 1671,
-      1672,   -1,   -1,   -1,   -1,   -1,   -1, 1673,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1632,   -1,
+        -1,   -1,   -1,   -1,   -1, 1633,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1634,   -1,   -1,   -1,   -1,   -1,   -1,
+      1635,   -1,   -1,   -1,   -1, 1636,   -1, 1637,
+        -1,   -1, 1638,   -1,   -1,   -1, 1639, 1640,
+        -1, 1641, 1642, 1643,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1644,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1645,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1646,   -1,   -1,   -1,   -1,   -1, 1647,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1648,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1649,   -1,   -1, 1650,   -1,   -1,   -1,   -1,
+        -1, 1651,   -1,   -1,   -1,   -1,   -1, 1652,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1653,
+        -1,   -1,   -1,   -1,   -1,   -1, 1654,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1655,   -1,
+        -1,   -1, 1656,   -1,   -1,   -1,   -1,   -1,
+      1657,   -1,   -1,   -1, 1658,   -1, 1659,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1660,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1661,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1662,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1663,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1674,   -1,   -1, 1675,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1676,   -1,
-        -1,   -1,   -1,   -1, 1677, 1678,   -1,   -1,
+        -1,   -1,   -1,   -1, 1664,   -1,   -1,   -1,
+        -1,   -1, 1665,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1666,   -1, 1667,
+      1668,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1669,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1670,
+        -1,   -1,   -1, 1671,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1679,
-        -1,   -1, 1680, 1681,   -1,   -1,   -1,   -1,
+        -1,   -1, 1672,   -1,   -1,   -1,   -1, 1673,
+        -1,   -1, 1674,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1675,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1676,   -1,   -1,   -1,   -1,   -1, 1677,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1682,   -1, 1683,   -1,   -1,   -1,   -1,
-        -1,   -1, 1684,   -1,   -1,   -1,   -1,   -1,
-      1685,   -1,   -1,   -1, 1686,   -1,   -1, 1687,
-        -1, 1688,   -1,   -1, 1689,   -1,   -1,   -1,
-      1690,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1691,   -1,
-        -1,   -1,   -1,   -1,   -1, 1692,   -1, 1693,
+        -1,   -1, 1678, 1679,   -1,   -1, 1680,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1694,   -1,   -1,   -1,   -1,   -1,
-        -1, 1695,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1696,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1697,
+        -1, 1681,   -1,   -1,   -1,   -1,   -1,   -1,
+      1682,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1698,
-        -1,   -1,   -1,   -1, 1699,   -1,   -1, 1700,
-        -1, 1701,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1702,   -1,   -1,
-        -1,   -1,   -1,   -1, 1703,   -1, 1704,   -1,
-      1705, 1706,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1707,   -1, 1708,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1709,   -1,
+        -1, 1683,   -1,   -1,   -1,   -1, 1684,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1685,   -1,
+        -1,   -1,   -1, 1686,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1687,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1688,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1689,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1710,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1711,
+        -1,   -1,   -1,   -1, 1690,   -1,   -1,   -1,
+        -1,   -1, 1691,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1712,   -1,
-        -1, 1713,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1714,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1692,
+      1693,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1694, 1695,   -1,   -1,   -1,   -1, 1696,   -1,
+        -1,   -1, 1697, 1698,   -1, 1699,   -1,   -1,
+      1700, 1701,   -1,   -1, 1702,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1715,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1716,
-        -1,   -1,   -1,   -1,   -1,   -1, 1717, 1718,
-        -1,   -1,   -1,   -1,   -1,   -1, 1719,   -1,
-        -1,   -1,   -1,   -1,   -1, 1720,   -1,   -1,
-        -1, 1721,   -1,   -1,   -1,   -1, 1722,   -1,
-        -1,   -1,   -1,   -1,   -1, 1723,   -1,   -1,
-      1724,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1703,   -1,   -1,   -1,   -1,   -1,
+      1704, 1705, 1706,   -1, 1707,   -1,   -1, 1708,
+        -1,   -1,   -1,   -1,   -1,   -1, 1709, 1710,
+        -1,   -1, 1711,   -1,   -1,   -1, 1712,   -1,
+      1713,   -1,   -1,   -1, 1714, 1715,   -1, 1716,
+        -1,   -1,   -1,   -1,   -1,   -1, 1717,   -1,
+        -1,   -1,   -1, 1718,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1719,   -1,   -1, 1720,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1721, 1722,   -1, 1723,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1724,
         -1,   -1,   -1,   -1,   -1, 1725,   -1,   -1,
-        -1,   -1,   -1,   -1, 1726,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1726,   -1, 1727,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1727,
-        -1,   -1, 1728, 1729,   -1,   -1,   -1,   -1,
-        -1,   -1, 1730, 1731, 1732,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1728,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1729,   -1,   -1,   -1,
+      1730,   -1, 1731,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1732,   -1,
         -1,   -1,   -1,   -1, 1733,   -1,   -1,   -1,
+        -1, 1734,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1734,   -1, 1735,   -1,   -1,   -1,
+        -1,   -1,   -1, 1735, 1736,   -1,   -1,   -1,
+        -1, 1737,   -1, 1738,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1739,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1736,   -1,   -1,   -1, 1737,
-      1738,   -1,   -1,   -1,   -1,   -1,   -1, 1739,
-        -1,   -1,   -1,   -1, 1740, 1741,   -1,   -1,
-        -1,   -1, 1742,   -1,   -1,   -1,   -1,   -1,
-        -1, 1743,   -1,   -1, 1744,   -1,   -1,   -1,
-      1745,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1746,   -1,   -1, 1747,
-        -1,   -1,   -1, 1748,   -1,   -1, 1749,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1750,   -1,
+        -1,   -1,   -1, 1740,   -1,   -1,   -1,   -1,
+        -1,   -1, 1741,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1742,   -1,   -1, 1743,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1744,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1751,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1752,
+        -1,   -1,   -1,   -1,   -1,   -1, 1745,   -1,
+      1746, 1747,   -1, 1748,   -1, 1749,   -1,   -1,
+        -1, 1750,   -1, 1751,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1752, 1753,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1753,   -1, 1754,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1754,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1755,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1756,   -1, 1757,   -1,   -1,   -1,
-        -1, 1758,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1759,
+      1755,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1756,   -1,   -1,   -1,   -1,
+        -1, 1757,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1758,
+        -1,   -1,   -1, 1759,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1760,   -1,   -1, 1761,
+        -1,   -1, 1762,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1763,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1764, 1765,   -1,   -1,
+        -1,   -1, 1766,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1767,   -1, 1768,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1769,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1760,   -1,   -1, 1761, 1762,
-        -1,   -1,   -1,   -1,   -1, 1763,   -1,   -1,
-        -1, 1764,   -1,   -1,   -1, 1765,   -1,   -1,
-        -1,   -1,   -1, 1766,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1767,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1768, 1769,
-        -1, 1770,   -1, 1771,   -1,   -1,   -1,   -1,
+      1770,   -1, 1771,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1772,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1773,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1774,   -1,   -1, 1775, 1776,   -1,
-        -1,   -1, 1777,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1772, 1773,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1774,
+        -1,   -1,   -1,   -1,   -1,   -1, 1775,   -1,
+        -1,   -1,   -1,   -1, 1776,   -1,   -1, 1777,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1778,   -1, 1779,   -1,   -1,   -1,
+        -1,   -1,   -1, 1778,   -1,   -1,   -1, 1779,
+        -1,   -1,   -1,   -1,   -1,   -1, 1780,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1780,   -1,   -1,
-        -1,   -1, 1781,   -1,   -1,   -1,   -1,   -1,
-        -1, 1782,   -1, 1783, 1784,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1785,   -1,   -1,
-      1786,   -1,   -1,   -1,   -1, 1787,   -1,   -1,
+        -1,   -1, 1781,   -1, 1782, 1783,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1788,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1789,   -1,   -1,   -1, 1790,   -1,
-        -1,   -1, 1791,   -1,   -1,   -1,   -1,   -1,
-        -1, 1792,   -1,   -1,   -1, 1793,   -1,   -1,
+        -1,   -1, 1784,   -1,   -1,   -1, 1785,   -1,
+      1786,   -1,   -1,   -1,   -1, 1787, 1788,   -1,
+      1789,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1790,   -1, 1791,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1792,   -1,   -1,
+        -1,   -1,   -1, 1793,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 1794,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1795,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1795,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1, 1796,   -1,   -1,   -1,
-      1797,   -1,   -1,   -1,   -1,   -1, 1798,   -1,
-        -1,   -1, 1799,   -1,   -1,   -1,   -1,   -1,
+      1797,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1798,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1799,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1800,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1800,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1801, 1802,
-        -1,   -1,   -1, 1803,   -1,   -1,   -1,   -1,
-      1804,   -1,   -1,   -1,   -1, 1805,   -1,   -1,
-        -1, 1806, 1807,   -1, 1808, 1809,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1810,   -1,
-        -1,   -1, 1811,   -1, 1812, 1813, 1814,   -1,
-        -1, 1815,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1816,   -1,   -1,   -1,   -1,   -1,   -1,
+      1801,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1802,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1817,   -1,
-      1818,   -1,   -1, 1819,   -1,   -1,   -1,   -1,
-        -1,   -1, 1820,   -1,   -1,   -1,   -1,   -1,
+      1803,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1804,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1805,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1806,   -1,   -1,   -1,   -1, 1807,
+        -1,   -1,   -1,   -1,   -1,   -1, 1808,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1809,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1810,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1811,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1812,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1813,   -1,   -1,   -1,   -1, 1814,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1815,
+        -1, 1816,   -1,   -1,   -1,   -1,   -1, 1817,
+        -1,   -1,   -1,   -1,   -1, 1818, 1819,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1820,
         -1,   -1,   -1,   -1, 1821,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1822,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1822,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1823,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1823,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1824,   -1, 1825,   -1, 1826,
-        -1,   -1, 1827,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1828,   -1,
-        -1,   -1,   -1,   -1, 1829, 1830,   -1,   -1,
-      1831,   -1,   -1,   -1,   -1,   -1,   -1, 1832,
-        -1,   -1,   -1,   -1, 1833,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1824,   -1,
+        -1,   -1,   -1,   -1, 1825,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1834,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1826,
+        -1, 1827,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1828,   -1,   -1,   -1,   -1,
+        -1, 1829,   -1,   -1, 1830,   -1,   -1, 1831,
+        -1,   -1,   -1,   -1,   -1,   -1, 1832,   -1,
+        -1, 1833,   -1,   -1, 1834,   -1,   -1,   -1,
+        -1,   -1, 1835,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1835,   -1,
-      1836,   -1,   -1, 1837,   -1,   -1,   -1,   -1,
-        -1,   -1, 1838, 1839,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1840,   -1,   -1,   -1,   -1,
-      1841,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1842,   -1,   -1,   -1, 1843,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1844,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1845,   -1,
-        -1,   -1,   -1,   -1, 1846,   -1,   -1,   -1,
-        -1,   -1,   -1, 1847,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1848,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1849,   -1, 1850,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1851,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1836,   -1,   -1, 1837,
+        -1,   -1,   -1,   -1,   -1, 1838,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1839,   -1,
+        -1,   -1, 1840,   -1, 1841,   -1,   -1,   -1,
+        -1,   -1, 1842,   -1,   -1,   -1,   -1,   -1,
+      1843,   -1,   -1, 1844,   -1,   -1,   -1, 1845,
+        -1,   -1,   -1,   -1,   -1, 1846,   -1,   -1,
+        -1,   -1,   -1, 1847,   -1, 1848,   -1,   -1,
+      1849,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1850, 1851,   -1,
         -1,   -1, 1852,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1853,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1854,   -1,   -1, 1855,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1853,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1856,   -1,   -1,   -1,   -1,   -1,   -1,
+      1854,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1855, 1856,   -1,   -1,   -1,
+        -1,   -1,   -1, 1857,   -1, 1858,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1857,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1858,   -1, 1859,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1859,   -1,
         -1, 1860,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1861,   -1, 1862,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1863,   -1,   -1,   -1,
-      1864,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1865,   -1, 1866,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1867,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1868,   -1,   -1, 1869,   -1, 1870,   -1, 1871,
-        -1, 1872,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1873,   -1,
-        -1, 1874,   -1,   -1,   -1,   -1,   -1, 1875,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1876, 1877,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1878,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1879,   -1, 1880,
+        -1,   -1,   -1,   -1, 1861,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1862,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1863, 1864,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1865,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1866,   -1,   -1, 1867,   -1,   -1,   -1, 1868,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1869,   -1,   -1, 1870,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1871,   -1,   -1,   -1, 1872,   -1,   -1,
+        -1,   -1, 1873,   -1, 1874,   -1,   -1,   -1,
+        -1,   -1,   -1, 1875, 1876,   -1,   -1,   -1,
+        -1,   -1,   -1, 1877,   -1,   -1, 1878,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1879,
+        -1, 1880,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1, 1881,   -1,   -1,   -1,
-        -1,   -1, 1882,   -1, 1883, 1884,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1882, 1883,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1884,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1885,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1886,   -1,   -1,
+        -1,   -1,   -1,   -1, 1887,   -1,   -1,   -1,
+        -1, 1888,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1889,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1890,   -1, 1891, 1892,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1893,   -1,
+        -1,   -1,   -1,   -1, 1894,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1885,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1895,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1896,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1886,   -1,   -1,   -1,   -1,   -1,
+      1897,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1887,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1888,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1889,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1890,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1891,   -1, 1892,   -1,
-        -1,   -1,   -1,   -1,   -1, 1893,   -1,   -1,
-        -1,   -1,   -1,   -1, 1894,   -1, 1895,   -1,
-        -1, 1896,   -1,   -1,   -1,   -1, 1897,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 1898,
+        -1, 1899,   -1,   -1,   -1,   -1,   -1,   -1,
+      1900,   -1,   -1,   -1,   -1,   -1,   -1, 1901,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1902,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1899,   -1, 1900, 1901,   -1,   -1,   -1,
+      1903,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1904,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1902,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1903,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1904, 1905,
+        -1,   -1, 1905,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1906,   -1,   -1,   -1,
+      1907,   -1,   -1, 1908, 1909,   -1,   -1,   -1,
+        -1, 1910,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1911,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 1912,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1913,   -1,   -1,
+        -1,   -1, 1914,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 1915,   -1,   -1,
+      1916, 1917,   -1,   -1,   -1,   -1, 1918,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1919,
+        -1,   -1,   -1,   -1, 1920, 1921,   -1,   -1,
+        -1, 1922,   -1,   -1,   -1, 1923,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1924,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1906,   -1,
-        -1,   -1,   -1,   -1, 1907,   -1, 1908,   -1,
-        -1,   -1,   -1, 1909,   -1,   -1,   -1,   -1,
-      1910,   -1, 1911,   -1,   -1,   -1, 1912,   -1,
+        -1,   -1, 1925,   -1,   -1,   -1,   -1, 1926,
+        -1,   -1,   -1, 1927,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1913,
-        -1,   -1,   -1, 1914,   -1,   -1,   -1, 1915,
+      1928,   -1,   -1,   -1,   -1, 1929,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1916,   -1,   -1,   -1,   -1,   -1,
-        -1, 1917,   -1, 1918,   -1,   -1,   -1, 1919,
+        -1,   -1,   -1,   -1,   -1,   -1, 1930,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1920,   -1,
+        -1,   -1,   -1, 1931,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1932,   -1,
+        -1, 1933,   -1,   -1,   -1,   -1,   -1,   -1,
+      1934,   -1,   -1,   -1,   -1,   -1, 1935,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1936, 1937,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1938,   -1,   -1,   -1,   -1, 1939,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1940, 1941,   -1,   -1,
+        -1, 1942,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1943,   -1, 1944,   -1,
+        -1, 1945,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1921,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1922,   -1, 1923,   -1, 1924, 1925,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1926,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 1927,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1928,   -1, 1929,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1930,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1931,   -1, 1932,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1933,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1934,
-        -1,   -1, 1935,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1936,   -1,   -1, 1937,   -1,   -1,   -1,
-        -1, 1938,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1939,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1940,   -1,   -1,   -1,   -1, 1941,   -1,
-        -1,   -1,   -1,   -1, 1942,   -1, 1943,   -1,
-        -1,   -1, 1944,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1945,   -1,   -1,   -1, 1946,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1947,
-        -1,   -1, 1948,   -1,   -1, 1949,   -1,   -1,
-        -1,   -1,   -1,   -1, 1950,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1951,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1952,
-      1953,   -1,   -1,   -1,   -1,   -1, 1954,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1946,   -1,   -1,   -1,
+      1947,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1948,   -1,
+        -1, 1949,   -1, 1950,   -1,   -1,   -1, 1951,
+        -1,   -1,   -1, 1952,   -1,   -1,   -1,   -1,
+      1953,   -1, 1954,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1, 1955, 1956,   -1,
-        -1,   -1,   -1, 1957,   -1, 1958,   -1, 1959,
-        -1,   -1, 1960,   -1,   -1, 1961, 1962,   -1,
-      1963,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1964,   -1,   -1, 1965,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 1966, 1967,   -1,   -1,
-        -1,   -1,   -1,   -1, 1968, 1969, 1970,   -1,
-      1971, 1972, 1973,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1974,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 1975,   -1,
-        -1, 1976,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1977, 1978,   -1,   -1,   -1,
-      1979,   -1,   -1, 1980,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1981,   -1,   -1,   -1,   -1, 1982, 1983,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1984,   -1, 1985,   -1,   -1,   -1, 1986,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 1987,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1988, 1989,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1990,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      1991,   -1, 1992,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 1993, 1994,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 1995,   -1,   -1, 1996,   -1,   -1, 1997,
-        -1, 1998,   -1,   -1,   -1, 1999, 2000,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2001,   -1,   -1,   -1,   -1,   -1,
-        -1, 2002,   -1,   -1, 2003,   -1,   -1, 2004,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2005,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2006,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2007,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2008,   -1,   -1, 2009,   -1,   -1,   -1,
-        -1, 2010,   -1,   -1,   -1,   -1,   -1, 2011,
-        -1,   -1,   -1, 2012,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2013,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2014,
-        -1, 2015,   -1,   -1, 2016,   -1,   -1,   -1,
-        -1, 2017,   -1,   -1,   -1, 2018,   -1,   -1,
-        -1,   -1,   -1, 2019, 2020,   -1,   -1, 2021,
-        -1,   -1,   -1,   -1,   -1,   -1, 2022, 2023,
-        -1,   -1,   -1, 2024,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2025,
-        -1, 2026, 2027,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2028,   -1, 2029, 2030,
-        -1,   -1,   -1,   -1, 2031,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2032,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2033,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2034,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2035,
-        -1, 2036,   -1, 2037,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2038,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2039, 2040,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2041, 2042, 2043, 2044,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2045,   -1,   -1,   -1, 2046,
-      2047,   -1,   -1,   -1,   -1,   -1,   -1, 2048,
-        -1, 2049,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2050,   -1,   -1,   -1, 2051,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2052,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2053,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2054,   -1,   -1, 2055,   -1,   -1, 2056,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2057,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2058,   -1,   -1,   -1,
-        -1, 2059,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2060,   -1,   -1,   -1,
-        -1,   -1, 2061,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2062,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2063,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2064,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1957,
+      1958,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2065,   -1,   -1,
-        -1,   -1,   -1,   -1, 2066,   -1,   -1,   -1,
-        -1,   -1, 2067,   -1,   -1,   -1,   -1,   -1,
-      2068,   -1, 2069, 2070,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2071,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2072,   -1,   -1,   -1, 2073,   -1, 2074,
-        -1,   -1,   -1,   -1, 2075,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2076,   -1,   -1,
-        -1, 2077, 2078, 2079,   -1,   -1,   -1,   -1,
-        -1, 2080,   -1,   -1,   -1,   -1, 2081,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2082,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2083,   -1,   -1,   -1,   -1,
-        -1, 2084,   -1,   -1,   -1,   -1,   -1, 2085,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2086,   -1,   -1,   -1,
-        -1,   -1, 2087,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 1959,   -1,   -1,   -1,   -1,   -1,   -1,
+      1960, 1961, 1962, 1963,   -1,   -1, 1964,   -1,
+        -1,   -1,   -1,   -1, 1965, 1966,   -1,   -1,
+        -1,   -1, 1967,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1968,   -1, 1969,   -1,   -1,   -1,
+        -1,   -1,   -1, 1970,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1971,   -1,   -1,   -1,   -1, 1972,   -1,   -1,
+        -1, 1973,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 1974, 1975,   -1,   -1, 1976,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 1977,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      1978,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 1979,
+        -1,   -1,   -1,   -1, 1980,   -1,   -1, 1981,
+        -1,   -1, 1982,   -1,   -1,   -1,   -1,   -1,
+      1983,   -1,   -1,   -1, 1984,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1985,   -1,   -1,   -1,
+      1986,   -1,   -1,   -1, 1987,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1988,   -1,   -1, 1989,
+        -1,   -1,   -1,   -1, 1990,   -1, 1991,   -1,
+      1992,   -1, 1993,   -1,   -1,   -1,   -1, 1994,
+      1995, 1996,   -1,   -1,   -1, 1997,   -1,   -1,
+      1998,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 1999,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2000,   -1,   -1, 2001,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2002,   -1,   -1,   -1,   -1,   -1,   -1,
+      2003,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2004,
+        -1,   -1, 2005,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2006,   -1,   -1,
+        -1, 2007,   -1, 2008, 2009,   -1,   -1, 2010,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2011,   -1, 2012,   -1,   -1, 2013,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2014,   -1,   -1,   -1,   -1,
+        -1,   -1, 2015,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2016,   -1,   -1,   -1, 2017,   -1,   -1,
+        -1,   -1,   -1,   -1, 2018,   -1,   -1, 2019,
+        -1,   -1,   -1,   -1, 2020,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2021,   -1,   -1,   -1,
+        -1,   -1,   -1, 2022, 2023,   -1,   -1,   -1,
+        -1, 2024,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2025,   -1, 2026,   -1,   -1,   -1,
+        -1,   -1, 2027,   -1,   -1,   -1, 2028,   -1,
+        -1,   -1, 2029,   -1,   -1, 2030, 2031, 2032,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2033,
+        -1,   -1,   -1,   -1,   -1, 2034,   -1,   -1,
+        -1,   -1, 2035,   -1,   -1,   -1,   -1,   -1,
+        -1, 2036,   -1,   -1,   -1,   -1, 2037,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2038,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2039,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2040,   -1, 2041,   -1,   -1,   -1,   -1,
+      2042,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2043,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2044,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2045,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2046, 2047,   -1,   -1,   -1, 2048,   -1,
+        -1,   -1,   -1,   -1,   -1, 2049,   -1, 2050,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2051,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2052,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2053,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2054,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2055,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2056,   -1,
+        -1,   -1, 2057, 2058,   -1, 2059,   -1,   -1,
+        -1, 2060, 2061,   -1,   -1, 2062,   -1,   -1,
+        -1,   -1, 2063,   -1,   -1, 2064,   -1,   -1,
+        -1,   -1,   -1,   -1, 2065,   -1,   -1,   -1,
+      2066, 2067,   -1,   -1,   -1,   -1,   -1, 2068,
+        -1,   -1,   -1, 2069, 2070,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2071,   -1,   -1,
+      2072,   -1,   -1,   -1, 2073,   -1,   -1,   -1,
+        -1, 2074, 2075,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2076,   -1,   -1, 2077,   -1, 2078,   -1,
+        -1, 2079,   -1,   -1, 2080,   -1,   -1,   -1,
+        -1, 2081,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2082,
+      2083,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2084,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2085,   -1,
+        -1,   -1,   -1,   -1,   -1, 2086,   -1, 2087,
         -1,   -1,   -1,   -1, 2088,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2089,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2089,   -1,   -1,   -1,   -1,   -1,   -1, 2090,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2091,
-      2092,   -1,   -1, 2093,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2094,   -1,
+      2090,   -1,   -1, 2091,   -1, 2092,   -1, 2093,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2094,
+        -1,   -1,   -1,   -1, 2095,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2096,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2095,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2096,   -1,   -1, 2097,   -1,
-      2098,   -1, 2099,   -1, 2100,   -1,   -1, 2101,
-      2102,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2103,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2104,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2105,   -1,   -1,
+        -1,   -1,   -1, 2097, 2098,   -1,   -1,   -1,
+        -1,   -1, 2099,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2100,
+        -1, 2101,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2102,   -1, 2103,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2104,
+      2105,   -1,   -1, 2106,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2106,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2107,   -1,   -1,   -1,
-        -1,   -1, 2108,   -1,   -1,   -1, 2109,   -1,
-        -1, 2110,   -1, 2111,   -1,   -1, 2112, 2113,
-        -1,   -1,   -1, 2114,   -1,   -1, 2115,   -1,
-        -1,   -1, 2116,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2117,   -1,   -1,   -1,
-        -1,   -1, 2118,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2119, 2120,   -1, 2121,
-        -1,   -1,   -1, 2122,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2123,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2124,   -1, 2125,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2126,
-        -1,   -1,   -1,   -1,   -1, 2127,   -1,   -1,
+        -1,   -1,   -1,   -1, 2107,   -1, 2108,   -1,
+      2109,   -1,   -1,   -1,   -1, 2110,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2111,   -1,   -1,
+        -1,   -1, 2112,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2113,   -1,
+      2114,   -1, 2115,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2116,   -1,   -1, 2117, 2118,
+        -1,   -1, 2119,   -1,   -1,   -1,   -1,   -1,
+      2120,   -1,   -1, 2121,   -1,   -1,   -1,   -1,
+      2122,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2123, 2124,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2125,   -1, 2126,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2128,   -1, 2129,   -1,   -1,   -1,
-        -1,   -1,   -1, 2130,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2131,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2132,   -1,   -1,
-        -1, 2133,   -1, 2134, 2135,   -1,   -1,   -1,
-        -1, 2136,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2127,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2128,   -1,   -1,   -1, 2129,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2130,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2131,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2132,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2133,   -1,   -1, 2134,   -1,   -1,   -1, 2135,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2136,   -1,   -1,   -1,   -1,
         -1,   -1,   -1, 2137,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2138,
-        -1,   -1,   -1,   -1,   -1,   -1, 2139,   -1,
-        -1, 2140,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2141,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2142,   -1,   -1,   -1,   -1,
-        -1, 2143,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2138,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2139,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2140,   -1,   -1, 2141,   -1,   -1, 2142,   -1,
+      2143,   -1, 2144,   -1,   -1,   -1, 2145,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2146,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2147,   -1,   -1,
+        -1,   -1,   -1,   -1, 2148,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2149,   -1, 2150,   -1,   -1,   -1,   -1,
+        -1,   -1, 2151,   -1,   -1,   -1,   -1, 2152,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2153,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2154,   -1,
+        -1, 2155,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2156,   -1,   -1,   -1,   -1, 2157,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2158,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2144, 2145, 2146,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2147,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2159,
+        -1, 2160, 2161,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2148,   -1,   -1,   -1,   -1,
-      2149,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2162,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2163,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2164,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2165,   -1,   -1,   -1, 2166,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2167,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2168,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2169, 2170,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2150,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2171,   -1,
+        -1,   -1,   -1,   -1, 2172,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2173,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2151, 2152,   -1, 2153,   -1,
-        -1,   -1,   -1, 2154,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2155,   -1,   -1,   -1,   -1,
-        -1,   -1, 2156, 2157,   -1, 2158,   -1, 2159,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2160,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2161,   -1,
-        -1,   -1,   -1,   -1,   -1, 2162, 2163,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2164, 2165,   -1,   -1,   -1,
-        -1, 2166,   -1, 2167,   -1,   -1, 2168,   -1,
-        -1, 2169,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2170,   -1, 2171, 2172,   -1,   -1, 2173,
-        -1,   -1,   -1,   -1,   -1, 2174, 2175,   -1,
+      2174,   -1,   -1,   -1, 2175,   -1,   -1,   -1,
+      2176,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2177,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2178,   -1,   -1,   -1, 2179,
+      2180,   -1,   -1,   -1, 2181,   -1,   -1,   -1,
+        -1, 2182,   -1,   -1,   -1,   -1,   -1,   -1,
+      2183,   -1,   -1,   -1,   -1, 2184,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2185,   -1,
+        -1,   -1, 2186,   -1, 2187,   -1, 2188,   -1,
+        -1,   -1,   -1, 2189,   -1,   -1,   -1,   -1,
+      2190,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2176,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2177,
-      2178,   -1,   -1, 2179,   -1, 2180,   -1, 2181,
-        -1,   -1, 2182,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2183,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2184,   -1,   -1,   -1, 2185,   -1,   -1,
-      2186,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2187, 2188,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2191,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2192,   -1,   -1, 2193,
+      2194,   -1,   -1,   -1, 2195,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2196,   -1, 2197,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2189,   -1,   -1, 2190, 2191,   -1,   -1,
+        -1,   -1, 2198,   -1,   -1,   -1,   -1,   -1,
+      2199,   -1,   -1, 2200,   -1, 2201,   -1,   -1,
+      2202,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2203,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2204,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2205,   -1,
+        -1, 2206,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2207,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2208,   -1, 2209,   -1,   -1, 2210, 2211,   -1,
+        -1, 2212, 2213,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2192,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2193,   -1,
+        -1,   -1, 2214,   -1,   -1,   -1,   -1, 2215,
+        -1,   -1,   -1, 2216,   -1,   -1,   -1,   -1,
+      2217,   -1,   -1,   -1,   -1,   -1, 2218,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2219,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2220,   -1, 2221, 2222,   -1, 2223,
+        -1, 2224, 2225,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2194,   -1,   -1,   -1,
-      2195,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2196,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2197,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2198,   -1,   -1, 2199,   -1,
-        -1,   -1, 2200,   -1,   -1,   -1,   -1,   -1,
+      2226,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2227,
+        -1, 2228,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2229,   -1, 2230, 2231,   -1,   -1,
+        -1, 2232,   -1,   -1, 2233,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2234,   -1,   -1,   -1,
+        -1, 2235,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2236,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2201, 2202,
-        -1,   -1, 2203, 2204,   -1,   -1, 2205,   -1,
+        -1,   -1,   -1,   -1, 2237,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2206,   -1,   -1,   -1,   -1,   -1, 2207,   -1,
-        -1,   -1,   -1,   -1, 2208,   -1,   -1,   -1,
-      2209,   -1,   -1,   -1, 2210, 2211,   -1, 2212,
-        -1,   -1,   -1, 2213,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2214,   -1,
-        -1,   -1,   -1,   -1,   -1, 2215, 2216, 2217,
-        -1,   -1,   -1,   -1, 2218,   -1,   -1,   -1,
-      2219,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2220,   -1,
-        -1,   -1, 2221,   -1,   -1,   -1, 2222,   -1,
+        -1,   -1,   -1,   -1,   -1, 2238,   -1, 2239,
+        -1,   -1,   -1,   -1, 2240,   -1,   -1, 2241,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2223,   -1,   -1, 2224,   -1, 2225,
-      2226, 2227,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2228,   -1,   -1,   -1,   -1,   -1, 2229,
+        -1,   -1, 2242,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2230,
-        -1, 2231,   -1,   -1,   -1,   -1,   -1, 2232,
-        -1,   -1, 2233,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2234,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2235,   -1, 2236,   -1,   -1, 2237,   -1,
-        -1, 2238,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2239,   -1,   -1,
-        -1,   -1,   -1,   -1, 2240,   -1, 2241,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2242, 2243,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2244,   -1, 2245,   -1,   -1,   -1,
+        -1,   -1,   -1, 2243,   -1,   -1,   -1,   -1,
+      2244,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2245,   -1,
         -1, 2246,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2247,   -1,   -1,   -1,   -1,   -1,
-        -1, 2248,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2249,   -1, 2250, 2251,   -1, 2252,   -1,
-      2253, 2254,   -1,   -1,   -1,   -1, 2255,   -1,
-        -1,   -1, 2256,   -1, 2257,   -1,   -1,   -1,
-        -1,   -1, 2258,   -1, 2259,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2260,   -1, 2261,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2262,
-        -1,   -1,   -1,   -1,   -1,   -1, 2263,   -1,
-        -1, 2264,   -1,   -1,   -1, 2265,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2266,
+        -1,   -1,   -1,   -1, 2247,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2248,   -1,   -1,
+      2249,   -1,   -1,   -1,   -1,   -1,   -1, 2250,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2267, 2268, 2269,   -1,   -1,   -1,   -1, 2270,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2271, 2272,   -1,
-        -1,   -1,   -1,   -1,   -1, 2273,   -1,   -1,
-        -1, 2274,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2275,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2251, 2252,   -1,   -1, 2253,   -1,   -1,
+      2254,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2255,
+        -1,   -1,   -1, 2256,   -1,   -1, 2257, 2258,
+        -1, 2259,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2260,
+        -1,   -1,   -1, 2261,   -1, 2262, 2263,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2264,
+      2265,   -1, 2266,   -1, 2267,   -1,   -1, 2268,
+        -1,   -1,   -1,   -1, 2269,   -1,   -1,   -1,
+      2270,   -1, 2271,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2272,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2273,
+      2274,   -1, 2275,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1, 2276,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2277,
+      2278,   -1,   -1,   -1, 2279,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2277,   -1,   -1, 2278,   -1,
-      2279,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2280, 2281,   -1,   -1,   -1,
+        -1,   -1,   -1, 2280,   -1, 2281, 2282,   -1,
+      2283,   -1, 2284,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2285,   -1, 2286, 2287,   -1,
+        -1,   -1, 2288, 2289,   -1,   -1,   -1,   -1,
+      2290, 2291, 2292, 2293,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2294, 2295,   -1,
+      2296,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2297, 2298,   -1,   -1,   -1,
+        -1,   -1,   -1, 2299,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2282,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2283,   -1,   -1,   -1,
-        -1,   -1, 2284,   -1,   -1,   -1,   -1,   -1,
-      2285,   -1,   -1,   -1,   -1,   -1, 2286,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2300,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2287,   -1,
-      2288,   -1,   -1,   -1,   -1,   -1, 2289,   -1,
+        -1,   -1,   -1,   -1, 2301,   -1,   -1,   -1,
+      2302, 2303,   -1,   -1,   -1,   -1, 2304, 2305,
+        -1,   -1,   -1,   -1, 2306,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2307,
+        -1,   -1, 2308,   -1,   -1,   -1, 2309, 2310,
+      2311,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2312,   -1,   -1,   -1,
+        -1,   -1, 2313,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2314,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2315,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2290,   -1,   -1,   -1,   -1,   -1, 2291,
-      2292,   -1, 2293,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2294, 2295,   -1,   -1, 2296,   -1,
-        -1, 2297,   -1,   -1, 2298,   -1,   -1, 2299,
+        -1, 2316,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2317,   -1, 2318, 2319,   -1, 2320,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2300,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2301,   -1,
-        -1,   -1,   -1,   -1,   -1, 2302,   -1,   -1,
-        -1,   -1, 2303, 2304,   -1,   -1, 2305,   -1,
-        -1,   -1, 2306,   -1,   -1,   -1, 2307, 2308,
-      2309,   -1,   -1,   -1,   -1, 2310, 2311, 2312,
-        -1,   -1, 2313,   -1,   -1, 2314,   -1, 2315,
+        -1,   -1,   -1,   -1,   -1,   -1, 2321,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2322,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2323,   -1,   -1,   -1, 2324,   -1,   -1,   -1,
+        -1, 2325,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2326, 2327,   -1,   -1,
+        -1,   -1,   -1,   -1, 2328,   -1,   -1,   -1,
+        -1, 2329,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2330,   -1, 2331,   -1,   -1,   -1,
+      2332,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2333,   -1, 2334, 2335,   -1, 2336,
+        -1,   -1,   -1,   -1,   -1,   -1, 2337,   -1,
+        -1,   -1, 2338,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2339,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2340, 2341, 2342,   -1,   -1,   -1,   -1,
+      2343,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2344,   -1,   -1,   -1,   -1, 2345,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2346, 2347,   -1, 2348,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2349,   -1,   -1,
+        -1, 2350, 2351,   -1, 2352,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2353,   -1, 2354,   -1,
+      2355, 2356, 2357,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2358,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2359,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2360, 2361,   -1,   -1, 2362,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2363,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2364,
+        -1,   -1,   -1,   -1, 2365,   -1,   -1, 2366,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2316,
+      2367,   -1,   -1,   -1,   -1, 2368,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2369,
+        -1,   -1, 2370,   -1,   -1,   -1,   -1,   -1,
+      2371,   -1,   -1,   -1,   -1, 2372,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2317,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2318,
+        -1,   -1, 2373,   -1,   -1,   -1,   -1, 2374,
+        -1,   -1, 2375,   -1,   -1, 2376,   -1, 2377,
+      2378,   -1, 2379,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2380,   -1,   -1,
+        -1,   -1,   -1, 2381,   -1,   -1,   -1, 2382,
+        -1,   -1,   -1, 2383,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2384,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2385,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2386, 2387,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2319,   -1,   -1,
-        -1,   -1, 2320, 2321,   -1,   -1,   -1, 2322,
-        -1, 2323, 2324, 2325, 2326,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2388,
+        -1,   -1,   -1,   -1, 2389,   -1, 2390,   -1,
+        -1,   -1,   -1,   -1, 2391,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2327,   -1,   -1,   -1, 2328, 2329,   -1, 2330,
-      2331,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2332, 2333,   -1,   -1, 2334,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2335,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2336,   -1, 2337,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2338,   -1,   -1,   -1,   -1, 2339, 2340,
-        -1,   -1,   -1, 2341, 2342,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2343,   -1,   -1, 2344,
-      2345, 2346,   -1,   -1,   -1,   -1,   -1,   -1,
-      2347,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2348,   -1,   -1,   -1,   -1, 2349,
-        -1,   -1,   -1,   -1, 2350,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2351,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2352,   -1,   -1,   -1,   -1,   -1,
-      2353,   -1,   -1, 2354,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2355,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2356,   -1,   -1,   -1, 2357,   -1,   -1,
-        -1,   -1,   -1,   -1, 2358, 2359,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2360,   -1,
-        -1,   -1,   -1,   -1,   -1, 2361, 2362,   -1,
-        -1,   -1,   -1,   -1, 2363,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2364,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2365,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2366, 2367,   -1,   -1,
-        -1,   -1,   -1,   -1, 2368, 2369,   -1,   -1,
-        -1,   -1,   -1, 2370,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2371,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2372,   -1, 2373,   -1,
-        -1, 2374,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2375,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2376,   -1,   -1,
-        -1,   -1,   -1,   -1, 2377, 2378,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2379,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2380,   -1,
-        -1, 2381,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2382,   -1,   -1,   -1, 2383,   -1,   -1, 2384,
-        -1,   -1,   -1,   -1, 2385,   -1,   -1,   -1,
-        -1,   -1,   -1, 2386,   -1, 2387,   -1, 2388,
-        -1,   -1, 2389, 2390,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2391, 2392,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2393,
-        -1,   -1, 2394,   -1,   -1,   -1, 2395,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2396,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2397,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2398,   -1, 2399, 2400,   -1,   -1,   -1,
+      2392,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2393,   -1,   -1,   -1,   -1, 2394,
+      2395,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2396,   -1,   -1, 2397,   -1,   -1,
+        -1,   -1, 2398,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2399,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2400,   -1,
         -1, 2401,   -1,   -1,   -1,   -1, 2402,   -1,
-      2403,   -1,   -1, 2404,   -1, 2405,   -1, 2406,
-        -1,   -1,   -1,   -1, 2407,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2408,   -1,   -1, 2409,   -1, 2410,
-      2411,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2403,   -1, 2404, 2405,
+        -1, 2406,   -1,   -1,   -1,   -1,   -1, 2407,
+      2408,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2409,   -1,   -1,   -1,   -1,   -1,   -1, 2410,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2411,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2412, 2413,   -1, 2414,
+        -1, 2415,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2416,   -1, 2417,   -1,   -1,   -1,   -1,   -1,
+        -1, 2418,   -1, 2419, 2420,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2421,
+        -1,   -1,   -1, 2422,   -1,   -1,   -1,   -1,
+        -1, 2423,   -1,   -1,   -1, 2424, 2425,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2426,
+        -1,   -1,   -1, 2427,   -1,   -1, 2428,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2429,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2430,   -1,
+        -1,   -1,   -1,   -1, 2431,   -1,   -1,   -1,
+        -1, 2432,   -1,   -1,   -1, 2433,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2434,   -1, 2435,   -1,   -1,   -1,   -1,   -1,
+      2436,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2437,   -1, 2438,   -1, 2439,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2440,   -1,
+        -1, 2441,   -1, 2442, 2443,   -1,   -1, 2444,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2445,   -1,   -1,   -1,   -1,
+      2446,   -1,   -1,   -1,   -1,   -1,   -1, 2447,
+        -1, 2448,   -1,   -1,   -1, 2449,   -1, 2450,
+      2451,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2452,   -1,   -1, 2453,   -1, 2454,   -1,
+        -1,   -1,   -1,   -1, 2455,   -1,   -1, 2456,
+        -1,   -1,   -1,   -1,   -1,   -1, 2457,   -1,
+        -1,   -1, 2458,   -1, 2459,   -1,   -1, 2460,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2461, 2462, 2463,   -1,   -1,   -1,
+      2464,   -1,   -1, 2465,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2466,   -1,   -1,
+        -1,   -1,   -1,   -1, 2467,   -1, 2468,   -1,
+      2469,   -1, 2470,   -1,   -1, 2471,   -1,   -1,
+        -1, 2472,   -1,   -1,   -1,   -1,   -1, 2473,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2474,   -1,   -1,   -1,   -1,   -1, 2475,
+        -1, 2476,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2477,
+      2478,   -1,   -1,   -1, 2479,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2480,   -1,   -1,
+      2481,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2412,   -1, 2413, 2414,   -1,   -1,   -1,
-        -1,   -1,   -1, 2415,   -1,   -1, 2416,   -1,
-        -1, 2417,   -1,   -1, 2418,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2419,   -1,   -1,
-        -1,   -1,   -1, 2420,   -1, 2421,   -1,   -1,
-        -1,   -1,   -1,   -1, 2422,   -1,   -1,   -1,
+        -1,   -1, 2482,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2423,   -1,   -1,   -1,   -1,   -1, 2424,   -1,
+        -1,   -1,   -1,   -1, 2483,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2425, 2426, 2427,
-        -1,   -1,   -1,   -1, 2428,   -1,   -1, 2429,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2430,   -1,   -1, 2431,   -1, 2432,   -1,   -1,
-        -1,   -1,   -1, 2433, 2434,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2435, 2436,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2437,   -1,   -1,   -1,   -1,
-      2438,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2439,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2440, 2441,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2442,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2443,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2444,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2445,   -1,   -1,   -1, 2446,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2447,
-        -1,   -1, 2448,   -1,   -1,   -1,   -1, 2449,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2450, 2451,   -1,   -1,   -1,   -1,
-      2452,   -1,   -1,   -1,   -1,   -1, 2453, 2454,
-      2455,   -1,   -1, 2456,   -1,   -1,   -1, 2457,
-        -1,   -1,   -1, 2458,   -1,   -1,   -1, 2459,
-        -1,   -1,   -1,   -1,   -1,   -1, 2460,   -1,
-        -1, 2461,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2462,   -1,   -1, 2463, 2464,   -1,
-        -1,   -1,   -1, 2465,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2466,   -1,   -1,   -1,   -1, 2467, 2468,   -1,
-        -1,   -1,   -1, 2469,   -1,   -1, 2470,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2471,   -1,   -1, 2472,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2473, 2474,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2475,   -1, 2476,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2477,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2478, 2479,   -1,   -1,
-      2480,   -1, 2481,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2482,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2483,   -1,   -1, 2484,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2485, 2486,
-        -1,   -1, 2487,   -1,   -1,   -1,   -1, 2488,
+        -1,   -1,   -1,   -1, 2484,   -1,   -1, 2485,
+        -1,   -1,   -1, 2486,   -1,   -1,   -1,   -1,
+        -1,   -1, 2487,   -1,   -1, 2488,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 2489,
-        -1, 2490,   -1,   -1, 2491,   -1,   -1, 2492,
-        -1,   -1,   -1,   -1, 2493,   -1,   -1,   -1,
+        -1, 2490,   -1,   -1,   -1, 2491,   -1,   -1,
+        -1,   -1,   -1, 2492, 2493,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2494,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2494,   -1, 2495, 2496,   -1,   -1,   -1,
+        -1, 2497,   -1,   -1,   -1,   -1, 2498,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2499,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2500,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2501,
+        -1,   -1,   -1,   -1, 2502,   -1,   -1,   -1,
+      2503,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2504,
+      2505,   -1,   -1,   -1,   -1,   -1, 2506,   -1,
+        -1, 2507,   -1,   -1,   -1,   -1, 2508,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2509,
+        -1,   -1,   -1,   -1, 2510,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2511,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2495, 2496,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2497, 2498,   -1,   -1,   -1, 2499,
-        -1,   -1,   -1,   -1,   -1, 2500, 2501, 2502,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2503,
+      2512, 2513,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2504,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2505,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2506,   -1, 2507,
-        -1,   -1,   -1,   -1,   -1,   -1, 2508,   -1,
-        -1,   -1,   -1,   -1, 2509,   -1,   -1, 2510,
-        -1,   -1,   -1,   -1, 2511,   -1,   -1,   -1,
-        -1,   -1,   -1, 2512,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2514,   -1,
+        -1,   -1,   -1,   -1, 2515,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2513,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2514,   -1,   -1,   -1,
-      2515,   -1,   -1,   -1,   -1,   -1,   -1, 2516,
-        -1, 2517,   -1,   -1,   -1,   -1, 2518,   -1,
+        -1,   -1,   -1,   -1,   -1, 2516,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2517,   -1,
+        -1,   -1, 2518,   -1, 2519,   -1,   -1,   -1,
+      2520,   -1,   -1,   -1,   -1,   -1, 2521,   -1,
+        -1,   -1,   -1,   -1,   -1, 2522,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2519,   -1,   -1,
+        -1,   -1,   -1, 2523,   -1,   -1,   -1, 2524,
+        -1,   -1,   -1,   -1,   -1,   -1, 2525,   -1,
+        -1,   -1,   -1, 2526,   -1,   -1,   -1,   -1,
+        -1,   -1, 2527,   -1,   -1,   -1,   -1, 2528,
+      2529,   -1,   -1, 2530,   -1,   -1,   -1, 2531,
+      2532,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2533,   -1, 2534, 2535,   -1,   -1,   -1, 2536,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2520,   -1,
+        -1,   -1, 2537, 2538,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2539,
+      2540,   -1,   -1, 2541, 2542,   -1,   -1,   -1,
+        -1, 2543,   -1, 2544,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2545,   -1,   -1,   -1,
+        -1,   -1, 2546,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2521,   -1,   -1,   -1,   -1,   -1,   -1,
+      2547,   -1,   -1,   -1,   -1,   -1,   -1, 2548,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2549,
+      2550,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2551, 2552,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2553,
+        -1, 2554,   -1,   -1,   -1,   -1,   -1, 2555,
+        -1,   -1, 2556,   -1,   -1,   -1,   -1,   -1,
+      2557, 2558,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2522,   -1,   -1,   -1,   -1,   -1, 2523,
-        -1,   -1,   -1, 2524,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2525,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2559,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2526, 2527,   -1,   -1,   -1,   -1, 2528,   -1,
-        -1,   -1,   -1, 2529,   -1, 2530,   -1,   -1,
-        -1,   -1, 2531,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2560,   -1,   -1,   -1,   -1,   -1, 2561,   -1,
+      2562,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2563,   -1,   -1, 2564,   -1,   -1,   -1, 2565,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2566,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2567,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2568,   -1,   -1,   -1,   -1,   -1, 2569,   -1,
+        -1, 2570,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2571, 2572,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2573, 2574,   -1,
+        -1,   -1,   -1,   -1, 2575,   -1, 2576,   -1,
+        -1,   -1,   -1, 2577,   -1,   -1,   -1,   -1,
+      2578, 2579,   -1,   -1,   -1,   -1,   -1, 2580,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2532,   -1,   -1,   -1,   -1, 2533,   -1,   -1,
-        -1,   -1,   -1,   -1, 2534,   -1, 2535,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2536,   -1,
+      2581, 2582,   -1,   -1,   -1,   -1,   -1, 2583,
+        -1,   -1,   -1, 2584, 2585,   -1, 2586,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2537,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2538,   -1,
+        -1,   -1,   -1, 2587,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2539,   -1, 2540,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2541,
-        -1,   -1,   -1,   -1,   -1,   -1, 2542,   -1,
+        -1,   -1,   -1,   -1, 2588,   -1, 2589,   -1,
+        -1,   -1, 2590,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2591,   -1,   -1,   -1,   -1,   -1,
+        -1, 2592,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2593,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2543,   -1,   -1,   -1,   -1,   -1,   -1,
+      2594,   -1,   -1,   -1,   -1,   -1, 2595,   -1,
+      2596,   -1,   -1,   -1,   -1,   -1, 2597,   -1,
+        -1, 2598,   -1, 2599,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2544,   -1,
-      2545,   -1,   -1,   -1,   -1,   -1,   -1, 2546,
+        -1,   -1,   -1,   -1, 2600,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2601,
+        -1,   -1,   -1,   -1, 2602,   -1,   -1, 2603,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2547, 2548,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2549,   -1,   -1,   -1,   -1,   -1,
-      2550,   -1,   -1,   -1, 2551,   -1,   -1,   -1,
-        -1,   -1,   -1, 2552,   -1,   -1,   -1,   -1,
-      2553, 2554, 2555,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2556,   -1, 2557,   -1,
-        -1, 2558,   -1,   -1,   -1,   -1,   -1, 2559,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2560,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2561,   -1,   -1,
-        -1,   -1,   -1,   -1, 2562,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2563, 2564,   -1,   -1,   -1,
-        -1, 2565,   -1,   -1,   -1,   -1,   -1, 2566,
-        -1,   -1,   -1,   -1,   -1,   -1, 2567,   -1,
-      2568,   -1, 2569,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2570,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2571,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2572, 2573,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2574,
-        -1, 2575,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2576,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2577,   -1,   -1,   -1,   -1,   -1,
-      2578,   -1,   -1, 2579,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2580,   -1,   -1,   -1, 2581,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2582,
-        -1, 2583, 2584, 2585,   -1, 2586,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2587,   -1, 2588,   -1, 2589,
-        -1,   -1,   -1, 2590,   -1,   -1,   -1, 2591,
-        -1,   -1,   -1,   -1, 2592,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2593,   -1, 2594,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2595,   -1,   -1,
-        -1,   -1,   -1, 2596, 2597,   -1, 2598,   -1,
-        -1,   -1,   -1,   -1,   -1, 2599, 2600,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2601, 2602,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2603,   -1, 2604,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2605,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2606,   -1,   -1,   -1,   -1,
-      2607, 2608,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2609,   -1,   -1,   -1,
-      2610,   -1,   -1,   -1,   -1, 2611,   -1,   -1,
-        -1,   -1, 2612,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2613,   -1,
-        -1,   -1,   -1,   -1,   -1, 2614,   -1,   -1,
-        -1,   -1, 2615, 2616,   -1,   -1,   -1, 2617,
-        -1,   -1,   -1, 2618,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2619,   -1,   -1,
-        -1,   -1,   -1,   -1, 2620,   -1,   -1,   -1,
-        -1,   -1, 2621,   -1,   -1,   -1,   -1,   -1,
-      2622,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2623,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2624,   -1, 2625,
-        -1,   -1,   -1, 2626,   -1, 2627,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2604,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2605,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2606,
+        -1,   -1,   -1,   -1,   -1, 2607,   -1,   -1,
+        -1,   -1,   -1,   -1, 2608,   -1, 2609,   -1,
+        -1,   -1, 2610,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2611,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2612,   -1,
+      2613,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2614, 2615,   -1,   -1,   -1, 2616,   -1,   -1,
+        -1, 2617, 2618,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2619,   -1,   -1, 2620,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2621,   -1, 2622, 2623,   -1,   -1,
+        -1,   -1,   -1,   -1, 2624, 2625,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2628,   -1, 2629,   -1,   -1, 2630,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2626,
+        -1,   -1,   -1, 2627,   -1,   -1,   -1,   -1,
+        -1,   -1, 2628,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2629,   -1,   -1,   -1, 2630, 2631,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2631, 2632,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2633,   -1,   -1,
-      2634,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2635,   -1,   -1,   -1,
-        -1,   -1,   -1, 2636,   -1,   -1,   -1,   -1,
+        -1,   -1, 2632,   -1,   -1,   -1,   -1, 2633,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2637, 2638, 2639,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2634,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2640,   -1,   -1, 2641,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2642,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2643,   -1,
+        -1,   -1,   -1, 2635,   -1, 2636,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2644,   -1,   -1,
-        -1, 2645,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2646,   -1,   -1, 2647,   -1,   -1,   -1,
+      2637,   -1,   -1,   -1, 2638,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2639,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2640,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2648,   -1, 2649,   -1,   -1,
-        -1,   -1,   -1, 2650,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2641,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2642,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2651,   -1,   -1,   -1,   -1, 2652,   -1,
-        -1,   -1,   -1,   -1,   -1, 2653,   -1,   -1,
+        -1, 2643,   -1,   -1,   -1, 2644,   -1,   -1,
+        -1,   -1,   -1, 2645,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2646,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2647,   -1,
+      2648,   -1,   -1, 2649,   -1,   -1,   -1,   -1,
+      2650, 2651, 2652,   -1,   -1,   -1,   -1, 2653,
       2654,   -1,   -1,   -1,   -1, 2655,   -1,   -1,
-        -1, 2656,   -1,   -1,   -1,   -1,   -1,   -1,
-      2657,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2656,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2657,   -1,   -1,   -1,
+      2658,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2658,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2659, 2660,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2659,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2661,   -1,
+        -1,   -1,   -1,   -1, 2662,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2663,   -1,
+        -1,   -1,   -1,   -1,   -1, 2664,   -1, 2665,
+        -1,   -1,   -1, 2666,   -1, 2667,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2660,   -1,   -1,   -1, 2661, 2662,   -1,   -1,
+        -1, 2668,   -1, 2669,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2663,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2664,   -1, 2665,   -1,
-        -1,   -1,   -1,   -1,   -1, 2666,   -1,   -1,
+      2670,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2667,   -1,   -1, 2668,   -1,
-        -1, 2669,   -1, 2670,   -1,   -1,   -1,   -1,
         -1, 2671,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1, 2672,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2673,   -1,   -1,   -1,
-        -1,   -1,   -1, 2674, 2675,   -1,   -1,   -1,
+        -1,   -1, 2673, 2674,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2675, 2676,   -1,
+        -1,   -1,   -1,   -1, 2677,   -1,   -1,   -1,
+        -1, 2678,   -1,   -1,   -1,   -1,   -1,   -1,
+      2679,   -1, 2680,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2681, 2682,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2683,   -1,   -1,   -1,   -1,
+        -1,   -1, 2684,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2685,   -1, 2686,
+        -1,   -1,   -1,   -1, 2687,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2676,   -1,   -1, 2677, 2678,   -1,   -1,
-        -1, 2679,   -1,   -1,   -1,   -1,   -1, 2680,
-        -1, 2681, 2682,   -1,   -1, 2683,   -1,   -1,
-      2684,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2685,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2686,
-        -1, 2687,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2688,   -1,   -1,   -1,   -1,   -1,   -1,
+      2689,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2688,
-      2689,   -1, 2690,   -1,   -1, 2691,   -1, 2692,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2693,   -1,   -1,   -1,
+        -1, 2690,   -1, 2691,   -1,   -1, 2692,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2693,   -1,
+        -1,   -1, 2694,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2694,   -1,   -1,   -1,
+        -1, 2695,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2695,   -1,   -1, 2696,   -1,   -1,   -1,
+        -1,   -1, 2696,   -1,   -1,   -1,   -1, 2697,
+      2698, 2699,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2700,
+        -1, 2701,   -1,   -1,   -1,   -1,   -1,   -1,
+      2702, 2703,   -1,   -1,   -1, 2704,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2697, 2698,   -1,   -1,   -1,
-      2699,   -1, 2700, 2701,   -1, 2702,   -1,   -1,
-        -1,   -1,   -1, 2703,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2705, 2706,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2707,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2704,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2705, 2706,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2708,   -1,   -1,   -1, 2709,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2707,   -1,   -1, 2708, 2709,   -1,
-        -1, 2710,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2711,   -1,
+        -1,   -1, 2710,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2711,   -1,   -1,   -1,   -1,   -1,
         -1, 2712,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2713,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2713, 2714,
+      2715,   -1,   -1,   -1,   -1, 2716,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2717,   -1,   -1, 2718,   -1,
+        -1, 2719,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2720,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2721,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2722, 2723,   -1,
+        -1, 2724,   -1,   -1, 2725, 2726,   -1,   -1,
+        -1, 2727,   -1, 2728,   -1,   -1,   -1,   -1,
+      2729,   -1,   -1,   -1, 2730, 2731,   -1,   -1,
+        -1,   -1,   -1, 2732, 2733, 2734,   -1,   -1,
+        -1, 2735,   -1,   -1, 2736,   -1,   -1, 2737,
+        -1,   -1,   -1,   -1,   -1,   -1, 2738,   -1,
+        -1, 2739,   -1,   -1,   -1, 2740,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2741,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2742,
+        -1,   -1,   -1, 2743, 2744,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2745,   -1,   -1,   -1, 2746,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2747,   -1,   -1,   -1, 2748,   -1,   -1, 2749,
+        -1, 2750,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2751,   -1,
+        -1, 2752,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2753, 2754,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2714,   -1,   -1,   -1, 2715, 2716,   -1,
-        -1,   -1,   -1,   -1, 2717, 2718,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2719,   -1,   -1,
-        -1,   -1,   -1, 2720,   -1, 2721,   -1,   -1,
-        -1,   -1,   -1,   -1, 2722,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2723,   -1,   -1,   -1, 2724, 2725,   -1,   -1,
-        -1,   -1,   -1,   -1, 2726, 2727,   -1,   -1,
-        -1,   -1,   -1, 2728,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2729,   -1, 2730,   -1,   -1,   -1,
-      2731,   -1,   -1,   -1,   -1, 2732,   -1,   -1,
-      2733,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2734, 2735,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2736,   -1,   -1,   -1,   -1,   -1,
-      2737,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2738,   -1,   -1,   -1,   -1,   -1,   -1, 2739,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2740,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2741,
-        -1,   -1,   -1,   -1,   -1,   -1, 2742,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2743,   -1,   -1,   -1, 2744,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2745,   -1,   -1,   -1,
-        -1,   -1, 2746,   -1,   -1,   -1,   -1, 2747,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2748,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2749,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2750,   -1,   -1,   -1,   -1,
-      2751,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2752,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2753,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2754,   -1,   -1,   -1,
       2755,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2756, 2757,   -1, 2758,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2759,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2760,   -1,
-        -1,   -1,   -1,   -1,   -1, 2761,   -1,   -1,
-      2762,   -1, 2763,   -1,   -1,   -1, 2764,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2765,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2766,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2756,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2767,   -1,
-      2768,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2769,   -1,   -1,   -1,   -1, 2770,
-        -1,   -1, 2771,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2772,   -1,   -1,   -1,
+        -1, 2757, 2758,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2759,   -1,   -1, 2760, 2761,   -1,   -1, 2762,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2763,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2764,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2765,   -1,   -1,   -1,   -1,   -1,
+        -1, 2766,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2767,
+        -1,   -1,   -1,   -1,   -1, 2768,   -1,   -1,
+        -1,   -1, 2769, 2770,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2771, 2772,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2773,   -1,   -1,   -1,   -1, 2774,   -1,   -1,
+        -1,   -1,   -1,   -1, 2775,   -1,   -1, 2776,
+        -1, 2777,   -1,   -1, 2778,   -1, 2779,   -1,
+        -1,   -1,   -1,   -1,   -1, 2780,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2773,   -1,   -1,   -1,   -1,
-        -1, 2774,   -1, 2775,   -1, 2776,   -1,   -1,
-        -1,   -1,   -1, 2777,   -1,   -1,   -1,   -1,
-      2778,   -1,   -1,   -1,   -1,   -1,   -1, 2779,
-      2780,   -1, 2781,   -1,   -1, 2782,   -1,   -1,
+        -1,   -1,   -1,   -1, 2781,   -1,   -1,   -1,
+        -1, 2782,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1, 2783,   -1,   -1,   -1,
-        -1, 2784,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2785,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2786,   -1,   -1, 2787,
-        -1,   -1,   -1, 2788,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2789,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2790,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2784,   -1,   -1,
+        -1, 2785,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2786,   -1,
+        -1,   -1,   -1,   -1, 2787,   -1,   -1,   -1,
+      2788,   -1,   -1,   -1, 2789,   -1,   -1, 2790,
+      2791,   -1,   -1,   -1,   -1,   -1, 2792,   -1,
+        -1,   -1,   -1,   -1,   -1, 2793,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2791,   -1, 2792,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2793,
-        -1,   -1, 2794,   -1,   -1, 2795,   -1,   -1,
-        -1,   -1, 2796, 2797,   -1,   -1,   -1,   -1,
+        -1, 2794,   -1, 2795,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2796,   -1, 2797,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2798,   -1,   -1,   -1,   -1, 2799,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2800,   -1,   -1,
+      2798, 2799,   -1,   -1,   -1,   -1,   -1,   -1,
+      2800,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2801,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2802, 2803,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2804,
+        -1, 2805,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2806, 2807,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2808, 2809,   -1,   -1,   -1,   -1,
+      2810,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2801,   -1,
-        -1,   -1,   -1,   -1,   -1, 2802,   -1, 2803,
+        -1,   -1,   -1, 2811, 2812, 2813, 2814,   -1,
+        -1,   -1,   -1, 2815,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2804, 2805,   -1,   -1,
-        -1, 2806,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2807,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2808,   -1,   -1,
+        -1, 2816,   -1,   -1, 2817,   -1,   -1,   -1,
+        -1,   -1,   -1, 2818,   -1,   -1,   -1,   -1,
+        -1, 2819,   -1,   -1,   -1, 2820,   -1,   -1,
+        -1, 2821,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2809,   -1,   -1,   -1,   -1,
+        -1, 2822,   -1,   -1,   -1,   -1,   -1,   -1,
+      2823, 2824,   -1,   -1, 2825,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2810, 2811,   -1,   -1,   -1, 2812,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2813,   -1,   -1,   -1,   -1, 2814,
-      2815,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2816,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2817,
-      2818, 2819,   -1,   -1, 2820,   -1,   -1,   -1,
-      2821,   -1,   -1, 2822,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2823,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2824,
-        -1, 2825,   -1,   -1,   -1,   -1,   -1, 2826,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2827,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2826,   -1, 2827,   -1,
         -1,   -1,   -1,   -1, 2828,   -1,   -1,   -1,
-        -1,   -1,   -1, 2829,   -1,   -1, 2830,   -1,
+        -1,   -1, 2829,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2831,   -1,   -1,   -1, 2832,
-        -1,   -1, 2833,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2830,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2831,   -1,   -1, 2832,   -1, 2833,   -1,
         -1,   -1,   -1, 2834,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2835,   -1,   -1,   -1,
-      2836,   -1,   -1,   -1,   -1, 2837,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2838,   -1,
-      2839,   -1, 2840, 2841, 2842,   -1,   -1,   -1,
-        -1,   -1,   -1, 2843,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2844,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2845, 2846,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2835,   -1,   -1,   -1,   -1,   -1,
+        -1, 2836,   -1,   -1,   -1,   -1,   -1,   -1,
+      2837,   -1, 2838,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2839,   -1,
+      2840, 2841,   -1,   -1,   -1,   -1,   -1,   -1,
+      2842,   -1,   -1, 2843,   -1,   -1,   -1, 2844,
+        -1, 2845, 2846,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 2847,
+        -1,   -1,   -1, 2848,   -1,   -1,   -1, 2849,
+        -1,   -1, 2850,   -1,   -1,   -1,   -1,   -1,
+        -1, 2851,   -1,   -1, 2852,   -1,   -1, 2853,
+      2854,   -1,   -1,   -1, 2855,   -1,   -1,   -1,
+        -1,   -1,   -1, 2856,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2848,
-        -1,   -1,   -1,   -1, 2849,   -1, 2850,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2857,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2858,
+      2859,   -1, 2860,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2861,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2862,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2863,   -1, 2864,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2865,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2866,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2867,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2868,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2869,
+        -1,   -1, 2870,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2851, 2852,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2871,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2853,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2854,   -1,   -1,   -1, 2855,   -1,   -1,
-        -1, 2856,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2857,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2858,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2859,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2860,   -1,   -1,
-        -1, 2861,   -1, 2862,   -1, 2863,   -1,   -1,
-      2864,   -1,   -1, 2865,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2866,   -1, 2867,   -1,   -1,
-        -1,   -1, 2868,   -1,   -1,   -1,   -1, 2869,
-        -1, 2870,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2871,   -1,   -1,   -1,   -1, 2872,   -1,
+        -1,   -1,   -1,   -1,   -1, 2872,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2873,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2873,   -1,   -1,
         -1,   -1, 2874,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2875,   -1, 2876,   -1, 2877,
-        -1,   -1,   -1, 2878, 2879,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2875,   -1, 2876,   -1,
+        -1,   -1,   -1, 2877,   -1,   -1,   -1,   -1,
+        -1,   -1, 2878,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2879,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2880,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2881,   -1,   -1,   -1, 2882,   -1,   -1,   -1,
+      2883, 2884,   -1, 2885,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2886,   -1, 2887,   -1,   -1,   -1,   -1,
+        -1,   -1, 2888,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2889,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2890, 2891,   -1,   -1,   -1,   -1,
+        -1, 2892,   -1,   -1,   -1,   -1, 2893,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2894,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2880,   -1, 2881,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2882,
-      2883,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2895,   -1, 2896,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2897,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2884,   -1,   -1, 2885,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2886,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2887,   -1, 2888,   -1, 2889,   -1,   -1,
-        -1, 2890, 2891,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2892,   -1,
-      2893,   -1,   -1,   -1,   -1,   -1, 2894,   -1,
-        -1,   -1, 2895,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2896,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2897, 2898,   -1,
+      2898,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1, 2899,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2900,   -1,
+        -1, 2900,   -1, 2901,   -1, 2902,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2901,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2902,   -1,   -1,   -1,   -1,   -1,   -1,
-      2903,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2904,   -1,   -1,   -1,
-      2905,   -1,   -1,   -1,   -1,   -1,   -1, 2906,
+      2903, 2904,   -1,   -1,   -1,   -1,   -1, 2905,
+        -1,   -1,   -1,   -1,   -1,   -1, 2906,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2907,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2908,
+        -1, 2909,   -1, 2910,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2907,   -1, 2908,
+        -1,   -1, 2911,   -1,   -1,   -1,   -1,   -1,
+      2912,   -1,   -1,   -1,   -1, 2913,   -1,   -1,
+        -1, 2914,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2915, 2916,   -1,   -1,
+      2917,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2918,   -1,   -1,   -1,
+      2919,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2909,   -1,   -1, 2910, 2911,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2912,   -1, 2913,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2914,
-        -1, 2915,   -1,   -1,   -1,   -1, 2916,   -1,
-        -1,   -1, 2917,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2918,   -1,   -1,   -1,   -1, 2919,
         -1,   -1,   -1,   -1,   -1, 2920,   -1,   -1,
-        -1,   -1,   -1,   -1, 2921, 2922,   -1,   -1,
+      2921,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2922,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2923,   -1, 2924,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2925,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2926,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2927,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2928,   -1,
-      2929,   -1,   -1,   -1,   -1, 2930,   -1, 2931,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2932,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2933,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2934,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2923,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2935,   -1,   -1,   -1,   -1,   -1,
-        -1, 2936,   -1,   -1, 2937, 2938,   -1,   -1,
-        -1,   -1,   -1,   -1, 2939,   -1,   -1, 2940,
-        -1, 2941,   -1,   -1, 2942,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2943, 2944,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2945,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2924, 2925,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2926,   -1,   -1,   -1,
+        -1,   -1, 2927,   -1,   -1,   -1, 2928,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2929,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2930, 2931,
+        -1,   -1,   -1, 2932,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2933, 2934,   -1,   -1,   -1,
+        -1,   -1,   -1, 2935,   -1,   -1,   -1, 2936,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2937,   -1,   -1, 2938,   -1,   -1,
+        -1, 2939,   -1, 2940,   -1,   -1, 2941,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2942,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2943,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2944,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2945,   -1,   -1,
         -1,   -1,   -1, 2946,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2947,   -1,   -1,   -1,   -1, 2948,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2949,   -1, 2950,   -1,   -1, 2951,   -1,
-        -1,   -1,   -1, 2952, 2953,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2947,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2948,
+        -1,   -1,   -1, 2949,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2950,   -1,
+      2951,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2952,   -1,   -1,   -1, 2953,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
       2954,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      2955,   -1, 2956,   -1,   -1,   -1,   -1,   -1,
+      2957,   -1,   -1,   -1,   -1,   -1, 2958,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2955,   -1, 2956,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2957,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2958,   -1,   -1,   -1,   -1, 2959,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2960,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2961,
-        -1,   -1,   -1,   -1,   -1,   -1, 2962,   -1,
-        -1,   -1,   -1,   -1, 2963,   -1, 2964,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2965,   -1, 2966,   -1,   -1,   -1,   -1,
+        -1,   -1, 2959,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2960,   -1,   -1, 2961,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2962,
+        -1, 2963, 2964,   -1,   -1,   -1,   -1, 2965,
+        -1,   -1,   -1,   -1, 2966,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1, 2967,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 2968,   -1,   -1,
+        -1,   -1,   -1,   -1, 2969,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2970,   -1,   -1, 2971,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2968,   -1, 2969,   -1,   -1,   -1,   -1,
-        -1,   -1, 2970,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2971,   -1,   -1,   -1,   -1,
         -1, 2972,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2973,   -1,   -1, 2974,   -1,   -1,   -1,
+        -1, 2975, 2976,   -1,   -1, 2977,   -1, 2978,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2979,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2980,   -1,   -1, 2981,   -1, 2982,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 2983,   -1,   -1, 2984,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2985,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 2986,   -1,   -1,   -1, 2987,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 2988,   -1, 2989, 2990,
+      2991,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 2992, 2993,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 2994,   -1,
+        -1,   -1, 2995,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2996,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2997,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2973,   -1, 2974,   -1,   -1,
+        -1,   -1,   -1, 2998, 2999,   -1,   -1,   -1,
+      3000,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2975, 2976,   -1,   -1, 2977,
+        -1,   -1,   -1, 3001,   -1, 3002,   -1,   -1,
+        -1, 3003,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3004,   -1, 3005,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2978,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2979,   -1,
+        -1,   -1,   -1,   -1, 3006,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3007,
+        -1,   -1,   -1, 3008, 3009,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3010,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      2980,   -1,   -1, 2981,   -1,   -1,   -1, 2982,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 2983,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2984,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2985,   -1, 2986,   -1, 2987,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 2988,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 2989,   -1,   -1,   -1,   -1,
+      3011,   -1,   -1,   -1,   -1, 3012,   -1,   -1,
+        -1,   -1,   -1, 3013,   -1,   -1,   -1,   -1,
+      3014, 3015,   -1, 3016,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3017,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3018,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3019, 3020,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2990,   -1,
-        -1, 2991,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3021, 3022,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3023,   -1,
+        -1,   -1,   -1,   -1, 3024,   -1,   -1,   -1,
+      3025,   -1,   -1, 3026,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3027,   -1, 3028,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 2992,   -1,   -1,   -1,
+        -1,   -1, 3029,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3030,   -1,   -1,
+        -1,   -1,   -1, 3031,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2993,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 2994,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2995, 2996, 2997,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 2998,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 2999,   -1,
-        -1,   -1,   -1,   -1, 3000, 3001,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3002,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3003,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3004,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3005,   -1,
-        -1,   -1,   -1, 3006, 3007,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3008,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3009,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3010,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3011,
-        -1,   -1,   -1,   -1,   -1, 3012,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3013,   -1,   -1,
-        -1,   -1,   -1,   -1, 3014,   -1,   -1,   -1,
-      3015,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3016,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3017,   -1,   -1, 3018,   -1,   -1,
+        -1,   -1,   -1, 3032,   -1,   -1,   -1,   -1,
+        -1, 3033,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3034,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3019,   -1,
-        -1,   -1,   -1, 3020,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3021,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3022,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3023,   -1,   -1,   -1,   -1,   -1,   -1,
-      3024, 3025,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3026,
-        -1,   -1,   -1,   -1,   -1,   -1, 3027,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3028,
-      3029,   -1,   -1,   -1,   -1,   -1, 3030,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3031,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3032,   -1, 3033,
-        -1,   -1,   -1,   -1,   -1,   -1, 3034,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3035,   -1,
-        -1,   -1, 3036,   -1,   -1,   -1,   -1,   -1,
-      3037, 3038,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3039, 3040,   -1, 3041,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3042,
-        -1,   -1,   -1,   -1,   -1,   -1, 3043,   -1,
-      3044,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3035,   -1,   -1,   -1,   -1, 3036, 3037,   -1,
+      3038,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3039, 3040, 3041,   -1,
+        -1,   -1,   -1,   -1, 3042,   -1, 3043,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3045,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3044,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3045,
         -1,   -1,   -1, 3046,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1, 3047,   -1,   -1,
-        -1,   -1,   -1,   -1, 3048, 3049,   -1,   -1,
-        -1, 3050, 3051,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3052,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3053,   -1,
+        -1,   -1,   -1,   -1, 3048,   -1,   -1,   -1,
+        -1,   -1,   -1, 3049,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3050,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3051,
+        -1,   -1,   -1,   -1,   -1, 3052,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3053,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1, 3054,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1, 3055,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3056, 3057,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3056,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3057,   -1,   -1,
-        -1, 3058, 3059,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3058,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3060,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3059,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3061,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3062,
+        -1,   -1,   -1,   -1, 3060,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3063,   -1,   -1,
+        -1,   -1, 3061,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3064,
-        -1,   -1, 3065,   -1,   -1,   -1, 3066,   -1,
+        -1,   -1,   -1, 3062, 3063, 3064,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3067,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3068,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3065,   -1,
+        -1,   -1,   -1,   -1,   -1, 3066,   -1,   -1,
+        -1,   -1,   -1, 3067,   -1,   -1, 3068,   -1,
         -1,   -1,   -1,   -1, 3069,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3070, 3071,   -1,   -1,
-      3072,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3073,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3074,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3070,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3071,   -1,
+        -1,   -1,   -1, 3072,   -1,   -1, 3073,   -1,
+      3074,   -1,   -1,   -1, 3075,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3076,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3075,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3076,   -1,   -1, 3077,   -1,
-        -1,   -1,   -1,   -1, 3078,   -1,   -1,   -1,
-        -1,   -1, 3079,   -1, 3080,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3077,   -1,
+        -1,   -1,   -1, 3078,   -1,   -1,   -1, 3079,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3080,
+      3081,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3081, 3082,   -1, 3083,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3084,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3085,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3086,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3087,
-      3088,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3089,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3090,
-        -1,   -1,   -1,   -1,   -1, 3091,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3092,   -1,   -1,
-        -1,   -1, 3093,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3094,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3095,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3096,   -1,   -1,   -1,
-      3097,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3098,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3099,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3100,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3101,
-        -1,   -1, 3102,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3103,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3104,
-        -1, 3105,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3106,   -1,   -1,
-        -1,   -1,   -1, 3107,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3082,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3108,
-        -1, 3109,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3110,   -1, 3111,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3112,   -1, 3113,   -1,   -1,
+      3083,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3084,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3114,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3085,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3086,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3115,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3116, 3117,   -1,
-      3118,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3119,
+        -1,   -1,   -1,   -1, 3087,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3088,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3089,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3090,   -1,   -1,   -1, 3091,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3120,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3092,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3093,
+        -1, 3094,   -1,   -1,   -1,   -1, 3095,   -1,
+        -1,   -1,   -1, 3096,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3097,   -1,   -1,   -1,
+        -1, 3098,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3121,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3122, 3123,   -1,   -1,
-        -1, 3124,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3099,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3100,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3101,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3102,   -1,   -1,   -1,
+        -1, 3103,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3104,   -1,   -1, 3105, 3106,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3107,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3108, 3109,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3110,   -1,   -1,
+      3111,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3125,   -1,
+        -1,   -1, 3112,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3113,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3114,   -1,   -1,   -1, 3115,   -1, 3116,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3117,   -1, 3118,   -1,   -1,
+        -1, 3119,   -1,   -1,   -1,   -1,   -1,   -1,
+      3120,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3121,   -1,   -1, 3122,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3123,   -1, 3124,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3125,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 3126,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3127,   -1,   -1,   -1,
-        -1, 3128,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3129,   -1,
-        -1,   -1,   -1, 3130,   -1,   -1,   -1,   -1,
+        -1, 3127, 3128,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3129,   -1,   -1,
+      3130,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1, 3131,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3132,   -1,
+        -1, 3132,   -1, 3133,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3133,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3134,   -1,   -1,
+        -1,   -1, 3134,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3135,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3136,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3135,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3136,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3137,   -1,   -1,   -1,   -1, 3138,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3137,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3138,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1, 3139,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3140,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3140,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3141,
+        -1,   -1,   -1, 3141,   -1,   -1,   -1,   -1,
+        -1,   -1, 3142,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3143,   -1,   -1,
+        -1, 3144,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3142,   -1, 3143,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3144,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1, 3145,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3146,   -1,   -1,   -1,
+        -1, 3147,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3146, 3147,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3148,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3149,   -1,   -1,
-        -1, 3150,   -1,   -1,   -1,   -1, 3151,   -1,
-        -1,   -1,   -1,   -1,   -1, 3152,   -1,   -1,
-        -1,   -1,   -1,   -1, 3153,   -1,   -1,   -1,
-        -1, 3154,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3155,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3156,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3157,   -1,   -1,
+        -1,   -1,   -1, 3148,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3158, 3159, 3160,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3149,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3150,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3151,   -1, 3152,   -1, 3153,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3154,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3155,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -10159,310 +10321,312 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3161,   -1,   -1,
-        -1,   -1,   -1, 3162,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3163,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3164,   -1,   -1,   -1,   -1,
+      3156,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3157,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3158,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3165,   -1,   -1,   -1,   -1,   -1,
-      3166,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3159,   -1,   -1, 3160,   -1,
+      3161,   -1,   -1,   -1,   -1,   -1, 3162,   -1,
+        -1,   -1, 3163,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3164,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3165,   -1,   -1,   -1,
+        -1, 3166, 3167,   -1,   -1, 3168, 3169,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3170,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3171,   -1,   -1,   -1,   -1, 3172,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3167,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3168,   -1,   -1,
-        -1,   -1,   -1, 3169,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3170,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3171,   -1,
-      3172,   -1,   -1,   -1,   -1,   -1, 3173,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3174,
-        -1,   -1,   -1, 3175,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3173,   -1,   -1,   -1,
+        -1, 3174,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3176,   -1, 3177,   -1,
+      3175,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3176,   -1,   -1,
+      3177,   -1,   -1,   -1,   -1,   -1,   -1, 3178,
+        -1,   -1, 3179,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3178,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3179,   -1,   -1, 3180,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3181,
+        -1,   -1,   -1, 3180,   -1,   -1,   -1,   -1,
+      3181,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1, 3182,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3183,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3184,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3183,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3185,   -1,   -1,   -1, 3186,   -1,   -1,
-      3187,   -1,   -1,   -1, 3188,   -1,   -1,   -1,
-      3189,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3184,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3190,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3191,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3185,
+        -1, 3186,   -1,   -1,   -1,   -1,   -1,   -1,
+      3187,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3188,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3189, 3190,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3191,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3192,   -1,   -1,   -1, 3193,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3194,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3195,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3196,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3197,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3198,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3199,   -1,
+        -1,   -1,   -1, 3200,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3201,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3202, 3203,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3204,   -1, 3205,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3206,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3207,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3192,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3208,
+        -1,   -1,   -1, 3209, 3210,   -1,   -1,   -1,
+        -1,   -1,   -1, 3211,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3193,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3194,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3195,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3196,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3197,   -1,   -1,   -1,   -1, 3198,   -1,   -1,
-        -1,   -1,   -1,   -1, 3199,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3200,   -1, 3201,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3202,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3203,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3204, 3205,
-        -1,   -1,   -1, 3206,   -1, 3207,   -1, 3208,
-        -1, 3209,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3210,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3211,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3212,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3212,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1, 3213,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3214,   -1,   -1, 3215,   -1, 3216,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3214,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3215,   -1,   -1,   -1,   -1,   -1, 3216,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1, 3217,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1, 3218,   -1, 3219,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3220,   -1,   -1,   -1, 3221,
-        -1,   -1,   -1,   -1, 3222,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3223,   -1,   -1,
+        -1, 3220,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3221, 3222,   -1,   -1,   -1,   -1, 3223,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3224,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3225,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3226,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3224,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3225,   -1,   -1,   -1,   -1, 3226,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3227,   -1,   -1,   -1,
+        -1,   -1,   -1, 3227,   -1,   -1,   -1,   -1,
+      3228,   -1,   -1,   -1,   -1,   -1, 3229,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3230,
+        -1, 3231,   -1,   -1,   -1, 3232,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3233,   -1,   -1,   -1, 3234,   -1,
+        -1,   -1,   -1, 3235,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3236,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3237,   -1,   -1,   -1, 3238,   -1,
+        -1,   -1, 3239, 3240, 3241,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3228,   -1,   -1,   -1,   -1,   -1,
-        -1, 3229,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3242,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3243,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3230,   -1,   -1, 3231,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3232,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3233,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3234,
-        -1,   -1,   -1,   -1,   -1, 3235,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3236,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3237,
-        -1,   -1,   -1,   -1, 3238,   -1,   -1,   -1,
-        -1,   -1,   -1, 3239,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3240,   -1,   -1,   -1,   -1,   -1, 3241,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3242,   -1,   -1, 3243,   -1,   -1,
-        -1,   -1,   -1, 3244,   -1,   -1,   -1,   -1,
-        -1, 3245,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3244, 3245,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1, 3246,   -1,   -1,
-        -1,   -1,   -1,   -1, 3247,   -1,   -1, 3248,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3247,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3248,   -1,
         -1,   -1,   -1,   -1, 3249,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3250,   -1, 3251,   -1,   -1,   -1,
+        -1,   -1,   -1, 3250,   -1, 3251,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3252,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3253,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3254,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3255,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3256,   -1,   -1,   -1,   -1,   -1,   -1,
+      3257,   -1,   -1,   -1,   -1,   -1, 3258,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3252,   -1,   -1,   -1,   -1,   -1,   -1, 3253,
-        -1,   -1,   -1,   -1, 3254,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3255,   -1,   -1,   -1,
+        -1,   -1,   -1, 3259,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3256,   -1,   -1,   -1,
-        -1,   -1, 3257,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3258,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3259,   -1,   -1,   -1,
+      3260,   -1,   -1, 3261,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3260,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3261,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1, 3262,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3263,
+        -1,   -1, 3263,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3264,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3265,   -1,   -1,
-        -1,   -1,   -1, 3266,   -1, 3267,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3264,   -1, 3265,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3266,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3267,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1, 3268,   -1,   -1,
-        -1,   -1,   -1,   -1, 3269,   -1,   -1,   -1,
-        -1, 3270,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3269, 3270,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 3271,
-        -1,   -1, 3272, 3273,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3274,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3275,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3276,   -1, 3277,
-        -1, 3278,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3279,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3280,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3272,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3281, 3282,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3283,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3284,
-        -1,   -1, 3285, 3286,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3287,   -1,
-        -1,   -1,   -1,   -1, 3288,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3273,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3274,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3275,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3276,   -1,   -1, 3277,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3278,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3279,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3280,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3281,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3282,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3283, 3284,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3285,   -1,
+        -1,   -1,   -1, 3286,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3287, 3288,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1, 3289,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3290,   -1,   -1,
-      3291,   -1,   -1,   -1,   -1,   -1, 3292,   -1,
-        -1,   -1, 3293, 3294,   -1, 3295,   -1,   -1,
-      3296,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3297,   -1,   -1,   -1,
-      3298,   -1,   -1, 3299,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3300,
-        -1,   -1,   -1, 3301,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3302,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3303,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3304,   -1,   -1,
-        -1,   -1,   -1, 3305,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3306,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3290,   -1,
+        -1,   -1,   -1, 3291,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3292,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3307,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3308,   -1,
-        -1, 3309, 3310,   -1,   -1, 3311,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3312,   -1,   -1,
-        -1,   -1, 3313,   -1,   -1,   -1,   -1,   -1,
+        -1, 3293,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3314,   -1,   -1,   -1,   -1, 3315,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3316,   -1,   -1,   -1,   -1, 3317, 3318,
+        -1,   -1,   -1,   -1,   -1, 3294,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3319,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3295,
+        -1,   -1, 3296,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3297,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3298,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3299,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3300,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3301,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3302, 3303,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3304,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3305,   -1,   -1, 3306,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3320,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3307,   -1,   -1,   -1,   -1,   -1,
+      3308,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3309,   -1,   -1,   -1,   -1,   -1,   -1,
+      3310,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3321,   -1,   -1,   -1,   -1,   -1, 3322,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3311,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -10471,147 +10635,200 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3312,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3313,   -1, 3314,   -1, 3315,   -1,
+      3316,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3317,   -1,   -1,   -1,
+        -1,   -1, 3318,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3319,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3320,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3321,   -1,   -1,
+        -1,   -1, 3322,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1, 3323,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3324,   -1,   -1,   -1, 3325,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3326,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3327,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3324,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3325,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3326,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3327,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3328,   -1,   -1,
+        -1, 3329,   -1,   -1,   -1,   -1,   -1, 3330,
+        -1, 3331,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3332,   -1,   -1,   -1, 3333,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3328,   -1,
+        -1,   -1, 3334,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3335,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3336,   -1,   -1,   -1,   -1,
+      3337,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3329,   -1,   -1,   -1, 3330,   -1,   -1, 3331,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3332,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3333,   -1,
+        -1,   -1,   -1,   -1, 3338,   -1, 3339,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3334,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3340,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3335,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3341,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3342,   -1,   -1,   -1,   -1,   -1,
+      3343,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3336,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3337,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3338,   -1,
-        -1,   -1,   -1,   -1, 3339, 3340,   -1,   -1,
-        -1,   -1, 3341,   -1,   -1,   -1,   -1,   -1,
-        -1, 3342,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3343,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3344,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3344,
+        -1,   -1,   -1,   -1,   -1, 3345,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3345,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3346,   -1,   -1,
+        -1,   -1, 3347,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3348,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3349,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3346,   -1, 3347,   -1, 3348,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3350, 3351,   -1,   -1,   -1,
+        -1,   -1, 3352,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3353,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3354,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3349,   -1,   -1,   -1,
+        -1,   -1,   -1, 3355,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3356,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3350,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3351,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3352,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3353,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3354,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3355,   -1,   -1,   -1, 3356,   -1,   -1,   -1,
+        -1,   -1,   -1, 3357,   -1,   -1,   -1, 3358,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3357,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3358,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3359,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3359,
         -1,   -1, 3360,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3361,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3361,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3362,   -1,   -1,
+      3362,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3363,   -1,   -1,   -1,   -1,   -1,
-        -1, 3364,   -1,   -1,   -1, 3365, 3366,   -1,
+        -1,   -1,   -1, 3363,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3367, 3368,   -1,   -1,   -1,   -1,   -1,
-        -1, 3369,   -1, 3370,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3371,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3364,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3365,   -1,   -1,   -1, 3366,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3367,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3368,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3369,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3370, 3371,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1, 3372,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3373,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3374,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3375,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3373,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3374,   -1,   -1,   -1,   -1,   -1,   -1, 3375,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
       3376,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3377,   -1, 3378,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3379,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3380,   -1,   -1, 3381,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3382,   -1,
+        -1,   -1, 3377,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3378,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3379,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3380,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -10622,43 +10839,34 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3381,   -1,   -1,   -1,   -1, 3382,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3383, 3384,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3383,   -1,
+        -1,   -1,   -1, 3385,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3386,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3387,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3388,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3384,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3385,
+        -1,   -1,   -1, 3389,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3390,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3386,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3387,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3388,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3389,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3390,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3391,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3391,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -10666,81 +10874,51 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 3392,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3393,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3393,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3394,
+      3394,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3395,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3396,   -1, 3397,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3398,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3399,   -1,   -1,   -1,   -1,
+        -1, 3395,   -1,   -1,   -1, 3396,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3400,   -1,   -1,   -1,   -1,
+      3397,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3401,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3398,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3399,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3400,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3401,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3402,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3403,   -1, 3404,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3405,
+        -1,   -1,   -1,   -1, 3406,   -1,   -1,   -1,
+      3407,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3408,   -1,   -1,   -1,
+        -1, 3409,   -1,   -1,   -1,   -1, 3410, 3411,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3412,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3402,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3403,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3404,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3405,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3406,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3407,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3408,   -1,
-        -1,   -1, 3409,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3410,   -1,   -1, 3411,   -1,   -1, 3412,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3413,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3413,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -10748,54 +10926,51 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 3414,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3415,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3416,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3417,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3418,   -1,
+        -1,   -1,   -1,   -1, 3415,   -1,   -1, 3416,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3417,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3418,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3419,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3419,
+      3420,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3420,   -1,   -1,   -1,   -1,   -1,   -1, 3421,
+        -1,   -1,   -1,   -1,   -1, 3421,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1, 3422,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3423,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3423,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1, 3424,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3425,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3426,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3427,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3428,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3425,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3426,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3427,
-        -1,   -1,   -1, 3428,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3429,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3430,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -10804,17 +10979,19 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3431,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3432,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3433,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3429,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -10823,143 +11000,130 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3434,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3430,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3431,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3432,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3433,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3434,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3435,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3435,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
       3436,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3437,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3437,   -1,   -1,   -1,   -1,
+        -1,   -1, 3438,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3438,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3439,
-        -1,   -1,   -1, 3440,   -1,   -1,   -1,   -1,
+      3439,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3440,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3441,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3441,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3442,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3443, 3444,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3445,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3446,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3442,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3447,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3448,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3449,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3443,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3450,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3451,   -1,   -1,   -1,
+        -1, 3452,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3453,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3444,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3454,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3445,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3446,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3455,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3456,   -1,   -1,   -1,
+      3457,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3458,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3459,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3460,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3447,   -1,   -1,   -1,   -1,
-      3448,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3461,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3462,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3463,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3464,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3449,
+        -1,   -1, 3465,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3466,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3467,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3450,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3468,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3451,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3469,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3470,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3471,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3452,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -10968,163 +11132,11 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3472,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3453,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3454,   -1,   -1,   -1,   -1,
-        -1,   -1, 3455,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3456,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3457,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3458,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3459,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3460,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3461,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3462,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3463,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3464,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3465,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3466,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3467,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3468,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3469,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3470,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3471,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3472,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3473,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3474,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3473,   -1,
+        -1,   -1,   -1, 3474,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11132,27 +11144,25 @@
         -1,   -1, 3475,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3476,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3476,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1, 3477,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3478,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3479,   -1,
+      3478,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3480,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3479,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3480,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11161,58 +11171,25 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3482,   -1,   -1,   -1, 3483,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3482,   -1,
+        -1,   -1,   -1,   -1,   -1, 3484,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3485,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3486,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3483,   -1,   -1,   -1,   -1,
+        -1, 3487,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3484, 3485,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3486,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3487,   -1,   -1,   -1,   -1,   -1,
-      3488,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3489,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3488,   -1,   -1,   -1,   -1,   -1, 3489,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1, 3490,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11226,8 +11203,20 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3491,   -1, 3492,   -1,   -1,   -1,
-        -1, 3493,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3491,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3492,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3493,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3494,   -1,   -1,   -1, 3495, 3496,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3497,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11236,194 +11225,97 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3494,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3495,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3496,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3497,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1, 3498,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3499,   -1,   -1,   -1,   -1,   -1,
+        -1, 3499, 3500,   -1,   -1,   -1,   -1, 3501,
+        -1,   -1, 3502,   -1,   -1,   -1, 3503,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3504,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3500,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3501,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3502,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3503,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3504,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3505,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3505,
         -1,   -1,   -1, 3506,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3507,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3507,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1, 3508,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3509,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3509,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 3510,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1, 3511,   -1,   -1,   -1,   -1,   -1,
+        -1, 3512,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3513,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3514,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3515,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3512,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3516,   -1,   -1,
+      3517,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3518,   -1,
+        -1,   -1,   -1,   -1,   -1, 3519,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3513,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3520,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3521,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3522,   -1,   -1,   -1,   -1, 3523,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3524,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3525,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3514,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3515,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3526,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3527,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3528,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3516,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3517,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3529,   -1,   -1, 3530,   -1,   -1,   -1,
+        -1, 3531,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3532,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3533,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3518,   -1,   -1,   -1,
+      3534,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3535,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3536,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11435,259 +11327,52 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3537,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3538,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3539,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3540,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3541,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3542,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3543,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3544,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3545,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3519,
-        -1, 3520,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3521,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3522,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3523,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3524,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3525,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3526,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3527,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3528,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3529,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3530,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3531,   -1,   -1, 3532,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3533,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3534,   -1,   -1,   -1,
-        -1, 3535,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3536,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3537,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3538,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3539,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3540,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3541,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3542,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3543,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3544,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3545,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3546,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3546,   -1,   -1,   -1,
         -1, 3547,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3548,   -1,   -1,   -1,
+        -1,   -1, 3549,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3550,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3551,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11695,6 +11380,7 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3552,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11702,75 +11388,17 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3553,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3548,   -1, 3549,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3550,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3551,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3552,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3553,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3554,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3554,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1, 3555,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3556,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3557,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3558,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11782,18 +11410,22 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3559,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3560,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3561,
+        -1,   -1,   -1, 3562,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3556,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3563,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11801,11 +11433,13 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3564,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3565,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11814,7 +11448,6 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3557,   -1,   -1,   -1, 3558,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11826,45 +11459,48 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3566,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3559,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3560,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3567,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3568,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3569,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3570,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3561,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3571,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11882,23 +11518,22 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3562,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3563,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3564,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3572,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3573,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -11911,114 +11546,22 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3574,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3575,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3565,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3566,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3567,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3568,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3569,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3570,   -1,   -1,   -1,   -1, 3571,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3572,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3573,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3574,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3575,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3576,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3576,   -1,   -1,
         -1,   -1,   -1, 3577,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12028,25 +11571,20 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3578,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3578,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 3579,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3580,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3581,   -1,   -1,   -1, 3582,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3583,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12067,15 +11605,17 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3580,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3581,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3582,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3584,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12083,11 +11623,11 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3585,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3583,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12099,13 +11639,16 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3584,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3585,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3586,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12126,6 +11669,7 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3587,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12141,210 +11685,41 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3588,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3589,   -1,   -1, 3590,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3586,   -1,
+        -1, 3591,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3592,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3593,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3594,   -1, 3595,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3596,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3597,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3587,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3588,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3598,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3589,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3590,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3591,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3592,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3593,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3594,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3595,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3596,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-      3597,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3598,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3599,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3599,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12357,17 +11732,21 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3601,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3602,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3603,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3604,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3601,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3605,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12375,6 +11754,7 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3606,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12389,262 +11769,15 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3607,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3608,   -1, 3609,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3602,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3603,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3604,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3605,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3606,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3607,   -1,   -1,   -1,
-        -1, 3608,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1, 3609,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3610,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3610,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12654,6 +11787,7 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1, 3611,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3612,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12662,11 +11796,12 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3613,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3614,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3612,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12680,6 +11815,7 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3615,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12687,96 +11823,22 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3616,
+        -1,   -1,   -1,   -1,   -1,   -1, 3617,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3613,   -1,
-        -1,   -1,   -1,   -1,   -1, 3614,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3615,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3616,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3617,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3618,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3618,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 3619,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3620,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3621,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12784,12 +11846,12 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3622,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3620,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12815,6 +11877,7 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3623,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12834,6 +11897,7 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3624,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12843,6 +11907,7 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3625,   -1,   -1,   -1,   -1, 3626,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -12855,344 +11920,7 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3621,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3622,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3623,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3624,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1, 3625,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1, 3626,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3627,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3627,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -13204,7 +11932,2453 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1, 3628,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3629, 3630,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3629,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3630,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3631,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3632,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3633,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3634,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3635,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3636,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3637,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3638,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3639,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3640,   -1,
+        -1,   -1,   -1,   -1, 3641,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3642,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3643,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3644,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3645,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3646,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3647,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3648,   -1,
+        -1, 3649,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3650,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3651,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3652,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3653,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3654,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3655,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3656,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3657,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3658,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3659,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3660,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3661,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3662,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3663,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3664,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3665,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3666,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3667,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3668,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3669,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3670,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3671,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3672,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3673,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3674,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3675,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3676,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1, 3677,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3678,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3679,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3680,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3681,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3682,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3683,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3684,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3685,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1, 3686,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3687,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3688,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3689,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3690,   -1,
+        -1,   -1,   -1,   -1, 3691,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1, 3692,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1, 3693,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1, 3694,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+      3695,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1, 3696,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+        -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -13300,8 +14474,6 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1, 3631,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3632,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -13355,7 +14527,6 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1, 3633,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -13503,7 +14674,6 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3634,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -13578,7 +14748,6 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1,   -1, 3635,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
@@ -14367,7 +15536,7 @@
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
         -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
-        -1,   -1,   -1,   -1,   -1,   -1, 3636
+        -1,   -1,   -1, 3697
     };
 
   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
@@ -14389,5 +15558,5 @@
     }
   return 0;
 }
-#line 3651 "effective_tld_names.gperf"
+#line 3712 "effective_tld_names.gperf"
 
diff --git a/net/base/effective_tld_names.dat b/net/base/effective_tld_names.dat
index b6de8d1..0298da0 100644
--- a/net/base/effective_tld_names.dat
+++ b/net/base/effective_tld_names.dat
@@ -1,30 +1,30 @@
 // ***** BEGIN LICENSE BLOCK *****
 // Version: MPL 1.1/GPL 2.0/LGPL 2.1
-// 
-// The contents of this file are subject to the Mozilla Public License Version 
-// 1.1 (the "License"); you may not use this file except in compliance with 
-// the License. You may obtain a copy of the License at 
+//
+// The contents of this file are subject to the Mozilla Public License Version
+// 1.1 (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
 // http://www.mozilla.org/MPL/
-// 
+//
 // Software distributed under the License is distributed on an "AS IS" basis,
 // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 // for the specific language governing rights and limitations under the
 // License.
-// 
+//
 // The Original Code is the Public Suffix List.
-// 
+//
 // The Initial Developer of the Original Code is
 // Jo Hermans <jo.hermans@gmail.com>.
 // Portions created by the Initial Developer are Copyright (C) 2007
 // the Initial Developer. All Rights Reserved.
-// 
+//
 // Contributor(s):
 //   Ruben Arakelyan <ruben@wackomenace.co.uk>
 //   Gervase Markham <gerv@gerv.net>
 //   Pamela Greene <pamg.bugs@gmail.com>
 //   David Triendl <david@triendl.name>
 //   The kind representatives of many TLD registries
-// 
+//
 // Alternatively, the contents of this file may be used under the terms of
 // either the GNU General Public License Version 2 or later (the "GPL"), or
 // the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -36,11 +36,11 @@
 // and other provisions required by the GPL or the LGPL. If you do not delete
 // the provisions above, a recipient may use your version of this file under
 // the terms of any one of the MPL, the GPL or the LGPL.
-// 
+//
 // ***** END LICENSE BLOCK *****
 
-// Chromium note: this based on Mozilla's file:
-//  http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/src/effective_tld_names.dat?raw=1
+// Chromium note: this is based on Mozilla's file:
+//  http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1
 
 // ac : http://en.wikipedia.org/wiki/.ac
 ac
@@ -181,13 +181,14 @@
 net.ai
 org.ai
 
-// al : http://www.inima.al/Domains.html
+// al : http://www.ert.gov.al/ert_alb/faq_det.html?Id=31
 al
-gov.al
-edu.al
-org.al
 com.al
+edu.al
+gov.al
+mil.al
 net.al
+org.al
 
 // am : http://en.wikipedia.org/wiki/.am
 am
@@ -229,6 +230,7 @@
 e164.arpa
 in-addr.arpa
 ip6.arpa
+iris.arpa
 uri.arpa
 urn.arpa
 
@@ -242,11 +244,15 @@
 // at : http://en.wikipedia.org/wiki/.at
 // Confirmed by registry <it@nic.at> 2008-06-17
 at
-gv.at
 ac.at
 co.at
+gv.at
 or.at
 
+// http://www.info.at/
+biz.at
+info.at
+
 // priv.at : http://www.nic.priv.at/
 // Submitted by registry <lendl@nic.at> 2008-06-09
 priv.at
@@ -263,13 +269,23 @@
 vic.edu.au
 wa.edu.au
 act.gov.au
-nsw.gov.au
+// Removed at request of Shae.Donelan@services.nsw.gov.au, 2010-03-04
+// nsw.gov.au
 nt.gov.au
 qld.gov.au
 sa.gov.au
 tas.gov.au
 vic.gov.au
 wa.gov.au
+// CGDNs - http://www.aucd.org.au/
+act.au
+nsw.au
+nt.au
+qld.au
+sa.au
+tas.au
+vic.au
+wa.au
 
 // aw : http://en.wikipedia.org/wiki/.aw
 aw
@@ -308,11 +324,14 @@
 
 // bb : http://en.wikipedia.org/wiki/.bb
 bb
+biz.bb
 com.bb
 edu.bb
 gov.bb
+info.bb
 net.bb
 org.bb
+store.bb
 
 // bd : http://en.wikipedia.org/wiki/.bd
 *.bd
@@ -364,12 +383,15 @@
 6.bg
 7.bg
 8.bg
-9.bg	 	 	
+9.bg
 
 // bh : http://en.wikipedia.org/wiki/.bh
-// list of other 2nd level tlds ?
 bh
 com.bh
+edu.bh
+net.bh
+org.bh
+gov.bh
 
 // bi : http://en.wikipedia.org/wiki/.bi
 // http://whois.nic.bi/
@@ -384,8 +406,10 @@
 biz
 
 // bj : http://en.wikipedia.org/wiki/.bj
-// list of 2nd level tlds ?
 bj
+asso.bj
+barreau.bj
+gouv.bj
 
 // bm : http://www.bermudanic.bm/dnr-text.txt
 bm
@@ -510,6 +534,9 @@
 // www.yahoo.com.by, for example), so we list it here for safety's sake.
 com.by
 
+// http://hoster.by/
+of.by
+
 // bz : http://en.wikipedia.org/wiki/.bz
 // http://www.belizenic.bz/
 bz
@@ -681,6 +708,9 @@
 uy.com
 za.com
 
+// Requested by Yngve Pettersen <yngve@opera.com> 2009-11-26
+operaunite.com
+
 // coop : http://en.wikipedia.org/wiki/.coop
 coop
 
@@ -810,6 +840,8 @@
 // completely removed.
 // TODO: Check for updates (expected to be phased out around Q1/2009)
 aland.fi
+// iki.fi : Submitted by Hannu Aronsson <haa@iki.fi> 2009-11-05
+iki.fi
 
 // fj : http://en.wikipedia.org/wiki/.fj
 *.fj
@@ -982,7 +1014,7 @@
 网絡.hk
 组织.hk
 組織.hk
-組织.hk 
+組织.hk
 
 // hm : http://en.wikipedia.org/wiki/.hm
 hm
@@ -1119,7 +1151,9 @@
 org.iq
 net.iq
 
-// ir : http://www.nic.ir/ascii/Appendix1.htm
+// ir : http://www.nic.ir/Terms_and_Conditions_ir,_Appendix_1_Domain_Rules
+// Also see http://www.nic.ir/Internationalized_Domain_Names
+// Two <iran>.ir entries added at request of <tech-team@nic.ir>, 2010-04-16
 ir
 ac.ir
 co.ir
@@ -1128,6 +1162,10 @@
 net.ir
 org.ir
 sch.ir
+// xn--mgba3a4f16a.ir (<iran>.ir, Persian YEH)
+ایران.ir
+// xn--mgba3a4fra.ir (<iran>.ir, Arabic YEH)
+ايران.ir
 
 // is : http://www.isnic.is/domain/rules.php
 // Confirmed by registry <marius@isgate.is> 2008-12-06
@@ -1409,7 +1447,7 @@
 // Submitted by registry <yone@jprs.co.jp> 2008-06-11
 // Updated by registry <yone@jprs.co.jp> 2008-12-04
 jp
-// jp organizational type names 
+// jp organizational type names
 ac.jp
 ad.jp
 co.jp
@@ -1809,7 +1847,15 @@
 name.mk
 
 // ml : http://www.gobin.info/domainname/ml-template.doc
-*.ml
+// see also: http://en.wikipedia.org/wiki/.ml
+ml
+com.ml
+edu.ml
+gouv.ml
+gov.ml
+net.ml
+org.ml
+presse.ml
 
 // mm : http://en.wikipedia.org/wiki/.mm
 *.mm
@@ -2411,7 +2457,22 @@
 иком.museum
 
 // mv : http://en.wikipedia.org/wiki/.mv
-*.mv
+// "mv" included because, contra Wikipedia, google.mv exists.
+mv
+aero.mv
+biz.mv
+com.mv
+coop.mv
+edu.mv
+gov.mv
+info.mv
+int.mv
+mil.mv
+museum.mv
+name.mv
+net.mv
+org.mv
+pro.mv
 
 // mw : http://www.registrar.mw/
 mw
@@ -2423,6 +2484,7 @@
 edu.mw
 gov.mw
 int.mw
+museum.mw
 net.mw
 org.mw
 
@@ -2474,6 +2536,7 @@
 
 // nc : http://www.cctld.nc/
 nc
+asso.nc
 
 // ne : http://en.wikipedia.org/wiki/.ne
 ne
@@ -2524,7 +2587,7 @@
 // no : http://www.norid.no/regelverk/index.en.html
 // The Norwegian registry has declined to notify us of updates. The web pages
 // referenced below are the official source of the data. There is also an
-// announce mailing list: 
+// announce mailing list:
 // https://postlister.uninett.no/sympa/info/norid-diskusjon
 no
 // Norid generic domains : http://www.norid.no/regelverk/vedlegg-c.en.html
@@ -3438,7 +3501,6 @@
 po.gov.pl
 pa.gov.pl
 // other functional domains
-med.pl
 ngo.pl
 irc.pl
 usenet.pl
@@ -3528,6 +3590,7 @@
 rzeszow.pl
 sanok.pl
 sejny.pl
+siedlce.pl
 slask.pl
 slupsk.pl
 sosnowiec.pl
@@ -3566,6 +3629,7 @@
 gda.pl
 gdansk.pl
 gdynia.pl
+med.pl
 sopot.pl
 // other geographical domains
 gliwice.pl
@@ -3829,6 +3893,7 @@
 gouv.rw
 
 // sa : http://www.nic.net.sa/
+sa
 com.sa
 net.sa
 org.sa
@@ -3947,23 +4012,31 @@
 
 // sn : http://en.wikipedia.org/wiki/.sn
 sn
+art.sn
+com.sn
+edu.sn
+gouv.sn
+org.sn
+perso.sn
+univ.sn
 
 // sr : http://en.wikipedia.org/wiki/.sr
 sr
 
 // st : http://www.nic.st/html/policyrules/
 st
-gov.st
-saotome.st
-principe.st
-consulado.st
-org.st
-edu.st
-net.st
-com.st
-store.st
-mil.st
 co.st
+com.st
+consulado.st
+edu.st
+embaixada.st
+gov.st
+mil.st
+net.st
+org.st
+principe.st
+saotome.st
+store.st
 
 // su : http://en.wikipedia.org/wiki/.su
 su
@@ -4021,17 +4094,19 @@
 tj
 ac.tj
 biz.tj
-com.tj
 co.tj
+com.tj
 edu.tj
+go.tj
+gov.tj
 int.tj
+mil.tj
 name.tj
 net.tj
+nic.tj
 org.tj
+test.tj
 web.tj
-gov.tj
-go.tj
-mil.tj
 
 // tk : http://en.wikipedia.org/wiki/.tk
 tk
@@ -4080,6 +4155,9 @@
 
 // tr : http://en.wikipedia.org/wiki/.tr
 *.tr
+// Used by government in the TRNC
+// http://en.wikipedia.org/wiki/.nc.tr
+gov.nc.tr
 
 // travel : http://en.wikipedia.org/wiki/.travel
 travel
@@ -4105,12 +4183,9 @@
 edu.tt
 
 // tv : http://en.wikipedia.org/wiki/.tv
-// list of other 2nd level tlds ?
+// Not listing any 2LDs as reserved since none seem to exist in practice,
+// Wikipedia notwithstanding.
 tv
-com.tv
-net.tv
-org.tv
-gov.tv
 
 // tw : http://en.wikipedia.org/wiki/.tw
 tw
@@ -4349,6 +4424,18 @@
 gov.ws
 edu.ws
 
+// xn--mgbaam7a8h (UAE) : http://nic.ae/english/arabicdomain/rules.jsp
+امارات
+
+// xn--mgberp4a5d4ar (Saudi Arabia) : http://www.nic.net.sa/
+السعودية
+
+// xn--p1ai (Russia) : http://www.cctld.ru/en/docs/rulesrf.php
+рф
+
+// xn--wgbh1c (Egypt) : http://www.dotmasr.eg/
+مصر
+
 // ye : http://www.y.net.ye/services/domain_name.htm
 *.ye
 
diff --git a/net/base/effective_tld_names.gperf b/net/base/effective_tld_names.gperf
index 8f511cf..48ac9d3 100644
--- a/net/base/effective_tld_names.gperf
+++ b/net/base/effective_tld_names.gperf
@@ -63,6 +63,7 @@
 academy.museum, 0
 accident-investigation.aero, 0
 accident-prevention.aero, 0
+act.au, 0
 act.edu.au, 0
 act.gov.au, 0
 ad, 0
@@ -75,6 +76,7 @@
 ae.org, 0
 aejrie.no, 0
 aero, 0
+aero.mv, 0
 aero.tt, 0
 aerobatic.aero, 0
 aeroclub.aero, 0
@@ -184,6 +186,7 @@
 art.ht, 0
 art.museum, 0
 art.pl, 0
+art.sn, 0
 artanddesign.museum, 0
 artcenter.museum, 0
 artdeco.museum, 0
@@ -212,6 +215,7 @@
 assedic.fr, 0
 assisi.museum, 0
 assn.lk, 0
+asso.bj, 0
 asso.ci, 0
 asso.dz, 0
 asso.fr, 0
@@ -219,6 +223,7 @@
 asso.ht, 0
 asso.km, 0
 asso.mc, 0
+asso.nc, 0
 asso.re, 0
 association.aero, 0
 association.museum, 0
@@ -284,6 +289,7 @@
 bari.it, 0
 barletta-andria-trani.it, 0
 barlettaandriatrani.it, 0
+barreau.bj, 0
 barum.no, 0
 baseball.museum, 0
 basel.museum, 0
@@ -338,8 +344,11 @@
 birkenes.no, 0
 birthplace.museum, 0
 biz, 0
+biz.at, 0
 biz.az, 0
+biz.bb, 0
 biz.ki, 0
+biz.mv, 0
 biz.mw, 0
 biz.nr, 0
 biz.pk, 0
@@ -643,8 +652,10 @@
 com.ly, 0
 com.mg, 0
 com.mk, 0
+com.ml, 0
 com.mo, 0
 com.mu, 0
+com.mv, 0
 com.mw, 0
 com.mx, 0
 com.my, 0
@@ -671,13 +682,13 @@
 com.sd, 0
 com.sg, 0
 com.sl, 0
+com.sn, 0
 com.st, 0
 com.sy, 0
 com.tj, 0
 com.tn, 0
 com.to, 0
 com.tt, 0
-com.tv, 0
 com.tw, 0
 com.ua, 0
 com.uz, 0
@@ -705,6 +716,7 @@
 coop.br, 0
 coop.ht, 0
 coop.km, 0
+coop.mv, 0
 coop.mw, 0
 coop.tt, 0
 copenhagen.museum, 0
@@ -820,6 +832,7 @@
 edu.az, 0
 edu.ba, 0
 edu.bb, 0
+edu.bh, 0
 edu.bi, 0
 edu.bm, 0
 edu.bo, 0
@@ -865,8 +878,10 @@
 edu.me, 0
 edu.mg, 0
 edu.mk, 0
+edu.ml, 0
 edu.mn, 0
 edu.mo, 0
+edu.mv, 0
 edu.mw, 0
 edu.mx, 0
 edu.my, 0
@@ -891,6 +906,7 @@
 edu.sd, 0
 edu.sg, 0
 edu.sl, 0
+edu.sn, 0
 edu.st, 0
 edu.sy, 0
 edu.tj, 0
@@ -923,6 +939,7 @@
 elk.pl, 0
 elvendrell.museum, 0
 elverum.no, 0
+embaixada.st, 0
 embroidery.museum, 0
 emergency.aero, 0
 en.it, 0
@@ -1172,11 +1189,14 @@
 gorizia.it, 0
 gorlice.pl, 0
 gos.pk, 0
+gouv.bj, 0
 gouv.ci, 0
 gouv.fr, 0
 gouv.ht, 0
 gouv.km, 0
+gouv.ml, 0
 gouv.rw, 0
+gouv.sn, 0
 gov, 0
 gov.ac, 0
 gov.ae, 0
@@ -1187,6 +1207,7 @@
 gov.ba, 0
 gov.bb, 0
 gov.bf, 0
+gov.bh, 0
 gov.bm, 0
 gov.bo, 0
 gov.br, 0
@@ -1238,12 +1259,15 @@
 gov.me, 0
 gov.mg, 0
 gov.mk, 0
+gov.ml, 0
 gov.mn, 0
 gov.mo, 0
 gov.mr, 0
 gov.mu, 0
+gov.mv, 0
 gov.mw, 0
 gov.my, 0
+gov.nc.tr, 0
 gov.ng, 0
 gov.nr, 0
 gov.ph, 0
@@ -1269,7 +1293,6 @@
 gov.tn, 0
 gov.to, 0
 gov.tt, 0
-gov.tv, 0
 gov.tw, 0
 gov.ua, 0
 gov.vc, 0
@@ -1448,6 +1471,7 @@
 idv.tw, 0
 ie, 0
 if.ua, 0
+iki.fi, 0
 il, 2
 il.us, 0
 ilawa.pl, 0
@@ -1477,13 +1501,16 @@
 inf.cu, 0
 inf.mk, 0
 info, 0
+info.at, 0
 info.az, 0
+info.bb, 0
 info.co, 0
 info.ec, 0
 info.ht, 0
 info.hu, 0
 info.ki, 0
 info.la, 0
+info.mv, 0
 info.na, 0
 info.nf, 0
 info.nr, 0
@@ -1506,6 +1533,7 @@
 int.is, 0
 int.la, 0
 int.lk, 0
+int.mv, 0
 int.mw, 0
 int.pt, 0
 int.ru, 0
@@ -1522,6 +1550,7 @@
 ir, 0
 iraq.museum, 0
 irc.pl, 0
+iris.arpa, 0
 irkutsk.ru, 0
 iron.museum, 0
 is, 0
@@ -1913,6 +1942,7 @@
 mil, 0
 mil.ac, 0
 mil.ae, 0
+mil.al, 0
 mil.az, 0
 mil.ba, 0
 mil.bo, 0
@@ -1933,6 +1963,7 @@
 mil.kz, 0
 mil.lv, 0
 mil.mg, 0
+mil.mv, 0
 mil.my, 0
 mil.no, 0
 mil.pe, 0
@@ -1961,7 +1992,7 @@
 mjondalen.no, 0
 mk, 0
 mk.ua, 0
-ml, 2
+ml, 0
 mm, 2
 mn, 0
 mn.it, 0
@@ -2020,12 +2051,14 @@
 mus.br, 0
 museet.museum, 0
 museum, 0
+museum.mv, 0
+museum.mw, 0
 museum.no, 0
 museum.tt, 0
 museumcenter.museum, 0
 museumvereniging.museum, 0
 music.museum, 0
-mv, 2
+mv, 0
 mw, 0
 mx, 0
 mx.na, 0
@@ -2050,6 +2083,7 @@
 name.hr, 0
 name.jo, 0
 name.mk, 0
+name.mv, 0
 name.my, 0
 name.na, 0
 name.pr, 0
@@ -2115,6 +2149,7 @@
 net.az, 0
 net.ba, 0
 net.bb, 0
+net.bh, 0
 net.bm, 0
 net.bo, 0
 net.br, 0
@@ -2158,8 +2193,10 @@
 net.ma, 0
 net.me, 0
 net.mk, 0
+net.ml, 0
 net.mo, 0
 net.mu, 0
+net.mv, 0
 net.mw, 0
 net.mx, 0
 net.my, 0
@@ -2190,7 +2227,6 @@
 net.tn, 0
 net.to, 0
 net.tt, 0
-net.tv, 0
 net.tw, 0
 net.ua, 0
 net.vc, 0
@@ -2217,6 +2253,7 @@
 nic.ar, 1
 nic.im, 0
 nic.in, 0
+nic.tj, 0
 niepce.museum, 0
 nieruchomosci.pl, 0
 niigata.jp, 2
@@ -2275,8 +2312,9 @@
 ns.ca, 0
 nsk.ru, 0
 nsn.us, 0
+nsw.au, 0
 nsw.edu.au, 0
-nsw.gov.au, 0
+nt.au, 0
 nt.ca, 0
 nt.edu.au, 0
 nt.gov.au, 0
@@ -2304,6 +2342,7 @@
 odda.no, 0
 odessa.ua, 0
 odo.br, 0
+of.by, 0
 of.no, 0
 off.ai, 0
 og.ao, 0
@@ -2326,6 +2365,7 @@
 online.museum, 0
 ontario.museum, 0
 openair.museum, 0
+operaunite.com, 0
 opoczno.pl, 0
 opole.pl, 0
 oppdal.no, 0
@@ -2358,6 +2398,7 @@
 org.az, 0
 org.ba, 0
 org.bb, 0
+org.bh, 0
 org.bi, 0
 org.bm, 0
 org.bo, 0
@@ -2410,9 +2451,11 @@
 org.me, 0
 org.mg, 0
 org.mk, 0
+org.ml, 0
 org.mn, 0
 org.mo, 0
 org.mu, 0
+org.mv, 0
 org.mw, 0
 org.mx, 0
 org.my, 0
@@ -2439,6 +2482,7 @@
 org.se, 0
 org.sg, 0
 org.sl, 0
+org.sn, 0
 org.st, 0
 org.sy, 0
 org.sz, 0
@@ -2446,7 +2490,6 @@
 org.tn, 0
 org.to, 0
 org.tt, 0
-org.tv, 0
 org.tw, 0
 org.ua, 0
 org.vc, 0
@@ -2520,6 +2563,7 @@
 per.sg, 0
 perm.ru, 0
 perso.ht, 0
+perso.sn, 0
 perso.tn, 0
 perugia.it, 0
 pesaro-urbino.it, 0
@@ -2646,6 +2690,7 @@
 presse.ci, 0
 presse.fr, 0
 presse.km, 0
+presse.ml, 0
 pri.ee, 0
 principe.st, 0
 priv.at, 0
@@ -2658,6 +2703,7 @@
 pro.br, 0
 pro.ec, 0
 pro.ht, 0
+pro.mv, 0
 pro.na, 0
 pro.pr, 0
 pro.tt, 0
@@ -2693,6 +2739,7 @@
 qc.ca, 0
 qc.com, 0
 qh.cn, 0
+qld.au, 0
 qld.edu.au, 0
 qld.gov.au, 0
 qsl.br, 0
@@ -2798,6 +2845,7 @@
 s.bg, 0
 s.se, 0
 sa, 0
+sa.au, 0
 sa.com, 0
 sa.cr, 0
 sa.edu.au, 0
@@ -2910,6 +2958,7 @@
 si, 0
 si.it, 0
 sibenik.museum, 0
+siedlce.pl, 0
 siellak.no, 0
 siena.it, 0
 sigdal.no, 0
@@ -3026,6 +3075,7 @@
 stor-elvdal.no, 0
 stord.no, 0
 stordal.no, 0
+store.bb, 0
 store.nf, 0
 store.ro, 0
 store.st, 0
@@ -3081,6 +3131,7 @@
 taranto.it, 0
 targi.pl, 0
 tarnobrzeg.pl, 0
+tas.au, 0
 tas.edu.au, 0
 tas.gov.au, 0
 tatarstan.ru, 0
@@ -3098,6 +3149,7 @@
 terni.it, 0
 ternopil.ua, 0
 test.ru, 0
+test.tj, 0
 texas.museum, 0
 textile.museum, 0
 tf, 0
@@ -3234,6 +3286,7 @@
 unbi.ba, 0
 undersea.museum, 0
 union.aero, 0
+univ.sn, 0
 university.museum, 0
 unjarga.no, 0
 unsa.ba, 0
@@ -3322,6 +3375,7 @@
 vi.us, 0
 vibo-valentia.it, 0
 vibovalentia.it, 0
+vic.au, 0
 vic.edu.au, 0
 vic.gov.au, 0
 vicenza.it, 0
@@ -3360,6 +3414,7 @@
 vyatka.ru, 0
 w.bg, 0
 w.se, 0
+wa.au, 0
 wa.edu.au, 0
 wa.gov.au, 0
 wa.us, 0
@@ -3506,6 +3561,10 @@
 xn--lury-ira.no, 0
 xn--mely-ira.no, 0
 xn--merker-kua.no, 0
+xn--mgba3a4f16a.ir, 0
+xn--mgba3a4fra.ir, 0
+xn--mgbaam7a8h, 0
+xn--mgberp4a5d4ar, 0
 xn--mjndalen-64a.no, 0
 xn--mk0axi.hk, 0
 xn--mlatvuopmi-s4a.no, 0
@@ -3528,6 +3587,7 @@
 xn--oppegrd-ixa.no, 0
 xn--ostery-fya.no, 0
 xn--osyro-wua.no, 0
+xn--p1ai, 0
 xn--porsgu-sta26f.no, 0
 xn--rady-ira.no, 0
 xn--rdal-poa.no, 0
@@ -3600,6 +3660,7 @@
 xn--vrggt-xqad.no, 0
 xn--vry-yla5g.no, 0
 xn--wcvs22d.hk, 0
+xn--wgbh1c, 0
 xn--yer-zna.no, 0
 xn--ygarden-p1a.no, 0
 xn--ystre-slidre-ujb.no, 0
diff --git a/net/base/escape.cc b/net/base/escape.cc
index bf23bcb..bc01e11 100644
--- a/net/base/escape.cc
+++ b/net/base/escape.cc
@@ -99,7 +99,7 @@
 //   0  1  2  3  4  5  6  7  8  9  :  ;  <  =  >  ?
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0,
 //   @  A  B  C  D  E  F  G  H  I  J  K  L  M  N  O
-     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 //   P  Q  R  S  T  U  V  W  X  Y  Z  [  \  ]  ^  _
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 //   `  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o
diff --git a/net/base/escape_unittest.cc b/net/base/escape_unittest.cc
index 0049528..28972cb 100644
--- a/net/base/escape_unittest.cc
+++ b/net/base/escape_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/basictypes.h"
 #include "base/i18n/icu_string_conversions.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -237,6 +238,10 @@
     {L"Hello%20%13%10world %23# %3F? %3D= %26& %25% %2B+",
      UnescapeRule::URL_SPECIAL_CHARS,
      L"Hello%20%13%10world ## ?? == && %% ++"},
+    // We can neither escape nor unescape '@' since some websites expect it to
+    // be preserved as either '@' or "%40".
+    // See http://b/996720 and http://crbug.com/23933 .
+    {L"me@my%40example", UnescapeRule::NORMAL, L"me@my%40example"},
     // Control characters.
     {L"%01%02%03%04%05%06%07%08%09 %25", UnescapeRule::URL_SPECIAL_CHARS,
      L"%01%02%03%04%05%06%07%08%09 %"},
diff --git a/net/base/ev_root_ca_metadata.cc b/net/base/ev_root_ca_metadata.cc
index 621a174..023b1fb 100644
--- a/net/base/ev_root_ca_metadata.cc
+++ b/net/base/ev_root_ca_metadata.cc
@@ -20,7 +20,7 @@
 struct EVMetadata {
   // The SHA-1 fingerprint of the root CA certificate, used as a unique
   // identifier for a root CA certificate.
-  X509Certificate::Fingerprint fingerprint;
+  SHA1Fingerprint fingerprint;
 
   // The EV policy OID of the root CA.
   // Note: a root CA may have multiple EV policies.  When that actually
@@ -35,6 +35,12 @@
         0x57, 0x69, 0x4d, 0xf5, 0xe4, 0x5b, 0x68, 0x85, 0x18, 0x68 } },
     "1.3.6.1.4.1.6449.1.2.1.5.1"
   },
+  // CertPlus Class 2 Primary CA (KEYNECTIS)
+  // https://www.keynectis.com/
+  { { { 0x74, 0x20, 0x74, 0x41, 0x72, 0x9c, 0xdd, 0x92, 0xec, 0x79,
+        0x31, 0xd8, 0x23, 0x10, 0x8d, 0xc2, 0x81, 0x92, 0xe2, 0xbb } },
+    "1.3.6.1.4.1.22234.2.5.2.3.1"
+  },
   // COMODO Certification Authority
   // https://secure.comodo.com/
   { { { 0x66, 0x31, 0xbf, 0x9e, 0xf7, 0x4f, 0x9e, 0xb6, 0xc9, 0xd5,
@@ -149,6 +155,12 @@
         0x3d, 0xd8, 0x90, 0x8f, 0xfd, 0x28, 0x86, 0x65, 0x64, 0x7d } },
     "1.2.392.200091.100.721.1"
   },
+  // StartCom Certification Authority
+  // https://www.startssl.com/
+  { { { 0x3e, 0x2b, 0xf7, 0xf2, 0x03, 0x1b, 0x96, 0xf3, 0x8c, 0xe6,
+        0xc4, 0xd8, 0xa8, 0x5d, 0x3e, 0x2d, 0x58, 0x47, 0x6a, 0x0f } },
+    "1.3.6.1.4.1.23223.1.1.1"
+  },
   // Starfield Class 2 Certification Authority
   // https://www.starfieldtech.com/
   { { { 0xad, 0x7e, 0x1c, 0x28, 0xb0, 0x64, 0xef, 0x8f, 0x60, 0x03,
@@ -221,7 +233,7 @@
 }
 
 bool EVRootCAMetadata::GetPolicyOID(
-    const X509Certificate::Fingerprint& fingerprint,
+    const SHA1Fingerprint& fingerprint,
     PolicyOID* policy_oid) const {
   PolicyOidMap::const_iterator iter = ev_policy_.find(fingerprint);
   if (iter == ev_policy_.end())
diff --git a/net/base/ev_root_ca_metadata.h b/net/base/ev_root_ca_metadata.h
index f006878..b1b2781 100644
--- a/net/base/ev_root_ca_metadata.h
+++ b/net/base/ev_root_ca_metadata.h
@@ -35,7 +35,7 @@
 
   // If the root CA cert has an EV policy OID, returns true and stores the
   // policy OID in *policy_oid.  Otherwise, returns false.
-  bool GetPolicyOID(const X509Certificate::Fingerprint& fingerprint,
+  bool GetPolicyOID(const SHA1Fingerprint& fingerprint,
                     PolicyOID* policy_oid) const;
 
   const PolicyOID* GetPolicyOIDs() const { return &policy_oids_[0]; }
@@ -47,8 +47,8 @@
 
   friend struct DefaultSingletonTraits<EVRootCAMetadata>;
 
-  typedef std::map<X509Certificate::Fingerprint, PolicyOID,
-                   X509Certificate::FingerprintLessThan> PolicyOidMap;
+  typedef std::map<SHA1Fingerprint, PolicyOID,
+                   SHA1FingerprintLessThan> PolicyOidMap;
 
   // Maps an EV root CA cert's SHA-1 fingerprint to its EV policy OID.
   PolicyOidMap ev_policy_;
diff --git a/net/base/file_stream.h b/net/base/file_stream.h
index 6b7f3dc..a6d5739 100644
--- a/net/base/file_stream.h
+++ b/net/base/file_stream.h
@@ -34,6 +34,8 @@
   // |file| is valid file handle.
   // |flags| is a bitfield of base::PlatformFileFlags when the file handle was
   // opened.
+  // The already opened file will not be automatically closed when FileStream
+  // is destructed.
   FileStream(base::PlatformFile file, int flags);
 
   ~FileStream();
@@ -118,6 +120,15 @@
   //   platform with this call.
   int64 Truncate(int64 bytes);
 
+  // Forces out a filesystem sync on this file to make sure that the file was
+  // written out to disk and is not currently sitting in the buffer. This does
+  // not have to be called, it just forces one to happen at the time of
+  // calling.
+  //
+  /// Returns an error code if the operation could not be performed.
+  //
+  // This method should not be called if the stream was opened READ_ONLY.
+  int Flush();
  private:
   class AsyncContext;
   friend class AsyncContext;
@@ -128,6 +139,7 @@
 
   base::PlatformFile file_;
   int open_flags_;
+  bool auto_closed_;
 
   DISALLOW_COPY_AND_ASSIGN(FileStream);
 };
diff --git a/net/base/file_stream_posix.cc b/net/base/file_stream_posix.cc
index eda0927..ba91db2 100644
--- a/net/base/file_stream_posix.cc
+++ b/net/base/file_stream_posix.cc
@@ -1,6 +1,6 @@
-// Copyright (c) 2008 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.
+// Copyright (c) 2010 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.
 
 // For 64-bit file access (off_t = off64_t, lseek64, etc).
 #define _FILE_OFFSET_BITS 64
@@ -14,8 +14,10 @@
 #include <errno.h>
 
 #include "base/basictypes.h"
+#include "base/callback.h"
 #include "base/eintr_wrapper.h"
 #include "base/file_path.h"
+#include "base/histogram.h"
 #include "base/logging.h"
 #include "base/message_loop.h"
 #include "base/string_util.h"
@@ -69,6 +71,16 @@
   return res;
 }
 
+// FlushFile() is a simple wrapper around fsync() that handles EINTR signals and
+// calls MapErrorCode() to map errno to net error codes.  It tries to flush to
+// completion.
+int FlushFile(base::PlatformFile file) {
+  ssize_t res = HANDLE_EINTR(fsync(file));
+  if (res == -1)
+    return MapErrorCode(errno);
+  return res;
+}
+
 // BackgroundReadTask is a simple task that reads a file and then runs
 // |callback|.  AsyncContext will post this task to the WorkerPool.
 class BackgroundReadTask : public Task {
@@ -222,11 +234,12 @@
     // still running the IO task, or the completion callback is queued up on the
     // MessageLoopForIO, but AsyncContext() got deleted before then.
     const bool need_to_wait = !background_io_completed_.IsSignaled();
-    base::Time start = base::Time::Now();
+    base::TimeTicks start = base::TimeTicks::Now();
     RunAsynchronousCallback();
     if (need_to_wait) {
       // We want to see if we block the message loop for too long.
-      UMA_HISTOGRAM_TIMES("AsyncIO.FileStreamClose", base::Time::Now() - start);
+      UMA_HISTOGRAM_TIMES("AsyncIO.FileStreamClose",
+                          base::TimeTicks::Now() - start);
     }
   }
 }
@@ -292,13 +305,15 @@
 
 FileStream::FileStream()
     : file_(base::kInvalidPlatformFileValue),
-      open_flags_(0) {
+      open_flags_(0),
+      auto_closed_(true) {
   DCHECK(!IsOpen());
 }
 
 FileStream::FileStream(base::PlatformFile file, int flags)
     : file_(file),
-      open_flags_(flags) {
+      open_flags_(flags),
+      auto_closed_(false) {
   // If the file handle is opened with base::PLATFORM_FILE_ASYNC, we need to
   // make sure we will perform asynchronous File IO to it.
   if (flags & base::PLATFORM_FILE_ASYNC) {
@@ -307,7 +322,8 @@
 }
 
 FileStream::~FileStream() {
-  Close();
+  if (auto_closed_)
+    Close();
 }
 
 void FileStream::Close() {
@@ -440,6 +456,13 @@
   }
 }
 
+int FileStream::Flush() {
+  if (!IsOpen())
+    return ERR_UNEXPECTED;
+
+  return FlushFile(file_);
+}
+
 int64 FileStream::Truncate(int64 bytes) {
   if (!IsOpen())
     return ERR_UNEXPECTED;
diff --git a/net/base/file_stream_unittest.cc b/net/base/file_stream_unittest.cc
index a7a650f..cf80699 100644
--- a/net/base/file_stream_unittest.cc
+++ b/net/base/file_stream_unittest.cc
@@ -1,7 +1,8 @@
-// Copyright (c) 2008 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.
+// Copyright (c) 2010 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/callback.h"
 #include "base/file_util.h"
 #include "base/path_service.h"
 #include "base/platform_file.h"
diff --git a/net/base/file_stream_win.cc b/net/base/file_stream_win.cc
index cec6a9d..0460b3d 100644
--- a/net/base/file_stream_win.cc
+++ b/net/base/file_stream_win.cc
@@ -1,12 +1,13 @@
-// Copyright (c) 2008 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.
+// Copyright (c) 2010 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/base/file_stream.h"
 
 #include <windows.h>
 
 #include "base/file_path.h"
+#include "base/histogram.h"
 #include "base/logging.h"
 #include "base/message_loop.h"
 #include "net/base/net_errors.h"
@@ -74,14 +75,15 @@
 FileStream::AsyncContext::~AsyncContext() {
   is_closing_ = true;
   bool waited = false;
-  base::Time start = base::Time::Now();
+  base::TimeTicks start = base::TimeTicks::Now();
   while (callback_) {
     waited = true;
     MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
   }
   if (waited) {
     // We want to see if we block the message loop for too long.
-    UMA_HISTOGRAM_TIMES("AsyncIO.FileStreamClose", base::Time::Now() - start);
+    UMA_HISTOGRAM_TIMES("AsyncIO.FileStreamClose",
+                        base::TimeTicks::Now() - start);
   }
 }
 
@@ -117,12 +119,14 @@
 
 FileStream::FileStream()
     : file_(INVALID_HANDLE_VALUE),
-      open_flags_(0) {
+      open_flags_(0),
+      auto_closed_(true) {
 }
 
 FileStream::FileStream(base::PlatformFile file, int flags)
     : file_(file),
-      open_flags_(flags) {
+      open_flags_(flags),
+      auto_closed_(false) {
   // If the file handle is opened with base::PLATFORM_FILE_ASYNC, we need to
   // make sure we will perform asynchronous File IO to it.
   if (flags & base::PLATFORM_FILE_ASYNC) {
@@ -133,7 +137,8 @@
 }
 
 FileStream::~FileStream() {
-  Close();
+  if (auto_closed_)
+    Close();
 }
 
 void FileStream::Close() {
@@ -298,6 +303,21 @@
   return rv;
 }
 
+int FileStream::Flush() {
+  if (!IsOpen())
+    return ERR_UNEXPECTED;
+
+  DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
+  if (FlushFileBuffers(file_)) {
+    return OK;
+  }
+
+  int rv;
+  DWORD error = GetLastError();
+  rv = MapErrorCode(error);
+  return rv;
+}
+
 int64 FileStream::Truncate(int64 bytes) {
   if (!IsOpen())
     return ERR_UNEXPECTED;
diff --git a/net/base/filter.cc b/net/base/filter.cc
index 4a7b8a6..c72e00e 100644
--- a/net/base/filter.cc
+++ b/net/base/filter.cc
@@ -36,6 +36,9 @@
 
 }  // namespace
 
+FilterContext::~FilterContext() {
+}
+
 Filter* Filter::Factory(const std::vector<FilterType>& filter_types,
                         const FilterContext& filter_context) {
   DCHECK_GT(filter_context.GetInputStreamBufferSize(), 0);
diff --git a/net/base/filter.h b/net/base/filter.h
index cf32d2f..acc0e7e 100644
--- a/net/base/filter.h
+++ b/net/base/filter.h
@@ -62,7 +62,7 @@
     SDCH_EXPERIMENT_HOLDBACK,
   };
 
-  virtual ~FilterContext() {}
+  virtual ~FilterContext();
 
   // What mime type was specified in the header for this data?
   // Only makes senses for some types of contexts, and returns false
diff --git a/net/base/forwarding_net_log.cc b/net/base/forwarding_net_log.cc
new file mode 100644
index 0000000..6cea529
--- /dev/null
+++ b/net/base/forwarding_net_log.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2010 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/base/forwarding_net_log.h"
+
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+
+namespace net {
+
+// Reference-counted wrapper, so we can use PostThread and it can safely
+// outlive the parent ForwardingNetLog.
+class ForwardingNetLog::Core
+    : public base::RefCountedThreadSafe<ForwardingNetLog::Core> {
+ public:
+  Core(NetLog* impl, MessageLoop* loop) : impl_(impl), loop_(loop) {
+    DCHECK(impl);
+    DCHECK(loop);
+  }
+
+  // Called once the parent ForwardingNetLog is being destroyed. It
+  // is invalid to access |loop_| and |impl_| afterwards.
+  void Orphan() {
+    AutoLock l(lock_);
+    loop_ = NULL;
+    impl_ = NULL;
+  }
+
+  void AddEntry(EventType type,
+                const base::TimeTicks& time,
+                const Source& source,
+                EventPhase phase,
+                EventParameters* params) {
+    AutoLock l(lock_);
+    if (!loop_)
+      return;  // Was orphaned.
+
+    loop_->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(
+            this, &Core::AddEntryOnLoop, type, time, source, phase,
+            scoped_refptr<EventParameters>(params)));
+  }
+
+ private:
+  void AddEntryOnLoop(EventType type,
+                      const base::TimeTicks& time,
+                      const Source& source,
+                      EventPhase phase,
+                      scoped_refptr<EventParameters> params) {
+    AutoLock l(lock_);
+    if (!loop_)
+      return;  // Was orphaned.
+
+    DCHECK_EQ(MessageLoop::current(), loop_);
+
+    // TODO(eroman): This shouldn't be necessary. See crbug.com/48806.
+    NetLog::Source effective_source = source;
+    if (effective_source.id == NetLog::Source::kInvalidId)
+      effective_source.id = impl_->NextID();
+
+    impl_->AddEntry(type, time, effective_source, phase, params);
+  }
+
+  Lock lock_;
+  NetLog* impl_;
+  MessageLoop* loop_;
+};
+
+ForwardingNetLog::ForwardingNetLog(NetLog* impl, MessageLoop* loop)
+    : core_(new Core(impl, loop)) {
+}
+
+ForwardingNetLog::~ForwardingNetLog() {
+  core_->Orphan();
+}
+
+void ForwardingNetLog::AddEntry(EventType type,
+                                const base::TimeTicks& time,
+                                const Source& source,
+                                EventPhase phase,
+                                EventParameters* params) {
+  core_->AddEntry(type, time, source, phase, params);
+}
+
+uint32 ForwardingNetLog::NextID() {
+  // Can't forward a synchronous API.
+  CHECK(false) << "Not supported";
+  return 0;
+}
+
+bool ForwardingNetLog::HasListener() const {
+  // Can't forward a synchronous API.
+  CHECK(false) << "Not supported";
+  return false;
+}
+
+}  // namespace net
+
diff --git a/net/base/forwarding_net_log.h b/net/base/forwarding_net_log.h
new file mode 100644
index 0000000..a97d4ac
--- /dev/null
+++ b/net/base/forwarding_net_log.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2010 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_BASE_FORWARDING_NET_LOG_H_
+#define NET_BASE_FORWARDING_NET_LOG_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_log.h"
+
+class MessageLoop;
+
+namespace net {
+
+// ForwardingNetLog is a wrapper that can be called on any thread, and will
+// forward any calls to NetLog::AddEntry() over to |impl| on the specified
+// message loop.
+//
+// This allows using a non-threadsafe NetLog implementation from another
+// thread.
+//
+// TODO(eroman): Explore making NetLog threadsafe and obviating the need
+//               for this class.
+class ForwardingNetLog : public NetLog {
+ public:
+  // Both |impl| and |loop| must outlive the lifetime of this instance.
+  // |impl| will be operated only from |loop|.
+  ForwardingNetLog(NetLog* impl, MessageLoop* loop);
+
+  // On destruction any outstanding call to AddEntry() which didn't make
+  // it to |loop| yet will be cancelled.
+  ~ForwardingNetLog();
+
+  // NetLog methods:
+  virtual void AddEntry(EventType type,
+                        const base::TimeTicks& time,
+                        const Source& source,
+                        EventPhase phase,
+                        EventParameters* params);
+  virtual uint32 NextID();
+  virtual bool HasListener() const;
+
+ private:
+  class Core;
+  scoped_refptr<Core> core_;
+
+  DISALLOW_COPY_AND_ASSIGN(ForwardingNetLog);
+};
+
+}  // namespace net
+
+#endif  // NET_BASE_FORWARDING_NET_LOG_H_
+
diff --git a/net/base/forwarding_net_log_unittest.cc b/net/base/forwarding_net_log_unittest.cc
new file mode 100644
index 0000000..3f25129
--- /dev/null
+++ b/net/base/forwarding_net_log_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2010 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/base/forwarding_net_log.h"
+
+#include "base/message_loop.h"
+#include "net/base/capturing_net_log.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+// Test forwarding a call to AddEntry() to another implementation, operating
+// on this same message loop.
+TEST(ForwardingNetLogTest, Basic) {
+  // Create a forwarding NetLog that sends messages to this same thread.
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
+  ForwardingNetLog forwarding(&log, MessageLoop::current());
+
+  EXPECT_EQ(0u, log.entries().size());
+
+  NetLogStringParameter* params = new NetLogStringParameter("xxx", "yyy");
+
+  forwarding.AddEntry(
+      NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
+      base::TimeTicks(),
+      NetLog::Source(),
+      NetLog::PHASE_NONE,
+      params);
+
+  // Should still be empty, since we posted an async task.
+  EXPECT_EQ(0u, log.entries().size());
+
+  MessageLoop::current()->RunAllPending();
+
+  // After draining the message loop, we should now have executed the task
+  // and hence emitted the log entry.
+  ASSERT_EQ(1u, log.entries().size());
+
+  // Check that the forwarded call contained received all the right inputs.
+  EXPECT_EQ(NetLog::TYPE_PAC_JAVASCRIPT_ALERT, log.entries()[0].type);
+  EXPECT_EQ(NetLog::SOURCE_NONE, log.entries()[0].source.type);
+  EXPECT_EQ(NetLog::PHASE_NONE, log.entries()[0].phase);
+  EXPECT_EQ(params, log.entries()[0].extra_parameters.get());
+
+  // Check that the parameters is still referenced. (if the reference was
+  // lost then this will be a memory error and probaby crash).
+  EXPECT_EQ("yyy", params->value());
+}
+
+// Test forwarding a call to AddEntry() to another implementation that runs
+// on the same message loop. However destroy the forwarder before the posted
+// task has a chance to run.
+TEST(ForwardingNetLogTest, Orphan) {
+  // Create a forwarding NetLog that sends messages to this same thread.
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
+  {
+    ForwardingNetLog forwarding(&log, MessageLoop::current());
+    EXPECT_EQ(0u, log.entries().size());
+
+    forwarding.AddEntry(
+        NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
+        base::TimeTicks(),
+        NetLog::Source(),
+        NetLog::PHASE_NONE,
+        NULL);
+
+    // Should still be empty, since we posted an async task.
+    EXPECT_EQ(0u, log.entries().size());
+  }
+
+  // At this point the ForwardingNetLog is deleted. However it had already
+  // posted a task to the message loop. Once we drain the message loop, we
+  // verify that the task didn't actually try to emit to the NetLog.
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(0u, log.entries().size());
+}
+
+}  // namespace
+
+}  // namespace net
+
diff --git a/net/base/gzip_filter.h b/net/base/gzip_filter.h
index d22b9e4..eb88b7f 100644
--- a/net/base/gzip_filter.h
+++ b/net/base/gzip_filter.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -136,7 +136,7 @@
   // we don't get a valid gzip header.
   bool possible_sdch_pass_through_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(GZipFilter);
+  DISALLOW_COPY_AND_ASSIGN(GZipFilter);
 };
 
 #endif  // NET_BASE_GZIP_FILTER_H__
diff --git a/net/base/host_cache.cc b/net/base/host_cache.cc
index 791ecca..28173d2 100644
--- a/net/base/host_cache.cc
+++ b/net/base/host_cache.cc
@@ -35,6 +35,7 @@
 
 const HostCache::Entry* HostCache::Lookup(const Key& key,
                                           base::TimeTicks now) const {
+  DCHECK(CalledOnValidThread());
   if (caching_is_disabled())
     return NULL;
 
@@ -51,8 +52,9 @@
 
 HostCache::Entry* HostCache::Set(const Key& key,
                                  int error,
-                                 const AddressList addrlist,
+                                 const AddressList& addrlist,
                                  base::TimeTicks now) {
+  DCHECK(CalledOnValidThread());
   if (caching_is_disabled())
     return NULL;
 
@@ -79,6 +81,37 @@
   }
 }
 
+void HostCache::clear() {
+  DCHECK(CalledOnValidThread());
+  entries_.clear();
+}
+
+size_t HostCache::size() const {
+  DCHECK(CalledOnValidThread());
+  return entries_.size();
+}
+
+size_t HostCache::max_entries() const {
+  DCHECK(CalledOnValidThread());
+  return max_entries_;
+}
+
+base::TimeDelta HostCache::success_entry_ttl() const {
+  DCHECK(CalledOnValidThread());
+  return success_entry_ttl_;
+}
+
+base::TimeDelta HostCache::failure_entry_ttl() const {
+  DCHECK(CalledOnValidThread());
+  return failure_entry_ttl_;
+}
+
+// Note that this map may contain expired entries.
+const HostCache::EntryMap& HostCache::entries() const {
+  DCHECK(CalledOnValidThread());
+  return entries_;
+}
+
 // static
 bool HostCache::CanUseEntry(const Entry* entry, const base::TimeTicks now) {
   return entry->expiration > now;
diff --git a/net/base/host_cache.h b/net/base/host_cache.h
index c13c6a0..1b40318 100644
--- a/net/base/host_cache.h
+++ b/net/base/host_cache.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -8,6 +8,7 @@
 #include <map>
 #include <string>
 
+#include "base/non_thread_safe.h"
 #include "base/ref_counted.h"
 #include "base/time.h"
 #include "net/base/address_family.h"
@@ -17,7 +18,7 @@
 namespace net {
 
 // Cache used by HostResolver to map hostnames to their resolved result.
-class HostCache {
+class HostCache : public NonThreadSafe {
  public:
   // Stores the latest address list that was looked up for a hostname.
   struct Entry : public base::RefCounted<Entry> {
@@ -37,22 +38,35 @@
   };
 
   struct Key {
-    Key(const std::string& hostname, AddressFamily address_family)
-        : hostname(hostname), address_family(address_family) {}
+    Key(const std::string& hostname, AddressFamily address_family,
+        HostResolverFlags host_resolver_flags)
+        : hostname(hostname),
+          address_family(address_family),
+          host_resolver_flags(host_resolver_flags) {}
 
     bool operator==(const Key& other) const {
-      return other.hostname == hostname &&
-             other.address_family == address_family;
+      // |address_family| and |host_resolver_flags| are compared before
+      // |hostname| under assumption that integer comparisons are faster than
+      // string comparisons.
+      return (other.address_family == address_family &&
+              other.host_resolver_flags == host_resolver_flags &&
+              other.hostname == hostname);
     }
 
     bool operator<(const Key& other) const {
-      if (address_family == other.address_family)
-        return hostname < other.hostname;
-      return address_family < other.address_family;
+      // |address_family| and |host_resolver_flags| are compared before
+      // |hostname| under assumption that integer comparisons are faster than
+      // string comparisons.
+      if (address_family != other.address_family)
+        return address_family < other.address_family;
+      if (host_resolver_flags != other.host_resolver_flags)
+        return host_resolver_flags < other.host_resolver_flags;
+      return hostname < other.hostname;
     }
 
     std::string hostname;
     AddressFamily address_family;
+    HostResolverFlags host_resolver_flags;
   };
 
   typedef std::map<Key, scoped_refptr<Entry> > EntryMap;
@@ -76,40 +90,24 @@
   // timestamp.
   Entry* Set(const Key& key,
              int error,
-             const AddressList addrlist,
+             const AddressList& addrlist,
              base::TimeTicks now);
 
-  // Empties the cache.
-  void clear() {
-    entries_.clear();
-  }
-
-  // Returns true if this HostCache can contain no entries.
-  bool caching_is_disabled() const {
-    return max_entries_ == 0;
-  }
+  // Empties the cache
+  void clear();
 
   // Returns the number of entries in the cache.
-  size_t size() const {
-    return entries_.size();
-  }
+  size_t size() const;
 
-  size_t max_entries() const {
-    return max_entries_;
-  }
+  // Following are used by net_internals UI.
+  size_t max_entries() const;
 
-  base::TimeDelta success_entry_ttl() const {
-    return success_entry_ttl_;
-  }
+  base::TimeDelta success_entry_ttl() const;
 
-  base::TimeDelta failure_entry_ttl() const {
-    return failure_entry_ttl_;
-  }
+  base::TimeDelta failure_entry_ttl() const;
 
   // Note that this map may contain expired entries.
-  const EntryMap& entries() const {
-    return entries_;
-  }
+  const EntryMap& entries() const;
 
  private:
   FRIEND_TEST(HostCacheTest, Compact);
@@ -122,6 +120,11 @@
   // matching |pinned_entry| will NOT be pruned.
   void Compact(base::TimeTicks now, const Entry* pinned_entry);
 
+  // Returns true if this HostCache can contain no entries.
+  bool caching_is_disabled() const {
+    return max_entries_ == 0;
+  }
+
   // Bound on total size of the cache.
   size_t max_entries_;
 
diff --git a/net/base/host_cache_unittest.cc b/net/base/host_cache_unittest.cc
index 141cfde..a851c0b 100644
--- a/net/base/host_cache_unittest.cc
+++ b/net/base/host_cache_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -20,7 +20,7 @@
 
 // Builds a key for |hostname|, defaulting the address family to unspecified.
 HostCache::Key Key(const std::string& hostname) {
-  return HostCache::Key(hostname, ADDRESS_FAMILY_UNSPECIFIED);
+  return HostCache::Key(hostname, ADDRESS_FAMILY_UNSPECIFIED, 0);
 }
 
 }  // namespace
@@ -276,8 +276,8 @@
   // t=0.
   base::TimeTicks now;
 
-  HostCache::Key key1("foobar.com", ADDRESS_FAMILY_UNSPECIFIED);
-  HostCache::Key key2("foobar.com", ADDRESS_FAMILY_IPV4);
+  HostCache::Key key1("foobar.com", ADDRESS_FAMILY_UNSPECIFIED, 0);
+  HostCache::Key key2("foobar.com", ADDRESS_FAMILY_IPV4, 0);
 
   const HostCache::Entry* entry1 = NULL;  // Entry for key1
   const HostCache::Entry* entry2 = NULL;  // Entry for key2
@@ -303,6 +303,42 @@
   EXPECT_NE(entry1, entry2);
 }
 
+// Tests that the same hostname can be duplicated in the cache, so long as
+// the HostResolverFlags differ.
+TEST(HostCacheTest, HostResolverFlagsArePartOfKey) {
+  HostCache cache(kMaxCacheEntries, kSuccessEntryTTL, kFailureEntryTTL);
+
+  // t=0.
+  base::TimeTicks now;
+
+  HostCache::Key key1("foobar.com", ADDRESS_FAMILY_IPV4, 0);
+  HostCache::Key key2("foobar.com", ADDRESS_FAMILY_IPV4,
+                      HOST_RESOLVER_CANONNAME);
+
+  const HostCache::Entry* entry1 = NULL;  // Entry for key1
+  const HostCache::Entry* entry2 = NULL;  // Entry for key2
+
+  EXPECT_EQ(0U, cache.size());
+
+  // Add an entry for ("foobar.com", IPV4, NONE) at t=0.
+  EXPECT_TRUE(cache.Lookup(key1, base::TimeTicks()) == NULL);
+  cache.Set(key1, OK, AddressList(), now);
+  entry1 = cache.Lookup(key1, base::TimeTicks());
+  EXPECT_FALSE(entry1 == NULL);
+  EXPECT_EQ(1U, cache.size());
+
+  // Add an entry for ("foobar.com", IPV4, CANONNAME) at t=0.
+  EXPECT_TRUE(cache.Lookup(key2, base::TimeTicks()) == NULL);
+  cache.Set(key2, OK, AddressList(), now);
+  entry2 = cache.Lookup(key2, base::TimeTicks());
+  EXPECT_FALSE(entry2 == NULL);
+  EXPECT_EQ(2U, cache.size());
+
+  // Even though the hostnames were the same, we should have two unique
+  // entries (because the HostResolverFlags differ).
+  EXPECT_NE(entry1, entry2);
+}
+
 TEST(HostCacheTest, NoCache) {
   // Disable caching.
   HostCache cache(0, kSuccessEntryTTL, kFailureEntryTTL);
@@ -353,33 +389,52 @@
     int expected_comparison;
   } tests[] = {
     {
-      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED),
-      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED),
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED, 0),
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED, 0),
       0
     },
     {
-      HostCache::Key("host1", ADDRESS_FAMILY_IPV4),
-      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED),
+      HostCache::Key("host1", ADDRESS_FAMILY_IPV4, 0),
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED, 0),
       1
     },
     {
-      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED),
-      HostCache::Key("host1", ADDRESS_FAMILY_IPV4),
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED, 0),
+      HostCache::Key("host1", ADDRESS_FAMILY_IPV4, 0),
       -1
     },
     {
-      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED),
-      HostCache::Key("host2", ADDRESS_FAMILY_UNSPECIFIED),
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED, 0),
+      HostCache::Key("host2", ADDRESS_FAMILY_UNSPECIFIED, 0),
       -1
     },
     {
-      HostCache::Key("host1", ADDRESS_FAMILY_IPV4),
-      HostCache::Key("host2", ADDRESS_FAMILY_UNSPECIFIED),
+      HostCache::Key("host1", ADDRESS_FAMILY_IPV4, 0),
+      HostCache::Key("host2", ADDRESS_FAMILY_UNSPECIFIED, 0),
       1
     },
     {
-      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED),
-      HostCache::Key("host2", ADDRESS_FAMILY_IPV4),
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED, 0),
+      HostCache::Key("host2", ADDRESS_FAMILY_IPV4, 0),
+      -1
+    },
+        {
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED, 0),
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED,
+                     HOST_RESOLVER_CANONNAME),
+      -1
+    },
+    {
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED,
+                     HOST_RESOLVER_CANONNAME),
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED, 0),
+      1
+    },
+    {
+      HostCache::Key("host1", ADDRESS_FAMILY_UNSPECIFIED,
+                     HOST_RESOLVER_CANONNAME),
+      HostCache::Key("host2", ADDRESS_FAMILY_UNSPECIFIED,
+                     HOST_RESOLVER_CANONNAME),
       -1
     },
   };
diff --git a/net/base/host_mapping_rules.cc b/net/base/host_mapping_rules.cc
new file mode 100644
index 0000000..3296b0e
--- /dev/null
+++ b/net/base/host_mapping_rules.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2010 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/base/host_mapping_rules.h"
+
+#include "base/logging.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_util.h"
+
+namespace net {
+
+HostMappingRules::HostMappingRules() {}
+
+bool HostMappingRules::RewriteHost(HostPortPair* host_port) const {
+  // Check if the hostname was excluded.
+  for (ExclusionRuleList::const_iterator it = exclusion_rules_.begin();
+       it != exclusion_rules_.end(); ++it) {
+    const ExclusionRule& rule = *it;
+    if (MatchPatternASCII(host_port->host, rule.hostname_pattern))
+      return false;
+  }
+
+  // Check if the hostname was remapped.
+  for (MapRuleList::const_iterator it = map_rules_.begin();
+       it != map_rules_.end(); ++it) {
+    const MapRule& rule = *it;
+
+    if (!MatchPatternASCII(host_port->host, rule.hostname_pattern))
+      continue;  // This rule doesn't apply.
+
+    host_port->host = rule.replacement_hostname;
+    if (rule.replacement_port != -1)
+      host_port->port = rule.replacement_port;
+    return true;
+  }
+
+  return false;
+}
+
+bool HostMappingRules::AddRuleFromString(const std::string& rule_string) {
+  std::string trimmed;
+  TrimWhitespaceASCII(rule_string, TRIM_ALL, &trimmed);
+  std::vector<std::string> parts;
+  SplitString(trimmed, ' ', &parts);
+
+  // Test for EXCLUSION rule.
+  if (parts.size() == 2 && LowerCaseEqualsASCII(parts[0], "exclude")) {
+    ExclusionRule rule;
+    rule.hostname_pattern = StringToLowerASCII(parts[1]);
+    exclusion_rules_.push_back(rule);
+    return true;
+  }
+
+  // Test for MAP rule.
+  if (parts.size() == 3 && LowerCaseEqualsASCII(parts[0], "map")) {
+    MapRule rule;
+    rule.hostname_pattern = StringToLowerASCII(parts[1]);
+
+    if (!ParseHostAndPort(parts[2], &rule.replacement_hostname,
+                          &rule.replacement_port)) {
+      return false;  // Failed parsing the hostname/port.
+    }
+
+    map_rules_.push_back(rule);
+    return true;
+  }
+
+  return false;
+}
+
+void HostMappingRules::SetRulesFromString(const std::string& rules_string) {
+  exclusion_rules_.clear();
+  map_rules_.clear();
+
+  StringTokenizer rules(rules_string, ",");
+  while (rules.GetNext()) {
+    bool ok = AddRuleFromString(rules.token());
+    LOG_IF(ERROR, !ok) << "Failed parsing rule: " << rules.token();
+  }
+}
+
+}  // namespace net
diff --git a/net/base/host_mapping_rules.h b/net/base/host_mapping_rules.h
new file mode 100644
index 0000000..a754a47
--- /dev/null
+++ b/net/base/host_mapping_rules.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2010 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_BASE_HOST_MAPPING_RULES_H_
+#define NET_BASE_HOST_MAPPING_RULES_H_
+
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace net {
+
+struct HostPortPair;
+
+class HostMappingRules {
+ public:
+  HostMappingRules();
+  
+  // Modifies |*host_port| based on the current rules. Returns true if the
+  // RequestInfo was modified, false otherwise.
+  bool RewriteHost(HostPortPair* host_port) const;
+
+  // Adds a rule to this mapper. The format of the rule can be one of:
+  //
+  //   "MAP" <hostname_pattern> <replacement_host> [":" <replacement_port>]
+  //   "EXCLUDE" <hostname_pattern>
+  //
+  // The <replacement_host> can be either a hostname, or an IP address literal.
+  //
+  // Returns true if the rule was successfully parsed and added.
+  bool AddRuleFromString(const std::string& rule_string);
+
+  // Sets the rules from a comma separated list of rules.
+  void SetRulesFromString(const std::string& rules_string);
+
+ private:
+  struct MapRule {
+    MapRule() : replacement_port(-1) {}
+
+    std::string hostname_pattern;
+    std::string replacement_hostname;
+    int replacement_port;
+  };
+
+  struct ExclusionRule {
+    std::string hostname_pattern;
+  };
+
+  typedef std::vector<MapRule> MapRuleList;
+  typedef std::vector<ExclusionRule> ExclusionRuleList;
+
+  MapRuleList map_rules_;
+  ExclusionRuleList exclusion_rules_;
+
+  DISALLOW_COPY_AND_ASSIGN(HostMappingRules);
+};
+
+}  // namespace net
+
+#endif  // NET_BASE_HOST_MAPPING_RULES_H_
diff --git a/net/base/host_mapping_rules_unittest.cc b/net/base/host_mapping_rules_unittest.cc
new file mode 100644
index 0000000..0cea821
--- /dev/null
+++ b/net/base/host_mapping_rules_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 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/base/host_mapping_rules.h"
+
+#include "net/base/host_port_pair.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+TEST(HostMappingRulesTest, SetRulesFromString) {
+  HostMappingRules rules;
+  rules.SetRulesFromString(
+      "map *.com baz , map *.net bar:60, EXCLUDE *.foo.com");
+
+  HostPortPair host_port("test", 1234);
+  EXPECT_FALSE(rules.RewriteHost(&host_port));
+  EXPECT_EQ("test", host_port.host);
+  EXPECT_EQ(1234u, host_port.port);
+
+  host_port = HostPortPair("chrome.net", 80);
+  EXPECT_TRUE(rules.RewriteHost(&host_port));
+  EXPECT_EQ("bar", host_port.host);
+  EXPECT_EQ(60u, host_port.port);
+
+  host_port = HostPortPair("crack.com", 80);
+  EXPECT_TRUE(rules.RewriteHost(&host_port));
+  EXPECT_EQ("baz", host_port.host);
+  EXPECT_EQ(80u, host_port.port);
+
+  host_port = HostPortPair("wtf.foo.com", 666);
+  EXPECT_FALSE(rules.RewriteHost(&host_port));
+  EXPECT_EQ("wtf.foo.com", host_port.host);
+  EXPECT_EQ(666u, host_port.port);
+}
+
+// Parsing bad rules should silently discard the rule (and never crash).
+TEST(HostMappingRulesTest, ParseInvalidRules) {
+  HostMappingRules rules;
+
+  EXPECT_FALSE(rules.AddRuleFromString("xyz"));
+  EXPECT_FALSE(rules.AddRuleFromString(""));
+  EXPECT_FALSE(rules.AddRuleFromString(" "));
+  EXPECT_FALSE(rules.AddRuleFromString("EXCLUDE"));
+  EXPECT_FALSE(rules.AddRuleFromString("EXCLUDE foo bar"));
+  EXPECT_FALSE(rules.AddRuleFromString("INCLUDE"));
+  EXPECT_FALSE(rules.AddRuleFromString("INCLUDE x"));
+  EXPECT_FALSE(rules.AddRuleFromString("INCLUDE x :10"));
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/base/host_port_pair.cc b/net/base/host_port_pair.cc
new file mode 100644
index 0000000..d4e7d4e
--- /dev/null
+++ b/net/base/host_port_pair.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2010 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/base/host_port_pair.h"
+#include "base/string_util.h"
+
+namespace net {
+
+HostPortPair::HostPortPair() : port(0) {}
+HostPortPair::HostPortPair(const std::string& in_host, uint16 in_port)
+    : host(in_host), port(in_port) {}
+
+std::string HostPortPair::ToString() const {
+  // Check to see if the host is an IPv6 address.  If so, added brackets.
+  if (host.find(':') != std::string::npos)
+    return StringPrintf("[%s]:%u", host.c_str(), port);
+  return StringPrintf("%s:%u", host.c_str(), port);
+}
+
+}  // namespace net
diff --git a/net/base/host_port_pair.h b/net/base/host_port_pair.h
new file mode 100644
index 0000000..78f74ea
--- /dev/null
+++ b/net/base/host_port_pair.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2010 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_BASE_HOST_PORT_PAIR_H_
+#define NET_BASE_HOST_PORT_PAIR_H_
+
+#include <string>
+#include "base/basictypes.h"
+
+namespace net {
+
+struct HostPortPair {
+  HostPortPair();
+  // If |in_host| represents an IPv6 address, it should not bracket the address.
+  HostPortPair(const std::string& in_host, uint16 in_port);
+
+  // TODO(willchan): Define a functor instead.
+  // Comparator function so this can be placed in a std::map.
+  // TODO(jar): Violation of style guide, and should be removed.
+  bool operator<(const HostPortPair& other) const {
+    if (port != other.port)
+      return port < other.port;
+    return host < other.host;
+  }
+
+  // Equality test of contents. (Probably another violation of style guide).
+  bool Equals(const HostPortPair& other) const {
+    return host == other.host && port == other.port;
+  }
+
+  // ToString() will convert the HostPortPair to "host:port".  If |host| is an
+  // IPv6 literal, it will add brackets around |host|.
+  std::string ToString() const;
+
+  // If |host| represents an IPv6 address, this string will not contain brackets
+  // around the address.
+  std::string host;
+  uint16 port;
+};
+
+}  // namespace net
+
+#endif  // NET_BASE_HOST_PORT_PAIR_H_
diff --git a/net/base/host_resolver.cc b/net/base/host_resolver.cc
index ebad57e..320406c 100644
--- a/net/base/host_resolver.cc
+++ b/net/base/host_resolver.cc
@@ -26,7 +26,7 @@
 int SingleRequestHostResolver::Resolve(const HostResolver::RequestInfo& info,
                                        AddressList* addresses,
                                        CompletionCallback* callback,
-                                       LoadLog* load_log) {
+                                       const BoundNetLog& net_log) {
   DCHECK(!cur_request_ && !cur_request_callback_) << "resolver already in use";
 
   HostResolver::RequestHandle request = NULL;
@@ -36,7 +36,7 @@
   CompletionCallback* transient_callback = callback ? &callback_ : NULL;
 
   int rv = resolver_->Resolve(
-      info, addresses, transient_callback, &request, load_log);
+      info, addresses, transient_callback, &request, net_log);
 
   if (rv == ERR_IO_PENDING) {
     // Cleared in OnResolveCompletion().
diff --git a/net/base/host_resolver.h b/net/base/host_resolver.h
index 32706a0..895ffc2 100644
--- a/net/base/host_resolver.h
+++ b/net/base/host_resolver.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -18,9 +18,9 @@
 namespace net {
 
 class AddressList;
+class BoundNetLog;
 class HostCache;
-class LoadLog;
-class NetworkChangeNotifier;
+class HostResolverImpl;
 
 // This class represents the task of resolving hostnames (or IP address
 // literal) to an AddressList object.
@@ -31,7 +31,7 @@
 // request at a time is to create a SingleRequestHostResolver wrapper around
 // HostResolver (which will automatically cancel the single request when it
 // goes out of scope).
-class HostResolver : public base::RefCountedThreadSafe<HostResolver> {
+class HostResolver : public base::RefCounted<HostResolver> {
  public:
   // The parameters for doing a Resolve(). |hostname| and |port| are required,
   // the rest are optional (and have reasonable defaults).
@@ -40,19 +40,34 @@
     RequestInfo(const std::string& hostname, int port)
         : hostname_(hostname),
           address_family_(ADDRESS_FAMILY_UNSPECIFIED),
+          host_resolver_flags_(0),
           port_(port),
           allow_cached_response_(true),
           is_speculative_(false),
           priority_(MEDIUM) {}
 
-    const int port() const { return port_; }
+    int port() const { return port_; }
+    void set_port(int port) {
+      port_ = port;
+    }
+
     const std::string& hostname() const { return hostname_; }
+    void set_hostname(const std::string& hostname) {
+      hostname_ = hostname;
+    }
 
     AddressFamily address_family() const { return address_family_; }
     void set_address_family(AddressFamily address_family) {
       address_family_ = address_family;
     }
 
+    HostResolverFlags host_resolver_flags() const {
+      return host_resolver_flags_;
+    }
+    void set_host_resolver_flags(HostResolverFlags host_resolver_flags) {
+      host_resolver_flags_ = host_resolver_flags;
+    }
+
     bool allow_cached_response() const { return allow_cached_response_; }
     void set_allow_cached_response(bool b) { allow_cached_response_ = b; }
 
@@ -72,6 +87,9 @@
     // The address family to restrict results to.
     AddressFamily address_family_;
 
+    // Flags to use when resolving this request.
+    HostResolverFlags host_resolver_flags_;
+
     // The port number to set in the result's sockaddrs.
     int port_;
 
@@ -113,6 +131,11 @@
   // Opaque type used to cancel a request.
   typedef void* RequestHandle;
 
+  // This value can be passed into CreateSystemHostResolver as the
+  // |max_concurrent_resolves| parameter. It will select a default level of
+  // concurrency.
+  static const size_t kDefaultParallelism = 0;
+
   // Resolves the given hostname (or IP address literal), filling out the
   // |addresses| object upon success.  The |info.port| parameter will be set as
   // the sin(6)_port field of the sockaddr_in{6} struct.  Returns OK if
@@ -129,12 +152,12 @@
   // the async request. This handle is not valid after the request has
   // completed.
   //
-  // Profiling information for the request is saved to |load_log| if non-NULL.
+  // Profiling information for the request is saved to |net_log| if non-NULL.
   virtual int Resolve(const RequestInfo& info,
                       AddressList* addresses,
                       CompletionCallback* callback,
                       RequestHandle* out_req,
-                      LoadLog* load_log) = 0;
+                      const BoundNetLog& net_log) = 0;
 
   // Cancels the specified request. |req| is the handle returned by Resolve().
   // After a request is cancelled, its completion callback will not be called.
@@ -148,22 +171,22 @@
   // Unregisters an observer previously added by AddObserver().
   virtual void RemoveObserver(Observer* observer) = 0;
 
-  // TODO(eroman): temp hack for http://crbug.com/18373
-  virtual void Shutdown() = 0;
-
   // Sets the default AddressFamily to use when requests have left it
   // unspecified. For example, this could be used to restrict resolution
   // results to AF_INET by passing in ADDRESS_FAMILY_IPV4, or to
   // AF_INET6 by passing in ADDRESS_FAMILY_IPV6.
   virtual void SetDefaultAddressFamily(AddressFamily address_family) {}
 
-  // Returns true if this HostResolver is an instance of HostResolverImpl.
-  // Used primarily to expose additional functionality on the
-  // about:net-internals page.
-  virtual bool IsHostResolverImpl() { return false; }
+  // Returns |this| cast to a HostResolverImpl*, or NULL if the subclass
+  // is not compatible with HostResolverImpl. Used primarily to expose
+  // additional functionality on the about:net-internals page.
+  virtual HostResolverImpl* GetAsHostResolverImpl() { return NULL; }
+
+  // Does additional cleanup prior to destruction.
+  virtual void Shutdown() {}
 
  protected:
-  friend class base::RefCountedThreadSafe<HostResolver>;
+  friend class base::RefCounted<HostResolver>;
 
   HostResolver() { }
 
@@ -193,7 +216,7 @@
   int Resolve(const HostResolver::RequestInfo& info,
               AddressList* addresses,
               CompletionCallback* callback,
-              LoadLog* load_log);
+              const BoundNetLog& net_log);
 
   // Cancels the in-progress request, if any. This prevents the callback
   // from being invoked. Resolve() can be called again after cancelling.
@@ -220,10 +243,10 @@
 // Creates a HostResolver implementation that queries the underlying system.
 // (Except if a unit-test has changed the global HostResolverProc using
 // ScopedHostResolverProc to intercept requests to the system).
-// |network_change_notifier| must outlive HostResolver.  It can optionally be
-// NULL, in which case HostResolver will not respond to network changes.
-HostResolver* CreateSystemHostResolver(
-    NetworkChangeNotifier* network_change_notifier);
+// |max_concurrent_resolves| is how many resolve requests will be allowed to
+// run in parallel. Pass HostResolver::kDefaultParallelism to choose a
+// default value.
+HostResolver* CreateSystemHostResolver(size_t max_concurrent_resolves);
 
 }  // namespace net
 
diff --git a/net/base/host_resolver_impl.cc b/net/base/host_resolver_impl.cc
index 94c3857..ffba57f 100644
--- a/net/base/host_resolver_impl.cc
+++ b/net/base/host_resolver_impl.cc
@@ -1,26 +1,35 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/base/host_resolver_impl.h"
 
+#if defined(OS_WIN)
+#include <Winsock2.h>
+#elif defined(OS_POSIX)
+#include <netdb.h>
+#endif
+
 #include <cmath>
 #include <deque>
+#include <vector>
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/debug_util.h"
+#include "base/histogram.h"
 #include "base/lock.h"
 #include "base/message_loop.h"
 #include "base/stl_util-inl.h"
 #include "base/string_util.h"
 #include "base/time.h"
+#include "base/values.h"
 #include "base/worker_pool.h"
 #include "net/base/address_list.h"
 #include "net/base/host_resolver_proc.h"
-#include "net/base/load_log.h"
+#include "net/base/net_log.h"
 #include "net/base/net_errors.h"
-#include "net/base/network_change_notifier.h"
+#include "net/base/net_util.h"
 
 #if defined(OS_WIN)
 #include "net/base/winsock_init.h"
@@ -36,22 +45,24 @@
   HostCache* cache = new HostCache(
       kMaxHostCacheEntries,
       base::TimeDelta::FromMinutes(1),
-      base::TimeDelta::FromSeconds(1));
+      base::TimeDelta::FromSeconds(0));  // Disable caching of failed DNS.
 
   return cache;
 }
 
 }  // anonymous namespace
 
-HostResolver* CreateSystemHostResolver(
-    NetworkChangeNotifier* network_change_notifier) {
+HostResolver* CreateSystemHostResolver(size_t max_concurrent_resolves) {
   // Maximum of 50 concurrent threads.
   // TODO(eroman): Adjust this, do some A/B experiments.
-  static const size_t kMaxJobs = 50u;
+  static const size_t kDefaultMaxJobs = 50u;
 
-  // TODO(willchan): Pass in the NetworkChangeNotifier.
-  HostResolverImpl* resolver = new HostResolverImpl(
-      NULL, CreateDefaultCache(), network_change_notifier, kMaxJobs);
+  if (max_concurrent_resolves == HostResolver::kDefaultParallelism)
+    max_concurrent_resolves = kDefaultMaxJobs;
+
+  HostResolverImpl* resolver =
+      new HostResolverImpl(NULL, CreateDefaultCache(),
+                           max_concurrent_resolves);
 
   return resolver;
 }
@@ -59,26 +70,117 @@
 static int ResolveAddrInfo(HostResolverProc* resolver_proc,
                            const std::string& host,
                            AddressFamily address_family,
-                           AddressList* out) {
+                           HostResolverFlags host_resolver_flags,
+                           AddressList* out,
+                           int* os_error) {
   if (resolver_proc) {
     // Use the custom procedure.
-    return resolver_proc->Resolve(host, address_family, out);
+    return resolver_proc->Resolve(host, address_family,
+                                  host_resolver_flags, out, os_error);
   } else {
     // Use the system procedure (getaddrinfo).
-    return SystemHostResolverProc(host, address_family, out);
+    return SystemHostResolverProc(host, address_family,
+                                  host_resolver_flags, out, os_error);
   }
 }
 
+// Extra parameters to attach to the NetLog when the resolve failed.
+class HostResolveFailedParams : public NetLog::EventParameters {
+ public:
+  HostResolveFailedParams(int net_error, int os_error, bool was_from_cache)
+      : net_error_(net_error),
+        os_error_(os_error),
+        was_from_cache_(was_from_cache) {
+  }
+
+  virtual Value* ToValue() const {
+    DictionaryValue* dict = new DictionaryValue();
+    dict->SetInteger(L"net_error", net_error_);
+    dict->SetBoolean(L"was_from_cache", was_from_cache_);
+
+    if (os_error_) {
+      dict->SetInteger(L"os_error", os_error_);
+#if defined(OS_POSIX)
+      dict->SetString(L"os_error_string", gai_strerror(os_error_));
+#elif defined(OS_WIN)
+      // Map the error code to a human-readable string.
+      LPWSTR error_string = NULL;
+      int size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                               FORMAT_MESSAGE_FROM_SYSTEM,
+                               0,  // Use the internal message table.
+                               os_error_,
+                               0,  // Use default language.
+                               (LPWSTR)&error_string,
+                               0,  // Buffer size.
+                               0);  // Arguments (unused).
+      dict->SetString(L"os_error_string", error_string);
+      LocalFree(error_string);
+#endif
+    }
+
+    return dict;
+  }
+
+ private:
+  const int net_error_;
+  const int os_error_;
+  const bool was_from_cache_;
+};
+
+// Gets a list of the likely error codes that getaddrinfo() can return
+// (non-exhaustive). These are the error codes that we will track via
+// a histogram.
+std::vector<int> GetAllGetAddrinfoOSErrors() {
+  int os_errors[] = {
+#if defined(OS_POSIX)
+    EAI_ADDRFAMILY,
+    EAI_AGAIN,
+    EAI_BADFLAGS,
+    EAI_FAIL,
+    EAI_FAMILY,
+    EAI_MEMORY,
+    EAI_NODATA,
+    EAI_NONAME,
+    EAI_SERVICE,
+    EAI_SOCKTYPE,
+    EAI_SYSTEM,
+#elif defined(OS_WIN)
+    // See: http://msdn.microsoft.com/en-us/library/ms738520(VS.85).aspx
+    WSA_NOT_ENOUGH_MEMORY,
+    WSAEAFNOSUPPORT,
+    WSAEINVAL,
+    WSAESOCKTNOSUPPORT,
+    WSAHOST_NOT_FOUND,
+    WSANO_DATA,
+    WSANO_RECOVERY,
+    WSANOTINITIALISED,
+    WSATRY_AGAIN,
+    WSATYPE_NOT_FOUND,
+#endif
+  };
+
+  // Histogram enumerations require positive numbers.
+  std::vector<int> errors;
+  for (size_t i = 0; i < arraysize(os_errors); ++i) {
+    errors.push_back(std::abs(os_errors[i]));
+    // Also add N+1 for each error, so the bucket that contains our expected
+    // error is of size 1. That way if we get unexpected error codes, they
+    // won't fall into the same buckets as the expected ones.
+    errors.push_back(std::abs(os_errors[i]) + 1);
+  }
+  return errors;
+}
+
 //-----------------------------------------------------------------------------
 
 class HostResolverImpl::Request {
  public:
-  Request(LoadLog* load_log,
+  Request(const BoundNetLog& net_log,
           int id,
           const RequestInfo& info,
           CompletionCallback* callback,
           AddressList* addresses)
-      : load_log_(load_log),
+      : net_log_(net_log),
         id_(id),
         info_(info),
         job_(NULL),
@@ -117,8 +219,8 @@
     return job_;
   }
 
-  LoadLog* load_log() const {
-    return load_log_;
+  const BoundNetLog& net_log() {
+    return net_log_;
   }
 
   int id() const {
@@ -130,7 +232,7 @@
   }
 
  private:
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
 
   // Unique ID for this request. Used by observers to identify requests.
   int id_;
@@ -152,71 +254,35 @@
 
 //-----------------------------------------------------------------------------
 
-// Threadsafe log.
-class HostResolverImpl::RequestsTrace
-    : public base::RefCountedThreadSafe<HostResolverImpl::RequestsTrace> {
- public:
-  RequestsTrace() : log_(new LoadLog(LoadLog::kUnbounded)) {}
-
-  void Add(const std::string& msg) {
-    AutoLock l(lock_);
-    LoadLog::AddString(log_, msg);
-  }
-
-  void Get(LoadLog* out) {
-    AutoLock l(lock_);
-    out->Append(log_);
-  }
-
-  void Clear() {
-    AutoLock l(lock_);
-    log_ = new LoadLog(LoadLog::kUnbounded);
-  }
-
- private:
-  Lock lock_;
-  scoped_refptr<LoadLog> log_;
-};
-
-//-----------------------------------------------------------------------------
-
 // This class represents a request to the worker pool for a "getaddrinfo()"
 // call.
 class HostResolverImpl::Job
     : public base::RefCountedThreadSafe<HostResolverImpl::Job> {
  public:
-  Job(int id, HostResolverImpl* resolver, const Key& key,
-      RequestsTrace* requests_trace)
-      : id_(id), key_(key),
+  Job(int id, HostResolverImpl* resolver, const Key& key)
+      : id_(id),
+        key_(key),
         resolver_(resolver),
         origin_loop_(MessageLoop::current()),
         resolver_proc_(resolver->effective_resolver_proc()),
-        requests_trace_(requests_trace),
-        error_(OK) {
-    if (requests_trace_) {
-      requests_trace_->Add(StringPrintf(
-          "Created job j%d for {hostname='%s', address_family=%d}",
-          id_, key.hostname.c_str(),
-          static_cast<int>(key.address_family)));
-    }
+        error_(OK),
+        os_error_(0),
+        had_non_speculative_request_(false) {
   }
 
   // Attaches a request to this job. The job takes ownership of |req| and will
   // take care to delete it.
   void AddRequest(Request* req) {
-    if (requests_trace_) {
-      requests_trace_->Add(StringPrintf(
-          "Attached request r%d to job j%d", req->id(), id_));
-    }
-
     req->set_job(this);
     requests_.push_back(req);
+
+    if (!req->info().is_speculative())
+      had_non_speculative_request_ = true;
   }
 
   // Called from origin loop.
   void Start() {
-    if (requests_trace_)
-      requests_trace_->Add(StringPrintf("Starting job j%d", id_));
+    start_time_ = base::TimeTicks::Now();
 
     // Dispatch the job to a worker thread.
     if (!WorkerPool::PostTask(FROM_HERE,
@@ -237,9 +303,6 @@
     HostResolver* resolver = resolver_;
     resolver_ = NULL;
 
-    if (requests_trace_)
-      requests_trace_->Add(StringPrintf("Cancelled job j%d", id_));
-
     // Mark the job as cancelled, so when worker thread completes it will
     // not try to post completion to origin loop.
     {
@@ -267,6 +330,14 @@
     return key_;
   }
 
+  int id() const {
+    return id_;
+  }
+
+  base::TimeTicks start_time() const {
+    return start_time_;
+  }
+
   // Called from origin thread.
   const RequestsList& requests() const {
     return requests_;
@@ -279,6 +350,11 @@
     return requests_[0];
   }
 
+  // Returns true if |req_info| can be fulfilled by this job.
+  bool CanServiceRequest(const RequestInfo& req_info) const {
+    return key_ == resolver_->GetEffectiveKeyForRequest(req_info);
+  }
+
  private:
   friend class base::RefCountedThreadSafe<HostResolverImpl::Job>;
 
@@ -287,37 +363,28 @@
     STLDeleteElements(&requests_);
   }
 
+  // WARNING: This code runs inside a worker pool. The shutdown code cannot
+  // wait for it to finish, so we must be very careful here about using other
+  // objects (like MessageLoops, Singletons, etc). During shutdown these objects
+  // may no longer exist.
   void DoLookup() {
-    if (requests_trace_) {
-      requests_trace_->Add(StringPrintf(
-          "[resolver thread] Running job j%d", id_));
-    }
-
     // Running on the worker thread
     error_ = ResolveAddrInfo(resolver_proc_,
                              key_.hostname,
                              key_.address_family,
-                             &results_);
-
-    if (requests_trace_) {
-      requests_trace_->Add(StringPrintf(
-          "[resolver thread] Completed job j%d", id_));
-    }
-
-    Task* reply = NewRunnableMethod(this, &Job::OnLookupComplete);
+                             key_.host_resolver_flags,
+                             &results_,
+                             &os_error_);
 
     // The origin loop could go away while we are trying to post to it, so we
     // need to call its PostTask method inside a lock.  See ~HostResolver.
     {
       AutoLock locked(origin_loop_lock_);
       if (origin_loop_) {
-        origin_loop_->PostTask(FROM_HERE, reply);
-        reply = NULL;
+        origin_loop_->PostTask(FROM_HERE,
+                               NewRunnableMethod(this, &Job::OnLookupComplete));
       }
     }
-
-    // Does nothing if it got posted.
-    delete reply;
   }
 
   // Callback for when DoLookup() completes (runs on origin thread).
@@ -328,8 +395,18 @@
     //DCHECK_EQ(origin_loop_, MessageLoop::current());
     DCHECK(error_ || results_.head());
 
-    if (requests_trace_)
-      requests_trace_->Add(StringPrintf("Completing job j%d", id_));
+    base::TimeDelta job_duration = base::TimeTicks::Now() - start_time_;
+
+    if (had_non_speculative_request_) {
+      // TODO(eroman): Add histogram for job times of non-speculative
+      // requests.
+    }
+
+    if (error_ != OK) {
+      UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.OSErrorsForGetAddrinfo",
+                                       std::abs(os_error_),
+                                       GetAllGetAddrinfoOSErrors());
+    }
 
     if (was_cancelled())
       return;
@@ -340,7 +417,7 @@
     if (error_ == OK)
       results_.SetPort(requests_[0]->port());
 
-    resolver_->OnJobComplete(this, error_, results_);
+    resolver_->OnJobComplete(this, error_, os_error_, results_);
   }
 
   // Immutable. Can be read from either thread,
@@ -363,18 +440,122 @@
   // reference ensures that it remains valid until we are done.
   scoped_refptr<HostResolverProc> resolver_proc_;
 
-  // Thread safe log to write details into, or NULL.
-  scoped_refptr<RequestsTrace> requests_trace_;
-
   // Assigned on the worker thread, read on the origin thread.
   int error_;
+  int os_error_;
+
+  // True if a non-speculative request was ever attached to this job
+  // (regardless of whether or not it was later cancelled.
+  // This boolean is used for histogramming the duration of jobs used to
+  // service non-speculative requests.
+  bool had_non_speculative_request_;
+
   AddressList results_;
 
+  // The time when the job was started.
+  base::TimeTicks start_time_;
+
   DISALLOW_COPY_AND_ASSIGN(Job);
 };
 
 //-----------------------------------------------------------------------------
 
+// This class represents a request to the worker pool for a "probe for IPv6
+// support" call.
+class HostResolverImpl::IPv6ProbeJob
+    : public base::RefCountedThreadSafe<HostResolverImpl::IPv6ProbeJob> {
+ public:
+  explicit IPv6ProbeJob(HostResolverImpl* resolver)
+      : resolver_(resolver),
+        origin_loop_(MessageLoop::current()) {
+    DCHECK(!was_cancelled());
+  }
+
+  void Start() {
+    if (was_cancelled())
+      return;
+    DCHECK(IsOnOriginThread());
+    const bool kIsSlow = true;
+    WorkerPool::PostTask(
+        FROM_HERE, NewRunnableMethod(this, &IPv6ProbeJob::DoProbe), kIsSlow);
+  }
+
+  // Cancels the current job.
+  void Cancel() {
+    if (was_cancelled())
+      return;
+    DCHECK(IsOnOriginThread());
+    resolver_ = NULL;  // Read/write ONLY on origin thread.
+    {
+      AutoLock locked(origin_loop_lock_);
+      // Origin loop may be destroyed before we can use it!
+      origin_loop_ = NULL;  // Write ONLY on origin thread.
+    }
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<HostResolverImpl::IPv6ProbeJob>;
+
+  ~IPv6ProbeJob() {
+  }
+
+  // Should be run on |orgin_thread_|, but that may not be well defined now.
+  bool was_cancelled() const {
+    if (!resolver_ || !origin_loop_) {
+      DCHECK(!resolver_);
+      DCHECK(!origin_loop_);
+      return true;
+    }
+    return false;
+  }
+
+  // Run on worker thread.
+  void DoProbe() {
+    // Do actual testing on this thread, as it takes 40-100ms.
+    AddressFamily family = IPv6Supported() ? ADDRESS_FAMILY_UNSPECIFIED
+                                           : ADDRESS_FAMILY_IPV4;
+
+    Task* reply = NewRunnableMethod(this, &IPv6ProbeJob::OnProbeComplete,
+                                    family);
+
+    // The origin loop could go away while we are trying to post to it, so we
+    // need to call its PostTask method inside a lock.  See ~HostResolver.
+    {
+      AutoLock locked(origin_loop_lock_);
+      if (origin_loop_) {
+        origin_loop_->PostTask(FROM_HERE, reply);
+        return;
+      }
+    }
+
+    // We didn't post, so delete the reply.
+    delete reply;
+  }
+
+  // Callback for when DoProbe() completes (runs on origin thread).
+  void OnProbeComplete(AddressFamily address_family) {
+    if (was_cancelled())
+      return;
+    DCHECK(IsOnOriginThread());
+    resolver_->IPv6ProbeSetDefaultAddressFamily(address_family);
+  }
+
+  bool IsOnOriginThread() const {
+    return !MessageLoop::current() || origin_loop_ == MessageLoop::current();
+  }
+
+  // Used/set only on origin thread.
+  HostResolverImpl* resolver_;
+
+  // Used to post ourselves onto the origin thread.
+  Lock origin_loop_lock_;
+  MessageLoop* origin_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(IPv6ProbeJob);
+};
+
+//-----------------------------------------------------------------------------
+
 // We rely on the priority enum values being sequential having starting at 0,
 // and increasing for lower priorities.
 COMPILE_ASSERT(HIGHEST == 0u &&
@@ -402,7 +583,7 @@
   // specific meaning of these parameters.
   void SetConstraints(size_t max_outstanding_jobs,
                       size_t max_pending_requests) {
-    CHECK(max_outstanding_jobs != 0u);
+    CHECK_NE(max_outstanding_jobs, 0u);
     max_outstanding_jobs_ = max_outstanding_jobs;
     max_pending_requests_ = max_pending_requests;
   }
@@ -483,15 +664,15 @@
   }
 
   // Removes any pending requests from the queue which are for the
-  // same hostname/address-family as |job|, and attaches them to |job|.
+  // same (hostname / effective address-family) as |job|, and attaches them to
+  // |job|.
   void MoveRequestsToJob(Job* job) {
     for (size_t i = 0u; i < arraysize(pending_requests_); ++i) {
       PendingRequestsQueue& q = pending_requests_[i];
       PendingRequestsQueue::iterator req_it = q.begin();
       while (req_it != q.end()) {
         Request* req = *req_it;
-        Key req_key(req->info().hostname(), req->info().address_family());
-        if (req_key == job->key()) {
+        if (job->CanServiceRequest(req->info())) {
           // Job takes ownership of |req|.
           job->AddRequest(req);
           req_it = q.erase(req_it);
@@ -526,7 +707,6 @@
 HostResolverImpl::HostResolverImpl(
     HostResolverProc* resolver_proc,
     HostCache* cache,
-    NetworkChangeNotifier* network_change_notifier,
     size_t max_jobs)
     : cache_(cache),
       max_jobs_(max_jobs),
@@ -535,7 +715,7 @@
       resolver_proc_(resolver_proc),
       default_address_family_(ADDRESS_FAMILY_UNSPECIFIED),
       shutdown_(false),
-      network_change_notifier_(network_change_notifier) {
+      ipv6_probe_monitoring_(false) {
   DCHECK_GT(max_jobs, 0u);
 
   // It is cumbersome to expose all of the constraints in the constructor,
@@ -545,13 +725,14 @@
 #if defined(OS_WIN)
   EnsureWinsockInit();
 #endif
-  if (network_change_notifier_)
-    network_change_notifier_->AddObserver(this);
+  NetworkChangeNotifier::AddObserver(this);
 }
 
 HostResolverImpl::~HostResolverImpl() {
   // Cancel the outstanding jobs. Those jobs may contain several attached
   // requests, which will also be cancelled.
+  DiscardIPv6ProbeJob();
+
   for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
     it->second->Cancel();
 
@@ -559,8 +740,7 @@
   if (cur_completing_job_)
     cur_completing_job_->Cancel();
 
-  if (network_change_notifier_)
-    network_change_notifier_->RemoveObserver(this);
+  NetworkChangeNotifier::RemoveObserver(this);
 
   // Delete the job pools.
   for (size_t i = 0u; i < arraysize(job_pools_); ++i)
@@ -573,43 +753,47 @@
                               AddressList* addresses,
                               CompletionCallback* callback,
                               RequestHandle* out_req,
-                              LoadLog* load_log) {
+                              const BoundNetLog& net_log) {
+  DCHECK(CalledOnValidThread());
+
   if (shutdown_)
     return ERR_UNEXPECTED;
 
   // Choose a unique ID number for observers to see.
   int request_id = next_request_id_++;
 
-  // Update the load log and notify registered observers.
-  OnStartRequest(load_log, request_id, info);
+  // Update the net log and notify registered observers.
+  OnStartRequest(net_log, request_id, info);
 
   // Build a key that identifies the request in the cache and in the
   // outstanding jobs map.
-  Key key(info.hostname(), info.address_family());
-  if (key.address_family == ADDRESS_FAMILY_UNSPECIFIED)
-    key.address_family = default_address_family_;
+  Key key = GetEffectiveKeyForRequest(info);
 
   // If we have an unexpired cache entry, use it.
   if (info.allow_cached_response() && cache_.get()) {
     const HostCache::Entry* cache_entry = cache_->Lookup(
         key, base::TimeTicks::Now());
     if (cache_entry) {
-      int error = cache_entry->error;
-      if (error == OK)
+      int net_error = cache_entry->error;
+      if (net_error == OK)
         addresses->SetFrom(cache_entry->addrlist, info.port());
 
-      // Update the load log and notify registered observers.
-      OnFinishRequest(load_log, request_id, info, error);
+      // Update the net log and notify registered observers.
+      OnFinishRequest(net_log, request_id, info, net_error,
+                      0,  /* os_error (unknown since from cache) */
+                      true  /* was_from_cache */);
 
-      return error;
+      return net_error;
     }
   }
 
   // If no callback was specified, do a synchronous resolution.
   if (!callback) {
     AddressList addrlist;
+    int os_error = 0;
     int error = ResolveAddrInfo(
-        effective_resolver_proc(), key.hostname, key.address_family, &addrlist);
+        effective_resolver_proc(), key.hostname, key.address_family,
+        key.host_resolver_flags, &addrlist, &os_error);
     if (error == OK) {
       addrlist.SetPort(info.port());
       *addresses = addrlist;
@@ -619,15 +803,16 @@
     if (cache_.get())
       cache_->Set(key, error, addrlist, base::TimeTicks::Now());
 
-    // Update the load log and notify registered observers.
-    OnFinishRequest(load_log, request_id, info, error);
+    // Update the net log and notify registered observers.
+    OnFinishRequest(net_log, request_id, info, error, os_error,
+                    false /* was_from_cache */);
 
     return error;
   }
 
   // Create a handle for this request, and pass it back to the user if they
   // asked for it (out_req != NULL).
-  Request* req = new Request(load_log, request_id, info, callback, addresses);
+  Request* req = new Request(net_log, request_id, info, callback, addresses);
   if (out_req)
     *out_req = reinterpret_cast<RequestHandle>(req);
 
@@ -656,6 +841,7 @@
 // See OnJobComplete(Job*) for why it is important not to clean out
 // cancelled requests from Job::requests_.
 void HostResolverImpl::CancelRequest(RequestHandle req_handle) {
+  DCHECK(CalledOnValidThread());
   if (shutdown_) {
     // TODO(eroman): temp hack for: http://crbug.com/18373
     // Because we destroy outstanding requests during Shutdown(),
@@ -681,14 +867,16 @@
 
   // NULL out the fields of req, to mark it as cancelled.
   req->MarkAsCancelled();
-  OnCancelRequest(req->load_log(), req->id(), req->info());
+  OnCancelRequest(req->net_log(), req->id(), req->info());
 }
 
 void HostResolverImpl::AddObserver(HostResolver::Observer* observer) {
+  DCHECK(CalledOnValidThread());
   observers_.push_back(observer);
 }
 
 void HostResolverImpl::RemoveObserver(HostResolver::Observer* observer) {
+  DCHECK(CalledOnValidThread());
   ObserversList::iterator it =
       std::find(observers_.begin(), observers_.end(), observer);
 
@@ -698,56 +886,37 @@
   observers_.erase(it);
 }
 
+void HostResolverImpl::SetDefaultAddressFamily(AddressFamily address_family) {
+  DCHECK(CalledOnValidThread());
+  ipv6_probe_monitoring_ = false;
+  DiscardIPv6ProbeJob();
+  default_address_family_ = address_family;
+}
+
+void HostResolverImpl::ProbeIPv6Support() {
+  DCHECK(CalledOnValidThread());
+  DCHECK(!ipv6_probe_monitoring_);
+  ipv6_probe_monitoring_ = true;
+  OnIPAddressChanged();  // Give initial setup call.
+}
+
 void HostResolverImpl::Shutdown() {
+  DCHECK(CalledOnValidThread());
   shutdown_ = true;
 
   // Cancel the outstanding jobs.
   for (JobMap::iterator it = jobs_.begin(); it != jobs_.end(); ++it)
     it->second->Cancel();
   jobs_.clear();
-}
-
-void HostResolverImpl::ClearRequestsTrace() {
-  if (requests_trace_)
-    requests_trace_->Clear();
-}
-
-void HostResolverImpl::EnableRequestsTracing(bool enable) {
-  requests_trace_ = enable ? new RequestsTrace : NULL;
-  if (enable) {
-    // Print the state of the world when logging was started.
-    requests_trace_->Add("Enabled tracing");
-    requests_trace_->Add(StringPrintf(
-        "Current num outstanding jobs: %d",
-        static_cast<int>(jobs_.size())));
-
-    size_t total = 0u;
-    for (size_t i = 0; i < arraysize(job_pools_); ++i)
-      total += job_pools_[i]->GetNumPendingRequests();
-
-    requests_trace_->Add(StringPrintf(
-        "Number of queued requests: %d", static_cast<int>(total)));
-  }
-}
-
-bool HostResolverImpl::IsRequestsTracingEnabled() const {
-  return !!requests_trace_;  // Cast to bool.
-}
-
-scoped_refptr<LoadLog> HostResolverImpl::GetRequestsTrace() {
-  if (!requests_trace_)
-    return NULL;
-
-  scoped_refptr<LoadLog> copy_of_log = new LoadLog(LoadLog::kUnbounded);
-  requests_trace_->Get(copy_of_log);
-  return copy_of_log;
+  DiscardIPv6ProbeJob();
 }
 
 void HostResolverImpl::SetPoolConstraints(JobPoolIndex pool_index,
                                           size_t max_outstanding_jobs,
                                           size_t max_pending_requests) {
-  CHECK(pool_index >= 0);
-  CHECK(pool_index < POOL_COUNT);
+  DCHECK(CalledOnValidThread());
+  CHECK_GE(pool_index, 0);
+  CHECK_LT(pool_index, POOL_COUNT);
   CHECK(jobs_.empty()) << "Can only set constraints during setup";
   JobPool* pool = job_pools_[pool_index];
   pool->SetConstraints(max_outstanding_jobs, max_pending_requests);
@@ -780,13 +949,14 @@
 }
 
 void HostResolverImpl::OnJobComplete(Job* job,
-                                     int error,
+                                     int net_error,
+                                     int os_error,
                                      const AddressList& addrlist) {
   RemoveOutstandingJob(job);
 
   // Write result to the cache.
   if (cache_.get())
-    cache_->Set(job->key(), error, addrlist, base::TimeTicks::Now());
+    cache_->Set(job->key(), net_error, addrlist, base::TimeTicks::Now());
 
   // Make a note that we are executing within OnJobComplete() in case the
   // HostResolver is deleted by a callback invocation.
@@ -803,10 +973,11 @@
     if (!req->was_cancelled()) {
       DCHECK_EQ(job, req->job());
 
-      // Update the load log and notify registered observers.
-      OnFinishRequest(req->load_log(), req->id(), req->info(), error);
+      // Update the net log and notify registered observers.
+      OnFinishRequest(req->net_log(), req->id(), req->info(), net_error,
+                      os_error, false  /* was_from_cache */);
 
-      req->OnComplete(error, addrlist);
+      req->OnComplete(net_error, addrlist);
 
       // Check if the job was cancelled as a result of running the callback.
       // (Meaning that |this| was deleted).
@@ -818,95 +989,93 @@
   cur_completing_job_ = NULL;
 }
 
-void HostResolverImpl::OnStartRequest(LoadLog* load_log,
+void HostResolverImpl::OnStartRequest(const BoundNetLog& net_log,
                                       int request_id,
                                       const RequestInfo& info) {
-  LoadLog::BeginEvent(load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL);
-
-  if (requests_trace_) {
-    requests_trace_->Add(StringPrintf(
-        "Received request r%d for {hostname='%s', port=%d, priority=%d, "
-        "speculative=%d, address_family=%d, allow_cached=%d, referrer='%s'}",
-         request_id,
-         info.hostname().c_str(),
-         info.port(),
-         static_cast<int>(info.priority()),
-         static_cast<int>(info.is_speculative()),
-         static_cast<int>(info.address_family()),
-         static_cast<int>(info.allow_cached_response()),
-         info.referrer().spec().c_str()));
-  }
+  net_log.BeginEvent(NetLog::TYPE_HOST_RESOLVER_IMPL, NULL);
 
   // Notify the observers of the start.
   if (!observers_.empty()) {
-    LoadLog::BeginEvent(
-        load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART);
-
     for (ObserversList::iterator it = observers_.begin();
          it != observers_.end(); ++it) {
       (*it)->OnStartResolution(request_id, info);
     }
-
-    LoadLog::EndEvent(
-        load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART);
   }
 }
 
-void HostResolverImpl::OnFinishRequest(LoadLog* load_log,
+void HostResolverImpl::OnFinishRequest(const BoundNetLog& net_log,
                                        int request_id,
                                        const RequestInfo& info,
-                                       int error) {
-  if (requests_trace_) {
-    requests_trace_->Add(StringPrintf(
-        "Finished request r%d with error=%d", request_id, error));
-  }
+                                       int net_error,
+                                       int os_error,
+                                       bool was_from_cache) {
+  bool was_resolved = net_error == OK;
 
   // Notify the observers of the completion.
   if (!observers_.empty()) {
-    LoadLog::BeginEvent(
-        load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONFINISH);
-
-    bool was_resolved = error == OK;
     for (ObserversList::iterator it = observers_.begin();
          it != observers_.end(); ++it) {
       (*it)->OnFinishResolutionWithStatus(request_id, was_resolved, info);
     }
-
-    LoadLog::EndEvent(
-        load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONFINISH);
   }
 
-  LoadLog::EndEvent(load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL);
+  // Log some extra parameters on failure.
+  scoped_refptr<NetLog::EventParameters> params;
+  if (!was_resolved)
+    params = new HostResolveFailedParams(net_error, os_error, was_from_cache);
+
+  net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL, params);
 }
 
-void HostResolverImpl::OnCancelRequest(LoadLog* load_log,
+void HostResolverImpl::OnCancelRequest(const BoundNetLog& net_log,
                                        int request_id,
                                        const RequestInfo& info) {
-  LoadLog::AddEvent(load_log, LoadLog::TYPE_CANCELLED);
-
-  if (requests_trace_)
-    requests_trace_->Add(StringPrintf("Cancelled request r%d", request_id));
+  net_log.AddEvent(NetLog::TYPE_CANCELLED, NULL);
 
   // Notify the observers of the cancellation.
   if (!observers_.empty()) {
-    LoadLog::BeginEvent(
-        load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONCANCEL);
-
     for (ObserversList::iterator it = observers_.begin();
          it != observers_.end(); ++it) {
       (*it)->OnCancelResolution(request_id, info);
     }
-
-    LoadLog::EndEvent(
-        load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONCANCEL);
   }
 
-  LoadLog::EndEvent(load_log, LoadLog::TYPE_HOST_RESOLVER_IMPL);
+  net_log.EndEvent(NetLog::TYPE_HOST_RESOLVER_IMPL, NULL);
 }
 
 void HostResolverImpl::OnIPAddressChanged() {
   if (cache_.get())
     cache_->clear();
+  if (ipv6_probe_monitoring_) {
+    DCHECK(!shutdown_);
+    if (shutdown_)
+      return;
+    DiscardIPv6ProbeJob();
+    ipv6_probe_job_ = new IPv6ProbeJob(this);
+    ipv6_probe_job_->Start();
+  }
+}
+
+void HostResolverImpl::DiscardIPv6ProbeJob() {
+  if (ipv6_probe_job_.get()) {
+    ipv6_probe_job_->Cancel();
+    ipv6_probe_job_ = NULL;
+  }
+}
+
+void HostResolverImpl::IPv6ProbeSetDefaultAddressFamily(
+    AddressFamily address_family) {
+  DCHECK(address_family == ADDRESS_FAMILY_UNSPECIFIED ||
+         address_family == ADDRESS_FAMILY_IPV4);
+  if (default_address_family_ != address_family) {
+    LOG(INFO) << "IPv6Probe forced AddressFamily setting to "
+              << ((address_family == ADDRESS_FAMILY_UNSPECIFIED)
+                  ? "ADDRESS_FAMILY_UNSPECIFIED"
+                  : "ADDRESS_FAMILY_IPV4");
+  }
+  default_address_family_ = address_family;
+  // Drop reference since the job has called us back.
+  DiscardIPv6ProbeJob();
 }
 
 // static
@@ -949,10 +1118,19 @@
   }
 }
 
+HostResolverImpl::Key HostResolverImpl::GetEffectiveKeyForRequest(
+    const RequestInfo& info) const {
+  AddressFamily effective_address_family = info.address_family();
+  if (effective_address_family == ADDRESS_FAMILY_UNSPECIFIED)
+    effective_address_family = default_address_family_;
+  return Key(info.hostname(), effective_address_family,
+             info.host_resolver_flags());
+}
+
 HostResolverImpl::Job* HostResolverImpl::CreateAndStartJob(Request* req) {
   DCHECK(CanCreateJobForPool(*GetPoolForRequest(req)));
-  Key key(req->info().hostname(), req->info().address_family());
-  scoped_refptr<Job> job = new Job(next_job_id_++, this, key, requests_trace_);
+  Key key = GetEffectiveKeyForRequest(req->info());
+  scoped_refptr<Job> job = new Job(next_job_id_++, this, key);
   job->AddRequest(req);
   AddOutstandingJob(job);
   job->Start();
@@ -960,9 +1138,6 @@
 }
 
 int HostResolverImpl::EnqueueRequest(JobPool* pool, Request* req) {
-  if (requests_trace_)
-    requests_trace_->Add(StringPrintf("Queued request r%d", req->id()));
-
   scoped_ptr<Request> req_evicted_from_queue(
       pool->InsertPendingRequest(req));
 
@@ -971,10 +1146,9 @@
     Request* r = req_evicted_from_queue.get();
     int error = ERR_HOST_RESOLVER_QUEUE_TOO_LARGE;
 
-    if (requests_trace_)
-      requests_trace_->Add(StringPrintf("Evicted request r%d", r->id()));
-
-    OnFinishRequest(r->load_log(), r->id(), r->info(), error);
+    OnFinishRequest(r->net_log(), r->id(), r->info(), error,
+                    0,  /* os_error (not applicable) */
+                    false  /* was_from_cache */);
 
     if (r == req)
       return error;
diff --git a/net/base/host_resolver_impl.h b/net/base/host_resolver_impl.h
index feedc05..28526d1 100644
--- a/net/base/host_resolver_impl.h
+++ b/net/base/host_resolver_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -8,10 +8,13 @@
 #include <string>
 #include <vector>
 
+#include "base/non_thread_safe.h"
 #include "base/scoped_ptr.h"
+#include "net/base/capturing_net_log.h"
 #include "net/base/host_cache.h"
 #include "net/base/host_resolver.h"
 #include "net/base/host_resolver_proc.h"
+#include "net/base/net_log.h"
 #include "net/base/network_change_notifier.h"
 
 namespace net {
@@ -47,6 +50,7 @@
 // Requests are ordered in the queue based on their priority.
 
 class HostResolverImpl : public HostResolver,
+                         public NonThreadSafe,
                          public NetworkChangeNotifier::Observer {
  public:
   // The index into |job_pools_| for the various job pools. Pools with a higher
@@ -69,13 +73,10 @@
   // thread-safe since it is run from multiple worker threads. If
   // |resolver_proc| is NULL then the default host resolver procedure is
   // used (which is SystemHostResolverProc except if overridden).
-  // |notifier| must outlive HostResolverImpl.  It can optionally be NULL, in
-  // which case HostResolverImpl will not respond to network changes.
   // |max_jobs| specifies the maximum number of threads that the host resolver
   // will use. Use SetPoolConstraints() to specify finer-grain settings.
   HostResolverImpl(HostResolverProc* resolver_proc,
                    HostCache* cache,
-                   NetworkChangeNotifier* notifier,
                    size_t max_jobs);
 
   // HostResolver methods:
@@ -83,34 +84,26 @@
                       AddressList* addresses,
                       CompletionCallback* callback,
                       RequestHandle* out_req,
-                      LoadLog* load_log);
+                      const BoundNetLog& net_log);
   virtual void CancelRequest(RequestHandle req);
   virtual void AddObserver(HostResolver::Observer* observer);
   virtual void RemoveObserver(HostResolver::Observer* observer);
 
-  // TODO(eroman): temp hack for http://crbug.com/15513
+  // Set address family, and disable IPv6 probe support.
+  virtual void SetDefaultAddressFamily(AddressFamily address_family);
+
+  // Continuously observe whether IPv6 is supported, and set the allowable
+  // address family to IPv4 iff IPv6 is not supported.
+  void ProbeIPv6Support();
+
+  virtual HostResolverImpl* GetAsHostResolverImpl() { return this; }
+
+  // TODO(eroman): hack for http://crbug.com/15513
   virtual void Shutdown();
 
-  virtual void SetDefaultAddressFamily(AddressFamily address_family) {
-    default_address_family_ = address_family;
-  }
-
-  virtual bool IsHostResolverImpl() { return true; }
-
   // Returns the cache this resolver uses, or NULL if caching is disabled.
   HostCache* cache() { return cache_.get(); }
 
-  // Clears the request trace log.
-  void ClearRequestsTrace();
-
-  // Starts/ends capturing requests to a trace log.
-  void EnableRequestsTracing(bool enable);
-
-  bool IsRequestsTracingEnabled() const;
-
-  // Returns a copy of the requests trace log, or NULL if there is none.
-  scoped_refptr<LoadLog> GetRequestsTrace();
-
   // Applies a set of constraints for requests that belong to the specified
   // pool. NOTE: Don't call this after requests have been already been started.
   //
@@ -129,8 +122,8 @@
  private:
   class Job;
   class JobPool;
+  class IPv6ProbeJob;
   class Request;
-  class RequestsTrace;
   typedef std::vector<Request*> RequestsList;
   typedef HostCache::Key Key;
   typedef std::map<Key, scoped_refptr<Job> > JobMap;
@@ -156,28 +149,37 @@
   // Removes |job| from the outstanding jobs list.
   void RemoveOutstandingJob(Job* job);
 
-  // Callback for when |job| has completed with |error| and |addrlist|.
-  void OnJobComplete(Job* job, int error, const AddressList& addrlist);
+  // Callback for when |job| has completed with |net_error| and |addrlist|.
+  void OnJobComplete(Job* job, int net_error, int os_error,
+                     const AddressList& addrlist);
 
   // Called when a request has just been started.
-  void OnStartRequest(LoadLog* load_log,
+  void OnStartRequest(const BoundNetLog& net_log,
                       int request_id,
                       const RequestInfo& info);
 
   // Called when a request has just completed (before its callback is run).
-  void OnFinishRequest(LoadLog* load_log,
+  void OnFinishRequest(const BoundNetLog& net_log,
                        int request_id,
                        const RequestInfo& info,
-                       int error);
+                       int net_error,
+                       int os_error,
+                       bool was_from_cache);
 
   // Called when a request has been cancelled.
-  void OnCancelRequest(LoadLog* load_log,
+  void OnCancelRequest(const BoundNetLog& net_log,
                        int request_id,
                        const RequestInfo& info);
 
   // NetworkChangeNotifier::Observer methods:
   virtual void OnIPAddressChanged();
 
+  // Notify IPv6ProbeJob not to call back, and discard reference to the job.
+  void DiscardIPv6ProbeJob();
+
+  // Callback from IPv6 probe activity.
+  void IPv6ProbeSetDefaultAddressFamily(AddressFamily address_family);
+
   // Returns true if the constraints for |pool| are met, and a new job can be
   // created for this pool.
   bool CanCreateJobForPool(const JobPool& pool) const;
@@ -193,6 +195,11 @@
   // may have multiple requests attached to it.
   void ProcessQueuedRequests();
 
+  // Returns the (hostname, address_family) key to use for |info|, choosing an
+  // "effective" address family by inheriting the resolver's default address
+  // family when the request leaves it unspecified.
+  Key GetEffectiveKeyForRequest(const RequestInfo& info) const;
+
   // Attaches |req| to a new job, and starts it. Returns that job.
   Job* CreateAndStartJob(Request* req);
 
@@ -234,12 +241,16 @@
   // Address family to use when the request doesn't specify one.
   AddressFamily default_address_family_;
 
-  // TODO(eroman): temp hack for http://crbug.com/15513
+  // TODO(eroman): hack for http://crbug.com/15513
   bool shutdown_;
 
-  NetworkChangeNotifier* const network_change_notifier_;
+  // Indicate if probing is done after each network change event to set address
+  // family.
+  // When false, explicit setting of address family is used.
+  bool ipv6_probe_monitoring_;
 
-  scoped_refptr<RequestsTrace> requests_trace_;
+  // The last un-cancelled IPv6ProbeJob (if any).
+  scoped_refptr<IPv6ProbeJob> ipv6_probe_job_;
 
   DISALLOW_COPY_AND_ASSIGN(HostResolverImpl);
 };
diff --git a/net/base/host_resolver_impl_unittest.cc b/net/base/host_resolver_impl_unittest.cc
index 8ca58f0..6b33a56 100644
--- a/net/base/host_resolver_impl_unittest.cc
+++ b/net/base/host_resolver_impl_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -9,12 +9,13 @@
 #include "base/compiler_specific.h"
 #include "base/message_loop.h"
 #include "base/ref_counted.h"
+#include "base/string_util.h"
 #include "net/base/address_list.h"
 #include "net/base/completion_callback.h"
-#include "net/base/load_log_unittest.h"
 #include "net/base/mock_host_resolver.h"
-#include "net/base/mock_network_change_notifier.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log_unittest.h"
+#include "net/base/net_util.h"
 #include "net/base/sys_addrinfo.h"
 #include "net/base/test_completion_callback.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -37,11 +38,7 @@
 static const size_t kMaxJobs = 10u;
 
 HostResolverImpl* CreateHostResolverImpl(HostResolverProc* resolver_proc) {
-  return new HostResolverImpl(
-      resolver_proc,
-      CreateDefaultCache(),
-      NULL,  // network_change_notifier
-      kMaxJobs);
+  return new HostResolverImpl(resolver_proc, CreateDefaultCache(), kMaxJobs);
 }
 
 // Helper to create a HostResolver::RequestInfo.
@@ -53,11 +50,31 @@
   return info;
 }
 
+// Helper to create a HostResolver::RequestInfo.
+HostResolver::RequestInfo CreateResolverRequestForAddressFamily(
+    const std::string& hostname,
+    RequestPriority priority,
+    AddressFamily address_family) {
+  HostResolver::RequestInfo info(hostname, 80);
+  info.set_priority(priority);
+  info.set_address_family(address_family);
+  return info;
+}
+
 // A variant of WaitingHostResolverProc that pushes each host mapped into a
 // list.
 // (and uses a manual-reset event rather than auto-reset).
 class CapturingHostResolverProc : public HostResolverProc {
  public:
+  struct CaptureEntry {
+    CaptureEntry(const std::string& hostname, AddressFamily address_family)
+        : hostname(hostname), address_family(address_family) {}
+    std::string hostname;
+    AddressFamily address_family;
+  };
+
+  typedef std::vector<CaptureEntry> CaptureList;
+
   explicit CapturingHostResolverProc(HostResolverProc* previous)
       : HostResolverProc(previous), event_(true, false) {
   }
@@ -66,19 +83,22 @@
     event_.Signal();
   }
 
-  virtual int Resolve(const std::string& host,
+  virtual int Resolve(const std::string& hostname,
                       AddressFamily address_family,
-                      AddressList* addrlist) {
+                      HostResolverFlags host_resolver_flags,
+                      AddressList* addrlist,
+                      int* os_error) {
     event_.Wait();
     {
       AutoLock l(lock_);
-      capture_list_.push_back(host);
+      capture_list_.push_back(CaptureEntry(hostname, address_family));
     }
-    return ResolveUsingPrevious(host, address_family, addrlist);
+    return ResolveUsingPrevious(hostname, address_family,
+                                host_resolver_flags, addrlist, os_error);
   }
 
-  std::vector<std::string> GetCaptureList() const {
-    std::vector<std::string> copy;
+  CaptureList GetCaptureList() const {
+    CaptureList copy;
     {
       AutoLock l(lock_);
       copy = capture_list_;
@@ -89,11 +109,45 @@
  private:
   ~CapturingHostResolverProc() {}
 
-  std::vector<std::string> capture_list_;
+  CaptureList capture_list_;
   mutable Lock lock_;
   base::WaitableEvent event_;
 };
 
+// This resolver function creates an IPv4 address, whose numeral value
+// describes a hash of the requested hostname, and the value of the requested
+// address_family.
+//
+// The resolved address for (hostname, address_family) will take the form:
+//    192.x.y.z
+//
+// Where:
+//   x = length of hostname
+//   y = ASCII value of hostname[0]
+//   z = value of address_family
+//
+class EchoingHostResolverProc : public HostResolverProc {
+ public:
+  EchoingHostResolverProc() : HostResolverProc(NULL) {}
+
+  virtual int Resolve(const std::string& hostname,
+                      AddressFamily address_family,
+                      HostResolverFlags host_resolver_flags,
+                      AddressList* addrlist,
+                      int* os_error) {
+    // Encode the request's hostname and address_family in the output address.
+    std::string ip_literal = StringPrintf("192.%d.%d.%d",
+        static_cast<int>(hostname.size()),
+        static_cast<int>(hostname[0]),
+        static_cast<int>(address_family));
+
+    return SystemHostResolverProc(ip_literal,
+                                  ADDRESS_FAMILY_UNSPECIFIED,
+                                  host_resolver_flags,
+                                  addrlist, os_error);
+  }
+};
+
 // Helper that represents a single Resolve() result, used to inspect all the
 // resolve results by forwarding them to Delegate.
 class ResolveRequest {
@@ -113,7 +167,8 @@
         ALLOW_THIS_IN_INITIALIZER_LIST(
             callback_(this, &ResolveRequest::OnLookupFinished)) {
     // Start the request.
-    int err = resolver->Resolve(info_, &addrlist_, &callback_, &req_, NULL);
+    int err = resolver->Resolve(info_, &addrlist_, &callback_, &req_,
+                                BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, err);
   }
 
@@ -124,7 +179,8 @@
         ALLOW_THIS_IN_INITIALIZER_LIST(
             callback_(this, &ResolveRequest::OnLookupFinished)) {
     // Start the request.
-    int err = resolver->Resolve(info, &addrlist_, &callback_, &req_, NULL);
+    int err = resolver->Resolve(info, &addrlist_, &callback_, &req_,
+                                BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, err);
   }
 
@@ -209,13 +265,15 @@
       CreateHostResolverImpl(resolver_proc));
 
   HostResolver::RequestInfo info("just.testing", kPortnum);
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  int err = host_resolver->Resolve(info, &adrlist, NULL, NULL, log);
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  int err = host_resolver->Resolve(info, &adrlist, NULL, NULL, log.bound());
   EXPECT_EQ(OK, err);
 
-  EXPECT_EQ(2u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_HOST_RESOLVER_IMPL));
-  EXPECT_TRUE(LogContainsEndEvent(*log, 1, LoadLog::TYPE_HOST_RESOLVER_IMPL));
+  EXPECT_EQ(2u, log.entries().size());
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 0, NetLog::TYPE_HOST_RESOLVER_IMPL));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 1, NetLog::TYPE_HOST_RESOLVER_IMPL));
 
   const struct addrinfo* ainfo = adrlist.head();
   EXPECT_EQ(static_cast<addrinfo*>(NULL), ainfo->ai_next);
@@ -239,20 +297,23 @@
       CreateHostResolverImpl(resolver_proc));
 
   HostResolver::RequestInfo info("just.testing", kPortnum);
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  int err = host_resolver->Resolve(info, &adrlist, &callback_, NULL, log);
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  int err = host_resolver->Resolve(info, &adrlist, &callback_, NULL,
+                                   log.bound());
   EXPECT_EQ(ERR_IO_PENDING, err);
 
-  EXPECT_EQ(1u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_HOST_RESOLVER_IMPL));
+  EXPECT_EQ(1u, log.entries().size());
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 0, NetLog::TYPE_HOST_RESOLVER_IMPL));
 
   MessageLoop::current()->Run();
 
   ASSERT_TRUE(callback_called_);
   ASSERT_EQ(OK, callback_result_);
 
-  EXPECT_EQ(2u, log->entries().size());
-  EXPECT_TRUE(LogContainsEndEvent(*log, 1, LoadLog::TYPE_HOST_RESOLVER_IMPL));
+  EXPECT_EQ(2u, log.entries().size());
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 1, NetLog::TYPE_HOST_RESOLVER_IMPL));
 
   const struct addrinfo* ainfo = adrlist.head();
   EXPECT_EQ(static_cast<addrinfo*>(NULL), ainfo->ai_next);
@@ -268,7 +329,7 @@
   scoped_refptr<WaitingHostResolverProc> resolver_proc =
       new WaitingHostResolverProc(NULL);
 
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
   {
     scoped_refptr<HostResolver> host_resolver(
         CreateHostResolverImpl(resolver_proc));
@@ -276,7 +337,8 @@
     const int kPortnum = 80;
 
     HostResolver::RequestInfo info("just.testing", kPortnum);
-    int err = host_resolver->Resolve(info, &adrlist, &callback_, NULL, log);
+    int err = host_resolver->Resolve(info, &adrlist, &callback_, NULL,
+                                     log.bound());
     EXPECT_EQ(ERR_IO_PENDING, err);
 
     // Make sure we will exit the queue even when callback is not called.
@@ -288,11 +350,13 @@
 
   resolver_proc->Signal();
 
-  EXPECT_EQ(3u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_HOST_RESOLVER_IMPL));
+  EXPECT_EQ(3u, log.entries().size());
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 0, NetLog::TYPE_HOST_RESOLVER_IMPL));
   EXPECT_TRUE(LogContainsEvent(
-      *log, 1, LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE));
-  EXPECT_TRUE(LogContainsEndEvent(*log, 2, LoadLog::TYPE_HOST_RESOLVER_IMPL));
+      log.entries(), 1, NetLog::TYPE_CANCELLED, NetLog::PHASE_NONE));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 2, NetLog::TYPE_HOST_RESOLVER_IMPL));
 
   EXPECT_FALSE(callback_called_);
 }
@@ -309,7 +373,7 @@
   AddressList adrlist;
   const int kPortnum = 5555;
   HostResolver::RequestInfo info("127.1.2.3", kPortnum);
-  int err = host_resolver->Resolve(info, &adrlist, NULL, NULL, NULL);
+  int err = host_resolver->Resolve(info, &adrlist, NULL, NULL, BoundNetLog());
   EXPECT_EQ(OK, err);
 
   const struct addrinfo* ainfo = adrlist.head();
@@ -334,7 +398,7 @@
   AddressList adrlist;
   const int kPortnum = 5555;
   HostResolver::RequestInfo info("2001:db8::1", kPortnum);
-  int err = host_resolver->Resolve(info, &adrlist, NULL, NULL, NULL);
+  int err = host_resolver->Resolve(info, &adrlist, NULL, NULL, BoundNetLog());
   // On computers without IPv6 support, getaddrinfo cannot convert IPv6
   // address literals to addresses (getaddrinfo returns EAI_NONAME).  So this
   // test has to allow host_resolver->Resolve to fail.
@@ -369,7 +433,7 @@
   AddressList adrlist;
   const int kPortnum = 5555;
   HostResolver::RequestInfo info("", kPortnum);
-  int err = host_resolver->Resolve(info, &adrlist, NULL, NULL, NULL);
+  int err = host_resolver->Resolve(info, &adrlist, NULL, NULL, BoundNetLog());
   EXPECT_EQ(ERR_NAME_NOT_RESOLVED, err);
 }
 
@@ -403,7 +467,8 @@
 
       // The resolver_proc should have been called only twice -- once with "a",
       // once with "b".
-      std::vector<std::string> capture_list = resolver_proc_->GetCaptureList();
+      CapturingHostResolverProc::CaptureList capture_list =
+          resolver_proc_->GetCaptureList();
       EXPECT_EQ(2U, capture_list.size());
 
       // End this test, we are done.
@@ -678,7 +743,7 @@
 
   // Turn off caching for this host resolver.
   scoped_refptr<HostResolver> host_resolver(
-      new HostResolverImpl(resolver_proc, NULL, NULL, kMaxJobs));
+      new HostResolverImpl(resolver_proc, NULL, kMaxJobs));
 
   // The class will receive callbacks for when each resolve completes. It
   // checks that the right things happened.
@@ -720,7 +785,8 @@
       AddressList addrlist;
 
       HostResolver::RequestInfo info("a", 70);
-      int error = resolver->Resolve(info, &addrlist, junk_callback, NULL, NULL);
+      int error = resolver->Resolve(info, &addrlist, junk_callback, NULL,
+                                    BoundNetLog());
       EXPECT_EQ(OK, error);
 
       // Ok good. Now make sure that if we ask to bypass the cache, it can no
@@ -838,22 +904,15 @@
 
   // Resolve "host1".
   HostResolver::RequestInfo info1("host1", 70);
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  int rv = host_resolver->Resolve(info1, &addrlist, NULL, NULL, log);
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  int rv = host_resolver->Resolve(info1, &addrlist, NULL, NULL, log.bound());
   EXPECT_EQ(OK, rv);
 
-  EXPECT_EQ(6u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_HOST_RESOLVER_IMPL));
+  EXPECT_EQ(2u, log.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 1, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART));
+      log.entries(), 0, NetLog::TYPE_HOST_RESOLVER_IMPL));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 2, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONSTART));
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 3, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONFINISH));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log, 4, LoadLog::TYPE_HOST_RESOLVER_IMPL_OBSERVER_ONFINISH));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log, 5, LoadLog::TYPE_HOST_RESOLVER_IMPL));
+      log.entries(), 1, NetLog::TYPE_HOST_RESOLVER_IMPL));
 
   EXPECT_EQ(1U, observer.start_log.size());
   EXPECT_EQ(1U, observer.finish_log.size());
@@ -866,7 +925,7 @@
   // Resolve "host1" again -- this time it  will be served from cache, but it
   // should still notify of completion.
   TestCompletionCallback callback;
-  rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, NULL);
+  rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, BoundNetLog());
   ASSERT_EQ(OK, rv);  // Should complete synchronously.
 
   EXPECT_EQ(2U, observer.start_log.size());
@@ -880,7 +939,7 @@
   // Resolve "host2", setting referrer to "http://foobar.com"
   HostResolver::RequestInfo info2("host2", 70);
   info2.set_referrer(GURL("http://foobar.com"));
-  rv = host_resolver->Resolve(info2, &addrlist, NULL, NULL, NULL);
+  rv = host_resolver->Resolve(info2, &addrlist, NULL, NULL, BoundNetLog());
   EXPECT_EQ(OK, rv);
 
   EXPECT_EQ(3U, observer.start_log.size());
@@ -896,7 +955,7 @@
 
   // Resolve "host3"
   HostResolver::RequestInfo info3("host3", 70);
-  host_resolver->Resolve(info3, &addrlist, NULL, NULL, NULL);
+  host_resolver->Resolve(info3, &addrlist, NULL, NULL, BoundNetLog());
 
   // No effect this time, since observer was removed.
   EXPECT_EQ(3U, observer.start_log.size());
@@ -926,7 +985,8 @@
     HostResolver::RequestInfo info1("host1", 70);
     HostResolver::RequestHandle req = NULL;
     AddressList addrlist;
-    int rv = host_resolver->Resolve(info1, &addrlist, &callback, &req, NULL);
+    int rv = host_resolver->Resolve(info1, &addrlist, &callback, &req,
+                                    BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
     EXPECT_TRUE(NULL != req);
 
@@ -949,7 +1009,8 @@
 
     // Start an async request for (host2:60)
     HostResolver::RequestInfo info2("host2", 60);
-    rv = host_resolver->Resolve(info2, &addrlist, &callback, NULL, NULL);
+    rv = host_resolver->Resolve(info2, &addrlist, &callback, NULL,
+                                BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
     EXPECT_TRUE(NULL != req);
 
@@ -978,32 +1039,31 @@
 
 // Test that IP address changes flush the cache.
 TEST_F(HostResolverImplTest, FlushCacheOnIPAddressChange) {
-  MockNetworkChangeNotifier mock_network_change_notifier;
   scoped_refptr<HostResolver> host_resolver(
-      new HostResolverImpl(NULL, CreateDefaultCache(),
-                           &mock_network_change_notifier,
-                           kMaxJobs));
+      new HostResolverImpl(NULL, CreateDefaultCache(), kMaxJobs));
 
   AddressList addrlist;
 
   // Resolve "host1".
   HostResolver::RequestInfo info1("host1", 70);
   TestCompletionCallback callback;
-  int rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, NULL);
+  int rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL,
+                                  BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_EQ(OK, callback.WaitForResult());
 
   // Resolve "host1" again -- this time it will be served from cache, but it
   // should still notify of completion.
-  rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, NULL);
+  rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, BoundNetLog());
   ASSERT_EQ(OK, rv);  // Should complete synchronously.
 
   // Flush cache by triggering an IP address change.
-  mock_network_change_notifier.NotifyIPAddressChange();
+  NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+  MessageLoop::current()->RunAllPending();  // Notification happens async.
 
   // Resolve "host1" again -- this time it won't be served from cache, so it
   // will complete asynchronously.
-  rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, NULL);
+  rv = host_resolver->Resolve(info1, &addrlist, &callback, NULL, BoundNetLog());
   ASSERT_EQ(ERR_IO_PENDING, rv);  // Should complete asynchronously.
   EXPECT_EQ(OK, callback.WaitForResult());
 }
@@ -1017,8 +1077,7 @@
   // This HostResolverImpl will only allow 1 outstanding resolve at a time.
   size_t kMaxJobs = 1u;
   scoped_refptr<HostResolver> host_resolver(
-      new HostResolverImpl(resolver_proc, CreateDefaultCache(),
-                           NULL, kMaxJobs));
+      new HostResolverImpl(resolver_proc, CreateDefaultCache(), kMaxJobs));
 
   CapturingObserver observer;
   host_resolver->AddObserver(&observer);
@@ -1043,7 +1102,7 @@
   // Start all of the requests.
   for (size_t i = 0; i < arraysize(req); ++i) {
     int rv = host_resolver->Resolve(req[i], &addrlist[i],
-                                    &callback[i], NULL, NULL);
+                                    &callback[i], NULL, BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
   }
 
@@ -1061,16 +1120,17 @@
   // the requests should complete in order of priority (with the exception
   // of the first request, which gets started right away, since there is
   // nothing outstanding).
-  std::vector<std::string> capture_list = resolver_proc->GetCaptureList();
+  CapturingHostResolverProc::CaptureList capture_list =
+      resolver_proc->GetCaptureList();
   ASSERT_EQ(7u, capture_list.size());
 
-  EXPECT_EQ("req0", capture_list[0]);
-  EXPECT_EQ("req4", capture_list[1]);
-  EXPECT_EQ("req5", capture_list[2]);
-  EXPECT_EQ("req1", capture_list[3]);
-  EXPECT_EQ("req2", capture_list[4]);
-  EXPECT_EQ("req3", capture_list[5]);
-  EXPECT_EQ("req6", capture_list[6]);
+  EXPECT_EQ("req0", capture_list[0].hostname);
+  EXPECT_EQ("req4", capture_list[1].hostname);
+  EXPECT_EQ("req5", capture_list[2].hostname);
+  EXPECT_EQ("req1", capture_list[3].hostname);
+  EXPECT_EQ("req2", capture_list[4].hostname);
+  EXPECT_EQ("req3", capture_list[5].hostname);
+  EXPECT_EQ("req6", capture_list[6].hostname);
 
   // Also check using the observer's trace.
   EXPECT_EQ(8U, observer.start_log.size());
@@ -1101,8 +1161,7 @@
   // This HostResolverImpl will only allow 1 outstanding resolve at a time.
   const size_t kMaxJobs = 1u;
   scoped_refptr<HostResolver> host_resolver(
-      new HostResolverImpl(resolver_proc, CreateDefaultCache(),
-                           NULL, kMaxJobs));
+      new HostResolverImpl(resolver_proc, CreateDefaultCache(), kMaxJobs));
 
   // Note that at this point the CapturingHostResolverProc is blocked, so any
   // requests we make will not complete.
@@ -1124,7 +1183,7 @@
   // Start all of the requests.
   for (size_t i = 0; i < arraysize(req); ++i) {
     int rv = host_resolver->Resolve(req[i], &addrlist[i],
-                                    &callback[i], &handle[i], NULL);
+                                    &callback[i], &handle[i], BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
   }
 
@@ -1146,13 +1205,14 @@
 
   // Verify that they called out the the resolver proc (which runs on the
   // resolver thread) in the expected order.
-  std::vector<std::string> capture_list = resolver_proc->GetCaptureList();
+  CapturingHostResolverProc::CaptureList capture_list =
+      resolver_proc->GetCaptureList();
   ASSERT_EQ(4u, capture_list.size());
 
-  EXPECT_EQ("req0", capture_list[0]);
-  EXPECT_EQ("req2", capture_list[1]);
-  EXPECT_EQ("req6", capture_list[2]);
-  EXPECT_EQ("req3", capture_list[3]);
+  EXPECT_EQ("req0", capture_list[0].hostname);
+  EXPECT_EQ("req2", capture_list[1].hostname);
+  EXPECT_EQ("req6", capture_list[2].hostname);
+  EXPECT_EQ("req3", capture_list[3].hostname);
 }
 
 // Test that when too many requests are enqueued, old ones start to be aborted.
@@ -1162,9 +1222,8 @@
 
   // This HostResolverImpl will only allow 1 outstanding resolve at a time.
   const size_t kMaxOutstandingJobs = 1u;
-  scoped_refptr<HostResolverImpl> host_resolver(
-      new HostResolverImpl(resolver_proc, CreateDefaultCache(),
-                           NULL, kMaxOutstandingJobs));
+  scoped_refptr<HostResolverImpl> host_resolver(new HostResolverImpl(
+      resolver_proc, CreateDefaultCache(), kMaxOutstandingJobs));
 
   // Only allow up to 3 requests to be enqueued at a time.
   const size_t kMaxPendingRequests = 3u;
@@ -1198,7 +1257,7 @@
   // Start all of the requests.
   for (size_t i = 0; i < arraysize(req); ++i) {
     int rv = host_resolver->Resolve(req[i], &addrlist[i],
-                                    &callback[i], &handle[i], NULL);
+                                    &callback[i], &handle[i], BoundNetLog());
     if (i == 4u)
       EXPECT_EQ(ERR_HOST_RESOLVER_QUEUE_TOO_LARGE, rv);
     else
@@ -1223,15 +1282,208 @@
 
   // Verify that they called out the the resolver proc (which runs on the
   // resolver thread) in the expected order.
-  std::vector<std::string> capture_list = resolver_proc->GetCaptureList();
+  CapturingHostResolverProc::CaptureList capture_list =
+      resolver_proc->GetCaptureList();
   ASSERT_EQ(4u, capture_list.size());
 
-  EXPECT_EQ("req0", capture_list[0]);
-  EXPECT_EQ("req1", capture_list[1]);
-  EXPECT_EQ("req6", capture_list[2]);
-  EXPECT_EQ("req7", capture_list[3]);
+  EXPECT_EQ("req0", capture_list[0].hostname);
+  EXPECT_EQ("req1", capture_list[1].hostname);
+  EXPECT_EQ("req6", capture_list[2].hostname);
+  EXPECT_EQ("req7", capture_list[3].hostname);
 }
 
+// Tests that after changing the default AddressFamily to IPV4, requests
+// with UNSPECIFIED address family map to IPV4.
+TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv4) {
+  scoped_refptr<CapturingHostResolverProc> resolver_proc =
+      new CapturingHostResolverProc(new EchoingHostResolverProc);
+
+  // This HostResolverImpl will only allow 1 outstanding resolve at a time.
+  const size_t kMaxOutstandingJobs = 1u;
+  scoped_refptr<HostResolverImpl> host_resolver(new HostResolverImpl(
+      resolver_proc, CreateDefaultCache(), kMaxOutstandingJobs));
+
+  host_resolver->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4);
+
+  // Note that at this point the CapturingHostResolverProc is blocked, so any
+  // requests we make will not complete.
+
+  HostResolver::RequestInfo req[] = {
+      CreateResolverRequestForAddressFamily("h1", MEDIUM,
+                                            ADDRESS_FAMILY_UNSPECIFIED),
+      CreateResolverRequestForAddressFamily("h1", MEDIUM, ADDRESS_FAMILY_IPV4),
+      CreateResolverRequestForAddressFamily("h1", MEDIUM, ADDRESS_FAMILY_IPV6),
+  };
+
+  TestCompletionCallback callback[arraysize(req)];
+  AddressList addrlist[arraysize(req)];
+  HostResolver::RequestHandle handle[arraysize(req)];
+
+  // Start all of the requests.
+  for (size_t i = 0; i < arraysize(req); ++i) {
+    int rv = host_resolver->Resolve(req[i], &addrlist[i],
+                                    &callback[i], &handle[i], BoundNetLog());
+    EXPECT_EQ(ERR_IO_PENDING, rv) << i;
+  }
+
+  // Unblock the resolver thread so the requests can run.
+  resolver_proc->Signal();
+
+  // Wait for all the requests to complete.
+  for (size_t i = 0u; i < arraysize(req); ++i) {
+    EXPECT_EQ(OK, callback[i].WaitForResult());
+  }
+
+  // Since the requests all had the same priority and we limited the thread
+  // count to 1, they should have completed in the same order as they were
+  // requested. Moreover, request0 and request1 will have been serviced by
+  // the same job.
+
+  CapturingHostResolverProc::CaptureList capture_list =
+      resolver_proc->GetCaptureList();
+  ASSERT_EQ(2u, capture_list.size());
+
+  EXPECT_EQ("h1", capture_list[0].hostname);
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[0].address_family);
+
+  EXPECT_EQ("h1", capture_list[1].hostname);
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[1].address_family);
+
+  // Now check that the correct resolved IP addresses were returned.
+  // Addresses take the form: 192.x.y.z
+  //    x = length of hostname
+  //    y = ASCII value of hostname[0]
+  //    z = value of address family
+  EXPECT_EQ("192.2.104.1", NetAddressToString(addrlist[0].head()));
+  EXPECT_EQ("192.2.104.1", NetAddressToString(addrlist[1].head()));
+  EXPECT_EQ("192.2.104.2", NetAddressToString(addrlist[2].head()));
+}
+
+// This is the exact same test as SetDefaultAddressFamily_IPv4, except the order
+// of requests 0 and 1 is flipped, and the default is set to IPv6 in place of
+// IPv4.
+TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv6) {
+  scoped_refptr<CapturingHostResolverProc> resolver_proc =
+      new CapturingHostResolverProc(new EchoingHostResolverProc);
+
+  // This HostResolverImpl will only allow 1 outstanding resolve at a time.
+  const size_t kMaxOutstandingJobs = 1u;
+  scoped_refptr<HostResolverImpl> host_resolver(new HostResolverImpl(
+      resolver_proc, CreateDefaultCache(), kMaxOutstandingJobs));
+
+  host_resolver->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV6);
+
+  // Note that at this point the CapturingHostResolverProc is blocked, so any
+  // requests we make will not complete.
+
+  HostResolver::RequestInfo req[] = {
+      CreateResolverRequestForAddressFamily("h1", MEDIUM, ADDRESS_FAMILY_IPV6),
+      CreateResolverRequestForAddressFamily("h1", MEDIUM,
+                                            ADDRESS_FAMILY_UNSPECIFIED),
+      CreateResolverRequestForAddressFamily("h1", MEDIUM, ADDRESS_FAMILY_IPV4),
+  };
+
+  TestCompletionCallback callback[arraysize(req)];
+  AddressList addrlist[arraysize(req)];
+  HostResolver::RequestHandle handle[arraysize(req)];
+
+  // Start all of the requests.
+  for (size_t i = 0; i < arraysize(req); ++i) {
+    int rv = host_resolver->Resolve(req[i], &addrlist[i],
+                                    &callback[i], &handle[i], BoundNetLog());
+    EXPECT_EQ(ERR_IO_PENDING, rv) << i;
+  }
+
+  // Unblock the resolver thread so the requests can run.
+  resolver_proc->Signal();
+
+  // Wait for all the requests to complete.
+  for (size_t i = 0u; i < arraysize(req); ++i) {
+    EXPECT_EQ(OK, callback[i].WaitForResult());
+  }
+
+  // Since the requests all had the same priority and we limited the thread
+  // count to 1, they should have completed in the same order as they were
+  // requested. Moreover, request0 and request1 will have been serviced by
+  // the same job.
+
+  CapturingHostResolverProc::CaptureList capture_list =
+      resolver_proc->GetCaptureList();
+  ASSERT_EQ(2u, capture_list.size());
+
+  EXPECT_EQ("h1", capture_list[0].hostname);
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[0].address_family);
+
+  EXPECT_EQ("h1", capture_list[1].hostname);
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[1].address_family);
+
+  // Now check that the correct resolved IP addresses were returned.
+  // Addresses take the form: 192.x.y.z
+  //    x = length of hostname
+  //    y = ASCII value of hostname[0]
+  //    z = value of address family
+  EXPECT_EQ("192.2.104.2", NetAddressToString(addrlist[0].head()));
+  EXPECT_EQ("192.2.104.2", NetAddressToString(addrlist[1].head()));
+  EXPECT_EQ("192.2.104.1", NetAddressToString(addrlist[2].head()));
+}
+
+// This tests that the default address family is respected for synchronous
+// resolutions.
+TEST_F(HostResolverImplTest, SetDefaultAddressFamily_Synchronous) {
+  scoped_refptr<CapturingHostResolverProc> resolver_proc =
+      new CapturingHostResolverProc(new EchoingHostResolverProc);
+
+  const size_t kMaxOutstandingJobs = 10u;
+  scoped_refptr<HostResolverImpl> host_resolver(new HostResolverImpl(
+      resolver_proc, CreateDefaultCache(), kMaxOutstandingJobs));
+
+  host_resolver->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4);
+
+  // Unblock the resolver thread so the requests can run.
+  resolver_proc->Signal();
+
+  HostResolver::RequestInfo req[] = {
+      CreateResolverRequestForAddressFamily("b", MEDIUM,
+                                            ADDRESS_FAMILY_UNSPECIFIED),
+      CreateResolverRequestForAddressFamily("b", MEDIUM, ADDRESS_FAMILY_IPV6),
+      CreateResolverRequestForAddressFamily("b", MEDIUM,
+                                            ADDRESS_FAMILY_UNSPECIFIED),
+      CreateResolverRequestForAddressFamily("b", MEDIUM, ADDRESS_FAMILY_IPV4),
+  };
+  AddressList addrlist[arraysize(req)];
+
+  // Start and run all of the requests synchronously.
+  for (size_t i = 0; i < arraysize(req); ++i) {
+    int rv = host_resolver->Resolve(req[i], &addrlist[i],
+                                    NULL, NULL, BoundNetLog());
+    EXPECT_EQ(OK, rv) << i;
+  }
+
+  // We should have sent 2 requests to the resolver --
+  // one for (b, IPv4), and one for (b, IPv6).
+  CapturingHostResolverProc::CaptureList capture_list =
+      resolver_proc->GetCaptureList();
+  ASSERT_EQ(2u, capture_list.size());
+
+  EXPECT_EQ("b", capture_list[0].hostname);
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[0].address_family);
+
+  EXPECT_EQ("b", capture_list[1].hostname);
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[1].address_family);
+
+  // Now check that the correct resolved IP addresses were returned.
+  // Addresses take the form: 192.x.y.z
+  //    x = length of hostname
+  //    y = ASCII value of hostname[0]
+  //    z = value of address family
+  EXPECT_EQ("192.1.98.1", NetAddressToString(addrlist[0].head()));
+  EXPECT_EQ("192.1.98.2", NetAddressToString(addrlist[1].head()));
+  EXPECT_EQ("192.1.98.1", NetAddressToString(addrlist[2].head()));
+  EXPECT_EQ("192.1.98.1", NetAddressToString(addrlist[3].head()));
+}
+
+// TODO(cbentzel): Test a mix of requests with different HostResolverFlags.
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/base/host_resolver_proc.cc b/net/base/host_resolver_proc.cc
index 5ccf01a..804135c 100644
--- a/net/base/host_resolver_proc.cc
+++ b/net/base/host_resolver_proc.cc
@@ -67,14 +67,20 @@
   return default_proc_;
 }
 
-int HostResolverProc::ResolveUsingPrevious(const std::string& host,
-                                           AddressFamily address_family,
-                                           AddressList* addrlist) {
-  if (previous_proc_)
-    return previous_proc_->Resolve(host, address_family, addrlist);
+int HostResolverProc::ResolveUsingPrevious(
+    const std::string& host,
+    AddressFamily address_family,
+    HostResolverFlags host_resolver_flags,
+    AddressList* addrlist,
+    int* os_error) {
+  if (previous_proc_) {
+    return previous_proc_->Resolve(host, address_family, host_resolver_flags,
+                                   addrlist, os_error);
+  }
 
   // Final fallback is the system resolver.
-  return SystemHostResolverProc(host, address_family, addrlist);
+  return SystemHostResolverProc(host, address_family, host_resolver_flags,
+                                addrlist, os_error);
 }
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
@@ -152,7 +158,12 @@
 
 int SystemHostResolverProc(const std::string& host,
                            AddressFamily address_family,
-                           AddressList* addrlist) {
+                           HostResolverFlags host_resolver_flags,
+                           AddressList* addrlist,
+                           int* os_error) {
+  if (os_error)
+    *os_error = 0;
+
   // The result of |getaddrinfo| for empty hosts is inconsistent across systems.
   // On Windows it gives the default interface's address, whereas on Linux it
   // gives an error. We will make it fail on all platforms for consistency.
@@ -205,6 +216,9 @@
   hints.ai_flags = AI_ADDRCONFIG;
 #endif
 
+  if (host_resolver_flags & HOST_RESOLVER_CANONNAME)
+    hints.ai_flags |= AI_CANONNAME;
+
   // Restrict result set to only this socket type to avoid duplicates.
   hints.ai_socktype = SOCK_STREAM;
 
@@ -220,8 +234,16 @@
   }
 #endif
 
-  if (err)
+  if (err) {
+    if (os_error) {
+#if defined(OS_WIN)
+      *os_error = WSAGetLastError();
+#else
+      *os_error = err;
+#endif
+    }
     return ERR_NAME_NOT_RESOLVED;
+  }
 
   addrlist->Adopt(ai);
   return OK;
diff --git a/net/base/host_resolver_proc.h b/net/base/host_resolver_proc.h
index ca0c55d..3a8adc7 100644
--- a/net/base/host_resolver_proc.h
+++ b/net/base/host_resolver_proc.h
@@ -27,10 +27,13 @@
 
   // Resolves |host| to an address list, restricting the results to addresses
   // in |address_family|. If successful returns OK and fills |addrlist| with
-  // a list of socket addresses. Otherwise returns a network error code.
+  // a list of socket addresses. Otherwise returns a network error code, and
+  // fills |os_error| with a more specific error if it was non-NULL.
   virtual int Resolve(const std::string& host,
                       AddressFamily address_family,
-                      AddressList* addrlist) = 0;
+                      HostResolverFlags host_resolver_flags,
+                      AddressList* addrlist,
+                      int* os_error) = 0;
 
  protected:
   friend class base::RefCountedThreadSafe<HostResolverProc>;
@@ -40,7 +43,9 @@
   // Asks the fallback procedure (if set) to do the resolve.
   int ResolveUsingPrevious(const std::string& host,
                            AddressFamily address_family,
-                           AddressList* addrlist);
+                           HostResolverFlags host_resolver_flags,
+                           AddressList* addrlist,
+                           int* os_error);
 
  private:
   friend class HostResolverImpl;
@@ -75,10 +80,13 @@
 // Resolves |host| to an address list, using the system's default host resolver.
 // (i.e. this calls out to getaddrinfo()). If successful returns OK and fills
 // |addrlist| with a list of socket addresses. Otherwise returns a
-// network error code.
+// network error code, and fills |os_error| with a more specific errir if it
+// was non-NULL.
 int SystemHostResolverProc(const std::string& host,
                            AddressFamily address_family,
-                           AddressList* addrlist);
+                           HostResolverFlags host_resolver_flags,
+                           AddressList* addrlist,
+                           int* os_error);
 
 }  // namespace net
 
diff --git a/net/base/io_buffer.cc b/net/base/io_buffer.cc
index 9ad0570..b922733 100644
--- a/net/base/io_buffer.cc
+++ b/net/base/io_buffer.cc
@@ -8,17 +8,81 @@
 
 namespace net {
 
+IOBuffer::IOBuffer()
+    : data_(NULL) {
+}
+
 IOBuffer::IOBuffer(int buffer_size) {
   DCHECK(buffer_size > 0);
   data_ = new char[buffer_size];
 }
 
+IOBuffer::IOBuffer(char* data)
+    : data_(data) {
+}
+
+IOBuffer::~IOBuffer() {
+  delete[] data_;
+}
+
+IOBufferWithSize::IOBufferWithSize(int size)
+    : IOBuffer(size),
+      size_(size) {
+}
+
+StringIOBuffer::StringIOBuffer(const std::string& s)
+    : IOBuffer(static_cast<char*>(NULL)),
+      string_data_(s) {
+  data_ = const_cast<char*>(string_data_.data());
+}
+
+StringIOBuffer::~StringIOBuffer() {
+  // We haven't allocated the buffer, so remove it before the base class
+  // destructor tries to delete[] it.
+  data_ = NULL;
+}
+
+DrainableIOBuffer::DrainableIOBuffer(IOBuffer* base, int size)
+    : IOBuffer(base->data()),
+      base_(base),
+      size_(size),
+      used_(0) {
+}
+
+DrainableIOBuffer::~DrainableIOBuffer() {
+  // The buffer is owned by the |base_| instance.
+  data_ = NULL;
+}
+
+void DrainableIOBuffer::DidConsume(int bytes) {
+  SetOffset(used_ + bytes);
+}
+
+int DrainableIOBuffer::BytesRemaining() const {
+  return size_ - used_;
+}
+
+// Returns the number of consumed bytes.
+int DrainableIOBuffer::BytesConsumed() const {
+  return used_;
+}
+
 void DrainableIOBuffer::SetOffset(int bytes) {
   DCHECK(bytes >= 0 && bytes <= size_);
   used_ = bytes;
   data_ = base_->data() + used_;
 }
 
+GrowableIOBuffer::GrowableIOBuffer()
+    : IOBuffer(),
+      capacity_(0),
+      offset_(0) {
+}
+
+GrowableIOBuffer::~GrowableIOBuffer() {
+  data_ = NULL;
+}
+
 void GrowableIOBuffer::SetCapacity(int capacity) {
   DCHECK(capacity >= 0);
   // realloc will crash if it fails.
@@ -36,4 +100,29 @@
   data_ = real_data_.get() + offset;
 }
 
+int GrowableIOBuffer::RemainingCapacity() {
+  return capacity_ - offset_;
+}
+
+char* GrowableIOBuffer::StartOfBuffer() {
+  return real_data_.get();
+}
+
+PickledIOBuffer::PickledIOBuffer() : IOBuffer() {}
+
+PickledIOBuffer::~PickledIOBuffer() { data_ = NULL; }
+
+void PickledIOBuffer::Done() {
+  data_ = const_cast<char*>(static_cast<const char*>(pickle_.data()));
+}
+
+
+WrappedIOBuffer::WrappedIOBuffer(const char* data)
+    : IOBuffer(const_cast<char*>(data)) {
+}
+
+WrappedIOBuffer::~WrappedIOBuffer() {
+  data_ = NULL;
+}
+
 }  // namespace net
diff --git a/net/base/io_buffer.h b/net/base/io_buffer.h
index d908eec..cc6092a 100644
--- a/net/base/io_buffer.h
+++ b/net/base/io_buffer.h
@@ -17,7 +17,7 @@
 // easier asynchronous IO handling.
 class IOBuffer : public base::RefCountedThreadSafe<IOBuffer> {
  public:
-  IOBuffer() : data_(NULL) {}
+  IOBuffer();
   explicit IOBuffer(int buffer_size);
 
   char* data() { return data_; }
@@ -27,11 +27,9 @@
 
   // Only allow derived classes to specify data_.
   // In all other cases, we own data_, and must delete it at destruction time.
-  explicit IOBuffer(char* data) : data_(data) {}
+  explicit IOBuffer(char* data);
 
-  virtual ~IOBuffer() {
-    delete[] data_;
-  }
+  virtual ~IOBuffer();
 
   char* data_;
 };
@@ -42,7 +40,7 @@
 // argument to IO functions. Please keep using IOBuffer* for API declarations.
 class IOBufferWithSize : public IOBuffer {
  public:
-  explicit IOBufferWithSize(int size) : IOBuffer(size), size_(size) {}
+  explicit IOBufferWithSize(int size);
 
   int size() const { return size_; }
 
@@ -56,20 +54,12 @@
 // the IOBuffer interface does not provide a proper way to modify it.
 class StringIOBuffer : public IOBuffer {
  public:
-  explicit StringIOBuffer(const std::string& s)
-      : IOBuffer(static_cast<char*>(NULL)),
-        string_data_(s) {
-    data_ = const_cast<char*>(string_data_.data());
-  }
+  explicit StringIOBuffer(const std::string& s);
 
   int size() const { return string_data_.size(); }
 
  private:
-  ~StringIOBuffer() {
-    // We haven't allocated the buffer, so remove it before the base class
-    // destructor tries to delete[] it.
-    data_ = NULL;
-  }
+  ~StringIOBuffer();
 
   std::string string_data_;
 };
@@ -78,18 +68,17 @@
 // to progressively read all the data.
 class DrainableIOBuffer : public IOBuffer {
  public:
-  DrainableIOBuffer(IOBuffer* base, int size)
-      : IOBuffer(base->data()), base_(base), size_(size), used_(0) {}
+  DrainableIOBuffer(IOBuffer* base, int size);
 
   // DidConsume() changes the |data_| pointer so that |data_| always points
   // to the first unconsumed byte.
-  void DidConsume(int bytes) { SetOffset(used_ + bytes); }
+  void DidConsume(int bytes);
 
   // Returns the number of unconsumed bytes.
-  int BytesRemaining() const { return size_ - used_; }
+  int BytesRemaining() const;
 
   // Returns the number of consumed bytes.
-  int BytesConsumed() const { return used_; }
+  int BytesConsumed() const;
 
   // Seeks to an arbitrary point in the buffer. The notion of bytes consumed
   // and remaining are updated appropriately.
@@ -98,10 +87,7 @@
   int size() const { return size_; }
 
  private:
-  ~DrainableIOBuffer() {
-    // The buffer is owned by the |base_| instance.
-    data_ = NULL;
-  }
+  ~DrainableIOBuffer();
 
   scoped_refptr<IOBuffer> base_;
   int size_;
@@ -111,7 +97,7 @@
 // This version provides a resizable buffer and a changeable offset.
 class GrowableIOBuffer : public IOBuffer {
  public:
-  GrowableIOBuffer() : IOBuffer(), capacity_(0), offset_(0) {}
+  GrowableIOBuffer();
 
   // realloc memory to the specified capacity.
   void SetCapacity(int capacity);
@@ -121,11 +107,11 @@
   void set_offset(int offset);
   int offset() { return offset_; }
 
-  int RemainingCapacity() { return capacity_ - offset_; }
-  char* StartOfBuffer() { return real_data_.get(); }
+  int RemainingCapacity();
+  char* StartOfBuffer();
 
  private:
-  ~GrowableIOBuffer() { data_ = NULL; }
+  ~GrowableIOBuffer();
 
   scoped_ptr_malloc<char> real_data_;
   int capacity_;
@@ -136,18 +122,16 @@
 // operation, avoiding an extra data copy.
 class PickledIOBuffer : public IOBuffer {
  public:
-  PickledIOBuffer() : IOBuffer() {}
+  PickledIOBuffer();
 
   Pickle* pickle() { return &pickle_; }
 
   // Signals that we are done writing to the picke and we can use it for a
   // write-style IO operation.
-  void Done() {
-    data_ = const_cast<char*>(static_cast<const char*>(pickle_.data()));
-  }
+  void Done();
 
  private:
-  ~PickledIOBuffer() { data_ = NULL; }
+  ~PickledIOBuffer();
 
   Pickle pickle_;
 };
@@ -159,13 +143,10 @@
 // of the buffer can be completely managed by its intended owner.
 class WrappedIOBuffer : public IOBuffer {
  public:
-  explicit WrappedIOBuffer(const char* data)
-      : IOBuffer(const_cast<char*>(data)) {}
+  explicit WrappedIOBuffer(const char* data);
 
  protected:
-  ~WrappedIOBuffer() {
-    data_ = NULL;
-  }
+  ~WrappedIOBuffer();
 };
 
 }  // namespace net
diff --git a/net/base/keygen_handler.h b/net/base/keygen_handler.h
index 346b577..a582816 100644
--- a/net/base/keygen_handler.h
+++ b/net/base/keygen_handler.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -10,18 +10,36 @@
 namespace net {
 
 // This class handles keypair generation for generating client
-// certificates via the Netscape <keygen> tag.
+// certificates via the <keygen> tag.
+// <http://dev.w3.org/html5/spec/Overview.html#the-keygen-element>
+// <https://developer.mozilla.org/En/HTML/HTML_Extensions/KEYGEN_Tag>
 
 class KeygenHandler {
  public:
-  KeygenHandler(int key_size_index, const std::string& challenge);
+  // Creates a handler that will generate a key with the given key size
+  // and incorporate the |challenge| into the Netscape SPKAC structure.
+  inline KeygenHandler(int key_size_in_bits, const std::string& challenge);
+
+  // Actually generates the key-pair and the cert request (SPKAC), and returns
+  // a base64-encoded string suitable for use as the form value of <keygen>.
   std::string GenKeyAndSignChallenge();
 
+  // Exposed only for unit tests.
+  void set_stores_key(bool store) { stores_key_ = store;}
+
  private:
-  int key_size_index_;
-  std::string challenge_;
+  int key_size_in_bits_;  // key size in bits (usually 2048)
+  std::string challenge_;  // challenge string sent by server
+  bool stores_key_;  // should the generated key-pair be stored persistently?
 };
 
+KeygenHandler::KeygenHandler(int key_size_in_bits,
+                             const std::string& challenge)
+    : key_size_in_bits_(key_size_in_bits),
+      challenge_(challenge),
+      stores_key_(true) {
+}
+
 }  // namespace net
 
 #endif  // NET_BASE_KEYGEN_HANDLER_H_
diff --git a/net/base/keygen_handler_mac.cc b/net/base/keygen_handler_mac.cc
index f6b4551..c2c63d7 100644
--- a/net/base/keygen_handler_mac.cc
+++ b/net/base/keygen_handler_mac.cc
@@ -1,23 +1,297 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/base/keygen_handler.h"
 
+#include <Security/SecAsn1Coder.h>
+#include <Security/SecAsn1Templates.h>
+#include <Security/Security.h>
+
+#include "base/base64.h"
+#include "base/crypto/cssm_init.h"
+#include "base/lock.h"
 #include "base/logging.h"
+#include "base/scoped_cftyperef.h"
+
+// These are in Security.framework but not declared in a public header.
+extern const SecAsn1Template kSecAsn1AlgorithmIDTemplate[];
+extern const SecAsn1Template kSecAsn1SubjectPublicKeyInfoTemplate[];
 
 namespace net {
 
-KeygenHandler::KeygenHandler(int key_size_index,
-                             const std::string& challenge)
-    : key_size_index_(key_size_index),
-      challenge_(challenge) {
-  NOTIMPLEMENTED();
-}
+// Declarations of Netscape keygen cert structures for ASN.1 encoding:
+
+struct PublicKeyAndChallenge {
+  CSSM_X509_SUBJECT_PUBLIC_KEY_INFO spki;
+  CSSM_DATA challenge_string;
+};
+
+// This is a copy of the built-in kSecAsn1IA5StringTemplate, but without the
+// 'streamable' flag, which was causing bogus data to be written.
+const SecAsn1Template kIA5StringTemplate[] = {
+    { SEC_ASN1_IA5_STRING, 0, NULL, sizeof(CSSM_DATA) }
+};
+
+static const SecAsn1Template kPublicKeyAndChallengeTemplate[] = {
+  {
+    SEC_ASN1_SEQUENCE,
+    0,
+    NULL,
+    sizeof(PublicKeyAndChallenge)
+  },
+  {
+    SEC_ASN1_INLINE,
+    offsetof(PublicKeyAndChallenge, spki),
+    kSecAsn1SubjectPublicKeyInfoTemplate
+  },
+  {
+    SEC_ASN1_INLINE,
+    offsetof(PublicKeyAndChallenge, challenge_string),
+    kIA5StringTemplate
+  },
+  {
+    0
+  }
+};
+
+struct SignedPublicKeyAndChallenge {
+  PublicKeyAndChallenge pkac;
+  CSSM_X509_ALGORITHM_IDENTIFIER signature_algorithm;
+  CSSM_DATA signature;
+};
+
+static const SecAsn1Template kSignedPublicKeyAndChallengeTemplate[] = {
+  {
+    SEC_ASN1_SEQUENCE,
+    0,
+    NULL,
+    sizeof(SignedPublicKeyAndChallenge)
+  },
+  {
+    SEC_ASN1_INLINE,
+    offsetof(SignedPublicKeyAndChallenge, pkac),
+    kPublicKeyAndChallengeTemplate
+  },
+  {
+    SEC_ASN1_INLINE,
+    offsetof(SignedPublicKeyAndChallenge, signature_algorithm),
+    kSecAsn1AlgorithmIDTemplate
+  },
+  {
+    SEC_ASN1_BIT_STRING,
+    offsetof(SignedPublicKeyAndChallenge, signature)
+  },
+  {
+    0
+  }
+};
+
+
+static OSStatus CreateRSAKeyPair(int size_in_bits,
+                                 SecKeyRef* out_pub_key,
+                                 SecKeyRef* out_priv_key);
+static OSStatus SignData(CSSM_DATA data,
+                         SecKeyRef private_key,
+                         CSSM_DATA* signature);
 
 std::string KeygenHandler::GenKeyAndSignChallenge() {
-  NOTIMPLEMENTED();
-  return std::string();
+  std::string result;
+  OSStatus err;
+  SecKeyRef public_key = NULL;
+  SecKeyRef private_key = NULL;
+  SecAsn1CoderRef coder = NULL;
+  CSSM_DATA signature = {0, NULL};
+
+  {
+    // Create the key-pair.
+    err = CreateRSAKeyPair(key_size_in_bits_, &public_key, &private_key);
+    if (err)
+      goto failure;
+
+    // Get the public key data (DER sequence of modulus, exponent).
+    CFDataRef key_data = NULL;
+    err = SecKeychainItemExport(public_key, kSecFormatBSAFE, 0, NULL,
+                                &key_data);
+    if (err) {
+      base::LogCSSMError("SecKeychainItemExpor", err);
+      goto failure;
+    }
+    scoped_cftyperef<CFDataRef> scoped_key_data(key_data);
+
+    // Create an ASN.1 encoder.
+    err = SecAsn1CoderCreate(&coder);
+    if (err) {
+      base::LogCSSMError("SecAsn1CoderCreate", err);
+      goto failure;
+    }
+
+    // Fill in and DER-encode the PublicKeyAndChallenge:
+    SignedPublicKeyAndChallenge spkac;
+    memset(&spkac, 0, sizeof(spkac));
+    spkac.pkac.spki.algorithm.algorithm = CSSMOID_RSA;
+    spkac.pkac.spki.subjectPublicKey.Length =
+        CFDataGetLength(key_data) * 8;  // interpreted as a _bit_ count
+    spkac.pkac.spki.subjectPublicKey.Data =
+        const_cast<uint8_t*>(CFDataGetBytePtr(key_data));
+    spkac.pkac.challenge_string.Length = challenge_.length();
+    spkac.pkac.challenge_string.Data =
+        reinterpret_cast<uint8_t*>(const_cast<char*>(challenge_.data()));
+
+    CSSM_DATA encoded;
+    err = SecAsn1EncodeItem(coder, &spkac.pkac,
+                            kPublicKeyAndChallengeTemplate, &encoded);
+    if (err) {
+      base::LogCSSMError("SecAsn1EncodeItem", err);
+      goto failure;
+    }
+
+    // Compute a signature of the result:
+    err = SignData(encoded, private_key, &signature);
+    if (err)
+      goto failure;
+    spkac.signature.Data = signature.Data;
+    spkac.signature.Length = signature.Length * 8;  // a _bit_ count
+    spkac.signature_algorithm.algorithm = CSSMOID_MD5WithRSA;
+    // TODO(snej): MD5 is weak. Can we use SHA1 instead?
+    // See <https://bugzilla.mozilla.org/show_bug.cgi?id=549460>
+
+    // DER-encode the entire SignedPublicKeyAndChallenge:
+    err = SecAsn1EncodeItem(coder, &spkac,
+                            kSignedPublicKeyAndChallengeTemplate, &encoded);
+    if (err) {
+      base::LogCSSMError("SecAsn1EncodeItem", err);
+      goto failure;
+    }
+
+    // Base64 encode the result.
+    std::string input(reinterpret_cast<char*>(encoded.Data), encoded.Length);
+    base::Base64Encode(input, &result);
+  }
+
+ failure:
+  if (err) {
+    LOG(ERROR) << "SSL Keygen failed! OSStatus = " << err;
+  } else {
+    LOG(INFO) << "SSL Keygen succeeded! Output is: " << result;
+  }
+
+  // Remove keys from keychain if asked to during unit testing:
+  if (!stores_key_) {
+    if (public_key)
+      SecKeychainItemDelete(reinterpret_cast<SecKeychainItemRef>(public_key));
+    if (private_key)
+      SecKeychainItemDelete(reinterpret_cast<SecKeychainItemRef>(private_key));
+  }
+
+  // Clean up:
+  free(signature.Data);
+  if (coder)
+    SecAsn1CoderRelease(coder);
+  if (public_key)
+    CFRelease(public_key);
+  if (private_key)
+    CFRelease(private_key);
+  return result;
+}
+
+
+static OSStatus CreateRSAKeyPair(int size_in_bits,
+                                 SecKeyRef* out_pub_key,
+                                 SecKeyRef* out_priv_key) {
+  OSStatus err;
+  SecKeychainRef keychain;
+  err = SecKeychainCopyDefault(&keychain);
+  if (err) {
+    base::LogCSSMError("SecKeychainCopyDefault", err);
+    return err;
+  }
+  scoped_cftyperef<SecKeychainRef> scoped_keychain(keychain);
+  {
+    AutoLock locked(base::GetMacSecurityServicesLock());
+    err = SecKeyCreatePair(
+        keychain,
+        CSSM_ALGID_RSA,
+        size_in_bits,
+        0LL,
+        // public key usage and attributes:
+        CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP,
+        CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT,
+        // private key usage and attributes:
+        CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_UNWRAP,
+        CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT |
+            CSSM_KEYATTR_SENSITIVE,
+        NULL,
+        out_pub_key, out_priv_key);
+  }
+  if (err)
+    base::LogCSSMError("SecKeyCreatePair", err);
+  return err;
+}
+
+static OSStatus CreateSignatureContext(SecKeyRef key,
+                                       CSSM_ALGORITHMS algorithm,
+                                       CSSM_CC_HANDLE* out_cc_handle) {
+  OSStatus err;
+  const CSSM_ACCESS_CREDENTIALS* credentials = NULL;
+  {
+    AutoLock locked(base::GetMacSecurityServicesLock());
+    err = SecKeyGetCredentials(key,
+                               CSSM_ACL_AUTHORIZATION_SIGN,
+                               kSecCredentialTypeDefault,
+                               &credentials);
+  }
+  if (err) {
+    base::LogCSSMError("SecKeyGetCredentials", err);
+    return err;
+  }
+
+  CSSM_CSP_HANDLE csp_handle = 0;
+  {
+    AutoLock locked(base::GetMacSecurityServicesLock());
+    err = SecKeyGetCSPHandle(key, &csp_handle);
+  }
+  if (err) {
+    base::LogCSSMError("SecKeyGetCSPHandle", err);
+    return err;
+  }
+
+  const CSSM_KEY* cssm_key = NULL;
+  {
+    AutoLock locked(base::GetMacSecurityServicesLock());
+    err = SecKeyGetCSSMKey(key, &cssm_key);
+  }
+  if (err) {
+    base::LogCSSMError("SecKeyGetCSSMKey", err);
+    return err;
+  }
+
+  err = CSSM_CSP_CreateSignatureContext(csp_handle,
+                                        algorithm,
+                                        credentials,
+                                        cssm_key,
+                                        out_cc_handle);
+  if (err)
+    base::LogCSSMError("CSSM_CSP_CreateSignatureContext", err);
+  return err;
+}
+
+static OSStatus SignData(CSSM_DATA data,
+                         SecKeyRef private_key,
+                         CSSM_DATA* signature) {
+  CSSM_CC_HANDLE cc_handle;
+  OSStatus err = CreateSignatureContext(private_key,
+                                        CSSM_ALGID_MD5WithRSA,
+                                        &cc_handle);
+  if (err) {
+    base::LogCSSMError("CreateSignatureContext", err);
+    return err;
+  }
+  err = CSSM_SignData(cc_handle, &data, 1, CSSM_ALGID_NONE, signature);
+  if (err)
+    base::LogCSSMError("CSSM_SignData", err);
+  CSSM_DeleteContext(cc_handle);
+  return err;
 }
 
 }  // namespace net
diff --git a/net/base/keygen_handler_nss.cc b/net/base/keygen_handler_nss.cc
index 6c17298..638fbd5 100644
--- a/net/base/keygen_handler_nss.cc
+++ b/net/base/keygen_handler_nss.cc
@@ -1,255 +1,19 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/base/keygen_handler.h"
 
-#include <pk11pub.h>
-#include <secmod.h>
-#include <ssl.h>
-#include <nssb64.h>    // NSSBase64_EncodeItem()
-#include <secder.h>    // DER_Encode()
-#include <cryptohi.h>  // SEC_DerSignData()
-#include <keyhi.h>     // SECKEY_CreateSubjectPublicKeyInfo()
+#include "net/third_party/mozilla_security_manager/nsKeygenHandler.h"
 
-#include "base/nss_util.h"
-#include "base/logging.h"
+// PSM = Mozilla's Personal Security Manager.
+namespace psm = mozilla_security_manager;
 
 namespace net {
 
-const int64 DEFAULT_RSA_PUBLIC_EXPONENT = 0x10001;
-
-// Template for creating the signed public key structure to be sent to the CA.
-DERTemplate SECAlgorithmIDTemplate[] = {
-  { DER_SEQUENCE,
-    0, NULL, sizeof(SECAlgorithmID) },
-  { DER_OBJECT_ID,
-    offsetof(SECAlgorithmID, algorithm), },
-  { DER_OPTIONAL | DER_ANY,
-    offsetof(SECAlgorithmID, parameters), },
-  { 0, }
-};
-
-DERTemplate CERTSubjectPublicKeyInfoTemplate[] = {
-  { DER_SEQUENCE,
-    0, NULL, sizeof(CERTSubjectPublicKeyInfo) },
-  { DER_INLINE,
-    offsetof(CERTSubjectPublicKeyInfo, algorithm),
-    SECAlgorithmIDTemplate, },
-  { DER_BIT_STRING,
-    offsetof(CERTSubjectPublicKeyInfo, subjectPublicKey), },
-  { 0, }
-};
-
-DERTemplate CERTPublicKeyAndChallengeTemplate[] = {
-  { DER_SEQUENCE,
-    0, NULL, sizeof(CERTPublicKeyAndChallenge) },
-  { DER_ANY,
-    offsetof(CERTPublicKeyAndChallenge, spki), },
-  { DER_IA5_STRING,
-    offsetof(CERTPublicKeyAndChallenge, challenge), },
-  { 0, }
-};
-
-// This maps displayed strings indicating level of keysecurity in the <keygen>
-// menu to the key size in bits.
-// TODO(gauravsh): Should this mapping be moved else where?
-int RSAkeySizeMap[] = {2048, 1024};
-
-KeygenHandler::KeygenHandler(int key_size_index,
-                             const std::string& challenge)
-    : key_size_index_(key_size_index),
-      challenge_(challenge) {
-  if (key_size_index_ < 0 ||
-      key_size_index_ >=
-          static_cast<int>(sizeof(RSAkeySizeMap) / sizeof(RSAkeySizeMap[0])))
-    key_size_index_ = 0;
-}
-
-// This function is largely copied from the Firefox's
-// <keygen> implementation in security/manager/ssl/src/nsKeygenHandler.cpp
-// FIXME(gauravsh): Do we need a copy of the Mozilla license here?
-
 std::string KeygenHandler::GenKeyAndSignChallenge() {
-  // Key pair generation mechanism - only RSA is supported at present.
-  PRUint32 keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;  // from nss/pkcs11t.h
-  char *keystring = NULL;  // Temporary store for result/
-
-  // Temporary structures used for generating the result
-  // in the right format.
-  PK11SlotInfo *slot = NULL;
-  PK11RSAGenParams rsaKeyGenParams;  // Keygen parameters.
-  SECOidTag algTag;  // used by SEC_DerSignData().
-  SECKEYPrivateKey *privateKey = NULL;
-  SECKEYPublicKey *publicKey = NULL;
-  CERTSubjectPublicKeyInfo *spkInfo = NULL;
-  PRArenaPool *arena = NULL;
-  SECStatus sec_rv =SECFailure;
-  SECItem spkiItem;
-  SECItem pkacItem;
-  SECItem signedItem;
-  CERTPublicKeyAndChallenge pkac;
-  void *keyGenParams;
-  pkac.challenge.data = NULL;
-  bool isSuccess = true;  // Set to false as soon as a step fails.
-
-  std::string result_blob;  // the result.
-
-  // Ensure NSS is initialized.
-  base::EnsureNSSInit();
-
-  slot = PK11_GetInternalKeySlot();
-  if (!slot) {
-    LOG(ERROR) << "Couldn't get Internal key slot!";
-    isSuccess = false;
-    goto failure;
-  }
-
-  switch (keyGenMechanism) {
-    case CKM_RSA_PKCS_KEY_PAIR_GEN:
-      rsaKeyGenParams.keySizeInBits = RSAkeySizeMap[key_size_index_];
-      rsaKeyGenParams.pe = DEFAULT_RSA_PUBLIC_EXPONENT;
-      keyGenParams = &rsaKeyGenParams;
-
-      algTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;  // from <nss/secoidt.h>.
-      break;
-    default:
-      // TODO(gauravsh): If we ever support other mechanisms,
-      // this can be changed.
-      LOG(ERROR) << "Only RSA keygen mechanism is supported";
-      isSuccess = false;
-      goto failure;
-      break;
-  }
-
-  // Need to make sure that the token was initialized.
-  // Assume a null password.
-  sec_rv = PK11_Authenticate(slot, PR_TRUE, NULL);
-  if (SECSuccess != sec_rv) {
-    LOG(ERROR) << "Couldn't initialze PK11 token!";
-    isSuccess = false;
-    goto failure;
-  }
-
-  LOG(INFO) << "Creating key pair...";
-  privateKey = PK11_GenerateKeyPair(slot,
-                                    keyGenMechanism,
-                                    keyGenParams,
-                                    &publicKey,
-                                    PR_TRUE,  // isPermanent?
-                                    PR_TRUE,  // isSensitive?
-                                    NULL);
-  LOG(INFO) << "done.";
-
-  if (!privateKey) {
-    LOG(INFO) << "Generation of Keypair failed!";
-    isSuccess = false;
-    goto failure;
-  }
-
-  // The CA expects the signed public key in a specific format
-  // Let's create that now.
-
-  // Create a subject public key info from the public key.
-  spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey);
-  if (!spkInfo) {
-    LOG(ERROR) << "Couldn't create SubjectPublicKeyInfo from public key";
-    isSuccess = false;
-    goto failure;
-  }
-
-  // Temporary work store used by NSS.
-  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-  if (!arena) {
-    LOG(ERROR) << "PORT_NewArena: Couldn't allocate memory";
-    isSuccess = false;
-    goto failure;
-  }
-
-  // DER encode the whole subjectPublicKeyInfo.
-  sec_rv = DER_Encode(arena, &spkiItem, CERTSubjectPublicKeyInfoTemplate,
-                      spkInfo);
-  if (SECSuccess != sec_rv) {
-    LOG(ERROR) << "Couldn't DER Encode subjectPublicKeyInfo";
-    isSuccess = false;
-    goto failure;
-  }
-
-  // Set up the PublicKeyAndChallenge data structure, then DER encode it.
-  pkac.spki = spkiItem;
-  pkac.challenge.len = challenge_.length();
-  pkac.challenge.data = (unsigned char *)strdup(challenge_.c_str());
-  if (!pkac.challenge.data) {
-    LOG(ERROR) << "Out of memory while making a copy of challenge data";
-    isSuccess = false;
-    goto failure;
-  }
-  sec_rv = DER_Encode(arena, &pkacItem, CERTPublicKeyAndChallengeTemplate,
-                      &pkac);
-  if (SECSuccess != sec_rv) {
-    LOG(ERROR) << "Couldn't DER Encode PublicKeyAndChallenge";
-    isSuccess = false;
-    goto failure;
-  }
-
-  // Sign the DER encoded PublicKeyAndChallenge.
-  sec_rv = SEC_DerSignData(arena, &signedItem, pkacItem.data, pkacItem.len,
-                           privateKey, algTag);
-  if (SECSuccess != sec_rv) {
-    LOG(ERROR) << "Couldn't sign the DER encoded PublicKeyandChallenge";
-    isSuccess = false;
-    goto failure;
-  }
-
-  // Convert the signed public key and challenge into base64/ascii.
-  keystring = NSSBase64_EncodeItem(arena,
-                                   NULL,  // NSS will allocate a buffer for us.
-                                   0,
-                                   &signedItem);
-  if (!keystring) {
-    LOG(ERROR) << "Couldn't convert signed public key into base64";
-    isSuccess = false;
-    goto failure;
-  }
-
-  result_blob = keystring;
-
- failure:
-  if (!isSuccess) {
-    LOG(ERROR) << "SSL Keygen failed!";
-  } else {
-    LOG(INFO) << "SSl Keygen succeeded!";
-  }
-
-  // Do cleanups
-  if (privateKey) {
-    // TODO(gauravsh): We still need to maintain the private key because it's
-    // used for certificate enrollment checks.
-
-    // PK11_DestroyTokenObject(privateKey->pkcs11Slot,privateKey->pkcs11ID);
-    // SECKEY_DestroyPrivateKey(privateKey);
-  }
-
-  if (publicKey) {
-    PK11_DestroyTokenObject(publicKey->pkcs11Slot, publicKey->pkcs11ID);
-  }
-  if (spkInfo) {
-    SECKEY_DestroySubjectPublicKeyInfo(spkInfo);
-  }
-  if (publicKey) {
-    SECKEY_DestroyPublicKey(publicKey);
-  }
-  if (arena) {
-    PORT_FreeArena(arena, PR_TRUE);
-  }
-  if (slot != NULL) {
-    PK11_FreeSlot(slot);
-  }
-  if (pkac.challenge.data) {
-    free(pkac.challenge.data);
-  }
-
-  return (isSuccess ? result_blob : std::string());
+  return psm::GenKeyAndSignChallenge(key_size_in_bits_, challenge_,
+                                     stores_key_);
 }
 
 }  // namespace net
diff --git a/net/base/keygen_handler_unittest.cc b/net/base/keygen_handler_unittest.cc
new file mode 100644
index 0000000..15ec0ce
--- /dev/null
+++ b/net/base/keygen_handler_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2010 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/base/keygen_handler.h"
+
+#include "build/build_config.h" // Needs to be imported early for USE_NSS
+
+#if defined(USE_NSS)
+#include <private/pprthred.h>  // PR_DetachThread
+#endif
+
+#include <string>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/nss_util.h"
+#include "base/task.h"
+#include "base/waitable_event.h"
+#include "base/worker_pool.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+class KeygenHandlerTest : public ::testing::Test {
+ public:
+  KeygenHandlerTest() {}
+  virtual ~KeygenHandlerTest() {}
+
+  virtual void SetUp() {
+#if defined(OS_CHROMEOS)
+  base::OpenPersistentNSSDB();
+#endif
+  }
+};
+
+// Assert that |result| is a valid output for KeygenHandler given challenge
+// string of |challenge|.
+void AssertValidSignedPublicKeyAndChallenge(const std::string& result,
+                                            const std::string& challenge) {
+  ASSERT_GT(result.length(), 0U);
+
+  // Verify it's valid base64:
+  std::string spkac;
+  ASSERT_TRUE(base::Base64Decode(result, &spkac));
+  // In lieu of actually parsing and validating the DER data,
+  // just check that it exists and has a reasonable length.
+  // (It's almost always 590 bytes, but the DER encoding of the random key
+  // and signature could sometimes be a few bytes different.)
+  ASSERT_GE(spkac.length(), 580U);
+  ASSERT_LE(spkac.length(), 600U);
+
+  // NOTE:
+  // The value of |result| can be validated by prefixing 'SPKAC=' to it
+  // and piping it through
+  //   openssl spkac -verify
+  // whose output should look like:
+  //   Netscape SPKI:
+  //     Public Key Algorithm: rsaEncryption
+  //     RSA Public Key: (2048 bit)
+  //     Modulus (2048 bit):
+  //         00:b6:cc:14:c9:43:b5:2d:51:65:7e:11:8b:80:9e: .....
+  //     Exponent: 65537 (0x10001)
+  //     Challenge String: some challenge
+  //     Signature Algorithm: md5WithRSAEncryption
+  //         92:f3:cc:ff:0b:d3:d0:4a:3a:4c:ba:ff:d6:38:7f:a5:4b:b5: .....
+  //   Signature OK
+  //
+  // The value of |spkac| can be ASN.1-parsed with:
+  //    openssl asn1parse -inform DER
+}
+
+TEST_F(KeygenHandlerTest, FLAKY_SmokeTest) {
+  KeygenHandler handler(2048, "some challenge");
+  handler.set_stores_key(false);  // Don't leave the key-pair behind
+  std::string result = handler.GenKeyAndSignChallenge();
+  LOG(INFO) << "KeygenHandler produced: " << result;
+  AssertValidSignedPublicKeyAndChallenge(result, "some challenge");
+}
+
+class ConcurrencyTestTask : public Task {
+ public:
+  ConcurrencyTestTask(base::WaitableEvent* event,
+                      const std::string& challenge, std::string* result)
+      : event_(event),
+        challenge_(challenge),
+        result_(result) {
+  }
+
+  virtual void Run() {
+    KeygenHandler handler(2048, "some challenge");
+    handler.set_stores_key(false); // Don't leave the key-pair behind.
+    *result_ = handler.GenKeyAndSignChallenge();
+    event_->Signal();
+#if defined(USE_NSS)
+    // Detach the thread from NSPR.
+    // Calling NSS functions attaches the thread to NSPR, which stores
+    // the NSPR thread ID in thread-specific data.
+    // The threads in our thread pool terminate after we have called
+    // PR_Cleanup.  Unless we detach them from NSPR, net_unittests gets
+    // segfaults on shutdown when the threads' thread-specific data
+    // destructors run.
+    PR_DetachThread();
+#endif
+  }
+
+ private:
+  base::WaitableEvent* event_;
+  std::string challenge_;
+  std::string* result_;
+};
+
+// We asynchronously generate the keys so as not to hang up the IO thread. This
+// test tries to catch concurrency problems in the keygen implementation.
+TEST_F(KeygenHandlerTest, ConcurrencyTest) {
+  const int NUM_HANDLERS = 5;
+  base::WaitableEvent* events[NUM_HANDLERS] = { NULL };
+  std::string results[NUM_HANDLERS];
+  for (int i = 0; i < NUM_HANDLERS; i++) {
+    events[i] = new base::WaitableEvent(false, false);
+    WorkerPool::PostTask(FROM_HERE,
+                         new ConcurrencyTestTask(events[i], "some challenge",
+                                                 &results[i]),
+                         true);
+  }
+
+  for (int i = 0; i < NUM_HANDLERS; i++) {
+    // Make sure the job completed
+    bool signaled = events[i]->Wait();
+    EXPECT_TRUE(signaled);
+    delete events[i];
+    events[i] = NULL;
+
+    LOG(INFO) << "KeygenHandler " << i << " produced: " << results[i];
+    AssertValidSignedPublicKeyAndChallenge(results[i], "some challenge");
+  }
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/base/keygen_handler_win.cc b/net/base/keygen_handler_win.cc
index f6b4551..8fc32e5 100644
--- a/net/base/keygen_handler_win.cc
+++ b/net/base/keygen_handler_win.cc
@@ -1,23 +1,220 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/base/keygen_handler.h"
 
+#include <windows.h>
+#include <wincrypt.h>
+#pragma comment(lib, "crypt32.lib")
+#include <rpc.h>
+#pragma comment(lib, "rpcrt4.lib")
+
+#include <list>
+#include <string>
+#include <vector>
+
+#include "base/base64.h"
+#include "base/basictypes.h"
+#include "base/crypto/capi_util.h"
 #include "base/logging.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 
 namespace net {
 
-KeygenHandler::KeygenHandler(int key_size_index,
-                             const std::string& challenge)
-    : key_size_index_(key_size_index),
-      challenge_(challenge) {
-  NOTIMPLEMENTED();
+// Assigns the contents of a CERT_PUBLIC_KEY_INFO structure for the signing
+// key in |prov| to |output|. Returns true if encoding was successful.
+bool GetSubjectPublicKeyInfo(HCRYPTPROV prov, std::vector<BYTE>* output) {
+  BOOL ok;
+  DWORD size = 0;
+
+  // From the private key stored in HCRYPTPROV, obtain the public key, stored
+  // as a CERT_PUBLIC_KEY_INFO structure. Currently, only RSA public keys are
+  // supported.
+  ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
+                                  szOID_RSA_RSA, 0, NULL, NULL, &size);
+  DCHECK(ok);
+  if (!ok)
+    return false;
+
+  output->resize(size);
+
+  PCERT_PUBLIC_KEY_INFO public_key_casted =
+      reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&(*output)[0]);
+  ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
+                                  szOID_RSA_RSA, 0, NULL, public_key_casted,
+                                  &size);
+  DCHECK(ok);
+  if (!ok)
+    return false;
+
+  output->resize(size);
+
+  return true;
+}
+
+// Generates a DER encoded SignedPublicKeyAndChallenge structure from the
+// signing key of |prov| and the specified ASCII |challenge| string and
+// appends it to |output|.
+// True if the encoding was successfully generated.
+bool GetSignedPublicKeyAndChallenge(HCRYPTPROV prov,
+                                    const std::string& challenge,
+                                    std::string* output) {
+  std::wstring wide_challenge = ASCIIToWide(challenge);
+  std::vector<BYTE> spki;
+
+  if (!GetSubjectPublicKeyInfo(prov, &spki))
+    return false;
+
+  // PublicKeyAndChallenge ::= SEQUENCE {
+  //     spki SubjectPublicKeyInfo,
+  //     challenge IA5STRING
+  // }
+  CERT_KEYGEN_REQUEST_INFO pkac;
+  pkac.dwVersion = CERT_KEYGEN_REQUEST_V1;
+  pkac.SubjectPublicKeyInfo =
+      *reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&spki[0]);
+  pkac.pwszChallengeString = const_cast<wchar_t*>(wide_challenge.c_str());
+
+  CRYPT_ALGORITHM_IDENTIFIER sig_alg;
+  memset(&sig_alg, 0, sizeof(sig_alg));
+  sig_alg.pszObjId = szOID_RSA_MD5RSA;
+
+  BOOL ok;
+  DWORD size = 0;
+  std::vector<BYTE> signed_pkac;
+  ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
+                                     X509_KEYGEN_REQUEST_TO_BE_SIGNED,
+                                     &pkac, &sig_alg, NULL,
+                                     NULL, &size);
+  DCHECK(ok);
+  if (!ok)
+    return false;
+
+  signed_pkac.resize(size);
+  ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
+                                     X509_KEYGEN_REQUEST_TO_BE_SIGNED,
+                                     &pkac, &sig_alg, NULL,
+                                     &signed_pkac[0], &size);
+  DCHECK(ok);
+  if (!ok)
+    return false;
+
+  output->assign(reinterpret_cast<char*>(&signed_pkac[0]), size);
+  return true;
+}
+
+// Generates a unique name for the container which will store the key that is
+// generated. The traditional Windows approach is to use a GUID here.
+std::wstring GetNewKeyContainerId() {
+  RPC_STATUS status = RPC_S_OK;
+  std::wstring result;
+
+  UUID id = { 0 };
+  status = UuidCreateSequential(&id);
+  if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY)
+    return result;
+
+  RPC_WSTR rpc_string = NULL;
+  status = UuidToString(&id, &rpc_string);
+  if (status != RPC_S_OK)
+    return result;
+
+  // RPC_WSTR is unsigned short*.  wchar_t is a built-in type of Visual C++,
+  // so the type cast is necessary.
+  result.assign(reinterpret_cast<wchar_t*>(rpc_string));
+  RpcStringFree(&rpc_string);
+
+  return result;
 }
 
 std::string KeygenHandler::GenKeyAndSignChallenge() {
-  NOTIMPLEMENTED();
-  return std::string();
+  std::string result;
+
+  bool is_success = true;
+  HCRYPTPROV prov = NULL;
+  HCRYPTKEY key = NULL;
+  DWORD flags = (key_size_in_bits_ << 16) | CRYPT_EXPORTABLE;
+  std::string spkac;
+
+  std::wstring new_key_id;
+
+  // TODO(rsleevi): Have the user choose which provider they should use, which
+  // needs to be filtered by those providers which can provide the key type
+  // requested or the key size requested. This is especially important for
+  // generating certificates that will be stored on smart cards.
+  const int kMaxAttempts = 5;
+  BOOL ok = FALSE;
+  for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
+    // Per MSDN documentation for CryptAcquireContext, if applications will be
+    // creating their own keys, they should ensure unique naming schemes to
+    // prevent overlap with any other applications or consumers of CSPs, and
+    // *should not* store new keys within the default, NULL key container.
+    new_key_id = GetNewKeyContainerId();
+    if (new_key_id.empty())
+      return result;
+
+    // Only create new key containers, so that existing key containers are not
+    // overwritten.
+    ok = base::CryptAcquireContextLocked(&prov, new_key_id.c_str(), NULL,
+                                         PROV_RSA_FULL,
+                                         CRYPT_SILENT | CRYPT_NEWKEYSET);
+
+    if (ok || GetLastError() != NTE_BAD_KEYSET)
+      break;
+  }
+  if (!ok) {
+    LOG(ERROR) << "Couldn't acquire a CryptoAPI provider context: "
+               << GetLastError();
+    is_success = false;
+    goto failure;
+  }
+
+  if (!CryptGenKey(prov, CALG_RSA_KEYX, flags, &key)) {
+    LOG(ERROR) << "Couldn't generate an RSA key";
+    is_success = false;
+    goto failure;
+  }
+
+  if (!GetSignedPublicKeyAndChallenge(prov, challenge_, &spkac)) {
+    LOG(ERROR) << "Couldn't generate the signed public key and challenge";
+    is_success = false;
+    goto failure;
+  }
+
+  if (!base::Base64Encode(spkac, &result)) {
+    LOG(ERROR) << "Couldn't convert signed key into base64";
+    is_success = false;
+    goto failure;
+  }
+
+ failure:
+  if (!is_success) {
+    LOG(ERROR) << "SSL Keygen failed";
+  } else {
+    LOG(INFO) << "SSL Key succeeded";
+  }
+  if (key) {
+    // Securely destroys the handle, but leaves the underlying key alone. The
+    // key can be obtained again by resolving the key location. If
+    // |stores_key_| is false, the underlying key will be destroyed below.
+    CryptDestroyKey(key);
+  }
+
+  if (prov) {
+    CryptReleaseContext(prov, 0);
+    prov = NULL;
+    if (!stores_key_) {
+      // Fully destroys any of the keys that were created and releases prov.
+      base::CryptAcquireContextLocked(&prov, new_key_id.c_str(), NULL,
+                                      PROV_RSA_FULL,
+                                      CRYPT_SILENT | CRYPT_DELETEKEYSET);
+    }
+  }
+
+  return result;
 }
 
 }  // namespace net
diff --git a/net/base/listen_socket.cc b/net/base/listen_socket.cc
index 88e9592..0cb529d 100644
--- a/net/base/listen_socket.cc
+++ b/net/base/listen_socket.cc
@@ -10,6 +10,7 @@
 #include <winsock2.h>
 #elif defined(OS_POSIX)
 #include <errno.h>
+#include <netinet/in.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include "net/base/net_errors.h"
@@ -30,7 +31,7 @@
 
 namespace {
 
-const int kReadBufSize = 200;
+const int kReadBufSize = 4096;
 
 }  // namespace
 
@@ -159,7 +160,7 @@
       // TODO(ibrar): maybe change DidRead to take a length instead
       DCHECK(len > 0 && len <= kReadBufSize);
       buf[len] = 0;  // already create a buffer with +1 length
-      socket_delegate_->DidRead(this, buf);
+      socket_delegate_->DidRead(this, buf, len);
     }
   } while (len == kReadBufSize);
 }
@@ -205,19 +206,32 @@
 }
 
 void ListenSocket::SendInternal(const char* bytes, int len) {
-  int sent = HANDLE_EINTR(send(socket_, bytes, len, 0));
-  if (sent == kSocketError) {
-#if defined(OS_WIN)
-  int err = WSAGetLastError();
-  if (err == WSAEWOULDBLOCK) {
-#elif defined(OS_POSIX)
-    if (errno == EWOULDBLOCK || errno == EAGAIN) {
-#endif
-    // TODO(ibrar): there should be logic here to handle this because
-    // it is not an error
+  char* send_buf = const_cast<char *>(bytes);
+  int len_left = len;
+  while (true) {
+    int sent = HANDLE_EINTR(send(socket_, send_buf, len_left, 0));
+    if (sent == len_left) {  // A shortcut to avoid extraneous checks.
+      break;
     }
-  } else if (sent != len) {
-    LOG(ERROR) << "send failed: ";
+    if (sent == kSocketError) {
+#if defined(OS_WIN)
+      if (WSAGetLastError() != WSAEWOULDBLOCK) {
+        LOG(ERROR) << "send failed: WSAGetLastError()==" << WSAGetLastError();
+#elif defined(OS_POSIX)
+      if (errno != EWOULDBLOCK && errno != EAGAIN) {
+        LOG(ERROR) << "send failed: errno==" << errno;
+#endif
+        break;
+      }
+      // Otherwise we would block, and now we have to wait for a retry.
+      // Fall through to PlatformThread::YieldCurrentThread()
+    } else {
+      // sent != len_left according to the shortcut above.
+      // Shift the buffer start and send the remainder after a short while.
+      send_buf += sent;
+      len_left -= sent;
+    }
+    PlatformThread::YieldCurrentThread();
   }
 }
 
diff --git a/net/base/listen_socket.h b/net/base/listen_socket.h
index 585a094..923d361 100644
--- a/net/base/listen_socket.h
+++ b/net/base/listen_socket.h
@@ -51,7 +51,9 @@
     // Socket that was created.  Ownership of connection is transferred
     // to the delegate with this call.
     virtual void DidAccept(ListenSocket *server, ListenSocket *connection) = 0;
-    virtual void DidRead(ListenSocket *connection, const std::string& data) = 0;
+    virtual void DidRead(ListenSocket *connection,
+                         const char* data,
+                         int len) = 0;
     virtual void DidClose(ListenSocket *sock) = 0;
   };
 
diff --git a/net/base/listen_socket_unittest.cc b/net/base/listen_socket_unittest.cc
index eb224c3..b38019d 100644
--- a/net/base/listen_socket_unittest.cc
+++ b/net/base/listen_socket_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -54,7 +54,7 @@
 
   // verify the connect/accept and setup test_socket_
   test_socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-  ASSERT_NE(-1, test_socket_);
+  ASSERT_NE(INVALID_SOCKET, test_socket_);
   struct sockaddr_in client;
   client.sin_family = AF_INET;
   client.sin_addr.s_addr = inet_addr(kLoopback);
@@ -212,8 +212,10 @@
 }
 
 void ListenSocketTester::DidRead(ListenSocket *connection,
-                                 const std::string& data) {
-  ReportAction(ListenSocketTestAction(ACTION_READ, data));
+                                 const char* data,
+                                 int len) {
+  std::string str(data, len);
+  ReportAction(ListenSocketTestAction(ACTION_READ, str));
 }
 
 void ListenSocketTester::DidClose(ListenSocket *sock) {
@@ -317,6 +319,8 @@
   tester_->TestClientSendLong();
 }
 
-TEST_F(ListenSocketTest, ServerSend) {
+// This test is flaky; see comment in ::TestServerSend.
+// http://code.google.com/p/chromium/issues/detail?id=48562
+TEST_F(ListenSocketTest, FLAKY_ServerSend) {
   tester_->TestServerSend();
 }
diff --git a/net/base/listen_socket_unittest.h b/net/base/listen_socket_unittest.h
index 81cbb95..d43364a 100644
--- a/net/base/listen_socket_unittest.h
+++ b/net/base/listen_socket_unittest.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -16,6 +16,7 @@
 #include <arpa/inet.h>
 #endif
 
+#include "base/scoped_ptr.h"
 #include "base/thread.h"
 #include "base/basictypes.h"
 #include "base/message_loop.h"
@@ -52,7 +53,7 @@
         data_(data) {}
 
   const std::string data() const { return data_; }
-  const ActionType type() const { return action_; }
+  ActionType type() const { return action_; }
 
  private:
   ActionType action_;
@@ -94,7 +95,7 @@
   void Listen();
   void SendFromTester();
   virtual void DidAccept(ListenSocket *server, ListenSocket *connection);
-  virtual void DidRead(ListenSocket *connection, const std::string& data);
+  virtual void DidRead(ListenSocket *connection, const char* data, int len);
   virtual void DidClose(ListenSocket *sock);
   virtual bool Send(SOCKET sock, const std::string& str);
   // verify the send/read from client to server
diff --git a/net/base/load_flags.h b/net/base/load_flags.h
index 49c6daf..53b2d58 100644
--- a/net/base/load_flags.h
+++ b/net/base/load_flags.h
@@ -1,84 +1,22 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_BASE_LOAD_FLAGS_H__
-#define NET_BASE_LOAD_FLAGS_H__
+#ifndef NET_BASE_LOAD_FLAGS_H_
+#define NET_BASE_LOAD_FLAGS_H_
 
 namespace net {
 
 // These flags provide metadata about the type of the load request.  They are
 // intended to be OR'd together.
 enum {
-  LOAD_NORMAL = 0,
 
-  // This is "normal reload", meaning an if-none-match/if-modified-since query
-  LOAD_VALIDATE_CACHE = 1 << 0,
+#define LOAD_FLAG(label, value) LOAD_ ## label = value,
+#include "net/base/load_flags_list.h"
+#undef LOAD_FLAG
 
-  // This is "shift-reload", meaning a "pragma: no-cache" end-to-end fetch
-  LOAD_BYPASS_CACHE = 1 << 1,
-
-  // This is a back/forward style navigation where the cached content should
-  // be preferred over any protocol specific cache validation.
-  LOAD_PREFERRING_CACHE = 1 << 2,
-
-  // This is a navigation that will fail if it cannot serve the requested
-  // resource from the cache (or some equivalent local store).
-  LOAD_ONLY_FROM_CACHE = 1 << 3,
-
-  // This is a navigation that will not use the cache at all.  It does not
-  // impact the HTTP request headers.
-  LOAD_DISABLE_CACHE = 1 << 4,
-
-  // This is a navigation that will not be intercepted by any registered
-  // URLRequest::Interceptors.
-  LOAD_DISABLE_INTERCEPT = 1 << 5,
-
-  // If present, upload progress messages should be provided to initiator.
-  LOAD_ENABLE_UPLOAD_PROGRESS = 1 << 6,
-
-  // If present, ignores certificate mismatches with the domain name.
-  // (The default behavior is to trigger an OnSSLCertificateError callback.)
-  LOAD_IGNORE_CERT_COMMON_NAME_INVALID = 1 << 8,
-
-  // If present, ignores certificate expiration dates
-  // (The default behavior is to trigger an OnSSLCertificateError callback).
-  LOAD_IGNORE_CERT_DATE_INVALID = 1 << 9,
-
-  // If present, trusts all certificate authorities
-  // (The default behavior is to trigger an OnSSLCertificateError callback).
-  LOAD_IGNORE_CERT_AUTHORITY_INVALID = 1 << 10,
-
-  // If present, ignores certificate revocation
-  // (The default behavior is to trigger an OnSSLCertificateError callback).
-  LOAD_IGNORE_CERT_REVOCATION = 1 << 11,
-
-  // If present, ignores wrong key usage of the certificate
-  // (The default behavior is to trigger an OnSSLCertificateError callback).
-  LOAD_IGNORE_CERT_WRONG_USAGE = 1 << 12,
-
-  // This load will not make any changes to cookies, including storing new
-  // cookies or updating existing ones.
-  LOAD_DO_NOT_SAVE_COOKIES = 1 << 13,
-
-  // Do not resolve proxies. This override is used when downloading PAC files
-  // to avoid having a circular dependency.
-  LOAD_BYPASS_PROXY = 1 << 14,
-
-  // Indicate this request is for a download, as opposed to viewing.
-  LOAD_IS_DOWNLOAD = 1 << 15,
-
-  // Requires EV certificate verification.
-  LOAD_VERIFY_EV_CERT = 1 << 16,
-
-  // This load will not send any cookies.
-  LOAD_DO_NOT_SEND_COOKIES = 1 << 17,
-
-  // This load will not send authentication data (user name/password)
-  // to the server (as opposed to the proxy).
-  LOAD_DO_NOT_SEND_AUTH_DATA = 1 << 18,
 };
 
 }  // namespace net
 
-#endif  // NET_BASE_LOAD_FLAGS_H__
+#endif  // NET_BASE_LOAD_FLAGS_H_
diff --git a/net/base/load_flags_list.h b/net/base/load_flags_list.h
new file mode 100644
index 0000000..ff5e609
--- /dev/null
+++ b/net/base/load_flags_list.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2010 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.
+
+// This is the list of load flags and their values. For the enum values,
+// include the file "net/base/load_flags.h".
+//
+// Here we define the values using a macro LOAD_FLAG, so it can be
+// expanded differently in some places (for example, to automatically
+// map a load flag value to its symbolic name).
+
+LOAD_FLAG(NORMAL, 0)
+
+// This is "normal reload", meaning an if-none-match/if-modified-since query
+LOAD_FLAG(VALIDATE_CACHE, 1 << 0)
+
+// This is "shift-reload", meaning a "pragma: no-cache" end-to-end fetch
+LOAD_FLAG(BYPASS_CACHE, 1 << 1)
+
+// This is a back/forward style navigation where the cached content should
+// be preferred over any protocol specific cache validation.
+LOAD_FLAG(PREFERRING_CACHE, 1 << 2)
+
+// This is a navigation that will fail if it cannot serve the requested
+// resource from the cache (or some equivalent local store).
+LOAD_FLAG(ONLY_FROM_CACHE, 1 << 3)
+
+// This is a navigation that will not use the cache at all.  It does not
+// impact the HTTP request headers.
+LOAD_FLAG(DISABLE_CACHE, 1 << 4)
+
+// This is a navigation that will not be intercepted by any registered
+// URLRequest::Interceptors.
+LOAD_FLAG(DISABLE_INTERCEPT, 1 << 5)
+
+// If present, upload progress messages should be provided to initiator.
+LOAD_FLAG(ENABLE_UPLOAD_PROGRESS, 1 << 6)
+
+// If present, collect load timing for the request.
+LOAD_FLAG(ENABLE_LOAD_TIMING, 1 << 7)
+
+// If present, ignores certificate mismatches with the domain name.
+// (The default behavior is to trigger an OnSSLCertificateError callback.)
+LOAD_FLAG(IGNORE_CERT_COMMON_NAME_INVALID, 1 << 8)
+
+// If present, ignores certificate expiration dates
+// (The default behavior is to trigger an OnSSLCertificateError callback).
+LOAD_FLAG(IGNORE_CERT_DATE_INVALID, 1 << 9)
+
+// If present, trusts all certificate authorities
+// (The default behavior is to trigger an OnSSLCertificateError callback).
+LOAD_FLAG(IGNORE_CERT_AUTHORITY_INVALID, 1 << 10)
+
+// If present, ignores certificate revocation
+// (The default behavior is to trigger an OnSSLCertificateError callback).
+LOAD_FLAG(IGNORE_CERT_REVOCATION, 1 << 11)
+
+// If present, ignores wrong key usage of the certificate
+// (The default behavior is to trigger an OnSSLCertificateError callback).
+LOAD_FLAG(IGNORE_CERT_WRONG_USAGE, 1 << 12)
+
+// This load will not make any changes to cookies, including storing new
+// cookies or updating existing ones.
+LOAD_FLAG(DO_NOT_SAVE_COOKIES, 1 << 13)
+
+// Do not resolve proxies. This override is used when downloading PAC files
+// to avoid having a circular dependency.
+LOAD_FLAG(BYPASS_PROXY, 1 << 14)
+
+// Indicate this request is for a download, as opposed to viewing.
+LOAD_FLAG(IS_DOWNLOAD, 1 << 15)
+
+// Requires EV certificate verification.
+LOAD_FLAG(VERIFY_EV_CERT, 1 << 16)
+
+// This load will not send any cookies.
+LOAD_FLAG(DO_NOT_SEND_COOKIES, 1 << 17)
+
+// This load will not send authentication data (user name/password)
+// to the server (as opposed to the proxy).
+LOAD_FLAG(DO_NOT_SEND_AUTH_DATA, 1 << 18)
+
+// This should only be used for testing (set by HttpNetworkTransaction).
+LOAD_FLAG(IGNORE_ALL_CERT_ERRORS, 1 << 19)
diff --git a/net/base/load_states.h b/net/base/load_states.h
index 9555be9..321dee8 100644
--- a/net/base/load_states.h
+++ b/net/base/load_states.h
@@ -29,6 +29,10 @@
   // host before deciding what proxy to use.
   LOAD_STATE_RESOLVING_PROXY_FOR_URL,
 
+  // This state indicates that we're in the process of establishing a tunnel
+  // through the proxy server.
+  LOAD_STATE_ESTABLISHING_PROXY_TUNNEL,
+
   // This state corresponds to a resource load that is blocked waiting for a
   // host name to be resolved.  This could either indicate resolution of the
   // origin server corresponding to the resource or to the host name of a proxy
@@ -40,6 +44,10 @@
   // requests that reuse a keep-alive connection skip this state.
   LOAD_STATE_CONNECTING,
 
+  // This state corresponds to a resource load that is blocked waiting for the
+  // SSL handshake to complete.
+  LOAD_STATE_SSL_HANDSHAKE,
+
   // This state corresponds to a resource load that is blocked waiting to
   // completely upload a request to a server.  In the case of a HTTP POST
   // request, this state includes the period of time during which the message
diff --git a/net/base/mapped_host_resolver.cc b/net/base/mapped_host_resolver.cc
new file mode 100644
index 0000000..b401917
--- /dev/null
+++ b/net/base/mapped_host_resolver.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2010 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/base/mapped_host_resolver.h"
+
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_util.h"
+
+namespace net {
+
+MappedHostResolver::MappedHostResolver(HostResolver* impl)
+    : impl_(impl) {
+}
+
+int MappedHostResolver::Resolve(const RequestInfo& info,
+                                AddressList* addresses,
+                                CompletionCallback* callback,
+                                RequestHandle* out_req,
+                                const BoundNetLog& net_log) {
+  // Modify the request before forwarding it to |impl_|.
+  RequestInfo modified_info = info;
+  HostPortPair host_port(info.hostname(), info.port());
+  if (rules_.RewriteHost(&host_port)) {
+    modified_info.set_hostname(host_port.host);
+    modified_info.set_port(host_port.port);
+  }
+  return impl_->Resolve(modified_info, addresses, callback, out_req, net_log);
+}
+
+void MappedHostResolver::CancelRequest(RequestHandle req) {
+  impl_->CancelRequest(req);
+}
+
+void MappedHostResolver::AddObserver(Observer* observer) {
+  impl_->AddObserver(observer);
+}
+
+void MappedHostResolver::RemoveObserver(Observer* observer) {
+  impl_->RemoveObserver(observer);
+}
+
+HostResolverImpl* MappedHostResolver::GetAsHostResolverImpl() {
+  return impl_->GetAsHostResolverImpl();
+}
+
+MappedHostResolver::~MappedHostResolver() {
+}
+
+}  // namespace net
diff --git a/net/base/mapped_host_resolver.h b/net/base/mapped_host_resolver.h
new file mode 100644
index 0000000..3229209
--- /dev/null
+++ b/net/base/mapped_host_resolver.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2010 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_BASE_MAPPED_HOST_RESOLVER_H_
+#define NET_BASE_MAPPED_HOST_RESOLVER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "net/base/host_mapping_rules.h"
+#include "net/base/host_resolver.h"
+
+namespace net {
+
+// This class wraps an existing HostResolver instance, but modifies the
+// request before passing it off to |impl|. This is different from
+// MockHostResolver which does the remapping at the HostResolverProc
+// layer, so it is able to preserve the effectiveness of the cache.
+class MappedHostResolver : public HostResolver {
+ public:
+  // Creates a MappedHostResolver that forwards all of its requests through
+  // |impl|.
+  explicit MappedHostResolver(HostResolver* impl);
+
+  // HostResolver methods:
+  virtual int Resolve(const RequestInfo& info,
+                      AddressList* addresses,
+                      CompletionCallback* callback,
+                      RequestHandle* out_req,
+                      const BoundNetLog& net_log);
+  virtual void CancelRequest(RequestHandle req);
+  virtual void AddObserver(Observer* observer);
+  virtual void RemoveObserver(Observer* observer);
+  virtual HostResolverImpl* GetAsHostResolverImpl();
+
+  // Adds a rule to this mapper. The format of the rule can be one of:
+  //
+  //   "MAP" <hostname_pattern> <replacement_host> [":" <replacement_port>]
+  //   "EXCLUDE" <hostname_pattern>
+  //
+  // The <replacement_host> can be either a hostname, or an IP address literal.
+  //
+  // Returns true if the rule was successfully parsed and added.
+  bool AddRuleFromString(const std::string& rule_string) {
+    return rules_.AddRuleFromString(rule_string);
+  }
+
+  // Takes a comma separated list of rules, and assigns them to this resolver.
+  void SetRulesFromString(const std::string& rules_string) {
+    rules_.SetRulesFromString(rules_string);
+  }
+
+ private:
+  virtual ~MappedHostResolver();
+
+  scoped_refptr<HostResolver> impl_;
+
+  HostMappingRules rules_;
+};
+
+}  // namespace net
+
+#endif  // NET_BASE_MAPPED_HOST_RESOLVER_H_
diff --git a/net/base/mapped_host_resolver_unittest.cc b/net/base/mapped_host_resolver_unittest.cc
new file mode 100644
index 0000000..4b7281c
--- /dev/null
+++ b/net/base/mapped_host_resolver_unittest.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2010 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/base/mapped_host_resolver.h"
+
+#include "net/base/mock_host_resolver.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+TEST(MappedHostResolverTest, Inclusion) {
+  // Create a mock host resolver, with specific hostname to IP mappings.
+  scoped_refptr<MockHostResolver> resolver_impl = new MockHostResolver();
+  resolver_impl->rules()->AddSimulatedFailure("*google.com");
+  resolver_impl->rules()->AddRule("baz.com", "192.168.1.5");
+  resolver_impl->rules()->AddRule("foo.com", "192.168.1.8");
+  resolver_impl->rules()->AddRule("proxy", "192.168.1.11");
+
+  // Create a remapped resolver that uses |resolver_impl|.
+  scoped_refptr<MappedHostResolver> resolver =
+      new MappedHostResolver(resolver_impl);
+
+  int rv;
+  AddressList address_list;
+
+  // Try resolving "www.google.com:80". There are no mappings yet, so this
+  // hits |resolver_impl| and fails.
+  rv = resolver->Resolve(HostResolver::RequestInfo("www.google.com", 80),
+                         &address_list, NULL, NULL, BoundNetLog());
+  EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv);
+
+  // Remap *.google.com to baz.com.
+  EXPECT_TRUE(resolver->AddRuleFromString("map *.google.com baz.com"));
+
+  // Try resolving "www.google.com:80". Should be remapped to "baz.com:80".
+  rv = resolver->Resolve(HostResolver::RequestInfo("www.google.com", 80),
+                         &address_list, NULL, NULL, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ("192.168.1.5", NetAddressToString(address_list.head()));
+  EXPECT_EQ(80, address_list.GetPort());
+
+  // Try resolving "foo.com:77". This will NOT be remapped, so result
+  // is "foo.com:77".
+  rv = resolver->Resolve(HostResolver::RequestInfo("foo.com", 77),
+                         &address_list, NULL, NULL, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ("192.168.1.8", NetAddressToString(address_list.head()));
+  EXPECT_EQ(77, address_list.GetPort());
+
+  // Remap "*.org" to "proxy:99".
+  EXPECT_TRUE(resolver->AddRuleFromString("Map *.org proxy:99"));
+
+  // Try resolving "chromium.org:61". Should be remapped to "proxy:99".
+  rv = resolver->Resolve(HostResolver::RequestInfo("chromium.org", 61),
+                         &address_list, NULL, NULL, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ("192.168.1.11", NetAddressToString(address_list.head()));
+  EXPECT_EQ(99, address_list.GetPort());
+}
+
+// Tests that exclusions are respected.
+TEST(MappedHostResolverTest, Exclusion) {
+  // Create a mock host resolver, with specific hostname to IP mappings.
+  scoped_refptr<MockHostResolver> resolver_impl = new MockHostResolver();
+  resolver_impl->rules()->AddRule("baz", "192.168.1.5");
+  resolver_impl->rules()->AddRule("www.google.com", "192.168.1.3");
+
+  // Create a remapped resolver that uses |resolver_impl|.
+  scoped_refptr<MappedHostResolver> resolver =
+      new MappedHostResolver(resolver_impl);
+
+  int rv;
+  AddressList address_list;
+
+  // Remap "*.com" to "baz".
+  EXPECT_TRUE(resolver->AddRuleFromString("map *.com baz"));
+
+  // Add an exclusion for "*.google.com".
+  EXPECT_TRUE(resolver->AddRuleFromString("EXCLUDE *.google.com"));
+
+  // Try resolving "www.google.com". Should not be remapped due to exclusion).
+  rv = resolver->Resolve(HostResolver::RequestInfo("www.google.com", 80),
+                         &address_list, NULL, NULL, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ("192.168.1.3", NetAddressToString(address_list.head()));
+  EXPECT_EQ(80, address_list.GetPort());
+
+  // Try resolving "chrome.com:80". Should be remapped to "baz:80".
+  rv = resolver->Resolve(HostResolver::RequestInfo("chrome.com", 80),
+                         &address_list, NULL, NULL, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ("192.168.1.5", NetAddressToString(address_list.head()));
+  EXPECT_EQ(80, address_list.GetPort());
+}
+
+TEST(MappedHostResolverTest, SetRulesFromString) {
+  // Create a mock host resolver, with specific hostname to IP mappings.
+  scoped_refptr<MockHostResolver> resolver_impl = new MockHostResolver();
+  resolver_impl->rules()->AddRule("baz", "192.168.1.7");
+  resolver_impl->rules()->AddRule("bar", "192.168.1.9");
+
+  // Create a remapped resolver that uses |resolver_impl|.
+  scoped_refptr<MappedHostResolver> resolver =
+      new MappedHostResolver(resolver_impl);
+
+  int rv;
+  AddressList address_list;
+
+  // Remap "*.com" to "baz", and *.net to "bar:60".
+  resolver->SetRulesFromString("map *.com baz , map *.net bar:60");
+
+  // Try resolving "www.google.com". Should be remapped to "baz".
+  rv = resolver->Resolve(HostResolver::RequestInfo("www.google.com", 80),
+                         &address_list, NULL, NULL, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ("192.168.1.7", NetAddressToString(address_list.head()));
+  EXPECT_EQ(80, address_list.GetPort());
+
+  // Try resolving "chrome.net:80". Should be remapped to "bar:60".
+  rv = resolver->Resolve(HostResolver::RequestInfo("chrome.net", 80),
+                         &address_list, NULL, NULL, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ("192.168.1.9", NetAddressToString(address_list.head()));
+  EXPECT_EQ(60, address_list.GetPort());
+}
+
+// Parsing bad rules should silently discard the rule (and never crash).
+TEST(MappedHostResolverTest, ParseInvalidRules) {
+  scoped_refptr<MappedHostResolver> resolver = new MappedHostResolver(NULL);
+
+  EXPECT_FALSE(resolver->AddRuleFromString("xyz"));
+  EXPECT_FALSE(resolver->AddRuleFromString(""));
+  EXPECT_FALSE(resolver->AddRuleFromString(" "));
+  EXPECT_FALSE(resolver->AddRuleFromString("EXCLUDE"));
+  EXPECT_FALSE(resolver->AddRuleFromString("EXCLUDE foo bar"));
+  EXPECT_FALSE(resolver->AddRuleFromString("INCLUDE"));
+  EXPECT_FALSE(resolver->AddRuleFromString("INCLUDE x"));
+  EXPECT_FALSE(resolver->AddRuleFromString("INCLUDE x :10"));
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/base/mime_sniffer.cc b/net/base/mime_sniffer.cc
index 8e063cb..1961107 100644
--- a/net/base/mime_sniffer.cc
+++ b/net/base/mime_sniffer.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -105,9 +105,6 @@
 
 namespace net {
 
-// We aren't interested in looking at more than 512 bytes of content
-static const size_t kMaxBytesToSniff = 512;
-
 // The number of content bytes we need to use all our magic numbers.  Feel free
 // to increase this number if you add a longer magic number.
 static const size_t kBytesRequiredForMagic = 42;
@@ -224,7 +221,7 @@
   const size_t len = magic_entry->magic_len;
 
   // Keep kBytesRequiredForMagic honest.
-  DCHECK(len <= kBytesRequiredForMagic);
+  DCHECK_LE(len, kBytesRequiredForMagic);
 
   // To compare with magic strings, we need to compute strlen(content), but
   // content might not actually have a null terminator.  In that case, we
@@ -263,8 +260,29 @@
   return false;
 }
 
-static bool SniffForHTML(const char* content, size_t size,
+// Truncates |size| to |max_size| and returns true if |size| is at least
+// |max_size|.
+static bool TruncateSize(const size_t max_size, size_t* size) {
+  // Keep kMaxBytesToSniff honest.
+  DCHECK_LE(static_cast<int>(max_size), kMaxBytesToSniff);
+
+  if (*size >= max_size) {
+    *size = max_size;
+    return true;
+  }
+  return false;
+}
+
+// Returns true and sets result if the content appears to be HTML.
+// Clears have_enough_content if more data could possibly change the result.
+static bool SniffForHTML(const char* content,
+                         size_t size,
+                         bool* have_enough_content,
                          std::string* result) {
+  // For HTML, we are willing to consider up to 512 bytes. This may be overly
+  // conservative as IE only considers 256.
+  *have_enough_content &= TruncateSize(512, &size);
+
   // We adopt a strategy similar to that used by Mozilla to sniff HTML tags,
   // but with some modifications to better match the HTML5 spec.
   const char* const end = content + size;
@@ -282,8 +300,14 @@
                               counter.get(), result);
 }
 
-static bool SniffForMagicNumbers(const char* content, size_t size,
+// Returns true and sets result if the content matches any of kMagicNumbers.
+// Clears have_enough_content if more data could possibly change the result.
+static bool SniffForMagicNumbers(const char* content,
+                                 size_t size,
+                                 bool* have_enough_content,
                                  std::string* result) {
+  *have_enough_content &= TruncateSize(kBytesRequiredForMagic, &size);
+
   // Check our big table of Magic Numbers
   static scoped_refptr<Histogram> counter =
       UMASnifferHistogramGet("mime_sniffer.kMagicNumbers2",
@@ -305,18 +329,22 @@
   MAGIC_STRING("application/rss+xml", "<rss")  // UTF-8
 };
 
-// Sniff an XML document to judge whether it contains XHTML or a feed.
-// Returns true if it has seen enough content to make a definitive decision.
+// Returns true and sets result if the content appears to contain XHTML or a
+// feed.
+// Clears have_enough_content if more data could possibly change the result.
+//
 // TODO(evanm): this is similar but more conservative than what Safari does,
 // while HTML5 has a different recommendation -- what should we do?
 // TODO(evanm): this is incorrect for documents whose encoding isn't a superset
 // of ASCII -- do we care?
-static bool SniffXML(const char* content, size_t size, std::string* result) {
-  // We allow at most kFirstTagBytes bytes of content before we expect the
-  // opening tag.
-  const size_t kFeedAllowedHeaderBytes = 300;
-  const char* const end = content + std::min(size, kFeedAllowedHeaderBytes);
+static bool SniffXML(const char* content,
+                     size_t size,
+                     bool* have_enough_content,
+                     std::string* result) {
+  // We allow at most 300 bytes of content before we expect the opening tag.
+  *have_enough_content &= TruncateSize(300, &size);
   const char* pos = content;
+  const char* const end = content + size;
 
   // This loop iterates through tag-looking offsets in the file.
   // We want to skip XML processing instructions (of the form "<?xml ...")
@@ -389,7 +417,22 @@
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 0xF0 - 0xFF
 };
 
-static bool LooksBinary(const char* content, size_t size) {
+// Returns true and sets result to "application/octet-stream" if the content
+// appears to be binary data. Otherwise, returns false and sets "text/plain".
+// Clears have_enough_content if more data could possibly change the result.
+static bool SniffBinary(const char* content,
+                        size_t size,
+                        bool* have_enough_content,
+                        std::string* result) {
+  // There is no concensus about exactly how to sniff for binary content.
+  // * IE 7: Don't sniff for binary looking bytes, but trust the file extension.
+  // * Firefox 3.5: Sniff first 4096 bytes for a binary looking byte.
+  // Here, we side with FF, but with a smaller buffer. This size was chosen
+  // because it is small enough to comfortably fit into a single packet (after
+  // allowing for headers) and yet large enough to account for binary formats
+  // that have a significant amount of ASCII at the beginning (crbug.com/15314).
+  const bool is_truncated = TruncateSize(kMaxBytesToSniff, &size);
+
   // First, we look for a BOM.
   static scoped_refptr<Histogram> counter =
       UMASnifferHistogramGet("mime_sniffer.kByteOrderMark2",
@@ -399,17 +442,24 @@
                            kByteOrderMark, arraysize(kByteOrderMark),
                            counter.get(), &unused)) {
     // If there is BOM, we think the buffer is not binary.
+    result->assign("text/plain");
     return false;
   }
 
   // Next we look to see if any of the bytes "look binary."
   for (size_t i = 0; i < size; ++i) {
     // If we a see a binary-looking byte, we think the content is binary.
-    if (kByteLooksBinary[static_cast<unsigned char>(content[i])])
+    if (kByteLooksBinary[static_cast<unsigned char>(content[i])]) {
+      result->assign("application/octet-stream");
       return true;
+    }
   }
 
-  // No evidence either way, default to non-binary.
+  // No evidence either way. Default to non-binary and, if truncated, clear
+  // have_enough_content because there could be a binary looking byte in the
+  // truncated data.
+  *have_enough_content &= is_truncated;
+  result->assign("text/plain");
   return false;
 }
 
@@ -443,9 +493,15 @@
   return false;
 }
 
-// Sniff a crx (chrome extension) file.
-static bool SniffCRX(const char* content, size_t content_size, const GURL& url,
-                     const std::string& type_hint, std::string* result) {
+// Returns true and sets result if the content appears to be a crx (chrome
+// extension) file.
+// Clears have_enough_content if more data could possibly change the result.
+static bool SniffCRX(const char* content,
+                     size_t size,
+                     const GURL& url,
+                     const std::string& type_hint,
+                     bool* have_enough_content,
+                     std::string* result) {
   static scoped_refptr<Histogram> counter =
       UMASnifferHistogramGet("mime_sniffer.kSniffCRX", 3);
 
@@ -456,13 +512,14 @@
   //
   // TODO(aa): If we ever have another magic number, we'll want to pass a
   // histogram into CheckForMagicNumbers(), below, to see which one matched.
-  const struct MagicNumber kCRXMagicNumbers[] = {
+  static const struct MagicNumber kCRXMagicNumbers[] = {
     MAGIC_NUMBER("application/x-chrome-extension", "Cr24\x02\x00\x00\x00")
   };
 
   // Only consider files that have the extension ".crx".
-  const char kCRXExtension[] = ".crx";
-  const int kExtensionLength = arraysize(kCRXExtension) - 1;  // ignore null
+  static const char kCRXExtension[] = ".crx";
+  // Ignore null by subtracting 1.
+  static const int kExtensionLength = arraysize(kCRXExtension) - 1;
   if (url.path().rfind(kCRXExtension, std::string::npos, kExtensionLength) ==
       url.path().size() - kExtensionLength) {
     counter->Add(1);
@@ -470,7 +527,8 @@
     return false;
   }
 
-  if (CheckForMagicNumbers(content, content_size,
+  *have_enough_content &= TruncateSize(kBytesRequiredForMagic, &size);
+  if (CheckForMagicNumbers(content, size,
                            kCRXMagicNumbers, arraysize(kCRXMagicNumbers),
                            NULL, result)) {
     counter->Add(2);
@@ -488,7 +546,8 @@
   bool sniffable_scheme = url.is_empty() ||
                           url.SchemeIs("http") ||
                           url.SchemeIs("https") ||
-                          url.SchemeIs("ftp");
+                          url.SchemeIs("ftp") ||
+                          url.SchemeIsFile();
   if (!sniffable_scheme) {
     should_sniff_counter->Add(1);
     return false;
@@ -534,17 +593,14 @@
   DCHECK(content);
   DCHECK(result);
 
+  // By default, we assume we have enough content.
+  // Each sniff routine may unset this if it wasn't provided enough content.
+  bool have_enough_content = true;
+
   // By default, we'll return the type hint.
+  // Each sniff routine may modify this if it has a better guess..
   result->assign(type_hint);
 
-  // Flag for tracking whether our decision was limited by content_size.  We
-  // probably have enough content if we can use all our magic numbers.
-  const bool have_enough_content = content_size >= kBytesRequiredForMagic;
-
-  // We have an upper limit on the number of bytes we will consider.
-  if (content_size > kMaxBytesToSniff)
-    content_size = kMaxBytesToSniff;
-
   // Cache information about the type_hint
   const bool hint_is_unknown_mime_type = IsUnknownMimeType(type_hint);
 
@@ -553,34 +609,41 @@
     // We're only willing to sniff HTML if the server has not supplied a mime
     // type, or if the type it did supply indicates that it doesn't know what
     // the type should be.
-    if (SniffForHTML(content, content_size, result))
+    if (SniffForHTML(content, content_size, &have_enough_content, result))
       return true;  // We succeeded in sniffing HTML.  No more content needed.
   }
 
-  // We'll reuse this information later
+  // We're only willing to sniff for binary in 3 cases:
+  // 1. The server has not supplied a mime type.
+  // 2. The type it did supply indicates that it doesn't know what the type
+  //    should be.
+  // 3. The type is "text/plain" which is the default on some web servers and
+  //    could be indicative of a mis-configuration that we shield the user from.
   const bool hint_is_text_plain = (type_hint == "text/plain");
-  const bool looks_binary = LooksBinary(content, content_size);
-
-  if (hint_is_text_plain && !looks_binary) {
-    // The server said the content was text/plain and we don't really have any
-    // evidence otherwise.
-    result->assign("text/plain");
-    return have_enough_content;
+  if (hint_is_unknown_mime_type || hint_is_text_plain) {
+    if (!SniffBinary(content, content_size, &have_enough_content, result)) {
+      // If the server said the content was text/plain and it doesn't appear
+      // to be binary, then we trust it.
+      if (hint_is_text_plain) {
+        return have_enough_content;
+      }
+    }
   }
 
   // If we have plain XML, sniff XML subtypes.
   if (type_hint == "text/xml" || type_hint == "application/xml") {
     // We're not interested in sniffing these types for images and the like.
-    // Instead, we're looking explicitly for a feed.  If we don't find one we're
-    // done and return early.
-    if (SniffXML(content, content_size, result))
+    // Instead, we're looking explicitly for a feed.  If we don't find one
+    // we're done and return early.
+    if (SniffXML(content, content_size, &have_enough_content, result))
       return true;
-    return content_size >= kMaxBytesToSniff;
+    return have_enough_content;
   }
 
   // CRX files (chrome extensions) have a special sniffing algorithm. It is
   // tighter than the others because we don't have to match legacy behavior.
-  if (SniffCRX(content, content_size, url, type_hint, result))
+  if (SniffCRX(content, content_size, url, type_hint,
+               &have_enough_content, result))
     return true;
 
   // We're not interested in sniffing for magic numbers when the type_hint
@@ -590,21 +653,10 @@
 
   // Now we look in our large table of magic numbers to see if we can find
   // anything that matches the content.
-  if (SniffForMagicNumbers(content, content_size, result))
+  if (SniffForMagicNumbers(content, content_size,
+                           &have_enough_content, result))
     return true;  // We've matched a magic number.  No more content needed.
 
-  // Having failed thus far, we're willing to override unknown mime types and
-  // text/plain.
-  if (hint_is_unknown_mime_type || hint_is_text_plain) {
-    if (looks_binary)
-      result->assign("application/octet-stream");
-    else
-      result->assign("text/plain");
-    // We could change our mind if a binary-looking byte appears later in
-    // the content, so we only have enough content if we have the max.
-    return content_size >= kMaxBytesToSniff;
-  }
-
   return have_enough_content;
 }
 
diff --git a/net/base/mime_sniffer.h b/net/base/mime_sniffer.h
index 6fd7014..d0c4e78 100644
--- a/net/base/mime_sniffer.h
+++ b/net/base/mime_sniffer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -11,6 +11,12 @@
 
 namespace net {
 
+// The maximum number of bytes used by any internal mime sniffing routine. May
+// be useful for callers to determine an efficient buffer size to pass to
+// |SniffMimeType|.
+// This must be updated if any internal sniffing routine needs more bytes.
+const int kMaxBytesToSniff = 1024;
+
 // Examine the URL and the mime_type and decide whether we should sniff a
 // replacement mime type from the content.
 //
diff --git a/net/base/mime_sniffer_unittest.cc b/net/base/mime_sniffer_unittest.cc
index 56dfd51..d70cb23 100644
--- a/net/base/mime_sniffer_unittest.cc
+++ b/net/base/mime_sniffer_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -149,6 +149,9 @@
     { "Cr24\x02\x00\x00\x01", sizeof("Cr24\x02\x00\x00\x01")-1,
       "http://www.example.com/foo.crx?monkey",
       "", "application/octet-stream" },
+    { "PADDING_Cr24\x02\x00\x00\x00", sizeof("PADDING_Cr24\x02\x00\x00\x00")-1,
+      "http://www.example.com/foo.crx?monkey",
+      "", "application/octet-stream" },
   };
 
   TestArray(tests, arraysize(tests));
@@ -359,16 +362,33 @@
 
 }
 
-// Test content which is >= 512 bytes, and includes no open angle bracket.
+// Test content which is >= 1024 bytes, and includes no open angle bracket.
 // http://code.google.com/p/chromium/issues/detail?id=3521
 TEST(MimeSnifferTest, XMLTestLargeNoAngledBracket) {
-  // Make a large input, with 600 bytes of "x".
+  // Make a large input, with 1024 bytes of "x".
   std::string content;
-  content.resize(600);
+  content.resize(1024);
   std::fill(content.begin(), content.end(), 'x');
 
-  // content.size() >= kMaxBytesToSniff (512) so the sniff is unambiguous.
+  // content.size() >= 1024 so the sniff is unambiguous.
   std::string mime_type;
   EXPECT_TRUE(net::SniffMimeType(content.data(), content.size(), GURL(),
                                  "text/xml", &mime_type));
+  EXPECT_EQ("text/xml", mime_type);
+}
+
+// Test content which is >= 1024 bytes, and includes a binary looking byte.
+// http://code.google.com/p/chromium/issues/detail?id=15314
+TEST(MimeSnifferTest, LooksBinary) {
+  // Make a large input, with 1024 bytes of "x" and 1 byte of 0x01.
+  std::string content;
+  content.resize(1024);
+  std::fill(content.begin(), content.end(), 'x');
+  content[1000] = 0x01;
+
+  // content.size() >= 1024 so the sniff is unambiguous.
+  std::string mime_type;
+  EXPECT_TRUE(net::SniffMimeType(content.data(), content.size(), GURL(),
+                                 "text/plain", &mime_type));
+  EXPECT_EQ("application/octet-stream", mime_type);
 }
diff --git a/net/base/mime_util.cc b/net/base/mime_util.cc
index 0bccb0a..5863b68 100644
--- a/net/base/mime_util.cc
+++ b/net/base/mime_util.cc
@@ -1,7 +1,8 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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 <map>
 #include <string>
 
 #include "net/base/mime_util.h"
@@ -11,6 +12,7 @@
 #include "base/logging.h"
 #include "base/singleton.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 
 using std::string;
 
@@ -40,7 +42,12 @@
   bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs) const;
 
   void ParseCodecString(const std::string& codecs,
-                        std::vector<std::string>* codecs_out);
+                        std::vector<std::string>* codecs_out,
+                        bool strip);
+
+  bool IsStrictMediaMimeType(const std::string& mime_type) const;
+  bool IsSupportedStrictMediaMimeType(const std::string& mime_type,
+      const std::vector<std::string>& codecs) const;
 
  private:
   friend struct DefaultSingletonTraits<MimeUtil>;
@@ -58,6 +65,9 @@
   MimeMappings javascript_map_;
   MimeMappings view_source_map_;
   MimeMappings codecs_map_;
+
+  typedef std::map<std::string, base::hash_set<std::string> > StrictMappings;
+  StrictMappings strict_format_map_;
 };  // class MimeUtil
 
 struct MimeInfo {
@@ -77,6 +87,8 @@
   { "audio/mp3", "mp3" },
   { "video/ogg", "ogv,ogm" },
   { "audio/ogg", "ogg,oga" },
+  { "video/webm", "webm" },
+  { "audio/webm", "webm" },
   { "application/xhtml+xml", "xhtml,xht" },
   { "application/x-chrome-extension", "crx" }
 };
@@ -126,6 +138,11 @@
 
 bool MimeUtil::GetMimeTypeFromExtension(const FilePath::StringType& ext,
                                         string* result) const {
+  // Avoids crash when unable to handle a long file path. See crbug.com/48733.
+  const unsigned kMaxFilePathSize = 65536;
+  if (ext.length() > kMaxFilePathSize)
+    return false;
+
   // We implement the same algorithm as Mozilla for mapping a file extension to
   // a mime type.  That is, we first check a hard-coded list (that cannot be
   // overridden), and then if not found there, we defer to the system registry.
@@ -187,8 +204,10 @@
   "video/ogg",
   "audio/ogg",
   "application/ogg",
+  "video/webm",
+  "audio/webm",
 
-#if defined(GOOGLE_CHROME_BUILD)
+#if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS)
   // MPEG-4.
   "video/mp4",
   "video/x-m4v",
@@ -207,16 +226,18 @@
 // Refer to http://wiki.whatwg.org/wiki/Video_type_parameters#Browser_Support
 // for more information.
 static const char* const supported_media_codecs[] = {
-#if defined(GOOGLE_CHROME_BUILD)
+#if defined(GOOGLE_CHROME_BUILD) || defined(USE_PROPRIETARY_CODECS)
   "avc1",
   "mp4a",
 #endif
   "theora",
   "vorbis",
+  "vp8"
 };
 
 // Note: does not include javascript types list (see supported_javascript_types)
 static const char* const supported_non_image_types[] = {
+  "text/cache-manifest",
   "text/html",
   "text/xml",
   "text/xsl",
@@ -237,7 +258,11 @@
   "application/json",
   "application/x-x509-user-cert",
   "multipart/x-mixed-replace"
+  // Note: ADDING a new type here will probably render it AS HTML. This can
+  // result in cross site scripting.
 };
+COMPILE_ASSERT(arraysize(supported_non_image_types) == 16,
+               supported_non_images_types_must_equal_16);
 
 //  Mozilla 1.8 and WinIE 7 both accept text/javascript and text/ecmascript.
 //  Mozilla 1.8 accepts application/javascript, application/ecmascript, and
@@ -271,6 +296,16 @@
   "image/svg+xml"
 };
 
+struct MediaFormatStrict {
+  const char* mime_type;
+  const char* codecs_list;
+};
+
+static const MediaFormatStrict format_codec_mappings[] = {
+  { "video/webm", "vorbis,vp8,vp8.0" },
+  { "audio/webm", "vorbis" }
+};
+
 void MimeUtil::InitializeMimeTypeMaps() {
   for (size_t i = 0; i < arraysize(supported_image_types); ++i)
     image_map_.insert(supported_image_types[i]);
@@ -295,6 +330,19 @@
 
   for (size_t i = 0; i < arraysize(supported_media_codecs); ++i)
     codecs_map_.insert(supported_media_codecs[i]);
+
+  // Initialize the strict supported media types.
+  for (size_t i = 0; i < arraysize(format_codec_mappings); ++i) {
+    std::vector<std::string> mime_type_codecs;
+    ParseCodecString(format_codec_mappings[i].codecs_list,
+                     &mime_type_codecs,
+                     false);
+
+    MimeMappings codecs;
+    for (size_t j = 0; j < mime_type_codecs.size(); ++j)
+      codecs.insert(mime_type_codecs[j]);
+    strict_format_map_[format_codec_mappings[i].mime_type] = codecs;
+  }
 }
 
 bool MimeUtil::IsSupportedImageMimeType(const char* mime_type) const {
@@ -372,12 +420,16 @@
 }
 
 void MimeUtil::ParseCodecString(const std::string& codecs,
-                                std::vector<std::string>* codecs_out) {
+                                std::vector<std::string>* codecs_out,
+                                bool strip) {
   std::string no_quote_codecs;
   TrimString(codecs, "\"", &no_quote_codecs);
   SplitString(no_quote_codecs, ',', codecs_out);
 
-  // Truncate each string at the '.'
+  if (!strip)
+    return;
+
+  // Strip everything past the first '.'
   for (std::vector<std::string>::iterator it = codecs_out->begin();
        it != codecs_out->end();
        ++it) {
@@ -387,6 +439,28 @@
   }
 }
 
+bool MimeUtil::IsStrictMediaMimeType(const std::string& mime_type) const {
+  if (strict_format_map_.find(mime_type) == strict_format_map_.end())
+    return false;
+  return true;
+}
+
+bool MimeUtil::IsSupportedStrictMediaMimeType(const std::string& mime_type,
+    const std::vector<std::string>& codecs) const {
+  StrictMappings::const_iterator it = strict_format_map_.find(mime_type);
+
+  if (it == strict_format_map_.end())
+    return false;
+
+  const MimeMappings strict_codecs_map = it->second;
+  for (size_t i = 0; i < codecs.size(); ++i) {
+    if (strict_codecs_map.find(codecs[i]) == strict_codecs_map.end()) {
+      return false;
+    }
+  }
+  return true;
+}
+
 //----------------------------------------------------------------------------
 // Wrappers for the singleton
 //----------------------------------------------------------------------------
@@ -442,9 +516,19 @@
   return GetMimeUtil()->AreSupportedMediaCodecs(codecs);
 }
 
+bool IsStrictMediaMimeType(const std::string& mime_type) {
+  return GetMimeUtil()->IsStrictMediaMimeType(mime_type);
+}
+
+bool IsSupportedStrictMediaMimeType(const std::string& mime_type,
+                                    const std::vector<std::string>& codecs) {
+  return GetMimeUtil()->IsSupportedStrictMediaMimeType(mime_type, codecs);
+}
+
 void ParseCodecString(const std::string& codecs,
-                      std::vector<std::string>* codecs_out) {
-  GetMimeUtil()->ParseCodecString(codecs, codecs_out);
+                      std::vector<std::string>* codecs_out,
+                      const bool strip) {
+  GetMimeUtil()->ParseCodecString(codecs, codecs_out, strip);
 }
 
 }  // namespace net
diff --git a/net/base/mime_util.h b/net/base/mime_util.h
index 1846087..eebc2e8 100644
--- a/net/base/mime_util.h
+++ b/net/base/mime_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -50,11 +50,25 @@
 bool AreSupportedMediaCodecs(const std::vector<std::string>& codecs);
 
 // Parses a codec string, populating |codecs_out| with the prefix of each codec
-// in the string |codecs_in|. For example, passed "aaa.b.c,dd.eee", |codecs_out|
-// will contain {"aaa", "dd"}.
+// in the string |codecs_in|. For example, passed "aaa.b.c,dd.eee", if
+// |strip| == true |codecs_out| will contain {"aaa", "dd"}, if |strip| == false
+// |codecs_out| will contain {"aaa.b.c", "dd.eee"}.
 // See http://www.ietf.org/rfc/rfc4281.txt.
 void ParseCodecString(const std::string& codecs,
-                      std::vector<std::string>* codecs_out);
+                      std::vector<std::string>* codecs_out,
+                      bool strip);
+
+// Check to see if a particular MIME type is in our list which only supports a
+// certain subset of codecs.
+bool IsStrictMediaMimeType(const std::string& mime_type);
+
+// Check to see if a particular MIME type is in our list which only supports a
+// certain subset of codecs. Returns true if and only if all codecs are
+// supported for that specific MIME type, false otherwise. If this returns
+// false you will still need to check if the media MIME tpyes and codecs are
+// supported.
+bool IsSupportedStrictMediaMimeType(const std::string& mime_type,
+    const std::vector<std::string>& codecs);
 
 }  // namespace net
 
diff --git a/net/base/mime_util_unittest.cc b/net/base/mime_util_unittest.cc
index 0300da8..51b7f3a 100644
--- a/net/base/mime_util_unittest.cc
+++ b/net/base/mime_util_unittest.cc
@@ -118,10 +118,17 @@
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
     std::vector<std::string> codecs_out;
-    net::ParseCodecString(tests[i].original, &codecs_out);
+    net::ParseCodecString(tests[i].original, &codecs_out, true);
     EXPECT_EQ(tests[i].expected_size, codecs_out.size());
     for (size_t j = 0; j < tests[i].expected_size; ++j) {
       EXPECT_EQ(tests[i].results[j], codecs_out[j]);
     }
   }
+
+  // Test without stripping the codec type.
+  std::vector<std::string> codecs_out;
+  net::ParseCodecString("avc1.42E01E, mp4a.40.2", &codecs_out, false);
+  EXPECT_EQ(2u, codecs_out.size());
+  EXPECT_STREQ("avc1.42E01E", codecs_out[0].c_str());
+  EXPECT_STREQ("mp4a.40.2", codecs_out[1].c_str());
 }
diff --git a/net/base/mock_host_resolver.cc b/net/base/mock_host_resolver.cc
index 2185182..8d1fd89 100644
--- a/net/base/mock_host_resolver.cc
+++ b/net/base/mock_host_resolver.cc
@@ -7,31 +7,35 @@
 #include "base/string_util.h"
 #include "base/platform_thread.h"
 #include "base/ref_counted.h"
-#include "googleurl/src/url_canon_ip.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_util.h"
 
 namespace net {
 
 namespace {
-// Fills |addrlist| with a socket address for |host| which should be an
-// IPv6 literal. Returns OK on success.
-int ResolveIPV6LiteralUsingGURL(const std::string& host,
-                                AddressList* addrlist) {
-  // GURL expects the hostname to be surrounded with brackets.
-  std::string host_brackets = "[" + host + "]";
-  url_parse::Component host_comp(0, host_brackets.size());
 
-  // Try parsing the hostname as an IPv6 literal.
-  unsigned char ipv6_addr[16];  // 128 bits.
-  bool ok = url_canon::IPv6AddressToNumber(host_brackets.data(),
-                                           host_comp,
-                                           ipv6_addr);
-  if (!ok) {
-    LOG(WARNING) << "Not an IPv6 literal: " << host;
+// Fills |*addrlist| with a socket address for |host| which should be an
+// IPv4 or IPv6 literal without enclosing brackets. If |canonical_name| is
+// non-empty it is used as the DNS canonical name for the host. Returns OK on
+// success, ERR_UNEXPECTED otherwise.
+int CreateIPAddress(const std::string& host,
+                    const std::string& canonical_name,
+                    AddressList* addrlist) {
+  IPAddressNumber ip_number;
+  if (!ParseIPLiteralToNumber(host, &ip_number)) {
+    LOG(WARNING) << "Not a supported IP literal: " << host;
     return ERR_UNEXPECTED;
   }
 
-  *addrlist = AddressList::CreateIPv6Address(ipv6_addr);
+  if (ip_number.size() == 4) {
+    *addrlist = AddressList::CreateIPv4Address(&ip_number[0], canonical_name);
+  } else if (ip_number.size() == 16) {
+    *addrlist = AddressList::CreateIPv6Address(&ip_number[0], canonical_name);
+  } else {
+    NOTREACHED();
+    return ERR_UNEXPECTED;
+  }
+
   return OK;
 }
 
@@ -46,12 +50,12 @@
                                   AddressList* addresses,
                                   CompletionCallback* callback,
                                   RequestHandle* out_req,
-                                  LoadLog* load_log) {
+                                  const BoundNetLog& net_log) {
   if (synchronous_mode_) {
     callback = NULL;
     out_req = NULL;
   }
-  return impl_->Resolve(info, addresses, callback, out_req, load_log);
+  return impl_->Resolve(info, addresses, callback, out_req, net_log);
 }
 
 void MockHostResolverBase::CancelRequest(RequestHandle req) {
@@ -66,10 +70,6 @@
   impl_->RemoveObserver(observer);
 }
 
-void MockHostResolverBase::Shutdown() {
-  impl_->Shutdown();
-}
-
 void MockHostResolverBase::Reset(HostResolverProc* interceptor) {
   synchronous_mode_ = false;
 
@@ -98,7 +98,7 @@
         base::TimeDelta::FromSeconds(0));
   }
 
-  impl_ = new HostResolverImpl(proc, cache, NULL, 50u);
+  impl_ = new HostResolverImpl(proc, cache, 50u);
 }
 
 //-----------------------------------------------------------------------------
@@ -107,24 +107,30 @@
   enum ResolverType {
     kResolverTypeFail,
     kResolverTypeSystem,
-    kResolverTypeIPV6Literal,
+    kResolverTypeIPLiteral,
   };
 
   ResolverType resolver_type;
   std::string host_pattern;
   AddressFamily address_family;
+  HostResolverFlags host_resolver_flags;
   std::string replacement;
+  std::string canonical_name;
   int latency_ms;  // In milliseconds.
 
   Rule(ResolverType resolver_type,
        const std::string& host_pattern,
        AddressFamily address_family,
+       HostResolverFlags host_resolver_flags,
        const std::string& replacement,
+       const std::string& canonical_name,
        int latency_ms)
       : resolver_type(resolver_type),
         host_pattern(host_pattern),
         address_family(address_family),
+        host_resolver_flags(host_resolver_flags),
         replacement(replacement),
+        canonical_name(canonical_name),
         latency_ms(latency_ms) {}
 };
 
@@ -147,14 +153,21 @@
     const std::string& replacement) {
   DCHECK(!replacement.empty());
   Rule rule(Rule::kResolverTypeSystem, host_pattern,
-            address_family, replacement, 0);
+            address_family, 0, replacement, "", 0);
   rules_.push_back(rule);
 }
 
-void RuleBasedHostResolverProc::AddIPv6Rule(const std::string& host_pattern,
-                                            const std::string& ipv6_literal) {
-  Rule rule(Rule::kResolverTypeIPV6Literal, host_pattern,
-            ADDRESS_FAMILY_UNSPECIFIED, ipv6_literal, 0);
+void RuleBasedHostResolverProc::AddIPLiteralRule(
+    const std::string& host_pattern,
+    const std::string& ip_literal,
+    const std::string& canonical_name) {
+  Rule rule(Rule::kResolverTypeIPLiteral,
+            host_pattern,
+            ADDRESS_FAMILY_UNSPECIFIED,
+            canonical_name.empty() ? 0 : HOST_RESOLVER_CANONNAME,
+            ip_literal,
+            canonical_name,
+            0);
   rules_.push_back(rule);
 }
 
@@ -164,34 +177,42 @@
     int latency_ms) {
   DCHECK(!replacement.empty());
   Rule rule(Rule::kResolverTypeSystem, host_pattern,
-            ADDRESS_FAMILY_UNSPECIFIED, replacement, latency_ms);
+            ADDRESS_FAMILY_UNSPECIFIED, 0, replacement, "", latency_ms);
   rules_.push_back(rule);
 }
 
 void RuleBasedHostResolverProc::AllowDirectLookup(
     const std::string& host_pattern) {
   Rule rule(Rule::kResolverTypeSystem, host_pattern,
-            ADDRESS_FAMILY_UNSPECIFIED, "", 0);
+            ADDRESS_FAMILY_UNSPECIFIED, 0, "", "", 0);
   rules_.push_back(rule);
 }
 
 void RuleBasedHostResolverProc::AddSimulatedFailure(
     const std::string& host_pattern) {
   Rule rule(Rule::kResolverTypeFail, host_pattern,
-            ADDRESS_FAMILY_UNSPECIFIED, "", 0);
+            ADDRESS_FAMILY_UNSPECIFIED, 0, "", "", 0);
   rules_.push_back(rule);
 }
 
 int RuleBasedHostResolverProc::Resolve(const std::string& host,
                                        AddressFamily address_family,
-                                       AddressList* addrlist) {
+                                       HostResolverFlags host_resolver_flags,
+                                       AddressList* addrlist,
+                                       int* os_error) {
   RuleList::iterator r;
   for (r = rules_.begin(); r != rules_.end(); ++r) {
     bool matches_address_family =
         r->address_family == ADDRESS_FAMILY_UNSPECIFIED ||
         r->address_family == address_family;
-
-    if (matches_address_family && MatchPatternASCII(host, r->host_pattern)) {
+    // Flags match if all of the bitflags in host_resolver_flags are enabled
+    // in the rule's host_resolver_flags. However, the rule may have additional
+    // flags specified, in which case the flags should still be considered a
+    // match.
+    bool matches_flags = (r->host_resolver_flags & host_resolver_flags) ==
+        host_resolver_flags;
+    if (matches_flags && matches_address_family &&
+        MatchPatternASCII(host, r->host_pattern)) {
       if (r->latency_ms != 0)
         PlatformThread::Sleep(r->latency_ms);
 
@@ -206,16 +227,18 @@
         case Rule::kResolverTypeSystem:
           return SystemHostResolverProc(effective_host,
                                         address_family,
-                                        addrlist);
-        case Rule::kResolverTypeIPV6Literal:
-          return ResolveIPV6LiteralUsingGURL(effective_host, addrlist);
+                                        host_resolver_flags,
+                                        addrlist, os_error);
+        case Rule::kResolverTypeIPLiteral:
+          return CreateIPAddress(effective_host, r->canonical_name, addrlist);
         default:
           NOTREACHED();
           return ERR_UNEXPECTED;
       }
     }
   }
-  return ResolveUsingPrevious(host, address_family, addrlist);
+  return ResolveUsingPrevious(host, address_family,
+                              host_resolver_flags, addrlist, os_error);
 }
 
 //-----------------------------------------------------------------------------
@@ -228,7 +251,7 @@
 ScopedDefaultHostResolverProc::~ScopedDefaultHostResolverProc() {
   HostResolverProc* old_proc = HostResolverProc::SetDefault(previous_proc_);
   // The lifetimes of multiple instances must be nested.
-  CHECK(old_proc == current_proc_);
+  CHECK_EQ(old_proc, current_proc_);
 }
 
 void ScopedDefaultHostResolverProc::Init(HostResolverProc* proc) {
diff --git a/net/base/mock_host_resolver.h b/net/base/mock_host_resolver.h
index 8c843b6..ee1e55a 100644
--- a/net/base/mock_host_resolver.h
+++ b/net/base/mock_host_resolver.h
@@ -43,12 +43,10 @@
                       AddressList* addresses,
                       CompletionCallback* callback,
                       RequestHandle* out_req,
-                      LoadLog* load_log);
+                      const BoundNetLog& net_log);
   virtual void CancelRequest(RequestHandle req);
   virtual void AddObserver(Observer* observer);
   virtual void RemoveObserver(Observer* observer);
-  // TODO(eroman): temp hack for http://crbug.com/18373
-  virtual void Shutdown();
 
   RuleBasedHostResolverProc* rules() { return rules_; }
 
@@ -109,12 +107,14 @@
                                AddressFamily address_family,
                                const std::string& replacement);
 
-  // Same as AddRule(), but the replacement is expected to be an IPV6 literal.
-  // You should use this in place of AddRule(), since the system's host resolver
-  // may not support IPv6 literals on all systems. Whereas this variant
-  // constructs the socket address directly so it will always work.
-  void AddIPv6Rule(const std::string& host_pattern,
-                   const std::string& ipv6_literal);
+  // Same as AddRule(), but the replacement is expected to be an IPv4 or IPv6
+  // literal. This can be used in place of AddRule() to bypass the system's
+  // host resolver (the address list will be constructed manually).
+  // If |canonical-name| is non-empty, it is copied to the resulting AddressList
+  // but does not impact DNS resolution.
+  void AddIPLiteralRule(const std::string& host_pattern,
+                        const std::string& ip_literal,
+                        const std::string& canonical_name);
 
   void AddRuleWithLatency(const std::string& host_pattern,
                           const std::string& replacement,
@@ -130,7 +130,9 @@
   // HostResolverProc methods:
   virtual int Resolve(const std::string& host,
                       AddressFamily address_family,
-                      AddressList* addrlist);
+                      HostResolverFlags host_resolver_flags,
+                      AddressList* addrlist,
+                      int* os_error);
 
  private:
   ~RuleBasedHostResolverProc();
@@ -154,9 +156,12 @@
   // HostResolverProc methods:
   virtual int Resolve(const std::string& host,
                       AddressFamily address_family,
-                      AddressList* addrlist) {
+                      HostResolverFlags host_resolver_flags,
+                      AddressList* addrlist,
+                      int* os_error) {
     event_.Wait();
-    return ResolveUsingPrevious(host, address_family, addrlist);
+    return ResolveUsingPrevious(host, address_family, host_resolver_flags,
+                                addrlist, os_error);
   }
 
  private:
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index bff55d5..e302a9f 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -1,7 +1,10 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
+// This file intentionally does not have header guards, it's included
+// inside a macro to generate enum.
+
 // This file contains the list of network errors.
 
 //
@@ -11,6 +14,8 @@
 //   200-299 Certificate errors
 //   300-399 HTTP errors
 //   400-499 Cache errors
+//   500-599 ?
+//   600-699 FTP errors
 //
 
 // An asynchronous IO operation is not yet complete.  This usually does not
@@ -56,6 +61,10 @@
 // Memory allocation failed.
 NET_ERROR(OUT_OF_MEMORY, -13)
 
+// The file upload failed because the file's modification time was different
+// from the expectation.
+NET_ERROR(UPLOAD_FILE_CHANGED, -14)
+
 // A connection was closed (corresponding to a TCP FIN).
 NET_ERROR(CONNECTION_CLOSED, -100)
 
@@ -105,8 +114,9 @@
 // The server requested a renegotiation (rehandshake).
 NET_ERROR(SSL_RENEGOTIATION_REQUESTED, -114)
 
-// The proxy requested authentication (for tunnel establishment).
-NET_ERROR(PROXY_AUTH_REQUESTED, -115)
+// The proxy requested authentication (for tunnel establishment) with an
+// unsupported method.
+NET_ERROR(PROXY_AUTH_UNSUPPORTED, -115)
 
 // During SSL renegotiation (rehandshake), the server sent a certificate with
 // an error.
@@ -125,6 +135,43 @@
 // aborted.
 NET_ERROR(HOST_RESOLVER_QUEUE_TOO_LARGE, -119)
 
+// Failed establishing a connection to the SOCKS proxy server for a target host.
+NET_ERROR(SOCKS_CONNECTION_FAILED, -120)
+
+// The SOCKS proxy server failed establishing connection to the target host
+// because that host is unreachable.
+NET_ERROR(SOCKS_CONNECTION_HOST_UNREACHABLE, -121)
+
+// The request to negotiate an alternate protocol failed.
+NET_ERROR(NPN_NEGOTIATION_FAILED, -122)
+
+// The peer sent an SSL no_renegotiation alert message.
+NET_ERROR(SSL_NO_RENEGOTIATION, -123)
+
+// Winsock sometimes reports more data written than passed.  This is probably
+// due to a broken LSP.
+NET_ERROR(WINSOCK_UNEXPECTED_WRITTEN_BYTES, -124)
+
+// An SSL peer sent us a fatal decompression_failure alert. This typically
+// occurs when a peer selects DEFLATE compression in the mismaken belief that
+// it supports it.
+NET_ERROR(SSL_DECOMPRESSION_FAILURE_ALERT, -125)
+
+// An SSL peer sent us a fatal bad_record_mac alert. This has been observed
+// from servers with buggy DEFLATE support.
+NET_ERROR(SSL_BAD_RECORD_MAC_ALERT, -126)
+
+// The proxy requested authentication (for tunnel establishment).
+NET_ERROR(PROXY_AUTH_REQUESTED, -127)
+
+// A known TLS strict server didn't offer the renegotiation extension.
+NET_ERROR(SSL_UNSAFE_NEGOTIATION, -128)
+
+// The socket is reporting that we tried to provide new credentials after a
+// a failed attempt on a connection without keep alive.  We need to
+// reestablish the transport socket in order to retry the authentication.
+NET_ERROR(RETRY_CONNECTION, -129)
+
 // Certificate error codes
 //
 // The values of certificate error codes must be consecutive.
@@ -176,6 +223,8 @@
 //
 // MSDN describes this error as follows:
 //   "The SSL certificate contains errors."
+// NOTE: It's unclear how this differs from ERR_CERT_INVALID. For consistency,
+// use that code instead of this one from now on.
 //
 NET_ERROR(CERT_CONTAINS_ERRORS, -203)
 
@@ -289,14 +338,27 @@
 // The server sent an FTP directory listing in a format we do not understand.
 NET_ERROR(UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT, -334)
 
-// Attempted use of an unknown FLIP stream id.
-NET_ERROR(INVALID_FLIP_STREAM, -335)
+// Attempted use of an unknown SPDY stream id.
+NET_ERROR(INVALID_SPDY_STREAM, -335)
 
 // There are no supported proxies in the provided list.
 NET_ERROR(NO_SUPPORTED_PROXIES, -336)
 
-// There is a FLIP protocol framing error.
-NET_ERROR(FLIP_PROTOCOL_ERROR, -337)
+// There is a SPDY protocol framing error.
+NET_ERROR(SPDY_PROTOCOL_ERROR, -337)
+
+// Credentials could not be estalished during HTTP Authentication.
+NET_ERROR(INVALID_AUTH_CREDENTIALS, -338)
+
+// An HTTP Authentication scheme was tried which is not supported on this
+// machine.
+NET_ERROR(UNSUPPORTED_AUTH_SCHEME, -339)
+
+// Detecting the encoding of the response failed.
+NET_ERROR(ENCODING_DETECTION_FAILED, -340)
+
+// (GSSAPI) No Kerberos credentials were available during HTTP Authentication.
+NET_ERROR(MISSING_AUTH_CREDENTIALS, -341)
 
 // The cache does not have the requested entry.
 NET_ERROR(CACHE_MISS, -400)
@@ -323,3 +385,43 @@
 
 // The server's response was insecure (e.g. there was a cert error).
 NET_ERROR(INSECURE_RESPONSE, -501)
+
+// The server responded to a <keygen> with a generated client cert that we
+// don't have the matching private key for.
+NET_ERROR(NO_PRIVATE_KEY_FOR_CERT, -502)
+
+// An error adding to the OS certificate database (e.g. OS X Keychain).
+NET_ERROR(ADD_USER_CERT_FAILED, -503)
+
+// *** Code -600 is reserved (was FTP_PASV_COMMAND_FAILED). ***
+
+// A generic error for failed FTP control connection command.
+// If possible, please use or add a more specific error code.
+NET_ERROR(FTP_FAILED, -601)
+
+// The server cannot fulfill the request at this point. This is a temporary
+// error.
+// FTP response code 421.
+NET_ERROR(FTP_SERVICE_UNAVAILABLE, -602)
+
+// The server has aborted the transfer.
+// FTP response code 426.
+NET_ERROR(FTP_TRANSFER_ABORTED, -603)
+
+// The file is busy, or some other temporary error condition on opening
+// the file.
+// FTP response code 450.
+NET_ERROR(FTP_FILE_BUSY, -604)
+
+// Server rejected our command because of syntax errors.
+// FTP response codes 500, 501.
+NET_ERROR(FTP_SYNTAX_ERROR, -605)
+
+// Server does not support the command we issued.
+// FTP response codes 502, 504.
+NET_ERROR(FTP_COMMAND_NOT_SUPPORTED, -606)
+
+// Server rejected our command because we didn't issue the commands in right
+// order.
+// FTP response code 503.
+NET_ERROR(FTP_BAD_COMMAND_SEQUENCE, -607)
diff --git a/net/base/net_log.cc b/net/base/net_log.cc
new file mode 100644
index 0000000..bd62f91
--- /dev/null
+++ b/net/base/net_log.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 2010 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/base/net_log.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/values.h"
+
+namespace net {
+
+// static
+const char* NetLog::EventTypeToString(EventType event) {
+  switch (event) {
+#define EVENT_TYPE(label) case TYPE_ ## label: return #label;
+#include "net/base/net_log_event_type_list.h"
+#undef EVENT_TYPE
+  }
+  return NULL;
+}
+
+// static
+std::vector<NetLog::EventType> NetLog::GetAllEventTypes() {
+  std::vector<NetLog::EventType> types;
+#define EVENT_TYPE(label) types.push_back(TYPE_ ## label);
+#include "net/base/net_log_event_type_list.h"
+#undef EVENT_TYPE
+  return types;
+}
+
+void BoundNetLog::AddEntry(
+    NetLog::EventType type,
+    NetLog::EventPhase phase,
+    const scoped_refptr<NetLog::EventParameters>& params) const {
+  if (net_log_) {
+    net_log_->AddEntry(type, base::TimeTicks::Now(), source_, phase, params);
+  }
+}
+
+void BoundNetLog::AddEntryWithTime(
+    NetLog::EventType type,
+    const base::TimeTicks& time,
+    NetLog::EventPhase phase,
+    const scoped_refptr<NetLog::EventParameters>& params) const {
+  if (net_log_) {
+    net_log_->AddEntry(type, time, source_, phase, params);
+  }
+}
+
+bool BoundNetLog::HasListener() const {
+  if (net_log_)
+    return net_log_->HasListener();
+  return false;
+}
+
+void BoundNetLog::AddEvent(
+    NetLog::EventType event_type,
+    const scoped_refptr<NetLog::EventParameters>& params) const {
+  AddEntry(event_type, NetLog::PHASE_NONE, params);
+}
+
+void BoundNetLog::BeginEvent(
+    NetLog::EventType event_type,
+    const scoped_refptr<NetLog::EventParameters>& params) const {
+  AddEntry(event_type, NetLog::PHASE_BEGIN, params);
+}
+
+void BoundNetLog::EndEvent(
+    NetLog::EventType event_type,
+    const scoped_refptr<NetLog::EventParameters>& params) const {
+  AddEntry(event_type, NetLog::PHASE_END, params);
+}
+
+// static
+BoundNetLog BoundNetLog::Make(NetLog* net_log,
+                              NetLog::SourceType source_type) {
+  if (!net_log)
+    return BoundNetLog();
+
+  NetLog::Source source(source_type, net_log->NextID());
+  return BoundNetLog(source, net_log);
+}
+
+NetLogStringParameter::NetLogStringParameter(const char* name,
+                                             const std::string& value)
+    : name_(name), value_(value) {
+}
+
+Value* NetLogIntegerParameter::ToValue() const {
+  DictionaryValue* dict = new DictionaryValue();
+  dict->SetInteger(ASCIIToWide(name_), value_);
+  return dict;
+}
+
+Value* NetLogStringParameter::ToValue() const {
+  DictionaryValue* dict = new DictionaryValue();
+  dict->SetString(ASCIIToWide(name_), value_);
+  return dict;
+}
+
+Value* NetLogSourceParameter::ToValue() const {
+  DictionaryValue* dict = new DictionaryValue();
+
+  DictionaryValue* source_dict = new DictionaryValue();
+  source_dict->SetInteger(L"type", static_cast<int>(value_.type));
+  source_dict->SetInteger(L"id", static_cast<int>(value_.id));
+
+  dict->Set(ASCIIToWide(name_), source_dict);
+  return dict;
+}
+
+}  // namespace net
diff --git a/net/base/net_log.h b/net/base/net_log.h
new file mode 100644
index 0000000..266b93a
--- /dev/null
+++ b/net/base/net_log.h
@@ -0,0 +1,233 @@
+// Copyright (c) 2010 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_BASE_NET_LOG_H_
+#define NET_BASE_NET_LOG_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+
+class Value;
+
+namespace base {
+class TimeTicks;
+}
+
+namespace net {
+
+// NetLog is the destination for log messages generated by the network stack.
+// Each log message has a "source" field which identifies the specific entity
+// that generated the message (for example, which URLRequest or which
+// SocketStream).
+//
+// To avoid needing to pass in the "source id" to the logging functions, NetLog
+// is usually accessed through a BoundNetLog, which will always pass in a
+// specific source ID.
+//
+// Note that NetLog is NOT THREADSAFE.
+//
+// ******** The NetLog (and associated logging) is a work in progress ********
+//
+// TODO(eroman): Remove the 'const' qualitifer from the BoundNetLog methods.
+// TODO(eroman): Make the DNS jobs emit into the NetLog.
+// TODO(eroman): Start a new Source each time URLRequest redirects
+//               (simpler to reason about each as a separate entity).
+
+class NetLog {
+ public:
+  enum EventType {
+#define EVENT_TYPE(label) TYPE_ ## label,
+#include "net/base/net_log_event_type_list.h"
+#undef EVENT_TYPE
+  };
+
+  // The 'phase' of an event trace (whether it marks the beginning or end
+  // of an event.).
+  enum EventPhase {
+    PHASE_NONE,
+    PHASE_BEGIN,
+    PHASE_END,
+  };
+
+  // The "source" identifies the entity that generated the log message.
+  enum SourceType {
+#define SOURCE_TYPE(label, value) SOURCE_ ## label = value,
+#include "net/base/net_log_source_type_list.h"
+#undef SOURCE_TYPE
+  };
+
+  // Identifies the entity that generated this log. The |id| field should
+  // uniquely identify the source, and is used by log observers to infer
+  // message groupings. Can use NetLog::NextID() to create unique IDs.
+  struct Source {
+    static const uint32 kInvalidId = 0;
+
+    Source() : type(SOURCE_NONE), id(kInvalidId) {}
+    Source(SourceType type, uint32 id) : type(type), id(id) {}
+    bool is_valid() { return id != kInvalidId; }
+
+    SourceType type;
+    uint32 id;
+  };
+
+  // Base class for associating additional parameters with an event. Log
+  // observers need to know what specific derivations of EventParameters a
+  // particular EventType uses, in order to get at the individual components.
+  class EventParameters : public base::RefCounted<EventParameters> {
+   public:
+    EventParameters() {}
+    virtual ~EventParameters() {}
+
+    // Serializes the parameters to a Value tree. This is intended to be a
+    // lossless conversion, which is used to serialize the parameters to JSON.
+    // The caller takes ownership of the returned Value*.
+    virtual Value* ToValue() const = 0;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(EventParameters);
+  };
+
+  NetLog() {}
+  virtual ~NetLog() {}
+
+  // Emits an event to the log stream.
+  //  |type| - The type of the event.
+  //  |time| - The time when the event occurred.
+  //  |source| - The source that generated the event.
+  //  |phase| - An optional parameter indicating whether this is the start/end
+  //            of an action.
+  //  |params| - Optional (may be NULL) parameters for this event.
+  //             The specific subclass of EventParameters is defined
+  //             by the contract for events of this |type|.
+  //             TODO(eroman): Take a scoped_refptr<> instead.
+  virtual void AddEntry(EventType type,
+                        const base::TimeTicks& time,
+                        const Source& source,
+                        EventPhase phase,
+                        EventParameters* params) = 0;
+
+  // Returns a unique ID which can be used as a source ID.
+  virtual uint32 NextID() = 0;
+
+  // Returns true if more complicated messages should be sent to the log.
+  virtual bool HasListener() const = 0;
+
+  // Returns a C-String symbolic name for |event_type|.
+  static const char* EventTypeToString(EventType event_type);
+
+  // Returns a list of all the available EventTypes.
+  static std::vector<EventType> GetAllEventTypes();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NetLog);
+};
+
+// Helper that binds a Source to a NetLog, and exposes convenience methods to
+// output log messages without needing to pass in the source.
+class BoundNetLog {
+ public:
+  BoundNetLog() : net_log_(NULL) {}
+
+  BoundNetLog(const NetLog::Source& source, NetLog* net_log)
+      : source_(source), net_log_(net_log) {
+  }
+
+  // Convenience methods that call through to the NetLog, passing in the
+  // currently bound source.
+  void AddEntry(NetLog::EventType type,
+                NetLog::EventPhase phase,
+                const scoped_refptr<NetLog::EventParameters>& params) const;
+
+  void AddEntryWithTime(
+      NetLog::EventType type,
+      const base::TimeTicks& time,
+      NetLog::EventPhase phase,
+      const scoped_refptr<NetLog::EventParameters>& params) const;
+
+  // Convenience methods that call through to the NetLog, passing in the
+  // currently bound source, current time, and a fixed "capture phase"
+  // (begin, end, or none).
+  void AddEvent(NetLog::EventType event_type,
+                const scoped_refptr<NetLog::EventParameters>& params) const;
+  void BeginEvent(NetLog::EventType event_type,
+                  const scoped_refptr<NetLog::EventParameters>& params) const;
+  void EndEvent(NetLog::EventType event_type,
+                const scoped_refptr<NetLog::EventParameters>& params) const;
+
+  bool HasListener() const;
+
+  // Helper to create a BoundNetLog given a NetLog and a SourceType. Takes care
+  // of creating a unique source ID, and handles the case of NULL net_log.
+  static BoundNetLog Make(NetLog* net_log, NetLog::SourceType source_type);
+
+  const NetLog::Source& source() const { return source_; }
+  NetLog* net_log() const { return net_log_; }
+
+ private:
+  NetLog::Source source_;
+  NetLog* net_log_;
+};
+
+// NetLogStringParameter is a subclass of EventParameters that encapsulates a
+// single std::string parameter.
+class NetLogStringParameter : public NetLog::EventParameters {
+ public:
+  // |name| must be a string literal.
+  NetLogStringParameter(const char* name, const std::string& value);
+
+  const std::string& value() const {
+    return value_;
+  }
+
+  virtual Value* ToValue() const;
+
+ private:
+  const char* const name_;
+  const std::string value_;
+};
+
+// NetLogIntegerParameter is a subclass of EventParameters that encapsulates a
+// single integer parameter.
+class NetLogIntegerParameter : public NetLog::EventParameters {
+ public:
+  // |name| must be a string literal.
+  NetLogIntegerParameter(const char* name, int value)
+      : name_(name), value_(value) {}
+
+  int value() const {
+    return value_;
+  }
+
+  virtual Value* ToValue() const;
+
+ private:
+  const char* name_;
+  const int value_;
+};
+
+// NetLogSourceParameter is a subclass of EventParameters that encapsulates a
+// single NetLog::Source parameter.
+class NetLogSourceParameter : public NetLog::EventParameters {
+ public:
+  // |name| must be a string literal.
+  NetLogSourceParameter(const char* name, const NetLog::Source& value)
+      : name_(name), value_(value) {}
+
+  const NetLog::Source& value() const {
+    return value_;
+  }
+
+  virtual Value* ToValue() const;
+
+ private:
+  const char* name_;
+  const NetLog::Source value_;
+};
+
+}  // namespace net
+
+#endif  // NET_BASE_NET_LOG_H_
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h
new file mode 100644
index 0000000..2fcd6fa
--- /dev/null
+++ b/net/base/net_log_event_type_list.h
@@ -0,0 +1,609 @@
+// Copyright (c) 2010 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.
+
+// NOTE: No header guards are used, since this file is intended to be expanded
+// directly into net_log.h. DO NOT include this file anywhere else.
+
+// --------------------------------------------------------------------------
+// General pseudo-events
+// --------------------------------------------------------------------------
+
+// Something got cancelled (we determine what is cancelled based on the
+// log context around it.)
+EVENT_TYPE(CANCELLED)
+
+// Marks the creation/destruction of a request (URLRequest or SocketStream).
+EVENT_TYPE(REQUEST_ALIVE)
+
+// ------------------------------------------------------------------------
+// HostResolverImpl
+// ------------------------------------------------------------------------
+
+// The start/end of a host resolve (DNS) request.
+// If an error occurred, the end phase will contain these parameters:
+//   {
+//     "net_error": <The net error code integer for the failure>,
+//     "os_error": <The exact error code integer that getaddrinfo() returned>,
+//     "was_from_cache": <True if the response was gotten from the cache>
+//   }
+EVENT_TYPE(HOST_RESOLVER_IMPL)
+
+// ------------------------------------------------------------------------
+// InitProxyResolver
+// ------------------------------------------------------------------------
+
+// The start/end of auto-detect + custom PAC URL configuration.
+EVENT_TYPE(INIT_PROXY_RESOLVER)
+
+// The start/end of download of a PAC script. This could be the well-known
+// WPAD URL (if testing auto-detect), or a custom PAC URL.
+//
+// The START event has the parameters:
+//   {
+//     "url": <URL string of script being fetched>
+//   }
+//
+// If the fetch failed, then the END phase has these parameters:
+//   {
+//      "error_code": <Net error code integer>
+//   }
+EVENT_TYPE(INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT)
+
+// The start/end of the testing of a PAC script (trying to parse the fetched
+// file as javascript).
+//
+// If the parsing of the script failed, the END phase will have parameters:
+//   {
+//      "error_code": <Net error code integer>
+//   }
+EVENT_TYPE(INIT_PROXY_RESOLVER_SET_PAC_SCRIPT)
+
+// This event means that initialization failed because there was no
+// configured script fetcher. (This indicates a configuration error).
+EVENT_TYPE(INIT_PROXY_RESOLVER_HAS_NO_FETCHER)
+
+// This event is emitted after deciding to fall-back to the next PAC
+// script in the list.
+EVENT_TYPE(INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL)
+
+// ------------------------------------------------------------------------
+// ProxyService
+// ------------------------------------------------------------------------
+
+// The start/end of a proxy resolve request.
+EVENT_TYPE(PROXY_SERVICE)
+
+// The time while a request is waiting on InitProxyResolver to configure
+// against either WPAD or custom PAC URL. The specifics on this time
+// are found from ProxyService::init_proxy_resolver_log().
+EVENT_TYPE(PROXY_SERVICE_WAITING_FOR_INIT_PAC)
+
+// The time taken to fetch the system proxy configuration.
+EVENT_TYPE(PROXY_SERVICE_POLL_CONFIG_SERVICE_FOR_CHANGES)
+
+// This event is emitted to show what the PAC script returned. It can contain
+// extra parameters that are either:
+//   {
+//      "pac_string": <List of valid proxy servers, in PAC format>
+//   }
+//
+//  Or if the the resolver failed:
+//   {
+//      "net_error": <Net error code that resolver failed with>
+//   }
+EVENT_TYPE(PROXY_SERVICE_RESOLVED_PROXY_LIST)
+
+// ------------------------------------------------------------------------
+// Proxy Resolver
+// ------------------------------------------------------------------------
+
+// Measures the time taken to execute the "myIpAddress()" javascript binding.
+EVENT_TYPE(PAC_JAVASCRIPT_MY_IP_ADDRESS)
+
+// Measures the time taken to execute the "myIpAddressEx()" javascript binding.
+EVENT_TYPE(PAC_JAVASCRIPT_MY_IP_ADDRESS_EX)
+
+// Measures the time taken to execute the "dnsResolve()" javascript binding.
+EVENT_TYPE(PAC_JAVASCRIPT_DNS_RESOLVE)
+
+// Measures the time taken to execute the "dnsResolveEx()" javascript binding.
+EVENT_TYPE(PAC_JAVASCRIPT_DNS_RESOLVE_EX)
+
+// This event is emitted when a javascript error has been triggered by a
+// PAC script. It contains the following event parameters:
+//   {
+//      "line_number": <The line number in the PAC script
+//                      (or -1 if not applicable)>,
+//      "message": <The error message>
+//   }
+EVENT_TYPE(PAC_JAVASCRIPT_ERROR)
+
+// This event is emitted when a PAC script called alert(). It contains the
+// following event parameters:
+//   {
+//      "message": <The string of the alert>
+//   }
+EVENT_TYPE(PAC_JAVASCRIPT_ALERT)
+
+// Measures the time that a proxy resolve request was stalled waiting for a
+// proxy resolver thread to free-up.
+EVENT_TYPE(WAITING_FOR_PROXY_RESOLVER_THREAD)
+
+// This event is emitted just before a PAC request is bound to a thread. It
+// contains these parameters:
+//
+//   {
+//     "thread_number": <Identifier for the PAC thread that is going to
+//                       run this request>
+//   }
+EVENT_TYPE(SUBMITTED_TO_RESOLVER_THREAD)
+
+// ------------------------------------------------------------------------
+// ClientSocket
+// ------------------------------------------------------------------------
+
+// The start/end of a TCP connect(). This corresponds with a call to
+// TCPClientSocket::Connect().
+//
+// The START event contains these parameters:
+//
+//   {
+//     "address_list": <List of network address strings>
+//   }
+//
+// And the END event will contain the following parameters on failure:
+//
+//   {
+//     "net_error": <Net integer error code>
+//   }
+EVENT_TYPE(TCP_CONNECT)
+
+// Nested within TCP_CONNECT, there may be multiple attempts to connect
+// to the individual addresses. The START event will describe the
+// address the attempt is for:
+//
+//   {
+//     "address": <String of the network address>
+//   }
+//
+// And the END event will contain the system error code if it failed:
+//
+//   {
+//     "os_error": <Integer error code the operating system returned>
+//   }
+EVENT_TYPE(TCP_CONNECT_ATTEMPT)
+
+// Marks the begin/end of a socket (TCP/SOCKS/SSL).
+EVENT_TYPE(SOCKET_ALIVE)
+
+// This event is logged to the socket stream whenever the socket is
+// acquired/released via a ClientSocketHandle.
+//
+// The BEGIN phase contains the following parameters:
+//
+//   {
+//     "source_dependency": <Source identifier for the controlling entity>
+//   }
+EVENT_TYPE(SOCKET_IN_USE)
+
+// The start/end of a SOCKS connect().
+EVENT_TYPE(SOCKS_CONNECT)
+
+// The start/end of a SOCKS5 connect().
+EVENT_TYPE(SOCKS5_CONNECT)
+
+// This event is emitted when the SOCKS connect fails because the provided
+// was longer than 255 characters.
+EVENT_TYPE(SOCKS_HOSTNAME_TOO_BIG)
+
+// These events are emitted when insufficient data was read while
+// trying to establish a connection to the SOCKS proxy server
+// (during the greeting phase or handshake phase, respectively).
+EVENT_TYPE(SOCKS_UNEXPECTEDLY_CLOSED_DURING_GREETING)
+EVENT_TYPE(SOCKS_UNEXPECTEDLY_CLOSED_DURING_HANDSHAKE)
+
+// This event indicates that a bad version number was received in the
+// proxy server's response. The extra parameters show its value:
+//   {
+//     "version": <Integer version number in the response>
+//   }
+EVENT_TYPE(SOCKS_UNEXPECTED_VERSION)
+
+// This event indicates that the SOCKS proxy server returned an error while
+// trying to create a connection. The following parameters will be attached
+// to the event:
+//   {
+//     "error_code": <Integer error code returned by the server>
+//   }
+EVENT_TYPE(SOCKS_SERVER_ERROR)
+
+// This event indicates that the SOCKS proxy server asked for an authentication
+// method that we don't support. The following parameters are attached to the
+// event:
+//   {
+//     "method": <Integer method code>
+//   }
+EVENT_TYPE(SOCKS_UNEXPECTED_AUTH)
+
+// This event indicates that the SOCKS proxy server's response indicated an
+// address type which we are not prepared to handle.
+// The following parameters are attached to the event:
+//   {
+//     "address_type": <Integer code for the address type>
+//   }
+EVENT_TYPE(SOCKS_UNKNOWN_ADDRESS_TYPE)
+
+// The start/end of a SSL connect().
+EVENT_TYPE(SSL_CONNECT)
+
+// The specified number of bytes were sent on the socket.
+// The following parameters are attached:
+//   {
+//     "num_bytes": <Number of bytes that were just sent>
+//   }
+EVENT_TYPE(SOCKET_BYTES_SENT)
+
+// The specified number of bytes were received on the socket.
+// The following parameters are attached:
+//   {
+//     "num_bytes": <Number of bytes that were just sent>
+//   }
+EVENT_TYPE(SOCKET_BYTES_RECEIVED)
+
+// ------------------------------------------------------------------------
+// ClientSocketPoolBase::ConnectJob
+// ------------------------------------------------------------------------
+
+// The start/end of a ConnectJob.
+EVENT_TYPE(SOCKET_POOL_CONNECT_JOB)
+
+// The start/end of the ConnectJob::Connect().
+//
+// The BEGIN phase has these parameters:
+//
+//   {
+//     "group_name": <The group name for the socket request.>
+//   }
+EVENT_TYPE(SOCKET_POOL_CONNECT_JOB_CONNECT)
+
+// This event is logged whenever the ConnectJob gets a new socket
+// association. The event parameters point to that socket:
+//
+//   {
+//     "source_dependency": <The source identifier for the new socket.>
+//   }
+EVENT_TYPE(CONNECT_JOB_SET_SOCKET)
+
+// Whether the connect job timed out.
+EVENT_TYPE(SOCKET_POOL_CONNECT_JOB_TIMED_OUT)
+
+// ------------------------------------------------------------------------
+// ClientSocketPoolBaseHelper
+// ------------------------------------------------------------------------
+
+// The start/end of a client socket pool request for a socket.
+EVENT_TYPE(SOCKET_POOL)
+
+// The request stalled because there are too many sockets in the pool.
+EVENT_TYPE(SOCKET_POOL_STALLED_MAX_SOCKETS)
+
+// The request stalled because there are too many sockets in the group.
+EVENT_TYPE(SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP)
+
+// Indicates that we reused an existing socket. Attached to the event are
+// the parameters:
+//   {
+//     "idle_ms": <The number of milliseconds the socket was sitting idle for>
+//   }
+EVENT_TYPE(SOCKET_POOL_REUSED_AN_EXISTING_SOCKET)
+
+// This event simply describes the host:port that were requested from the
+// socket pool. Its parameters are:
+//   {
+//     "host_and_port": <String encoding the host and port>
+//   }
+EVENT_TYPE(TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET)
+
+
+// A backup socket is created due to slow connect
+EVENT_TYPE(SOCKET_BACKUP_CREATED)
+
+// This event is sent when a connect job is eventually bound to a request
+// (because of late binding and socket backup jobs, we don't assign the job to
+// a request until it has completed).
+//
+// The event parameters are:
+//   {
+//      "source_dependency": <Source identifer for the connect job we are
+//                            bound to>
+//   }
+EVENT_TYPE(SOCKET_POOL_BOUND_TO_CONNECT_JOB)
+
+// Identifies the NetLog::Source() for the Socket assigned to the pending
+// request. The event parameters are:
+//   {
+//      "source_dependency": <Source identifier for the socket we acquired>
+//   }
+EVENT_TYPE(SOCKET_POOL_BOUND_TO_SOCKET)
+
+// ------------------------------------------------------------------------
+// URLRequest
+// ------------------------------------------------------------------------
+
+// Measures the time it took a URLRequestJob to start. For the most part this
+// corresponds with the time between URLRequest::Start() and
+// URLRequest::ResponseStarted(), however it is also repeated for every
+// redirect, and every intercepted job that handles the request.
+//
+// For the BEGIN phase, the following parameters are attached:
+//   {
+//      "url": <String of URL being loaded>,
+//      "method": <The method ("POST" or "GET" or "HEAD" etc..)>,
+//      "load_flags": <Numeric value of the combined load flags>
+//   }
+//
+// For the END phase, if there was an error, the following parameters are
+// attached:
+//   {
+//      "net_error": <Net error code of the failure>
+//   }
+EVENT_TYPE(URL_REQUEST_START_JOB)
+
+// This event is sent once a URLRequest receives a redirect. The parameters
+// attached to the event are:
+//   {
+//     "location": <The URL that was redirected to>
+//   }
+EVENT_TYPE(URL_REQUEST_REDIRECTED)
+
+// ------------------------------------------------------------------------
+// HttpCache
+// ------------------------------------------------------------------------
+
+// Measures the time while opening a disk cache entry.
+EVENT_TYPE(HTTP_CACHE_OPEN_ENTRY)
+
+// Measures the time while creating a disk cache entry.
+EVENT_TYPE(HTTP_CACHE_CREATE_ENTRY)
+
+// Measures the time while deleting a disk cache entry.
+EVENT_TYPE(HTTP_CACHE_DOOM_ENTRY)
+
+// Measures the time while reading the response info from a disk cache entry.
+EVENT_TYPE(HTTP_CACHE_READ_INFO)
+
+// Measures the time that an HttpCache::Transaction is stalled waiting for
+// the cache entry to become available (for example if we are waiting for
+// exclusive access to an existing entry).
+EVENT_TYPE(HTTP_CACHE_WAITING)
+
+// ------------------------------------------------------------------------
+// HttpNetworkTransaction
+// ------------------------------------------------------------------------
+
+// Measures the time taken to send the tunnel request to the server.
+EVENT_TYPE(HTTP_TRANSACTION_TUNNEL_SEND_REQUEST)
+
+// This event is sent for a tunnel request.
+// The following parameters are attached:
+//   {
+//     "line": <The HTTP request line, CRLF terminated>,
+//     "headers": <The list of header:value pairs>
+//   }
+EVENT_TYPE(HTTP_TRANSACTION_SEND_TUNNEL_HEADERS)
+
+// Measures the time to read the tunnel response headers from the server.
+EVENT_TYPE(HTTP_TRANSACTION_TUNNEL_READ_HEADERS)
+
+// This event is sent on receipt of the HTTP response headers to a tunnel
+// request.
+// The following parameters are attached:
+//   {
+//     "headers": <The list of header:value pairs>
+//   }
+EVENT_TYPE(HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS)
+
+// Measures the time taken to send the request to the server.
+EVENT_TYPE(HTTP_TRANSACTION_SEND_REQUEST)
+
+// This event is sent for a HTTP request.
+// The following parameters are attached:
+//   {
+//     "line": <The HTTP request line, CRLF terminated>,
+//     "headers": <The list of header:value pairs>
+//   }
+EVENT_TYPE(HTTP_TRANSACTION_SEND_REQUEST_HEADERS)
+
+// Measures the time to read HTTP response headers from the server.
+EVENT_TYPE(HTTP_TRANSACTION_READ_HEADERS)
+
+// This event is sent on receipt of the HTTP response headers.
+// The following parameters are attached:
+//   {
+//     "headers": <The list of header:value pairs>
+//   }
+EVENT_TYPE(HTTP_TRANSACTION_READ_RESPONSE_HEADERS)
+
+// Measures the time to read the entity body from the server.
+EVENT_TYPE(HTTP_TRANSACTION_READ_BODY)
+
+// Measures the time taken to read the response out of the socket before
+// restarting for authentication, on keep alive connections.
+EVENT_TYPE(HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART)
+
+// ------------------------------------------------------------------------
+// SpdyNetworkTransaction
+// ------------------------------------------------------------------------
+
+// Measures the time taken to get a spdy stream.
+EVENT_TYPE(SPDY_TRANSACTION_INIT_CONNECTION)
+
+// Measures the time taken to send the request to the server.
+EVENT_TYPE(SPDY_TRANSACTION_SEND_REQUEST)
+
+// Measures the time to read HTTP response headers from the server.
+EVENT_TYPE(SPDY_TRANSACTION_READ_HEADERS)
+
+// Measures the time to read the entity body from the server.
+EVENT_TYPE(SPDY_TRANSACTION_READ_BODY)
+
+// ------------------------------------------------------------------------
+// SpdySession
+// ------------------------------------------------------------------------
+
+// The start/end of a SpdySession.
+EVENT_TYPE(SPDY_SESSION)
+
+// On sending a SPDY SETTINGS frame.
+// The following parameters are attached:
+//   {
+//     "settings": <The list of setting id:value pairs>
+//   }
+EVENT_TYPE(SPDY_SESSION_SEND_SETTINGS)
+
+// Receipt of a SPDY SETTINGS frame.
+// The following parameters are attached:
+//   {
+//     "settings": <The list of setting id:value pairs>
+//   }
+EVENT_TYPE(SPDY_SESSION_RECV_SETTINGS)
+
+// Receipt of a SPDY GOAWAY frame.
+// The following parameters are attached:
+//   {
+//     "last_accepted_stream_id": <Last stream id accepted by the server, duh>
+//   }
+EVENT_TYPE(SPDY_SESSION_GOAWAY)
+
+// This event is sent for a SPDY SYN_STREAM pushed by the server, but no
+// URLRequest has requested it yet.
+// The following parameters are attached:
+//   {
+//     "flags": <The control frame flags>
+//     "headers": <The list of header:value pairs>
+//     "id": <The stream id>
+//   }
+EVENT_TYPE(SPDY_SESSION_PUSHED_SYN_STREAM)
+
+// ------------------------------------------------------------------------
+// SpdyStream
+// ------------------------------------------------------------------------
+
+// This event is sent for a SPDY SYN_STREAM.
+// The following parameters are attached:
+//   {
+//     "flags": <The control frame flags>,
+//     "headers": <The list of header:value pairs>,
+//     "id": <The stream id>
+//   }
+EVENT_TYPE(SPDY_STREAM_SYN_STREAM)
+
+// This event is sent for a SPDY SYN_STREAM pushed by the server, where a
+// URLRequest is already waiting for the stream.
+// The following parameters are attached:
+//   {
+//     "flags": <The control frame flags>
+//     "headers": <The list of header:value pairs>
+//     "id": <The stream id>
+//   }
+EVENT_TYPE(SPDY_STREAM_PUSHED_SYN_STREAM)
+
+// Measures the time taken to send headers on a stream.
+EVENT_TYPE(SPDY_STREAM_SEND_HEADERS)
+
+// Measures the time taken to send the body (e.g. a POST) on a stream.
+EVENT_TYPE(SPDY_STREAM_SEND_BODY)
+
+// This event is sent for a SPDY SYN_REPLY.
+// The following parameters are attached:
+//   {
+//     "flags": <The control frame flags>,
+//     "headers": <The list of header:value pairs>,
+//     "id": <The stream id>
+//   }
+EVENT_TYPE(SPDY_STREAM_SYN_REPLY)
+
+// Measures the time taken to read headers on a stream.
+EVENT_TYPE(SPDY_STREAM_READ_HEADERS)
+
+// Measures the time taken to read the body on a stream.
+EVENT_TYPE(SPDY_STREAM_READ_BODY)
+
+// Logs that a stream attached to a pushed stream.
+EVENT_TYPE(SPDY_STREAM_ADOPTED_PUSH_STREAM)
+
+// The receipt of a RST_STREAM
+// The following parameters are attached:
+//   {
+//     "status": <The reason for the RST_STREAM>
+//   }
+EVENT_TYPE(SPDY_STREAM_RST_STREAM)
+
+// ------------------------------------------------------------------------
+// HttpStreamParser
+// ------------------------------------------------------------------------
+
+// Measures the time to read HTTP response headers from the server.
+EVENT_TYPE(HTTP_STREAM_PARSER_READ_HEADERS)
+
+// ------------------------------------------------------------------------
+// SocketStream
+// ------------------------------------------------------------------------
+
+// Measures the time between SocketStream::Connect() and
+// SocketStream::DidEstablishConnection()
+//
+// For the BEGIN phase, the following parameters are attached:
+//   {
+//      "url": <String of URL being loaded>
+//   }
+//
+// For the END phase, if there was an error, the following parameters are
+// attached:
+//   {
+//      "net_error": <Net error code of the failure>
+//   }
+EVENT_TYPE(SOCKET_STREAM_CONNECT)
+
+// A message sent on the SocketStream.
+EVENT_TYPE(SOCKET_STREAM_SENT)
+
+// A message received on the SocketStream.
+EVENT_TYPE(SOCKET_STREAM_RECEIVED)
+
+// ------------------------------------------------------------------------
+// SOCKS5ClientSocket
+// ------------------------------------------------------------------------
+
+// The time spent sending the "greeting" to the SOCKS server.
+EVENT_TYPE(SOCKS5_GREET_WRITE)
+
+// The time spent waiting for the "greeting" response from the SOCKS server.
+EVENT_TYPE(SOCKS5_GREET_READ)
+
+// The time spent sending the CONNECT request to the SOCKS server.
+EVENT_TYPE(SOCKS5_HANDSHAKE_WRITE)
+
+// The time spent waiting for the response to the CONNECT request.
+EVENT_TYPE(SOCKS5_HANDSHAKE_READ)
+
+// ------------------------------------------------------------------------
+// HTTP Authentication
+// ------------------------------------------------------------------------
+
+// The time spent authenticating to the proxy.
+EVENT_TYPE(AUTH_PROXY)
+
+// The time spent authentication to the server.
+EVENT_TYPE(AUTH_SERVER)
+
+// ------------------------------------------------------------------------
+// Global events
+// ------------------------------------------------------------------------
+// These are events which are not grouped by source id, as they have no
+// context.
+
+// This event is emitted whenever NetworkChangeNotifier determines that the
+// underlying network has changed.
+EVENT_TYPE(NETWORK_IP_ADDRESSSES_CHANGED)
diff --git a/net/base/net_log_source_type_list.h b/net/base/net_log_source_type_list.h
new file mode 100644
index 0000000..d416f3f
--- /dev/null
+++ b/net/base/net_log_source_type_list.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2010 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.
+
+// NOTE: No header guards are used, since this file is intended to be expanded
+// directly within a block where the SOURCE_TYPE macro is defined.
+
+// Used for global events which don't correspond to a particular entity.
+SOURCE_TYPE(NONE, 0)
+SOURCE_TYPE(URL_REQUEST, 1)
+SOURCE_TYPE(SOCKET_STREAM, 2)
+SOURCE_TYPE(INIT_PROXY_RESOLVER, 3)
+SOURCE_TYPE(CONNECT_JOB, 4)
+SOURCE_TYPE(SOCKET, 5)
+SOURCE_TYPE(SPDY_SESSION, 6)
+
+SOURCE_TYPE(COUNT, 7) // Always keep this as the last entry.
diff --git a/net/base/net_log_unittest.h b/net/base/net_log_unittest.h
new file mode 100644
index 0000000..376ceac
--- /dev/null
+++ b/net/base/net_log_unittest.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2009 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_BASE_NET_LOG_UNITTEST_H_
+#define NET_BASE_NET_LOG_UNITTEST_H_
+
+#include <cstddef>
+#include <vector>
+#include "net/base/capturing_net_log.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+// Create a timestamp with internal value of |t| milliseconds from the epoch.
+inline base::TimeTicks MakeTime(int t) {
+  base::TimeTicks ticks;  // initialized to 0.
+  ticks += base::TimeDelta::FromMilliseconds(t);
+  return ticks;
+}
+
+inline ::testing::AssertionResult LogContainsEventHelper(
+    const CapturingNetLog::EntryList& entries,
+    int i,  // Negative indices are reverse indices.
+    const base::TimeTicks& expected_time,
+    bool check_time,
+    NetLog::EventType expected_event,
+    NetLog::EventPhase expected_phase) {
+  // Negative indices are reverse indices.
+  size_t j = (i < 0) ? entries.size() + i : i;
+  if (j >= entries.size())
+    return ::testing::AssertionFailure() << j << " is out of bounds.";
+  const CapturingNetLog::Entry& entry = entries[j];
+  if (expected_event != entry.type) {
+    return ::testing::AssertionFailure()
+        << "Actual event: " << NetLog::EventTypeToString(entry.type)
+        << ". Expected event: " << NetLog::EventTypeToString(expected_event)
+        << ".";
+  }
+  if (expected_phase != entry.phase) {
+    return ::testing::AssertionFailure()
+        << "Actual phase: " << entry.phase
+        << ". Expected phase: " << expected_phase << ".";
+  }
+  if (check_time) {
+    if (expected_time != entry.time) {
+      return ::testing::AssertionFailure()
+          << "Actual time: " << entry.time.ToInternalValue()
+          << ". Expected time: " << expected_time.ToInternalValue()
+          << ".";
+    }
+  }
+  return ::testing::AssertionSuccess();
+}
+
+inline ::testing::AssertionResult LogContainsEventAtTime(
+    const CapturingNetLog::EntryList& log,
+    int i,  // Negative indices are reverse indices.
+    const base::TimeTicks& expected_time,
+    NetLog::EventType expected_event,
+    NetLog::EventPhase expected_phase) {
+  return LogContainsEventHelper(log, i, expected_time, true,
+                                expected_event, expected_phase);
+}
+
+// Version without timestamp.
+inline ::testing::AssertionResult LogContainsEvent(
+    const CapturingNetLog::EntryList& log,
+    int i,  // Negative indices are reverse indices.
+    NetLog::EventType expected_event,
+    NetLog::EventPhase expected_phase) {
+  return LogContainsEventHelper(log, i, base::TimeTicks(), false,
+                                expected_event, expected_phase);
+}
+
+// Version for PHASE_BEGIN (and no timestamp).
+inline ::testing::AssertionResult LogContainsBeginEvent(
+    const CapturingNetLog::EntryList& log,
+    int i,  // Negative indices are reverse indices.
+    NetLog::EventType expected_event) {
+  return LogContainsEvent(log, i, expected_event, NetLog::PHASE_BEGIN);
+}
+
+// Version for PHASE_END (and no timestamp).
+inline ::testing::AssertionResult LogContainsEndEvent(
+    const CapturingNetLog::EntryList& log,
+    int i,  // Negative indices are reverse indices.
+    NetLog::EventType expected_event) {
+  return LogContainsEvent(log, i, expected_event, NetLog::PHASE_END);
+}
+
+inline ::testing::AssertionResult LogContainsEntryWithType(
+    const CapturingNetLog::EntryList& entries,
+    int i, // Negative indices are reverse indices.
+    NetLog::EventType type) {
+  // Negative indices are reverse indices.
+  size_t j = (i < 0) ? entries.size() + i : i;
+  if (j >= entries.size())
+    return ::testing::AssertionFailure() << j << " is out of bounds.";
+  const CapturingNetLog::Entry& entry = entries[j];
+  if (entry.type != type)
+    return ::testing::AssertionFailure() << "Type does not match.";
+  return ::testing::AssertionSuccess();
+}
+
+
+// Expect that the log contains an event, but don't care about where
+// as long as the index where it is found is greater than min_index.
+// Returns the position where the event was found.
+inline size_t ExpectLogContainsSomewhere(
+    const CapturingNetLog::EntryList& entries,
+    size_t min_index,
+    NetLog::EventType expected_event,
+    NetLog::EventPhase expected_phase) {
+  size_t i = 0;
+  for (; i < entries.size(); ++i) {
+    const CapturingNetLog::Entry& entry = entries[i];
+    if (entry.type == expected_event &&
+        entry.phase == expected_phase)
+      break;
+  }
+  EXPECT_LT(i, entries.size());
+  EXPECT_GE(i, min_index);
+  return i;
+}
+
+}  // namespace net
+
+#endif  // NET_BASE_NET_LOG_UNITTEST_H_
diff --git a/net/base/net_resources.grd b/net/base/net_resources.grd
index f64dd60..327778a 100644
--- a/net/base/net_resources.grd
+++ b/net/base/net_resources.grd
@@ -4,8 +4,8 @@
     <output filename="grit/net_resources.h" type="rc_header">
       <emit emit_type='prepend'></emit>
     </output>
-    <output filename="net_resources.rc" type="rc_all" />
     <output filename="net_resources.pak" type="data_package" />
+    <output filename="net_resources.rc" type="rc_all" />
   </outputs>
   <translations />
   <release seq="1">
diff --git a/net/base/net_test_suite.h b/net/base/net_test_suite.h
index af58ca7..cc4ed2a 100644
--- a/net/base/net_test_suite.h
+++ b/net/base/net_test_suite.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -25,6 +25,8 @@
   // TestSuite::Initialize().  TestSuite::Initialize() performs some global
   // initialization that can only be done once.
   void InitializeTestThread() {
+    network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
+
     host_resolver_proc_ = new net::RuleBasedHostResolverProc(NULL);
     scoped_host_resolver_proc_.Init(host_resolver_proc_.get());
     // In case any attempts are made to resolve host names, force them all to
@@ -44,6 +46,7 @@
   }
 
  private:
+  scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
   scoped_ptr<MessageLoop> message_loop_;
   scoped_refptr<net::RuleBasedHostResolverProc> host_resolver_proc_;
   net::ScopedDefaultHostResolverProc scoped_host_resolver_proc_;
diff --git a/net/base/net_util.cc b/net/base/net_util.cc
index 43fa906..c7b6a6f 100644
--- a/net/base/net_util.cc
+++ b/net/base/net_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -21,15 +21,19 @@
 #include <ws2tcpip.h>
 #include <wspiapi.h>  // Needed for Win2k compat.
 #elif defined(OS_POSIX)
-#include <netdb.h>
-#include <sys/socket.h>
 #include <fcntl.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
 #endif
 
 #include "base/base64.h"
 #include "base/basictypes.h"
 #include "base/file_path.h"
 #include "base/file_util.h"
+#include "base/histogram.h"
 #include "base/i18n/file_util_icu.h"
 #include "base/i18n/icu_string_conversions.h"
 #include "base/i18n/time_formatting.h"
@@ -46,10 +50,13 @@
 #include "base/sys_string_conversions.h"
 #include "base/time.h"
 #include "base/utf_offset_string_conversions.h"
+#include "base/utf_string_conversions.h"
 #include "grit/net_resources.h"
 #include "googleurl/src/gurl.h"
 #include "googleurl/src/url_canon.h"
+#include "googleurl/src/url_canon_ip.h"
 #include "googleurl/src/url_parse.h"
+#include "net/base/dns_util.h"
 #include "net/base/escape.h"
 #include "net/base/net_module.h"
 #if defined(OS_WIN)
@@ -128,6 +135,13 @@
   3659, // apple-sasl / PasswordServer
   4045, // lockd
   6000, // X11
+  6665, // Alternate IRC [Apple addition]
+  6666, // Alternate IRC [Apple addition]
+  6667, // Standard IRC [Apple addition]
+  6668, // Alternate IRC [Apple addition]
+  6669, // Alternate IRC [Apple addition]
+  0xFFFF, // Used to block all invalid port numbers (see
+          // third_party/WebKit/WebCore/platform/KURLGoogle.cpp, port())
 };
 
 // FTP overrides the following restricted ports.
@@ -257,8 +271,13 @@
 
 bool DecodeWord(const std::string& encoded_word,
                 const std::string& referrer_charset,
-                bool *is_rfc2047,
+                bool* is_rfc2047,
                 std::string* output) {
+  *is_rfc2047 = false;
+  output->clear();
+  if (encoded_word.empty())
+    return true;
+
   if (!IsStringASCII(encoded_word)) {
     // Try UTF-8, referrer_charset and the native OS default charset in turn.
     if (IsStringUTF8(encoded_word)) {
@@ -274,7 +293,7 @@
         *output = WideToUTF8(base::SysNativeMBToWide(encoded_word));
       }
     }
-    *is_rfc2047 = false;
+
     return true;
   }
 
@@ -697,10 +716,30 @@
   return false;
 }
 
+// If |component| is valid, its begin is incremented by |delta|.
+void AdjustComponent(int delta, url_parse::Component* component) {
+  if (!component->is_valid())
+    return;
+
+  DCHECK(delta >= 0 || component->begin >= -delta);
+  component->begin += delta;
+}
+
+// Adjusts all the components of |parsed| by |delta|, except for the scheme.
+void AdjustComponents(int delta, url_parse::Parsed* parsed) {
+  AdjustComponent(delta, &(parsed->username));
+  AdjustComponent(delta, &(parsed->password));
+  AdjustComponent(delta, &(parsed->host));
+  AdjustComponent(delta, &(parsed->port));
+  AdjustComponent(delta, &(parsed->path));
+  AdjustComponent(delta, &(parsed->query));
+  AdjustComponent(delta, &(parsed->ref));
+}
+
 // Helper for FormatUrl().
 std::wstring FormatViewSourceUrl(const GURL& url,
                                  const std::wstring& languages,
-                                 bool omit_username_password,
+                                 net::FormatUrlTypes format_types,
                                  UnescapeRule::Type unescape_rules,
                                  url_parse::Parsed* new_parsed,
                                  size_t* prefix_end,
@@ -715,8 +754,7 @@
   size_t* temp_offset_ptr = (*offset_for_adjustment < kViewSourceLengthPlus1) ?
       NULL : &temp_offset;
   std::wstring result = net::FormatUrl(real_url, languages,
-      omit_username_password, unescape_rules, new_parsed, prefix_end,
-      temp_offset_ptr);
+      format_types, unescape_rules, new_parsed, prefix_end, temp_offset_ptr);
   result.insert(0, kWideViewSource);
 
   // Adjust position values.
@@ -727,20 +765,7 @@
     new_parsed->scheme.begin = 0;
     new_parsed->scheme.len = kViewSourceLengthPlus1 - 1;
   }
-  if (new_parsed->username.is_nonempty())
-    new_parsed->username.begin += kViewSourceLengthPlus1;
-  if (new_parsed->password.is_nonempty())
-    new_parsed->password.begin += kViewSourceLengthPlus1;
-  if (new_parsed->host.is_nonempty())
-    new_parsed->host.begin += kViewSourceLengthPlus1;
-  if (new_parsed->port.is_nonempty())
-    new_parsed->port.begin += kViewSourceLengthPlus1;
-  if (new_parsed->path.is_nonempty())
-    new_parsed->path.begin += kViewSourceLengthPlus1;
-  if (new_parsed->query.is_nonempty())
-    new_parsed->query.begin += kViewSourceLengthPlus1;
-  if (new_parsed->ref.is_nonempty())
-    new_parsed->ref.begin += kViewSourceLengthPlus1;
+  AdjustComponents(kViewSourceLengthPlus1, new_parsed);
   if (prefix_end)
     *prefix_end += kViewSourceLengthPlus1;
   if (temp_offset_ptr) {
@@ -754,6 +779,13 @@
 
 namespace net {
 
+const FormatUrlType kFormatUrlOmitNothing                     = 0;
+const FormatUrlType kFormatUrlOmitUsernamePassword            = 1 << 0;
+const FormatUrlType kFormatUrlOmitHTTP                        = 1 << 1;
+const FormatUrlType kFormatUrlOmitTrailingSlashOnBareHostname = 1 << 2;
+const FormatUrlType kFormatUrlOmitAll = kFormatUrlOmitUsernamePassword |
+    kFormatUrlOmitHTTP | kFormatUrlOmitTrailingSlashOnBareHostname;
+
 std::set<int> explicitly_allowed_ports;
 
 // Appends the substring |in_component| inside of the URL |spec| to |output|,
@@ -956,7 +988,8 @@
   return (c >= '0') && (c <= '9');
 }
 
-bool IsCanonicalizedHostCompliant(const std::string& host) {
+bool IsCanonicalizedHostCompliant(const std::string& host,
+                                  const std::string& desired_tld) {
   if (host.empty())
     return false;
 
@@ -986,7 +1019,8 @@
     }
   }
 
-  return most_recent_component_started_alpha;
+  return most_recent_component_started_alpha ||
+      (!desired_tld.empty() && IsHostCharAlpha(desired_tld[0]));
 }
 
 std::string GetDirectoryListingEntry(const string16& name,
@@ -1051,7 +1085,7 @@
   }
 
   const std::string filename_from_cd = GetFileNameFromCD(content_disposition,
-                                                          referrer_charset);
+                                                         referrer_charset);
 #if defined(OS_WIN)
   FilePath::StringType filename = UTF8ToWide(filename_from_cd);
 #elif defined(OS_POSIX)
@@ -1072,14 +1106,35 @@
       const std::string unescaped_url_filename = UnescapeURLComponent(
           url.ExtractFileName(),
           UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS);
+
+      // The URL's path should be escaped UTF-8, but may not be.
+      std::string decoded_filename = unescaped_url_filename;
+      if (!IsStringASCII(decoded_filename)) {
+        bool ignore;
+        // TODO(jshin): this is probably not robust enough. To be sure, we
+        // need encoding detection.
+        DecodeWord(unescaped_url_filename, referrer_charset, &ignore,
+                   &decoded_filename);
+      }
+
 #if defined(OS_WIN)
-      filename = UTF8ToWide(unescaped_url_filename);
+      filename = UTF8ToWide(decoded_filename);
 #elif defined(OS_POSIX)
-      filename = unescaped_url_filename;
+      filename = decoded_filename;
 #endif
     }
   }
 
+#if defined(OS_WIN)
+  { // Handle CreateFile() stripping trailing dots and spaces on filenames
+    // http://support.microsoft.com/kb/115827
+    std::string::size_type pos = filename.find_last_not_of(L" .");
+    if (pos == std::string::npos)
+      filename.resize(0);
+    else
+      filename.resize(++pos);
+  }
+#endif
   // Trim '.' once more.
   TrimString(filename, FILE_PATH_LITERAL("."), &filename);
 
@@ -1147,7 +1202,7 @@
 #elif defined(OS_POSIX)
   int flags = fcntl(fd, F_GETFL, 0);
   if (-1 == flags)
-    flags = 0;
+    return flags;
   return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 #endif
 }
@@ -1238,6 +1293,21 @@
   return std::string(buffer);
 }
 
+std::string NetAddressToStringWithPort(const struct addrinfo* net_address) {
+  std::string ip_address_string = NetAddressToString(net_address);
+  if (ip_address_string.empty())
+    return std::string();  // Failed.
+
+  int port = GetPortFromAddrinfo(net_address);
+
+  if (ip_address_string.find(':') != std::string::npos) {
+    // Surround with square brackets to avoid ambiguity.
+    return StringPrintf("[%s]:%d", ip_address_string.c_str(), port);
+  }
+
+  return StringPrintf("%s:%d", ip_address_string.c_str(), port);
+}
+
 std::string GetHostName() {
 #if defined(OS_WIN)
   EnsureWinsockInit();
@@ -1256,13 +1326,18 @@
 void GetIdentityFromURL(const GURL& url,
                         std::wstring* username,
                         std::wstring* password) {
-  UnescapeRule::Type flags = UnescapeRule::SPACES;
+  UnescapeRule::Type flags =
+      UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS;
   *username = UTF16ToWideHack(UnescapeAndDecodeUTF8URLComponent(url.username(),
                                                                 flags, NULL));
   *password = UTF16ToWideHack(UnescapeAndDecodeUTF8URLComponent(url.password(),
                                                                 flags, NULL));
 }
 
+std::string GetHostOrSpecFromURL(const GURL& url) {
+  return url.has_host() ? net::TrimEndingDot(url.host()) : url.spec();
+}
+
 void AppendFormattedHost(const GURL& url,
                          const std::wstring& languages,
                          std::wstring* output,
@@ -1349,7 +1424,7 @@
 
 std::wstring FormatUrl(const GURL& url,
                        const std::wstring& languages,
-                       bool omit_username_password,
+                       FormatUrlTypes format_types,
                        UnescapeRule::Type unescape_rules,
                        url_parse::Parsed* new_parsed,
                        size_t* prefix_end,
@@ -1357,6 +1432,8 @@
   url_parse::Parsed parsed_temp;
   if (!new_parsed)
     new_parsed = &parsed_temp;
+  else
+    *new_parsed = url_parse::Parsed();
   size_t offset_temp = std::wstring::npos;
   if (!offset_for_adjustment)
     offset_for_adjustment = &offset_temp;
@@ -1374,11 +1451,11 @@
   // Special handling for view-source:.  Don't use chrome::kViewSourceScheme
   // because this library shouldn't depend on chrome.
   const char* const kViewSource = "view-source";
+  // Reject "view-source:view-source:..." to avoid deep recursion.
   const char* const kViewSourceTwice = "view-source:view-source:";
-  // Rejects view-source:view-source:... to avoid deep recursive call.
   if (url.SchemeIs(kViewSource) &&
       !StartsWithASCII(url.possibly_invalid_spec(), kViewSourceTwice, false)) {
-    return FormatViewSourceUrl(url, languages, omit_username_password,
+    return FormatViewSourceUrl(url, languages, format_types,
         unescape_rules, new_parsed, prefix_end, offset_for_adjustment);
   }
 
@@ -1395,9 +1472,22 @@
       spec.begin() + parsed.CountCharactersBefore(url_parse::Parsed::USERNAME,
                                                   true),
       std::back_inserter(url_string));
+
+  const wchar_t kHTTP[] = L"http://";
+  const char kFTP[] = "ftp.";
+  // URLFixerUpper::FixupURL() treats "ftp.foo.com" as ftp://ftp.foo.com.  This
+  // means that if we trim "http://" off a URL whose host starts with "ftp." and
+  // the user inputs this into any field subject to fixup (which is basically
+  // all input fields), the meaning would be changed.  (In fact, often the
+  // formatted URL is directly pre-filled into an input field.)  For this reason
+  // we avoid stripping "http://" in this case.
+  bool omit_http =
+      (format_types & kFormatUrlOmitHTTP) && (url_string == kHTTP) &&
+      (url.host().compare(0, arraysize(kFTP) - 1, kFTP) != 0);
+
   new_parsed->scheme = parsed.scheme;
 
-  if (omit_username_password) {
+  if ((format_types & kFormatUrlOmitUsernamePassword) != 0) {
     // Remove the username and password fields. We don't want to display those
     // to the user since they can be used for attacks,
     // e.g. "http://google.com:search@evil.ru/"
@@ -1433,14 +1523,12 @@
   } else {
     AppendFormattedComponent(spec, parsed.username, unescape_rules, &url_string,
                              &new_parsed->username, offset_for_adjustment);
-    if (parsed.password.is_valid()) {
+    if (parsed.password.is_valid())
       url_string.push_back(':');
-    }
     AppendFormattedComponent(spec, parsed.password, unescape_rules, &url_string,
                              &new_parsed->password, offset_for_adjustment);
-    if (parsed.username.is_valid() || parsed.password.is_valid()) {
+    if (parsed.username.is_valid() || parsed.password.is_valid())
       url_string.push_back('@');
-    }
   }
   if (prefix_end)
     *prefix_end = static_cast<size_t>(url_string.length());
@@ -1460,8 +1548,11 @@
   }
 
   // Path and query both get the same general unescape & convert treatment.
-  AppendFormattedComponent(spec, parsed.path, unescape_rules, &url_string,
-                           &new_parsed->path, offset_for_adjustment);
+  if (!(format_types & kFormatUrlOmitTrailingSlashOnBareHostname) ||
+      !CanStripTrailingSlash(url)) {
+    AppendFormattedComponent(spec, parsed.path, unescape_rules, &url_string,
+                             &new_parsed->path, offset_for_adjustment);
+  }
   if (parsed.query.is_valid())
     url_string.push_back('?');
   AppendFormattedComponent(spec, parsed.query, unescape_rules, &url_string,
@@ -1497,9 +1588,37 @@
     }
   }
 
+  // If we need to strip out http do it after the fact. This way we don't need
+  // to worry about how offset_for_adjustment is interpreted.
+  const size_t kHTTPSize = arraysize(kHTTP) - 1;
+  if (omit_http && !url_string.compare(0, kHTTPSize, kHTTP)) {
+    url_string = url_string.substr(kHTTPSize);
+    if (*offset_for_adjustment != std::wstring::npos) {
+      if (*offset_for_adjustment < kHTTPSize)
+        *offset_for_adjustment = std::wstring::npos;
+      else
+        *offset_for_adjustment -= kHTTPSize;
+    }
+    if (prefix_end)
+      *prefix_end -= kHTTPSize;
+
+    // Adjust new_parsed.
+    DCHECK(new_parsed->scheme.is_valid());
+    int delta = -(new_parsed->scheme.len + 3);  // +3 for ://.
+    new_parsed->scheme.reset();
+    AdjustComponents(delta, new_parsed);
+  }
+
   return url_string;
 }
 
+bool CanStripTrailingSlash(const GURL& url) {
+  // Omit the path only for standard, non-file URLs with nothing but "/" after
+  // the hostname.
+  return url.IsStandard() && !url.SchemeIsFile() && !url.has_query() &&
+      !url.has_ref() && url.path() == "/";
+}
+
 GURL SimplifyUrlForRequest(const GURL& url) {
   DCHECK(url.is_valid());
   GURL::Replacements replacements;
@@ -1538,4 +1657,269 @@
   explicitly_allowed_ports = ports;
 }
 
+enum IPv6SupportStatus {
+  IPV6_CANNOT_CREATE_SOCKETS,
+  IPV6_CAN_CREATE_SOCKETS,
+  IPV6_GETIFADDRS_FAILED,
+  IPV6_GLOBAL_ADDRESS_MISSING,
+  IPV6_GLOBAL_ADDRESS_PRESENT,
+  IPV6_INTERFACE_ARRAY_TOO_SHORT,
+  IPV6_SUPPORT_MAX  // Bounding values for enumeration.
+};
+
+static void IPv6SupportResults(IPv6SupportStatus result) {
+  static bool run_once = false;
+  if (!run_once) {
+    run_once = true;
+    UMA_HISTOGRAM_ENUMERATION("Net.IPv6Status", result, IPV6_SUPPORT_MAX);
+  } else {
+    UMA_HISTOGRAM_ENUMERATION("Net.IPv6Status_retest", result,
+                              IPV6_SUPPORT_MAX);
+  }
+}
+
+// TODO(jar): The following is a simple estimate of IPv6 support.  We may need
+// to do a test resolution, and a test connection, to REALLY verify support.
+// static
+bool IPv6Supported() {
+#if defined(OS_POSIX)
+  int test_socket = socket(AF_INET6, SOCK_STREAM, 0);
+  if (test_socket == -1) {
+    IPv6SupportResults(IPV6_CANNOT_CREATE_SOCKETS);
+    return false;
+  }
+  close(test_socket);
+
+  // Check to see if any interface has a IPv6 address.
+  struct ifaddrs* interface_addr = NULL;
+  int rv = getifaddrs(&interface_addr);
+  if (rv != 0) {
+     IPv6SupportResults(IPV6_GETIFADDRS_FAILED);
+     return true;  // Don't yet block IPv6.
+  }
+
+  bool found_ipv6 = false;
+  for (struct ifaddrs* interface = interface_addr;
+       interface != NULL;
+       interface = interface->ifa_next) {
+    if (!(IFF_UP & interface->ifa_flags))
+      continue;
+    if (IFF_LOOPBACK & interface->ifa_flags)
+      continue;
+    struct sockaddr* addr = interface->ifa_addr;
+    if (!addr)
+      continue;
+    if (addr->sa_family != AF_INET6)
+      continue;
+    // Safe cast since this is AF_INET6.
+    struct sockaddr_in6* addr_in6 =
+        reinterpret_cast<struct sockaddr_in6*>(addr);
+    struct in6_addr* sin6_addr = &addr_in6->sin6_addr;
+    if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_LINKLOCAL(sin6_addr))
+      continue;
+    found_ipv6 = true;
+    break;
+  }
+  freeifaddrs(interface_addr);
+  if (!found_ipv6) {
+    IPv6SupportResults(IPV6_GLOBAL_ADDRESS_MISSING);
+    return false;
+  }
+
+  IPv6SupportResults(IPV6_GLOBAL_ADDRESS_PRESENT);
+  return true;
+#elif defined(OS_WIN)
+  EnsureWinsockInit();
+  SOCKET test_socket = socket(AF_INET6, SOCK_STREAM, 0);
+  if (test_socket == INVALID_SOCKET) {
+    IPv6SupportResults(IPV6_CANNOT_CREATE_SOCKETS);
+    return false;
+  }
+  closesocket(test_socket);
+
+  // TODO(jar): Bug 40851: The remainder of probe is not working.
+  IPv6SupportResults(IPV6_CAN_CREATE_SOCKETS);  // Record status.
+  return true;  // Don't disable IPv6 yet.
+
+  // Check to see if any interface has a IPv6 address.
+  // Note: The original IPv6 socket can't be used here, as WSAIoctl() will fail.
+  test_socket = socket(AF_INET, SOCK_STREAM, 0);
+  DCHECK(test_socket != INVALID_SOCKET);
+  INTERFACE_INFO interfaces[128];
+  DWORD bytes_written = 0;
+  int rv = WSAIoctl(test_socket, SIO_GET_INTERFACE_LIST, NULL, 0, interfaces,
+                    sizeof(interfaces), &bytes_written, NULL, NULL);
+  closesocket(test_socket);
+
+  if (0 != rv) {
+    if (WSAGetLastError() == WSAEFAULT)
+      IPv6SupportResults(IPV6_INTERFACE_ARRAY_TOO_SHORT);
+    else
+      IPv6SupportResults(IPV6_GETIFADDRS_FAILED);
+    return true;  // Don't yet block IPv6.
+  }
+  size_t interface_count = bytes_written / sizeof(interfaces[0]);
+  for (size_t i = 0; i < interface_count; ++i) {
+    INTERFACE_INFO* interface = &interfaces[i];
+    if (!(IFF_UP & interface->iiFlags))
+      continue;
+    if (IFF_LOOPBACK & interface->iiFlags)
+      continue;
+    sockaddr* addr = &interface->iiAddress.Address;
+    if (addr->sa_family != AF_INET6)
+      continue;
+    struct in6_addr* sin6_addr = &interface->iiAddress.AddressIn6.sin6_addr;
+    if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || IN6_IS_ADDR_LINKLOCAL(sin6_addr))
+      continue;
+    IPv6SupportResults(IPV6_GLOBAL_ADDRESS_PRESENT);
+    return true;
+  }
+
+  IPv6SupportResults(IPV6_GLOBAL_ADDRESS_MISSING);
+  return false;
+#else
+  NOTIMPLEMENTED();
+  return true;
+#endif  // defined(various platforms)
+}
+
+bool ParseIPLiteralToNumber(const std::string& ip_literal,
+                            IPAddressNumber* ip_number) {
+  // |ip_literal| could be either a IPv4 or an IPv6 literal. If it contains
+  // a colon however, it must be an IPv6 address.
+  if (ip_literal.find(':') != std::string::npos) {
+    // GURL expects IPv6 hostnames to be surrounded with brackets.
+    std::string host_brackets = "[" + ip_literal + "]";
+    url_parse::Component host_comp(0, host_brackets.size());
+
+    // Try parsing the hostname as an IPv6 literal.
+    ip_number->resize(16);  // 128 bits.
+    return url_canon::IPv6AddressToNumber(host_brackets.data(),
+                                          host_comp,
+                                          &(*ip_number)[0]);
+  }
+
+  // Otherwise the string is an IPv4 address.
+  ip_number->resize(4);  // 32 bits.
+  url_parse::Component host_comp(0, ip_literal.size());
+  int num_components;
+  url_canon::CanonHostInfo::Family family = url_canon::IPv4AddressToNumber(
+      ip_literal.data(), host_comp, &(*ip_number)[0], &num_components);
+  return family == url_canon::CanonHostInfo::IPV4;
+}
+
+IPAddressNumber ConvertIPv4NumberToIPv6Number(
+    const IPAddressNumber& ipv4_number) {
+  DCHECK(ipv4_number.size() == 4);
+
+  // IPv4-mapped addresses are formed by:
+  // <80 bits of zeros>  + <16 bits of ones> + <32-bit IPv4 address>.
+  IPAddressNumber ipv6_number;
+  ipv6_number.reserve(16);
+  ipv6_number.insert(ipv6_number.end(), 10, 0);
+  ipv6_number.push_back(0xFF);
+  ipv6_number.push_back(0xFF);
+  ipv6_number.insert(ipv6_number.end(), ipv4_number.begin(), ipv4_number.end());
+  return ipv6_number;
+}
+
+bool ParseCIDRBlock(const std::string& cidr_literal,
+                    IPAddressNumber* ip_number,
+                    size_t* prefix_length_in_bits) {
+  // We expect CIDR notation to match one of these two templates:
+  //   <IPv4-literal> "/" <number of bits>
+  //   <IPv6-literal> "/" <number of bits>
+
+  std::vector<std::string> parts;
+  SplitString(cidr_literal, '/', &parts);
+  if (parts.size() != 2)
+    return false;
+
+  // Parse the IP address.
+  if (!ParseIPLiteralToNumber(parts[0], ip_number))
+    return false;
+
+  // Parse the prefix length.
+  int number_of_bits = -1;
+  if (!StringToInt(parts[1], &number_of_bits))
+    return false;
+
+  // Make sure the prefix length is in a valid range.
+  if (number_of_bits < 0 ||
+      number_of_bits > static_cast<int>(ip_number->size() * 8))
+    return false;
+
+  *prefix_length_in_bits = static_cast<size_t>(number_of_bits);
+  return true;
+}
+
+bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number,
+                           const IPAddressNumber& ip_prefix,
+                           size_t prefix_length_in_bits) {
+  // Both the input IP address and the prefix IP address should be
+  // either IPv4 or IPv6.
+  DCHECK(ip_number.size() == 4 || ip_number.size() == 16);
+  DCHECK(ip_prefix.size() == 4 || ip_prefix.size() == 16);
+
+  DCHECK_LE(prefix_length_in_bits, ip_prefix.size() * 8);
+
+  // In case we have an IPv6 / IPv4 mismatch, convert the IPv4 addresses to
+  // IPv6 addresses in order to do the comparison.
+  if (ip_number.size() != ip_prefix.size()) {
+    if (ip_number.size() == 4) {
+      return IPNumberMatchesPrefix(ConvertIPv4NumberToIPv6Number(ip_number),
+                                   ip_prefix, prefix_length_in_bits);
+    }
+    return IPNumberMatchesPrefix(ip_number,
+                                 ConvertIPv4NumberToIPv6Number(ip_prefix),
+                                 96 + prefix_length_in_bits);
+  }
+
+  // Otherwise we are comparing two IPv4 addresses, or two IPv6 addresses.
+  // Compare all the bytes that fall entirely within the prefix.
+  int num_entire_bytes_in_prefix = prefix_length_in_bits / 8;
+  for (int i = 0; i < num_entire_bytes_in_prefix; ++i) {
+    if (ip_number[i] != ip_prefix[i])
+      return false;
+  }
+
+  // In case the prefix was not a multiple of 8, there will be 1 byte
+  // which is only partially masked.
+  int remaining_bits = prefix_length_in_bits % 8;
+  if (remaining_bits != 0) {
+    unsigned char mask = 0xFF << (8 - remaining_bits);
+    int i = num_entire_bytes_in_prefix;
+    if ((ip_number[i] & mask) != (ip_prefix[i] & mask))
+      return false;
+  }
+
+  return true;
+}
+
+// Returns the port field of the sockaddr in |info|.
+uint16* GetPortFieldFromAddrinfo(const struct addrinfo* info) {
+  DCHECK(info);
+  if (info->ai_family == AF_INET) {
+    DCHECK_EQ(sizeof(sockaddr_in), info->ai_addrlen);
+    struct sockaddr_in* sockaddr =
+        reinterpret_cast<struct sockaddr_in*>(info->ai_addr);
+    return &sockaddr->sin_port;
+  } else if (info->ai_family == AF_INET6) {
+    DCHECK_EQ(sizeof(sockaddr_in6), info->ai_addrlen);
+    struct sockaddr_in6* sockaddr =
+        reinterpret_cast<struct sockaddr_in6*>(info->ai_addr);
+    return &sockaddr->sin6_port;
+  } else {
+    NOTREACHED();
+    return NULL;
+  }
+}
+
+int GetPortFromAddrinfo(const struct addrinfo* info) {
+  uint16* port_field = GetPortFieldFromAddrinfo(info);
+  if (!port_field)
+    return -1;
+  return ntohs(*port_field);
+}
+
 }  // namespace net
diff --git a/net/base/net_util.h b/net/base/net_util.h
index d9affe6..5633c11 100644
--- a/net/base/net_util.h
+++ b/net/base/net_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -13,6 +13,7 @@
 
 #include <string>
 #include <set>
+#include <vector>
 
 #include "base/basictypes.h"
 #include "base/string16.h"
@@ -36,6 +37,26 @@
 
 namespace net {
 
+// Used by FormatUrl to specify handling of certain parts of the url.
+typedef uint32 FormatUrlType;
+typedef uint32 FormatUrlTypes;
+
+// Nothing is ommitted.
+extern const FormatUrlType kFormatUrlOmitNothing;
+
+// If set, any username and password are removed.
+extern const FormatUrlType kFormatUrlOmitUsernamePassword;
+
+// If the scheme is 'http://', it's removed.
+extern const FormatUrlType kFormatUrlOmitHTTP;
+
+// Omits the path if it is just a slash and there is no query or ref.  This is
+// meaningful for non-file "standard" URLs.
+extern const FormatUrlType kFormatUrlOmitTrailingSlashOnBareHostname;
+
+// Convenience for omitting all unecessary types.
+extern const FormatUrlType kFormatUrlOmitAll;
+
 // Holds a list of ports that should be accepted despite bans.
 extern std::set<int> explicitly_allowed_ports;
 
@@ -74,6 +95,10 @@
 // Returns empty string on failure.
 std::string NetAddressToString(const struct addrinfo* net_address);
 
+// Same as NetAddressToString, but additionally includes the port number. For
+// example: "192.168.0.1:99" or "[::1]:80".
+std::string NetAddressToStringWithPort(const struct addrinfo* net_address);
+
 // Returns the hostname of the current system. Returns empty string on failure.
 std::string GetHostName();
 
@@ -83,6 +108,9 @@
                         std::wstring* username,
                         std::wstring* password);
 
+// Returns either the host from |url|, or, if the host is empty, the full spec.
+std::string GetHostOrSpecFromURL(const GURL& url);
+
 // Return the value of the HTTP response header with name 'name'.  'headers'
 // should be in the format that URLRequest::GetResponseHeaders() returns.
 // Returns the empty string if the header is not found.
@@ -171,10 +199,15 @@
 //   * Each component contains only alphanumeric characters and '-' or '_'
 //   * The last component does not begin with a digit
 //   * Optional trailing dot after last component (means "treat as FQDN")
+// If |desired_tld| is non-NULL, the host will only be considered invalid if
+// appending it as a trailing component still results in an invalid host.  This
+// helps us avoid marking as "invalid" user attempts to open "www.401k.com" by
+// typing 4-0-1-k-<ctrl>+<enter>.
 //
 // NOTE: You should only pass in hosts that have been returned from
 // CanonicalizeHost(), or you may not get accurate results.
-bool IsCanonicalizedHostCompliant(const std::string& host);
+bool IsCanonicalizedHostCompliant(const std::string& host,
+                                  const std::string& desired_tld);
 
 // Call these functions to get the html snippet for a directory listing.
 // The return values of both functions are in UTF-8.
@@ -186,10 +219,12 @@
 // Currently, it's a script tag containing a call to a Javascript function
 // |addRow|.
 //
-// Its 1st parameter is derived from |name| and is the Javascript-string
-// escaped form of |name| (i.e \uXXXX). The 2nd parameter is the url-escaped
-// |raw_bytes| if it's not empty. If empty, the 2nd parameter is the
-// url-escaped |name| in UTF-8.
+// |name| is the file name to be displayed. |raw_bytes| will be used
+// as the actual target of the link (so for example, ftp links should use
+// server's encoding). If |raw_bytes| is an empty string, UTF-8 encoded |name|
+// will be used.
+//
+// Both |name| and |raw_bytes| are escaped internally.
 std::string GetDirectoryListingEntry(const string16& name,
                                      const std::string& raw_bytes,
                                      bool is_dir, int64 size,
@@ -240,13 +275,12 @@
                          size_t* offset_for_adjustment);
 
 // Creates a string representation of |url|. The IDN host name may be in Unicode
-// if |languages| accepts the Unicode representation. If
-// |omit_username_password| is true, any username and password are removed.
-// |unescape_rules| defines how to clean the URL for human readability.
-// You will generally want |UnescapeRule::SPACES| for display to the user if you
-// can handle spaces, or |UnescapeRule::NORMAL| if not. If the path part and the
-// query part seem to be encoded in %-encoded UTF-8, decodes %-encoding and
-// UTF-8.
+// if |languages| accepts the Unicode representation. |format_type| is a bitmask
+// of FormatUrlTypes, see it for details. |unescape_rules| defines how to clean
+// the URL for human readability. You will generally want |UnescapeRule::SPACES|
+// for display to the user if you can handle spaces, or |UnescapeRule::NORMAL|
+// if not. If the path part and the query part seem to be encoded in %-encoded
+// UTF-8, decodes %-encoding and UTF-8.
 //
 // The last three parameters may be NULL.
 // |new_parsed| will be set to the parsing parameters of the resultant URL.
@@ -262,20 +296,25 @@
 // std::wstring::npos.
 std::wstring FormatUrl(const GURL& url,
                        const std::wstring& languages,
-                       bool omit_username_password,
+                       FormatUrlTypes format_types,
                        UnescapeRule::Type unescape_rules,
                        url_parse::Parsed* new_parsed,
                        size_t* prefix_end,
                        size_t* offset_for_adjustment);
 
-// Creates a string representation of |url| for display to the user.
-// This is a shorthand of the above function with omit_username_password=true,
-// unescape=SPACES, new_parsed=NULL, and prefix_end=NULL.
+// This is a convenience function for FormatUrl() with
+// format_types = kFormatUrlOmitAll and unescape = SPACES.  This is the typical
+// set of flags for "URLs to display to the user".  You should be cautious about
+// using this for URLs which will be parsed or sent to other applications.
 inline std::wstring FormatUrl(const GURL& url, const std::wstring& languages) {
-  return FormatUrl(url, languages, true, UnescapeRule::SPACES, NULL, NULL,
-                   NULL);
+  return FormatUrl(url, languages, kFormatUrlOmitAll, UnescapeRule::SPACES,
+                   NULL, NULL, NULL);
 }
 
+// Returns whether FormatUrl() would strip a trailing slash from |url|, given a
+// format flag including kFormatUrlOmitTrailingSlashOnBareHostname.
+bool CanStripTrailingSlash(const GURL& url);
+
 // Strip the portions of |url| that aren't core to the network request.
 //   - user name / password
 //   - reference section
@@ -283,6 +322,62 @@
 
 void SetExplicitlyAllowedPorts(const std::wstring& allowed_ports);
 
+// Perform a simplistic test to see if IPv6 is supported by trying to create an
+// IPv6 socket.
+// TODO(jar): Make test more in-depth as needed.
+bool IPv6Supported();
+
+// IPAddressNumber is used to represent an IP address's numeric value as an
+// array of bytes, from most significant to least significant. This is the
+// network byte ordering.
+//
+// IPv4 addresses will have length 4, whereas IPv6 address will have length 16.
+typedef std::vector<unsigned char> IPAddressNumber;
+
+// Parses an IP address literal (either IPv4 or IPv6) to its numeric value.
+// Returns true on success and fills |ip_number| with the numeric value.
+bool ParseIPLiteralToNumber(const std::string& ip_literal,
+                            IPAddressNumber* ip_number);
+
+// Converts an IPv4 address to an IPv4-mapped IPv6 address.
+// For example 192.168.0.1 would be converted to ::ffff:192.168.0.1.
+IPAddressNumber ConvertIPv4NumberToIPv6Number(
+    const IPAddressNumber& ipv4_number);
+
+// Parses an IP block specifier from CIDR notation to an
+// (IP address, prefix length) pair. Returns true on success and fills
+// |*ip_number| with the numeric value of the IP address and sets
+// |*prefix_length_in_bits| with the length of the prefix.
+//
+// CIDR notation literals can use either IPv4 or IPv6 literals. Some examples:
+//
+//    10.10.3.1/20
+//    a:b:c::/46
+//    ::1/128
+bool ParseCIDRBlock(const std::string& cidr_literal,
+                    IPAddressNumber* ip_number,
+                    size_t* prefix_length_in_bits);
+
+// Compares an IP address to see if it falls within the specified IP block.
+// Returns true if it does, false otherwise.
+//
+// The IP block is given by (|ip_prefix|, |prefix_length_in_bits|) -- any
+// IP address whose |prefix_length_in_bits| most significant bits match
+// |ip_prefix| will be matched.
+//
+// In cases when an IPv4 address is being compared to an IPv6 address prefix
+// and vice versa, the IPv4 addresses will be converted to IPv4-mapped
+// (IPv6) addresses.
+bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number,
+                           const IPAddressNumber& ip_prefix,
+                           size_t prefix_length_in_bits);
+
+// Returns the port field of the sockaddr in |info|.
+uint16* GetPortFieldFromAddrinfo(const struct addrinfo* info);
+
+// Returns the value of |info's| port (in host byte ordering).
+int GetPortFromAddrinfo(const struct addrinfo* info);
+
 }  // namespace net
 
 #endif  // NET_BASE_NET_UTIL_H_
diff --git a/net/base/net_util_unittest.cc b/net/base/net_util_unittest.cc
index 9265f0a..461b6d3 100644
--- a/net/base/net_util_unittest.cc
+++ b/net/base/net_util_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -8,6 +8,7 @@
 #include "base/format_macros.h"
 #include "base/string_util.h"
 #include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
 #include "base/time.h"
 #include "googleurl/src/gurl.h"
 #include "net/base/sys_addrinfo.h"
@@ -347,6 +348,7 @@
 
 struct CompliantHostCase {
   const char* host;
+  const char* desired_tld;
   bool expected_output;
 };
 
@@ -362,7 +364,7 @@
   const char* description;
   const char* input;
   const std::wstring languages;
-  bool omit;
+  net::FormatUrlTypes format_types;
   UnescapeRule::Type escape_rules;
   const std::wstring output;
   size_t prefix_len;
@@ -371,7 +373,7 @@
 // Returns an addrinfo for the given 32-bit address (IPv4.)
 // The result lives in static storage, so don't delete it.
 // |bytes| should be an array of length 4.
-const struct addrinfo* GetIPv4Address(const uint8* bytes) {
+const struct addrinfo* GetIPv4Address(const uint8* bytes, int port) {
   static struct addrinfo static_ai;
   static struct sockaddr_in static_addr4;
 
@@ -384,7 +386,7 @@
 
   struct sockaddr_in* addr4 = &static_addr4;
   memset(addr4, 0, sizeof(static_addr4));
-  addr4->sin_port = htons(80);
+  addr4->sin_port = htons(port);
   addr4->sin_family = ai->ai_family;
   memcpy(&addr4->sin_addr, bytes, 4);
 
@@ -395,7 +397,7 @@
 // Returns a addrinfo for the given 128-bit address (IPv6.)
 // The result lives in static storage, so don't delete it.
 // |bytes| should be an array of length 16.
-const struct addrinfo* GetIPv6Address(const uint8* bytes) {
+const struct addrinfo* GetIPv6Address(const uint8* bytes, int port) {
   static struct addrinfo static_ai;
   static struct sockaddr_in6 static_addr6;
 
@@ -408,7 +410,7 @@
 
   struct sockaddr_in6* addr6 = &static_addr6;
   memset(addr6, 0, sizeof(static_addr6));
-  addr6->sin6_port = htons(80);
+  addr6->sin6_port = htons(port);
   addr6->sin6_family = ai->ai_family;
   memcpy(&addr6->sin6_addr, bytes, 16);
 
@@ -416,7 +418,6 @@
   return ai;
 }
 
-
 // A helper for IDN*{Fast,Slow}.
 // Append "::<language list>" to |expected| and |actual| to make it
 // easy to tell which sub-case fails without debugging.
@@ -429,6 +430,17 @@
   actual->append(languages);
 }
 
+// Helper to strignize an IP number (used to define expectations).
+std::string DumpIPNumber(const net::IPAddressNumber& v) {
+  std::string out;
+  for (size_t i = 0; i < v.size(); ++i) {
+    if (i != 0)
+      out.append(",");
+    out.append(IntToString(static_cast<int>(v[i])));
+  }
+  return out;
+}
+
 }  // anonymous namespace
 
 TEST(NetUtilTest, FileURLConversion) {
@@ -552,6 +564,11 @@
       L"username",
       L"p@ssword",
     },
+    { // Special URL characters should be unescaped.
+      "http://username:p%3fa%26s%2fs%23@google.com",
+      L"username",
+      L"p?a&s/s#",
+    },
     { // Username contains %20.
       "http://use rname:password@google.com",
       L"use rname",
@@ -837,30 +854,35 @@
 
 TEST(NetUtilTest, CompliantHost) {
   const CompliantHostCase compliant_host_cases[] = {
-    {"", false},
-    {"a", true},
-    {"-", false},
-    {".", false},
-    {"a.", true},
-    {"a.a", true},
-    {"9.a", true},
-    {"a.9", false},
-    {"_9a", false},
-    {"a.a9", true},
-    {"a.9a", false},
-    {"a+9a", false},
-    {"1-.a-b", false},
-    {"1-2.a_b", true},
-    {"a.b.c.d.e", true},
-    {"1.2.3.4.e", true},
-    {"a.b.c.d.5", false},
-    {"1.2.3.4.e.", true},
-    {"a.b.c.d.5.", false},
+    {"", "", false},
+    {"a", "", true},
+    {"-", "", false},
+    {".", "", false},
+    {"9", "", false},
+    {"9", "a", true},
+    {"9a", "", false},
+    {"9a", "a", true},
+    {"a.", "", true},
+    {"a.a", "", true},
+    {"9.a", "", true},
+    {"a.9", "", false},
+    {"_9a", "", false},
+    {"a.a9", "", true},
+    {"a.9a", "", false},
+    {"a+9a", "", false},
+    {"1-.a-b", "", false},
+    {"1-2.a_b", "", true},
+    {"a.b.c.d.e", "", true},
+    {"1.2.3.4.e", "", true},
+    {"a.b.c.d.5", "", false},
+    {"1.2.3.4.e.", "", true},
+    {"a.b.c.d.5.", "", false},
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(compliant_host_cases); ++i) {
     EXPECT_EQ(compliant_host_cases[i].expected_output,
-              net::IsCanonicalizedHostCompliant(compliant_host_cases[i].host));
+        net::IsCanonicalizedHostCompliant(compliant_host_cases[i].host,
+                                          compliant_host_cases[i].desired_tld));
   }
 }
 
@@ -1037,6 +1059,23 @@
     "",
     L"",
     L"test"},
+    // The filename encoding is specified by the referrer charset.
+    {"http://example.com/V%FDvojov%E1%20psychologie.doc",
+     "",
+     "iso-8859-1",
+     L"",
+     L"V\u00fdvojov\u00e1 psychologie.doc"},
+    // The filename encoding doesn't match the referrer charset, the
+    // system charset, or UTF-8.
+    // TODO(jshin): we need to handle this case.
+#if 0
+    {"http://example.com/V%FDvojov%E1%20psychologie.doc",
+     "",
+     "utf-8",
+     L"",
+     L"V\u00fdvojov\u00e1 psychologie.doc",
+    },
+#endif
   };
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
 #if defined(OS_WIN)
@@ -1220,7 +1259,7 @@
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
-    const addrinfo* ai = GetIPv4Address(tests[i].addr);
+    const addrinfo* ai = GetIPv4Address(tests[i].addr, 80);
     std::string result = net::NetAddressToString(ai);
     EXPECT_EQ(std::string(tests[i].result), result);
   }
@@ -1237,7 +1276,7 @@
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
-    const addrinfo* ai = GetIPv6Address(tests[i].addr);
+    const addrinfo* ai = GetIPv6Address(tests[i].addr, 80);
     std::string result = net::NetAddressToString(ai);
     // Allow NetAddressToString() to fail, in case the system doesn't
     // support IPv6.
@@ -1246,6 +1285,26 @@
   }
 }
 
+TEST(NetUtilTest, NetAddressToStringWithPort_IPv4) {
+  uint8 addr[] = {127, 0, 0, 1};
+  const addrinfo* ai = GetIPv4Address(addr, 166);
+  std::string result = net::NetAddressToStringWithPort(ai);
+  EXPECT_EQ("127.0.0.1:166", result);
+}
+
+TEST(NetUtilTest, NetAddressToStringWithPort_IPv6) {
+  uint8 addr[] = {
+      0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xFE, 0xDC, 0xBA,
+      0x98, 0x76, 0x54, 0x32, 0x10
+  };
+  const addrinfo* ai = GetIPv6Address(addr, 361);
+  std::string result = net::NetAddressToStringWithPort(ai);
+
+  // May fail on systems that don't support IPv6.
+  if (!result.empty())
+    EXPECT_EQ("[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:361", result);
+}
+
 TEST(NetUtilTest, GetHostName) {
   // We can't check the result of GetHostName() directly, since the result
   // will differ across machines. Our goal here is to simply exercise the
@@ -1255,74 +1314,78 @@
 }
 
 TEST(NetUtilTest, FormatUrl) {
+  net::FormatUrlTypes default_format_type = net::kFormatUrlOmitUsernamePassword;
   const UrlTestData tests[] = {
-    {"Empty URL", "", L"", true, UnescapeRule::NORMAL, L"", 0},
+    {"Empty URL", "", L"", default_format_type, UnescapeRule::NORMAL, L"", 0},
 
     {"Simple URL",
-     "http://www.google.com/", L"", true, UnescapeRule::NORMAL,
+     "http://www.google.com/", L"", default_format_type, UnescapeRule::NORMAL,
      L"http://www.google.com/", 7},
 
     {"With a port number and a reference",
-     "http://www.google.com:8080/#\xE3\x82\xB0", L"", true,
+     "http://www.google.com:8080/#\xE3\x82\xB0", L"", default_format_type,
      UnescapeRule::NORMAL,
      L"http://www.google.com:8080/#\x30B0", 7},
 
     // -------- IDN tests --------
     {"Japanese IDN with ja",
-     "http://xn--l8jvb1ey91xtjb.jp", L"ja", true, UnescapeRule::NORMAL,
-     L"http://\x671d\x65e5\x3042\x3055\x3072.jp/", 7},
+     "http://xn--l8jvb1ey91xtjb.jp", L"ja", default_format_type,
+     UnescapeRule::NORMAL, L"http://\x671d\x65e5\x3042\x3055\x3072.jp/", 7},
 
     {"Japanese IDN with en",
-     "http://xn--l8jvb1ey91xtjb.jp", L"en", true, UnescapeRule::NORMAL,
-     L"http://xn--l8jvb1ey91xtjb.jp/", 7},
+     "http://xn--l8jvb1ey91xtjb.jp", L"en", default_format_type,
+     UnescapeRule::NORMAL, L"http://xn--l8jvb1ey91xtjb.jp/", 7},
 
     {"Japanese IDN without any languages",
-     "http://xn--l8jvb1ey91xtjb.jp", L"", true, UnescapeRule::NORMAL,
+     "http://xn--l8jvb1ey91xtjb.jp", L"", default_format_type,
+     UnescapeRule::NORMAL,
      // Single script is safe for empty languages.
      L"http://\x671d\x65e5\x3042\x3055\x3072.jp/", 7},
 
     {"mailto: with Japanese IDN",
-     "mailto:foo@xn--l8jvb1ey91xtjb.jp", L"ja", true, UnescapeRule::NORMAL,
+     "mailto:foo@xn--l8jvb1ey91xtjb.jp", L"ja", default_format_type,
+     UnescapeRule::NORMAL,
      // GURL doesn't assume an email address's domain part as a host name.
      L"mailto:foo@xn--l8jvb1ey91xtjb.jp", 7},
 
     {"file: with Japanese IDN",
-     "file://xn--l8jvb1ey91xtjb.jp/config.sys", L"ja", true,
+     "file://xn--l8jvb1ey91xtjb.jp/config.sys", L"ja", default_format_type,
      UnescapeRule::NORMAL,
      L"file://\x671d\x65e5\x3042\x3055\x3072.jp/config.sys", 7},
 
     {"ftp: with Japanese IDN",
-     "ftp://xn--l8jvb1ey91xtjb.jp/config.sys", L"ja", true,
+     "ftp://xn--l8jvb1ey91xtjb.jp/config.sys", L"ja", default_format_type,
      UnescapeRule::NORMAL,
      L"ftp://\x671d\x65e5\x3042\x3055\x3072.jp/config.sys", 6},
 
     // -------- omit_username_password flag tests --------
     {"With username and password, omit_username_password=false",
-     "http://user:passwd@example.com/foo", L"", false, UnescapeRule::NORMAL,
+     "http://user:passwd@example.com/foo", L"",
+     net::kFormatUrlOmitNothing, UnescapeRule::NORMAL,
      L"http://user:passwd@example.com/foo", 19},
 
     {"With username and password, omit_username_password=true",
-     "http://user:passwd@example.com/foo", L"", true, UnescapeRule::NORMAL,
-     L"http://example.com/foo", 7},
+     "http://user:passwd@example.com/foo", L"", default_format_type,
+     UnescapeRule::NORMAL, L"http://example.com/foo", 7},
 
     {"With username and no password",
-     "http://user@example.com/foo", L"", true, UnescapeRule::NORMAL,
-     L"http://example.com/foo", 7},
+     "http://user@example.com/foo", L"", default_format_type,
+     UnescapeRule::NORMAL, L"http://example.com/foo", 7},
 
     {"Just '@' without username and password",
-     "http://@example.com/foo", L"", true, UnescapeRule::NORMAL,
+     "http://@example.com/foo", L"", default_format_type, UnescapeRule::NORMAL,
      L"http://example.com/foo", 7},
 
     // GURL doesn't think local-part of an email address is username for URL.
     {"mailto:, omit_username_password=true",
-     "mailto:foo@example.com", L"", true, UnescapeRule::NORMAL,
+     "mailto:foo@example.com", L"", default_format_type, UnescapeRule::NORMAL,
      L"mailto:foo@example.com", 7},
 
     // -------- unescape flag tests --------
     {"Do not unescape",
      "http://%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB.jp/"
      "%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
-     "?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB", L"en", true,
+     "?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB", L"en", default_format_type,
      UnescapeRule::NONE,
      // GURL parses %-encoded hostnames into Punycode.
      L"http://xn--qcka1pmc.jp/%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
@@ -1331,38 +1394,98 @@
     {"Unescape normally",
      "http://%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB.jp/"
      "%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"
-     "?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB", L"en", true,
+     "?q=%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB", L"en", default_format_type,
      UnescapeRule::NORMAL,
      L"http://xn--qcka1pmc.jp/\x30B0\x30FC\x30B0\x30EB"
      L"?q=\x30B0\x30FC\x30B0\x30EB", 7},
 
     {"Unescape normally including unescape spaces",
-     "http://www.google.com/search?q=Hello%20World", L"en", true,
-     UnescapeRule::SPACES,
-     L"http://www.google.com/search?q=Hello World", 7},
+     "http://www.google.com/search?q=Hello%20World", L"en", default_format_type,
+     UnescapeRule::SPACES, L"http://www.google.com/search?q=Hello World", 7},
 
     /*
     {"unescape=true with some special characters",
-    "http://user%3A:%40passwd@example.com/foo%3Fbar?q=b%26z", L"", false, true,
+    "http://user%3A:%40passwd@example.com/foo%3Fbar?q=b%26z", L"",
+    net::kFormatUrlOmitNothing, UnescapeRule::NORMAL,
     L"http://user%3A:%40passwd@example.com/foo%3Fbar?q=b%26z", 25},
     */
     // Disabled: the resultant URL becomes "...user%253A:%2540passwd...".
 
+    // -------- omit http: --------
+    {"omit http with user name",
+     "http://user@example.com/foo", L"", net::kFormatUrlOmitAll,
+     UnescapeRule::NORMAL, L"example.com/foo", 0},
+
+    {"omit http",
+     "http://www.google.com/", L"en", net::kFormatUrlOmitHTTP,
+     UnescapeRule::NORMAL, L"www.google.com/",
+     0},
+
+    {"omit http with https",
+     "https://www.google.com/", L"en", net::kFormatUrlOmitHTTP,
+     UnescapeRule::NORMAL, L"https://www.google.com/",
+     8},
+
+    {"omit http starts with ftp.",
+     "http://ftp.google.com/", L"en", net::kFormatUrlOmitHTTP,
+     UnescapeRule::NORMAL, L"http://ftp.google.com/",
+     7},
+
+    // -------- omit trailing slash on bare hostname --------
+    {"omit slash when it's the entire path",
+     "http://www.google.com/", L"en",
+     net::kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
+     L"http://www.google.com", 7},
+    {"omit slash when there's a ref",
+     "http://www.google.com/#ref", L"en",
+     net::kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
+     L"http://www.google.com/#ref", 7},
+    {"omit slash when there's a query",
+     "http://www.google.com/?", L"en",
+     net::kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
+     L"http://www.google.com/?", 7},
+    {"omit slash when it's not the entire path",
+     "http://www.google.com/foo", L"en",
+     net::kFormatUrlOmitTrailingSlashOnBareHostname, UnescapeRule::NORMAL,
+     L"http://www.google.com/foo", 7},
+    {"omit slash for nonstandard URLs",
+     "data:/", L"en", net::kFormatUrlOmitTrailingSlashOnBareHostname,
+     UnescapeRule::NORMAL, L"data:/", 5},
+    {"omit slash for file URLs",
+     "file:///", L"en", net::kFormatUrlOmitTrailingSlashOnBareHostname,
+     UnescapeRule::NORMAL, L"file:///", 7},
+
     // -------- view-source: --------
     {"view-source",
-     "view-source:http://xn--qcka1pmc.jp/", L"ja", true, UnescapeRule::NORMAL,
-     L"view-source:http://\x30B0\x30FC\x30B0\x30EB.jp/", 12 + 7},
+     "view-source:http://xn--qcka1pmc.jp/", L"ja", default_format_type,
+     UnescapeRule::NORMAL, L"view-source:http://\x30B0\x30FC\x30B0\x30EB.jp/",
+     19},
 
     {"view-source of view-source",
-     "view-source:view-source:http://xn--qcka1pmc.jp/", L"ja", true,
-     UnescapeRule::NORMAL,
+     "view-source:view-source:http://xn--qcka1pmc.jp/", L"ja",
+     default_format_type, UnescapeRule::NORMAL,
      L"view-source:view-source:http://xn--qcka1pmc.jp/", 12},
+
+    // view-source should omit http and trailing slash where non-view-source
+    // would.
+    {"view-source omit http",
+     "view-source:http://a.b/c", L"en", net::kFormatUrlOmitAll,
+     UnescapeRule::NORMAL, L"view-source:a.b/c",
+     12},
+    {"view-source omit http starts with ftp.",
+     "view-source:http://ftp.b/c", L"en", net::kFormatUrlOmitAll,
+     UnescapeRule::NORMAL, L"view-source:http://ftp.b/c",
+     19},
+    {"view-source omit slash when it's the entire path",
+     "view-source:http://a.b/", L"en", net::kFormatUrlOmitAll,
+     UnescapeRule::NORMAL, L"view-source:a.b",
+     12},
   };
 
   for (size_t i = 0; i < arraysize(tests); ++i) {
     size_t prefix_len;
     std::wstring formatted = net::FormatUrl(
-        GURL(tests[i].input), tests[i].languages, tests[i].omit,
+        GURL(tests[i].input), tests[i].languages, tests[i].format_types,
         tests[i].escape_rules, NULL, &prefix_len, NULL);
     EXPECT_EQ(tests[i].output, formatted) << tests[i].description;
     EXPECT_EQ(tests[i].prefix_len, prefix_len) << tests[i].description;
@@ -1375,7 +1498,8 @@
   std::wstring formatted = net::FormatUrl(
       GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
            "%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
-      L"ja", false, UnescapeRule::NONE, &parsed, NULL, NULL);
+      L"ja", net::kFormatUrlOmitNothing, UnescapeRule::NONE, &parsed, NULL,
+      NULL);
   EXPECT_EQ(L"http://%E3%82%B0:%E3%83%BC@\x30B0\x30FC\x30B0\x30EB.jp:8080"
       L"/%E3%82%B0/?q=%E3%82%B0#\x30B0", formatted);
   EXPECT_EQ(L"%E3%82%B0",
@@ -1395,7 +1519,8 @@
   formatted = net::FormatUrl(
       GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
            "%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
-      L"ja", false, UnescapeRule::NORMAL, &parsed, NULL, NULL);
+      L"ja", net::kFormatUrlOmitNothing, UnescapeRule::NORMAL, &parsed, NULL,
+      NULL);
   EXPECT_EQ(L"http://\x30B0:\x30FC@\x30B0\x30FC\x30B0\x30EB.jp:8080"
       L"/\x30B0/?q=\x30B0#\x30B0", formatted);
   EXPECT_EQ(L"\x30B0",
@@ -1414,7 +1539,8 @@
   formatted = net::FormatUrl(
       GURL("http://\xE3\x82\xB0:\xE3\x83\xBC@xn--qcka1pmc.jp:8080/"
            "%E3%82%B0/?q=%E3%82%B0#\xE3\x82\xB0"),
-      L"ja", true, UnescapeRule::NORMAL, &parsed, NULL, NULL);
+      L"ja", net::kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
+      &parsed, NULL, NULL);
   EXPECT_EQ(L"http://\x30B0\x30FC\x30B0\x30EB.jp:8080"
       L"/\x30B0/?q=\x30B0#\x30B0", formatted);
   EXPECT_FALSE(parsed.username.is_valid());
@@ -1430,7 +1556,8 @@
   // View-source case.
   formatted = net::FormatUrl(
       GURL("view-source:http://user:passwd@host:81/path?query#ref"),
-      L"", true, UnescapeRule::NORMAL, &parsed, NULL, NULL);
+      L"", net::kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL, &parsed,
+      NULL, NULL);
   EXPECT_EQ(L"view-source:http://host:81/path?query#ref", formatted);
   EXPECT_EQ(L"view-source:http",
       formatted.substr(parsed.scheme.begin, parsed.scheme.len));
@@ -1441,6 +1568,50 @@
   EXPECT_EQ(L"/path", formatted.substr(parsed.path.begin, parsed.path.len));
   EXPECT_EQ(L"query", formatted.substr(parsed.query.begin, parsed.query.len));
   EXPECT_EQ(L"ref", formatted.substr(parsed.ref.begin, parsed.ref.len));
+
+  // omit http case.
+  formatted = net::FormatUrl(
+      GURL("http://host:8000/a?b=c#d"),
+      L"", net::kFormatUrlOmitHTTP, UnescapeRule::NORMAL, &parsed, NULL, NULL);
+  EXPECT_EQ(L"host:8000/a?b=c#d", formatted);
+  EXPECT_FALSE(parsed.scheme.is_valid());
+  EXPECT_FALSE(parsed.username.is_valid());
+  EXPECT_FALSE(parsed.password.is_valid());
+  EXPECT_EQ(L"host", formatted.substr(parsed.host.begin, parsed.host.len));
+  EXPECT_EQ(L"8000", formatted.substr(parsed.port.begin, parsed.port.len));
+  EXPECT_EQ(L"/a", formatted.substr(parsed.path.begin, parsed.path.len));
+  EXPECT_EQ(L"b=c", formatted.substr(parsed.query.begin, parsed.query.len));
+  EXPECT_EQ(L"d", formatted.substr(parsed.ref.begin, parsed.ref.len));
+
+  // omit http starts with ftp case.
+  formatted = net::FormatUrl(
+      GURL("http://ftp.host:8000/a?b=c#d"),
+      L"", net::kFormatUrlOmitHTTP, UnescapeRule::NORMAL, &parsed, NULL, NULL);
+  EXPECT_EQ(L"http://ftp.host:8000/a?b=c#d", formatted);
+  EXPECT_TRUE(parsed.scheme.is_valid());
+  EXPECT_FALSE(parsed.username.is_valid());
+  EXPECT_FALSE(parsed.password.is_valid());
+  EXPECT_EQ(L"http", formatted.substr(parsed.scheme.begin, parsed.scheme.len));
+  EXPECT_EQ(L"ftp.host", formatted.substr(parsed.host.begin, parsed.host.len));
+  EXPECT_EQ(L"8000", formatted.substr(parsed.port.begin, parsed.port.len));
+  EXPECT_EQ(L"/a", formatted.substr(parsed.path.begin, parsed.path.len));
+  EXPECT_EQ(L"b=c", formatted.substr(parsed.query.begin, parsed.query.len));
+  EXPECT_EQ(L"d", formatted.substr(parsed.ref.begin, parsed.ref.len));
+
+  // omit http starts with 'f' case.
+  formatted = net::FormatUrl(
+      GURL("http://f/"),
+      L"", net::kFormatUrlOmitHTTP, UnescapeRule::NORMAL, &parsed, NULL, NULL);
+  EXPECT_EQ(L"f/", formatted);
+  EXPECT_FALSE(parsed.scheme.is_valid());
+  EXPECT_FALSE(parsed.username.is_valid());
+  EXPECT_FALSE(parsed.password.is_valid());
+  EXPECT_FALSE(parsed.port.is_valid());
+  EXPECT_TRUE(parsed.path.is_valid());
+  EXPECT_FALSE(parsed.query.is_valid());
+  EXPECT_FALSE(parsed.ref.is_valid());
+  EXPECT_EQ(L"f", formatted.substr(parsed.host.begin, parsed.host.len));
+  EXPECT_EQ(L"/", formatted.substr(parsed.path.begin, parsed.path.len));
 }
 
 TEST(NetUtilTest, FormatUrlAdjustOffset) {
@@ -1460,8 +1631,9 @@
   };
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(basic_cases); ++i) {
     size_t offset = basic_cases[i].input_offset;
-    net::FormatUrl(GURL("http://www.google.com/foo/"), L"en", true,
-                   UnescapeRule::NORMAL, NULL, NULL, &offset);
+    net::FormatUrl(GURL("http://www.google.com/foo/"), L"en",
+                   net::kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
+                   NULL, NULL, &offset);
     EXPECT_EQ(basic_cases[i].output_offset, offset);
   }
 
@@ -1483,8 +1655,9 @@
   };
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(omit_auth_cases); ++i) {
     size_t offset = omit_auth_cases[i].input_offset;
-    net::FormatUrl(GURL(omit_auth_cases[i].input_url), L"en", true,
-                   UnescapeRule::NORMAL, NULL, NULL, &offset);
+    net::FormatUrl(GURL(omit_auth_cases[i].input_url), L"en",
+                   net::kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
+                   NULL, NULL, &offset);
     EXPECT_EQ(omit_auth_cases[i].output_offset, offset);
   }
 
@@ -1502,8 +1675,9 @@
   };
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(view_source_cases); ++i) {
     size_t offset = view_source_cases[i].input_offset;
-    net::FormatUrl(GURL("view-source:http://foo@www.google.com/"), L"en", true,
-                   UnescapeRule::NORMAL, NULL, NULL, &offset);
+    net::FormatUrl(GURL("view-source:http://foo@www.google.com/"), L"en",
+                   net::kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
+                   NULL, NULL, &offset);
     EXPECT_EQ(view_source_cases[i].output_offset, offset);
   }
 
@@ -1517,8 +1691,9 @@
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(idn_hostname_cases); ++i) {
     size_t offset = idn_hostname_cases[i].input_offset;
     // "http://\x671d\x65e5\x3042\x3055\x3072.jp/foo/"
-    net::FormatUrl(GURL("http://xn--l8jvb1ey91xtjb.jp/foo/"), L"ja", true,
-                   UnescapeRule::NORMAL, NULL, NULL, &offset);
+    net::FormatUrl(GURL("http://xn--l8jvb1ey91xtjb.jp/foo/"), L"ja",
+                   net::kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL,
+                   NULL, NULL, &offset);
     EXPECT_EQ(idn_hostname_cases[i].output_offset, offset);
   }
 
@@ -1539,7 +1714,8 @@
     // "http://www.google.com/foo bar/\x30B0\x30FC\x30B0\x30EB"
     net::FormatUrl(GURL(
         "http://www.google.com/foo%20bar/%E3%82%B0%E3%83%BC%E3%82%B0%E3%83%AB"),
-        L"en", true, UnescapeRule::SPACES, NULL, NULL, &offset);
+        L"en", net::kFormatUrlOmitUsernamePassword, UnescapeRule::SPACES, NULL,
+        NULL, &offset);
     EXPECT_EQ(unescape_cases[i].output_offset, offset);
   }
 
@@ -1556,9 +1732,48 @@
     // "http://www.google.com/foo.html#\x30B0\x30B0z"
     net::FormatUrl(GURL(
         "http://www.google.com/foo.html#\xE3\x82\xB0\xE3\x82\xB0z"), L"en",
-        true, UnescapeRule::NORMAL, NULL, NULL, &offset);
+        net::kFormatUrlOmitUsernamePassword, UnescapeRule::NORMAL, NULL, NULL,
+        &offset);
     EXPECT_EQ(ref_cases[i].output_offset, offset);
   }
+
+  const AdjustOffsetCase omit_http_cases[] = {
+    {0, std::wstring::npos},
+    {3, std::wstring::npos},
+    {7, 0},
+    {8, 1},
+  };
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(omit_http_cases); ++i) {
+    size_t offset = omit_http_cases[i].input_offset;
+    net::FormatUrl(GURL("http://www.google.com"), L"en",
+        net::kFormatUrlOmitHTTP, UnescapeRule::NORMAL, NULL, NULL, &offset);
+    EXPECT_EQ(omit_http_cases[i].output_offset, offset);
+  }
+
+  const AdjustOffsetCase omit_http_start_with_ftp[] = {
+    {0, 0},
+    {3, 3},
+    {8, 8},
+  };
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(omit_http_start_with_ftp); ++i) {
+    size_t offset = omit_http_start_with_ftp[i].input_offset;
+    net::FormatUrl(GURL("http://ftp.google.com"), L"en",
+        net::kFormatUrlOmitHTTP, UnescapeRule::NORMAL, NULL, NULL, &offset);
+    EXPECT_EQ(omit_http_start_with_ftp[i].output_offset, offset);
+  }
+
+  const AdjustOffsetCase omit_all_cases[] = {
+    {12, 0},
+    {13, 1},
+    {0, std::wstring::npos},
+    {3, std::wstring::npos},
+  };
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(omit_all_cases); ++i) {
+    size_t offset = omit_all_cases[i].input_offset;
+    net::FormatUrl(GURL("http://user@foo.com/"), L"en", net::kFormatUrlOmitAll,
+                   UnescapeRule::NORMAL, NULL, NULL, &offset);
+    EXPECT_EQ(omit_all_cases[i].output_offset, offset);
+  }
 }
 
 TEST(NetUtilTest, SimplifyUrlForRequest) {
@@ -1592,9 +1807,9 @@
       "ftp://user:pass@google.com:80/sup?yo#X#X",
       "ftp://google.com:80/sup?yo",
     },
-    { // Try an standard URL with unknow scheme.
+    { // Try an nonstandard URL
       "foobar://user:pass@google.com:80/sup?yo#X#X",
-      "foobar://google.com:80/sup?yo",
+      "foobar://user:pass@google.com:80/sup?yo#X#X",
     },
   };
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
@@ -1619,3 +1834,173 @@
     EXPECT_EQ(i, net::explicitly_allowed_ports.size());
   }
 }
+
+TEST(NetUtilTest, GetHostOrSpecFromURL) {
+  EXPECT_EQ("example.com",
+            net::GetHostOrSpecFromURL(GURL("http://example.com/test")));
+  EXPECT_EQ("example.com",
+            net::GetHostOrSpecFromURL(GURL("http://example.com./test")));
+  EXPECT_EQ("file:///tmp/test.html",
+            net::GetHostOrSpecFromURL(GURL("file:///tmp/test.html")));
+}
+
+// Test that invalid IP literals fail to parse.
+TEST(NetUtilTest, ParseIPLiteralToNumber_FailParse) {
+  net::IPAddressNumber number;
+
+  EXPECT_FALSE(net::ParseIPLiteralToNumber("bad value", &number));
+  EXPECT_FALSE(net::ParseIPLiteralToNumber("bad:value", &number));
+  EXPECT_FALSE(net::ParseIPLiteralToNumber("", &number));
+  EXPECT_FALSE(net::ParseIPLiteralToNumber("192.168.0.1:30", &number));
+  EXPECT_FALSE(net::ParseIPLiteralToNumber("  192.168.0.1  ", &number));
+  EXPECT_FALSE(net::ParseIPLiteralToNumber("[::1]", &number));
+}
+
+// Test parsing an IPv4 literal.
+TEST(NetUtilTest, ParseIPLiteralToNumber_IPv4) {
+  net::IPAddressNumber number;
+  EXPECT_TRUE(net::ParseIPLiteralToNumber("192.168.0.1", &number));
+  EXPECT_EQ("192,168,0,1", DumpIPNumber(number));
+}
+
+// Test parsing an IPv6 literal.
+TEST(NetUtilTest, ParseIPLiteralToNumber_IPv6) {
+  net::IPAddressNumber number;
+  EXPECT_TRUE(net::ParseIPLiteralToNumber("1:abcd::3:4:ff", &number));
+  EXPECT_EQ("0,1,171,205,0,0,0,0,0,0,0,3,0,4,0,255", DumpIPNumber(number));
+}
+
+// Test mapping an IPv4 address to an IPv6 address.
+TEST(NetUtilTest, ConvertIPv4NumberToIPv6Number) {
+  net::IPAddressNumber ipv4_number;
+  EXPECT_TRUE(net::ParseIPLiteralToNumber("192.168.0.1", &ipv4_number));
+
+  net::IPAddressNumber ipv6_number =
+      net::ConvertIPv4NumberToIPv6Number(ipv4_number);
+
+  // ::ffff:192.168.1.1
+  EXPECT_EQ("0,0,0,0,0,0,0,0,0,0,255,255,192,168,0,1",
+            DumpIPNumber(ipv6_number));
+}
+
+// Test parsing invalid CIDR notation literals.
+TEST(NetUtilTest, ParseCIDRBlock_Invalid) {
+  const char* bad_literals[] = {
+      "foobar",
+      "",
+      "192.168.0.1",
+      "::1",
+      "/",
+      "/1",
+      "1",
+      "192.168.1.1/-1",
+      "192.168.1.1/33",
+      "::1/-3",
+      "a::3/129",
+      "::1/x",
+      "192.168.0.1//11"
+  };
+
+  for (size_t i = 0; i < arraysize(bad_literals); ++i) {
+    net::IPAddressNumber ip_number;
+    size_t prefix_length_in_bits;
+
+    EXPECT_FALSE(net::ParseCIDRBlock(bad_literals[i],
+                                     &ip_number,
+                                     &prefix_length_in_bits));
+  }
+}
+
+// Test parsing a valid CIDR notation literal.
+TEST(NetUtilTest, ParseCIDRBlock_Valid) {
+  net::IPAddressNumber ip_number;
+  size_t prefix_length_in_bits;
+
+  EXPECT_TRUE(net::ParseCIDRBlock("192.168.0.1/11",
+                                  &ip_number,
+                                  &prefix_length_in_bits));
+
+  EXPECT_EQ("192,168,0,1", DumpIPNumber(ip_number));
+  EXPECT_EQ(11u, prefix_length_in_bits);
+}
+
+TEST(NetUtilTest, IPNumberMatchesPrefix) {
+  struct {
+    const char* cidr_literal;
+    const char* ip_literal;
+    bool expected_to_match;
+  } tests[] = {
+    // IPv4 prefix with IPv4 inputs.
+    {
+      "10.10.1.32/27",
+      "10.10.1.44",
+      true
+    },
+    {
+      "10.10.1.32/27",
+      "10.10.1.90",
+      false
+    },
+    {
+      "10.10.1.32/27",
+      "10.10.1.90",
+      false
+    },
+
+    // IPv6 prefix with IPv6 inputs.
+    {
+      "2001:db8::/32",
+      "2001:DB8:3:4::5",
+      true
+    },
+    {
+      "2001:db8::/32",
+      "2001:c8::",
+      false
+    },
+
+    // IPv6 prefix with IPv4 inputs.
+    {
+      "2001:db8::/33",
+      "192.168.0.1",
+      false
+    },
+    {
+      "::ffff:192.168.0.1/112",
+      "192.168.33.77",
+      true
+    },
+
+    // IPv4 prefix with IPv6 inputs.
+    {
+      "10.11.33.44/16",
+      "::ffff:0a0b:89",
+      true
+    },
+    {
+      "10.11.33.44/16",
+      "::ffff:10.12.33.44",
+      false
+    },
+  };
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+    SCOPED_TRACE(StringPrintf("Test[%" PRIuS "]: %s, %s", i,
+                              tests[i].cidr_literal,
+                              tests[i].ip_literal));
+
+    net::IPAddressNumber ip_number;
+    EXPECT_TRUE(net::ParseIPLiteralToNumber(tests[i].ip_literal, &ip_number));
+
+    net::IPAddressNumber ip_prefix;
+    size_t prefix_length_in_bits;
+
+    EXPECT_TRUE(net::ParseCIDRBlock(tests[i].cidr_literal,
+                                    &ip_prefix,
+                                    &prefix_length_in_bits));
+
+    EXPECT_EQ(tests[i].expected_to_match,
+              net::IPNumberMatchesPrefix(ip_number,
+                                         ip_prefix,
+                                         prefix_length_in_bits));
+  }
+}
diff --git a/net/base/net_util_win.cc b/net/base/net_util_win.cc
index 244f4ad..a6d37c1 100644
--- a/net/base/net_util_win.cc
+++ b/net/base/net_util_win.cc
@@ -8,6 +8,7 @@
 #include "base/string_piece.h"
 #include "base/string_util.h"
 #include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
 #include "googleurl/src/gurl.h"
 #include "net/base/escape.h"
 
diff --git a/net/base/network_change_notifier.cc b/net/base/network_change_notifier.cc
index aeca2ab..a0bc6b5 100644
--- a/net/base/network_change_notifier.cc
+++ b/net/base/network_change_notifier.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -14,9 +14,22 @@
 
 namespace net {
 
-// static
-NetworkChangeNotifier*
-NetworkChangeNotifier::CreateDefaultNetworkChangeNotifier() {
+namespace {
+
+// The actual singleton notifier.  The class contract forbids usage of the API
+// in ways that would require us to place locks around access to this object.
+// (The prohibition on global non-POD objects makes it tricky to do such a thing
+// anyway.)
+NetworkChangeNotifier* g_network_change_notifier = NULL;
+
+}  // namespace
+
+NetworkChangeNotifier::~NetworkChangeNotifier() {
+  DCHECK_EQ(this, g_network_change_notifier);
+  g_network_change_notifier = NULL;
+}
+
+NetworkChangeNotifier* NetworkChangeNotifier::Create() {
 #if defined(OS_WIN)
   return new NetworkChangeNotifierWin();
 #elif defined(OS_LINUX)
@@ -29,4 +42,27 @@
 #endif
 }
 
+void NetworkChangeNotifier::AddObserver(Observer* observer) {
+  if (g_network_change_notifier)
+    g_network_change_notifier->observer_list_->AddObserver(observer);
+}
+
+void NetworkChangeNotifier::RemoveObserver(Observer* observer) {
+  if (g_network_change_notifier)
+    g_network_change_notifier->observer_list_->RemoveObserver(observer);
+}
+
+NetworkChangeNotifier::NetworkChangeNotifier()
+    : observer_list_(new ObserverListThreadSafe<Observer>()) {
+  DCHECK(!g_network_change_notifier);
+  g_network_change_notifier = this;
+}
+
+void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() {
+  if (g_network_change_notifier) {
+    g_network_change_notifier->observer_list_->Notify(
+        &Observer::OnIPAddressChanged);
+  }
+}
+
 }  // namespace net
diff --git a/net/base/network_change_notifier.h b/net/base/network_change_notifier.h
index 67d57ff..e0a8800 100644
--- a/net/base/network_change_notifier.h
+++ b/net/base/network_change_notifier.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -6,13 +6,14 @@
 #define NET_BASE_NETWORK_CHANGE_NOTIFIER_H_
 
 #include "base/basictypes.h"
-#include "base/non_thread_safe.h"
+#include "base/observer_list_threadsafe.h"
 
 namespace net {
 
 // NetworkChangeNotifier monitors the system for network changes, and notifies
-// observers on those events.
-class NetworkChangeNotifier : public NonThreadSafe {
+// registered observers of those events.  Observers may register on any thread,
+// and will be called back on the thread from which they registered.
+class NetworkChangeNotifier {
  public:
   class Observer {
    public:
@@ -29,21 +30,58 @@
     DISALLOW_COPY_AND_ASSIGN(Observer);
   };
 
-  NetworkChangeNotifier() {}
-  virtual ~NetworkChangeNotifier() {}
+  virtual ~NetworkChangeNotifier();
 
-  // These functions add and remove observers to/from the NetworkChangeNotifier.
-  // Each call to AddObserver() must be matched with a corresponding call to
-  // RemoveObserver() with the same parameter.  Observers must remove themselves
-  // before they get deleted, otherwise the NetworkChangeNotifier may try to
-  // notify the wrong object.
-  virtual void AddObserver(Observer* observer) = 0;
-  virtual void RemoveObserver(Observer* observer) = 0;
+  // Creates the process-wide, platform-specific NetworkChangeNotifier.  The
+  // caller owns the returned pointer.  You may call this on any thread.  You
+  // may also avoid creating this entirely (in which case nothing will be
+  // monitored), but if you do create it, you must do so before any other
+  // threads try to access the API below, and it must outlive all other threads
+  // which might try to use it.
+  static NetworkChangeNotifier* Create();
 
-  // This will create the platform specific default NetworkChangeNotifier.
-  static NetworkChangeNotifier* CreateDefaultNetworkChangeNotifier();
+#ifdef UNIT_TEST
+  // Like Create(), but for use in tests.  The mock object doesn't monitor any
+  // events, it merely rebroadcasts notifications when requested.
+  static NetworkChangeNotifier* CreateMock() {
+    return new NetworkChangeNotifier();
+  }
+#endif
+
+  // Registers |observer| to receive notifications of network changes.  The
+  // thread on which this is called is the thread on which |observer| will be
+  // called back with notifications.  This is safe to call if Create() has not
+  // been called (as long as it doesn't race the Create() call on another
+  // thread), in which case it will simply do nothing.
+  static void AddObserver(Observer* observer);
+
+  // Unregisters |observer| from receiving notifications.  This must be called
+  // on the same thread on which AddObserver() was called.  Like AddObserver(),
+  // this is safe to call if Create() has not been called (as long as it doesn't
+  // race the Create() call on another thread), in which case it will simply do
+  // nothing.  Technically, it's also safe to call after the notifier object has
+  // been destroyed, if the call doesn't race the notifier's destruction, but
+  // there's no reason to use the API in this risky way, so don't do it.
+  static void RemoveObserver(Observer* observer);
+
+#ifdef UNIT_TEST
+  // Allow unit tests to trigger notifications.
+  static void NotifyObserversOfIPAddressChangeForTests() {
+    NotifyObserversOfIPAddressChange();
+  }
+#endif
+
+ protected:
+  NetworkChangeNotifier();
+
+  // Broadcasts a notification to all registered observers.  Note that this
+  // happens asynchronously, even for observers on the current thread, even in
+  // tests.
+  static void NotifyObserversOfIPAddressChange();
 
  private:
+  const scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_;
+
   DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifier);
 };
 
diff --git a/net/base/network_change_notifier_linux.cc b/net/base/network_change_notifier_linux.cc
index e6eba24..22be563 100644
--- a/net/base/network_change_notifier_linux.cc
+++ b/net/base/network_change_notifier_linux.cc
@@ -1,12 +1,139 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/base/network_change_notifier_linux.h"
 
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "net/base/net_errors.h"
+#include "net/base/network_change_notifier_netlink_linux.h"
+
+// We only post tasks to a child thread we own, so we don't need refcounting.
+DISABLE_RUNNABLE_METHOD_REFCOUNT(net::NetworkChangeNotifierLinux);
+
 namespace net {
 
-NetworkChangeNotifierLinux::NetworkChangeNotifierLinux() {}
-NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {}
+namespace {
+
+const int kInvalidSocket = -1;
+
+}  // namespace
+
+NetworkChangeNotifierLinux::NetworkChangeNotifierLinux()
+    : notifier_thread_(new base::Thread("NetworkChangeNotifier")),
+      netlink_fd_(kInvalidSocket) {
+  // We create this notifier thread because the notification implementation
+  // needs a MessageLoopForIO, and there's no guarantee that
+  // MessageLoop::current() meets that criterion.
+  base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0);
+  notifier_thread_->StartWithOptions(thread_options);
+  notifier_thread_->message_loop()->PostTask(FROM_HERE,
+      NewRunnableMethod(this, &NetworkChangeNotifierLinux::Init));
+}
+
+NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {
+  // We don't need to explicitly Stop(), but doing so allows us to sanity-
+  // check that the notifier thread shut down properly.
+  notifier_thread_->Stop();
+  DCHECK_EQ(kInvalidSocket, netlink_fd_);
+}
+
+void NetworkChangeNotifierLinux::WillDestroyCurrentMessageLoop() {
+  DCHECK(notifier_thread_ != NULL);
+  // We can't check the notifier_thread_'s message_loop(), as it's now 0.
+  // DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
+
+  if (netlink_fd_ != kInvalidSocket) {
+    if (HANDLE_EINTR(close(netlink_fd_)) != 0)
+      PLOG(ERROR) << "Failed to close socket";
+    netlink_fd_ = kInvalidSocket;
+    netlink_watcher_.StopWatchingFileDescriptor();
+  }
+}
+
+void NetworkChangeNotifierLinux::OnFileCanReadWithoutBlocking(int fd) {
+  DCHECK(notifier_thread_ != NULL);
+  DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
+
+  DCHECK_EQ(fd, netlink_fd_);
+  ListenForNotifications();
+}
+
+void NetworkChangeNotifierLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {
+  DCHECK(notifier_thread_ != NULL);
+  DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
+
+  NOTREACHED();
+}
+
+void NetworkChangeNotifierLinux::Init() {
+  DCHECK(notifier_thread_ != NULL);
+  DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
+
+  netlink_fd_ = InitializeNetlinkSocket();
+  if (netlink_fd_ < 0) {
+    netlink_fd_ = kInvalidSocket;
+    return;
+  }
+  MessageLoop::current()->AddDestructionObserver(this);
+  ListenForNotifications();
+}
+
+void NetworkChangeNotifierLinux::ListenForNotifications() {
+  DCHECK(notifier_thread_ != NULL);
+  DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
+
+  char buf[4096];
+  int rv = ReadNotificationMessage(buf, arraysize(buf));
+  while (rv > 0) {
+    if (HandleNetlinkMessage(buf, rv)) {
+      LOG(INFO) << "Detected IP address changes.";
+#if defined(OS_CHROMEOS)
+      // TODO(zelidrag): chromium-os:3996 - introduced artificial delay to
+      // work around the issue of proxy initialization before name resolving
+      // is functional in ChromeOS. This should be removed once this bug
+      // is properly fixed.
+      const int kObserverNotificationDelayMS = 500;
+      MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableFunction(
+          &NetworkChangeNotifier::NotifyObserversOfIPAddressChange),
+          kObserverNotificationDelayMS);
+#else
+      NotifyObserversOfIPAddressChange();
+#endif
+    }
+    rv = ReadNotificationMessage(buf, arraysize(buf));
+  }
+
+  if (rv == ERR_IO_PENDING) {
+    rv = MessageLoopForIO::current()->WatchFileDescriptor(netlink_fd_, false,
+        MessageLoopForIO::WATCH_READ, &netlink_watcher_, this);
+    LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_;
+  }
+}
+
+int NetworkChangeNotifierLinux::ReadNotificationMessage(char* buf, size_t len) {
+  DCHECK(notifier_thread_ != NULL);
+  DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
+
+  DCHECK_NE(len, 0u);
+  DCHECK(buf);
+  memset(buf, 0, sizeof(buf));
+  int rv = recv(netlink_fd_, buf, len, 0);
+  if (rv > 0)
+    return rv;
+
+  DCHECK_NE(rv, 0);
+  if (errno != EAGAIN && errno != EWOULDBLOCK) {
+    PLOG(DFATAL) << "recv";
+    return ERR_FAILED;
+  }
+
+  return ERR_IO_PENDING;
+}
 
 }  // namespace net
diff --git a/net/base/network_change_notifier_linux.h b/net/base/network_change_notifier_linux.h
index 78111c9..aca9377 100644
--- a/net/base/network_change_notifier_linux.h
+++ b/net/base/network_change_notifier_linux.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -6,28 +6,54 @@
 #define NET_BASE_NETWORK_CHANGE_NOTIFIER_LINUX_H_
 
 #include "base/basictypes.h"
-#include "net/base/network_change_notifier_helper.h"
+#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
+#include "net/base/network_change_notifier.h"
+
+namespace base {
+class Thread;
+}
 
 namespace net {
 
-class NetworkChangeNotifierLinux : public NetworkChangeNotifier {
+class NetworkChangeNotifierLinux : public MessageLoop::DestructionObserver,
+                                   public MessageLoopForIO::Watcher,
+                                   public NetworkChangeNotifier {
  public:
   NetworkChangeNotifierLinux();
 
-  virtual void AddObserver(Observer* observer) {
-    helper_.AddObserver(observer);
-  }
-
-  virtual void RemoveObserver(Observer* observer) {
-    helper_.RemoveObserver(observer);
-  }
-
  private:
   virtual ~NetworkChangeNotifierLinux();
 
-  void OnIPAddressChanged() { helper_.OnIPAddressChanged(); }
+  // MessageLoop::DestructionObserver:
+  virtual void WillDestroyCurrentMessageLoop();
 
-  internal::NetworkChangeNotifierHelper helper_;
+  // MessageLoopForIO::Watcher:
+  virtual void OnFileCanReadWithoutBlocking(int fd);
+  virtual void OnFileCanWriteWithoutBlocking(int /* fd */);
+
+  // Called on the notifier thread to initialize the notification
+  // implementation.
+  void Init();
+
+  // Starts listening for netlink messages.  Also handles the messages if there
+  // are any available on the netlink socket.
+  void ListenForNotifications();
+
+  // Attempts to read from the netlink socket into |buf| of length |len|.
+  // Returns the bytes read on synchronous success and ERR_IO_PENDING if the
+  // recv() would block.  Otherwise, it returns a net error code.
+  int ReadNotificationMessage(char* buf, size_t len);
+
+  // The thread used to listen for notifications.  This relays the notification
+  // to the registered observers without posting back to the thread the object
+  // was created on.
+  scoped_ptr<base::Thread> notifier_thread_;
+
+  // The netlink socket descriptor.
+  int netlink_fd_;
+
+  MessageLoopForIO::FileDescriptorWatcher netlink_watcher_;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierLinux);
 };
diff --git a/net/base/network_change_notifier_mac.cc b/net/base/network_change_notifier_mac.cc
index 5606210..797de6d 100644
--- a/net/base/network_change_notifier_mac.cc
+++ b/net/base/network_change_notifier_mac.cc
@@ -1,168 +1,117 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
-// There are three classes involved here.  There's NetworkChangeNotifierMac,
-// which is the Mac specific implementation of NetworkChangeNotifier.  It is the
-// class with which clients can register themselves as network change 
-// observers.  There's NetworkChangeNotifierThread, which is a base::Thread
-// subclass of MessageLoop::TYPE_UI (since it needs a CFRunLoop) that contains
-// the NetworkChangeNotifierImpl.  NetworkChangeNotifierImpl is the object
-// that receives the actual OS X notifications and posts them to the
-// NetworkChangeNotifierMac's message loop, so that NetworkChangeNotifierMac
-// can notify all its observers.
-//
-// When NetworkChangeNotifierMac is being deleted, it will delete the
-// NetworkChangeNotifierThread, which will Stop() it and also delete the
-// NetworkChangeNotifierImpl.  Therefore, NetworkChangeNotifierImpl and
-// NetworkChangeNotifierThread's lifetimes generally begin after and end before
-// NetworkChangeNotifierMac.  There is an edge case where a notification task
-// gets posted to the IO thread, thereby maintaining a reference to
-// NetworkChangeNotifierImpl beyond the lifetime of NetworkChangeNotifierThread.
-// In this case, the notification is cancelled, and NetworkChangeNotifierImpl
-// will be deleted once all notification tasks that reference it have been run.
-
 #include "net/base/network_change_notifier_mac.h"
-#include <SystemConfiguration/SCDynamicStore.h>
+
 #include <SystemConfiguration/SCDynamicStoreKey.h>
 #include <SystemConfiguration/SCSchemaDefinitions.h>
 #include <algorithm>
-#include "base/logging.h"
-#include "base/scoped_cftyperef.h"
+
 #include "base/thread.h"
 
+// We only post tasks to a child thread we own, so we don't need refcounting.
+DISABLE_RUNNABLE_METHOD_REFCOUNT(net::NetworkChangeNotifierMac);
+
 namespace net {
 
-namespace {
-
-// NetworkChangeNotifierImpl should be created on a thread with a CFRunLoop,
-// since it requires one to pump notifications.  However, it also runs some
-// methods on |notifier_loop_|, because it cannot post calls to |notifier_|
-// since NetworkChangeNotifier is not ref counted in a thread safe manner.
-class NetworkChangeNotifierImpl
-    : public base::RefCountedThreadSafe<NetworkChangeNotifierImpl> {
- public:
-  NetworkChangeNotifierImpl(MessageLoop* notifier_loop,
-                            NetworkChangeNotifierMac* notifier);
-
-  void Shutdown();
-
- private:
-  friend class base::RefCountedThreadSafe<NetworkChangeNotifierImpl>;
-  ~NetworkChangeNotifierImpl();
-
-  static void DynamicStoreCallback(SCDynamicStoreRef /* store */,
-                                   CFArrayRef changed_keys,
-                                   void* config);
-
-  void OnNetworkConfigChange(CFArrayRef changed_keys);
-
-  // Runs on |notifier_loop_|.
-  void OnIPAddressChanged();
-
-  // Raw pointers.  Note that |notifier_| _must_ outlive the
-  // NetworkChangeNotifierImpl.  For lifecycle management details, read the
-  // comment at the top of the file.
-  MessageLoop* const notifier_loop_;
-  NetworkChangeNotifierMac* notifier_;
-
-  scoped_cftyperef<CFRunLoopSourceRef> source_;
-
-  DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierImpl);
-};
-
-NetworkChangeNotifierImpl::NetworkChangeNotifierImpl(
-    MessageLoop* notifier_loop, NetworkChangeNotifierMac* notifier)
-    : notifier_loop_(notifier_loop),
-      notifier_(notifier) {
-  DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
-  SCDynamicStoreContext context = {
-    0,    // Version 0.
-    this, // User data.
-    NULL, // This is not reference counted.  No retain function.
-    NULL, // This is not reference counted.  No release function.
-    NULL, // No description for this.
-  };
-
-  // Get a reference to the dynamic store.
-  scoped_cftyperef<SCDynamicStoreRef> store(
-      SCDynamicStoreCreate(NULL /* use default allocator */,
-                           CFSTR("org.chromium"),
-                           DynamicStoreCallback, &context));
-
-  // Create a run loop source for the dynamic store.
-  source_.reset(SCDynamicStoreCreateRunLoopSource(
-      NULL /* use default allocator */,
-      store.get(),
-      0 /* 0 sounds like a fine source order to me! */));
-
-  // Add the run loop source to the current run loop.
-  CFRunLoopAddSource(CFRunLoopGetCurrent(),
-                     source_.get(),
-                     kCFRunLoopCommonModes);
-
-  // Set up the notification keys.
-  scoped_cftyperef<CFMutableArrayRef> notification_keys(
-      CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
-
-  // Monitor interface changes.
-  scoped_cftyperef<CFStringRef> key(
-      SCDynamicStoreKeyCreateNetworkGlobalEntity(
-          NULL /* default allocator */, kSCDynamicStoreDomainState,
-          kSCEntNetInterface));
-  CFArrayAppendValue(notification_keys.get(), key.get());
-
-  // Monitor IP address changes.
-
-  key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
-      NULL /* default allocator */, kSCDynamicStoreDomainState,
-      kSCEntNetIPv4));
-  CFArrayAppendValue(notification_keys.get(), key.get());
-
-  key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
-      NULL /* default allocator */, kSCDynamicStoreDomainState,
-      kSCEntNetIPv6));
-  CFArrayAppendValue(notification_keys.get(), key.get());
-
-  // Ok, let's ask for notifications!
-  // TODO(willchan): Figure out a proper way to handle this rather than crash.
-  CHECK(SCDynamicStoreSetNotificationKeys(
-      store.get(), notification_keys.get(), NULL));
+NetworkChangeNotifierMac::NetworkChangeNotifierMac()
+    : notifier_thread_(new base::Thread("NetworkChangeNotifier")) {
+  // We create this notifier thread because the notification implementation
+  // needs a thread with a CFRunLoop, and there's no guarantee that
+  // MessageLoop::current() meets that criterion.
+  base::Thread::Options thread_options(MessageLoop::TYPE_UI, 0);
+  notifier_thread_->StartWithOptions(thread_options);
+  // TODO(willchan): Look to see if there's a better signal for when it's ok to
+  // initialize this, rather than just delaying it by a fixed time.
+  const int kNotifierThreadInitializationDelayMS = 1000;
+  notifier_thread_->message_loop()->PostDelayedTask(FROM_HERE,
+      NewRunnableMethod(this, &NetworkChangeNotifierMac::Init),
+      kNotifierThreadInitializationDelayMS);
 }
 
-NetworkChangeNotifierImpl::~NetworkChangeNotifierImpl() {
-  CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
-                        source_.get(),
-                        kCFRunLoopCommonModes);
-}
-
-void NetworkChangeNotifierImpl::Shutdown() {
-  CHECK(notifier_);
-  notifier_ = NULL;
+NetworkChangeNotifierMac::~NetworkChangeNotifierMac() {
+  // We don't need to explicitly Stop(), but doing so allows us to sanity-
+  // check that the notifier thread shut down properly.
+  notifier_thread_->Stop();
+  DCHECK(run_loop_source_ == NULL);
 }
 
 // static
-void NetworkChangeNotifierImpl::DynamicStoreCallback(
+void NetworkChangeNotifierMac::DynamicStoreCallback(
     SCDynamicStoreRef /* store */,
     CFArrayRef changed_keys,
     void* config) {
-  NetworkChangeNotifierImpl* net_config =
-      static_cast<NetworkChangeNotifierImpl*>(config);
+  NetworkChangeNotifierMac* net_config =
+      static_cast<NetworkChangeNotifierMac*>(config);
   net_config->OnNetworkConfigChange(changed_keys);
 }
 
-void NetworkChangeNotifierImpl::OnNetworkConfigChange(CFArrayRef changed_keys) {
+void NetworkChangeNotifierMac::WillDestroyCurrentMessageLoop() {
+  DCHECK(notifier_thread_ != NULL);
+  // We can't check the notifier_thread_'s message_loop(), as it's now 0.
+  // DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
+
+  DCHECK(run_loop_source_ != NULL);
+  CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
+                        kCFRunLoopCommonModes);
+  run_loop_source_.reset();
+}
+
+void NetworkChangeNotifierMac::Init() {
+  DCHECK(notifier_thread_ != NULL);
+  DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
+
+  // Add a run loop source for a dynamic store to the current run loop.
+  SCDynamicStoreContext context = {
+    0,     // Version 0.
+    this,  // User data.
+    NULL,  // This is not reference counted.  No retain function.
+    NULL,  // This is not reference counted.  No release function.
+    NULL,  // No description for this.
+  };
+  scoped_cftyperef<SCDynamicStoreRef> store(SCDynamicStoreCreate(
+      NULL, CFSTR("org.chromium"), DynamicStoreCallback, &context));
+  run_loop_source_.reset(SCDynamicStoreCreateRunLoopSource(
+      NULL, store.get(), 0));
+  CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
+                     kCFRunLoopCommonModes);
+
+  // Set up notifications for interface and IP address changes.
+  scoped_cftyperef<CFMutableArrayRef> notification_keys(
+      CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
+  scoped_cftyperef<CFStringRef> key(SCDynamicStoreKeyCreateNetworkGlobalEntity(
+      NULL, kSCDynamicStoreDomainState, kSCEntNetInterface));
+  CFArrayAppendValue(notification_keys.get(), key.get());
+  key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
+      NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4));
+  CFArrayAppendValue(notification_keys.get(), key.get());
+  key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
+      NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6));
+  CFArrayAppendValue(notification_keys.get(), key.get());
+
+  // Set the notification keys.  This starts us receiving notifications.
+  bool ret = SCDynamicStoreSetNotificationKeys(
+      store.get(), notification_keys.get(), NULL);
+  // TODO(willchan): Figure out a proper way to handle this rather than crash.
+  CHECK(ret);
+
+  MessageLoop::current()->AddDestructionObserver(this);
+}
+
+void NetworkChangeNotifierMac::OnNetworkConfigChange(CFArrayRef changed_keys) {
+  DCHECK(notifier_thread_ != NULL);
+  DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current());
+
   for (CFIndex i = 0; i < CFArrayGetCount(changed_keys); ++i) {
     CFStringRef key = static_cast<CFStringRef>(
         CFArrayGetValueAtIndex(changed_keys, i));
     if (CFStringHasSuffix(key, kSCEntNetIPv4) ||
         CFStringHasSuffix(key, kSCEntNetIPv6)) {
-      notifier_loop_->PostTask(
-          FROM_HERE,
-          NewRunnableMethod(
-              this,
-              &NetworkChangeNotifierImpl::OnIPAddressChanged));
-    } else if (CFStringHasSuffix(key, kSCEntNetInterface)) {
+      NotifyObserversOfIPAddressChange();
+      return;
+    }
+    if (CFStringHasSuffix(key, kSCEntNetInterface)) {
       // TODO(willchan): Does not appear to be working.  Look into this.
       // Perhaps this isn't needed anyway.
     } else {
@@ -171,60 +120,4 @@
   }
 }
 
-void NetworkChangeNotifierImpl::OnIPAddressChanged() {
-  // If |notifier_| doesn't exist, then that means we're shutting down, so
-  // notifications are all cancelled.
-  if (notifier_)
-    notifier_->OnIPAddressChanged();
-}
-
-class NetworkChangeNotifierThread : public base::Thread {
- public:
-  NetworkChangeNotifierThread(MessageLoop* notifier_loop,
-                              NetworkChangeNotifierMac* notifier);
-  ~NetworkChangeNotifierThread();
-
- protected:
-  virtual void Init();
-
- private:
-  MessageLoop* const notifier_loop_;
-  NetworkChangeNotifierMac* const notifier_;
-  scoped_refptr<NetworkChangeNotifierImpl> notifier_impl_;
-
-  DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierThread);
-};
-
-NetworkChangeNotifierThread::NetworkChangeNotifierThread(
-    MessageLoop* notifier_loop, NetworkChangeNotifierMac* notifier)
-    : base::Thread("NetworkChangeNotifier"),
-      notifier_loop_(notifier_loop),
-      notifier_(notifier) {}
-
-NetworkChangeNotifierThread::~NetworkChangeNotifierThread() {
-  notifier_impl_->Shutdown();
-  Stop();
-}
-
-// Note that |notifier_impl_| is initialized on the network change
-// notifier thread, not whatever thread constructs the
-// NetworkChangeNotifierThread object.  This is important, because this thread
-// is the one that has a CFRunLoop.
-void NetworkChangeNotifierThread::Init() {
-  notifier_impl_ =
-      new NetworkChangeNotifierImpl(notifier_loop_, notifier_);
-}
-
-}  // namespace
-
-NetworkChangeNotifierMac::NetworkChangeNotifierMac()
-    : notifier_thread_(
-          new NetworkChangeNotifierThread(MessageLoop::current(), this)) {
-  base::Thread::Options thread_options;
-  thread_options.message_loop_type = MessageLoop::TYPE_UI;
-  notifier_thread_->StartWithOptions(thread_options);
-}
-
-NetworkChangeNotifierMac::~NetworkChangeNotifierMac() {}
-
 }  // namespace net
diff --git a/net/base/network_change_notifier_mac.h b/net/base/network_change_notifier_mac.h
index cbf0128..c85f244 100644
--- a/net/base/network_change_notifier_mac.h
+++ b/net/base/network_change_notifier_mac.h
@@ -1,47 +1,56 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_BASE_NETWORK_CHANGE_NOTIFIER_MAC_H_
 #define NET_BASE_NETWORK_CHANGE_NOTIFIER_MAC_H_
 
+#include <SystemConfiguration/SCDynamicStore.h>
+
 #include "base/basictypes.h"
-#include "base/ref_counted.h"
+#include "base/message_loop.h"
+#include "base/scoped_cftyperef.h"
 #include "base/scoped_ptr.h"
 #include "net/base/network_change_notifier.h"
-#include "net/base/network_change_notifier_helper.h"
 
 namespace base {
 class Thread;
-}  // namespace base
+}
 
 namespace net {
 
-class NetworkChangeNotifierMac : public NetworkChangeNotifier {
+class NetworkChangeNotifierMac : public MessageLoop::DestructionObserver,
+                                 public NetworkChangeNotifier {
  public:
   NetworkChangeNotifierMac();
 
-  void OnIPAddressChanged() { helper_.OnIPAddressChanged(); }
-
-  // NetworkChangeNotifier methods:
-
-  virtual void AddObserver(Observer* observer) {
-    helper_.AddObserver(observer);
-  }
-
-  virtual void RemoveObserver(Observer* observer) {
-    helper_.RemoveObserver(observer);
-  }
-
  private:
-  friend class base::RefCounted<NetworkChangeNotifierMac>;
-  
   virtual ~NetworkChangeNotifierMac();
 
-  // Receives the OS X network change notifications on this thread.
-  const scoped_ptr<base::Thread> notifier_thread_;
+  // Called back by OS.  Calls OnNetworkConfigChange().
+  static void DynamicStoreCallback(SCDynamicStoreRef /* store */,
+                                   CFArrayRef changed_keys,
+                                   void* config);
 
-  internal::NetworkChangeNotifierHelper helper_;
+  // MessageLoop::DestructionObserver:
+  virtual void WillDestroyCurrentMessageLoop();
+
+  // Called on the notifier thread to initialize the notification
+  // implementation.  The SystemConfiguration calls in this function can lead to
+  // contention early on, so we invoke this function later on in startup to keep
+  // it fast.
+  void Init();
+
+  // Called by DynamicStoreCallback() when something about the network config
+  // changes.
+  void OnNetworkConfigChange(CFArrayRef changed_keys);
+
+  // The thread used to listen for notifications.  This relays the notification
+  // to the registered observers without posting back to the thread the object
+  // was created on.
+  scoped_ptr<base::Thread> notifier_thread_;
+
+  scoped_cftyperef<CFRunLoopSourceRef> run_loop_source_;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierMac);
 };
diff --git a/net/base/network_change_notifier_netlink_linux.cc b/net/base/network_change_notifier_netlink_linux.cc
new file mode 100644
index 0000000..5f71f45
--- /dev/null
+++ b/net/base/network_change_notifier_netlink_linux.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2010 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/base/network_change_notifier_netlink_linux.h"
+
+#include <fcntl.h>
+// socket.h is needed to define types for the linux kernel header netlink.h
+// so it needs to come before netlink.h.
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+
+namespace {
+
+// Return true on success, false on failure.
+// Too small a function to bother putting in a library?
+bool SetNonBlocking(int fd) {
+  int flags = fcntl(fd, F_GETFL, 0);
+  if (-1 == flags)
+    return false;
+  return fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0 ? true : false;
+}
+
+}  // namespace
+
+int InitializeNetlinkSocket() {
+  int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+  if (sock < 0) {
+    PLOG(ERROR) << "Error creating netlink socket";
+    return -1;
+  }
+
+  if (!SetNonBlocking(sock)) {
+    PLOG(ERROR) << "Failed to set netlink socket to non-blocking mode.";
+    if (close(sock) != 0)
+      PLOG(ERROR) << "Failed to close socket";
+    return -1;
+  }
+
+  struct sockaddr_nl local_addr;
+  memset(&local_addr, 0, sizeof(local_addr));
+  local_addr.nl_family = AF_NETLINK;
+  local_addr.nl_pid = getpid();
+  local_addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_NOTIFY;
+  int ret = bind(sock, reinterpret_cast<struct sockaddr*>(&local_addr),
+                 sizeof(local_addr));
+  if (ret < 0) {
+    PLOG(ERROR) << "Error binding netlink socket";
+    if (close(sock) != 0)
+      PLOG(ERROR) << "Failed to close socket";
+    return -1;
+  }
+
+  return sock;
+}
+
+bool HandleNetlinkMessage(char* buf, size_t len) {
+  const struct nlmsghdr* netlink_message_header =
+      reinterpret_cast<struct nlmsghdr*>(buf);
+  DCHECK(netlink_message_header);
+  for (; NLMSG_OK(netlink_message_header, len);
+       netlink_message_header = NLMSG_NEXT(netlink_message_header, len)) {
+    int netlink_message_type = netlink_message_header->nlmsg_type;
+    switch (netlink_message_type) {
+      case NLMSG_DONE:
+        NOTREACHED()
+            << "This is a monitoring netlink socket.  It should never be done.";
+        return false;
+      case NLMSG_ERROR:
+        LOG(ERROR) << "Unexpected netlink error.";
+        return false;
+      // During IP address changes, we will see all these messages.  Only fire
+      // the notification when we get a new address or remove an address.  We
+      // may still end up notifying observers more than strictly necessary, but
+      // if the primary interface goes down and back up, then this is necessary.
+      case RTM_NEWADDR:
+      case RTM_DELADDR:
+        return true;
+      case RTM_NEWLINK:
+      case RTM_DELLINK:
+        return false;
+      default:
+        LOG(DFATAL) << "Received unexpected netlink message type: "
+                    << netlink_message_type;
+        return false;
+    }
+  }
+
+  return false;
+}
diff --git a/net/base/network_change_notifier_netlink_linux.h b/net/base/network_change_notifier_netlink_linux.h
new file mode 100644
index 0000000..c06fdc7
--- /dev/null
+++ b/net/base/network_change_notifier_netlink_linux.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2010 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.
+
+// This file is to hide the netlink implementation details since the netlink.h
+// header contains a struct net; which conflicts with the net namespace.  So we
+// separate out all the netlink stuff into these files.
+
+#ifndef NET_BASE_NETWORK_CHANGE_NOTIFIER_NETLINK_LINUX_H_
+#define NET_BASE_NETWORK_CHANGE_NOTIFIER_NETLINK_LINUX_H_
+
+#include <cstddef>
+
+// Returns the file descriptor if successful.  Otherwise, returns -1.
+int InitializeNetlinkSocket();
+
+// Returns true if a network change has been detected, otherwise returns false.
+bool HandleNetlinkMessage(char* buf, size_t len);
+
+#endif  // NET_BASE_NETWORK_CHANGE_NOTIFIER_NETLINK_LINUX_H_
diff --git a/net/base/network_change_notifier_win.cc b/net/base/network_change_notifier_win.cc
index 711bd66..7bc5ddf 100644
--- a/net/base/network_change_notifier_win.cc
+++ b/net/base/network_change_notifier_win.cc
@@ -1,12 +1,40 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/base/network_change_notifier_win.h"
 
+#include <iphlpapi.h>
+#include <winsock2.h>
+
+#pragma comment(lib, "iphlpapi.lib")
+
 namespace net {
 
-NetworkChangeNotifierWin::NetworkChangeNotifierWin() {}
-NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {}
+NetworkChangeNotifierWin::NetworkChangeNotifierWin() {
+  memset(&addr_overlapped_, 0, sizeof addr_overlapped_);
+  addr_overlapped_.hEvent = WSACreateEvent();
+  WatchForAddressChange();
+}
+
+NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {
+  CancelIPChangeNotify(&addr_overlapped_);
+  addr_watcher_.StopWatching();
+  WSACloseEvent(addr_overlapped_.hEvent);
+}
+
+void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) {
+  NotifyObserversOfIPAddressChange();
+
+  // Start watching for the next address change.
+  WatchForAddressChange();
+}
+
+void NetworkChangeNotifierWin::WatchForAddressChange() {
+  HANDLE handle = NULL;
+  DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_);
+  CHECK(ret == ERROR_IO_PENDING);
+  addr_watcher_.StartWatching(addr_overlapped_.hEvent, this);
+}
 
 }  // namespace net
diff --git a/net/base/network_change_notifier_win.h b/net/base/network_change_notifier_win.h
index b208ad7..2077ca8 100644
--- a/net/base/network_change_notifier_win.h
+++ b/net/base/network_change_notifier_win.h
@@ -1,33 +1,34 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_BASE_NETWORK_CHANGE_NOTIFIER_WIN_H_
 #define NET_BASE_NETWORK_CHANGE_NOTIFIER_WIN_H_
 
+#include <windows.h>
+
 #include "base/basictypes.h"
-#include "net/base/network_change_notifier_helper.h"
+#include "base/object_watcher.h"
+#include "net/base/network_change_notifier.h"
 
 namespace net {
 
-class NetworkChangeNotifierWin : public NetworkChangeNotifier {
+class NetworkChangeNotifierWin : public NetworkChangeNotifier,
+                                 public base::ObjectWatcher::Delegate {
  public:
   NetworkChangeNotifierWin();
 
-  virtual void AddObserver(Observer* observer) {
-    helper_.AddObserver(observer);
-  }
-
-  virtual void RemoveObserver(Observer* observer) {
-    helper_.RemoveObserver(observer);
-  }
-
  private:
   virtual ~NetworkChangeNotifierWin();
 
-  void OnIPAddressChanged() { helper_.OnIPAddressChanged(); }
+  // ObjectWatcher::Delegate methods:
+  virtual void OnObjectSignaled(HANDLE object);
 
-  internal::NetworkChangeNotifierHelper helper_;
+  // Begins listening for a single subsequent address change.
+  void WatchForAddressChange();
+
+  base::ObjectWatcher addr_watcher_;
+  OVERLAPPED addr_overlapped_;
 
   DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierWin);
 };
diff --git a/net/base/nss_memio.c b/net/base/nss_memio.c
index 341cfee..5f7fd00 100644
--- a/net/base/nss_memio.c
+++ b/net/base/nss_memio.c
@@ -362,7 +362,7 @@
 void memio_SetPeerName(PRFileDesc *fd, const PRNetAddr *peername)
 {
     PRFileDesc *memiofd = PR_GetIdentitiesLayer(fd, memio_identity);
-    struct PRFilePrivate *secret =  memiofd->secret;
+    struct PRFilePrivate *secret = memiofd->secret;
     secret->peername = *peername;
 }
 
diff --git a/net/base/nss_memio.h b/net/base/nss_memio.h
index 0bee53e..a2b642a 100644
--- a/net/base/nss_memio.h
+++ b/net/base/nss_memio.h
@@ -6,6 +6,8 @@
 #ifndef __MEMIO_H
 #define __MEMIO_H
 
+#include <stddef.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/net/base/platform_mime_util_linux.cc b/net/base/platform_mime_util_linux.cc
index 7debe35..b39fe27 100644
--- a/net/base/platform_mime_util_linux.cc
+++ b/net/base/platform_mime_util_linux.cc
@@ -38,8 +38,37 @@
   return true;
 }
 
+struct MimeToExt {
+  const char* mime_type;
+  const char* ext;
+};
+
+const struct MimeToExt mime_type_ext_map[] = {
+  {"image/jpeg", "jpg"},
+  {"image/png", "png"},
+  {"image/gif", "gif"},
+  {"text/html", "html"},
+  {"video/mp4", "mp4"},
+  {"video/mpeg", "mpg"},
+  {"audio/mpeg", "mp3"},
+  {"text/plain", "txt"},
+  {"application/pdf", "pdf"},
+  {"application/x-tar", "tar"}
+};
+
 bool PlatformMimeUtil::GetPreferredExtensionForMimeType(
     const std::string& mime_type, FilePath::StringType* ext) const {
+
+  for (size_t x = 0;
+       x < (sizeof(mime_type_ext_map) / sizeof(MimeToExt));
+       x++) {
+    if (mime_type_ext_map[x].mime_type == mime_type) {
+      *ext = mime_type_ext_map[x].ext;
+      return true;
+    }
+  }
+
+  // TODO(dhg): Fix this the right way by implementing whats said below.
   // Unlike GetPlatformMimeTypeFromExtension, this method doesn't have a
   // default list that it uses, but for now we are also returning false since
   // this doesn't really matter as much under Linux.
diff --git a/net/base/platform_mime_util_win.cc b/net/base/platform_mime_util_win.cc
index b07a02d..26d924b 100644
--- a/net/base/platform_mime_util_win.cc
+++ b/net/base/platform_mime_util_win.cc
@@ -7,7 +7,7 @@
 #include "net/base/platform_mime_util.h"
 
 #include "base/registry.h"
-#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 
 namespace net {
 
diff --git a/net/base/run_all_unittests.cc b/net/base/run_all_unittests.cc
index 9786f6e..85bda7d 100644
--- a/net/base/run_all_unittests.cc
+++ b/net/base/run_all_unittests.cc
@@ -1,39 +1,28 @@
-// Copyright 2008, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//    * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//    * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//    * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Copyright (c) 2010 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 "build/build_config.h"
 #include "base/histogram.h"
 #include "net/base/net_test_suite.h"
+#if defined(OS_WIN)
+#include "net/socket/ssl_client_socket_nss_factory.h"
+#endif
 
 int main(int argc, char** argv) {
   // Record histograms, so we can get histograms data in tests.
   StatisticsRecorder recorder;
   NetTestSuite test_suite(argc, argv);
+
+#if defined(OS_WIN)
+  // Use NSS for SSL on Windows.  TODO(wtc): this should eventually be hidden
+  // inside DefaultClientSocketFactory::CreateSSLClientSocket.
+  net::ClientSocketFactory::SetSSLClientSocketFactory(
+      net::SSLClientSocketNSSFactory);
+  // We want to be sure to init NSPR on the main thread.
+  base::EnsureNSPRInit();
+#endif
+
   // TODO(phajdan.jr): Enforce test isolation, http://crbug.com/12710.
   return test_suite.Run();
 }
diff --git a/net/base/sdch_filter_unittest.cc b/net/base/sdch_filter_unittest.cc
index 99a3ffd..675a135 100644
--- a/net/base/sdch_filter_unittest.cc
+++ b/net/base/sdch_filter_unittest.cc
@@ -111,7 +111,7 @@
                            size_t input_block_length,
                            const size_t output_buffer_length,
                            Filter* filter, std::string* output) {
-  CHECK(input_block_length > 0);
+  CHECK_GT(input_block_length, 0u);
   Filter::FilterStatus status(Filter::FILTER_NEED_MORE_DATA);
   size_t source_index = 0;
   scoped_array<char> output_buffer(new char[output_buffer_length]);
@@ -375,8 +375,8 @@
   // list of dictionaries, so the filter should error out immediately.
   std::string dictionary_hash_postfix("4abcd\0", 6);
 
-  CHECK(dictionary_hash_postfix.size() <
-        static_cast<size_t>(input_buffer_size));
+  CHECK_LT(dictionary_hash_postfix.size(),
+           static_cast<size_t>(input_buffer_size));
   memcpy(input_buffer, dictionary_hash_postfix.data(),
          dictionary_hash_postfix.size());
   filter->FlushStreamBuffer(dictionary_hash_postfix.size());
@@ -792,7 +792,7 @@
                       8,  // DEF_MEM_LEVEL
                       Z_DEFAULT_STRATEGY);
 
-  CHECK(code == Z_OK);
+  CHECK_EQ(Z_OK, code);
 
   // Fill in zlib control block
   zlib_stream.next_in = bit_cast<Bytef*>(input.data());
@@ -819,7 +819,7 @@
   // Header value we generate:
   const char kGZipHeader[] = { '\037', '\213', '\010', '\000', '\000',
                                '\000', '\000', '\000', '\002', '\377' };
-  CHECK(zlib_stream.avail_out > sizeof(kGZipHeader));
+  CHECK_GT(zlib_stream.avail_out, sizeof(kGZipHeader));
   memcpy(zlib_stream.next_out, kGZipHeader, sizeof(kGZipHeader));
   zlib_stream.next_out += sizeof(kGZipHeader);
   zlib_stream.avail_out -= sizeof(kGZipHeader);
@@ -860,9 +860,9 @@
 
   // First try with a large buffer (larger than test input, or compressed data).
   const size_t kLargeInputBufferSize(1000);  // Used internally in filters.
-  CHECK(kLargeInputBufferSize > gzip_compressed_sdch.size());
-  CHECK(kLargeInputBufferSize > sdch_compressed.size());
-  CHECK(kLargeInputBufferSize > expanded_.size());
+  CHECK_GT(kLargeInputBufferSize, gzip_compressed_sdch.size());
+  CHECK_GT(kLargeInputBufferSize, sdch_compressed.size());
+  CHECK_GT(kLargeInputBufferSize, expanded_.size());
   MockFilterContext filter_context(kLargeInputBufferSize);
   filter_context.SetURL(url);
   scoped_ptr<Filter> filter(Filter::Factory(filter_types, filter_context));
@@ -884,11 +884,11 @@
   // Next try with a mid-sized internal buffer size.
   const size_t kMidSizedInputBufferSize(100);
   // Buffer should be big enough to swallow whole gzip content.
-  CHECK(kMidSizedInputBufferSize > gzip_compressed_sdch.size());
+  CHECK_GT(kMidSizedInputBufferSize, gzip_compressed_sdch.size());
   // Buffer should be small enough that entire SDCH content can't fit.
   // We'll go even further, and force the chain to flush the buffer between the
   // two filters more than once (that is why we multiply by 2).
-  CHECK(kMidSizedInputBufferSize * 2 < sdch_compressed.size());
+  CHECK_LT(kMidSizedInputBufferSize * 2, sdch_compressed.size());
   filter_context.SetBufferSize(kMidSizedInputBufferSize);
   filter_context.SetURL(url);
   filter.reset(Filter::Factory(filter_types, filter_context));
diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc
index b279749..6a8709b 100644
--- a/net/base/sdch_manager.cc
+++ b/net/base/sdch_manager.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -122,7 +122,7 @@
   global_->sdch_enabled_ = true;
 }
 
-const bool SdchManager::IsInSupportedDomain(const GURL& url) {
+bool SdchManager::IsInSupportedDomain(const GURL& url) {
   if (!sdch_enabled_ )
     return false;
   if (!supported_domain_.empty() &&
diff --git a/net/base/sdch_manager.h b/net/base/sdch_manager.h
index 67ca5e5..23d100d 100644
--- a/net/base/sdch_manager.h
+++ b/net/base/sdch_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -274,7 +274,7 @@
   // supported domain (i.e., not blacklisted, and either the specific supported
   // domain, or all domains were assumed supported).  If it is blacklist, reduce
   // by 1 the number of times it will be reported as blacklisted.
-  const bool IsInSupportedDomain(const GURL& url);
+  bool IsInSupportedDomain(const GURL& url);
 
   // Schedule the URL fetching to load a dictionary. This will always return
   // before the dictionary is actually loaded and added.
diff --git a/net/base/ssl_cipher_suite_names.cc b/net/base/ssl_cipher_suite_names.cc
new file mode 100644
index 0000000..dfff63f
--- /dev/null
+++ b/net/base/ssl_cipher_suite_names.cc
@@ -0,0 +1,350 @@
+// Copyright (c) 2010 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/base/ssl_cipher_suite_names.h"
+
+#include <stdlib.h>
+
+#include "base/logging.h"
+
+// Rather than storing the names of all the ciphersuites we eliminate the
+// redundancy and break each cipher suite into a key exchange method, cipher
+// and mac. For all the ciphersuites in the IANA registry, we extract each of
+// those components from the name, number them and pack the result into a
+// 16-bit number thus:
+//   (MSB to LSB)
+//   <4 bits> unused
+//   <5 bits> key exchange
+//   <4 bits> cipher
+//   <3 bits> mac
+
+// The following tables were generated by ssl_cipher_suite_names_generate.go,
+// found in the same directory as this file.
+
+struct CipherSuite {
+  uint16 cipher_suite, encoded;
+};
+
+static const struct CipherSuite kCipherSuites[] = {
+  {0x0, 0x0},  // TLS_NULL_WITH_NULL_NULL
+  {0x1, 0x81},  // TLS_RSA_WITH_NULL_MD5
+  {0x2, 0x82},  // TLS_RSA_WITH_NULL_SHA
+  {0x3, 0x109},  // TLS_RSA_EXPORT_WITH_RC4_40_MD5
+  {0x4, 0x91},  // TLS_RSA_WITH_RC4_128_MD5
+  {0x5, 0x92},  // TLS_RSA_WITH_RC4_128_SHA
+  {0x6, 0x119},  // TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
+  {0x7, 0xa2},  // TLS_RSA_WITH_IDEA_CBC_SHA
+  {0x8, 0x12a},  // TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
+  {0x9, 0xb2},  // TLS_RSA_WITH_DES_CBC_SHA
+  {0xa, 0xba},  // TLS_RSA_WITH_3DES_EDE_CBC_SHA
+  {0xb, 0x1aa},  // TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA
+  {0xc, 0x232},  // TLS_DH_DSS_WITH_DES_CBC_SHA
+  {0xd, 0x23a},  // TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA
+  {0xe, 0x2aa},  // TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA
+  {0xf, 0x332},  // TLS_DH_RSA_WITH_DES_CBC_SHA
+  {0x10, 0x33a},  // TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA
+  {0x11, 0x3aa},  // TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
+  {0x12, 0x432},  // TLS_DHE_DSS_WITH_DES_CBC_SHA
+  {0x13, 0x43a},  // TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
+  {0x14, 0x4aa},  // TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
+  {0x15, 0x532},  // TLS_DHE_RSA_WITH_DES_CBC_SHA
+  {0x16, 0x53a},  // TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
+  {0x17, 0x589},  // TLS_DH_anon_EXPORT_WITH_RC4_40_MD5
+  {0x18, 0x611},  // TLS_DH_anon_WITH_RC4_128_MD5
+  {0x19, 0x5aa},  // TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA
+  {0x1a, 0x632},  // TLS_DH_anon_WITH_DES_CBC_SHA
+  {0x1b, 0x63a},  // TLS_DH_anon_WITH_3DES_EDE_CBC_SHA
+  {0x1e, 0x6b2},  // TLS_KRB5_WITH_DES_CBC_SHA
+  {0x1f, 0x6ba},  // TLS_KRB5_WITH_3DES_EDE_CBC_SHA
+  {0x20, 0x692},  // TLS_KRB5_WITH_RC4_128_SHA
+  {0x21, 0x6a2},  // TLS_KRB5_WITH_IDEA_CBC_SHA
+  {0x22, 0x6b1},  // TLS_KRB5_WITH_DES_CBC_MD5
+  {0x23, 0x6b9},  // TLS_KRB5_WITH_3DES_EDE_CBC_MD5
+  {0x24, 0x691},  // TLS_KRB5_WITH_RC4_128_MD5
+  {0x25, 0x6a1},  // TLS_KRB5_WITH_IDEA_CBC_MD5
+  {0x26, 0x742},  // TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA
+  {0x27, 0x71a},  // TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA
+  {0x28, 0x70a},  // TLS_KRB5_EXPORT_WITH_RC4_40_SHA
+  {0x29, 0x741},  // TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5
+  {0x2a, 0x719},  // TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5
+  {0x2b, 0x709},  // TLS_KRB5_EXPORT_WITH_RC4_40_MD5
+  {0x2c, 0x782},  // TLS_PSK_WITH_NULL_SHA
+  {0x2d, 0x802},  // TLS_DHE_PSK_WITH_NULL_SHA
+  {0x2e, 0x882},  // TLS_RSA_PSK_WITH_NULL_SHA
+  {0x2f, 0xca},  // TLS_RSA_WITH_AES_128_CBC_SHA
+  {0x30, 0x24a},  // TLS_DH_DSS_WITH_AES_128_CBC_SHA
+  {0x31, 0x34a},  // TLS_DH_RSA_WITH_AES_128_CBC_SHA
+  {0x32, 0x44a},  // TLS_DHE_DSS_WITH_AES_128_CBC_SHA
+  {0x33, 0x54a},  // TLS_DHE_RSA_WITH_AES_128_CBC_SHA
+  {0x34, 0x64a},  // TLS_DH_anon_WITH_AES_128_CBC_SHA
+  {0x35, 0xd2},  // TLS_RSA_WITH_AES_256_CBC_SHA
+  {0x36, 0x252},  // TLS_DH_DSS_WITH_AES_256_CBC_SHA
+  {0x37, 0x352},  // TLS_DH_RSA_WITH_AES_256_CBC_SHA
+  {0x38, 0x452},  // TLS_DHE_DSS_WITH_AES_256_CBC_SHA
+  {0x39, 0x552},  // TLS_DHE_RSA_WITH_AES_256_CBC_SHA
+  {0x3a, 0x652},  // TLS_DH_anon_WITH_AES_256_CBC_SHA
+  {0x3b, 0x83},  // TLS_RSA_WITH_NULL_SHA256
+  {0x3c, 0xcb},  // TLS_RSA_WITH_AES_128_CBC_SHA256
+  {0x3d, 0xd3},  // TLS_RSA_WITH_AES_256_CBC_SHA256
+  {0x3e, 0x24b},  // TLS_DH_DSS_WITH_AES_128_CBC_SHA256
+  {0x3f, 0x34b},  // TLS_DH_RSA_WITH_AES_128_CBC_SHA256
+  {0x40, 0x44b},  // TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
+  {0x41, 0xda},  // TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
+  {0x42, 0x25a},  // TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA
+  {0x43, 0x35a},  // TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA
+  {0x44, 0x45a},  // TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA
+  {0x45, 0x55a},  // TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
+  {0x46, 0x65a},  // TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA
+  {0x67, 0x54b},  // TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
+  {0x68, 0x253},  // TLS_DH_DSS_WITH_AES_256_CBC_SHA256
+  {0x69, 0x353},  // TLS_DH_RSA_WITH_AES_256_CBC_SHA256
+  {0x6a, 0x453},  // TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
+  {0x6b, 0x553},  // TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
+  {0x6c, 0x64b},  // TLS_DH_anon_WITH_AES_128_CBC_SHA256
+  {0x6d, 0x653},  // TLS_DH_anon_WITH_AES_256_CBC_SHA256
+  {0x84, 0xe2},  // TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
+  {0x85, 0x262},  // TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA
+  {0x86, 0x362},  // TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA
+  {0x87, 0x462},  // TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA
+  {0x88, 0x562},  // TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
+  {0x89, 0x662},  // TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA
+  {0x8a, 0x792},  // TLS_PSK_WITH_RC4_128_SHA
+  {0x8b, 0x7ba},  // TLS_PSK_WITH_3DES_EDE_CBC_SHA
+  {0x8c, 0x7ca},  // TLS_PSK_WITH_AES_128_CBC_SHA
+  {0x8d, 0x7d2},  // TLS_PSK_WITH_AES_256_CBC_SHA
+  {0x8e, 0x812},  // TLS_DHE_PSK_WITH_RC4_128_SHA
+  {0x8f, 0x83a},  // TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA
+  {0x90, 0x84a},  // TLS_DHE_PSK_WITH_AES_128_CBC_SHA
+  {0x91, 0x852},  // TLS_DHE_PSK_WITH_AES_256_CBC_SHA
+  {0x92, 0x892},  // TLS_RSA_PSK_WITH_RC4_128_SHA
+  {0x93, 0x8ba},  // TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA
+  {0x94, 0x8ca},  // TLS_RSA_PSK_WITH_AES_128_CBC_SHA
+  {0x95, 0x8d2},  // TLS_RSA_PSK_WITH_AES_256_CBC_SHA
+  {0x96, 0xea},  // TLS_RSA_WITH_SEED_CBC_SHA
+  {0x97, 0x26a},  // TLS_DH_DSS_WITH_SEED_CBC_SHA
+  {0x98, 0x36a},  // TLS_DH_RSA_WITH_SEED_CBC_SHA
+  {0x99, 0x46a},  // TLS_DHE_DSS_WITH_SEED_CBC_SHA
+  {0x9a, 0x56a},  // TLS_DHE_RSA_WITH_SEED_CBC_SHA
+  {0x9b, 0x66a},  // TLS_DH_anon_WITH_SEED_CBC_SHA
+  {0x9c, 0xf3},  // TLS_RSA_WITH_AES_128_GCM_SHA256
+  {0x9d, 0xfc},  // TLS_RSA_WITH_AES_256_GCM_SHA384
+  {0x9e, 0x573},  // TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
+  {0x9f, 0x57c},  // TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
+  {0xa0, 0x373},  // TLS_DH_RSA_WITH_AES_128_GCM_SHA256
+  {0xa1, 0x37c},  // TLS_DH_RSA_WITH_AES_256_GCM_SHA384
+  {0xa2, 0x473},  // TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
+  {0xa3, 0x47c},  // TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
+  {0xa4, 0x273},  // TLS_DH_DSS_WITH_AES_128_GCM_SHA256
+  {0xa5, 0x27c},  // TLS_DH_DSS_WITH_AES_256_GCM_SHA384
+  {0xa6, 0x673},  // TLS_DH_anon_WITH_AES_128_GCM_SHA256
+  {0xa7, 0x67c},  // TLS_DH_anon_WITH_AES_256_GCM_SHA384
+  {0xa8, 0x7f3},  // TLS_PSK_WITH_AES_128_GCM_SHA256
+  {0xa9, 0x7fc},  // TLS_PSK_WITH_AES_256_GCM_SHA384
+  {0xaa, 0x873},  // TLS_DHE_PSK_WITH_AES_128_GCM_SHA256
+  {0xab, 0x87c},  // TLS_DHE_PSK_WITH_AES_256_GCM_SHA384
+  {0xac, 0x8f3},  // TLS_RSA_PSK_WITH_AES_128_GCM_SHA256
+  {0xad, 0x8fc},  // TLS_RSA_PSK_WITH_AES_256_GCM_SHA384
+  {0xae, 0x7cb},  // TLS_PSK_WITH_AES_128_CBC_SHA256
+  {0xaf, 0x7d4},  // TLS_PSK_WITH_AES_256_CBC_SHA384
+  {0xb0, 0x783},  // TLS_PSK_WITH_NULL_SHA256
+  {0xb1, 0x784},  // TLS_PSK_WITH_NULL_SHA384
+  {0xb2, 0x84b},  // TLS_DHE_PSK_WITH_AES_128_CBC_SHA256
+  {0xb3, 0x854},  // TLS_DHE_PSK_WITH_AES_256_CBC_SHA384
+  {0xb4, 0x803},  // TLS_DHE_PSK_WITH_NULL_SHA256
+  {0xb5, 0x804},  // TLS_DHE_PSK_WITH_NULL_SHA384
+  {0xb6, 0x8cb},  // TLS_RSA_PSK_WITH_AES_128_CBC_SHA256
+  {0xb7, 0x8d4},  // TLS_RSA_PSK_WITH_AES_256_CBC_SHA384
+  {0xb8, 0x883},  // TLS_RSA_PSK_WITH_NULL_SHA256
+  {0xb9, 0x884},  // TLS_RSA_PSK_WITH_NULL_SHA384
+  {0xba, 0xdb},  // TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256
+  {0xbb, 0x25b},  // TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256
+  {0xbc, 0x35b},  // TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256
+  {0xbd, 0x45b},  // TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256
+  {0xbe, 0x55b},  // TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
+  {0xbf, 0x65b},  // TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256
+  {0xc0, 0xe3},  // TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256
+  {0xc1, 0x263},  // TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256
+  {0xc2, 0x363},  // TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256
+  {0xc3, 0x463},  // TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256
+  {0xc4, 0x563},  // TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256
+  {0xc5, 0x663},  // TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256
+  {0xc001, 0x902},  // TLS_ECDH_ECDSA_WITH_NULL_SHA
+  {0xc002, 0x912},  // TLS_ECDH_ECDSA_WITH_RC4_128_SHA
+  {0xc003, 0x93a},  // TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
+  {0xc004, 0x94a},  // TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
+  {0xc005, 0x952},  // TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
+  {0xc006, 0x982},  // TLS_ECDHE_ECDSA_WITH_NULL_SHA
+  {0xc007, 0x992},  // TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
+  {0xc008, 0x9ba},  // TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
+  {0xc009, 0x9ca},  // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
+  {0xc00a, 0x9d2},  // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+  {0xc00b, 0xa02},  // TLS_ECDH_RSA_WITH_NULL_SHA
+  {0xc00c, 0xa12},  // TLS_ECDH_RSA_WITH_RC4_128_SHA
+  {0xc00d, 0xa3a},  // TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
+  {0xc00e, 0xa4a},  // TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
+  {0xc00f, 0xa52},  // TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
+  {0xc010, 0xa82},  // TLS_ECDHE_RSA_WITH_NULL_SHA
+  {0xc011, 0xa92},  // TLS_ECDHE_RSA_WITH_RC4_128_SHA
+  {0xc012, 0xaba},  // TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
+  {0xc013, 0xaca},  // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+  {0xc014, 0xad2},  // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
+  {0xc015, 0xb02},  // TLS_ECDH_anon_WITH_NULL_SHA
+  {0xc016, 0xb12},  // TLS_ECDH_anon_WITH_RC4_128_SHA
+  {0xc017, 0xb3a},  // TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA
+  {0xc018, 0xb4a},  // TLS_ECDH_anon_WITH_AES_128_CBC_SHA
+  {0xc019, 0xb52},  // TLS_ECDH_anon_WITH_AES_256_CBC_SHA
+  {0xc01a, 0xbba},  // TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA
+  {0xc01b, 0xc3a},  // TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA
+  {0xc01c, 0xcba},  // TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA
+  {0xc01d, 0xbca},  // TLS_SRP_SHA_WITH_AES_128_CBC_SHA
+  {0xc01e, 0xc4a},  // TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA
+  {0xc01f, 0xcca},  // TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA
+  {0xc020, 0xbd2},  // TLS_SRP_SHA_WITH_AES_256_CBC_SHA
+  {0xc021, 0xc52},  // TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA
+  {0xc022, 0xcd2},  // TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA
+  {0xc023, 0x9cb},  // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
+  {0xc024, 0x9d4},  // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
+  {0xc025, 0x94b},  // TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
+  {0xc026, 0x954},  // TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
+  {0xc027, 0xacb},  // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
+  {0xc028, 0xad4},  // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
+  {0xc029, 0xa4b},  // TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
+  {0xc02a, 0xa54},  // TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
+  {0xc02b, 0x9f3},  // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+  {0xc02c, 0x9fc},  // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
+  {0xc02d, 0x973},  // TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
+  {0xc02e, 0x97c},  // TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
+  {0xc02f, 0xaf3},  // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+  {0xc030, 0xafc},  // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+  {0xc031, 0xa73},  // TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
+  {0xc032, 0xa7c},  // TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
+  {0xc033, 0xd12},  // TLS_ECDHE_PSK_WITH_RC4_128_SHA
+  {0xc034, 0xd3a},  // TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA
+  {0xc035, 0xd4a},  // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA
+  {0xc036, 0xd52},  // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA
+  {0xc037, 0xd4b},  // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
+  {0xc038, 0xd54},  // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384
+  {0xc039, 0xd02},  // TLS_ECDHE_PSK_WITH_NULL_SHA
+  {0xc03a, 0xd03},  // TLS_ECDHE_PSK_WITH_NULL_SHA256
+  {0xc03b, 0xd04},  // TLS_ECDHE_PSK_WITH_NULL_SHA384
+};
+
+static const struct {
+  char name[15];
+} kKeyExchangeNames[27] = {
+  {"NULL"},  // 0
+  {"RSA"},  // 1
+  {"RSA_EXPORT"},  // 2
+  {"DH_DSS_EXPORT"},  // 3
+  {"DH_DSS"},  // 4
+  {"DH_RSA_EXPORT"},  // 5
+  {"DH_RSA"},  // 6
+  {"DHE_DSS_EXPORT"},  // 7
+  {"DHE_DSS"},  // 8
+  {"DHE_RSA_EXPORT"},  // 9
+  {"DHE_RSA"},  // 10
+  {"DH_anon_EXPORT"},  // 11
+  {"DH_anon"},  // 12
+  {"KRB5"},  // 13
+  {"KRB5_EXPORT"},  // 14
+  {"PSK"},  // 15
+  {"DHE_PSK"},  // 16
+  {"RSA_PSK"},  // 17
+  {"ECDH_ECDSA"},  // 18
+  {"ECDHE_ECDSA"},  // 19
+  {"ECDH_RSA"},  // 20
+  {"ECDHE_RSA"},  // 21
+  {"ECDH_anon"},  // 22
+  {"SRP_SHA"},  // 23
+  {"SRP_SHA_RSA"},  // 24
+  {"SRP_SHA_DSS"},  // 25
+  {"ECDHE_PSK"},  // 26
+};
+
+static const struct {
+  char name[17];
+} kCipherNames[16] = {
+  {"NULL"},  // 0
+  {"RC4_40"},  // 1
+  {"RC4_128"},  // 2
+  {"RC2_CBC_40"},  // 3
+  {"IDEA_CBC"},  // 4
+  {"DES40_CBC"},  // 5
+  {"DES_CBC"},  // 6
+  {"3DES_EDE_CBC"},  // 7
+  {"DES_CBC_40"},  // 8
+  {"AES_128_CBC"},  // 9
+  {"AES_256_CBC"},  // 10
+  {"CAMELLIA_128_CBC"},  // 11
+  {"CAMELLIA_256_CBC"},  // 12
+  {"SEED_CBC"},  // 13
+  {"AES_128_GCM"},  // 14
+  {"AES_256_GCM"},  // 15
+};
+
+static const struct {
+  char name[7];
+} kMacNames[5] = {
+  {"NULL"},  // 0
+  {"MD5"},  // 1
+  {"SHA1"},  // 2
+  {"SHA256"},  // 3
+  {"SHA384"},  // 4
+};
+
+
+namespace net {
+
+static int CipherSuiteCmp(const void* ia, const void* ib) {
+  const CipherSuite* a = static_cast<const CipherSuite*>(ia);
+  const CipherSuite* b = static_cast<const CipherSuite*>(ib);
+
+  if (a->cipher_suite < b->cipher_suite) {
+    return -1;
+  } else if (a->cipher_suite == b->cipher_suite) {
+    return 0;
+  } else {
+    return 1;
+  }
+}
+
+void SSLCipherSuiteToStrings(const char** key_exchange_str,
+                             const char** cipher_str,
+                             const char** mac_str, uint16 cipher_suite) {
+  *key_exchange_str = *cipher_str = *mac_str = "???";
+
+  struct CipherSuite desired;
+  desired.cipher_suite = cipher_suite;
+
+  void* r = bsearch(&desired, kCipherSuites,
+                    arraysize(kCipherSuites), sizeof(kCipherSuites[0]),
+                    CipherSuiteCmp);
+
+  if (!r)
+    return;
+
+  const CipherSuite* cs = static_cast<CipherSuite*>(r);
+
+  const int key_exchange = cs->encoded >> 7;
+  const int cipher = (cs->encoded >> 3) & 0xf;
+  const int mac = cs->encoded & 0x7;
+
+  *key_exchange_str = kKeyExchangeNames[key_exchange].name;
+  *cipher_str = kCipherNames[cipher].name;
+  *mac_str = kMacNames[mac].name;
+}
+
+void SSLCompressionToString(const char** name, uint8 compresssion) {
+  if (compresssion == 0) {
+    *name = "NONE";
+  } else if (compresssion == 1) {
+    *name = "DEFLATE";
+  } else if (compresssion == 64) {
+    *name = "LZS";
+  } else {
+    *name = "???";
+  }
+}
+
+}  // namespace net
diff --git a/net/base/ssl_cipher_suite_names.h b/net/base/ssl_cipher_suite_names.h
new file mode 100644
index 0000000..09429ae
--- /dev/null
+++ b/net/base/ssl_cipher_suite_names.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2010 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_BASE_SSL_CIPHER_SUITE_NAMES_H_
+#define NET_BASE_SSL_CIPHER_SUITE_NAMES_H_
+
+#include "base/basictypes.h"
+
+namespace net {
+
+// SSLCipherSuiteToStrings returns three strings for a given cipher suite
+// number, the name of the key exchange algorithm, the name of the cipher and
+// the name of the MAC. The cipher suite number is the number as sent on the
+// wire and recorded at
+// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
+// If the cipher suite is unknown, the strings are set to "???".
+void SSLCipherSuiteToStrings(const char** key_exchange_str,
+                             const char** cipher_str, const char** mac_str,
+                             uint16 cipher_suite);
+
+// SSLCompressionToString returns the name of the compression algorithm
+// specified by |compression_method|, which is the TLS compression id.
+// If the algorithm is unknown, |name| is set to "???".
+void SSLCompressionToString(const char** name, uint8 compression_method);
+
+}  // namespace net
+
+#endif  // NET_BASE_SSL_CIPHER_SUITE_NAMES_H_
diff --git a/net/base/ssl_cipher_suite_names_generate.go b/net/base/ssl_cipher_suite_names_generate.go
new file mode 100644
index 0000000..f286ce3
--- /dev/null
+++ b/net/base/ssl_cipher_suite_names_generate.go
@@ -0,0 +1,189 @@
+// This program reads in the contents of [1] from /tmp/tls-parameters.xml and
+// writes out a compact form the ciphersuite information found there in.
+// It's used to generate the tables in net/base/ssl_cipher_suite_names.cc
+//
+// [1] http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
+package main
+
+import (
+	"fmt"
+	"os"
+	"sort"
+	"strings"
+	"xml"
+)
+
+// Structures for parsing the XML
+
+type TLSRegistry struct {
+	Registry []Registry
+}
+
+type Registry struct {
+	Id     string "attr"
+	Title  string
+	Record []Record
+}
+
+type Record struct {
+	Value       string
+	Description string
+}
+
+type CipherSuite struct {
+	value  uint16
+	kx     string
+	cipher string
+	mac    string
+}
+
+func fromHex(c byte) int {
+	if c >= '0' && c <= '9' {
+		return int(c - '0')
+	}
+	if c >= 'a' && c <= 'f' {
+		return int(c - 'a' + 10)
+	}
+	if c >= 'A' && c <= 'F' {
+		return int(c - 'A' + 10)
+	}
+	panic("Bad char passed to fromHex")
+}
+
+type TLSValue struct {
+	v    int
+	name string
+}
+
+type TLSMapping []TLSValue
+
+func (m TLSMapping) Len() int {
+	return len(m)
+}
+
+func (m TLSMapping) Less(i, j int) bool {
+	return m[i].v < m[j].v
+}
+
+func (m TLSMapping) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}
+
+func printDict(d map[string]int, name string) {
+	a := make([]TLSValue, len(d))
+
+	maxLen := 0
+	i := 0
+	for k, v := range d {
+		if len(k) > maxLen {
+			maxLen = len(k)
+		}
+		a[i].v = v
+		a[i].name = k
+		i++
+	}
+
+	sort.Sort(TLSMapping(a))
+
+	fmt.Printf("static const struct {\n  char name[%d];\n} %s[%d] = {\n", maxLen+1, name, len(d))
+	for _, m := range a {
+		fmt.Printf("  {\"%s\"},  // %d\n", m.name, m.v)
+	}
+
+	fmt.Printf("};\n\n")
+}
+
+func parseCipherSuiteString(s string) (kx, cipher, mac string) {
+	s = s[4:]
+	i := strings.Index(s, "_WITH_")
+	kx = s[0:i]
+	s = s[i+6:]
+	i = strings.LastIndex(s, "_")
+	cipher = s[0:i]
+	mac = s[i+1:]
+	return
+}
+
+func main() {
+	infile, err := os.Open("/tmp/tls-parameters.xml", os.O_RDONLY, 0)
+	if err != nil {
+		fmt.Printf("Cannot open input: %s\n", err)
+		return
+	}
+
+	var input TLSRegistry
+	err = xml.Unmarshal(infile, &input)
+	if err != nil {
+		fmt.Printf("Error parsing XML: %s\n", err)
+		return
+	}
+
+	var cipherSuitesRegistry *Registry
+	for _, r := range input.Registry {
+		if r.Id == "tls-parameters-4" {
+			cipherSuitesRegistry = &r
+			break
+		}
+	}
+
+	if cipherSuitesRegistry == nil {
+		fmt.Printf("Didn't find tls-parameters-4 registry\n")
+	}
+
+	kxs := make(map[string]int)
+	next_kx := 0
+	ciphers := make(map[string]int)
+	next_cipher := 0
+	macs := make(map[string]int)
+	next_mac := 0
+	lastValue := uint16(0)
+
+	fmt.Printf("struct CipherSuite {\n  uint16 cipher_suite, encoded;\n};\n\n")
+	fmt.Printf("static const struct CipherSuite kCipherSuites[] = {\n")
+
+	for _, r := range cipherSuitesRegistry.Record {
+		if strings.Index(r.Description, "_WITH_") == -1 {
+			continue
+		}
+
+		value := uint16(fromHex(r.Value[2])<<12 | fromHex(r.Value[3])<<8 | fromHex(r.Value[7])<<4 | fromHex(r.Value[8]))
+		kx, cipher, mac := parseCipherSuiteString(r.Description)
+
+		if value < lastValue {
+			panic("Input isn't sorted")
+		}
+		lastValue = value
+
+		var kx_n, cipher_n, mac_n int
+		var ok bool
+
+		if kx_n, ok = kxs[kx]; !ok {
+			kxs[kx] = next_kx
+			kx_n = next_kx
+			next_kx++
+		}
+		if cipher_n, ok = ciphers[cipher]; !ok {
+			ciphers[cipher] = next_cipher
+			cipher_n = next_cipher
+			next_cipher++
+		}
+		if mac_n, ok = macs[mac]; !ok {
+			macs[mac] = next_mac
+			mac_n = next_mac
+			next_mac++
+		}
+
+		if kx_n > 32 || cipher_n > 15 || mac_n > 7 {
+			panic("Need to shift bit boundaries")
+		}
+
+		encoded := (kx_n << 7) | (cipher_n << 3) | mac_n
+		fmt.Printf("  {0x%x, 0x%x},  // %s\n", value, encoded, r.Description)
+	}
+
+	fmt.Printf("};\n\n")
+
+	printDict(kxs, "kKeyExchangeNames")
+	printDict(ciphers, "kCipherNames")
+	printDict(macs, "kMacNames")
+}
diff --git a/net/base/ssl_cipher_suite_names_unittest.cc b/net/base/ssl_cipher_suite_names_unittest.cc
new file mode 100644
index 0000000..3a9c2ee
--- /dev/null
+++ b/net/base/ssl_cipher_suite_names_unittest.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2010 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/base/ssl_cipher_suite_names.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+TEST(CipherSuiteNamesTest, Basic) {
+  const char *key_exchange, *cipher, *mac;
+  SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, 0xc001);
+  EXPECT_STREQ(key_exchange, "ECDH_ECDSA");
+  EXPECT_STREQ(cipher, "NULL");
+  EXPECT_STREQ(mac, "SHA1");
+
+  SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, 0xff31);
+  EXPECT_STREQ(key_exchange, "???");
+  EXPECT_STREQ(cipher, "???");
+  EXPECT_STREQ(mac, "???");
+}
+
+}  // anonymous namespace
+
+}  // namespace net
diff --git a/net/base/ssl_config_service.cc b/net/base/ssl_config_service.cc
index 67d1349..06dc610 100644
--- a/net/base/ssl_config_service.cc
+++ b/net/base/ssl_config_service.cc
@@ -25,4 +25,31 @@
 #endif
 }
 
+// static
+bool SSLConfigService::IsKnownStrictTLSServer(const std::string& hostname) {
+  // If you wish to add an entry to this list, please contact agl AT chromium
+  // DOT org.
+  //
+  // If this list starts growing, it'll need to be something more efficient
+  // than a linear list.
+  static const char kStrictServers[][20] = {
+      "www.google.com",
+      "mail.google.com",
+      "www.gmail.com",
+      "docs.google.com",
+      "clients1.google.com",
+
+      // Removed until we update the XMPP servers with the renegotiation
+      // extension.
+      // "gmail.com",
+  };
+
+  for (size_t i = 0; i < arraysize(kStrictServers); i++) {
+    if (strcmp(hostname.c_str(), kStrictServers[i]) == 0)
+      return true;
+  }
+
+  return false;
+}
+
 }  // namespace net
diff --git a/net/base/ssl_config_service.h b/net/base/ssl_config_service.h
index 45c1fc6..3f0f479 100644
--- a/net/base/ssl_config_service.h
+++ b/net/base/ssl_config_service.h
@@ -18,8 +18,8 @@
   // Default to SSL 2.0 off, SSL 3.0 on, and TLS 1.0 on.
   SSLConfig()
       : rev_checking_enabled(true),  ssl2_enabled(false), ssl3_enabled(true),
-        tls1_enabled(true), send_client_cert(false), verify_ev_cert(false),
-        next_protos("\007http1.1") {
+        tls1_enabled(true), ssl3_fallback(false), send_client_cert(false),
+        verify_ev_cert(false) {
   }
 
   bool rev_checking_enabled;  // True if server certificate revocation
@@ -27,6 +27,8 @@
   bool ssl2_enabled;  // True if SSL 2.0 is enabled.
   bool ssl3_enabled;  // True if SSL 3.0 is enabled.
   bool tls1_enabled;  // True if TLS 1.0 is enabled.
+  bool ssl3_fallback;  // True if we are falling back to SSL 3.0 (one still
+                       // needs to clear tls1_enabled).
 
   // TODO(wtc): move the following members to a new SSLParams structure.  They
   // are not SSL configuration settings.
@@ -85,6 +87,14 @@
   // May not be thread-safe, should only be called on the IO thread.
   virtual void GetSSLConfig(SSLConfig* config) = 0;
 
+  // Returns true if the given hostname is known to be 'strict'. This means
+  // that we will require the renegotiation extension and will always use TLS
+  // (no SSLv3 fallback).
+  //
+  // If you wish to add an element to this list, file a bug at
+  // http://crbug.com and email the link to agl AT chromium DOT org.
+  static bool IsKnownStrictTLSServer(const std::string& hostname);
+
  protected:
   friend class base::RefCountedThreadSafe<SSLConfigService>;
 
diff --git a/net/base/ssl_config_service_mac.h b/net/base/ssl_config_service_mac.h
index 5a60803..6d639ca 100644
--- a/net/base/ssl_config_service_mac.h
+++ b/net/base/ssl_config_service_mac.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -48,7 +48,7 @@
   base::TimeTicks config_time_;
   bool ever_updated_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(SSLConfigServiceMac);
+  DISALLOW_COPY_AND_ASSIGN(SSLConfigServiceMac);
 };
 
 }  // namespace net
diff --git a/net/base/ssl_config_service_win.h b/net/base/ssl_config_service_win.h
index e7b77c7..928ca1a 100644
--- a/net/base/ssl_config_service_win.h
+++ b/net/base/ssl_config_service_win.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -53,7 +53,7 @@
   base::TimeTicks config_time_;
   bool ever_updated_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(SSLConfigServiceWin);
+  DISALLOW_COPY_AND_ASSIGN(SSLConfigServiceWin);
 };
 
 }  // namespace net
diff --git a/net/base/ssl_connection_status_flags.h b/net/base/ssl_connection_status_flags.h
new file mode 100644
index 0000000..9ec22fa
--- /dev/null
+++ b/net/base/ssl_connection_status_flags.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2010 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_BASE_SSL_CONNECTION_STATUS_FLAGS_H_
+#define NET_BASE_SSL_CONNECTION_STATUS_FLAGS_H_
+
+namespace net {
+
+// Status flags for SSLInfo::connection_status.
+enum {
+  // The lower 16 bits are reserved for the TLS ciphersuite id.
+  SSL_CONNECTION_CIPHERSUITE_SHIFT = 0,
+  SSL_CONNECTION_CIPHERSUITE_MASK = 0xffff,
+
+  // The next two bits are reserved for the compression used.
+  SSL_CONNECTION_COMPRESSION_SHIFT = 16,
+  SSL_CONNECTION_COMPRESSION_MASK = 3,
+
+  // We fell back to SSLv3 for this connection.
+  SSL_CONNECTION_SSL3_FALLBACK = 1 << 18,
+
+  // The server doesn't support the renegotiation_info extension. If this bit
+  // is not set then either the extension isn't supported, or we don't have any
+  // knowledge either way. (The latter case will occur when we use an SSL
+  // library that doesn't report it, like SChannel.)
+  SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION = 1 << 19,
+
+  // 1 << 31 (the sign bit) is reserved so that the SSL connection status will
+  // never be negative.
+};
+
+inline int SSLConnectionStatusToCipherSuite(int connection_status) {
+  return (connection_status >> SSL_CONNECTION_CIPHERSUITE_SHIFT) &
+         SSL_CONNECTION_CIPHERSUITE_MASK;
+}
+
+inline int SSLConnectionStatusToCompression(int connection_status) {
+  return (connection_status >> SSL_CONNECTION_COMPRESSION_SHIFT) &
+         SSL_CONNECTION_COMPRESSION_MASK;
+}
+
+}  // namespace net
+
+#endif  // NET_BASE_SSL_CONNECTION_STATUS_FLAGS_H_
diff --git a/net/base/ssl_info.h b/net/base/ssl_info.h
index 3fe0ce4..7c14163 100644
--- a/net/base/ssl_info.h
+++ b/net/base/ssl_info.h
@@ -16,12 +16,13 @@
 // This is really a struct.  All members are public.
 class SSLInfo {
  public:
-  SSLInfo() : cert_status(0), security_bits(-1) { }
+  SSLInfo() : cert_status(0), security_bits(-1), connection_status(0) { }
 
   void Reset() {
     cert = NULL;
-    security_bits = -1;
     cert_status = 0;
+    security_bits = -1;
+    connection_status = 0;
   }
 
   bool is_valid() const { return cert != NULL; }
@@ -43,6 +44,12 @@
   // 0 means the connection is not encrypted.
   // -1 means the security strength is unknown.
   int security_bits;
+
+  // Information about the SSL connection itself. See
+  // ssl_connection_status_flags.h for values. The ciphersuite and compression
+  // in use are encoded within.
+  // TODO(agl): also encode the protocol version used.
+  int connection_status;
 };
 
 }  // namespace net
diff --git a/net/base/static_cookie_policy.h b/net/base/static_cookie_policy.h
index 4734d33..d903149 100644
--- a/net/base/static_cookie_policy.h
+++ b/net/base/static_cookie_policy.h
@@ -5,6 +5,8 @@
 #ifndef NET_BASE_STATIC_COOKIE_POLICY_H_
 #define NET_BASE_STATIC_COOKIE_POLICY_H_
 
+#include <string>
+
 #include "base/basictypes.h"
 #include "net/base/cookie_policy.h"
 
diff --git a/net/base/sys_addrinfo.h b/net/base/sys_addrinfo.h
index cfdd424..62a6317 100644
--- a/net/base/sys_addrinfo.h
+++ b/net/base/sys_addrinfo.h
@@ -20,5 +20,6 @@
 #include <ws2tcpip.h>
 #elif defined(OS_POSIX)
 #include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
 #endif
-
diff --git a/net/base/telnet_server.cc b/net/base/telnet_server.cc
index 33eb5b1..5b027d7 100644
--- a/net/base/telnet_server.cc
+++ b/net/base/telnet_server.cc
@@ -197,7 +197,9 @@
     case EXPECTING_NEW_LINE:
       if (c == TelnetProtocol::LF) {
         Send("\n", 1);
-        socket_delegate_->DidRead(this, command_line_);
+        socket_delegate_->DidRead(this,
+                                  command_line_.c_str(),
+                                  command_line_.length());
         command_line_ = "";
       }
       input_state_ = NOT_IN_IAC_OR_ESC_SEQUENCE;
diff --git a/net/base/telnet_server.h b/net/base/telnet_server.h
index d8d21e8..8160141 100644
--- a/net/base/telnet_server.h
+++ b/net/base/telnet_server.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -55,7 +55,7 @@
   int iac_option_;  // Last option read.
   std::string command_line_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(TelnetServer);
+  DISALLOW_COPY_AND_ASSIGN(TelnetServer);
 };
 
 #endif // NET_BASE_TELNET_SERVER_H_
diff --git a/net/base/telnet_server_unittest.cc b/net/base/telnet_server_unittest.cc
index 483309c..1112bd2 100644
--- a/net/base/telnet_server_unittest.cc
+++ b/net/base/telnet_server_unittest.cc
@@ -59,7 +59,8 @@
   scoped_refptr<TelnetServerTester> tester_;
 };
 
-TEST_F(TelnetServerTest, ServerClientSend) {
+// Flaky, http://crbug.com/38093.
+TEST_F(TelnetServerTest, FLAKY_ServerClientSend) {
   tester_->TestClientSend();
 }
 
diff --git a/net/base/test_certificate_data.h b/net/base/test_certificate_data.h
index e475906..1ca6db8 100644
--- a/net/base/test_certificate_data.h
+++ b/net/base/test_certificate_data.h
@@ -443,127 +443,4 @@
   0x23, 0x82, 0x6f, 0xdb, 0xb8, 0x22, 0x1c, 0x43, 0x96, 0x07, 0xa8, 0xbb
 };
 
-// A certificate for https://www.unosoft.hu/, whose AIA extension contains
-// an LDAP URL without a host name.  Expires on 2011-09-08.
-unsigned char unosoft_hu_der[] = {
-  0x30, 0x82, 0x05, 0x7f, 0x30, 0x82, 0x04, 0x67, 0xa0, 0x03, 0x02, 0x01,
-  0x02, 0x02, 0x0a, 0x17, 0x73, 0x3e, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x3f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
-  0x01, 0x05, 0x05, 0x00, 0x30, 0x4a, 0x31, 0x15, 0x30, 0x13, 0x06, 0x0a,
-  0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x05,
-  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x31, 0x17, 0x30, 0x15, 0x06, 0x0a, 0x09,
-  0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x07, 0x75,
-  0x6e, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03,
-  0x55, 0x04, 0x03, 0x13, 0x0f, 0x55, 0x4e, 0x4f, 0x2d, 0x53, 0x4f, 0x46,
-  0x54, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d,
-  0x30, 0x39, 0x31, 0x32, 0x30, 0x37, 0x31, 0x30, 0x33, 0x35, 0x35, 0x34,
-  0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x39, 0x30, 0x38, 0x31, 0x38, 0x30,
-  0x36, 0x30, 0x37, 0x5a, 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
-  0x55, 0x04, 0x06, 0x13, 0x02, 0x48, 0x55, 0x31, 0x11, 0x30, 0x0f, 0x06,
-  0x03, 0x55, 0x04, 0x07, 0x13, 0x08, 0x42, 0x75, 0x64, 0x61, 0x70, 0x65,
-  0x73, 0x74, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
-  0x1e, 0x55, 0x4e, 0x4f, 0x2d, 0x53, 0x4f, 0x46, 0x54, 0x20, 0x53, 0x7a,
-  0x61, 0x6d, 0x69, 0x74, 0x61, 0x73, 0x74, 0x65, 0x63, 0x68, 0x6e, 0x69,
-  0x6b, 0x61, 0x69, 0x20, 0x4b, 0x46, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06,
-  0x03, 0x55, 0x04, 0x03, 0x13, 0x0e, 0x77, 0x77, 0x77, 0x2e, 0x75, 0x6e,
-  0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x68, 0x75, 0x30, 0x81, 0x9f, 0x30,
-  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
-  0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81,
-  0x00, 0xa4, 0x07, 0x76, 0xf1, 0xe0, 0xfa, 0x16, 0x58, 0xcc, 0x45, 0xee,
-  0xbb, 0x8f, 0xa1, 0x1b, 0x4b, 0x95, 0xe5, 0x6e, 0xc1, 0x93, 0xa3, 0xc1,
-  0x0a, 0x79, 0x33, 0x92, 0xfa, 0x6a, 0xb8, 0xf1, 0x14, 0x35, 0x45, 0x8f,
-  0xc5, 0x82, 0x0b, 0x71, 0xca, 0x2c, 0x48, 0x7e, 0x15, 0xa6, 0xa6, 0x66,
-  0x4f, 0x0b, 0x41, 0xe8, 0xdf, 0xeb, 0x96, 0x06, 0xb0, 0xed, 0x08, 0xbb,
-  0xe9, 0x14, 0x97, 0xf8, 0xdd, 0x66, 0x3f, 0xb7, 0x32, 0x7e, 0x8b, 0xab,
-  0x80, 0xd1, 0x2b, 0xd7, 0x8b, 0x0e, 0xfb, 0x88, 0x68, 0x73, 0x6b, 0xd7,
-  0x94, 0x22, 0x42, 0x94, 0x07, 0xd5, 0xe3, 0xc4, 0x98, 0x25, 0x01, 0x66,
-  0xe7, 0xc2, 0x5a, 0x1a, 0x3d, 0x0b, 0x01, 0x14, 0x49, 0x32, 0x47, 0x6c,
-  0xff, 0x19, 0x07, 0x6c, 0x1a, 0x47, 0x2f, 0x87, 0x4c, 0x3a, 0xc4, 0x61,
-  0xae, 0x77, 0x33, 0x4f, 0x27, 0xc1, 0xa5, 0xdd, 0x63, 0x02, 0x03, 0x01,
-  0x00, 0x01, 0xa3, 0x82, 0x02, 0xd1, 0x30, 0x82, 0x02, 0xcd, 0x30, 0x1d,
-  0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xac, 0xee, 0x30,
-  0xf0, 0x0e, 0x73, 0x42, 0x73, 0x2f, 0x8f, 0xb5, 0x62, 0xcb, 0xb6, 0x3e,
-  0xdb, 0x62, 0x6c, 0xb2, 0x0a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
-  0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x29, 0x53, 0x4a, 0x9d, 0x9c, 0xd9,
-  0xf9, 0xda, 0x04, 0xfe, 0x46, 0x3a, 0x76, 0x49, 0x5c, 0xdd, 0x3b, 0x0e,
-  0x98, 0x76, 0x30, 0x82, 0x01, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
-  0x82, 0x01, 0x05, 0x30, 0x82, 0x01, 0x01, 0x30, 0x81, 0xfe, 0xa0, 0x81,
-  0xfb, 0xa0, 0x81, 0xf8, 0x86, 0x81, 0xb8, 0x6c, 0x64, 0x61, 0x70, 0x3a,
-  0x2f, 0x2f, 0x2f, 0x43, 0x4e, 0x3d, 0x55, 0x4e, 0x4f, 0x2d, 0x53, 0x4f,
-  0x46, 0x54, 0x25, 0x32, 0x30, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2c,
-  0x43, 0x4e, 0x3d, 0x55, 0x4e, 0x4f, 0x44, 0x43, 0x2c, 0x43, 0x4e, 0x3d,
-  0x43, 0x44, 0x50, 0x2c, 0x43, 0x4e, 0x3d, 0x50, 0x75, 0x62, 0x6c, 0x69,
-  0x63, 0x25, 0x32, 0x30, 0x4b, 0x65, 0x79, 0x25, 0x32, 0x30, 0x53, 0x65,
-  0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x65,
-  0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x6f,
-  0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c,
-  0x44, 0x43, 0x3d, 0x75, 0x6e, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2c, 0x44,
-  0x43, 0x3d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x3f, 0x63, 0x65, 0x72, 0x74,
-  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x6f, 0x63,
-  0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x3f, 0x62, 0x61,
-  0x73, 0x65, 0x3f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61,
-  0x73, 0x73, 0x3d, 0x63, 0x52, 0x4c, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69,
-  0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x86,
-  0x3b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x75, 0x6e, 0x6f, 0x64,
-  0x63, 0x2e, 0x75, 0x6e, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x6c, 0x6f,
-  0x63, 0x61, 0x6c, 0x2f, 0x43, 0x65, 0x72, 0x74, 0x45, 0x6e, 0x72, 0x6f,
-  0x6c, 0x6c, 0x2f, 0x55, 0x4e, 0x4f, 0x2d, 0x53, 0x4f, 0x46, 0x54, 0x25,
-  0x32, 0x30, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c,
-  0x30, 0x82, 0x01, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
-  0x01, 0x01, 0x04, 0x82, 0x01, 0x16, 0x30, 0x82, 0x01, 0x12, 0x30, 0x81,
-  0xb2, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86,
-  0x81, 0xa5, 0x6c, 0x64, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x2f, 0x43, 0x4e,
-  0x3d, 0x55, 0x4e, 0x4f, 0x2d, 0x53, 0x4f, 0x46, 0x54, 0x25, 0x32, 0x30,
-  0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x41, 0x49,
-  0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x25,
-  0x32, 0x30, 0x4b, 0x65, 0x79, 0x25, 0x32, 0x30, 0x53, 0x65, 0x72, 0x76,
-  0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x53, 0x65, 0x72, 0x76,
-  0x69, 0x63, 0x65, 0x73, 0x2c, 0x43, 0x4e, 0x3d, 0x43, 0x6f, 0x6e, 0x66,
-  0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x44, 0x43,
-  0x3d, 0x75, 0x6e, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2c, 0x44, 0x43, 0x3d,
-  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x3f, 0x63, 0x41, 0x43, 0x65, 0x72, 0x74,
-  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x3f, 0x62, 0x61, 0x73, 0x65,
-  0x3f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73,
-  0x3d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
-  0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
-  0x5b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86,
-  0x4f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x75, 0x6e, 0x6f, 0x64,
-  0x63, 0x2e, 0x75, 0x6e, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x6c, 0x6f,
-  0x63, 0x61, 0x6c, 0x2f, 0x43, 0x65, 0x72, 0x74, 0x45, 0x6e, 0x72, 0x6f,
-  0x6c, 0x6c, 0x2f, 0x55, 0x4e, 0x4f, 0x44, 0x43, 0x2e, 0x75, 0x6e, 0x6f,
-  0x73, 0x6f, 0x66, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x55,
-  0x4e, 0x4f, 0x2d, 0x53, 0x4f, 0x46, 0x54, 0x25, 0x32, 0x30, 0x52, 0x6f,
-  0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x21, 0x06, 0x09,
-  0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x04, 0x14, 0x1e,
-  0x12, 0x00, 0x57, 0x00, 0x65, 0x00, 0x62, 0x00, 0x53, 0x00, 0x65, 0x00,
-  0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x30, 0x0c, 0x06, 0x03, 0x55,
-  0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06,
-  0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x13,
-  0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, 0x2b,
-  0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a,
-  0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
-  0x01, 0x01, 0x00, 0xaa, 0xc8, 0x95, 0xc4, 0x61, 0xcf, 0x3a, 0x6d, 0xf1,
-  0x66, 0xba, 0x2d, 0x24, 0x06, 0xe3, 0x1e, 0x33, 0x9a, 0x21, 0xda, 0x48,
-  0x99, 0xce, 0xfa, 0x36, 0x98, 0x78, 0x20, 0xc4, 0x81, 0xda, 0x5a, 0x9e,
-  0x45, 0xfd, 0xfa, 0x1e, 0xe6, 0x11, 0x72, 0xf2, 0xfd, 0x58, 0x79, 0x29,
-  0x52, 0x6a, 0xe6, 0xf3, 0x1b, 0xab, 0x28, 0xe6, 0x92, 0xd3, 0x65, 0x30,
-  0xf5, 0xd6, 0x51, 0x40, 0x01, 0xc0, 0x92, 0xbb, 0x4f, 0x8f, 0x74, 0xae,
-  0x78, 0x5d, 0x2c, 0x78, 0xce, 0x9c, 0xae, 0x90, 0x50, 0x1e, 0x67, 0x79,
-  0xc1, 0x84, 0xba, 0x0c, 0xff, 0x02, 0xe2, 0x31, 0xbb, 0x72, 0x75, 0x31,
-  0x16, 0x08, 0x10, 0x0e, 0xb2, 0x40, 0x6d, 0xa1, 0x52, 0xb8, 0x4b, 0x52,
-  0x47, 0xbe, 0xaa, 0x6f, 0x31, 0xd0, 0xb9, 0x52, 0xef, 0xb7, 0x3e, 0xf3,
-  0xae, 0x49, 0xf5, 0x1e, 0x33, 0x76, 0x43, 0xf3, 0x74, 0xeb, 0x7b, 0x22,
-  0xdf, 0x46, 0x15, 0x2b, 0xb5, 0xe5, 0x10, 0x24, 0x5d, 0x69, 0x30, 0x5a,
-  0xb0, 0xfe, 0xa2, 0x4d, 0xf5, 0xe6, 0x67, 0x87, 0x18, 0x81, 0x2d, 0x3d,
-  0xa2, 0xfb, 0xc3, 0x47, 0xc2, 0x87, 0x03, 0x5a, 0x2b, 0x3d, 0xdf, 0x7c,
-  0x52, 0xed, 0x24, 0xf3, 0x9e, 0x55, 0x4c, 0x76, 0x59, 0x0d, 0x29, 0x04,
-  0x70, 0xaa, 0x9c, 0x83, 0x8e, 0x6c, 0x3e, 0x46, 0xe3, 0x1b, 0x2e, 0x88,
-  0xd7, 0x68, 0x06, 0xbe, 0x92, 0x2b, 0x4a, 0x1d, 0x4a, 0x6a, 0xa6, 0x13,
-  0x75, 0xf1, 0x52, 0x5f, 0xfa, 0xf7, 0x14, 0x83, 0x62, 0x54, 0xb5, 0x69,
-  0x83, 0xd8, 0x9f, 0xce, 0xf0, 0xb9, 0xec, 0x05, 0x33, 0x85, 0xfc, 0xd1,
-  0xe0, 0x23, 0xd4, 0xab, 0xa2, 0xcd, 0xac, 0x90, 0xc5, 0xe4, 0x23, 0x1a,
-  0xc4, 0xd7, 0x38, 0xcf, 0x34, 0x43, 0xba, 0x58, 0x63, 0x92, 0xed, 0x11,
-  0x8e, 0x05, 0x08, 0xbe, 0xc9, 0x9a, 0x4b
-};
-
 }  // namespace
diff --git a/net/base/test_completion_callback.h b/net/base/test_completion_callback.h
index 5fa14ae..1d24532 100644
--- a/net/base/test_completion_callback.h
+++ b/net/base/test_completion_callback.h
@@ -5,6 +5,7 @@
 #ifndef NET_BASE_TEST_COMPLETION_CALLBACK_H_
 #define NET_BASE_TEST_COMPLETION_CALLBACK_H_
 
+#include "base/callback.h"
 #include "base/message_loop.h"
 #include "net/base/completion_callback.h"
 #include "net/base/net_errors.h"
diff --git a/net/base/transport_security_state.cc b/net/base/transport_security_state.cc
index 35b930c..5ddcf39 100644
--- a/net/base/transport_security_state.cc
+++ b/net/base/transport_security_state.cc
@@ -27,12 +27,21 @@
   const std::string canonicalised_host = CanonicaliseHost(host);
   if (canonicalised_host.empty())
     return;
+
+  bool temp;
+  if (isPreloadedSTS(canonicalised_host, &temp))
+    return;
+
   char hashed[base::SHA256_LENGTH];
   base::SHA256HashString(canonicalised_host, hashed, sizeof(hashed));
 
-  AutoLock lock(lock_);
+  // Use the original creation date if we already have this host.
+  DomainState state_copy(state);
+  DomainState existing_state;
+  if (IsEnabledForHost(&existing_state, host))
+    state_copy.created = existing_state.created;
 
-  enabled_hosts_[std::string(hashed, sizeof(hashed))] = state;
+  enabled_hosts_[std::string(hashed, sizeof(hashed))] = state_copy;
   DirtyNotify();
 }
 
@@ -42,8 +51,15 @@
   if (canonicalised_host.empty())
     return false;
 
+  bool include_subdomains;
+  if (isPreloadedSTS(canonicalised_host, &include_subdomains)) {
+    result->created = result->expiry = base::Time::FromTimeT(0);
+    result->mode = DomainState::MODE_STRICT;
+    result->include_subdomains = include_subdomains;
+    return true;
+  }
+
   base::Time current_time(base::Time::Now());
-  AutoLock lock(lock_);
 
   for (size_t i = 0; canonicalised_host[i]; i += canonicalised_host[i] + 1) {
     char hashed_domain[base::SHA256_LENGTH];
@@ -175,8 +191,6 @@
 
 void TransportSecurityState::SetDelegate(
     TransportSecurityState::Delegate* delegate) {
-  AutoLock lock(lock_);
-
   delegate_ = delegate;
 }
 
@@ -202,13 +216,12 @@
 }
 
 bool TransportSecurityState::Serialise(std::string* output) {
-  AutoLock lock(lock_);
-
   DictionaryValue toplevel;
   for (std::map<std::string, DomainState>::const_iterator
        i = enabled_hosts_.begin(); i != enabled_hosts_.end(); ++i) {
     DictionaryValue* state = new DictionaryValue;
     state->SetBoolean(L"include_subdomains", i->second.include_subdomains);
+    state->SetReal(L"created", i->second.created.ToDoubleT());
     state->SetReal(L"expiry", i->second.expiry.ToDoubleT());
 
     switch (i->second.mode) {
@@ -234,9 +247,8 @@
   return true;
 }
 
-bool TransportSecurityState::Deserialise(const std::string& input) {
-  AutoLock lock(lock_);
-
+bool TransportSecurityState::Deserialise(const std::string& input,
+                                         bool* dirty) {
   enabled_hosts_.clear();
 
   scoped_ptr<Value> value(
@@ -246,6 +258,7 @@
 
   DictionaryValue* dict_value = reinterpret_cast<DictionaryValue*>(value.get());
   const base::Time current_time(base::Time::Now());
+  bool dirtied = false;
 
   for (DictionaryValue::key_iterator i = dict_value->begin_keys();
        i != dict_value->end_keys(); ++i) {
@@ -255,6 +268,7 @@
 
     bool include_subdomains;
     std::string mode_string;
+    double created;
     double expiry;
 
     if (!state->GetBoolean(L"include_subdomains", &include_subdomains) ||
@@ -277,8 +291,21 @@
     }
 
     base::Time expiry_time = base::Time::FromDoubleT(expiry);
-    if (expiry_time <= current_time)
+    base::Time created_time;
+    if (state->GetReal(L"created", &created)) {
+      created_time = base::Time::FromDoubleT(created);
+    } else {
+      // We're migrating an old entry with no creation date. Make sure we
+      // write the new date back in a reasonable time frame.
+      dirtied = true;
+      created_time = base::Time::Now();
+    }
+
+    if (expiry_time <= current_time) {
+      // Make sure we dirty the state if we drop an entry.
+      dirtied = true;
       continue;
+    }
 
     std::string hashed = ExternalStringToHashedDomain(*i);
     if (hashed.empty())
@@ -286,14 +313,33 @@
 
     DomainState new_state;
     new_state.mode = mode;
+    new_state.created = created_time;
     new_state.expiry = expiry_time;
     new_state.include_subdomains = include_subdomains;
     enabled_hosts_[hashed] = new_state;
   }
 
+  *dirty = dirtied;
   return true;
 }
 
+void TransportSecurityState::DeleteSince(const base::Time& time) {
+  bool dirtied = false;
+
+  std::map<std::string, DomainState>::iterator i = enabled_hosts_.begin();
+  while (i != enabled_hosts_.end()) {
+    if (i->second.created >= time) {
+      dirtied = true;
+      enabled_hosts_.erase(i++);
+    } else {
+      i++;
+    }
+  }
+
+  if (dirtied)
+    DirtyNotify();
+}
+
 void TransportSecurityState::DirtyNotify() {
   if (delegate_)
     delegate_->StateIsDirty(this);
@@ -334,4 +380,37 @@
   return new_host;
 }
 
+// isPreloadedSTS returns true if the canonicalised hostname should always be
+// considered to have STS enabled.
+// static
+bool TransportSecurityState::isPreloadedSTS(
+    const std::string& canonicalised_host, bool *include_subdomains) {
+  // In the medium term this list is likely to just be hardcoded here. This,
+  // slightly odd, form removes the need for additional relocations records.
+  static const struct {
+    uint8 length;
+    bool include_subdomains;
+    char dns_name[30];
+  } kPreloadedSTS[] = {
+    {16, false, "\003www\006paypal\003com"},
+    {16, false, "\003www\006elanex\003biz"},
+    {12, true,  "\006jottit\003com"},
+  };
+  static const size_t kNumPreloadedSTS = ARRAYSIZE_UNSAFE(kPreloadedSTS);
+
+  for (size_t i = 0; canonicalised_host[i]; i += canonicalised_host[i] + 1) {
+    for (size_t j = 0; j < kNumPreloadedSTS; j++) {
+      if (kPreloadedSTS[j].length == canonicalised_host.size() + 1 - i &&
+          (kPreloadedSTS[j].include_subdomains || i == 0) &&
+          memcmp(kPreloadedSTS[j].dns_name, &canonicalised_host[i],
+                 kPreloadedSTS[j].length) == 0) {
+        *include_subdomains = kPreloadedSTS[j].include_subdomains;
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
 }  // namespace
diff --git a/net/base/transport_security_state.h b/net/base/transport_security_state.h
index 360eb0b..6d03776 100644
--- a/net/base/transport_security_state.h
+++ b/net/base/transport_security_state.h
@@ -12,6 +12,7 @@
 #include "base/lock.h"
 #include "base/ref_counted.h"
 #include "base/time.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
 
 class GURL;
 
@@ -48,8 +49,10 @@
 
     DomainState()
         : mode(MODE_STRICT),
+          created(base::Time::Now()),
           include_subdomains(false) { }
 
+    base::Time created;  // when this host entry was first created
     base::Time expiry;  // the absolute time (UTC) when this record expires
     bool include_subdomains;  // subdomains included?
   };
@@ -61,6 +64,9 @@
   // *result is filled out.
   bool IsEnabledForHost(DomainState* result, const std::string& host);
 
+  // Deletes all records created since a given time.
+  void DeleteSince(const base::Time& time);
+
   // Returns |true| if |value| parses as a valid *-Transport-Security
   // header value.  The values of max-age and and includeSubDomains are
   // returned in |max_age| and |include_subdomains|, respectively.  The out
@@ -74,15 +80,19 @@
     // This function may not block and may be called with internal locks held.
     // Thus it must not reenter the TransportSecurityState object.
     virtual void StateIsDirty(TransportSecurityState* state) = 0;
+
+   protected:
+    virtual ~Delegate() {}
   };
 
   void SetDelegate(Delegate*);
 
   bool Serialise(std::string* output);
-  bool Deserialise(const std::string& state);
+  bool Deserialise(const std::string& state, bool* dirty);
 
  private:
   friend class base::RefCountedThreadSafe<TransportSecurityState>;
+  FRIEND_TEST(TransportSecurityStateTest, IsPreloaded);
 
   ~TransportSecurityState() {}
 
@@ -95,13 +105,12 @@
   // ('www.google.com') to the form used in DNS: "\x03www\x06google\x03com"
   std::map<std::string, DomainState> enabled_hosts_;
 
-  // Protect access to our data members with this lock.
-  Lock lock_;
-
   // Our delegate who gets notified when we are dirtied, or NULL.
   Delegate* delegate_;
 
   static std::string CanonicaliseHost(const std::string& host);
+  static bool isPreloadedSTS(const std::string& canonicalised_host,
+                             bool* out_include_subdomains);
 
   DISALLOW_COPY_AND_ASSIGN(TransportSecurityState);
 };
diff --git a/net/base/transport_security_state_unittest.cc b/net/base/transport_security_state_unittest.cc
index f52912c..00eeae4 100644
--- a/net/base/transport_security_state_unittest.cc
+++ b/net/base/transport_security_state_unittest.cc
@@ -5,6 +5,8 @@
 #include "net/base/transport_security_state.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace net {
+
 class TransportSecurityStateTest : public testing::Test {
 };
 
@@ -12,71 +14,71 @@
   int max_age = 42;
   bool include_subdomains = false;
 
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "    ", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "abc", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "  abc", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "  abc   ", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "  max-age", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "  max-age  ", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "   max-age=", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "   max-age  =", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "   max-age=   ", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "   max-age  =     ", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "   max-age  =     xy", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "   max-age  =     3488a923", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488a923  ", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-ag=3488923", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-aged=3488923", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age==3488923", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "amax-age=3488923", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=-3488923", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488923;", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488923     e", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488923     includesubdomain", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488923includesubdomains", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488923=includesubdomains", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488923 includesubdomainx", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488923 includesubdomain=", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488923 includesubdomain=true", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488923 includesubdomainsx", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=3488923 includesubdomains x", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=34889.23 includesubdomains", &max_age, &include_subdomains));
-  EXPECT_FALSE(net::TransportSecurityState::ParseHeader(
+  EXPECT_FALSE(TransportSecurityState::ParseHeader(
       "max-age=34889 includesubdomains", &max_age, &include_subdomains));
 
   EXPECT_EQ(max_age, 42);
@@ -87,51 +89,51 @@
   int max_age = 42;
   bool include_subdomains = true;
 
-  EXPECT_TRUE(net::TransportSecurityState::ParseHeader(
+  EXPECT_TRUE(TransportSecurityState::ParseHeader(
       "max-age=243", &max_age, &include_subdomains));
   EXPECT_EQ(max_age, 243);
   EXPECT_FALSE(include_subdomains);
 
-  EXPECT_TRUE(net::TransportSecurityState::ParseHeader(
+  EXPECT_TRUE(TransportSecurityState::ParseHeader(
       "  Max-agE    = 567", &max_age, &include_subdomains));
   EXPECT_EQ(max_age, 567);
   EXPECT_FALSE(include_subdomains);
 
-  EXPECT_TRUE(net::TransportSecurityState::ParseHeader(
+  EXPECT_TRUE(TransportSecurityState::ParseHeader(
       "  mAx-aGe    = 890      ", &max_age, &include_subdomains));
   EXPECT_EQ(max_age, 890);
   EXPECT_FALSE(include_subdomains);
 
-  EXPECT_TRUE(net::TransportSecurityState::ParseHeader(
+  EXPECT_TRUE(TransportSecurityState::ParseHeader(
       "max-age=123;incLudesUbdOmains", &max_age, &include_subdomains));
   EXPECT_EQ(max_age, 123);
   EXPECT_TRUE(include_subdomains);
 
-  EXPECT_TRUE(net::TransportSecurityState::ParseHeader(
+  EXPECT_TRUE(TransportSecurityState::ParseHeader(
       "max-age=394082;  incLudesUbdOmains", &max_age, &include_subdomains));
   EXPECT_EQ(max_age, 394082);
   EXPECT_TRUE(include_subdomains);
 
-  EXPECT_TRUE(net::TransportSecurityState::ParseHeader(
+  EXPECT_TRUE(TransportSecurityState::ParseHeader(
       "max-age=39408299  ;incLudesUbdOmains", &max_age, &include_subdomains));
   EXPECT_EQ(max_age, 39408299);
   EXPECT_TRUE(include_subdomains);
 
-  EXPECT_TRUE(net::TransportSecurityState::ParseHeader(
+  EXPECT_TRUE(TransportSecurityState::ParseHeader(
       "max-age=394082038  ;  incLudesUbdOmains", &max_age, &include_subdomains));
   EXPECT_EQ(max_age, 394082038);
   EXPECT_TRUE(include_subdomains);
 
-  EXPECT_TRUE(net::TransportSecurityState::ParseHeader(
+  EXPECT_TRUE(TransportSecurityState::ParseHeader(
       "  max-age=0  ;  incLudesUbdOmains   ", &max_age, &include_subdomains));
   EXPECT_EQ(max_age, 0);
   EXPECT_TRUE(include_subdomains);
 }
 
 TEST_F(TransportSecurityStateTest, SimpleMatches) {
-  scoped_refptr<net::TransportSecurityState> state(
-      new net::TransportSecurityState);
-  net::TransportSecurityState::DomainState domain_state;
+  scoped_refptr<TransportSecurityState> state(
+      new TransportSecurityState);
+  TransportSecurityState::DomainState domain_state;
   const base::Time current_time(base::Time::Now());
   const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
 
@@ -142,9 +144,9 @@
 }
 
 TEST_F(TransportSecurityStateTest, MatchesCase1) {
-  scoped_refptr<net::TransportSecurityState> state(
-      new net::TransportSecurityState);
-  net::TransportSecurityState::DomainState domain_state;
+  scoped_refptr<TransportSecurityState> state(
+      new TransportSecurityState);
+  TransportSecurityState::DomainState domain_state;
   const base::Time current_time(base::Time::Now());
   const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
 
@@ -155,9 +157,9 @@
 }
 
 TEST_F(TransportSecurityStateTest, MatchesCase2) {
-  scoped_refptr<net::TransportSecurityState> state(
-      new net::TransportSecurityState);
-  net::TransportSecurityState::DomainState domain_state;
+  scoped_refptr<TransportSecurityState> state(
+      new TransportSecurityState);
+  TransportSecurityState::DomainState domain_state;
   const base::Time current_time(base::Time::Now());
   const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
 
@@ -168,9 +170,9 @@
 }
 
 TEST_F(TransportSecurityStateTest, SubdomainMatches) {
-  scoped_refptr<net::TransportSecurityState> state(
-      new net::TransportSecurityState);
-  net::TransportSecurityState::DomainState domain_state;
+  scoped_refptr<TransportSecurityState> state(
+      new TransportSecurityState);
+  TransportSecurityState::DomainState domain_state;
   const base::Time current_time(base::Time::Now());
   const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
 
@@ -187,61 +189,149 @@
 }
 
 TEST_F(TransportSecurityStateTest, Serialise1) {
-  scoped_refptr<net::TransportSecurityState> state(
-      new net::TransportSecurityState);
+  scoped_refptr<TransportSecurityState> state(
+      new TransportSecurityState);
   std::string output;
+  bool dirty;
   state->Serialise(&output);
-  EXPECT_TRUE(state->Deserialise(output));
+  EXPECT_TRUE(state->Deserialise(output, &dirty));
+  EXPECT_FALSE(dirty);
 }
 
 TEST_F(TransportSecurityStateTest, Serialise2) {
-  scoped_refptr<net::TransportSecurityState> state(
-      new net::TransportSecurityState);
+  scoped_refptr<TransportSecurityState> state(
+      new TransportSecurityState);
 
-  net::TransportSecurityState::DomainState domain_state;
+  TransportSecurityState::DomainState domain_state;
   const base::Time current_time(base::Time::Now());
   const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
 
   EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "google.com"));
-  domain_state.mode = net::TransportSecurityState::DomainState::MODE_STRICT;
+  domain_state.mode = TransportSecurityState::DomainState::MODE_STRICT;
   domain_state.expiry = expiry;
   domain_state.include_subdomains = true;
   state->EnableHost("google.com", domain_state);
 
   std::string output;
+  bool dirty;
   state->Serialise(&output);
-  EXPECT_TRUE(state->Deserialise(output));
+  EXPECT_TRUE(state->Deserialise(output, &dirty));
 
   EXPECT_TRUE(state->IsEnabledForHost(&domain_state, "google.com"));
-  EXPECT_EQ(domain_state.mode, net::TransportSecurityState::DomainState::MODE_STRICT);
+  EXPECT_EQ(domain_state.mode, TransportSecurityState::DomainState::MODE_STRICT);
   EXPECT_TRUE(state->IsEnabledForHost(&domain_state, "foo.google.com"));
-  EXPECT_EQ(domain_state.mode, net::TransportSecurityState::DomainState::MODE_STRICT);
+  EXPECT_EQ(domain_state.mode, TransportSecurityState::DomainState::MODE_STRICT);
   EXPECT_TRUE(state->IsEnabledForHost(&domain_state, "foo.bar.google.com"));
-  EXPECT_EQ(domain_state.mode, net::TransportSecurityState::DomainState::MODE_STRICT);
+  EXPECT_EQ(domain_state.mode, TransportSecurityState::DomainState::MODE_STRICT);
   EXPECT_TRUE(state->IsEnabledForHost(&domain_state,
                                       "foo.bar.baz.google.com"));
-  EXPECT_EQ(domain_state.mode, net::TransportSecurityState::DomainState::MODE_STRICT);
+  EXPECT_EQ(domain_state.mode, TransportSecurityState::DomainState::MODE_STRICT);
   EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "com"));
 }
 
 TEST_F(TransportSecurityStateTest, Serialise3) {
-  scoped_refptr<net::TransportSecurityState> state(
-      new net::TransportSecurityState);
+  scoped_refptr<TransportSecurityState> state(
+      new TransportSecurityState);
 
-  net::TransportSecurityState::DomainState domain_state;
+  TransportSecurityState::DomainState domain_state;
   const base::Time current_time(base::Time::Now());
   const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
 
   EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "google.com"));
-  domain_state.mode = net::TransportSecurityState::DomainState::MODE_OPPORTUNISTIC;
+  domain_state.mode = TransportSecurityState::DomainState::MODE_OPPORTUNISTIC;
   domain_state.expiry = expiry;
   state->EnableHost("google.com", domain_state);
 
   std::string output;
+  bool dirty;
   state->Serialise(&output);
-  EXPECT_TRUE(state->Deserialise(output));
+  EXPECT_TRUE(state->Deserialise(output, &dirty));
 
   EXPECT_TRUE(state->IsEnabledForHost(&domain_state, "google.com"));
   EXPECT_EQ(domain_state.mode,
-            net::TransportSecurityState::DomainState::MODE_OPPORTUNISTIC);
+            TransportSecurityState::DomainState::MODE_OPPORTUNISTIC);
 }
+
+TEST_F(TransportSecurityStateTest, DeleteSince) {
+  scoped_refptr<TransportSecurityState> state(
+      new TransportSecurityState);
+
+  TransportSecurityState::DomainState domain_state;
+  const base::Time current_time(base::Time::Now());
+  const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000);
+  const base::Time older = current_time - base::TimeDelta::FromSeconds(1000);
+
+  EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "google.com"));
+  domain_state.mode = TransportSecurityState::DomainState::MODE_STRICT;
+  domain_state.expiry = expiry;
+  state->EnableHost("google.com", domain_state);
+
+  state->DeleteSince(expiry);
+  EXPECT_TRUE(state->IsEnabledForHost(&domain_state, "google.com"));
+  state->DeleteSince(older);
+  EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "google.com"));
+}
+
+TEST_F(TransportSecurityStateTest, SerialiseOld) {
+  scoped_refptr<TransportSecurityState> state(
+      new TransportSecurityState);
+  // This is an old-style piece of transport state JSON, which has no creation
+  // date.
+  std::string output =
+      "{ "
+        "\"NiyD+3J1r6z1wjl2n1ALBu94Zj9OsEAMo0kCN8js0Uk=\": {"
+          "\"expiry\": 1266815027.983453, "
+          "\"include_subdomains\": false, "
+          "\"mode\": \"strict\" "
+        "}"
+      "}";
+  bool dirty;
+  EXPECT_TRUE(state->Deserialise(output, &dirty));
+  EXPECT_TRUE(dirty);
+}
+
+TEST_F(TransportSecurityStateTest, IsPreloaded) {
+  const std::string paypal =
+      TransportSecurityState::CanonicaliseHost("paypal.com");
+  const std::string www_paypal =
+      TransportSecurityState::CanonicaliseHost("www.paypal.com");
+  const std::string a_www_paypal =
+      TransportSecurityState::CanonicaliseHost("a.www.paypal.com");
+  const std::string abc_paypal =
+      TransportSecurityState::CanonicaliseHost("a.b.c.paypal.com");
+  const std::string example =
+      TransportSecurityState::CanonicaliseHost("example.com");
+  const std::string aypal =
+      TransportSecurityState::CanonicaliseHost("aypal.com");
+
+  bool b;
+  EXPECT_FALSE(TransportSecurityState::isPreloadedSTS(paypal, &b));
+  EXPECT_TRUE(TransportSecurityState::isPreloadedSTS(www_paypal, &b));
+  EXPECT_FALSE(b);
+  EXPECT_FALSE(TransportSecurityState::isPreloadedSTS(a_www_paypal, &b));
+  EXPECT_FALSE(TransportSecurityState::isPreloadedSTS(abc_paypal, &b));
+  EXPECT_FALSE(TransportSecurityState::isPreloadedSTS(example, &b));
+  EXPECT_FALSE(TransportSecurityState::isPreloadedSTS(aypal, &b));
+}
+
+TEST_F(TransportSecurityStateTest, Preloaded) {
+  scoped_refptr<TransportSecurityState> state(
+      new TransportSecurityState);
+  TransportSecurityState::DomainState domain_state;
+  EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "paypal.com"));
+  EXPECT_TRUE(state->IsEnabledForHost(&domain_state, "www.paypal.com"));
+  EXPECT_EQ(domain_state.mode,
+            TransportSecurityState::DomainState::MODE_STRICT);
+  EXPECT_FALSE(domain_state.include_subdomains);
+  EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "www2.paypal.com"));
+  EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "a.www.paypal.com"));
+
+  EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "elanex.biz"));
+  EXPECT_TRUE(state->IsEnabledForHost(&domain_state, "www.elanex.biz"));
+  EXPECT_EQ(domain_state.mode,
+            TransportSecurityState::DomainState::MODE_STRICT);
+  EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "foo.elanex.biz"));
+  EXPECT_FALSE(state->IsEnabledForHost(&domain_state, "a.foo.elanex.biz"));
+}
+
+}  // namespace net
diff --git a/net/base/upload_data.cc b/net/base/upload_data.cc
index 0045409..198b051 100644
--- a/net/base/upload_data.cc
+++ b/net/base/upload_data.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -6,27 +6,42 @@
 
 #include "base/file_util.h"
 #include "base/logging.h"
+#include "net/base/net_errors.h"
 
 namespace net {
 
-uint64 UploadData::GetContentLength() const {
+uint64 UploadData::GetContentLength() {
   uint64 len = 0;
-  std::vector<Element>::const_iterator it = elements_.begin();
+  std::vector<Element>::iterator it = elements_.begin();
   for (; it != elements_.end(); ++it)
     len += (*it).GetContentLength();
   return len;
 }
 
-uint64 UploadData::Element::GetContentLength() const {
+uint64 UploadData::Element::GetContentLength() {
+  if (override_content_length_ || content_length_computed_)
+    return content_length_;
+
   if (type_ == TYPE_BYTES)
     return static_cast<uint64>(bytes_.size());
 
-  DCHECK(type_ == TYPE_FILE);
+  DCHECK_EQ(TYPE_FILE, type_);
+  DCHECK(!file_stream_);
 
   // TODO(darin): This size calculation could be out of sync with the state of
   // the file when we get around to reading it.  We should probably find a way
   // to lock the file or somehow protect against this error condition.
 
+  content_length_computed_ = true;
+  content_length_ = 0;
+
+  // We need to open the file here to decide if we should report the file's
+  // size or zero.  We cache the open file, so that we can still read it when
+  // it comes time to.
+  file_stream_ = NewFileStreamForReading();
+  if (!file_stream_)
+    return 0;
+
   int64 length = 0;
   if (!file_util::GetFileSize(file_path_, &length))
     return 0;
@@ -35,7 +50,40 @@
     return 0;  // range is beyond eof
 
   // compensate for the offset and clip file_range_length_ to eof
-  return std::min(length - file_range_offset_, file_range_length_);
+  content_length_ =  std::min(length - file_range_offset_, file_range_length_);
+  return content_length_;
+}
+
+FileStream* UploadData::Element::NewFileStreamForReading() {
+  // In common usage GetContentLength() will call this first and store the
+  // result into |file_| and a subsequent call (from UploadDataStream) will
+  // get the cached open FileStream.
+  if (file_stream_) {
+    FileStream* file = file_stream_;
+    file_stream_ = NULL;
+    return file;
+  }
+
+  scoped_ptr<FileStream> file(new FileStream());
+  int64 rv = file->Open(file_path_,
+                      base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
+  if (rv != OK) {
+    // If the file can't be opened, we'll just upload an empty file.
+    DLOG(WARNING) << "Failed to open \"" << file_path_.value()
+                  << "\" for reading: " << rv;
+    return NULL;
+  }
+  if (file_range_offset_) {
+    rv = file->Seek(FROM_BEGIN, file_range_offset_);
+    if (rv < 0) {
+      DLOG(WARNING) << "Failed to seek \"" << file_path_.value()
+                    << "\" to offset: " << file_range_offset_ << " (" << rv
+                    << ")";
+      return NULL;
+    }
+  }
+
+  return file.release();
 }
 
 }  // namespace net
diff --git a/net/base/upload_data.h b/net/base/upload_data.h
index 02880d3..fe395f0 100644
--- a/net/base/upload_data.h
+++ b/net/base/upload_data.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -7,8 +7,13 @@
 
 #include <vector>
 
+#include "base/basictypes.h"
 #include "base/file_path.h"
+#include "base/logging.h"
 #include "base/ref_counted.h"
+#include "net/base/file_stream.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
 
 namespace net {
 
@@ -24,7 +29,15 @@
   class Element {
    public:
     Element() : type_(TYPE_BYTES), file_range_offset_(0),
-                file_range_length_(kuint64max) {
+                file_range_length_(kuint64max),
+                override_content_length_(false),
+                content_length_computed_(false),
+                file_stream_(NULL) {
+    }
+
+    ~Element() {
+      // In the common case |file__stream_| will be null.
+      delete file_stream_;
     }
 
     Type type() const { return type_; }
@@ -32,6 +45,10 @@
     const FilePath& file_path() const { return file_path_; }
     uint64 file_range_offset() const { return file_range_offset_; }
     uint64 file_range_length() const { return file_range_length_; }
+    // If NULL time is returned, we do not do the check.
+    const base::Time& expected_file_modification_time() const {
+      return expected_file_modification_time_;
+    }
 
     void SetToBytes(const char* bytes, int bytes_len) {
       type_ = TYPE_BYTES;
@@ -39,27 +56,53 @@
     }
 
     void SetToFilePath(const FilePath& path) {
-      SetToFilePathRange(path, 0, kuint64max);
+      SetToFilePathRange(path, 0, kuint64max, base::Time());
     }
 
+    // If expected_modification_time is NULL, we do not check for the file
+    // change. Also note that the granularity for comparison is time_t, not
+    // the full precision.
     void SetToFilePathRange(const FilePath& path,
-                            uint64 offset, uint64 length) {
+                            uint64 offset, uint64 length,
+                            const base::Time& expected_modification_time) {
       type_ = TYPE_FILE;
       file_path_ = path;
       file_range_offset_ = offset;
       file_range_length_ = length;
+      expected_file_modification_time_ = expected_modification_time;
     }
 
     // Returns the byte-length of the element.  For files that do not exist, 0
     // is returned.  This is done for consistency with Mozilla.
-    uint64 GetContentLength() const;
+    // Once called, this function will always return the same value.
+    uint64 GetContentLength();
+
+    // Returns a FileStream opened for reading for this element, positioned at
+    // |file_range_offset_|.  The caller gets ownership and is responsible
+    // for cleaning up the FileStream. Returns NULL if this element is not of
+    // type TYPE_FILE or if the file is not openable.
+    FileStream* NewFileStreamForReading();
 
    private:
+    // Allows tests to override the result of GetContentLength.
+    void SetContentLength(uint64 content_length) {
+      override_content_length_ = true;
+      content_length_ = content_length;
+    }
+
     Type type_;
     std::vector<char> bytes_;
     FilePath file_path_;
     uint64 file_range_offset_;
     uint64 file_range_length_;
+    base::Time expected_file_modification_time_;
+    bool override_content_length_;
+    bool content_length_computed_;
+    uint64 content_length_;
+    FileStream* file_stream_;
+
+    FRIEND_TEST(UploadDataStreamTest, FileSmallerThanLength);
+    FRIEND_TEST(HttpNetworkTransactionTest, UploadFileSmallerThanLength);
   };
 
   void AppendBytes(const char* bytes, int bytes_len) {
@@ -75,16 +118,18 @@
   }
 
   void AppendFileRange(const FilePath& file_path,
-                       uint64 offset, uint64 length) {
+                       uint64 offset, uint64 length,
+                       const base::Time& expected_modification_time) {
     elements_.push_back(Element());
-    elements_.back().SetToFilePathRange(file_path, offset, length);
+    elements_.back().SetToFilePathRange(file_path, offset, length,
+                                        expected_modification_time);
   }
 
   // Returns the total size in bytes of the data to upload.
-  uint64 GetContentLength() const;
+  uint64 GetContentLength();
 
-  const std::vector<Element>& elements() const {
-    return elements_;
+  std::vector<Element>* elements() {
+    return &elements_;
   }
 
   void set_elements(const std::vector<Element>& elements) {
diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc
index d22929a..140aa8a 100644
--- a/net/base/upload_data_stream.cc
+++ b/net/base/upload_data_stream.cc
@@ -1,33 +1,45 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/base/upload_data_stream.h"
 
+#include "base/file_util.h"
 #include "base/logging.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 
 namespace net {
 
-UploadDataStream::UploadDataStream(const UploadData* data)
+UploadDataStream* UploadDataStream::Create(UploadData* data, int* error_code) {
+  scoped_ptr<UploadDataStream> stream(new UploadDataStream(data));
+  int rv = stream->FillBuf();
+  if (error_code)
+    *error_code = rv;
+  if (rv != OK)
+    return NULL;
+
+  return stream.release();
+}
+
+UploadDataStream::UploadDataStream(UploadData* data)
     : data_(data),
       buf_(new IOBuffer(kBufSize)),
       buf_len_(0),
-      next_element_(data->elements().begin()),
+      next_element_(data->elements()->begin()),
       next_element_offset_(0),
       next_element_remaining_(0),
       total_size_(data->GetContentLength()),
-      current_position_(0) {
-  FillBuf();
+      current_position_(0),
+      eof_(false) {
 }
 
 UploadDataStream::~UploadDataStream() {
 }
 
 void UploadDataStream::DidConsume(size_t num_bytes) {
-  // TODO(vandebo): Change back to a DCHECK when issue 27870 is resolved.
-  CHECK(num_bytes <= buf_len_);
+  DCHECK_LE(num_bytes, buf_len_);
+  DCHECK(!eof_);
 
   buf_len_ -= num_bytes;
   if (buf_len_)
@@ -38,14 +50,14 @@
   current_position_ += num_bytes;
 }
 
-void UploadDataStream::FillBuf() {
-  std::vector<UploadData::Element>::const_iterator end =
-      data_->elements().end();
+int UploadDataStream::FillBuf() {
+  std::vector<UploadData::Element>::iterator end =
+      data_->elements()->end();
 
   while (buf_len_ < kBufSize && next_element_ != end) {
     bool advance_to_next_element = false;
 
-    const UploadData::Element& element = *next_element_;
+    UploadData::Element& element = *next_element_;
 
     size_t size_remaining = kBufSize - buf_len_;
     if (element.type() == UploadData::TYPE_BYTES) {
@@ -65,47 +77,58 @@
     } else {
       DCHECK(element.type() == UploadData::TYPE_FILE);
 
-      if (!next_element_stream_.IsOpen()) {
-        int flags = base::PLATFORM_FILE_OPEN |
-                    base::PLATFORM_FILE_READ;
-        int rv = next_element_stream_.Open(element.file_path(), flags);
-        // If the file does not exist, that's technically okay.. we'll just
-        // upload an empty file.  This is for consistency with Mozilla.
-        DLOG_IF(WARNING, rv != OK) << "Failed to open \""
-                                   << element.file_path().value()
-                                   << "\" for reading: " << rv;
-
-        next_element_remaining_ = 0;  // Default to reading nothing.
-        if (rv == OK) {
-          uint64 offset = element.file_range_offset();
-          if (offset && next_element_stream_.Seek(FROM_BEGIN, offset) < 0) {
-            DLOG(WARNING) << "Failed to seek \"" << element.file_path().value()
-                          << "\" to offset: " << offset;
-          } else {
-            next_element_remaining_ = element.file_range_length();
+      if (!next_element_remaining_) {
+        // If the underlying file has been changed, treat it as error.
+        // Note that the expected modification time from WebKit is based on
+        // time_t precision. So we have to convert both to time_t to compare.
+        if (!element.expected_file_modification_time().is_null()) {
+          file_util::FileInfo info;
+          if (file_util::GetFileInfo(element.file_path(), &info) &&
+              element.expected_file_modification_time().ToTimeT() !=
+                  info.last_modified.ToTimeT()) {
+            return ERR_UPLOAD_FILE_CHANGED;
           }
         }
+        next_element_remaining_ = element.GetContentLength();
+        next_element_stream_.reset(element.NewFileStreamForReading());
       }
 
       int rv = 0;
-      int count = static_cast<int>(std::min(
-          static_cast<uint64>(size_remaining), next_element_remaining_));
-      if (count > 0 &&
-          (rv = next_element_stream_.Read(buf_->data() + buf_len_,
-                                          count, NULL)) > 0) {
+      int count =
+          static_cast<int>(std::min(next_element_remaining_,
+                                    static_cast<uint64>(size_remaining)));
+      if (count > 0) {
+        if (next_element_stream_.get())
+          rv = next_element_stream_->Read(buf_->data() + buf_len_, count, NULL);
+        if (rv <= 0) {
+          // If there's less data to read than we initially observed, then
+          // pad with zero.  Otherwise the server will hang waiting for the
+          // rest of the data.
+          memset(buf_->data() + buf_len_, 0, count);
+          rv = count;
+        }
         buf_len_ += rv;
-        next_element_remaining_ -= rv;
-      } else {
+      }
+
+      if (static_cast<int>(next_element_remaining_) == rv) {
         advance_to_next_element = true;
+      } else {
+        next_element_remaining_ -= rv;
       }
     }
 
     if (advance_to_next_element) {
       ++next_element_;
       next_element_offset_ = 0;
-      next_element_stream_.Close();
+      next_element_remaining_ = 0;
+      next_element_stream_.reset();
     }
   }
+
+  if (next_element_ == end && !buf_len_)
+    eof_ = true;
+
+  return OK;
 }
 
 }  // namespace net
diff --git a/net/base/upload_data_stream.h b/net/base/upload_data_stream.h
index d703c3d..5e28721 100644
--- a/net/base/upload_data_stream.h
+++ b/net/base/upload_data_stream.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -14,7 +14,11 @@
 
 class UploadDataStream {
  public:
-  explicit UploadDataStream(const UploadData* data);
+  // Returns a new instance of UploadDataStream if it can be created and
+  // initialized successfully. If not, NULL will be returned and the error
+  // code will be set if the output parameter error_code is not empty.
+  static UploadDataStream* Create(UploadData* data, int* error_code);
+
   ~UploadDataStream();
 
   // Returns the stream's buffer and buffer length.
@@ -27,13 +31,27 @@
   void DidConsume(size_t num_bytes);
 
   // Returns the total size of the data stream and the current position.
+  // size() is not to be used to determine whether the stream has ended
+  // because it is possible for the stream to end before its size is reached,
+  // for example, if the file is truncated.
   uint64 size() const { return total_size_; }
   uint64 position() const { return current_position_; }
 
- private:
-  void FillBuf();
+  // Returns whether there is no more data to read, regardless of whether
+  // position < size.
+  bool eof() const { return eof_; }
 
-  const UploadData* data_;
+ private:
+  // Protects from public access since now we have a static creator function
+  // which will do both creation and initialization and might return an error.
+  explicit UploadDataStream(UploadData* data);
+
+  // Fills the buffer with any remaining data and sets eof_ if there was nothing
+  // left to fill the buffer with.
+  // Returns OK if the operation succeeds. Otherwise error code is returned.
+  int FillBuf();
+
+  UploadData* data_;
 
   // This buffer is filled with data to be uploaded.  The data to be sent is
   // always at the front of the buffer.  If we cannot send all of the buffer at
@@ -44,7 +62,7 @@
   size_t buf_len_;
 
   // Iterator to the upload element to be written to the send buffer next.
-  std::vector<UploadData::Element>::const_iterator next_element_;
+  std::vector<UploadData::Element>::iterator next_element_;
 
   // The byte offset into next_element_'s data buffer if the next element is
   // a TYPE_BYTES element.
@@ -52,7 +70,7 @@
 
   // A stream to the currently open file, for next_element_ if the next element
   // is a TYPE_FILE element.
-  FileStream next_element_stream_;
+  scoped_ptr<FileStream> next_element_stream_;
 
   // The number of bytes remaining to be read from the currently open file
   // if the next element is of TYPE_FILE.
@@ -62,7 +80,10 @@
   uint64 total_size_;
   uint64 current_position_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(UploadDataStream);
+  // Whether there is no data left to read.
+  bool eof_;
+
+  DISALLOW_COPY_AND_ASSIGN(UploadDataStream);
 };
 
 }  // namespace net
diff --git a/net/base/upload_data_stream_unittest.cc b/net/base/upload_data_stream_unittest.cc
new file mode 100644
index 0000000..92c065e
--- /dev/null
+++ b/net/base/upload_data_stream_unittest.cc
@@ -0,0 +1,127 @@
+// Copyright (c) 2010 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/base/upload_data_stream.h"
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "net/base/net_errors.h"
+#include "net/base/upload_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace net {
+
+namespace {
+
+const char kTestData[] = "0123456789";
+const int kTestDataSize = arraysize(kTestData) - 1;
+
+}  // namespace
+
+class UploadDataStreamTest : public PlatformTest {
+ public:
+  UploadDataStreamTest() : upload_data_(new UploadData) { }
+
+  void FileChangedHelper(const FilePath& file_path,
+                         const base::Time& time,
+                         bool error_expected);
+
+  scoped_refptr<UploadData> upload_data_;
+};
+
+TEST_F(UploadDataStreamTest, EmptyUploadData) {
+  upload_data_->AppendBytes("", 0);
+  scoped_ptr<UploadDataStream> stream(
+      UploadDataStream::Create(upload_data_, NULL));
+  ASSERT_TRUE(stream.get());
+  EXPECT_TRUE(stream->eof());
+}
+
+TEST_F(UploadDataStreamTest, ConsumeAll) {
+  upload_data_->AppendBytes(kTestData, kTestDataSize);
+  scoped_ptr<UploadDataStream> stream(
+      UploadDataStream::Create(upload_data_, NULL));
+  ASSERT_TRUE(stream.get());
+  while (!stream->eof()) {
+    stream->DidConsume(stream->buf_len());
+  }
+}
+
+TEST_F(UploadDataStreamTest, FileSmallerThanLength) {
+  FilePath temp_file_path;
+  ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path));
+  ASSERT_EQ(kTestDataSize, file_util::WriteFile(temp_file_path,
+                                                kTestData, kTestDataSize));
+  const uint64 kFakeSize = kTestDataSize*2;
+
+  std::vector<UploadData::Element> elements;
+  UploadData::Element element;
+  element.SetToFilePath(temp_file_path);
+  element.SetContentLength(kFakeSize);
+  elements.push_back(element);
+  upload_data_->set_elements(elements);
+  EXPECT_EQ(kFakeSize, upload_data_->GetContentLength());
+
+  scoped_ptr<UploadDataStream> stream(
+      UploadDataStream::Create(upload_data_, NULL));
+  ASSERT_TRUE(stream.get());
+  EXPECT_FALSE(stream->eof());
+  uint64 read_counter = 0;
+  while (!stream->eof()) {
+    read_counter += stream->buf_len();
+    stream->DidConsume(stream->buf_len());
+  }
+  // UpdateDataStream will pad out the file with 0 bytes so that the HTTP
+  // transaction doesn't hang.  Therefore we expected the full size.
+  EXPECT_EQ(read_counter, stream->size());
+
+  file_util::Delete(temp_file_path, false);
+}
+
+void UploadDataStreamTest::FileChangedHelper(const FilePath& file_path,
+                                             const base::Time& time,
+                                             bool error_expected) {
+  std::vector<UploadData::Element> elements;
+  UploadData::Element element;
+  element.SetToFilePathRange(file_path, 1, 2, time);
+  elements.push_back(element);
+  upload_data_->set_elements(elements);
+
+  int error_code;
+  scoped_ptr<UploadDataStream> stream(
+      UploadDataStream::Create(upload_data_, &error_code));
+  if (error_expected)
+    ASSERT_TRUE(!stream.get() && error_code == net::ERR_UPLOAD_FILE_CHANGED);
+  else
+    ASSERT_TRUE(stream.get() && error_code == net::OK);
+}
+
+TEST_F(UploadDataStreamTest, FileChanged) {
+  FilePath temp_file_path;
+  ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path));
+  ASSERT_EQ(kTestDataSize, file_util::WriteFile(temp_file_path,
+                                                kTestData, kTestDataSize));
+
+  file_util::FileInfo file_info;
+  ASSERT_TRUE(file_util::GetFileInfo(temp_file_path, &file_info));
+
+  // Test file not changed.
+  FileChangedHelper(temp_file_path, file_info.last_modified, false);
+
+  // Test file changed.
+  FileChangedHelper(temp_file_path,
+                    file_info.last_modified - base::TimeDelta::FromSeconds(1),
+                    true);
+
+  file_util::Delete(temp_file_path, false);
+}
+
+}  // namespace net
+
diff --git a/net/base/winsock_init.cc b/net/base/winsock_init.cc
index 5c3f005..ccaf01c 100644
--- a/net/base/winsock_init.cc
+++ b/net/base/winsock_init.cc
@@ -13,11 +13,11 @@
 
 class WinsockInitSingleton {
  public:
-  WinsockInitSingleton() : did_init_(false) {
+  WinsockInitSingleton() {
     WORD winsock_ver = MAKEWORD(2, 2);
     WSAData wsa_data;
-    did_init_ = (WSAStartup(winsock_ver, &wsa_data) == 0);
-    if (did_init_) {
+    bool did_init = (WSAStartup(winsock_ver, &wsa_data) == 0);
+    if (did_init) {
       DCHECK(wsa_data.wVersion == winsock_ver);
 
       // The first time WSAGetLastError is called, the delay load helper will
@@ -32,12 +32,9 @@
   }
 
   ~WinsockInitSingleton() {
-    if (did_init_)
-      WSACleanup();
+    // Don't call WSACleanup() since the worker pool threads can continue to
+    // call getaddrinfo() after Winsock has shutdown, which can lead to crashes.
   }
-
- private:
-  bool did_init_;
 };
 
 }  // namespace
diff --git a/net/base/winsock_init.h b/net/base/winsock_init.h
index ab34fb0..9608e95 100644
--- a/net/base/winsock_init.h
+++ b/net/base/winsock_init.h
@@ -3,9 +3,7 @@
 // found in the LICENSE file.
 
 // Winsock initialization must happen before any Winsock calls are made.  The
-// EnsureWinsockInit method will make sure that WSAStartup has been called.  If
-// the call to WSAStartup caused Winsock to initialize, WSACleanup will be
-// called automatically on program shutdown.
+// EnsureWinsockInit method will make sure that WSAStartup has been called.
 
 #ifndef NET_BASE_WINSOCK_INIT_H_
 #define NET_BASE_WINSOCK_INIT_H_
diff --git a/net/base/x509_cert_types.cc b/net/base/x509_cert_types.cc
new file mode 100644
index 0000000..8f7a2ae
--- /dev/null
+++ b/net/base/x509_cert_types.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2006-2010 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/base/x509_cert_types.h"
+
+#include <ostream>
+
+#include "net/base/x509_certificate.h"
+#include "base/logging.h"
+
+namespace net {
+
+bool match(const std::string &str, const std::string &against) {
+  // TODO(snej): Use the full matching rules specified in RFC 5280 sec. 7.1
+  // including trimming and case-folding: <http://www.ietf.org/rfc/rfc5280.txt>.
+  return against == str;
+}
+
+bool match(const std::vector<std::string> &rdn1,
+           const std::vector<std::string> &rdn2) {
+  // "Two relative distinguished names RDN1 and RDN2 match if they have the
+  // same number of naming attributes and for each naming attribute in RDN1
+  // there is a matching naming attribute in RDN2." --RFC 5280 sec. 7.1.
+  if (rdn1.size() != rdn2.size())
+    return false;
+  for (unsigned i1 = 0; i1 < rdn1.size(); ++i1) {
+    unsigned i2;
+    for (i2 = 0; i2 < rdn2.size(); ++i2) {
+      if (match(rdn1[i1], rdn2[i2]))
+          break;
+    }
+    if (i2 == rdn2.size())
+      return false;
+  }
+  return true;
+}
+
+
+bool CertPrincipal::Matches(const CertPrincipal& against) const {
+  return match(common_name, against.common_name) &&
+      match(common_name, against.common_name) &&
+      match(locality_name, against.locality_name) &&
+      match(state_or_province_name, against.state_or_province_name) &&
+      match(country_name, against.country_name) &&
+      match(street_addresses, against.street_addresses) &&
+      match(organization_names, against.organization_names) &&
+      match(organization_unit_names, against.organization_unit_names) &&
+      match(domain_components, against.domain_components);
+}
+
+std::ostream& operator<<(std::ostream& s, const CertPrincipal& p) {
+  s << "CertPrincipal[";
+  if (!p.common_name.empty())
+    s << "cn=\"" << p.common_name << "\" ";
+  for (unsigned i = 0; i < p.street_addresses.size(); ++i)
+    s << "street=\"" << p.street_addresses[i] << "\" ";
+  if (!p.locality_name.empty())
+    s << "l=\"" << p.locality_name << "\" ";
+  for (unsigned i = 0; i < p.organization_names.size(); ++i)
+    s << "o=\"" << p.organization_names[i] << "\" ";
+  for (unsigned i = 0; i < p.organization_unit_names.size(); ++i)
+    s << "ou=\"" << p.organization_unit_names[i] << "\" ";
+  if (!p.state_or_province_name.empty())
+    s << "st=\"" << p.state_or_province_name << "\" ";
+  if (!p.country_name.empty())
+    s << "c=\"" << p.country_name << "\" ";
+  for (unsigned i = 0; i < p.domain_components.size(); ++i)
+    s << "dc=\"" << p.domain_components[i] << "\" ";
+  return s << "]";
+}
+
+CertPolicy::Judgment CertPolicy::Check(
+    X509Certificate* cert) const {
+  // It shouldn't matter which set we check first, but we check denied first
+  // in case something strange has happened.
+
+  if (denied_.find(cert->fingerprint()) != denied_.end()) {
+    // DCHECK that the order didn't matter.
+    DCHECK(allowed_.find(cert->fingerprint()) == allowed_.end());
+    return DENIED;
+  }
+
+  if (allowed_.find(cert->fingerprint()) != allowed_.end()) {
+    // DCHECK that the order didn't matter.
+    DCHECK(denied_.find(cert->fingerprint()) == denied_.end());
+    return ALLOWED;
+  }
+
+  // We don't have a policy for this cert.
+  return UNKNOWN;
+}
+
+void CertPolicy::Allow(X509Certificate* cert) {
+  // Put the cert in the allowed set and (maybe) remove it from the denied set.
+  denied_.erase(cert->fingerprint());
+  allowed_.insert(cert->fingerprint());
+}
+
+void CertPolicy::Deny(X509Certificate* cert) {
+  // Put the cert in the denied set and (maybe) remove it from the allowed set.
+  allowed_.erase(cert->fingerprint());
+  denied_.insert(cert->fingerprint());
+}
+
+bool CertPolicy::HasAllowedCert() const {
+  return !allowed_.empty();
+}
+
+bool CertPolicy::HasDeniedCert() const {
+  return !denied_.empty();
+}
+
+}  // namespace net
diff --git a/net/base/x509_cert_types.h b/net/base/x509_cert_types.h
new file mode 100644
index 0000000..ad3ea2d
--- /dev/null
+++ b/net/base/x509_cert_types.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2007-2010 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_BASE_X509_CERT_TYPES_H_
+#define NET_BASE_X509_CERT_TYPES_H_
+
+#include <string.h>
+
+#include <functional>
+#include <iosfwd>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "base/singleton.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <wincrypt.h>
+#elif defined(OS_MACOSX)
+#include <Security/x509defs.h>
+#elif defined(USE_NSS)
+// Forward declaration; real one in <cert.h>
+struct CERTCertificateStr;
+#endif
+
+namespace net {
+
+class X509Certificate;
+
+// SHA-1 fingerprint (160 bits) of a certificate.
+struct SHA1Fingerprint {
+  bool Equals(const SHA1Fingerprint& other) const {
+    return memcmp(data, other.data, sizeof(data)) == 0;
+  }
+
+  unsigned char data[20];
+};
+
+class SHA1FingerprintLessThan
+    : public std::binary_function<SHA1Fingerprint, SHA1Fingerprint, bool> {
+ public:
+  bool operator() (const SHA1Fingerprint& lhs,
+                   const SHA1Fingerprint& rhs) const {
+    return memcmp(lhs.data, rhs.data, sizeof(lhs.data)) < 0;
+  }
+};
+
+// CertPrincipal represents the issuer or subject field of an X.509 certificate.
+struct CertPrincipal {
+  CertPrincipal() { }
+  explicit CertPrincipal(const std::string& name) : common_name(name) { }
+
+  // Parses a BER-format DistinguishedName.
+  bool ParseDistinguishedName(const void* ber_name_data, size_t length);
+
+#if defined(OS_MACOSX)
+  // Parses a CSSM_X509_NAME struct.
+  void Parse(const CSSM_X509_NAME* name);
+#endif
+
+  // Returns true if all attributes of the two objects match,
+  // where "match" is defined in RFC 5280 sec. 7.1.
+  bool Matches(const CertPrincipal& against) const;
+
+  // The different attributes for a principal.  They may be "".
+  // Note that some of them can have several values.
+
+  std::string common_name;
+  std::string locality_name;
+  std::string state_or_province_name;
+  std::string country_name;
+
+  std::vector<std::string> street_addresses;
+  std::vector<std::string> organization_names;
+  std::vector<std::string> organization_unit_names;
+  std::vector<std::string> domain_components;
+};
+
+// Writes a human-readable description of a CertPrincipal, for debugging.
+std::ostream& operator<<(std::ostream& s, const CertPrincipal& p);
+
+// This class is useful for maintaining policies about which certificates are
+// permitted or forbidden for a particular purpose.
+class CertPolicy {
+ public:
+  // The judgments this policy can reach.
+  enum Judgment {
+    // We don't have policy information for this certificate.
+    UNKNOWN,
+
+    // This certificate is allowed.
+    ALLOWED,
+
+    // This certificate is denied.
+    DENIED,
+  };
+
+  // Returns the judgment this policy makes about this certificate.
+  Judgment Check(X509Certificate* cert) const;
+
+  // Causes the policy to allow this certificate.
+  void Allow(X509Certificate* cert);
+
+  // Causes the policy to deny this certificate.
+  void Deny(X509Certificate* cert);
+
+  // Returns true if this policy has allowed at least one certificate.
+  bool HasAllowedCert() const;
+
+  // Returns true if this policy has denied at least one certificate.
+  bool HasDeniedCert() const;
+
+ private:
+  // The set of fingerprints of allowed certificates.
+  std::set<SHA1Fingerprint, SHA1FingerprintLessThan> allowed_;
+
+  // The set of fingerprints of denied certificates.
+  std::set<SHA1Fingerprint, SHA1FingerprintLessThan> denied_;
+};
+
+#if defined(OS_MACOSX)
+// Compares two OIDs by value.
+inline bool CSSMOIDEqual(const CSSM_OID* oid1, const CSSM_OID* oid2) {
+  return oid1->Length == oid2->Length &&
+  (memcmp(oid1->Data, oid2->Data, oid1->Length) == 0);
+}
+#endif
+
+}  // namespace net
+
+#endif  // NET_BASE_X509_CERT_TYPES_H_
diff --git a/net/base/x509_cert_types_mac.cc b/net/base/x509_cert_types_mac.cc
new file mode 100644
index 0000000..504dde5
--- /dev/null
+++ b/net/base/x509_cert_types_mac.cc
@@ -0,0 +1,293 @@
+// Copyright (c) 2006-2010 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/base/x509_cert_types.h"
+
+#include <CoreServices/CoreServices.h>
+#include <Security/Security.h>
+#include <Security/SecAsn1Coder.h>
+
+#include "base/logging.h"
+#include "base/i18n/icu_string_conversions.h"
+#include "base/utf_string_conversions.h"
+
+namespace net {
+
+static const CSSM_OID* kOIDs[] = {
+    &CSSMOID_CommonName,
+    &CSSMOID_LocalityName,
+    &CSSMOID_StateProvinceName,
+    &CSSMOID_CountryName,
+    &CSSMOID_StreetAddress,
+    &CSSMOID_OrganizationName,
+    &CSSMOID_OrganizationalUnitName,
+    &CSSMOID_DNQualifier      // This should be "DC" but is undoubtedly wrong.
+};                            // TODO(avi): Find the right OID.
+
+// Converts raw CSSM_DATA to a std::string. (Char encoding is unaltered.)
+static std::string DataToString(CSSM_DATA data);
+
+// Converts raw CSSM_DATA in ISO-8859-1 to a std::string in UTF-8.
+static std::string Latin1DataToUTF8String(CSSM_DATA data);
+
+// Converts big-endian UTF-16 to UTF-8 in a std::string.
+// Note: The byte-order flipping is done in place on the input buffer!
+static bool UTF16BigEndianToUTF8(char16* chars, size_t length,
+                                 std::string* out_string);
+
+// Converts big-endian UTF-32 to UTF-8 in a std::string.
+// Note: The byte-order flipping is done in place on the input buffer!
+static bool UTF32BigEndianToUTF8(char32* chars, size_t length,
+                                 std::string* out_string);
+
+// Adds a type+value pair to the appropriate vector from a C array.
+// The array is keyed by the matching OIDs from kOIDS[].
+  static void AddTypeValuePair(const CSSM_OID type,
+                               const std::string& value,
+                               std::vector<std::string>* values[]);
+
+// Stores the first string of the vector, if any, to *single_value.
+static void SetSingle(const std::vector<std::string> &values,
+                      std::string* single_value);
+
+
+void CertPrincipal::Parse(const CSSM_X509_NAME* name) {
+  std::vector<std::string> common_names, locality_names, state_names,
+      country_names;
+
+  std::vector<std::string>* values[] = {
+      &common_names, &locality_names,
+      &state_names, &country_names,
+      &(this->street_addresses),
+      &(this->organization_names),
+      &(this->organization_unit_names),
+      &(this->domain_components)
+  };
+  DCHECK(arraysize(kOIDs) == arraysize(values));
+
+  for (size_t rdn = 0; rdn < name->numberOfRDNs; ++rdn) {
+    CSSM_X509_RDN rdn_struct = name->RelativeDistinguishedName[rdn];
+    for (size_t pair = 0; pair < rdn_struct.numberOfPairs; ++pair) {
+      CSSM_X509_TYPE_VALUE_PAIR pair_struct =
+          rdn_struct.AttributeTypeAndValue[pair];
+      AddTypeValuePair(pair_struct.type,
+                       DataToString(pair_struct.value),
+                       values);
+    }
+  }
+
+  SetSingle(common_names, &this->common_name);
+  SetSingle(locality_names, &this->locality_name);
+  SetSingle(state_names, &this->state_or_province_name);
+  SetSingle(country_names, &this->country_name);
+}
+
+
+// The following structs and templates work with Apple's very arcane and under-
+// documented SecAsn1Parser API, which is apparently the same as NSS's ASN.1
+// decoder:
+// http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn1.html
+
+// These are used to parse the contents of a raw
+// BER DistinguishedName structure.
+
+struct KeyValuePair {
+  CSSM_OID key;
+  int value_type;
+  CSSM_DATA value;
+
+  enum {
+    kTypeOther = 0,
+    kTypePrintableString,
+    kTypeIA5String,
+    kTypeT61String,
+    kTypeUTF8String,
+    kTypeBMPString,
+    kTypeUniversalString,
+  };
+};
+
+static const SecAsn1Template kStringValueTemplate[] = {
+  { SEC_ASN1_CHOICE, offsetof(KeyValuePair, value_type), },
+  { SEC_ASN1_PRINTABLE_STRING,
+    offsetof(KeyValuePair, value), 0, KeyValuePair::kTypePrintableString },
+  { SEC_ASN1_IA5_STRING,
+    offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeIA5String },
+  { SEC_ASN1_T61_STRING,
+    offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeT61String },
+  { SEC_ASN1_UTF8_STRING,
+    offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeUTF8String },
+  { SEC_ASN1_BMP_STRING,
+    offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeBMPString },
+  { SEC_ASN1_UNIVERSAL_STRING,
+    offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeUniversalString },
+  { 0, }
+};
+
+static const SecAsn1Template kKeyValuePairTemplate[] = {
+  { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(KeyValuePair) },
+  { SEC_ASN1_OBJECT_ID, offsetof(KeyValuePair, key), },
+  { SEC_ASN1_INLINE, 0, &kStringValueTemplate, },
+  { 0, }
+};
+
+struct KeyValuePairs {
+  KeyValuePair* pairs;
+};
+
+static const SecAsn1Template kKeyValuePairSetTemplate[] = {
+  { SEC_ASN1_SET_OF, offsetof(KeyValuePairs,pairs),
+      kKeyValuePairTemplate, sizeof(KeyValuePairs) }
+};
+
+struct X509Name {
+  KeyValuePairs** pairs_list;
+};
+
+static const SecAsn1Template kNameTemplate[] = {
+  { SEC_ASN1_SEQUENCE_OF, offsetof(X509Name,pairs_list),
+      kKeyValuePairSetTemplate, sizeof(X509Name) }
+};
+
+bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data,
+                                           size_t length) {
+  DCHECK(ber_name_data);
+
+  // First parse the BER |name_data| into the above structs.
+  SecAsn1CoderRef coder = NULL;
+  SecAsn1CoderCreate(&coder);
+  DCHECK(coder);
+  X509Name* name = NULL;
+  OSStatus err = SecAsn1Decode(coder, ber_name_data, length, kNameTemplate,
+                               &name);
+  if (err) {
+    LOG(ERROR) << "SecAsn1Decode returned " << err << "; name=" << name;
+    SecAsn1CoderRelease(coder);
+    return false;
+  }
+
+  // Now scan the structs and add the values to my string vectors.
+  // I don't store multiple common/locality/state/country names, so use
+  // temporary vectors for those.
+  std::vector<std::string> common_names, locality_names, state_names,
+      country_names;
+  std::vector<std::string>* values[] = {
+      &common_names, &locality_names,
+      &state_names, &country_names,
+      &this->street_addresses,
+      &this->organization_names,
+      &this->organization_unit_names,
+      &this->domain_components
+  };
+  DCHECK(arraysize(kOIDs) == arraysize(values));
+
+  for (int rdn=0; name[rdn].pairs_list; ++rdn) {
+    KeyValuePair *pair;
+    for (int pair_index = 0;
+         NULL != (pair = name[rdn].pairs_list[0][pair_index].pairs);
+         ++pair_index) {
+      switch (pair->value_type) {
+        case KeyValuePair::kTypeIA5String:          // ASCII (that means 7-bit!)
+        case KeyValuePair::kTypePrintableString:    // a subset of ASCII
+        case KeyValuePair::kTypeUTF8String:         // UTF-8
+          AddTypeValuePair(pair->key, DataToString(pair->value), values);
+          break;
+        case KeyValuePair::kTypeT61String:          // T61, pretend it's Latin-1
+          AddTypeValuePair(pair->key,
+                           Latin1DataToUTF8String(pair->value),
+                           values);
+          break;
+        case KeyValuePair::kTypeBMPString: {        // UTF-16, big-endian
+          std::string value;
+          UTF16BigEndianToUTF8(reinterpret_cast<char16*>(pair->value.Data),
+                               pair->value.Length / sizeof(char16),
+                               &value);
+          AddTypeValuePair(pair->key, value, values);
+          break;
+        }
+        case KeyValuePair::kTypeUniversalString: {  // UTF-32, big-endian
+          std::string value;
+          UTF32BigEndianToUTF8(reinterpret_cast<char32*>(pair->value.Data),
+                               pair->value.Length / sizeof(char32),
+                               &value);
+          AddTypeValuePair(pair->key, value, values);
+          break;
+        }
+        default:
+          DCHECK_EQ(pair->value_type, KeyValuePair::kTypeOther);
+          // We don't know what data type this is, but we'll store it as a blob.
+          // Displaying the string may not work, but at least it can be compared
+          // byte-for-byte by a Matches() call.
+          AddTypeValuePair(pair->key, DataToString(pair->value), values);
+          break;
+      }
+    }
+  }
+
+  SetSingle(common_names, &this->common_name);
+  SetSingle(locality_names, &this->locality_name);
+  SetSingle(state_names, &this->state_or_province_name);
+  SetSingle(country_names, &this->country_name);
+
+  // Releasing |coder| frees all the memory pointed to via |name|.
+  SecAsn1CoderRelease(coder);
+  return true;
+}
+
+  
+// SUBROUTINES:
+
+static std::string DataToString(CSSM_DATA data) {
+  return std::string(
+      reinterpret_cast<std::string::value_type*>(data.Data),
+      data.Length);
+}
+
+static std::string Latin1DataToUTF8String(CSSM_DATA data) {
+  string16 utf16;
+  if (!CodepageToUTF16(DataToString(data), base::kCodepageLatin1,
+                       base::OnStringConversionError::FAIL, &utf16))
+    return "";
+  return UTF16ToUTF8(utf16);
+}
+
+bool UTF16BigEndianToUTF8(char16* chars, size_t length,
+                          std::string* out_string) {
+  for (size_t i = 0; i < length; i++)
+    chars[i] = EndianU16_BtoN(chars[i]);
+  return UTF16ToUTF8(chars, length, out_string);
+}
+
+bool UTF32BigEndianToUTF8(char32* chars, size_t length,
+                          std::string* out_string) {
+  for (size_t i = 0; i < length; i++)
+    chars[i] = EndianS32_BtoN(chars[i]);
+#if defined(WCHAR_T_IS_UTF32)
+  return WideToUTF8(reinterpret_cast<const wchar_t*>(chars),
+                    length, out_string);
+#else
+#error This code doesn't handle 16-bit wchar_t.
+#endif
+}
+
+  static void AddTypeValuePair(const CSSM_OID type,
+                               const std::string& value,
+                               std::vector<std::string>* values[]) {
+  for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
+    if (CSSMOIDEqual(&type, kOIDs[oid])) {
+      values[oid]->push_back(value);
+      break;
+    }
+  }
+}
+
+static void SetSingle(const std::vector<std::string> &values,
+                      std::string* single_value) {
+  // We don't expect to have more than one CN, L, S, and C.
+  LOG_IF(WARNING, values.size() > 1) << "Didn't expect multiple values";
+  if (values.size() > 0)
+    *single_value = values[0];
+}
+
+}  // namespace net
diff --git a/net/base/x509_cert_types_unittest.cc b/net/base/x509_cert_types_unittest.cc
new file mode 100644
index 0000000..50012b1
--- /dev/null
+++ b/net/base/x509_cert_types_unittest.cc
@@ -0,0 +1,344 @@
+// Copyright (c) 2010 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/base/x509_cert_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+//  0:d=0  hl=2 l=  95 cons: SEQUENCE
+//  2:d=1  hl=2 l=  11 cons:  SET
+//  4:d=2  hl=2 l=   9 cons:   SEQUENCE
+//  6:d=3  hl=2 l=   3 prim:    OBJECT            :countryName
+// 11:d=3  hl=2 l=   2 prim:    PRINTABLESTRING   :US
+// 15:d=1  hl=2 l=  23 cons:  SET
+// 17:d=2  hl=2 l=  21 cons:   SEQUENCE
+// 19:d=3  hl=2 l=   3 prim:    OBJECT            :organizationName
+// 24:d=3  hl=2 l=  14 prim:    PRINTABLESTRING   :VeriSign, Inc.
+// 40:d=1  hl=2 l=  55 cons:  SET
+// 42:d=2  hl=2 l=  53 cons:   SEQUENCE
+// 44:d=3  hl=2 l=   3 prim:    OBJECT            :organizationalUnitName
+// 49:d=3  hl=2 l=  46 prim:    PRINTABLESTRING   :Class 1 Public Primary Certification Authority
+static const uint8 VerisignDN[] = {
+  0x30, 0x5f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+  0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+  0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+  0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2e, 0x43,
+  0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
+  0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74,
+  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+  0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
+};
+
+//  0:d=0  hl=2 l= 125 cons: SEQUENCE
+//  2:d=1  hl=2 l=  11 cons:  SET
+//  4:d=2  hl=2 l=   9 cons:   SEQUENCE
+//  6:d=3  hl=2 l=   3 prim:    OBJECT            :countryName
+// 11:d=3  hl=2 l=   2 prim:    PRINTABLESTRING   :IL
+// 15:d=1  hl=2 l=  22 cons:  SET
+// 17:d=2  hl=2 l=  20 cons:   SEQUENCE
+// 19:d=3  hl=2 l=   3 prim:    OBJECT            :organizationName
+// 24:d=3  hl=2 l=  13 prim:    PRINTABLESTRING   :StartCom Ltd.
+// 39:d=1  hl=2 l=  43 cons:  SET
+// 41:d=2  hl=2 l=  41 cons:   SEQUENCE
+// 43:d=3  hl=2 l=   3 prim:    OBJECT            :organizationalUnitName
+// 48:d=3  hl=2 l=  34 prim:    PRINTABLESTRING   :Secure Digital Certificate Signing
+// 84:d=1  hl=2 l=  41 cons:  SET
+// 86:d=2  hl=2 l=  39 cons:   SEQUENCE
+// 88:d=3  hl=2 l=   3 prim:    OBJECT            :commonName
+// 93:d=3  hl=2 l=  32 prim:    PRINTABLESTRING   :StartCom Certification Authority
+static const uint8 StartComDN[] = {
+  0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+  0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d,
+  0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e,
+  0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65,
+  0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20,
+  0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53,
+  0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55,
+  0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20,
+  0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+  0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
+};
+
+//  0:d=0  hl=3 l= 174 cons: SEQUENCE
+//  3:d=1  hl=2 l=  11 cons:  SET
+//  5:d=2  hl=2 l=   9 cons:   SEQUENCE
+//  7:d=3  hl=2 l=   3 prim:    OBJECT            :countryName
+// 12:d=3  hl=2 l=   2 prim:    PRINTABLESTRING   :US
+// 16:d=1  hl=2 l=  11 cons:  SET
+// 18:d=2  hl=2 l=   9 cons:   SEQUENCE
+// 20:d=3  hl=2 l=   3 prim:    OBJECT            :stateOrProvinceName
+// 25:d=3  hl=2 l=   2 prim:    PRINTABLESTRING   :UT
+// 29:d=1  hl=2 l=  23 cons:  SET
+// 31:d=2  hl=2 l=  21 cons:   SEQUENCE
+// 33:d=3  hl=2 l=   3 prim:    OBJECT            :localityName
+// 38:d=3  hl=2 l=  14 prim:    PRINTABLESTRING   :Salt Lake City
+// 54:d=1  hl=2 l=  30 cons:  SET
+// 56:d=2  hl=2 l=  28 cons:   SEQUENCE
+// 58:d=3  hl=2 l=   3 prim:    OBJECT            :organizationName
+// 63:d=3  hl=2 l=  21 prim:    PRINTABLESTRING   :The USERTRUST Network
+// 86:d=1  hl=2 l=  33 cons:  SET
+// 88:d=2  hl=2 l=  31 cons:   SEQUENCE
+// 90:d=3  hl=2 l=   3 prim:    OBJECT            :organizationalUnitName
+// 95:d=3  hl=2 l=  24 prim:    PRINTABLESTRING   :http://www.usertrust.com
+//121:d=1  hl=2 l=  54 cons:  SET
+//123:d=2  hl=2 l=  52 cons:   SEQUENCE
+//125:d=3  hl=2 l=   3 prim:    OBJECT            :commonName
+//130:d=3  hl=2 l=  45 prim:    PRINTABLESTRING   :UTN-USERFirst-Client Authentication and Email
+static const uint8 UserTrustDN[] = {
+  0x30, 0x81, 0xae, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+  0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+  0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
+  0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65, 0x20, 0x43, 0x69,
+  0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15,
+  0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54,
+  0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x21, 0x30, 0x1f, 0x06,
+  0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+  0x77, 0x77, 0x77, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74,
+  0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03,
+  0x13, 0x2d, 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72,
+  0x73, 0x74, 0x2d, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x41, 0x75, 0x74,
+  0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61,
+  0x6e, 0x64, 0x20, 0x45, 0x6d, 0x61, 0x69, 0x6c
+};
+
+//  0:d=0  hl=3 l= 190 cons: SEQUENCE
+//  3:d=1  hl=2 l=  63 cons:  SET
+//  5:d=2  hl=2 l=  61 cons:   SEQUENCE
+//  7:d=3  hl=2 l=   3 prim:    OBJECT            :commonName
+// 12:d=3  hl=2 l=  54 prim:    UTF8STRING        :TÜRKTRUST Elektronik Sertifika Hizmet SaÄŸlayıcısı
+// 68:d=1  hl=2 l=  11 cons:  SET
+// 70:d=2  hl=2 l=   9 cons:   SEQUENCE
+// 72:d=3  hl=2 l=   3 prim:    OBJECT            :countryName
+// 77:d=3  hl=2 l=   2 prim:    PRINTABLESTRING   :TR
+// 81:d=1  hl=2 l=  15 cons:  SET
+// 83:d=2  hl=2 l=  13 cons:   SEQUENCE
+// 85:d=3  hl=2 l=   3 prim:    OBJECT            :localityName
+// 90:d=3  hl=2 l=   6 prim:    UTF8STRING        :Ankara
+// 98:d=1  hl=2 l=  93 cons:  SET
+//100:d=2  hl=2 l=  91 cons:   SEQUENCE
+//102:d=3  hl=2 l=   3 prim:    OBJECT            :organizationName
+//107:d=3  hl=2 l=  84 prim:    UTF8STRING        :TÜRKTRUST Bilgi Ä°letiÅŸim ve BiliÅŸim GüvenliÄŸi Hizmetleri A.Åž. (c) Kasım 2005
+static const uint8 TurkTrustDN[] = {
+  0x30, 0x81, 0xbe, 0x31, 0x3f, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+  0x36, 0x54, 0xc3, 0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x45,
+  0x6c, 0x65, 0x6b, 0x74, 0x72, 0x6f, 0x6e, 0x69, 0x6b, 0x20, 0x53, 0x65, 0x72,
+  0x74, 0x69, 0x66, 0x69, 0x6b, 0x61, 0x20, 0x48, 0x69, 0x7a, 0x6d, 0x65, 0x74,
+  0x20, 0x53, 0x61, 0xc4, 0x9f, 0x6c, 0x61, 0x79, 0xc4, 0xb1, 0x63, 0xc4, 0xb1,
+  0x73, 0xc4, 0xb1, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+  0x02, 0x54, 0x52, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c,
+  0x06, 0x41, 0x6e, 0x6b, 0x61, 0x72, 0x61, 0x31, 0x5d, 0x30, 0x5b, 0x06, 0x03,
+  0x55, 0x04, 0x0a, 0x0c, 0x54, 0x54, 0xc3, 0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55,
+  0x53, 0x54, 0x20, 0x42, 0x69, 0x6c, 0x67, 0x69, 0x20, 0xc4, 0xb0, 0x6c, 0x65,
+  0x74, 0x69, 0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x76, 0x65, 0x20, 0x42, 0x69, 0x6c,
+  0x69, 0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x47, 0xc3, 0xbc, 0x76, 0x65, 0x6e, 0x6c,
+  0x69, 0xc4, 0x9f, 0x69, 0x20, 0x48, 0x69, 0x7a, 0x6d, 0x65, 0x74, 0x6c, 0x65,
+  0x72, 0x69, 0x20, 0x41, 0x2e, 0xc5, 0x9e, 0x2e, 0x20, 0x28, 0x63, 0x29, 0x20,
+  0x4b, 0x61, 0x73, 0xc4, 0xb1, 0x6d, 0x20, 0x32, 0x30, 0x30, 0x35, 0x30, 0x1e,
+  0x17, 0x0d, 0x30, 0x35, 0x31, 0x31, 0x30, 0x37, 0x31, 0x30, 0x30, 0x37, 0x35,
+  0x37
+};
+
+// 33:d=2  hl=3 l= 207 cons:   SEQUENCE
+// 36:d=3  hl=2 l=  11 cons:    SET
+// 38:d=4  hl=2 l=   9 cons:     SEQUENCE
+// 40:d=5  hl=2 l=   3 prim:      OBJECT            :countryName
+// 45:d=5  hl=2 l=   2 prim:      PRINTABLESTRING   :AT
+// 49:d=3  hl=3 l= 139 cons:    SET
+// 52:d=4  hl=3 l= 136 cons:     SEQUENCE
+// 55:d=5  hl=2 l=   3 prim:      OBJECT            :organizationName
+// 60:d=5  hl=3 l= 128 prim:      BMPSTRING         :A-Trust Ges. für Sicherheitssysteme im elektr. Datenverkehr GmbH
+//191:d=3  hl=2 l=  24 cons:    SET
+//193:d=4  hl=2 l=  22 cons:     SEQUENCE
+//195:d=5  hl=2 l=   3 prim:      OBJECT            :organizationalUnitName
+//200:d=5  hl=2 l=  15 prim:      PRINTABLESTRING   :A-Trust-Qual-01
+//217:d=3  hl=2 l=  24 cons:    SET
+//219:d=4  hl=2 l=  22 cons:     SEQUENCE
+//221:d=5  hl=2 l=   3 prim:      OBJECT            :commonName
+//226:d=5  hl=2 l=  15 prim:      PRINTABLESTRING   :A-Trust-Qual-01
+static const uint8 ATrustQual01DN[] = {
+  0x30, 0x81, 0xcf, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+  0x02, 0x41, 0x54, 0x31, 0x81, 0x8b, 0x30, 0x81, 0x88, 0x06, 0x03, 0x55, 0x04,
+  0x0a, 0x1e, 0x81, 0x80, 0x00, 0x41, 0x00, 0x2d, 0x00, 0x54, 0x00, 0x72, 0x00,
+  0x75, 0x00, 0x73, 0x00, 0x74, 0x00, 0x20, 0x00, 0x47, 0x00, 0x65, 0x00, 0x73,
+  0x00, 0x2e, 0x00, 0x20, 0x00, 0x66, 0x00, 0xfc, 0x00, 0x72, 0x00, 0x20, 0x00,
+  0x53, 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x68,
+  0x00, 0x65, 0x00, 0x69, 0x00, 0x74, 0x00, 0x73, 0x00, 0x73, 0x00, 0x79, 0x00,
+  0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69,
+  0x00, 0x6d, 0x00, 0x20, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x6b, 0x00,
+  0x74, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x44, 0x00, 0x61, 0x00, 0x74,
+  0x00, 0x65, 0x00, 0x6e, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6b, 0x00,
+  0x65, 0x00, 0x68, 0x00, 0x72, 0x00, 0x20, 0x00, 0x47, 0x00, 0x6d, 0x00, 0x62,
+  0x00, 0x48, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0f,
+  0x41, 0x2d, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x51, 0x75, 0x61, 0x6c, 0x2d,
+  0x30, 0x31, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f,
+  0x41, 0x2d, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x51, 0x75, 0x61, 0x6c, 0x2d,
+  0x30, 0x31, 0x30, 0x1e, 0x17
+};
+
+// 34:d=2  hl=3 l= 180 cons:   SEQUENCE
+// 37:d=3  hl=2 l=  20 cons:    SET
+// 39:d=4  hl=2 l=  18 cons:     SEQUENCE
+// 41:d=5  hl=2 l=   3 prim:      OBJECT            :organizationName
+// 46:d=5  hl=2 l=  11 prim:      PRINTABLESTRING   :Entrust.net
+// 59:d=3  hl=2 l=  64 cons:    SET
+// 61:d=4  hl=2 l=  62 cons:     SEQUENCE
+// 63:d=5  hl=2 l=   3 prim:      OBJECT            :organizationalUnitName
+// 68:d=5  hl=2 l=  55 prim:      T61STRING         :www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)
+//125:d=3  hl=2 l=  37 cons:    SET
+//127:d=4  hl=2 l=  35 cons:     SEQUENCE
+//129:d=5  hl=2 l=   3 prim:      OBJECT            :organizationalUnitName
+//134:d=5  hl=2 l=  28 prim:      PRINTABLESTRING   :(c) 1999 Entrust.net Limited
+//164:d=3  hl=2 l=  51 cons:    SET
+//166:d=4  hl=2 l=  49 cons:     SEQUENCE
+//168:d=5  hl=2 l=   3 prim:      OBJECT            :commonName
+//173:d=5  hl=2 l=  42 prim:      PRINTABLESTRING   :Entrust.net Certification Authority (2048)
+static const uint8 EntrustDN[] = {
+  0x30, 0x81, 0xb4, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+  0x0b, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x31,
+  0x40, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x14, 0x37, 0x77, 0x77, 0x77,
+  0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f,
+  0x43, 0x50, 0x53, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x20, 0x69, 0x6e, 0x63, 0x6f,
+  0x72, 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x2e, 0x20, 0x28,
+  0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c, 0x69, 0x61, 0x62, 0x2e, 0x29,
+  0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1c, 0x28, 0x63,
+  0x29, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73,
+  0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64,
+  0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x45, 0x6e,
+  0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x43, 0x65, 0x72,
+  0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+  0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x32, 0x30, 0x34, 0x38,
+  0x29
+};
+
+namespace net {
+
+TEST(X509TypesTest, Matching) {
+  CertPrincipal spamco;
+  spamco.common_name = "SpamCo Dept. Of Certificization";
+  spamco.country_name = "EB";
+  spamco.organization_names.push_back("SpamCo Holding Company, LLC");
+  spamco.organization_names.push_back("SpamCo Evil Masterminds");
+  spamco.organization_unit_names.push_back("Class Z Obfuscation Authority");
+  ASSERT_TRUE(spamco.Matches(spamco));
+
+  CertPrincipal bogus;
+  EXPECT_FALSE(bogus.Matches(spamco));
+  EXPECT_FALSE(spamco.Matches(bogus));
+
+  bogus = spamco;
+  EXPECT_TRUE(bogus.Matches(spamco));
+  EXPECT_TRUE(spamco.Matches(bogus));
+
+  bogus.organization_names.erase(bogus.organization_names.begin(),
+                                 bogus.organization_names.end());
+  EXPECT_FALSE(bogus.Matches(spamco));
+  EXPECT_FALSE(spamco.Matches(bogus));
+
+  bogus.organization_names.push_back("SpamCo Holding Company, LLC");
+  bogus.organization_names.push_back("SpamCo Evil Masterminds");
+  EXPECT_TRUE(bogus.Matches(spamco));
+  EXPECT_TRUE(spamco.Matches(bogus));
+
+  bogus.locality_name = "Elbosdorf";
+  EXPECT_FALSE(bogus.Matches(spamco));
+  EXPECT_FALSE(spamco.Matches(bogus));
+
+  bogus.locality_name = "";
+  bogus.organization_unit_names.push_back("Q Division");
+  EXPECT_FALSE(bogus.Matches(spamco));
+  EXPECT_FALSE(spamco.Matches(bogus));
+}
+
+#if defined(OS_MACOSX)  // ParseDistinguishedName not implemented for Win/Linux
+
+TEST(X509TypesTest, ParseDNVerisign) {
+  CertPrincipal verisign;
+  EXPECT_TRUE(verisign.ParseDistinguishedName(VerisignDN, sizeof(VerisignDN)));
+  EXPECT_EQ("", verisign.common_name);
+  EXPECT_EQ("US", verisign.country_name);
+  ASSERT_EQ(1U, verisign.organization_names.size());
+  EXPECT_EQ("VeriSign, Inc.", verisign.organization_names[0]);
+  ASSERT_EQ(1U, verisign.organization_unit_names.size());
+  EXPECT_EQ("Class 1 Public Primary Certification Authority",
+            verisign.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNStartcom) {
+  CertPrincipal startcom;
+  EXPECT_TRUE(startcom.ParseDistinguishedName(StartComDN, sizeof(StartComDN)));
+  EXPECT_EQ("StartCom Certification Authority", startcom.common_name);
+  EXPECT_EQ("IL", startcom.country_name);
+  ASSERT_EQ(1U, startcom.organization_names.size());
+  EXPECT_EQ("StartCom Ltd.", startcom.organization_names[0]);
+  ASSERT_EQ(1U, startcom.organization_unit_names.size());
+  EXPECT_EQ("Secure Digital Certificate Signing",
+            startcom.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNUserTrust) {
+  CertPrincipal usertrust;
+  EXPECT_TRUE(usertrust.ParseDistinguishedName(UserTrustDN,
+                                               sizeof(UserTrustDN)));
+  EXPECT_EQ("UTN-USERFirst-Client Authentication and Email",
+            usertrust.common_name);
+  EXPECT_EQ("US", usertrust.country_name);
+  EXPECT_EQ("UT", usertrust.state_or_province_name);
+  EXPECT_EQ("Salt Lake City", usertrust.locality_name);
+  ASSERT_EQ(1U, usertrust.organization_names.size());
+  EXPECT_EQ("The USERTRUST Network", usertrust.organization_names[0]);
+  ASSERT_EQ(1U, usertrust.organization_unit_names.size());
+  EXPECT_EQ("http://www.usertrust.com",
+            usertrust.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNTurkTrust) {
+  // Note: This tests parsing UTF8STRINGs.
+  CertPrincipal turktrust;
+  EXPECT_TRUE(turktrust.ParseDistinguishedName(TurkTrustDN,
+                                               sizeof(TurkTrustDN)));
+  EXPECT_EQ("TÜRKTRUST Elektronik Sertifika Hizmet SaÄŸlayıcısı",
+            turktrust.common_name);
+  EXPECT_EQ("TR", turktrust.country_name);
+  EXPECT_EQ("Ankara", turktrust.locality_name);
+  ASSERT_EQ(1U, turktrust.organization_names.size());
+  EXPECT_EQ("TÜRKTRUST Bilgi Ä°letiÅŸim ve BiliÅŸim GüvenliÄŸi Hizmetleri A.Åž. (c) Kasım 2005",
+            turktrust.organization_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNATrust) {
+  // Note: This tests parsing 16-bit BMPSTRINGs.
+  CertPrincipal atrust;
+  EXPECT_TRUE(atrust.ParseDistinguishedName(ATrustQual01DN,
+                                            sizeof(ATrustQual01DN)));
+  EXPECT_EQ("A-Trust-Qual-01",
+            atrust.common_name);
+  EXPECT_EQ("AT", atrust.country_name);
+  ASSERT_EQ(1U, atrust.organization_names.size());
+  EXPECT_EQ("A-Trust Ges. für Sicherheitssysteme im elektr. Datenverkehr GmbH",
+            atrust.organization_names[0]);
+  ASSERT_EQ(1U, atrust.organization_unit_names.size());
+  EXPECT_EQ("A-Trust-Qual-01",
+            atrust.organization_unit_names[0]);
+}
+
+TEST(X509TypesTest, ParseDNEntrust) {
+  // Note: This tests parsing T61STRINGs and fields with multiple values.
+  CertPrincipal entrust;
+  EXPECT_TRUE(entrust.ParseDistinguishedName(EntrustDN,
+                                             sizeof(EntrustDN)));
+  EXPECT_EQ("Entrust.net Certification Authority (2048)",
+            entrust.common_name);
+  EXPECT_EQ("", entrust.country_name);
+  ASSERT_EQ(1U, entrust.organization_names.size());
+  EXPECT_EQ("Entrust.net",
+            entrust.organization_names[0]);
+  ASSERT_EQ(2U, entrust.organization_unit_names.size());
+  EXPECT_EQ("www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)",
+            entrust.organization_unit_names[0]);
+  EXPECT_EQ("(c) 1999 Entrust.net Limited",
+            entrust.organization_unit_names[1]);
+}
+
+#endif
+
+}
diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc
index 24388d8..f5b28a6 100644
--- a/net/base/x509_certificate.cc
+++ b/net/base/x509_certificate.cc
@@ -4,8 +4,17 @@
 
 #include "net/base/x509_certificate.h"
 
+#if defined(OS_MACOSX)
+#include <Security/Security.h>
+#elif defined(USE_NSS)
+#include <cert.h>
+#endif
+
+#include <map>
+
 #include "base/histogram.h"
 #include "base/logging.h"
+#include "base/singleton.h"
 #include "base/time.h"
 
 namespace net {
@@ -14,7 +23,7 @@
 
 // Returns true if this cert fingerprint is the null (all zero) fingerprint.
 // We use this as a bogus fingerprint value.
-bool IsNullFingerprint(const X509Certificate::Fingerprint& fingerprint) {
+bool IsNullFingerprint(const SHA1Fingerprint& fingerprint) {
   for (size_t i = 0; i < arraysize(fingerprint.data); ++i) {
     if (fingerprint.data[i] != 0)
       return false;
@@ -24,16 +33,31 @@
 
 }  // namespace
 
-bool X509Certificate::FingerprintLessThan::operator()(
-    const Fingerprint& lhs,
-    const Fingerprint& rhs) const {
-  for (size_t i = 0; i < sizeof(lhs.data); ++i) {
-    if (lhs.data[i] < rhs.data[i])
-      return true;
-    if (lhs.data[i] > rhs.data[i])
-      return false;
-  }
+// static
+bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
+                                   X509Certificate::OSCertHandle b) {
+  DCHECK(a && b);
+  if (a == b)
+    return true;
+#if defined(OS_WIN)
+  return a->cbCertEncoded == b->cbCertEncoded &&
+      memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0;
+#elif defined(OS_MACOSX)
+  if (CFEqual(a, b))
+    return true;
+  CSSM_DATA a_data, b_data;
+  return SecCertificateGetData(a, &a_data) == noErr &&
+      SecCertificateGetData(b, &b_data) == noErr &&
+      a_data.Length == b_data.Length &&
+      memcmp(a_data.Data, b_data.Data, a_data.Length) == 0;
+#elif defined(USE_NSS)
+  return a->derCert.len == b->derCert.len &&
+      memcmp(a->derCert.data, b->derCert.data, a->derCert.len) == 0;
+#else
+  // TODO(snej): not implemented
+  UNREACHED();
   return false;
+#endif
 }
 
 bool X509Certificate::LessThan::operator()(X509Certificate* lhs,
@@ -41,7 +65,7 @@
   if (lhs == rhs)
     return false;
 
-  X509Certificate::FingerprintLessThan fingerprint_functor;
+  SHA1FingerprintLessThan fingerprint_functor;
   return fingerprint_functor(lhs->fingerprint_, rhs->fingerprint_);
 }
 
@@ -50,6 +74,32 @@
 // The cache does not hold a reference to the certificate objects.  The objects
 // must |Remove| themselves from the cache upon destruction (or else the cache
 // will be holding dead pointers to the objects).
+// TODO(rsleevi): There exists a chance of a use-after-free, due to a race
+// between Find() and Remove(). See http://crbug.com/49377
+class X509Certificate::Cache {
+ public:
+  static Cache* GetInstance();
+  void Insert(X509Certificate* cert);
+  void Remove(X509Certificate* cert);
+  X509Certificate* Find(const SHA1Fingerprint& fingerprint);
+
+ private:
+  typedef std::map<SHA1Fingerprint, X509Certificate*, SHA1FingerprintLessThan>
+      CertMap;
+
+  // Obtain an instance of X509Certificate::Cache via GetInstance().
+  Cache() {}
+  friend struct DefaultSingletonTraits<Cache>;
+
+  // You must acquire this lock before using any private data of this object.
+  // You must not block while holding this lock.
+  Lock lock_;
+
+  // The certificate cache.  You must acquire |lock_| before using |cache_|.
+  CertMap cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(Cache);
+};
 
 // Get the singleton object for the cache.
 // static
@@ -57,14 +107,13 @@
   return Singleton<X509Certificate::Cache>::get();
 }
 
-// Insert |cert| into the cache.  The cache does NOT AddRef |cert|.  The cache
-// must not already contain a certificate with the same fingerprint.
+// Insert |cert| into the cache.  The cache does NOT AddRef |cert|.
+// Any existing certificate with the same fingerprint will be replaced.
 void X509Certificate::Cache::Insert(X509Certificate* cert) {
   AutoLock lock(lock_);
 
   DCHECK(!IsNullFingerprint(cert->fingerprint())) <<
       "Only insert certs with real fingerprints.";
-  DCHECK(cache_.find(cert->fingerprint()) == cache_.end());
   cache_[cert->fingerprint()] = cert;
 };
 
@@ -81,7 +130,8 @@
 
 // Find a certificate in the cache with the given fingerprint.  If one does
 // not exist, this method returns NULL.
-X509Certificate* X509Certificate::Cache::Find(const Fingerprint& fingerprint) {
+X509Certificate* X509Certificate::Cache::Find(
+    const SHA1Fingerprint& fingerprint) {
   AutoLock lock(lock_);
 
   CertMap::iterator pos(cache_.find(fingerprint));
@@ -91,50 +141,11 @@
   return pos->second;
 };
 
-X509Certificate::Policy::Judgment X509Certificate::Policy::Check(
-    X509Certificate* cert) const {
-  // It shouldn't matter which set we check first, but we check denied first
-  // in case something strange has happened.
-
-  if (denied_.find(cert->fingerprint()) != denied_.end()) {
-    // DCHECK that the order didn't matter.
-    DCHECK(allowed_.find(cert->fingerprint()) == allowed_.end());
-    return DENIED;
-  }
-
-  if (allowed_.find(cert->fingerprint()) != allowed_.end()) {
-    // DCHECK that the order didn't matter.
-    DCHECK(denied_.find(cert->fingerprint()) == denied_.end());
-    return ALLOWED;
-  }
-
-  // We don't have a policy for this cert.
-  return UNKNOWN;
-}
-
-void X509Certificate::Policy::Allow(X509Certificate* cert) {
-  // Put the cert in the allowed set and (maybe) remove it from the denied set.
-  denied_.erase(cert->fingerprint());
-  allowed_.insert(cert->fingerprint());
-}
-
-void X509Certificate::Policy::Deny(X509Certificate* cert) {
-  // Put the cert in the denied set and (maybe) remove it from the allowed set.
-  allowed_.erase(cert->fingerprint());
-  denied_.insert(cert->fingerprint());
-}
-
-bool X509Certificate::Policy::HasAllowedCert() const {
-  return !allowed_.empty();
-}
-
-bool X509Certificate::Policy::HasDeniedCert() const {
-  return !denied_.empty();
-}
-
 // static
-X509Certificate* X509Certificate::CreateFromHandle(OSCertHandle cert_handle,
-                                                   Source source) {
+X509Certificate* X509Certificate::CreateFromHandle(
+    OSCertHandle cert_handle,
+    Source source,
+    const OSCertHandles& intermediates) {
   DCHECK(cert_handle);
   DCHECK(source != SOURCE_UNUSED);
 
@@ -144,18 +155,21 @@
       cache->Find(CalculateFingerprint(cert_handle));
   if (cached_cert) {
     DCHECK(cached_cert->source_ != SOURCE_UNUSED);
-    if (cached_cert->source_ >= source) {
-      // We've found a certificate with the same fingerprint in our cache.  We
-      // own the |cert_handle|, which makes it our job to free it.
-      FreeOSCertHandle(cert_handle);
+    if (cached_cert->source_ > source ||
+        (cached_cert->source_ == source &&
+         cached_cert->HasIntermediateCertificates(intermediates))) {
+      // Return the certificate with the same fingerprint from our cache.
       DHISTOGRAM_COUNTS("X509CertificateReuseCount", 1);
       return cached_cert;
     }
-    // Kick out the old certificate from our cache.  The new one is better.
-    cache->Remove(cached_cert);
+    // Else the new cert is better and will replace the old one in the cache.
   }
-  // Otherwise, allocate a new object.
-  return new X509Certificate(cert_handle, source);
+
+  // Otherwise, allocate and cache a new object.
+  X509Certificate* cert = new X509Certificate(cert_handle, source,
+                                              intermediates);
+  cache->Insert(cert);
+  return cert;
 }
 
 // static
@@ -165,12 +179,24 @@
   if (!cert_handle)
     return NULL;
 
-  return CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT);
+  X509Certificate* cert = CreateFromHandle(cert_handle,
+                                           SOURCE_LONE_CERT_IMPORT,
+                                           OSCertHandles());
+  FreeOSCertHandle(cert_handle);
+  return cert;
 }
 
-X509Certificate::X509Certificate(OSCertHandle cert_handle, Source source)
-    : cert_handle_(cert_handle),
+X509Certificate::X509Certificate(OSCertHandle cert_handle,
+                                 Source source,
+                                 const OSCertHandles& intermediates)
+    : cert_handle_(DupOSCertHandle(cert_handle)),
       source_(source) {
+#if defined(OS_MACOSX) || defined(OS_WIN)
+  // Copy/retain the intermediate cert handles.
+  for (size_t i = 0; i < intermediates.size(); ++i)
+    intermediate_ca_certs_.push_back(DupOSCertHandle(intermediates[i]));
+#endif
+  // Platform-specific initialization.
   Initialize();
 }
 
@@ -202,4 +228,24 @@
   return base::Time::Now() > valid_expiry();
 }
 
+bool X509Certificate::HasIntermediateCertificate(OSCertHandle cert) {
+#if defined(OS_MACOSX) || defined(OS_WIN)
+  for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
+    if (IsSameOSCert(cert, intermediate_ca_certs_[i]))
+      return true;
+  }
+  return false;
+#else
+  return true;
+#endif
+}
+
+bool X509Certificate::HasIntermediateCertificates(const OSCertHandles& certs) {
+  for (size_t i = 0; i < certs.size(); ++i) {
+    if (!HasIntermediateCertificate(certs[i]))
+      return false;
+  }
+  return true;
+}
+
 }  // namespace net
diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h
index 215489b..d6b3447 100644
--- a/net/base/x509_certificate.h
+++ b/net/base/x509_certificate.h
@@ -7,21 +7,20 @@
 
 #include <string.h>
 
-#include <map>
-#include <set>
 #include <string>
 #include <vector>
 
 #include "base/ref_counted.h"
-#include "base/singleton.h"
 #include "base/time.h"
+#include "net/base/x509_cert_types.h"
 #include "testing/gtest/include/gtest/gtest_prod.h"
 
 #if defined(OS_WIN)
 #include <windows.h>
 #include <wincrypt.h>
 #elif defined(OS_MACOSX)
-#include <Security/Security.h>
+#include <CoreFoundation/CFArray.h>
+#include <Security/SecBase.h>
 #elif defined(USE_NSS)
 // Forward declaration; real one in <cert.h>
 struct CERTCertificateStr;
@@ -36,28 +35,6 @@
 // X509Certificate represents an X.509 certificate used by SSL.
 class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> {
  public:
-  // SHA-1 fingerprint (160 bits) of a certificate.
-  struct Fingerprint {
-    bool Equals(const Fingerprint& other) const {
-      return memcmp(data, other.data, sizeof(data)) == 0;
-    }
-
-    unsigned char data[20];
-  };
-
-  class FingerprintLessThan
-      : public std::binary_function<Fingerprint, Fingerprint, bool> {
-   public:
-    bool operator() (const Fingerprint& lhs, const Fingerprint& rhs) const;
-  };
-
-  // Predicate functor used in maps when X509Certificate is used as the key.
-  class LessThan
-      : public std::binary_function<X509Certificate*, X509Certificate*, bool> {
-   public:
-    bool operator() (X509Certificate* lhs,  X509Certificate* rhs) const;
-  };
-
   // A handle to the certificate object in the underlying crypto library.
   // We assume that OSCertHandle is a pointer type on all platforms and
   // NULL is an invalid OSCertHandle.
@@ -72,62 +49,13 @@
   typedef void* OSCertHandle;
 #endif
 
-  // Principal represent an X.509 principal.
-  struct Principal {
-    Principal() { }
-    explicit Principal(const std::string& name) : common_name(name) { }
+  typedef std::vector<OSCertHandle> OSCertHandles;
 
-    // The different attributes for a principal.  They may be "".
-    // Note that some of them can have several values.
-
-    std::string common_name;
-    std::string locality_name;
-    std::string state_or_province_name;
-    std::string country_name;
-
-    std::vector<std::string> street_addresses;
-    std::vector<std::string> organization_names;
-    std::vector<std::string> organization_unit_names;
-    std::vector<std::string> domain_components;
-  };
-
-  // This class is useful for maintaining policies about which certificates are
-  // permitted or forbidden for a particular purpose.
-  class Policy {
+  // Predicate functor used in maps when X509Certificate is used as the key.
+  class LessThan
+      : public std::binary_function<X509Certificate*, X509Certificate*, bool> {
    public:
-    // The judgments this policy can reach.
-    enum Judgment {
-      // We don't have policy information for this certificate.
-      UNKNOWN,
-
-      // This certificate is allowed.
-      ALLOWED,
-
-      // This certificate is denied.
-      DENIED,
-    };
-
-    // Returns the judgment this policy makes about this certificate.
-    Judgment Check(X509Certificate* cert) const;
-
-    // Causes the policy to allow this certificate.
-    void Allow(X509Certificate* cert);
-
-    // Causes the policy to deny this certificate.
-    void Deny(X509Certificate* cert);
-
-    // Returns true if this policy has allowed at least one certificate.
-    bool HasAllowedCert() const;
-
-    // Returns true if this policy has denied at least one certificate.
-    bool HasDeniedCert() const;
-
-   private:
-    // The set of fingerprints of allowed certificates.
-    std::set<Fingerprint, FingerprintLessThan> allowed_;
-
-    // The set of fingerprints of denied certificates.
-    std::set<Fingerprint, FingerprintLessThan> denied_;
+    bool operator() (X509Certificate* lhs,  X509Certificate* rhs) const;
   };
 
   // Where the certificate comes from.  The enumeration constants are
@@ -144,18 +72,17 @@
     VERIFY_EV_CERT = 1 << 1,
   };
 
-  // Create an X509Certificate from a handle to the certificate object
-  // in the underlying crypto library. This is a transfer of ownership;
-  // X509Certificate will properly dispose of |cert_handle| for you.
-  // |source| specifies where |cert_handle| comes from.  Given two
-  // certificate handles for the same certificate, our certificate cache
-  // prefers the handle from the network because our HTTP cache isn't
-  // caching the corresponding intermediate CA certificates yet
+  // Create an X509Certificate from a handle to the certificate object in the
+  // underlying crypto library. |source| specifies where |cert_handle| comes
+  // from.  Given two certificate handles for the same certificate, our
+  // certificate cache prefers the handle from the network because our HTTP
+  // cache isn't caching the corresponding intermediate CA certificates yet
   // (http://crbug.com/7065).
-  //
+  // The list of intermediate certificates is ignored under NSS (i.e. Linux.)
   // The returned pointer must be stored in a scoped_refptr<X509Certificate>.
   static X509Certificate* CreateFromHandle(OSCertHandle cert_handle,
-                                           Source source);
+      Source source,
+      const OSCertHandles& intermediates);
 
   // Create an X509Certificate from the BER-encoded representation.
   // Returns NULL on failure.
@@ -183,10 +110,10 @@
   // The subject of the certificate.  For HTTPS server certificates, this
   // represents the web server.  The common name of the subject should match
   // the host name of the web server.
-  const Principal& subject() const { return subject_; }
+  const CertPrincipal& subject() const { return subject_; }
 
   // The issuer of the certificate.
-  const Principal& issuer() const { return issuer_; }
+  const CertPrincipal& issuer() const { return issuer_; }
 
   // Time period during which the certificate is valid.  More precisely, this
   // certificate is invalid before the |valid_start| date and invalid after
@@ -197,7 +124,7 @@
   const base::Time& valid_expiry() const { return valid_expiry_; }
 
   // The fingerprint of this certificate.
-  const Fingerprint& fingerprint() const { return fingerprint_; }
+  const SHA1Fingerprint& fingerprint() const { return fingerprint_; }
 
   // Gets the DNS names in the certificate.  Pursuant to RFC 2818, Section 3.1
   // Server Identity, if the certificate has a subjectAltName extension of
@@ -210,20 +137,45 @@
   bool HasExpired() const;
 
 #if defined(OS_MACOSX) || defined(OS_WIN)
-  // Adds an untrusted intermediate certificate that may be needed for
-  // chain building.
-  void AddIntermediateCertificate(OSCertHandle cert) {
-    intermediate_ca_certs_.push_back(cert);
-  }
-
   // Returns intermediate certificates added via AddIntermediateCertificate().
   // Ownership follows the "get" rule: it is the caller's responsibility to
   // retain the elements of the result.
-  const std::vector<OSCertHandle>& GetIntermediateCertificates() const {
+  const OSCertHandles& GetIntermediateCertificates() const {
     return intermediate_ca_certs_;
   }
 #endif
 
+  // Returns true if I already contain the given intermediate cert.
+  bool HasIntermediateCertificate(OSCertHandle cert);
+
+  // Returns true if I already contain all the given intermediate certs.
+  bool HasIntermediateCertificates(const OSCertHandles& certs);
+
+#if defined(OS_MACOSX)
+  // Does this certificate's usage allow SSL client authentication?
+  bool SupportsSSLClientAuth() const;
+
+  // Do any of the given issuer names appear in this cert's chain of trust?
+  bool IsIssuedBy(const std::vector<CertPrincipal>& valid_issuers);
+
+  // Creates a security policy for SSL client certificates.
+  static OSStatus CreateSSLClientPolicy(SecPolicyRef* outPolicy);
+
+  // Adds all available SSL client identity certs to the given vector.
+  // |server_domain| is a hint for which domain the cert is to be sent to
+  // (a cert previously specified as the default for that domain will be given
+  // precedence and returned first in the output vector.)
+  // If valid_issuers is non-empty, only certs that were transitively issued by
+  // one of the given names will be included in the list.
+  static bool GetSSLClientCertificates(
+      const std::string& server_domain,
+      const std::vector<CertPrincipal>& valid_issuers,
+      std::vector<scoped_refptr<X509Certificate> >* certs);
+
+  // Creates the chain of certs to use for this client identity cert.
+  CFArrayRef CreateClientCertificateChain() const;
+#endif
+
   // Verifies the certificate against the given hostname.  Returns OK if
   // successful or an error code upon failure.
   //
@@ -243,39 +195,31 @@
 
   OSCertHandle os_cert_handle() const { return cert_handle_; }
 
+  // Returns true if two OSCertHandles refer to identical certificates.
+  static bool IsSameOSCert(OSCertHandle a, OSCertHandle b);
+
+  // Creates an OS certificate handle from the BER-encoded representation.
+  // Returns NULL on failure.
+  static OSCertHandle CreateOSCertHandleFromBytes(const char* data,
+                                                  int length);
+
+  // Duplicates (or adds a reference to) an OS certificate handle.
+  static OSCertHandle DupOSCertHandle(OSCertHandle cert_handle);
+
+  // Frees (or releases a reference to) an OS certificate handle.
+  static void FreeOSCertHandle(OSCertHandle cert_handle);
+
  private:
   friend class base::RefCountedThreadSafe<X509Certificate>;
   FRIEND_TEST(X509CertificateTest, Cache);
+  FRIEND_TEST(X509CertificateTest, IntermediateCertificates);
 
-  // A cache of X509Certificate objects.
-  class Cache {
-   public:
-    static Cache* GetInstance();
-    void Insert(X509Certificate* cert);
-    void Remove(X509Certificate* cert);
-    X509Certificate* Find(const Fingerprint& fingerprint);
-
-   private:
-    typedef std::map<Fingerprint, X509Certificate*, FingerprintLessThan>
-        CertMap;
-
-    // Obtain an instance of X509Certificate::Cache via GetInstance().
-    Cache() { }
-    friend struct DefaultSingletonTraits<Cache>;
-
-    // You must acquire this lock before using any private data of this object.
-    // You must not block while holding this lock.
-    Lock lock_;
-
-    // The certificate cache.  You must acquire |lock_| before using |cache_|.
-    CertMap cache_;
-
-    DISALLOW_COPY_AND_ASSIGN(Cache);
-  };
+  class Cache;
 
   // Construct an X509Certificate from a handle to the certificate object
   // in the underlying crypto library.
-  X509Certificate(OSCertHandle cert_handle, Source source);
+  X509Certificate(OSCertHandle cert_handle, Source source,
+                  const OSCertHandles& intermediates);
 
   ~X509Certificate();
 
@@ -284,23 +228,15 @@
 
   bool VerifyEV() const;
 
-  // Creates an OS certificate handle from the BER-encoded representation.
-  // Returns NULL on failure.
-  static OSCertHandle CreateOSCertHandleFromBytes(const char* data,
-                                                  int length);
-
-  // Frees an OS certificate handle.
-  static void FreeOSCertHandle(OSCertHandle cert_handle);
-
   // Calculates the SHA-1 fingerprint of the certificate.  Returns an empty
   // (all zero) fingerprint on failure.
-  static Fingerprint CalculateFingerprint(OSCertHandle cert_handle);
+  static SHA1Fingerprint CalculateFingerprint(OSCertHandle cert_handle);
 
   // The subject of the certificate.
-  Principal subject_;
+  CertPrincipal subject_;
 
   // The issuer of the certificate.
-  Principal issuer_;
+  CertPrincipal issuer_;
 
   // This certificate is not valid before |valid_start_|
   base::Time valid_start_;
@@ -309,15 +245,21 @@
   base::Time valid_expiry_;
 
   // The fingerprint of this certificate.
-  Fingerprint fingerprint_;
+  SHA1Fingerprint fingerprint_;
 
   // A handle to the certificate object in the underlying crypto library.
   OSCertHandle cert_handle_;
 
 #if defined(OS_MACOSX) || defined(OS_WIN)
   // Untrusted intermediate certificates associated with this certificate
-  // that may be needed for chain building.
-  std::vector<OSCertHandle> intermediate_ca_certs_;
+  // that may be needed for chain building. (NSS impl does not need these.)
+  OSCertHandles intermediate_ca_certs_;
+#endif
+
+#if defined(OS_MACOSX)
+  // Blocks multiple threads from verifying the cert simultaneously.
+  // (Marked mutable because it's used in a const method.)
+  mutable Lock verification_lock_;
 #endif
 
   // Where the certificate comes from.
diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc
index 2b96be4..ed46adc 100644
--- a/net/base/x509_certificate_mac.cc
+++ b/net/base/x509_certificate_mac.cc
@@ -5,11 +5,13 @@
 #include "net/base/x509_certificate.h"
 
 #include <CommonCrypto/CommonDigest.h>
+#include <Security/Security.h>
 #include <time.h>
 
 #include "base/scoped_cftyperef.h"
 #include "base/logging.h"
 #include "base/pickle.h"
+#include "base/sys_string_conversions.h"
 #include "net/base/cert_status_flags.h"
 #include "net/base/cert_verify_result.h"
 #include "net/base/net_errors.h"
@@ -79,11 +81,6 @@
 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef,
                                                       CFDictionaryRef*);
 
-inline bool CSSMOIDEqual(const CSSM_OID* oid1, const CSSM_OID* oid2) {
-  return oid1->Length == oid2->Length &&
-      (memcmp(oid1->Data, oid2->Data, oid1->Length) == 0);
-}
-
 int NetErrorFromOSStatus(OSStatus status) {
   switch (status) {
     case noErr:
@@ -172,64 +169,6 @@
   return override_hostname_mismatch;
 }
 
-void ParsePrincipal(const CSSM_X509_NAME* name,
-                    X509Certificate::Principal* principal) {
-  std::vector<std::string> common_names, locality_names, state_names,
-      country_names;
-
-  // TODO(jcampan): add business_category and serial_number.
-  const CSSM_OID* kOIDs[] = { &CSSMOID_CommonName,
-                              &CSSMOID_LocalityName,
-                              &CSSMOID_StateProvinceName,
-                              &CSSMOID_CountryName,
-                              &CSSMOID_StreetAddress,
-                              &CSSMOID_OrganizationName,
-                              &CSSMOID_OrganizationalUnitName,
-                              &CSSMOID_DNQualifier };  // This should be "DC"
-                                                       // but is undoubtedly
-                                                       // wrong. TODO(avi):
-                                                       // Find the right OID.
-
-  std::vector<std::string>* values[] = {
-      &common_names, &locality_names,
-      &state_names, &country_names,
-      &(principal->street_addresses),
-      &(principal->organization_names),
-      &(principal->organization_unit_names),
-      &(principal->domain_components) };
-  DCHECK(arraysize(kOIDs) == arraysize(values));
-
-  for (size_t rdn = 0; rdn < name->numberOfRDNs; ++rdn) {
-    CSSM_X509_RDN rdn_struct = name->RelativeDistinguishedName[rdn];
-    for (size_t pair = 0; pair < rdn_struct.numberOfPairs; ++pair) {
-      CSSM_X509_TYPE_VALUE_PAIR pair_struct =
-          rdn_struct.AttributeTypeAndValue[pair];
-      for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
-        if (CSSMOIDEqual(&pair_struct.type, kOIDs[oid])) {
-          std::string value =
-              std::string(reinterpret_cast<std::string::value_type*>
-                              (pair_struct.value.Data),
-                          pair_struct.value.Length);
-          values[oid]->push_back(value);
-          break;
-        }
-      }
-    }
-  }
-
-  // We don't expect to have more than one CN, L, S, and C.
-  std::vector<std::string>* single_value_lists[4] = {
-      &common_names, &locality_names, &state_names, &country_names };
-  std::string* single_values[4] = {
-      &principal->common_name, &principal->locality_name,
-      &principal->state_or_province_name, &principal->country_name };
-  for (size_t i = 0; i < arraysize(single_value_lists); ++i) {
-    DCHECK(single_value_lists[i]->size() <= 1);
-    if (single_value_lists[i]->size() > 0)
-      *(single_values[i]) = (*(single_value_lists[i]))[0];
-  }
-}
-
 struct CSSMFields {
   CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {}
   ~CSSMFields() {
@@ -356,17 +295,94 @@
   }
 }
 
+// Creates a SecPolicyRef for the given OID, with optional value.
+OSStatus CreatePolicy(const CSSM_OID* policy_OID,
+                      void* option_data,
+                      size_t option_length,
+                      SecPolicyRef* policy) {
+  SecPolicySearchRef search;
+  OSStatus err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy_OID, NULL,
+                                       &search);
+  if (err)
+    return err;
+  err = SecPolicySearchCopyNext(search, policy);
+  CFRelease(search);
+  if (err)
+    return err;
+
+  if (option_data) {
+    CSSM_DATA options_data = {
+      option_length,
+      reinterpret_cast<uint8_t*>(option_data)
+    };
+    err = SecPolicySetValue(*policy, &options_data);
+    if (err) {
+      CFRelease(*policy);
+      return err;
+    }
+  }
+  return noErr;
+}
+
+// Gets the issuer for a given cert, starting with the cert itself and
+// including the intermediate and finally root certificates (if any).
+// This function calls SecTrust but doesn't actually pay attention to the trust
+// result: it shouldn't be used to determine trust, just to traverse the chain.
+// Caller is responsible for releasing the value stored into *out_cert_chain.
+OSStatus CopyCertChain(SecCertificateRef cert_handle,
+                       CFArrayRef* out_cert_chain) {
+  DCHECK(cert_handle && out_cert_chain);
+  // Create an SSL policy ref configured for client cert evaluation.
+  SecPolicyRef ssl_policy;
+  OSStatus result = X509Certificate::CreateSSLClientPolicy(&ssl_policy);
+  if (result)
+    return result;
+  scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
+
+  // Create a SecTrustRef.
+  scoped_cftyperef<CFArrayRef> input_certs(
+      CFArrayCreate(NULL, (const void**)&cert_handle, 1,
+                    &kCFTypeArrayCallBacks));
+  SecTrustRef trust_ref = NULL;
+  result = SecTrustCreateWithCertificates(input_certs, ssl_policy, &trust_ref);
+  if (result)
+    return result;
+  scoped_cftyperef<SecTrustRef> trust(trust_ref);
+
+  // Evaluate trust, which creates the cert chain.
+  SecTrustResultType status;
+  CSSM_TP_APPLE_EVIDENCE_INFO* status_chain;
+  result = SecTrustEvaluate(trust, &status);
+  if (result)
+    return result;
+  return SecTrustGetResult(trust, &status, out_cert_chain, &status_chain);
+}
+
+// Returns true if |purpose| is listed as allowed in |usage|. This
+// function also considers the "Any" purpose. If the attribute is
+// present and empty, we return false.
+bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage,
+                            const CSSM_OID* purpose) {
+  for (unsigned p = 0; p < usage->numPurposes; ++p) {
+    if (CSSMOIDEqual(&usage->purposes[p], purpose))
+      return true;
+    if (CSSMOIDEqual(&usage->purposes[p], &CSSMOID_ExtendedKeyUsageAny))
+      return true;
+  }
+  return false;
+}
+
 }  // namespace
 
 void X509Certificate::Initialize() {
   const CSSM_X509_NAME* name;
   OSStatus status = SecCertificateGetSubject(cert_handle_, &name);
   if (!status) {
-    ParsePrincipal(name, &subject_);
+    subject_.Parse(name);
   }
   status = SecCertificateGetIssuer(cert_handle_, &name);
   if (!status) {
-    ParsePrincipal(name, &issuer_);
+    issuer_.Parse(name);
   }
 
   GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore,
@@ -375,9 +391,6 @@
                     &valid_expiry_);
 
   fingerprint_ = CalculateFingerprint(cert_handle_);
-
-  // Store the certificate in the cache in case we need it later.
-  X509Certificate::Cache::GetInstance()->Insert(this);
 }
 
 // static
@@ -419,29 +432,20 @@
   // Create an SSL SecPolicyRef, and configure it to perform hostname
   // validation. The hostname check does 99% of what we want, with the
   // exception of dotted IPv4 addreses, which we handle ourselves below.
-  SecPolicySearchRef ssl_policy_search_ref = NULL;
-  OSStatus status = SecPolicySearchCreate(CSSM_CERT_X_509v3,
-                                          &CSSMOID_APPLE_TP_SSL,
-                                          NULL,
-                                          &ssl_policy_search_ref);
-  if (status)
-    return NetErrorFromOSStatus(status);
-  scoped_cftyperef<SecPolicySearchRef>
-      scoped_ssl_policy_search_ref(ssl_policy_search_ref);
-  SecPolicyRef ssl_policy = NULL;
-  status = SecPolicySearchCopyNext(ssl_policy_search_ref, &ssl_policy);
+  CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = {
+    CSSM_APPLE_TP_SSL_OPTS_VERSION,
+    hostname.size(),
+    hostname.data(),
+    0
+  };
+  SecPolicyRef ssl_policy;
+  OSStatus status = CreatePolicy(&CSSMOID_APPLE_TP_SSL,
+                                 &tp_ssl_options,
+                                 sizeof(tp_ssl_options),
+                                 &ssl_policy);
   if (status)
     return NetErrorFromOSStatus(status);
   scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
-  CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = { CSSM_APPLE_TP_SSL_OPTS_VERSION };
-  tp_ssl_options.ServerName = hostname.data();
-  tp_ssl_options.ServerNameLen = hostname.size();
-  CSSM_DATA tp_ssl_options_data_value;
-  tp_ssl_options_data_value.Data = reinterpret_cast<uint8*>(&tp_ssl_options);
-  tp_ssl_options_data_value.Length = sizeof(tp_ssl_options);
-  status = SecPolicySetValue(ssl_policy, &tp_ssl_options_data_value);
-  if (status)
-    return NetErrorFromOSStatus(status);
 
   // Create and configure a SecTrustRef, which takes our certificate(s)
   // and our SSL SecPolicyRef. SecTrustCreateWithCertificates() takes an
@@ -457,6 +461,12 @@
   for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i)
     CFArrayAppendValue(cert_array, intermediate_ca_certs_[i]);
 
+  // From here on, only one thread can be active at a time. We have had a number
+  // of sporadic crashes in the SecTrustEvaluate call below, way down inside
+  // Apple's cert code, which we suspect are caused by a thread-safety issue.
+  // So as a speculative fix allow only one thread to use SecTrust on this cert.
+  AutoLock lock(verification_lock_);
+
   SecTrustRef trust_ref = NULL;
   status = SecTrustCreateWithCertificates(cert_array, ssl_policy, &trust_ref);
   if (status)
@@ -668,14 +678,22 @@
 }
 
 // static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+    OSCertHandle handle) {
+  if (!handle)
+    return NULL;
+  return reinterpret_cast<OSCertHandle>(const_cast<void*>(CFRetain(handle)));
+}
+
+// static
 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
   CFRelease(cert_handle);
 }
 
 // static
-X509Certificate::Fingerprint X509Certificate::CalculateFingerprint(
+SHA1Fingerprint X509Certificate::CalculateFingerprint(
     OSCertHandle cert) {
-  Fingerprint sha1;
+  SHA1Fingerprint sha1;
   memset(sha1.data, 0, sizeof(sha1.data));
 
   CSSM_DATA cert_data;
@@ -691,4 +709,194 @@
   return sha1;
 }
 
+bool X509Certificate::SupportsSSLClientAuth() const {
+  CSSMFields fields;
+  if (GetCertFields(cert_handle_, &fields) != noErr)
+    return false;
+
+  // Gather the extensions we care about. We do not support
+  // CSSMOID_NetscapeCertType on OS X.
+  const CE_ExtendedKeyUsage* ext_key_usage = NULL;
+  const CE_KeyUsage* key_usage = NULL;
+  for (unsigned f = 0; f < fields.num_of_fields; ++f) {
+    const CSSM_FIELD& field = fields.fields[f];
+    const CSSM_X509_EXTENSION* ext =
+        reinterpret_cast<const CSSM_X509_EXTENSION*>(field.FieldValue.Data);
+    if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_KeyUsage)) {
+      key_usage = reinterpret_cast<const CE_KeyUsage*>(ext->value.parsedValue);
+    } else if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_ExtendedKeyUsage)) {
+      ext_key_usage =
+          reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue);
+    }
+  }
+
+  // RFC5280 says to take the intersection of the two extensions.
+  //
+  // Our underlying crypto libraries don't expose
+  // ClientCertificateType, so for now we will not support fixed
+  // Diffie-Hellman mechanisms. For rsa_sign, we need the
+  // digitalSignature bit.
+  //
+  // In particular, if a key has the nonRepudiation bit and not the
+  // digitalSignature one, we will not offer it to the user.
+  if (key_usage && !((*key_usage) & CE_KU_DigitalSignature))
+    return false;
+  if (ext_key_usage && !ExtendedKeyUsageAllows(ext_key_usage,
+                                               &CSSMOID_ClientAuth))
+    return false;
+  return true;
+}
+
+bool X509Certificate::IsIssuedBy(
+    const std::vector<CertPrincipal>& valid_issuers) {
+  // Get the cert's issuer chain.
+  CFArrayRef cert_chain = NULL;
+  OSStatus result;
+  result = CopyCertChain(os_cert_handle(), &cert_chain);
+  if (result != noErr)
+    return false;
+  scoped_cftyperef<CFArrayRef> scoped_cert_chain(cert_chain);
+
+  // Check all the certs in the chain for a match.
+  int n = CFArrayGetCount(cert_chain);
+  for (int i = 0; i < n; ++i) {
+    SecCertificateRef cert_handle = reinterpret_cast<SecCertificateRef>(
+        const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
+    scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
+        cert_handle,
+        X509Certificate::SOURCE_LONE_CERT_IMPORT,
+        X509Certificate::OSCertHandles());
+    for (unsigned j = 0; j < valid_issuers.size(); j++) {
+      if (cert->subject().Matches(valid_issuers[j]))
+        return true;
+    }
+  }
+  return false;
+}
+
+// static
+OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) {
+  CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = {
+    CSSM_APPLE_TP_SSL_OPTS_VERSION,
+    0,
+    NULL,
+    CSSM_APPLE_TP_SSL_CLIENT
+  };
+  return CreatePolicy(&CSSMOID_APPLE_TP_SSL,
+                      &tp_ssl_options,
+                      sizeof(tp_ssl_options),
+                      out_policy);
+}
+
+// static
+bool X509Certificate::GetSSLClientCertificates (
+    const std::string& server_domain,
+    const std::vector<CertPrincipal>& valid_issuers,
+    std::vector<scoped_refptr<X509Certificate> >* certs) {
+  scoped_cftyperef<SecIdentityRef> preferred_identity;
+  if (!server_domain.empty()) {
+    // See if there's an identity preference for this domain:
+    scoped_cftyperef<CFStringRef> domain_str(
+        base::SysUTF8ToCFStringRef("https://" + server_domain));
+    SecIdentityRef identity = NULL;
+    if (SecIdentityCopyPreference(domain_str,
+                                  0,
+                                  NULL, // validIssuers argument is ignored :(
+                                  &identity) == noErr)
+      preferred_identity.reset(identity);
+  }
+
+  // Now enumerate the identities in the available keychains.
+  SecIdentitySearchRef search = nil;
+  OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search);
+  scoped_cftyperef<SecIdentitySearchRef> scoped_search(search);
+  while (!err) {
+    SecIdentityRef identity = NULL;
+    err = SecIdentitySearchCopyNext(search, &identity);
+    if (err)
+      break;
+    scoped_cftyperef<SecIdentityRef> scoped_identity(identity);
+
+    SecCertificateRef cert_handle;
+    err = SecIdentityCopyCertificate(identity, &cert_handle);
+    if (err != noErr)
+      continue;
+    scoped_cftyperef<SecCertificateRef> scoped_cert_handle(cert_handle);
+
+    scoped_refptr<X509Certificate> cert(
+        CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT,
+                         OSCertHandles()));
+    if (cert->HasExpired() || !cert->SupportsSSLClientAuth())
+      continue;
+
+    // Skip duplicates (a cert may be in multiple keychains).
+    const SHA1Fingerprint& fingerprint = cert->fingerprint();
+    unsigned i;
+    for (i = 0; i < certs->size(); ++i) {
+      if ((*certs)[i]->fingerprint().Equals(fingerprint))
+        break;
+    }
+    if (i < certs->size())
+      continue;
+
+    bool is_preferred = preferred_identity &&
+        CFEqual(preferred_identity, identity);
+
+    // Make sure the issuer matches valid_issuers, if given.
+    // But an explicit cert preference overrides this.
+    if (!is_preferred &&
+        valid_issuers.size() > 0 &&
+        !cert->IsIssuedBy(valid_issuers))
+      continue;
+
+    // The cert passes, so add it to the vector.
+    // If it's the preferred identity, add it at the start (so it'll be
+    // selected by default in the UI.)
+    if (is_preferred)
+      certs->insert(certs->begin(), cert);
+    else
+      certs->push_back(cert);
+  }
+
+  if (err != errSecItemNotFound) {
+    LOG(ERROR) << "SecIdentitySearch error " << err;
+    return false;
+  }
+  return true;
+}
+
+CFArrayRef X509Certificate::CreateClientCertificateChain() const {
+  // Initialize the result array with just the IdentityRef of the receiver:
+  OSStatus result;
+  SecIdentityRef identity;
+  result = SecIdentityCreateWithCertificate(NULL, cert_handle_, &identity);
+  if (result) {
+    LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result;
+    return NULL;
+  }
+  scoped_cftyperef<CFMutableArrayRef> chain(
+      CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
+  CFArrayAppendValue(chain, identity);
+
+  CFArrayRef cert_chain = NULL;
+  result = CopyCertChain(cert_handle_, &cert_chain);
+  if (result)
+    goto exit;
+
+  // Append the intermediate certs from SecTrust to the result array:
+  if (cert_chain) {
+    int chain_count = CFArrayGetCount(cert_chain);
+    if (chain_count > 1) {
+      CFArrayAppendArray(chain,
+                         cert_chain,
+                         CFRangeMake(1, chain_count - 1));
+    }
+    CFRelease(cert_chain);
+  }
+exit:
+  if (result)
+    LOG(ERROR) << "CreateIdentityCertificateChain error " << result;
+  return chain.release();
+}
+
 }  // namespace net
diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc
index 05ed979..8eb337f 100644
--- a/net/base/x509_certificate_nss.cc
+++ b/net/base/x509_certificate_nss.cc
@@ -5,6 +5,7 @@
 #include "net/base/x509_certificate.h"
 
 #include <cert.h>
+#include <nss.h>
 #include <pk11pub.h>
 #include <prerror.h>
 #include <prtime.h>
@@ -138,6 +139,7 @@
     case SEC_ERROR_CERT_NOT_VALID:
     // TODO(port): add an ERR_CERT_WRONG_USAGE error code.
     case SEC_ERROR_CERT_USAGES_INVALID:
+    case SEC_ERROR_POLICY_VALIDATION_FAILED:
       return ERR_CERT_INVALID;
     default:
       LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
@@ -169,6 +171,7 @@
     case SEC_ERROR_CERT_NOT_VALID:
     // TODO(port): add a CERT_STATUS_WRONG_USAGE error code.
     case SEC_ERROR_CERT_USAGES_INVALID:
+    case SEC_ERROR_POLICY_VALIDATION_FAILED:
       return CERT_STATUS_INVALID;
     default:
       return 0;
@@ -216,7 +219,7 @@
 typedef char* (*CERTGetNameFunc)(CERTName* name);
 
 void ParsePrincipal(CERTName* name,
-                    X509Certificate::Principal* principal) {
+                    CertPrincipal* principal) {
   // TODO(jcampan): add business_category and serial_number.
   // TODO(wtc): NSS has the CERT_GetOrgName, CERT_GetOrgUnitName, and
   // CERT_GetDomainComponentName functions, but they return only the most
@@ -319,6 +322,12 @@
   PORT_FreeArena(arena, PR_FALSE);
 }
 
+// Forward declarations.
+SECStatus RetryPKIXVerifyCertWithWorkarounds(
+    X509Certificate::OSCertHandle cert_handle, int num_policy_oids,
+    std::vector<CERTValInParam>* cvin, CERTValOutParam* cvout);
+SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle);
+
 // Call CERT_PKIXVerifyCert for the cert_handle.
 // Verification results are stored in an array of CERTValOutParam.
 // If policy_oids is not NULL and num_policy_oids is positive, policies
@@ -392,60 +401,160 @@
   revocation_flags.chainTests.cert_rev_method_independent_flags =
       revocation_method_independent_flags;
 
-  CERTValInParam cvin[4];
-  int cvin_index = 0;
+  std::vector<CERTValInParam> cvin;
+  cvin.reserve(5);
+  CERTValInParam in_param;
   // No need to set cert_pi_trustAnchors here.
-  cvin[cvin_index].type = cert_pi_revocationFlags;
-  cvin[cvin_index].value.pointer.revocation = &revocation_flags;
-  cvin_index++;
-  std::vector<SECOidTag> policies;
+  in_param.type = cert_pi_revocationFlags;
+  in_param.value.pointer.revocation = &revocation_flags;
+  cvin.push_back(in_param);
   if (policy_oids && num_policy_oids > 0) {
-    cvin[cvin_index].type = cert_pi_policyOID;
-    cvin[cvin_index].value.arraySize = num_policy_oids;
-    cvin[cvin_index].value.array.oids = policy_oids;
-    cvin_index++;
+    in_param.type = cert_pi_policyOID;
+    in_param.value.arraySize = num_policy_oids;
+    in_param.value.array.oids = policy_oids;
+    cvin.push_back(in_param);
   }
-  // Add cert_pi_useAIACertFetch last so we can easily remove it from the
-  // cvin array in the workaround below.
-  cvin[cvin_index].type = cert_pi_useAIACertFetch;
-  cvin[cvin_index].value.scalar.b = PR_TRUE;
-  cvin_index++;
-  cvin[cvin_index].type = cert_pi_end;
+  in_param.type = cert_pi_end;
+  cvin.push_back(in_param);
 
   SECStatus rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer,
-                                     cvin, cvout, NULL);
+                                     &cvin[0], cvout, NULL);
   if (rv != SECSuccess) {
-    // cert_pi_useAIACertFetch can't handle a CA issuers access location that
-    // is an LDAP URL with an empty host name (NSS bug 528741).  If cert fetch
-    // fails because of a network error, it also causes CERT_PKIXVerifyCert
-    // to report the network error rather than SEC_ERROR_UNKNOWN_ISSUER.  To
-    // work around these NSS bugs, we retry without cert_pi_useAIACertFetch.
-    int nss_error = PORT_GetError();
-    if (nss_error == SEC_ERROR_INVALID_ARGS || !IS_SEC_ERROR(nss_error)) {
-      cvin_index--;
-      DCHECK_EQ(cvin[cvin_index].type, cert_pi_useAIACertFetch);
-      cvin[cvin_index].type = cert_pi_end;
-      rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer,
-                               cvin, cvout, NULL);
-    }
+    rv = RetryPKIXVerifyCertWithWorkarounds(cert_handle, num_policy_oids,
+                                            &cvin, cvout);
   }
   return rv;
 }
 
-bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle,
-                       SECOidTag ev_policy_tag) {
+// PKIXVerifyCert calls this function to work around some bugs in
+// CERT_PKIXVerifyCert.  All the arguments of this function are either the
+// arguments or local variables of PKIXVerifyCert.
+SECStatus RetryPKIXVerifyCertWithWorkarounds(
+    X509Certificate::OSCertHandle cert_handle, int num_policy_oids,
+    std::vector<CERTValInParam>* cvin, CERTValOutParam* cvout) {
+  // We call this function when the first CERT_PKIXVerifyCert call in
+  // PKIXVerifyCert failed,  so we initialize |rv| to SECFailure.
+  SECStatus rv = SECFailure;
+  int nss_error = PORT_GetError();
+  CERTValInParam in_param;
+
+  // If we get SEC_ERROR_UNKNOWN_ISSUER, we may be missing an intermediate
+  // CA certificate, so we retry with cert_pi_useAIACertFetch.
+  // cert_pi_useAIACertFetch has several bugs in its error handling and
+  // error reporting (NSS bug 528743), so we don't use it by default.
+  // Note: When building a certificate chain, CERT_PKIXVerifyCert may
+  // incorrectly pick a CA certificate with the same subject name as the
+  // missing intermediate CA certificate, and  fail with the
+  // SEC_ERROR_BAD_SIGNATURE error (NSS bug 524013), so we also retry with
+  // cert_pi_useAIACertFetch on SEC_ERROR_BAD_SIGNATURE.
+  if (nss_error == SEC_ERROR_UNKNOWN_ISSUER ||
+      nss_error == SEC_ERROR_BAD_SIGNATURE) {
+    DCHECK_EQ(cvin->back().type,  cert_pi_end);
+    cvin->pop_back();
+    in_param.type = cert_pi_useAIACertFetch;
+    in_param.value.scalar.b = PR_TRUE;
+    cvin->push_back(in_param);
+    in_param.type = cert_pi_end;
+    cvin->push_back(in_param);
+    rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer,
+                             &(*cvin)[0], cvout, NULL);
+    if (rv == SECSuccess)
+      return rv;
+    int new_nss_error = PORT_GetError();
+    if (new_nss_error == SEC_ERROR_INVALID_ARGS ||
+        new_nss_error == SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE ||
+        new_nss_error == SEC_ERROR_BAD_HTTP_RESPONSE ||
+        new_nss_error == SEC_ERROR_BAD_LDAP_RESPONSE ||
+        !IS_SEC_ERROR(new_nss_error)) {
+      // Use the original error code because of cert_pi_useAIACertFetch's
+      // bad error reporting.
+      PORT_SetError(nss_error);
+      return rv;
+    }
+    nss_error = new_nss_error;
+  }
+
+  // If an intermediate CA certificate has requireExplicitPolicy in its
+  // policyConstraints extension, CERT_PKIXVerifyCert fails with
+  // SEC_ERROR_POLICY_VALIDATION_FAILED because we didn't specify any
+  // certificate policy (NSS bug 552775).  So we retry with the certificate
+  // policy found in the server certificate.
+  if (nss_error == SEC_ERROR_POLICY_VALIDATION_FAILED &&
+      num_policy_oids == 0) {
+    SECOidTag policy = GetFirstCertPolicy(cert_handle);
+    if (policy != SEC_OID_UNKNOWN) {
+      DCHECK_EQ(cvin->back().type,  cert_pi_end);
+      cvin->pop_back();
+      in_param.type = cert_pi_policyOID;
+      in_param.value.arraySize = 1;
+      in_param.value.array.oids = &policy;
+      cvin->push_back(in_param);
+      in_param.type = cert_pi_end;
+      cvin->push_back(in_param);
+      rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer,
+                               &(*cvin)[0], cvout, NULL);
+      if (rv != SECSuccess) {
+        // Use the original error code.
+        PORT_SetError(nss_error);
+      }
+    }
+  }
+
+  return rv;
+}
+
+// Decodes the certificatePolicies extension of the certificate.  Returns
+// NULL if the certificate doesn't have the extension or the extension can't
+// be decoded.  The returned value must be freed with a
+// CERT_DestroyCertificatePoliciesExtension call.
+CERTCertificatePolicies* DecodeCertPolicies(
+    X509Certificate::OSCertHandle cert_handle) {
   SECItem policy_ext;
   SECStatus rv = CERT_FindCertExtension(
       cert_handle, SEC_OID_X509_CERTIFICATE_POLICIES, &policy_ext);
-  if (rv != SECSuccess) {
-    LOG(ERROR) << "Cert has no policies extension.";
-    return false;
-  }
+  if (rv != SECSuccess)
+    return NULL;
   CERTCertificatePolicies* policies =
       CERT_DecodeCertificatePoliciesExtension(&policy_ext);
   SECITEM_FreeItem(&policy_ext, PR_FALSE);
+  return policies;
+}
+
+// Returns the OID tag for the first certificate policy in the certificate's
+// certificatePolicies extension.  Returns SEC_OID_UNKNOWN if the certificate
+// has no certificate policy.
+SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle) {
+  CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle);
+  if (!policies)
+    return SEC_OID_UNKNOWN;
+  ScopedCERTCertificatePolicies scoped_policies(policies);
+  CERTPolicyInfo* policy_info = policies->policyInfos[0];
+  if (!policy_info)
+    return SEC_OID_UNKNOWN;
+  if (policy_info->oid != SEC_OID_UNKNOWN)
+    return policy_info->oid;
+
+  // The certificate policy is unknown to NSS.  We need to create a dynamic
+  // OID tag for the policy.
+  SECOidData od;
+  od.oid.len = policy_info->policyID.len;
+  od.oid.data = policy_info->policyID.data;
+  od.offset = SEC_OID_UNKNOWN;
+  // NSS doesn't allow us to pass an empty description, so I use a hardcoded,
+  // default description here.  The description doesn't need to be unique for
+  // each OID.
+  od.desc = "a certificate policy";
+  od.mechanism = CKM_INVALID_MECHANISM;
+  od.supportedExtension = INVALID_CERT_EXTENSION;
+  return SECOID_AddEntry(&od);
+}
+
+bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle,
+                       SECOidTag ev_policy_tag) {
+  CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle);
   if (!policies) {
-    LOG(ERROR) << "Failed to decode certificate policy.";
+    LOG(ERROR) << "Cert has no policies extension or extension couldn't be "
+                  "decoded.";
     return false;
   }
   ScopedCERTCertificatePolicies scoped_policies(policies);
@@ -472,9 +581,6 @@
   ParseDate(&cert_handle_->validity.notAfter, &valid_expiry_);
 
   fingerprint_ = CalculateFingerprint(cert_handle_);
-
-  // Store the certificate in the cache in case we need it later.
-  X509Certificate::Cache::GetInstance()->Insert(this);
 }
 
 // static
@@ -600,7 +706,7 @@
       cvout[cvout_trust_anchor_index].value.pointer.cert;
   if (root_ca == NULL)
     return false;
-  X509Certificate::Fingerprint fingerprint =
+  SHA1Fingerprint fingerprint =
       X509Certificate::CalculateFingerprint(root_ca);
   SECOidTag ev_policy_tag = SEC_OID_UNKNOWN;
   if (!metadata->GetPolicyOID(fingerprint, &ev_policy_tag))
@@ -617,11 +723,25 @@
     const char* data, int length) {
   base::EnsureNSSInit();
 
-  SECItem der_cert;
-  der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data));
-  der_cert.len = length;
-  return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert,
-                                 NULL, PR_FALSE, PR_TRUE);
+  if (!NSS_IsInitialized())
+    return NULL;
+
+  // Make a copy of |data| since CERT_DecodeCertPackage might modify it.
+  char* data_copy = new char[length];
+  memcpy(data_copy, data, length);
+
+  // Parse into a certificate structure.
+  CERTCertificate* cert = CERT_DecodeCertFromPackage(data_copy, length);
+  delete [] data_copy;
+  if (!cert)
+    LOG(ERROR) << "Couldn't parse a certificate from " << length << " bytes";
+  return cert;
+}
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+    OSCertHandle cert_handle) {
+  return CERT_DupCertificate(cert_handle);
 }
 
 // static
@@ -630,9 +750,9 @@
 }
 
 // static
-X509Certificate::Fingerprint X509Certificate::CalculateFingerprint(
+SHA1Fingerprint X509Certificate::CalculateFingerprint(
     OSCertHandle cert) {
-  Fingerprint sha1;
+  SHA1Fingerprint sha1;
   memset(sha1.data, 0, sizeof(sha1.data));
 
   DCHECK(NULL != cert->derCert.data);
diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc
index 7904cf0..63eec15 100644
--- a/net/base/x509_certificate_unittest.cc
+++ b/net/base/x509_certificate_unittest.cc
@@ -2,8 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
 #include "base/pickle.h"
 #include "net/base/cert_status_flags.h"
+#include "net/base/cert_test_util.h"
 #include "net/base/cert_verify_result.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_certificate_data.h"
@@ -23,6 +27,8 @@
 
 using base::Time;
 
+namespace net {
+
 namespace {
 
 // Certificates for test data. They're obtained with:
@@ -70,9 +76,31 @@
   0x25, 0x66, 0xf2, 0xec, 0x8b, 0x0f, 0xbf, 0xd8
 };
 
-}  // namespace
+// Returns a FilePath object representing the src/net/data/ssl/certificates
+// directory in the source tree.
+FilePath GetTestCertsDirectory() {
+  FilePath certs_dir;
+  PathService::Get(base::DIR_SOURCE_ROOT, &certs_dir);
+  certs_dir = certs_dir.AppendASCII("net");
+  certs_dir = certs_dir.AppendASCII("data");
+  certs_dir = certs_dir.AppendASCII("ssl");
+  certs_dir = certs_dir.AppendASCII("certificates");
+  return certs_dir;
+}
 
-namespace net {
+// Imports a certificate file in the src/net/data/ssl/certificates directory.
+// certs_dir represents the test certificates directory.  cert_file is the
+// name of the certificate file.
+X509Certificate* ImportCertFromFile(const FilePath& certs_dir,
+                                    const std::string& cert_file) {
+  FilePath cert_path = certs_dir.AppendASCII(cert_file);
+  std::string cert_data;
+  if (!file_util::ReadFileToString(cert_path, &cert_data))
+    return NULL;
+  return X509Certificate::CreateFromBytes(cert_data.data(), cert_data.size());
+}
+
+}  // namespace
 
 TEST(X509CertificateTest, GoogleCertParsing) {
   scoped_refptr<X509Certificate> google_cert = X509Certificate::CreateFromBytes(
@@ -80,7 +108,7 @@
 
   ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert);
 
-  const X509Certificate::Principal& subject = google_cert->subject();
+  const CertPrincipal& subject = google_cert->subject();
   EXPECT_EQ("www.google.com", subject.common_name);
   EXPECT_EQ("Mountain View", subject.locality_name);
   EXPECT_EQ("California", subject.state_or_province_name);
@@ -91,7 +119,7 @@
   EXPECT_EQ(0U, subject.organization_unit_names.size());
   EXPECT_EQ(0U, subject.domain_components.size());
 
-  const X509Certificate::Principal& issuer = google_cert->issuer();
+  const CertPrincipal& issuer = google_cert->issuer();
   EXPECT_EQ("Thawte SGC CA", issuer.common_name);
   EXPECT_EQ("", issuer.locality_name);
   EXPECT_EQ("", issuer.state_or_province_name);
@@ -109,7 +137,7 @@
   const Time& valid_expiry = google_cert->valid_expiry();
   EXPECT_EQ(1269728407, valid_expiry.ToDoubleT());  // Mar 27 22:20:07 2010 GMT
 
-  const X509Certificate::Fingerprint& fingerprint = google_cert->fingerprint();
+  const SHA1Fingerprint& fingerprint = google_cert->fingerprint();
   for (size_t i = 0; i < 20; ++i)
     EXPECT_EQ(google_fingerprint[i], fingerprint.data[i]);
 
@@ -134,7 +162,7 @@
 
   ASSERT_NE(static_cast<X509Certificate*>(NULL), webkit_cert);
 
-  const X509Certificate::Principal& subject = webkit_cert->subject();
+  const CertPrincipal& subject = webkit_cert->subject();
   EXPECT_EQ("Cupertino", subject.locality_name);
   EXPECT_EQ("California", subject.state_or_province_name);
   EXPECT_EQ("US", subject.country_name);
@@ -145,7 +173,7 @@
   EXPECT_EQ("Mac OS Forge", subject.organization_unit_names[0]);
   EXPECT_EQ(0U, subject.domain_components.size());
 
-  const X509Certificate::Principal& issuer = webkit_cert->issuer();
+  const CertPrincipal& issuer = webkit_cert->issuer();
   EXPECT_EQ("Go Daddy Secure Certification Authority", issuer.common_name);
   EXPECT_EQ("Scottsdale", issuer.locality_name);
   EXPECT_EQ("Arizona", issuer.state_or_province_name);
@@ -165,7 +193,7 @@
   const Time& valid_expiry = webkit_cert->valid_expiry();
   EXPECT_EQ(1300491319, valid_expiry.ToDoubleT());  // Mar 18 23:35:19 2011 GMT
 
-  const X509Certificate::Fingerprint& fingerprint = webkit_cert->fingerprint();
+  const SHA1Fingerprint& fingerprint = webkit_cert->fingerprint();
   for (size_t i = 0; i < 20; ++i)
     EXPECT_EQ(webkit_fingerprint[i], fingerprint.data[i]);
 
@@ -190,7 +218,7 @@
 
   ASSERT_NE(static_cast<X509Certificate*>(NULL), thawte_cert);
 
-  const X509Certificate::Principal& subject = thawte_cert->subject();
+  const CertPrincipal& subject = thawte_cert->subject();
   EXPECT_EQ("www.thawte.com", subject.common_name);
   EXPECT_EQ("Mountain View", subject.locality_name);
   EXPECT_EQ("California", subject.state_or_province_name);
@@ -201,7 +229,7 @@
   EXPECT_EQ(0U, subject.organization_unit_names.size());
   EXPECT_EQ(0U, subject.domain_components.size());
 
-  const X509Certificate::Principal& issuer = thawte_cert->issuer();
+  const CertPrincipal& issuer = thawte_cert->issuer();
   EXPECT_EQ("thawte Extended Validation SSL CA", issuer.common_name);
   EXPECT_EQ("", issuer.locality_name);
   EXPECT_EQ("", issuer.state_or_province_name);
@@ -221,7 +249,7 @@
   const Time& valid_expiry = thawte_cert->valid_expiry();
   EXPECT_EQ(1263772799, valid_expiry.ToDoubleT());  // Jan 17 23:59:59 2010 GMT
 
-  const X509Certificate::Fingerprint& fingerprint = thawte_cert->fingerprint();
+  const SHA1Fingerprint& fingerprint = thawte_cert->fingerprint();
   for (size_t i = 0; i < 20; ++i)
     EXPECT_EQ(thawte_fingerprint[i], fingerprint.data[i]);
 
@@ -253,7 +281,7 @@
 
   ASSERT_NE(static_cast<X509Certificate*>(NULL), paypal_null_cert);
 
-  const X509Certificate::Fingerprint& fingerprint =
+  const SHA1Fingerprint& fingerprint =
       paypal_null_cert->fingerprint();
   for (size_t i = 0; i < 20; ++i)
     EXPECT_EQ(paypal_null_fingerprint[i], fingerprint.data[i]);
@@ -266,22 +294,22 @@
   // Either the system crypto library should correctly report a certificate
   // name mismatch, or our certificate blacklist should cause us to report an
   // invalid certificate.
-#if defined(OS_LINUX) || defined(OS_WIN)
+#if !defined(OS_MACOSX)
   EXPECT_NE(0, verify_result.cert_status &
             (CERT_STATUS_COMMON_NAME_INVALID | CERT_STATUS_INVALID));
 #endif
 }
 
+// A certificate whose AIA extension contains an LDAP URL without a host name.
 // This certificate will expire on 2011-09-08.
 TEST(X509CertificateTest, UnoSoftCertParsing) {
+  FilePath certs_dir = GetTestCertsDirectory();
   scoped_refptr<X509Certificate> unosoft_hu_cert =
-      X509Certificate::CreateFromBytes(
-          reinterpret_cast<const char*>(unosoft_hu_der),
-          sizeof(unosoft_hu_der));
+      ImportCertFromFile(certs_dir, "unosoft_hu_cert.der");
 
   ASSERT_NE(static_cast<X509Certificate*>(NULL), unosoft_hu_cert);
 
-  const X509Certificate::Fingerprint& fingerprint =
+  const SHA1Fingerprint& fingerprint =
       unosoft_hu_cert->fingerprint();
   for (size_t i = 0; i < 20; ++i)
     EXPECT_EQ(unosoft_hu_fingerprint[i], fingerprint.data[i]);
@@ -294,6 +322,39 @@
   EXPECT_NE(0, verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID);
 }
 
+#if defined(USE_NSS)
+// A regression test for http://crbug.com/31497.
+// This certificate will expire on 2012-04-08.
+// TODO(wtc): we can't run this test on Mac because MacTrustedCertificates
+// can hold only one additional trusted root certificate for unit tests.
+// TODO(wtc): we can't run this test on Windows because LoadTemporaryRootCert
+// isn't implemented (http//crbug.com/8470).
+TEST(X509CertificateTest, IntermediateCARequireExplicitPolicy) {
+  FilePath certs_dir = GetTestCertsDirectory();
+
+  scoped_refptr<X509Certificate> server_cert =
+      ImportCertFromFile(certs_dir, "www_us_army_mil_cert.der");
+  ASSERT_NE(static_cast<X509Certificate*>(NULL), server_cert);
+
+  // The intermediate CA certificate's policyConstraints extension has a
+  // requireExplicitPolicy field with SkipCerts=0.
+  scoped_refptr<X509Certificate> intermediate_cert =
+      ImportCertFromFile(certs_dir, "dod_ca_17_cert.der");
+  ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert);
+
+  FilePath root_cert_path = certs_dir.AppendASCII("dod_root_ca_2_cert.der");
+  scoped_refptr<X509Certificate> root_cert =
+      LoadTemporaryRootCert(root_cert_path);
+  ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert);
+
+  int flags = 0;
+  CertVerifyResult verify_result;
+  int error = server_cert->Verify("www.us.army.mil", flags, &verify_result);
+  EXPECT_EQ(OK, error);
+  EXPECT_EQ(0, verify_result.cert_status);
+}
+#endif
+
 // Tests X509Certificate::Cache via X509Certificate::CreateFromHandle.  We
 // call X509Certificate::CreateFromHandle several times and observe whether
 // it returns a cached or new X509Certificate object.
@@ -309,14 +370,18 @@
   google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes(
       reinterpret_cast<const char*>(google_der), sizeof(google_der));
   scoped_refptr<X509Certificate> cert1 = X509Certificate::CreateFromHandle(
-      google_cert_handle, X509Certificate::SOURCE_LONE_CERT_IMPORT);
+      google_cert_handle, X509Certificate::SOURCE_LONE_CERT_IMPORT,
+      X509Certificate::OSCertHandles());
+  X509Certificate::FreeOSCertHandle(google_cert_handle);
 
   // Add a certificate from the same source (SOURCE_LONE_CERT_IMPORT).  This
   // should return the cached certificate (cert1).
   google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes(
       reinterpret_cast<const char*>(google_der), sizeof(google_der));
   scoped_refptr<X509Certificate> cert2 = X509Certificate::CreateFromHandle(
-      google_cert_handle, X509Certificate::SOURCE_LONE_CERT_IMPORT);
+      google_cert_handle, X509Certificate::SOURCE_LONE_CERT_IMPORT,
+      X509Certificate::OSCertHandles());
+  X509Certificate::FreeOSCertHandle(google_cert_handle);
 
   EXPECT_EQ(cert1, cert2);
 
@@ -325,7 +390,9 @@
   google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes(
       reinterpret_cast<const char*>(google_der), sizeof(google_der));
   scoped_refptr<X509Certificate> cert3 = X509Certificate::CreateFromHandle(
-      google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK);
+      google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK,
+      X509Certificate::OSCertHandles());
+  X509Certificate::FreeOSCertHandle(google_cert_handle);
 
   EXPECT_NE(cert1, cert3);
 
@@ -334,14 +401,18 @@
   google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes(
       reinterpret_cast<const char*>(google_der), sizeof(google_der));
   scoped_refptr<X509Certificate> cert4 = X509Certificate::CreateFromHandle(
-      google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK);
+      google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK,
+      X509Certificate::OSCertHandles());
+  X509Certificate::FreeOSCertHandle(google_cert_handle);
 
   EXPECT_EQ(cert3, cert4);
 
   google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes(
       reinterpret_cast<const char*>(google_der), sizeof(google_der));
   scoped_refptr<X509Certificate> cert5 = X509Certificate::CreateFromHandle(
-      google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK);
+      google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK,
+      X509Certificate::OSCertHandles());
+  X509Certificate::FreeOSCertHandle(google_cert_handle);
 
   EXPECT_EQ(cert3, cert5);
 }
@@ -367,33 +438,94 @@
   scoped_refptr<X509Certificate> webkit_cert = X509Certificate::CreateFromBytes(
       reinterpret_cast<const char*>(webkit_der), sizeof(webkit_der));
 
-  X509Certificate::Policy policy;
+  CertPolicy policy;
 
-  EXPECT_EQ(policy.Check(google_cert.get()), X509Certificate::Policy::UNKNOWN);
-  EXPECT_EQ(policy.Check(webkit_cert.get()), X509Certificate::Policy::UNKNOWN);
+  EXPECT_EQ(policy.Check(google_cert.get()), CertPolicy::UNKNOWN);
+  EXPECT_EQ(policy.Check(webkit_cert.get()), CertPolicy::UNKNOWN);
   EXPECT_FALSE(policy.HasAllowedCert());
   EXPECT_FALSE(policy.HasDeniedCert());
 
   policy.Allow(google_cert.get());
 
-  EXPECT_EQ(policy.Check(google_cert.get()), X509Certificate::Policy::ALLOWED);
-  EXPECT_EQ(policy.Check(webkit_cert.get()), X509Certificate::Policy::UNKNOWN);
+  EXPECT_EQ(policy.Check(google_cert.get()), CertPolicy::ALLOWED);
+  EXPECT_EQ(policy.Check(webkit_cert.get()), CertPolicy::UNKNOWN);
   EXPECT_TRUE(policy.HasAllowedCert());
   EXPECT_FALSE(policy.HasDeniedCert());
 
   policy.Deny(google_cert.get());
 
-  EXPECT_EQ(policy.Check(google_cert.get()), X509Certificate::Policy::DENIED);
-  EXPECT_EQ(policy.Check(webkit_cert.get()), X509Certificate::Policy::UNKNOWN);
+  EXPECT_EQ(policy.Check(google_cert.get()), CertPolicy::DENIED);
+  EXPECT_EQ(policy.Check(webkit_cert.get()), CertPolicy::UNKNOWN);
   EXPECT_FALSE(policy.HasAllowedCert());
   EXPECT_TRUE(policy.HasDeniedCert());
 
   policy.Allow(webkit_cert.get());
 
-  EXPECT_EQ(policy.Check(google_cert.get()), X509Certificate::Policy::DENIED);
-  EXPECT_EQ(policy.Check(webkit_cert.get()), X509Certificate::Policy::ALLOWED);
+  EXPECT_EQ(policy.Check(google_cert.get()), CertPolicy::DENIED);
+  EXPECT_EQ(policy.Check(webkit_cert.get()), CertPolicy::ALLOWED);
   EXPECT_TRUE(policy.HasAllowedCert());
   EXPECT_TRUE(policy.HasDeniedCert());
 }
 
+#if defined(OS_MACOSX) || defined(OS_WIN)
+TEST(X509CertificateTest, IntermediateCertificates) {
+  scoped_refptr<X509Certificate> webkit_cert =
+      X509Certificate::CreateFromBytes(
+          reinterpret_cast<const char*>(webkit_der), sizeof(webkit_der));
+
+  scoped_refptr<X509Certificate> thawte_cert =
+      X509Certificate::CreateFromBytes(
+          reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der));
+
+  scoped_refptr<X509Certificate> paypal_cert =
+      X509Certificate::CreateFromBytes(
+          reinterpret_cast<const char*>(paypal_null_der),
+          sizeof(paypal_null_der));
+
+  X509Certificate::OSCertHandle google_handle;
+  // Create object with no intermediates:
+  google_handle = X509Certificate::CreateOSCertHandleFromBytes(
+      reinterpret_cast<const char*>(google_der), sizeof(google_der));
+  X509Certificate::OSCertHandles intermediates1;
+  scoped_refptr<X509Certificate> cert1;
+  cert1 = X509Certificate::CreateFromHandle(
+      google_handle, X509Certificate::SOURCE_FROM_NETWORK, intermediates1);
+  EXPECT_TRUE(cert1->HasIntermediateCertificates(intermediates1));
+  EXPECT_FALSE(cert1->HasIntermediateCertificate(
+      webkit_cert->os_cert_handle()));
+
+  // Create object with 2 intermediates:
+  X509Certificate::OSCertHandles intermediates2;
+  intermediates2.push_back(webkit_cert->os_cert_handle());
+  intermediates2.push_back(thawte_cert->os_cert_handle());
+  scoped_refptr<X509Certificate> cert2;
+  cert2 = X509Certificate::CreateFromHandle(
+      google_handle, X509Certificate::SOURCE_FROM_NETWORK, intermediates2);
+
+  // The cache should have stored cert2 'cause it has more intermediates:
+  EXPECT_NE(cert1, cert2);
+
+  // Verify it has all the intermediates:
+  EXPECT_TRUE(cert2->HasIntermediateCertificate(
+      webkit_cert->os_cert_handle()));
+  EXPECT_TRUE(cert2->HasIntermediateCertificate(
+      thawte_cert->os_cert_handle()));
+  EXPECT_FALSE(cert2->HasIntermediateCertificate(
+      paypal_cert->os_cert_handle()));
+
+  // Create object with 1 intermediate:
+  X509Certificate::OSCertHandles intermediates3;
+  intermediates2.push_back(thawte_cert->os_cert_handle());
+  scoped_refptr<X509Certificate> cert3;
+  cert3 = X509Certificate::CreateFromHandle(
+      google_handle, X509Certificate::SOURCE_FROM_NETWORK, intermediates3);
+
+  // The cache should have returned cert2 'cause it has more intermediates:
+  EXPECT_EQ(cert3, cert2);
+
+  // Cleanup
+  X509Certificate::FreeOSCertHandle(google_handle);
+}
+#endif
+
 }  // namespace net
diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc
index cc6fda2..901c0a6 100644
--- a/net/base/x509_certificate_win.cc
+++ b/net/base/x509_certificate_win.cc
@@ -8,6 +8,7 @@
 #include "base/pickle.h"
 #include "base/string_tokenizer.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "net/base/cert_status_flags.h"
 #include "net/base/cert_verify_result.h"
 #include "net/base/ev_root_ca_metadata.h"
@@ -50,6 +51,9 @@
     case SEC_E_CERT_UNKNOWN:
     case CERT_E_ROLE:
       return ERR_CERT_INVALID;
+    case CERT_E_WRONG_USAGE:
+      // TODO(wtc): Should we add ERR_CERT_WRONG_USAGE?
+      return ERR_CERT_INVALID;
     // We received an unexpected_message or illegal_parameter alert message
     // from the server.
     case SEC_E_ILLEGAL_MESSAGE:
@@ -98,8 +102,8 @@
   const DWORD kWrongUsageErrors = CERT_TRUST_IS_NOT_VALID_FOR_USAGE |
                                   CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE;
   if (error_status & kWrongUsageErrors) {
-    // TODO(wtc): Handle these errors.
-    // cert_status = |= CERT_STATUS_WRONG_USAGE;
+    // TODO(wtc): Should we add CERT_STATUS_WRONG_USAGE?
+    cert_status |= CERT_STATUS_INVALID;
   }
 
   // The rest of the errors.
@@ -373,7 +377,7 @@
 // Helper function to parse a principal from a WinInet description of that
 // principal.
 void ParsePrincipal(const std::string& description,
-                    X509Certificate::Principal* principal) {
+                    CertPrincipal* principal) {
   // The description of the principal is a string with each LDAP value on
   // a separate line.
   const std::string kDelimiters("\r\n");
@@ -460,9 +464,6 @@
   valid_expiry_ = Time::FromFileTime(cert_handle_->pCertInfo->NotAfter);
 
   fingerprint_ = CalculateFingerprint(cert_handle_);
-
-  // Store the certificate in the cache in case we need it later.
-  X509Certificate::Cache::GetInstance()->Insert(this);
 }
 
 // static
@@ -481,7 +482,11 @@
       NULL, reinterpret_cast<const void **>(&cert_handle)))
     return NULL;
 
-  return CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT);
+  X509Certificate* cert = CreateFromHandle(cert_handle,
+                                           SOURCE_LONE_CERT_IMPORT,
+                                           OSCertHandles());
+  FreeOSCertHandle(cert_handle);
+  return cert;
 }
 
 void X509Certificate::Persist(Pickle* pickle) {
@@ -534,11 +539,19 @@
   CERT_CHAIN_PARA chain_para;
   memset(&chain_para, 0, sizeof(chain_para));
   chain_para.cbSize = sizeof(chain_para);
-  // TODO(wtc): consider requesting the usage szOID_PKIX_KP_SERVER_AUTH
-  // or szOID_SERVER_GATED_CRYPTO or szOID_SGC_NETSCAPE
-  chain_para.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
-  chain_para.RequestedUsage.Usage.cUsageIdentifier = 0;
-  chain_para.RequestedUsage.Usage.rgpszUsageIdentifier = NULL;  // LPSTR*
+  // ExtendedKeyUsage.
+  // We still need to request szOID_SERVER_GATED_CRYPTO and szOID_SGC_NETSCAPE
+  // today because some certificate chains need them.  IE also requests these
+  // two usages.
+  static const LPSTR usage[] = {
+    szOID_PKIX_KP_SERVER_AUTH,
+    szOID_SERVER_GATED_CRYPTO,
+    szOID_SGC_NETSCAPE
+  };
+  chain_para.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+  chain_para.RequestedUsage.Usage.cUsageIdentifier = arraysize(usage);
+  chain_para.RequestedUsage.Usage.rgpszUsageIdentifier =
+      const_cast<LPSTR*>(usage);
   // We can set CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS to get more chains.
   DWORD chain_flags = CERT_CHAIN_CACHE_END_CERT;
   if (flags & VERIFY_REV_CHECKING_ENABLED) {
@@ -550,6 +563,9 @@
     flags &= ~VERIFY_EV_CERT;
   }
   PCCERT_CHAIN_CONTEXT chain_context;
+  // IE passes a non-NULL pTime argument that specifies the current system
+  // time.  IE passes CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT as the
+  // chain_flags argument.
   if (!CertGetCertificateChain(
            NULL,  // default chain engine, HCCE_CURRENT_USER
            cert_handle_,
@@ -706,7 +722,7 @@
 
   // Look up the EV policy OID of the root CA.
   PCCERT_CONTEXT root_cert = element[num_elements - 1]->pCertContext;
-  Fingerprint fingerprint = CalculateFingerprint(root_cert);
+  SHA1Fingerprint fingerprint = CalculateFingerprint(root_cert);
   const char* ev_policy_oid = NULL;
   if (!metadata->GetPolicyOID(fingerprint, &ev_policy_oid))
     return false;
@@ -737,19 +753,26 @@
   return cert_handle;
 }
 
+
+// static
+X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
+    OSCertHandle cert_handle) {
+  return CertDuplicateCertificateContext(cert_handle);
+}
+
 // static
 void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
   CertFreeCertificateContext(cert_handle);
 }
 
 // static
-X509Certificate::Fingerprint X509Certificate::CalculateFingerprint(
+SHA1Fingerprint X509Certificate::CalculateFingerprint(
     OSCertHandle cert) {
   DCHECK(NULL != cert->pbCertEncoded);
   DCHECK(0 != cert->cbCertEncoded);
 
   BOOL rv;
-  Fingerprint sha1;
+  SHA1Fingerprint sha1;
   DWORD sha1_size = sizeof(sha1.data);
   rv = CryptHashCertificate(NULL, CALG_SHA1, 0, cert->pbCertEncoded,
                             cert->cbCertEncoded, sha1.data, &sha1_size);
diff --git a/net/crash_cache.target.mk b/net/crash_cache.target.mk
new file mode 100644
index 0000000..d339afc
--- /dev/null
+++ b/net/crash_cache.target.mk
@@ -0,0 +1,183 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := crash_cache
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I.
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I.
+
+OBJS := $(obj).target/$(TARGET)/net/tools/crash_cache/crash_cache.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/net/libnet.a $(obj).target/net/libnet_test_support.a $(obj).target/base/libbase.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a $(obj).target/testing/libgtest.a
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-Wl,--gc-sections
+
+LIBS := -lrt \
+	-ldl \
+	-lgtk-x11-2.0 \
+	-lgdk-x11-2.0 \
+	-latk-1.0 \
+	-lgio-2.0 \
+	-lpangoft2-1.0 \
+	-lgdk_pixbuf-2.0 \
+	-lm \
+	-lpangocairo-1.0 \
+	-lcairo \
+	-lpango-1.0 \
+	-lfreetype \
+	-lfontconfig \
+	-lgobject-2.0 \
+	-lgmodule-2.0 \
+	-lgthread-2.0 \
+	-lglib-2.0 \
+	-lnss3 \
+	-lnssutil3 \
+	-lsmime3 \
+	-lplds4 \
+	-lplc4 \
+	-lnspr4 \
+	-lpthread \
+	-lz \
+	-lgconf-2
+
+$(builddir)/crash_cache: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(builddir)/crash_cache: LIBS := $(LIBS)
+$(builddir)/crash_cache: TOOLSET := $(TOOLSET)
+$(builddir)/crash_cache: $(OBJS) $(obj).target/net/libnet.a $(obj).target/net/libnet_test_support.a $(obj).target/base/libbase.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a $(obj).target/testing/libgtest.a FORCE_DO_CMD
+	$(call do_cmd,link)
+
+all_deps += $(builddir)/crash_cache
+# Add target alias
+.PHONY: crash_cache
+crash_cache: $(builddir)/crash_cache
+
+# Add executable to "all" target.
+.PHONY: all
+all: $(builddir)/crash_cache
+
diff --git a/net/data/ftp/dir-listing-ls-1-utf8.expected b/net/data/ftp/dir-listing-ls-1-utf8.expected
index bdb02ba..8e4508f 100644
--- a/net/data/ftp/dir-listing-ls-1-utf8.expected
+++ b/net/data/ftp/dir-listing-ls-1-utf8.expected
@@ -1,7 +1,7 @@
 d
 .
 -1
-current
+1994
 5
 15
 18
@@ -10,7 +10,7 @@
 d
 ..
 -1
-current
+1994
 5
 15
 18
diff --git a/net/data/ftp/dir-listing-ls-1.expected b/net/data/ftp/dir-listing-ls-1.expected
index 6e78dc2..4037590 100644
--- a/net/data/ftp/dir-listing-ls-1.expected
+++ b/net/data/ftp/dir-listing-ls-1.expected
@@ -1,7 +1,7 @@
 d
 .
 -1
-current
+1994
 5
 15
 18
@@ -10,7 +10,7 @@
 d
 ..
 -1
-current
+1994
 5
 15
 18
diff --git a/net/data/ftp/dir-listing-ls-10.expected b/net/data/ftp/dir-listing-ls-10.expected
index bbc4cd3..6e63cd0 100644
--- a/net/data/ftp/dir-listing-ls-10.expected
+++ b/net/data/ftp/dir-listing-ls-10.expected
@@ -37,7 +37,7 @@
 d
 lost+found
 -1
-current
+1994
 8
 14
 13
diff --git a/net/data/ftp/dir-listing-ls-12.expected b/net/data/ftp/dir-listing-ls-12.expected
index 761f7f4..c2f3232 100644
--- a/net/data/ftp/dir-listing-ls-12.expected
+++ b/net/data/ftp/dir-listing-ls-12.expected
@@ -37,7 +37,7 @@
 d
 pub
 -1
-current
+1994
 6
 30
 9
diff --git a/net/data/ftp/dir-listing-ls-13.expected b/net/data/ftp/dir-listing-ls-13.expected
index 7bbf9ad..049f3bf 100644
--- a/net/data/ftp/dir-listing-ls-13.expected
+++ b/net/data/ftp/dir-listing-ls-13.expected
@@ -10,7 +10,7 @@
 d
 kernels
 -1
-current
+1993
 11
 17
 17
diff --git a/net/data/ftp/dir-listing-ls-15.expected b/net/data/ftp/dir-listing-ls-15.expected
index fd064ad..c521d00 100644
--- a/net/data/ftp/dir-listing-ls-15.expected
+++ b/net/data/ftp/dir-listing-ls-15.expected
@@ -10,7 +10,7 @@
 d
 incoming
 -1
-current
+1993
 12
 8
 15
@@ -28,7 +28,7 @@
 d
 pub
 -1
-current
+1994
 9
 25
 9
diff --git a/net/data/ftp/dir-listing-ls-16.expected b/net/data/ftp/dir-listing-ls-16.expected
index 6a8fa74..90e39e6 100644
--- a/net/data/ftp/dir-listing-ls-16.expected
+++ b/net/data/ftp/dir-listing-ls-16.expected
@@ -37,7 +37,7 @@
 d
 test
 -1
-current
+1994
 11
 2
 15
@@ -46,7 +46,7 @@
 d
 tmp
 -1
-current
+1993
 11
 25
 10
diff --git a/net/data/ftp/dir-listing-ls-17 b/net/data/ftp/dir-listing-ls-17
new file mode 100644
index 0000000..c07fbf3
--- /dev/null
+++ b/net/data/ftp/dir-listing-ls-17
@@ -0,0 +1 @@
+ftpd-BSD: .: Permission denied
diff --git a/net/data/ftp/dir-listing-ls-17.expected b/net/data/ftp/dir-listing-ls-17.expected
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/net/data/ftp/dir-listing-ls-17.expected
diff --git a/net/data/ftp/dir-listing-ls-7.expected b/net/data/ftp/dir-listing-ls-7.expected
index 8f4cfe0..90c1351 100644
--- a/net/data/ftp/dir-listing-ls-7.expected
+++ b/net/data/ftp/dir-listing-ls-7.expected
@@ -10,7 +10,7 @@
 d
 OCU
 -1
-current
+1994
 10
 19
 13
diff --git a/net/data/ftp/dir-listing-ls-9.expected b/net/data/ftp/dir-listing-ls-9.expected
index 08bdb20..afc9791 100644
--- a/net/data/ftp/dir-listing-ls-9.expected
+++ b/net/data/ftp/dir-listing-ls-9.expected
@@ -1,7 +1,7 @@
 -
 Akademia Teatralna spot.mpg
 174680068 
-current
+1994
 6
 4
 23
@@ -19,7 +19,7 @@
 -
 Zdjecia.zip
 23197684 
-current
+1994
 6
 9
 13
diff --git a/net/data/ftp/dir-listing-netware-2.expected b/net/data/ftp/dir-listing-netware-2.expected
index ea7423e..3c78ff0 100644
--- a/net/data/ftp/dir-listing-netware-2.expected
+++ b/net/data/ftp/dir-listing-netware-2.expected
@@ -10,7 +10,7 @@
 d
 Driver
 -1
-current
+1994
 11
 13
 7
@@ -19,7 +19,7 @@
 d
 temp
 -1
-current
+1993
 11
 16
 15
diff --git a/net/data/proxy_resolver_v8_unittest/binding_from_global.js b/net/data/proxy_resolver_v8_unittest/binding_from_global.js
new file mode 100644
index 0000000..91bbcf2
--- /dev/null
+++ b/net/data/proxy_resolver_v8_unittest/binding_from_global.js
@@ -0,0 +1,8 @@
+// Calls a bindings outside of FindProxyForURL(). This causes the code to
+// get exercised during initialization.
+
+var x = myIpAddress();
+
+function FindProxyForURL(url, host) {
+  return "PROXY " + x + ":80";
+}
diff --git a/net/data/proxy_resolver_v8_unittest/bindings.js b/net/data/proxy_resolver_v8_unittest/bindings.js
index 3026569..7cf9f26 100644
--- a/net/data/proxy_resolver_v8_unittest/bindings.js
+++ b/net/data/proxy_resolver_v8_unittest/bindings.js
@@ -10,16 +10,25 @@
   throw "exception from calling toString()";
 }
 
+function expectEquals(expectation, actual) {
+  if (!(expectation === actual)) {
+    throw "FAIL: expected: " + expectation + ", actual: " + actual;
+  }
+}
+
 function FindProxyForURL(url, host) {
   // Call dnsResolve with some wonky arguments.
-  dnsResolve();
-  dnsResolve(null);
-  dnsResolve(undefined);
-  dnsResolve("");
-  dnsResolve({foo: 'bar'});
-  dnsResolve(fn);
-  dnsResolve(['3']);
-  dnsResolve("arg1", "arg2", "arg3", "arg4");
+  // Those expected to fail (because we have passed a non-string parameter)
+  // will return |null|, whereas those that have called through to the C++
+  // bindings will return '127.0.0.1'.
+  expectEquals(null, dnsResolve());
+  expectEquals(null, dnsResolve(null));
+  expectEquals(null, dnsResolve(undefined));
+  expectEquals('127.0.0.1', dnsResolve(""));
+  expectEquals(null, dnsResolve({foo: 'bar'}));
+  expectEquals(null, dnsResolve(fn));
+  expectEquals(null, dnsResolve(['3']));
+  expectEquals('127.0.0.1', dnsResolve("arg1", "arg2", "arg3", "arg4"));
 
   // Call alert with some wonky arguments.
   alert();
diff --git a/net/data/proxy_resolver_v8_unittest/international_domain_names.js b/net/data/proxy_resolver_v8_unittest/international_domain_names.js
new file mode 100644
index 0000000..546af13
--- /dev/null
+++ b/net/data/proxy_resolver_v8_unittest/international_domain_names.js
@@ -0,0 +1,16 @@
+// Try resolving hostnames containing non-ASCII characters.
+
+function FindProxyForURL(url, host) {
+  // This international hostname has a non-ASCII character. It is represented
+  // in punycode as 'xn--bcher-kva.ch'
+  var idn = 'B\u00fccher.ch';
+
+  // We disregard the actual return value -- all we care about is that on
+  // the C++ end the bindings were passed the punycode equivalent of this
+  // unicode hostname.
+  dnsResolve(idn);
+  dnsResolveEx(idn);
+
+  return "DIRECT";
+}
+
diff --git a/net/data/ssl/certificates/dod_ca_17_cert.der b/net/data/ssl/certificates/dod_ca_17_cert.der
new file mode 100644
index 0000000..50e509e
--- /dev/null
+++ b/net/data/ssl/certificates/dod_ca_17_cert.der
Binary files differ
diff --git a/net/data/ssl/certificates/dod_root_ca_2_cert.der b/net/data/ssl/certificates/dod_root_ca_2_cert.der
new file mode 100644
index 0000000..30dcdb9
--- /dev/null
+++ b/net/data/ssl/certificates/dod_root_ca_2_cert.der
Binary files differ
diff --git a/net/data/ssl/certificates/www_us_army_mil_cert.der b/net/data/ssl/certificates/www_us_army_mil_cert.der
new file mode 100644
index 0000000..bee38ff
--- /dev/null
+++ b/net/data/ssl/certificates/www_us_army_mil_cert.der
Binary files differ
diff --git a/net/data/valgrind/net_unittests.gtest-memcheck.txt b/net/data/valgrind/net_unittests.gtest-memcheck.txt
new file mode 100644
index 0000000..458ddef
--- /dev/null
+++ b/net/data/valgrind/net_unittests.gtest-memcheck.txt
@@ -0,0 +1,16 @@
+# These tests leak data intentionally, so are inappropriate for Valgrind tests.
+# Similar list in ../purify/net_unittests.exe.gtest.txt
+# TODO(dkegel): either merge the two files or keep them in sync,
+# see http://code.google.com/p/chromium/issues/detail?id=8951
+DiskCacheBackendTest.InvalidEntry
+DiskCacheBackendTest.InvalidEntryRead
+DiskCacheBackendTest.InvalidEntryWithLoad
+DiskCacheBackendTest.TrimInvalidEntry
+DiskCacheBackendTest.TrimInvalidEntry2
+DiskCacheBackendTest.InvalidEntryEnumeration
+DiskCacheBackendTest.NewEvictionInvalidEntry
+DiskCacheBackendTest.NewEvictionInvalidEntryRead
+DiskCacheBackendTest.NewEvictionInvalidEntryWithLoad
+DiskCacheBackendTest.NewEvictionTrimInvalidEntry
+DiskCacheBackendTest.NewEvictionTrimInvalidEntry2
+DiskCacheBackendTest.NewEvictionInvalidEntryEnumeration
diff --git a/net/data/valgrind/net_unittests.gtest-tsan_mac.txt b/net/data/valgrind/net_unittests.gtest-tsan_mac.txt
index 10e23a5..4362060 100644
--- a/net/data/valgrind/net_unittests.gtest-tsan_mac.txt
+++ b/net/data/valgrind/net_unittests.gtest-tsan_mac.txt
@@ -2,27 +2,23 @@
 # Tsan still tends to hang on these tests unexpectedly on Mac OS.
 # (see https://bugs.kde.org/show_bug.cgi?id=192634 and
 # http://code.google.com/p/data-race-test/issues/detail?id=11)
-FileStreamTest.*Async*
-HostResolverImplTest.*
-TestCompletionCallbackTest.Simple
-FtpNetworkTransactionTest.*
 HttpNetworkLayerTest.GET
 HttpNetworkLayerTest.SimpleGET
-HttpNetworkTransactionTest.*
-ProxyScriptFetcherTest.*
-SOCKS5ClientSocketTest.*
-SOCKSClientSocketTest.*
-TCPClientSocketPoolTest.*
-URLRequestTest.*
-URLRequestTestHTTP.*
-URLRequestTestFTP.*
-SSLClientSocketTest.*
-DiskCacheTest.*
-DiskCacheEntryTest.*
-FlipNetworkTransactionTest.*
-SocketStreamTest.BasicAuthProxy
 
 # WebSocketTest tests are extraordinary slow under ThreadSanitizer,
 # (see http://crbug.com/25392)
 # TODO(glider): investigate this.
 WebSocketTest.*
+
+# These tests die because of unhandled shm_unlink call
+# (see http://crbug.com/36657)
+DiskCacheBackendTest.*InvalidRankings*
+DiskCacheBackendTest.*DisableSuccess*
+DiskCacheBackendTest.*DisableFailure*
+
+# Strange reports from __NSThread__main__ appeared with the new TSan binaries
+# See http://crbug.com/38926
+DirectoryLister*
+
+# See http://crbug.com/44570
+HttpNetworkTransactionTest.StopsReading204
diff --git a/net/data/valgrind/net_unittests.gtest-tsan_win32.txt b/net/data/valgrind/net_unittests.gtest-tsan_win32.txt
new file mode 100644
index 0000000..52de2b7
--- /dev/null
+++ b/net/data/valgrind/net_unittests.gtest-tsan_win32.txt
@@ -0,0 +1,26 @@
+# These tests fail due to unknown reasons
+# TODO(timurrrr): investigate
+CookieMonsterTest.TestLastAccess
+SpdyNetwork*Error*
+SpdyNetwork*Get*
+SpdyNetworkTransactionTest.SynReplyHeadersVary
+X509CertificateTest.UnoSoftCertParsing
+URLRequestTest.DoNotSaveCookies
+URLRequestTest.QuitTest
+DiskCacheEntryTest.*HugeSparse*
+
+# See http://crbug.com/46647
+DiskCacheBackendTest.*
+
+# See http://crbug.com/47836
+ClientSocketPoolBaseTest.CancelPendingSocketAtSocketLimit
+
+#########################################
+# These tests fail if you don't have our SSL certificate installed.
+# Please see http://dev.chromium.org/developers/testing#TOC-SSL-tests
+# if you think you want to un-comment one of the following lines.
+#SSLClientSocketTest.*
+#URLRequestTest*
+#HTTPSRequestTest.*
+#X509CertificateTest.*
+#ProxyScriptFetcherTest.*
diff --git a/net/data/valgrind/net_unittests.gtest.txt b/net/data/valgrind/net_unittests.gtest.txt
index 458ddef..0af617c 100644
--- a/net/data/valgrind/net_unittests.gtest.txt
+++ b/net/data/valgrind/net_unittests.gtest.txt
@@ -1,16 +1,6 @@
-# These tests leak data intentionally, so are inappropriate for Valgrind tests.
-# Similar list in ../purify/net_unittests.exe.gtest.txt
-# TODO(dkegel): either merge the two files or keep them in sync,
-# see http://code.google.com/p/chromium/issues/detail?id=8951
-DiskCacheBackendTest.InvalidEntry
-DiskCacheBackendTest.InvalidEntryRead
-DiskCacheBackendTest.InvalidEntryWithLoad
-DiskCacheBackendTest.TrimInvalidEntry
-DiskCacheBackendTest.TrimInvalidEntry2
-DiskCacheBackendTest.InvalidEntryEnumeration
-DiskCacheBackendTest.NewEvictionInvalidEntry
-DiskCacheBackendTest.NewEvictionInvalidEntryRead
-DiskCacheBackendTest.NewEvictionInvalidEntryWithLoad
-DiskCacheBackendTest.NewEvictionTrimInvalidEntry
-DiskCacheBackendTest.NewEvictionTrimInvalidEntry2
-DiskCacheBackendTest.NewEvictionInvalidEntryEnumeration
+# Very slow under Valgrind.
+KeygenHandlerTest.*SmokeTest
+KeygenHandlerTest.*ConcurrencyTest
+
+# Fails Valgrind with varying stack traces. http://crbug.com/43179
+SpdyNetworkTransactionTest.PostWithEarlySynReply
diff --git a/net/data/valgrind/net_unittests.gtest_linux.txt b/net/data/valgrind/net_unittests.gtest_linux.txt
new file mode 100644
index 0000000..1b69eec
--- /dev/null
+++ b/net/data/valgrind/net_unittests.gtest_linux.txt
@@ -0,0 +1,3 @@
+# These tests fail due to certificate errors; see http://crbug.com/36770
+HTTPSRequestTest.HTTPSMismatchedTest
+SSLClientSocketTest.ConnectMismatched
diff --git a/net/data/valgrind/net_unittests.gtest_mac.txt b/net/data/valgrind/net_unittests.gtest_mac.txt
new file mode 100644
index 0000000..ec7da13
--- /dev/null
+++ b/net/data/valgrind/net_unittests.gtest_mac.txt
@@ -0,0 +1,2 @@
+# Very slow under Valgrind, (see <http://crbug.com/37289>).
+KeygenHandlerTest.FLAKY_SmokeTest
diff --git a/net/data/valgrind/net_unittests.gtest_wine.txt b/net/data/valgrind/net_unittests.gtest_wine.txt
new file mode 100644
index 0000000..2492f86
--- /dev/null
+++ b/net/data/valgrind/net_unittests.gtest_wine.txt
@@ -0,0 +1,49 @@
+# crash             Crashes in Wine
+# crash-valgrind    Crashes in Wine + Valgrind
+# dontcare          Safe to ignore
+# dontcare-hangwin  Ignore, hangs on Windows too
+# dontcare-winfail  Ignore, fails on Windows too
+# dontcare-flaky    Ignore, flaky test
+# dontcare-hang     Ignore, hangs we don't care about
+# fail              Fails, needs triaging or needs to be fixed
+# fail-valgrind     Fails only under Valgrind
+# fail_wine_vmware  Fails in Wine under VMware? TODO(dank) clarify
+# flaky-valgrind    Flaky under Valgrind, needs investigation
+# hang              Test that hangs for some reason
+# hang-valgrind     Test that hangs under valgrind, or just takes too long
+
+# fail
+http://bugs.winehq.org/show_bug.cgi?id=20748
+SSLClientSocketTest.Read_Interrupted
+
+# fail
+# https/ssl failing on the bot, bad Wine? TODO(thestig): investigate
+HTTPSRequestTest.HTTPSExpiredTest
+
+# fail
+# https/ssl failing on the bot, bad Wine? TODO(thestig): investigate
+HTTPSRequestTest.HTTPSGetTest
+
+# fail
+# https/ssl failing on the bot, bad Wine? TODO(thestig): investigate
+HTTPSRequestTest.HTTPSMismatchedTest
+
+# fail
+# https/ssl failing on the bot, bad Wine? TODO(thestig): investigate
+SSLClientSocketTest.Connect
+
+# fail
+# https/ssl failing on the bot, bad Wine? TODO(thestig): investigate
+SSLClientSocketTest.Read
+
+# fail
+# https/ssl failing on the bot, bad Wine? TODO(thestig): investigate
+SSLClientSocketTest.Read_FullDuplex
+
+# fail
+# https/ssl failing on the bot, bad Wine? TODO(thestig): investigate
+SSLClientSocketTest.Read_SmallChunks
+
+# fail
+# https/ssl failing on the bot, bad Wine? TODO(thestig): investigate
+URLRequestTestHTTP.HTTPSToHTTPRedirectNoRefererTest
diff --git a/net/disk_cache/addr.h b/net/disk_cache/addr.h
index a504019..938a442 100644
--- a/net/disk_cache/addr.h
+++ b/net/disk_cache/addr.h
@@ -23,7 +23,7 @@
 const int kMaxBlockSize = 4096 * 4;
 const int kMaxBlockFile = 255;
 const int kMaxNumBlocks = 4;
-const int kFirstAdditionlBlockFile = 4;
+const int kFirstAdditionalBlockFile = 4;
 
 // Defines a storage address for a cache record
 //
diff --git a/net/disk_cache/backend_impl.cc b/net/disk_cache/backend_impl.cc
index 3b3ca6a..13b17f4 100644
--- a/net/disk_cache/backend_impl.cc
+++ b/net/disk_cache/backend_impl.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -20,6 +20,7 @@
 #include "net/disk_cache/errors.h"
 #include "net/disk_cache/hash.h"
 #include "net/disk_cache/file.h"
+#include "net/disk_cache/mem_backend_impl.h"
 
 // This has to be defined before including histogram_macros.h from this file.
 #define NET_DISK_CACHE_BACKEND_IMPL_CC_
@@ -27,6 +28,7 @@
 
 using base::Time;
 using base::TimeDelta;
+using base::TimeTicks;
 
 namespace {
 
@@ -83,7 +85,7 @@
  private:
   FilePath path_;
   std::string name_;
-  DISALLOW_EVIL_CONSTRUCTORS(CleanupTask);
+  DISALLOW_COPY_AND_ASSIGN(CleanupTask);
 };
 
 void CleanupTask::Run() {
@@ -129,12 +131,7 @@
     return false;
   }
 
-#if defined(OS_WIN)
   WorkerPool::PostTask(FROM_HERE, new CleanupTask(path, name_str), true);
-#elif defined(OS_POSIX)
-  // TODO(rvargas): Use the worker pool.
-  MessageLoop::current()->PostTask(FROM_HERE, new CleanupTask(path, name_str));
-#endif
   return true;
 }
 
@@ -165,42 +162,153 @@
   trial1->AppendGroup(group1, FieldTrial::kAllRemainingProbability);
 }
 
+// ------------------------------------------------------------------------
+
+// This class takes care of building an instance of the backend.
+class CacheCreator {
+ public:
+  CacheCreator(const FilePath& path, bool force, int max_bytes,
+               net::CacheType type, uint32 flags,
+               base::MessageLoopProxy* thread, disk_cache::Backend** backend,
+               net::CompletionCallback* callback)
+      : path_(path), force_(force), retry_(false), max_bytes_(max_bytes),
+        type_(type), flags_(flags), thread_(thread), backend_(backend),
+        callback_(callback), cache_(NULL),
+        ALLOW_THIS_IN_INITIALIZER_LIST(
+            my_callback_(this, &CacheCreator::OnIOComplete)) {
+  }
+  ~CacheCreator() {}
+
+  // Creates the backend.
+  int Run();
+
+  // Callback implementation.
+  void OnIOComplete(int result);
+
+ private:
+  void DoCallback(int result);
+
+  const FilePath& path_;
+  bool force_;
+  bool retry_;
+  int max_bytes_;
+  net::CacheType type_;
+  uint32 flags_;
+  scoped_refptr<base::MessageLoopProxy> thread_;
+  disk_cache::Backend** backend_;
+  net::CompletionCallback* callback_;
+  disk_cache::BackendImpl* cache_;
+  net::CompletionCallbackImpl<CacheCreator> my_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(CacheCreator);
+};
+
+int CacheCreator::Run() {
+  cache_ = new disk_cache::BackendImpl(path_, thread_);
+  cache_->SetMaxSize(max_bytes_);
+  cache_->SetType(type_);
+  cache_->SetFlags(flags_);
+  int rv = cache_->Init(&my_callback_);
+  DCHECK_EQ(net::ERR_IO_PENDING, rv);
+  return rv;
+}
+
+void CacheCreator::OnIOComplete(int result) {
+  if (result == net::OK || !force_ || retry_)
+    return DoCallback(result);
+
+  // This is a failure and we are supposed to try again, so delete the object,
+  // delete all the files, and try again.
+  retry_ = true;
+  delete cache_;
+  cache_ = NULL;
+  if (!DelayedCacheCleanup(path_))
+    return DoCallback(result);
+
+  // The worker thread will start deleting files soon, but the original folder
+  // is not there anymore... let's create a new set of files.
+  int rv = Run();
+  DCHECK_EQ(net::ERR_IO_PENDING, rv);
+}
+
+void CacheCreator::DoCallback(int result) {
+  DCHECK_NE(net::ERR_IO_PENDING, result);
+  if (result == net::OK) {
+    *backend_ = cache_;
+  } else {
+    LOG(ERROR) << "Unable to create cache";
+    *backend_ = NULL;
+    delete cache_;
+  }
+  callback_->Run(result);
+  delete this;
+}
+
+// ------------------------------------------------------------------------
+
+// A task to perform final cleanup on the background thread.
+class FinalCleanup : public Task {
+ public:
+  explicit FinalCleanup(disk_cache::BackendImpl* backend) : backend_(backend) {}
+  ~FinalCleanup() {}
+
+  virtual void Run();
+ private:
+  disk_cache::BackendImpl* backend_;
+  DISALLOW_EVIL_CONSTRUCTORS(FinalCleanup);
+};
+
+void FinalCleanup::Run() {
+  backend_->StartCleanup();
+}
+
 }  // namespace
 
 // ------------------------------------------------------------------------
 
 namespace disk_cache {
 
-Backend* CreateCacheBackend(const FilePath& full_path, bool force,
-                            int max_bytes, net::CacheType type) {
-  // Create a backend without extra flags.
-  return BackendImpl::CreateBackend(full_path, force, max_bytes, type, kNone);
+int CreateCacheBackend(net::CacheType type, const FilePath& path, int max_bytes,
+                       bool force, base::MessageLoopProxy* thread,
+                       Backend** backend, CompletionCallback* callback) {
+  DCHECK(callback);
+  if (type == net::MEMORY_CACHE) {
+    *backend = MemBackendImpl::CreateBackend(max_bytes);
+    return *backend ? net::OK : net::ERR_FAILED;
+  }
+  DCHECK(thread);
+
+  return BackendImpl::CreateBackend(path, force, max_bytes, type, kNone, thread,
+                                    backend, callback);
 }
 
+// Returns the preferred maximum number of bytes for the cache given the
+// number of available bytes.
 int PreferedCacheSize(int64 available) {
-  // If there is not enough space to use kDefaultCacheSize, use 80% of the
-  // available space.
-  if (available < kDefaultCacheSize)
+  // Return 80% of the available space if there is not enough space to use
+  // kDefaultCacheSize.
+  if (available < kDefaultCacheSize * 10 / 8)
     return static_cast<int32>(available * 8 / 10);
 
-  // Don't use more than 10% of the available space.
-  if (available < 10 * kDefaultCacheSize)
+  // Return kDefaultCacheSize if it uses 80% to 10% of the available space.
+  if (available < kDefaultCacheSize * 10)
     return kDefaultCacheSize;
 
-  // Use 10% of the free space until we reach 2.5 * kDefaultCacheSize.
+  // Return 10% of the available space if the target size
+  // (2.5 * kDefaultCacheSize) is more than 10%.
   if (available < static_cast<int64>(kDefaultCacheSize) * 25)
     return static_cast<int32>(available / 10);
 
-  // After reaching our target size (2.5 * kDefaultCacheSize), attempt to use
-  // 1% of the availabe space.
-  if (available < static_cast<int64>(kDefaultCacheSize) * 100)
+  // Return the target size (2.5 * kDefaultCacheSize) if it uses 10% to 1%
+  // of the available space.
+  if (available < static_cast<int64>(kDefaultCacheSize) * 250)
     return kDefaultCacheSize * 5 / 2;
 
-  int64 one_percent = available / 100;
-  if (one_percent > kint32max)
-    return kint32max;
+  // Return 1% of the available space if it does not exceed kint32max.
+  if (available < static_cast<int64>(kint32max) * 100)
+    return static_cast<int32>(available / 100);
 
-  return static_cast<int32>(one_percent);
+  return kint32max;
 }
 
 // ------------------------------------------------------------------------
@@ -215,123 +323,33 @@
 // desired path) cannot be created.
 //
 // Static.
-Backend* BackendImpl::CreateBackend(const FilePath& full_path, bool force,
-                                    int max_bytes, net::CacheType type,
-                                    BackendFlags flags) {
-  BackendImpl* cache = new BackendImpl(full_path);
-  cache->SetMaxSize(max_bytes);
-  cache->SetType(type);
-  cache->SetFlags(flags);
-  if (cache->Init())
-    return cache;
-
-  delete cache;
-  if (!force)
-    return NULL;
-
-  if (!DelayedCacheCleanup(full_path))
-    return NULL;
-
-  // The worker thread will start deleting files soon, but the original folder
-  // is not there anymore... let's create a new set of files.
-  cache = new BackendImpl(full_path);
-  cache->SetMaxSize(max_bytes);
-  cache->SetType(type);
-  cache->SetFlags(flags);
-  if (cache->Init())
-    return cache;
-
-  delete cache;
-  LOG(ERROR) << "Unable to create cache";
-  return NULL;
+int BackendImpl::CreateBackend(const FilePath& full_path, bool force,
+                               int max_bytes, net::CacheType type,
+                               uint32 flags, base::MessageLoopProxy* thread,
+                               Backend** backend,
+                               CompletionCallback* callback) {
+  CacheCreator* creator = new CacheCreator(full_path, force, max_bytes, type,
+                                           flags, thread, backend, callback);
+  // This object will self-destroy when finished.
+  return creator->Run();
 }
 
-bool BackendImpl::Init() {
-  DCHECK(!init_);
-  if (init_)
-    return false;
-
-  bool create_files = false;
-  if (!InitBackingStore(&create_files)) {
-    ReportError(ERR_STORAGE_ERROR);
-    return false;
-  }
-
-  num_refs_ = num_pending_io_ = max_refs_ = 0;
-
-  if (!restarted_) {
-    trace_object_ = TraceObject::GetTraceObject();
-    // Create a recurrent timer of 30 secs.
-    int timer_delay = unit_test_ ? 1000 : 30000;
-    timer_.Start(TimeDelta::FromMilliseconds(timer_delay), this,
-                 &BackendImpl::OnStatsTimer);
-  }
-
-  init_ = true;
-
-  if (data_->header.experiment != 0 && cache_type_ != net::DISK_CACHE) {
-    // No experiment for other caches.
-    return false;
-  }
-
-  if (!(user_flags_ & disk_cache::kNoRandom)) {
-    // The unit test controls directly what to test.
-    if (!InitExperiment(&data_->header.experiment))
-      return false;
-
-    new_eviction_ = (cache_type_ == net::DISK_CACHE);
-  }
-
-  if (!CheckIndex()) {
-    ReportError(ERR_INIT_FAILED);
-    return false;
-  }
-
-  // We don't care if the value overflows. The only thing we care about is that
-  // the id cannot be zero, because that value is used as "not dirty".
-  // Increasing the value once per second gives us many years before a we start
-  // having collisions.
-  data_->header.this_id++;
-  if (!data_->header.this_id)
-    data_->header.this_id++;
-
-  if (data_->header.crash) {
-    ReportError(ERR_PREVIOUS_CRASH);
-  } else {
-    ReportError(0);
-    data_->header.crash = 1;
-  }
-
-  if (!block_files_.Init(create_files))
-    return false;
-
-  // stats_ and rankings_ may end up calling back to us so we better be enabled.
-  disabled_ = false;
-  if (!stats_.Init(this, &data_->header.stats))
-    return false;
-
-  disabled_ = !rankings_.Init(this, new_eviction_);
-  eviction_.Init(this);
-
-  // Setup load-time data only for the main cache.
-  if (cache_type() == net::DISK_CACHE)
-    SetFieldTrialInfo(GetSizeGroup());
-
-  return !disabled_;
+int BackendImpl::Init(CompletionCallback* callback) {
+  background_queue_.Init(callback);
+  return net::ERR_IO_PENDING;
 }
 
 BackendImpl::~BackendImpl() {
-  Trace("Backend destructor");
-  if (!init_)
-    return;
+  background_queue_.WaitForPendingIO();
 
-  if (data_)
-    data_->header.crash = 0;
-
-  timer_.Stop();
-
-  File::WaitForPendingIO(&num_pending_io_);
-  DCHECK(!num_refs_);
+  if (background_queue_.BackgroundIsCurrentThread()) {
+    // Unit tests may use the same thread for everything.
+    CleanupCache();
+  } else {
+    background_queue_.background_thread()->PostTask(FROM_HERE,
+                                                    new FinalCleanup(this));
+    done_.Wait();
+  }
 }
 
 // ------------------------------------------------------------------------
@@ -351,271 +369,57 @@
   return not_deleted;
 }
 
-bool BackendImpl::OpenEntry(const std::string& key, Entry** entry) {
-  if (disabled_)
-    return false;
-
-  Time start = Time::Now();
-  uint32 hash = Hash(key);
-
-  EntryImpl* cache_entry = MatchEntry(key, hash, false);
-  if (!cache_entry) {
-    stats_.OnEvent(Stats::OPEN_MISS);
-    return false;
-  }
-
-  if (ENTRY_NORMAL != cache_entry->entry()->Data()->state) {
-    // The entry was already evicted.
-    cache_entry->Release();
-    stats_.OnEvent(Stats::OPEN_MISS);
-    return false;
-  }
-
-  eviction_.OnOpenEntry(cache_entry);
-  DCHECK(entry);
-  *entry = cache_entry;
-
-  CACHE_UMA(AGE_MS, "OpenTime", GetSizeGroup(), start);
-  stats_.OnEvent(Stats::OPEN_HIT);
-  return true;
-}
-
 int BackendImpl::OpenEntry(const std::string& key, Entry** entry,
                            CompletionCallback* callback) {
-  if (OpenEntry(key, entry))
-    return net::OK;
-
-  return net::ERR_FAILED;
-}
-
-bool BackendImpl::CreateEntry(const std::string& key, Entry** entry) {
-  if (disabled_ || key.empty())
-    return false;
-
-  DCHECK(entry);
-  *entry = NULL;
-
-  Time start = Time::Now();
-  uint32 hash = Hash(key);
-
-  scoped_refptr<EntryImpl> parent;
-  Addr entry_address(data_->table[hash & mask_]);
-  if (entry_address.is_initialized()) {
-    // We have an entry already. It could be the one we are looking for, or just
-    // a hash conflict.
-    EntryImpl* old_entry = MatchEntry(key, hash, false);
-    if (old_entry)
-      return ResurrectEntry(old_entry, entry);
-
-    EntryImpl* parent_entry = MatchEntry(key, hash, true);
-    if (!parent_entry) {
-      NOTREACHED();
-      return false;
-    }
-    parent.swap(&parent_entry);
-  }
-
-  int num_blocks;
-  size_t key1_len = sizeof(EntryStore) - offsetof(EntryStore, key);
-  if (key.size() < key1_len ||
-      key.size() > static_cast<size_t>(kMaxInternalKeyLength))
-    num_blocks = 1;
-  else
-    num_blocks = static_cast<int>((key.size() - key1_len) / 256 + 2);
-
-  if (!block_files_.CreateBlock(BLOCK_256, num_blocks, &entry_address)) {
-    LOG(ERROR) << "Create entry failed " << key.c_str();
-    stats_.OnEvent(Stats::CREATE_ERROR);
-    return false;
-  }
-
-  Addr node_address(0);
-  if (!block_files_.CreateBlock(RANKINGS, 1, &node_address)) {
-    block_files_.DeleteBlock(entry_address, false);
-    LOG(ERROR) << "Create entry failed " << key.c_str();
-    stats_.OnEvent(Stats::CREATE_ERROR);
-    return false;
-  }
-
-  scoped_refptr<EntryImpl> cache_entry(new EntryImpl(this, entry_address));
-  IncreaseNumRefs();
-
-  if (!cache_entry->CreateEntry(node_address, key, hash)) {
-    block_files_.DeleteBlock(entry_address, false);
-    block_files_.DeleteBlock(node_address, false);
-    LOG(ERROR) << "Create entry failed " << key.c_str();
-    stats_.OnEvent(Stats::CREATE_ERROR);
-    return false;
-  }
-
-  // We are not failing the operation; let's add this to the map.
-  open_entries_[entry_address.value()] = cache_entry;
-
-  if (parent.get())
-    parent->SetNextAddress(entry_address);
-
-  block_files_.GetFile(entry_address)->Store(cache_entry->entry());
-  block_files_.GetFile(node_address)->Store(cache_entry->rankings());
-
-  IncreaseNumEntries();
-  eviction_.OnCreateEntry(cache_entry);
-  if (!parent.get())
-    data_->table[hash & mask_] = entry_address.value();
-
-  cache_entry.swap(reinterpret_cast<EntryImpl**>(entry));
-
-  CACHE_UMA(AGE_MS, "CreateTime", GetSizeGroup(), start);
-  stats_.OnEvent(Stats::CREATE_HIT);
-  Trace("create entry hit ");
-  return true;
+  DCHECK(callback);
+  background_queue_.OpenEntry(key, entry, callback);
+  return net::ERR_IO_PENDING;
 }
 
 int BackendImpl::CreateEntry(const std::string& key, Entry** entry,
                              CompletionCallback* callback) {
-  if (CreateEntry(key, entry))
-    return net::OK;
-
-  return net::ERR_FAILED;
-}
-
-bool BackendImpl::DoomEntry(const std::string& key) {
-  if (disabled_)
-    return false;
-
-  Entry* entry;
-  if (!OpenEntry(key, &entry))
-    return false;
-
-  // Note that you'd think you could just pass &entry_impl to OpenEntry,
-  // but that triggers strict aliasing problems with gcc.
-  EntryImpl* entry_impl = reinterpret_cast<EntryImpl*>(entry);
-  entry_impl->Doom();
-  entry_impl->Release();
-  return true;
+  DCHECK(callback);
+  background_queue_.CreateEntry(key, entry, callback);
+  return net::ERR_IO_PENDING;
 }
 
 int BackendImpl::DoomEntry(const std::string& key,
                            CompletionCallback* callback) {
-  if (DoomEntry(key))
-    return net::OK;
-
-  return net::ERR_FAILED;
-}
-
-bool BackendImpl::DoomAllEntries() {
-  if (!num_refs_) {
-    PrepareForRestart();
-    DeleteCache(path_, false);
-    return Init();
-  } else {
-    if (disabled_)
-      return false;
-
-    eviction_.TrimCache(true);
-    stats_.OnEvent(Stats::DOOM_CACHE);
-    return true;
-  }
+  DCHECK(callback);
+  background_queue_.DoomEntry(key, callback);
+  return net::ERR_IO_PENDING;
 }
 
 int BackendImpl::DoomAllEntries(CompletionCallback* callback) {
-  if (DoomAllEntries())
-    return net::OK;
-
-  return net::ERR_FAILED;
-}
-
-bool BackendImpl::DoomEntriesBetween(const Time initial_time,
-                                     const Time end_time) {
-  if (end_time.is_null())
-    return DoomEntriesSince(initial_time);
-
-  DCHECK(end_time >= initial_time);
-
-  if (disabled_)
-    return false;
-
-  Entry* node, *next;
-  void* iter = NULL;
-  if (!OpenNextEntry(&iter, &next))
-    return true;
-
-  while (next) {
-    node = next;
-    if (!OpenNextEntry(&iter, &next))
-      next = NULL;
-
-    if (node->GetLastUsed() >= initial_time &&
-        node->GetLastUsed() < end_time) {
-      node->Doom();
-    } else if (node->GetLastUsed() < initial_time) {
-      if (next)
-        next->Close();
-      next = NULL;
-      EndEnumeration(&iter);
-    }
-
-    node->Close();
-  }
-
-  return true;
+  DCHECK(callback);
+  background_queue_.DoomAllEntries(callback);
+  return net::ERR_IO_PENDING;
 }
 
 int BackendImpl::DoomEntriesBetween(const base::Time initial_time,
                                     const base::Time end_time,
                                     CompletionCallback* callback) {
-  if (DoomEntriesBetween(initial_time, end_time))
-    return net::OK;
-
-  return net::ERR_FAILED;
-}
-
-// We use OpenNextEntry to retrieve elements from the cache, until we get
-// entries that are too old.
-bool BackendImpl::DoomEntriesSince(const Time initial_time) {
-  if (disabled_)
-    return false;
-
-  for (;;) {
-    Entry* entry;
-    void* iter = NULL;
-    if (!OpenNextEntry(&iter, &entry))
-      return true;
-
-    if (initial_time > entry->GetLastUsed()) {
-      entry->Close();
-      EndEnumeration(&iter);
-      return true;
-    }
-
-    entry->Doom();
-    entry->Close();
-    EndEnumeration(&iter);  // Dooming the entry invalidates the iterator.
-  }
+  DCHECK(callback);
+  background_queue_.DoomEntriesBetween(initial_time, end_time, callback);
+  return net::ERR_IO_PENDING;
 }
 
 int BackendImpl::DoomEntriesSince(const base::Time initial_time,
                                   CompletionCallback* callback) {
-  if (DoomEntriesSince(initial_time))
-    return net::OK;
-
-  return net::ERR_FAILED;
-}
-
-bool BackendImpl::OpenNextEntry(void** iter, Entry** next_entry) {
-  return OpenFollowingEntry(true, iter, next_entry);
+  DCHECK(callback);
+  background_queue_.DoomEntriesSince(initial_time, callback);
+  return net::ERR_IO_PENDING;
 }
 
 int BackendImpl::OpenNextEntry(void** iter, Entry** next_entry,
                                CompletionCallback* callback) {
-  if (OpenNextEntry(iter, next_entry))
-    return net::OK;
-
-  return net::ERR_FAILED;
+  DCHECK(callback);
+  background_queue_.OpenNextEntry(iter, next_entry, callback);
+  return net::ERR_IO_PENDING;
 }
 
 void BackendImpl::EndEnumeration(void** iter) {
-  scoped_ptr<Rankings::Iterator> iterator(
-      reinterpret_cast<Rankings::Iterator*>(*iter));
+  background_queue_.EndEnumeration(*iter);
   *iter = NULL;
 }
 
@@ -646,6 +450,342 @@
 
 // ------------------------------------------------------------------------
 
+int BackendImpl::SyncInit() {
+  DCHECK(!init_);
+  if (init_)
+    return net::ERR_FAILED;
+
+  bool create_files = false;
+  if (!InitBackingStore(&create_files)) {
+    ReportError(ERR_STORAGE_ERROR);
+    return net::ERR_FAILED;
+  }
+
+  num_refs_ = num_pending_io_ = max_refs_ = 0;
+  entry_count_ = byte_count_ = 0;
+
+  if (!restarted_) {
+    trace_object_ = TraceObject::GetTraceObject();
+    // Create a recurrent timer of 30 secs.
+    int timer_delay = unit_test_ ? 1000 : 30000;
+    timer_.Start(TimeDelta::FromMilliseconds(timer_delay), this,
+                 &BackendImpl::OnStatsTimer);
+  }
+
+  init_ = true;
+
+  if (data_->header.experiment != 0 && cache_type_ != net::DISK_CACHE) {
+    // No experiment for other caches.
+    return net::ERR_FAILED;
+  }
+
+  if (!(user_flags_ & disk_cache::kNoRandom)) {
+    // The unit test controls directly what to test.
+    if (!InitExperiment(&data_->header.experiment))
+      return net::ERR_FAILED;
+
+    new_eviction_ = (cache_type_ == net::DISK_CACHE);
+  }
+
+  if (!CheckIndex()) {
+    ReportError(ERR_INIT_FAILED);
+    return net::ERR_FAILED;
+  }
+
+  // We don't care if the value overflows. The only thing we care about is that
+  // the id cannot be zero, because that value is used as "not dirty".
+  // Increasing the value once per second gives us many years before we start
+  // having collisions.
+  data_->header.this_id++;
+  if (!data_->header.this_id)
+    data_->header.this_id++;
+
+  if (data_->header.crash) {
+    ReportError(ERR_PREVIOUS_CRASH);
+  } else {
+    ReportError(0);
+    data_->header.crash = 1;
+  }
+
+  if (!block_files_.Init(create_files))
+    return net::ERR_FAILED;
+
+  // stats_ and rankings_ may end up calling back to us so we better be enabled.
+  disabled_ = false;
+  if (!stats_.Init(this, &data_->header.stats))
+    return net::ERR_FAILED;
+
+  disabled_ = !rankings_.Init(this, new_eviction_);
+  eviction_.Init(this);
+
+  // Setup load-time data only for the main cache.
+  if (cache_type() == net::DISK_CACHE)
+    SetFieldTrialInfo(GetSizeGroup());
+
+  return disabled_ ? net::ERR_FAILED : net::OK;
+}
+
+void BackendImpl::StartCleanup() {
+  Trace("Backend StartCleanup");
+  eviction_.Stop();
+
+  // Give a chance for any posted evictions to be discarded.
+  MessageLoop::current()->PostTask(FROM_HERE,
+        factory_.NewRunnableMethod(&BackendImpl::CleanupCache));
+}
+
+void BackendImpl::CleanupCache() {
+  Trace("Backend Cleanup");
+  if (init_) {
+    if (data_)
+      data_->header.crash = 0;
+
+    timer_.Stop();
+    File::WaitForPendingIO(&num_pending_io_);
+    DCHECK(!num_refs_);
+  }
+  factory_.RevokeAll();
+  done_.Signal();
+}
+
+// ------------------------------------------------------------------------
+
+int BackendImpl::OpenPrevEntry(void** iter, Entry** prev_entry,
+                               CompletionCallback* callback) {
+  DCHECK(callback);
+  background_queue_.OpenPrevEntry(iter, prev_entry, callback);
+  return net::ERR_IO_PENDING;
+}
+
+int BackendImpl::SyncOpenEntry(const std::string& key, Entry** entry) {
+  DCHECK(entry);
+  *entry = OpenEntryImpl(key);
+  return (*entry) ? net::OK : net::ERR_FAILED;
+}
+
+int BackendImpl::SyncCreateEntry(const std::string& key, Entry** entry) {
+  DCHECK(entry);
+  *entry = CreateEntryImpl(key);
+  return (*entry) ? net::OK : net::ERR_FAILED;
+}
+
+int BackendImpl::SyncDoomEntry(const std::string& key) {
+  if (disabled_)
+    return net::ERR_FAILED;
+
+  EntryImpl* entry = OpenEntryImpl(key);
+  if (!entry)
+    return net::ERR_FAILED;
+
+  entry->DoomImpl();
+  entry->Release();
+  return net::OK;
+}
+
+int BackendImpl::SyncDoomAllEntries() {
+  if (!num_refs_) {
+    PrepareForRestart();
+    DeleteCache(path_, false);
+    return SyncInit();
+  } else {
+    if (disabled_)
+      return net::ERR_FAILED;
+
+    eviction_.TrimCache(true);
+    stats_.OnEvent(Stats::DOOM_CACHE);
+    return net::OK;
+  }
+}
+
+int BackendImpl::SyncDoomEntriesBetween(const base::Time initial_time,
+                                        const base::Time end_time) {
+  if (end_time.is_null())
+    return SyncDoomEntriesSince(initial_time);
+
+  DCHECK(end_time >= initial_time);
+
+  if (disabled_)
+    return net::ERR_FAILED;
+
+  EntryImpl* node;
+  void* iter = NULL;
+  EntryImpl* next = OpenNextEntryImpl(&iter);
+  if (!next)
+    return net::OK;
+
+  while (next) {
+    node = next;
+    next = OpenNextEntryImpl(&iter);
+
+    if (node->GetLastUsed() >= initial_time &&
+        node->GetLastUsed() < end_time) {
+      node->DoomImpl();
+    } else if (node->GetLastUsed() < initial_time) {
+      if (next)
+        next->Release();
+      next = NULL;
+      SyncEndEnumeration(iter);
+    }
+
+    node->Release();
+  }
+
+  return net::OK;
+}
+
+// We use OpenNextEntryImpl to retrieve elements from the cache, until we get
+// entries that are too old.
+int BackendImpl::SyncDoomEntriesSince(const base::Time initial_time) {
+  if (disabled_)
+    return net::ERR_FAILED;
+
+  for (;;) {
+    void* iter = NULL;
+    EntryImpl* entry = OpenNextEntryImpl(&iter);
+    if (!entry)
+      return net::OK;
+
+    if (initial_time > entry->GetLastUsed()) {
+      entry->Release();
+      SyncEndEnumeration(iter);
+      return net::OK;
+    }
+
+    entry->DoomImpl();
+    entry->Release();
+    SyncEndEnumeration(iter);  // Dooming the entry invalidates the iterator.
+  }
+}
+
+int BackendImpl::SyncOpenNextEntry(void** iter, Entry** next_entry) {
+  *next_entry = OpenNextEntryImpl(iter);
+  return (*next_entry) ? net::OK : net::ERR_FAILED;
+}
+
+int BackendImpl::SyncOpenPrevEntry(void** iter, Entry** prev_entry) {
+  *prev_entry = OpenPrevEntryImpl(iter);
+  return (*prev_entry) ? net::OK : net::ERR_FAILED;
+}
+
+void BackendImpl::SyncEndEnumeration(void* iter) {
+  scoped_ptr<Rankings::Iterator> iterator(
+      reinterpret_cast<Rankings::Iterator*>(iter));
+}
+
+EntryImpl* BackendImpl::OpenEntryImpl(const std::string& key) {
+  if (disabled_)
+    return NULL;
+
+  TimeTicks start = TimeTicks::Now();
+  uint32 hash = Hash(key);
+
+  EntryImpl* cache_entry = MatchEntry(key, hash, false);
+  if (!cache_entry) {
+    stats_.OnEvent(Stats::OPEN_MISS);
+    return NULL;
+  }
+
+  if (ENTRY_NORMAL != cache_entry->entry()->Data()->state) {
+    // The entry was already evicted.
+    cache_entry->Release();
+    stats_.OnEvent(Stats::OPEN_MISS);
+    return NULL;
+  }
+
+  eviction_.OnOpenEntry(cache_entry);
+  entry_count_++;
+
+  CACHE_UMA(AGE_MS, "OpenTime", GetSizeGroup(), start);
+  stats_.OnEvent(Stats::OPEN_HIT);
+  return cache_entry;
+}
+
+EntryImpl* BackendImpl::CreateEntryImpl(const std::string& key) {
+  if (disabled_ || key.empty())
+    return NULL;
+
+  TimeTicks start = TimeTicks::Now();
+  uint32 hash = Hash(key);
+
+  scoped_refptr<EntryImpl> parent;
+  Addr entry_address(data_->table[hash & mask_]);
+  if (entry_address.is_initialized()) {
+    // We have an entry already. It could be the one we are looking for, or just
+    // a hash conflict.
+    EntryImpl* old_entry = MatchEntry(key, hash, false);
+    if (old_entry)
+      return ResurrectEntry(old_entry);
+
+    EntryImpl* parent_entry = MatchEntry(key, hash, true);
+    if (!parent_entry) {
+      NOTREACHED();
+      return NULL;
+    }
+    parent.swap(&parent_entry);
+  }
+
+  int num_blocks;
+  size_t key1_len = sizeof(EntryStore) - offsetof(EntryStore, key);
+  if (key.size() < key1_len ||
+      key.size() > static_cast<size_t>(kMaxInternalKeyLength))
+    num_blocks = 1;
+  else
+    num_blocks = static_cast<int>((key.size() - key1_len) / 256 + 2);
+
+  if (!block_files_.CreateBlock(BLOCK_256, num_blocks, &entry_address)) {
+    LOG(ERROR) << "Create entry failed " << key.c_str();
+    stats_.OnEvent(Stats::CREATE_ERROR);
+    return NULL;
+  }
+
+  Addr node_address(0);
+  if (!block_files_.CreateBlock(RANKINGS, 1, &node_address)) {
+    block_files_.DeleteBlock(entry_address, false);
+    LOG(ERROR) << "Create entry failed " << key.c_str();
+    stats_.OnEvent(Stats::CREATE_ERROR);
+    return NULL;
+  }
+
+  scoped_refptr<EntryImpl> cache_entry(new EntryImpl(this, entry_address));
+  IncreaseNumRefs();
+
+  if (!cache_entry->CreateEntry(node_address, key, hash)) {
+    block_files_.DeleteBlock(entry_address, false);
+    block_files_.DeleteBlock(node_address, false);
+    LOG(ERROR) << "Create entry failed " << key.c_str();
+    stats_.OnEvent(Stats::CREATE_ERROR);
+    return NULL;
+  }
+
+  // We are not failing the operation; let's add this to the map.
+  open_entries_[entry_address.value()] = cache_entry;
+
+  if (parent.get())
+    parent->SetNextAddress(entry_address);
+
+  block_files_.GetFile(entry_address)->Store(cache_entry->entry());
+  block_files_.GetFile(node_address)->Store(cache_entry->rankings());
+
+  IncreaseNumEntries();
+  eviction_.OnCreateEntry(cache_entry);
+  entry_count_++;
+  if (!parent.get())
+    data_->table[hash & mask_] = entry_address.value();
+
+  CACHE_UMA(AGE_MS, "CreateTime", GetSizeGroup(), start);
+  stats_.OnEvent(Stats::CREATE_HIT);
+  Trace("create entry hit ");
+  return cache_entry.release();
+}
+
+EntryImpl* BackendImpl::OpenNextEntryImpl(void** iter) {
+  return OpenFollowingEntry(true, iter);
+}
+
+EntryImpl* BackendImpl::OpenPrevEntryImpl(void** iter) {
+  return OpenFollowingEntry(false, iter);
+}
+
 bool BackendImpl::SetMaxSize(int max_bytes) {
   COMPILE_ASSERT(sizeof(max_bytes) == sizeof(max_size_), unsupported_int_model);
   if (max_bytes < 0)
@@ -882,10 +1022,17 @@
   Time create_time = Time::FromInternalValue(data_->header.create_time);
   CACHE_UMA(AGE, "FillupAge", 0, create_time);
 
-  int64 use_hours = stats_.GetCounter(Stats::TIMER) / 120;
-  CACHE_UMA(HOURS, "FillupTime", 0, static_cast<int>(use_hours));
+  int64 use_time = stats_.GetCounter(Stats::TIMER);
+  CACHE_UMA(HOURS, "FillupTime", 0, static_cast<int>(use_time / 120));
   CACHE_UMA(PERCENTAGE, "FirstHitRatio", 0, stats_.GetHitRatio());
 
+  if (!use_time)
+    use_time = 1;
+  CACHE_UMA(COUNTS_10000, "FirstEntryAccessRate", 0,
+            static_cast<int>(data_->header.num_entries / use_time));
+  CACHE_UMA(COUNTS, "FirstByteIORate", 0,
+            static_cast<int>((data_->header.num_bytes / 1024) / use_time));
+
   int avg_size = data_->header.num_bytes / GetEntryCount();
   CACHE_UMA(COUNTS, "FirstEntrySize", 0, avg_size);
 
@@ -926,7 +1073,7 @@
 
 void BackendImpl::ReportError(int error) {
   // We transmit positive numbers, instead of direct error codes.
-  DCHECK(error <= 0);
+  DCHECK_LE(error, 0);
   CACHE_UMA(CACHE_ERROR, "Error", 0, error * -1);
 }
 
@@ -934,6 +1081,18 @@
   stats_.OnEvent(an_event);
 }
 
+void BackendImpl::OnRead(int32 bytes) {
+  DCHECK_GE(bytes, 0);
+  byte_count_ += bytes;
+  if (byte_count_ < 0)
+    byte_count_ = kint32max;
+}
+
+void BackendImpl::OnWrite(int32 bytes) {
+  // We use the same implementation as OnRead... just log the number of bytes.
+  OnRead(bytes);
+}
+
 void BackendImpl::OnStatsTimer() {
   stats_.OnEvent(Stats::TIMER);
   int64 time = stats_.GetCounter(Stats::TIMER);
@@ -952,6 +1111,11 @@
 
   CACHE_UMA(COUNTS, "NumberOfReferences", 0, num_refs_);
 
+  CACHE_UMA(COUNTS_10000, "EntryAccessRate", 0, entry_count_);
+  CACHE_UMA(COUNTS, "ByteIORate", 0, byte_count_ / 1024);
+  entry_count_ = 0;
+  byte_count_ = 0;
+
   if (!data_)
     first_timer_ = false;
   if (first_timer_) {
@@ -996,6 +1160,11 @@
   num_refs_ = 0;
 }
 
+int BackendImpl::FlushQueueForTest(CompletionCallback* callback) {
+  background_queue_.FlushQueue(callback);
+  return net::ERR_IO_PENDING;
+}
+
 int BackendImpl::SelfCheck() {
   if (!init_) {
     LOG(ERROR) << "Init failed";
@@ -1016,10 +1185,6 @@
   return CheckAllEntries();
 }
 
-bool BackendImpl::OpenPrevEntry(void** iter, Entry** prev_entry) {
-  return OpenFollowingEntry(false, iter, prev_entry);
-}
-
 // ------------------------------------------------------------------------
 
 // We just created a new file so we're going to write the header and set the
@@ -1071,6 +1236,14 @@
     LOG(ERROR) << "Unable to map Index file";
     return false;
   }
+
+  if (index_->GetLength() < sizeof(Index)) {
+    // We verify this again on CheckIndex() but it's easier to make sure now
+    // that the header is there.
+    LOG(ERROR) << "Corrupt Index file";
+    return false;
+  }
+
   return true;
 }
 
@@ -1123,7 +1296,7 @@
   // trying to re-enable the cache.
   if (unit_test_)
     init_ = true;  // Let the destructor do proper cleanup.
-  else if (Init())
+  else if (SyncInit())
     stats_.SetCounter(Stats::FATAL_ERROR, errors + 1);
 }
 
@@ -1165,9 +1338,14 @@
     return ERR_INVALID_ADDRESS;
   }
 
+  TimeTicks start = TimeTicks::Now();
   if (!cache_entry->entry()->Load())
     return ERR_READ_FAILURE;
 
+  if (IsLoaded()) {
+    CACHE_UMA(AGE_MS, "LoadTime", GetSizeGroup(), start);
+  }
+
   if (!cache_entry->SanityCheck()) {
     LOG(WARNING) << "Messed up entry found.";
     return ERR_INVALID_ENTRY;
@@ -1268,14 +1446,11 @@
 }
 
 // This is the actual implementation for OpenNextEntry and OpenPrevEntry.
-bool BackendImpl::OpenFollowingEntry(bool forward, void** iter,
-                                     Entry** next_entry) {
+EntryImpl* BackendImpl::OpenFollowingEntry(bool forward, void** iter) {
   if (disabled_)
-    return false;
+    return NULL;
 
   DCHECK(iter);
-  DCHECK(next_entry);
-  *next_entry = NULL;
 
   const int kListsToSearch = 3;
   scoped_refptr<EntryImpl> entries[kListsToSearch];
@@ -1295,7 +1470,7 @@
       entries[i].swap(&temp);  // The entry was already addref'd.
     }
     if (!ret)
-      return false;
+      return NULL;
   } else {
     // Get the next entry from the last list, and the actual entries for the
     // elements on the other lists.
@@ -1319,7 +1494,7 @@
     if (entries[i].get()) {
       access_times[i] = entries[i]->GetLastUsed();
       if (newest < 0) {
-        DCHECK(oldest < 0);
+        DCHECK_LT(oldest, 0);
         newest = oldest = i;
         continue;
       }
@@ -1331,18 +1506,19 @@
   }
 
   if (newest < 0 || oldest < 0)
-    return false;
+    return NULL;
 
+  EntryImpl* next_entry;
   if (forward) {
-    entries[newest].swap(reinterpret_cast<EntryImpl**>(next_entry));
+    next_entry = entries[newest].release();
     iterator->list = static_cast<Rankings::List>(newest);
   } else {
-    entries[oldest].swap(reinterpret_cast<EntryImpl**>(next_entry));
+    next_entry = entries[oldest].release();
     iterator->list = static_cast<Rankings::List>(oldest);
   }
 
   *iter = iterator.release();
-  return true;
+  return next_entry;
 }
 
 bool BackendImpl::OpenFollowingEntryFromList(bool forward, Rankings::List list,
@@ -1391,26 +1567,29 @@
     return NULL;
   }
 
+  // Make sure that we save the key for later.
+  entry->GetKey();
+
   return entry;
 }
 
-bool BackendImpl::ResurrectEntry(EntryImpl* deleted_entry, Entry** entry) {
+EntryImpl* BackendImpl::ResurrectEntry(EntryImpl* deleted_entry) {
   if (ENTRY_NORMAL == deleted_entry->entry()->Data()->state) {
     deleted_entry->Release();
     stats_.OnEvent(Stats::CREATE_MISS);
     Trace("create entry miss ");
-    return false;
+    return NULL;
   }
 
   // We are attempting to create an entry and found out that the entry was
   // previously deleted.
 
   eviction_.OnCreateEntry(deleted_entry);
-  *entry = deleted_entry;
+  entry_count_++;
 
   stats_.OnEvent(Stats::CREATE_HIT);
   Trace("Resurrect entry hit ");
-  return true;
+  return deleted_entry;
 }
 
 void BackendImpl::DestroyInvalidEntry(EntryImpl* entry) {
@@ -1444,7 +1623,7 @@
     DestroyInvalidEntry(entry);
     entry->Release();
   }
-  DoomEntry(key);
+  SyncDoomEntry(key);
 
   if (!next_entry)
     return;
@@ -1465,7 +1644,7 @@
 
 void BackendImpl::AddStorageSize(int32 bytes) {
   data_->header.num_bytes += bytes;
-  DCHECK(data_->header.num_bytes >= 0);
+  DCHECK_GE(data_->header.num_bytes, 0);
 
   if (data_->header.num_bytes > max_size_)
     eviction_.TrimCache(false);
@@ -1473,7 +1652,7 @@
 
 void BackendImpl::SubstractStorageSize(int32 bytes) {
   data_->header.num_bytes -= bytes;
-  DCHECK(data_->header.num_bytes >= 0);
+  DCHECK_GE(data_->header.num_bytes, 0);
 }
 
 void BackendImpl::IncreaseNumRefs() {
@@ -1493,7 +1672,7 @@
 
 void BackendImpl::IncreaseNumEntries() {
   data_->header.num_entries++;
-  DCHECK(data_->header.num_entries > 0);
+  DCHECK_GT(data_->header.num_entries, 0);
 }
 
 void BackendImpl::DecreaseNumEntries() {
@@ -1515,12 +1694,12 @@
 
 void BackendImpl::ReportStats() {
   CACHE_UMA(COUNTS, "Entries", 0, data_->header.num_entries);
-  CACHE_UMA(COUNTS, "Size", 0, data_->header.num_bytes / (1024 * 1024));
-  CACHE_UMA(COUNTS, "MaxSize", 0, max_size_ / (1024 * 1024));
+  CACHE_UMA(COUNTS_10000, "Size2", 0, data_->header.num_bytes / (1024 * 1024));
+  CACHE_UMA(COUNTS_10000, "MaxSize2", 0, max_size_ / (1024 * 1024));
 
-  CACHE_UMA(COUNTS, "AverageOpenEntries", 0,
+  CACHE_UMA(COUNTS_10000, "AverageOpenEntries2", 0,
             static_cast<int>(stats_.GetCounter(Stats::OPEN_ENTRIES)));
-  CACHE_UMA(COUNTS, "MaxOpenEntries", 0,
+  CACHE_UMA(COUNTS_10000, "MaxOpenEntries2", 0,
             static_cast<int>(stats_.GetCounter(Stats::MAX_ENTRIES)));
   stats_.SetCounter(Stats::MAX_ENTRIES, 0);
 
@@ -1552,6 +1731,10 @@
 
   int avg_size = data_->header.num_bytes / GetEntryCount();
   CACHE_UMA(COUNTS, "EntrySize", 0, avg_size);
+  CACHE_UMA(COUNTS, "EntriesFull", 0, data_->header.num_entries);
+
+  CACHE_UMA(PERCENTAGE, "IndexLoad", 0,
+            data_->header.num_entries * 100 / (mask_ + 1));
 
   int large_entries_bytes = stats_.GetLargeEntriesSize();
   int large_ratio = large_entries_bytes * 100 / data_->header.num_bytes;
@@ -1571,6 +1754,9 @@
 
   stats_.ResetRatios();
   stats_.SetCounter(Stats::TRIM_ENTRY, 0);
+
+  if (cache_type_ == net::DISK_CACHE)
+    block_files_.ReportStats();
 }
 
 void BackendImpl::UpgradeTo2_1() {
@@ -1622,7 +1808,9 @@
 
   AdjustMaxCacheSize(data_->header.table_len);
 
-  if (data_->header.num_bytes < 0) {
+  if (data_->header.num_bytes < 0 ||
+      (max_size_ < kint32max - kDefaultCacheSize &&
+       data_->header.num_bytes > max_size_ + kDefaultCacheSize)) {
     LOG(ERROR) << "Invalid cache (current) size";
     return false;
   }
@@ -1635,7 +1823,9 @@
   if (!mask_)
     mask_ = data_->header.table_len - 1;
 
-  return true;
+  // Load the table into memory with a single read.
+  scoped_array<char> buf(new char[current_size]);
+  return index_->Read(buf.get(), current_size, 0);
 }
 
 int BackendImpl::CheckAllEntries() {
diff --git a/net/disk_cache/backend_impl.h b/net/disk_cache/backend_impl.h
index 6267fac..90283fb 100644
--- a/net/disk_cache/backend_impl.h
+++ b/net/disk_cache/backend_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -13,6 +13,7 @@
 #include "net/disk_cache/block_files.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/disk_cache/eviction.h"
+#include "net/disk_cache/in_flight_backend_io.h"
 #include "net/disk_cache/rankings.h"
 #include "net/disk_cache/stats.h"
 #include "net/disk_cache/trace.h"
@@ -35,56 +36,81 @@
 class BackendImpl : public Backend {
   friend class Eviction;
  public:
-  explicit BackendImpl(const FilePath& path)
-      : path_(path), block_files_(path), mask_(0), max_size_(0),
+  BackendImpl(const FilePath& path, base::MessageLoopProxy* cache_thread)
+      : ALLOW_THIS_IN_INITIALIZER_LIST(background_queue_(this, cache_thread)),
+        path_(path), block_files_(path), mask_(0), max_size_(0),
         cache_type_(net::DISK_CACHE), uma_report_(0), user_flags_(0),
         init_(false), restarted_(false), unit_test_(false), read_only_(false),
-        new_eviction_(false), first_timer_(true),
+        new_eviction_(false), first_timer_(true), done_(true, false),
         ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {}
   // mask can be used to limit the usable size of the hash table, for testing.
-  BackendImpl(const FilePath& path, uint32 mask)
-      : path_(path), block_files_(path), mask_(mask), max_size_(0),
+  BackendImpl(const FilePath& path, uint32 mask,
+              base::MessageLoopProxy* cache_thread)
+      : ALLOW_THIS_IN_INITIALIZER_LIST(background_queue_(this, cache_thread)),
+        path_(path), block_files_(path), mask_(mask), max_size_(0),
         cache_type_(net::DISK_CACHE), uma_report_(0), user_flags_(kMask),
         init_(false), restarted_(false), unit_test_(false), read_only_(false),
-        new_eviction_(false), first_timer_(true),
+        new_eviction_(false), first_timer_(true), done_(true, false),
         ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {}
   ~BackendImpl();
 
   // Returns a new backend with the desired flags. See the declaration of
   // CreateCacheBackend().
-  static Backend* CreateBackend(const FilePath& full_path, bool force,
-                                int max_bytes, net::CacheType type,
-                                BackendFlags flags);
+  static int CreateBackend(const FilePath& full_path, bool force,
+                           int max_bytes, net::CacheType type,
+                           uint32 flags, base::MessageLoopProxy* thread,
+                           Backend** backend, CompletionCallback* callback);
 
   // Performs general initialization for this current instance of the cache.
-  bool Init();
+  int Init(CompletionCallback* callback);
 
   // Backend interface.
   virtual int32 GetEntryCount() const;
-  virtual bool OpenEntry(const std::string& key, Entry** entry);
   virtual int OpenEntry(const std::string& key, Entry** entry,
                         CompletionCallback* callback);
-  virtual bool CreateEntry(const std::string& key, Entry** entry);
   virtual int CreateEntry(const std::string& key, Entry** entry,
                           CompletionCallback* callback);
-  virtual bool DoomEntry(const std::string& key);
   virtual int DoomEntry(const std::string& key, CompletionCallback* callback);
-  virtual bool DoomAllEntries();
   virtual int DoomAllEntries(CompletionCallback* callback);
-  virtual bool DoomEntriesBetween(const base::Time initial_time,
-                                  const base::Time end_time);
   virtual int DoomEntriesBetween(const base::Time initial_time,
                                  const base::Time end_time,
                                  CompletionCallback* callback);
-  virtual bool DoomEntriesSince(const base::Time initial_time);
   virtual int DoomEntriesSince(const base::Time initial_time,
                                CompletionCallback* callback);
-  virtual bool OpenNextEntry(void** iter, Entry** next_entry);
   virtual int OpenNextEntry(void** iter, Entry** next_entry,
                             CompletionCallback* callback);
   virtual void EndEnumeration(void** iter);
   virtual void GetStats(StatsItems* stats);
 
+  // Performs the actual initialization and final cleanup on destruction.
+  // Cleanup is a two step process (with a trip to the message loop in between).
+  // Note that these methods are not intended for external consumption.
+  int SyncInit();
+  void StartCleanup();
+  void CleanupCache();
+
+  // Same bahavior as OpenNextEntry but walks the list from back to front.
+  int OpenPrevEntry(void** iter, Entry** prev_entry,
+                    CompletionCallback* callback);
+
+  // Synchronous implementation of the asynchronous interface.
+  int SyncOpenEntry(const std::string& key, Entry** entry);
+  int SyncCreateEntry(const std::string& key, Entry** entry);
+  int SyncDoomEntry(const std::string& key);
+  int SyncDoomAllEntries();
+  int SyncDoomEntriesBetween(const base::Time initial_time,
+                             const base::Time end_time);
+  int SyncDoomEntriesSince(const base::Time initial_time);
+  int SyncOpenNextEntry(void** iter, Entry** next_entry);
+  int SyncOpenPrevEntry(void** iter, Entry** prev_entry);
+  void SyncEndEnumeration(void* iter);
+
+  // Open or create an entry for the given |key| or |iter|.
+  EntryImpl* OpenEntryImpl(const std::string& key);
+  EntryImpl* CreateEntryImpl(const std::string& key);
+  EntryImpl* OpenNextEntryImpl(void** iter);
+  EntryImpl* OpenPrevEntryImpl(void** iter);
+
   // Sets the maximum size for the total amount of data stored by this instance.
   bool SetMaxSize(int max_bytes);
 
@@ -97,6 +123,10 @@
   // Returns the actual file used to store a given (non-external) address.
   MappedFile* File(Addr address);
 
+  InFlightBackendIO* background_queue() {
+    return &background_queue_;
+  }
+
   // Creates an external storage file.
   bool CreateExternalFile(Addr* address);
 
@@ -177,6 +207,10 @@
   // Called when an interesting event should be logged (counted).
   void OnEvent(Stats::Counters an_event);
 
+  // Keeps track of paylod access (doesn't include metadata).
+  void OnRead(int bytes);
+  void OnWrite(int bytes);
+
   // Timer callback to calculate usage statistics.
   void OnStatsTimer();
 
@@ -199,13 +233,13 @@
   // Clears the counter of references to test handling of corruptions.
   void ClearRefCountForTest();
 
+  // Sends a dummy operation through the operation queue, for unit tests.
+  int FlushQueueForTest(CompletionCallback* callback);
+
   // Peforms a simple self-check, and returns the number of dirty items
   // or an error code (negative value).
   int SelfCheck();
 
-  // Same bahavior as OpenNextEntry but walks the list from back to front.
-  bool OpenPrevEntry(void** iter, Entry** prev_entry);
-
  private:
   typedef base::hash_map<CacheAddr, EntryImpl*> EntriesMap;
 
@@ -228,7 +262,7 @@
   EntryImpl* MatchEntry(const std::string& key, uint32 hash, bool find_parent);
 
   // Opens the next or previous entry on a cache iteration.
-  bool OpenFollowingEntry(bool forward, void** iter, Entry** next_entry);
+  EntryImpl* OpenFollowingEntry(bool forward, void** iter);
 
   // Opens the next or previous entry on a single list. If successfull,
   // |from_entry| will be updated to point to the new entry, otherwise it will
@@ -242,7 +276,7 @@
   EntryImpl* GetEnumeratedEntry(CacheRankingsBlock* next, bool to_evict);
 
   // Re-opens an entry that was previously deleted.
-  bool ResurrectEntry(EntryImpl* deleted_entry, Entry** entry);
+  EntryImpl* ResurrectEntry(EntryImpl* deleted_entry);
 
   void DestroyInvalidEntry(EntryImpl* entry);
   void DestroyInvalidEntryFromEnumeration(EntryImpl* entry);
@@ -275,6 +309,7 @@
   // Part of the self test. Returns false if the entry is corrupt.
   bool CheckEntry(EntryImpl* cache_entry);
 
+  InFlightBackendIO background_queue_;  // The controller of pending operations.
   scoped_refptr<MappedFile> index_;  // The main cache index.
   FilePath path_;  // Path to the folder used as backing storage.
   Index* data_;  // Pointer to the index data.
@@ -287,6 +322,8 @@
   int num_refs_;  // Number of referenced cache entries.
   int max_refs_;  // Max number of referenced cache entries.
   int num_pending_io_;  // Number of pending IO operations.
+  int entry_count_;  // Number of entries accessed lately.
+  int byte_count_;  // Number of bytes read/written lately.
   net::CacheType cache_type_;
   int uma_report_;  // Controls transmision of UMA data.
   uint32 user_flags_;  // Flags set by the user.
@@ -300,10 +337,11 @@
 
   Stats stats_;  // Usage statistcs.
   base::RepeatingTimer<BackendImpl> timer_;  // Usage timer.
+  base::WaitableEvent done_;  // Signals the end of background work.
   scoped_refptr<TraceObject> trace_object_;  // Inits internal tracing.
   ScopedRunnableMethodFactory<BackendImpl> factory_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(BackendImpl);
+  DISALLOW_COPY_AND_ASSIGN(BackendImpl);
 };
 
 // Returns the prefered max cache size given the available disk space.
diff --git a/net/disk_cache/backend_unittest.cc b/net/disk_cache/backend_unittest.cc
index 9d4194b..7bb27d2 100644
--- a/net/disk_cache/backend_unittest.cc
+++ b/net/disk_cache/backend_unittest.cc
@@ -1,10 +1,9 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "base/basictypes.h"
 #include "base/file_util.h"
-#include "base/path_service.h"
 #include "base/platform_thread.h"
 #include "base/string_util.h"
 #include "net/base/io_buffer.h"
@@ -13,30 +12,13 @@
 #include "net/disk_cache/backend_impl.h"
 #include "net/disk_cache/disk_cache_test_base.h"
 #include "net/disk_cache/disk_cache_test_util.h"
+#include "net/disk_cache/histogram_macros.h"
 #include "net/disk_cache/mapped_file.h"
+#include "net/disk_cache/mem_backend_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::Time;
 
-namespace {
-
-// Copies a set of cache files from the data folder to the test folder.
-bool CopyTestCache(const std::wstring& name) {
-  FilePath path;
-  PathService::Get(base::DIR_SOURCE_ROOT, &path);
-  path = path.AppendASCII("net");
-  path = path.AppendASCII("data");
-  path = path.AppendASCII("cache_tests");
-  path = path.Append(FilePath::FromWStringHack(name));
-
-  FilePath dest = GetCacheFilePath();
-  if (!DeleteCache(dest))
-    return false;
-  return file_util::CopyDirectory(path, dest, false);
-}
-
-}  // namespace
-
 // Tests that can run with different types of caches.
 class DiskCacheBackendTest : public DiskCacheTestWithCache {
  protected:
@@ -56,11 +38,11 @@
   void BackendFixEnumerators();
   void BackendDoomRecent();
   void BackendDoomBetween();
-  void BackendTransaction(const std::wstring& name, int num_entries, bool load);
+  void BackendTransaction(const std::string& name, int num_entries, bool load);
   void BackendRecoverInsert();
   void BackendRecoverRemove();
   void BackendInvalidEntry2();
-  void BackendNotMarkedButDirty(const std::wstring& name);
+  void BackendNotMarkedButDirty(const std::string& name);
   void BackendDoomAll();
   void BackendDoomAll2();
   void BackendInvalidRankings();
@@ -74,45 +56,45 @@
 void DiskCacheBackendTest::BackendBasics() {
   InitCache();
   disk_cache::Entry *entry1 = NULL, *entry2 = NULL;
-  EXPECT_FALSE(cache_->OpenEntry("the first key", &entry1));
-  ASSERT_TRUE(cache_->CreateEntry("the first key", &entry1));
+  EXPECT_NE(net::OK, OpenEntry("the first key", &entry1));
+  ASSERT_EQ(net::OK, CreateEntry("the first key", &entry1));
   ASSERT_TRUE(NULL != entry1);
   entry1->Close();
   entry1 = NULL;
 
-  ASSERT_TRUE(cache_->OpenEntry("the first key", &entry1));
+  ASSERT_EQ(net::OK, OpenEntry("the first key", &entry1));
   ASSERT_TRUE(NULL != entry1);
   entry1->Close();
   entry1 = NULL;
 
-  EXPECT_FALSE(cache_->CreateEntry("the first key", &entry1));
-  ASSERT_TRUE(cache_->OpenEntry("the first key", &entry1));
-  EXPECT_FALSE(cache_->OpenEntry("some other key", &entry2));
-  ASSERT_TRUE(cache_->CreateEntry("some other key", &entry2));
+  EXPECT_NE(net::OK, CreateEntry("the first key", &entry1));
+  ASSERT_EQ(net::OK, OpenEntry("the first key", &entry1));
+  EXPECT_NE(net::OK, OpenEntry("some other key", &entry2));
+  ASSERT_EQ(net::OK, CreateEntry("some other key", &entry2));
   ASSERT_TRUE(NULL != entry1);
   ASSERT_TRUE(NULL != entry2);
   EXPECT_EQ(2, cache_->GetEntryCount());
 
   disk_cache::Entry* entry3 = NULL;
-  ASSERT_TRUE(cache_->OpenEntry("some other key", &entry3));
+  ASSERT_EQ(net::OK, OpenEntry("some other key", &entry3));
   ASSERT_TRUE(NULL != entry3);
   EXPECT_TRUE(entry2 == entry3);
   EXPECT_EQ(2, cache_->GetEntryCount());
 
-  EXPECT_TRUE(cache_->DoomEntry("some other key"));
+  EXPECT_EQ(net::OK, DoomEntry("some other key"));
   EXPECT_EQ(1, cache_->GetEntryCount());
   entry1->Close();
   entry2->Close();
   entry3->Close();
 
-  EXPECT_TRUE(cache_->DoomEntry("the first key"));
+  EXPECT_EQ(net::OK, DoomEntry("the first key"));
   EXPECT_EQ(0, cache_->GetEntryCount());
 
-  ASSERT_TRUE(cache_->CreateEntry("the first key", &entry1));
-  ASSERT_TRUE(cache_->CreateEntry("some other key", &entry2));
+  ASSERT_EQ(net::OK, CreateEntry("the first key", &entry1));
+  ASSERT_EQ(net::OK, CreateEntry("some other key", &entry2));
   entry1->Doom();
   entry1->Close();
-  EXPECT_TRUE(cache_->DoomEntry("some other key"));
+  EXPECT_EQ(net::OK, DoomEntry("some other key"));
   EXPECT_EQ(0, cache_->GetEntryCount());
   entry2->Close();
 }
@@ -136,25 +118,25 @@
   const char* kName1 = "the first key";
   const char* kName2 = "the first Key";
   disk_cache::Entry *entry1, *entry2;
-  ASSERT_TRUE(cache_->CreateEntry(kName1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(kName1, &entry1));
 
-  ASSERT_TRUE(cache_->CreateEntry(kName2, &entry2));
+  ASSERT_EQ(net::OK, CreateEntry(kName2, &entry2));
   EXPECT_TRUE(entry1 != entry2) << "Case sensitive";
   entry2->Close();
 
   char buffer[30];
   base::strlcpy(buffer, kName1, arraysize(buffer));
-  ASSERT_TRUE(cache_->OpenEntry(buffer, &entry2));
+  ASSERT_EQ(net::OK, OpenEntry(buffer, &entry2));
   EXPECT_TRUE(entry1 == entry2);
   entry2->Close();
 
   base::strlcpy(buffer + 1, kName1, arraysize(buffer) - 1);
-  ASSERT_TRUE(cache_->OpenEntry(buffer + 1, &entry2));
+  ASSERT_EQ(net::OK, OpenEntry(buffer + 1, &entry2));
   EXPECT_TRUE(entry1 == entry2);
   entry2->Close();
 
   base::strlcpy(buffer + 3,  kName1, arraysize(buffer) - 3);
-  ASSERT_TRUE(cache_->OpenEntry(buffer + 3, &entry2));
+  ASSERT_EQ(net::OK, OpenEntry(buffer + 3, &entry2));
   EXPECT_TRUE(entry1 == entry2);
   entry2->Close();
 
@@ -162,12 +144,12 @@
   char buffer2[20000];
   memset(buffer2, 's', sizeof(buffer2));
   buffer2[1023] = '\0';
-  ASSERT_TRUE(cache_->CreateEntry(buffer2, &entry2)) << "key on block file";
+  ASSERT_EQ(net::OK, CreateEntry(buffer2, &entry2)) << "key on block file";
   entry2->Close();
 
   buffer2[1023] = 'g';
   buffer2[19999] = '\0';
-  ASSERT_TRUE(cache_->CreateEntry(buffer2, &entry2)) << "key on external file";
+  ASSERT_EQ(net::OK, CreateEntry(buffer2, &entry2)) << "key on external file";
   entry2->Close();
   entry1->Close();
 }
@@ -186,6 +168,49 @@
   BackendKeying();
 }
 
+TEST_F(DiskCacheTest, CreateBackend) {
+  TestCompletionCallback cb;
+
+  {
+    FilePath path = GetCacheFilePath();
+    ASSERT_TRUE(DeleteCache(path));
+    base::Thread cache_thread("CacheThread");
+    ASSERT_TRUE(cache_thread.StartWithOptions(
+                    base::Thread::Options(MessageLoop::TYPE_IO, 0)));
+
+    // Test the private factory methods.
+    disk_cache::Backend* cache = NULL;
+    int rv = disk_cache::BackendImpl::CreateBackend(
+                 path, false, 0, net::DISK_CACHE, disk_cache::kNoRandom,
+                 cache_thread.message_loop_proxy(), &cache, &cb);
+    ASSERT_EQ(net::OK, cb.GetResult(rv));
+    ASSERT_TRUE(cache);
+    delete cache;
+
+    cache = disk_cache::MemBackendImpl::CreateBackend(0);
+    ASSERT_TRUE(cache);
+    delete cache;
+    cache = NULL;
+
+    // Now test the public API.
+    rv = disk_cache::CreateCacheBackend(net::DISK_CACHE, path, 0, false,
+                                        cache_thread.message_loop_proxy(),
+                                        &cache, &cb);
+    ASSERT_EQ(net::OK, cb.GetResult(rv));
+    ASSERT_TRUE(cache);
+    delete cache;
+    cache = NULL;
+
+    rv = disk_cache::CreateCacheBackend(net::MEMORY_CACHE, FilePath(), 0, false,
+                                        NULL, &cache, &cb);
+    ASSERT_EQ(net::OK, cb.GetResult(rv));
+    ASSERT_TRUE(cache);
+    delete cache;
+  }
+
+  MessageLoop::current()->RunAllPending();
+}
+
 TEST_F(DiskCacheBackendTest, ExternalFiles) {
   InitCache();
   // First, lets create a file on the folder.
@@ -198,7 +223,7 @@
 
   // Now let's create a file with the cache.
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry("key", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("key", &entry));
   ASSERT_EQ(0, entry->WriteData(0, 20000, buffer1, 0, NULL, false));
   entry->Close();
 
@@ -208,39 +233,115 @@
   EXPECT_EQ(0, memcmp(buffer1->data(), buffer2->data(), kSize));
 }
 
+// Tests that we deal with file-level pending operations at destruction time.
 TEST_F(DiskCacheTest, ShutdownWithPendingIO) {
-  TestCompletionCallback callback;
+  TestCompletionCallback cb;
 
   {
     FilePath path = GetCacheFilePath();
     ASSERT_TRUE(DeleteCache(path));
+    base::Thread cache_thread("CacheThread");
+    ASSERT_TRUE(cache_thread.StartWithOptions(
+                    base::Thread::Options(MessageLoop::TYPE_IO, 0)));
 
-    disk_cache::Backend* cache =
-        disk_cache::CreateCacheBackend(path, false, 0, net::DISK_CACHE);
+    disk_cache::Backend* cache;
+    int rv = disk_cache::BackendImpl::CreateBackend(
+                 path, false, 0, net::DISK_CACHE, disk_cache::kNoRandom,
+                 base::MessageLoopProxy::CreateForCurrentThread(), &cache, &cb);
+    ASSERT_EQ(net::OK, cb.GetResult(rv));
 
-    disk_cache::Entry* entry;
-    ASSERT_TRUE(cache->CreateEntry("some key", &entry));
+    disk_cache::EntryImpl* entry;
+    rv = cache->CreateEntry("some key",
+                            reinterpret_cast<disk_cache::Entry**>(&entry), &cb);
+    ASSERT_EQ(net::OK, cb.GetResult(rv));
 
     const int kSize = 25000;
     scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kSize);
     CacheTestFillBuffer(buffer->data(), kSize, false);
 
     for (int i = 0; i < 10 * 1024 * 1024; i += 64 * 1024) {
-      int rv = entry->WriteData(0, i, buffer, kSize, &callback, false);
+      // We are using the current thread as the cache thread because we want to
+      // be able to call directly this method to make sure that the OS (instead
+      // of us switching thread) is returning IO pending.
+      rv = entry->WriteDataImpl(0, i, buffer, kSize, &cb, false);
       if (rv == net::ERR_IO_PENDING)
         break;
       EXPECT_EQ(kSize, rv);
     }
 
-    entry->Close();
+    // Don't call Close() to avoid going through the queue or we'll deadlock
+    // waiting for the operation to finish.
+    entry->Release();
 
     // The cache destructor will see one pending operation here.
     delete cache;
+
+    if (rv == net::ERR_IO_PENDING) {
+      EXPECT_TRUE(cb.have_result());
+    }
+  }
+
+  MessageLoop::current()->RunAllPending();
+}
+
+// Tests that we deal with background-thread pending operations.
+TEST_F(DiskCacheTest, ShutdownWithPendingIO2) {
+  TestCompletionCallback cb;
+
+  {
+    FilePath path = GetCacheFilePath();
+    ASSERT_TRUE(DeleteCache(path));
+    base::Thread cache_thread("CacheThread");
+    ASSERT_TRUE(cache_thread.StartWithOptions(
+                    base::Thread::Options(MessageLoop::TYPE_IO, 0)));
+
+    disk_cache::Backend* cache;
+    int rv = disk_cache::BackendImpl::CreateBackend(
+                 path, false, 0, net::DISK_CACHE, disk_cache::kNoRandom,
+                 cache_thread.message_loop_proxy(), &cache, &cb);
+    ASSERT_EQ(net::OK, cb.GetResult(rv));
+
+    disk_cache::Entry* entry;
+    rv = cache->CreateEntry("some key", &entry, &cb);
+    ASSERT_EQ(net::OK, cb.GetResult(rv));
+
+    const int kSize = 25000;
+    scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kSize);
+    CacheTestFillBuffer(buffer->data(), kSize, false);
+
+    rv = entry->WriteData(0, 0, buffer, kSize, &cb, false);
+    EXPECT_EQ(net::ERR_IO_PENDING, rv);
+
+    entry->Close();
+
+    // The cache destructor will see two pending operations here.
+    delete cache;
   }
 
   MessageLoop::current()->RunAllPending();
 }
 
+TEST_F(DiskCacheTest, TruncatedIndex) {
+  FilePath path = GetCacheFilePath();
+  ASSERT_TRUE(DeleteCache(path));
+  FilePath index = path.AppendASCII("index");
+  ASSERT_EQ(5, file_util::WriteFile(index, "hello", 5));
+
+  base::Thread cache_thread("CacheThread");
+  ASSERT_TRUE(cache_thread.StartWithOptions(
+                  base::Thread::Options(MessageLoop::TYPE_IO, 0)));
+  TestCompletionCallback cb;
+
+  disk_cache::Backend* backend = NULL;
+  int rv = disk_cache::BackendImpl::CreateBackend(
+               path, false, 0, net::DISK_CACHE, disk_cache::kNone,
+               cache_thread.message_loop_proxy(), &backend, &cb);
+  ASSERT_NE(net::OK, cb.GetResult(rv));
+
+  ASSERT_TRUE(backend == NULL);
+  delete backend;
+}
+
 void DiskCacheBackendTest::BackendSetSize() {
   SetDirectMode();
   const int cache_size = 0x10000;  // 64 kB
@@ -250,7 +351,7 @@
   std::string first("some key");
   std::string second("something else");
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry(first, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(first, &entry));
 
   scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(cache_size);
   memset(buffer->data(), 0, cache_size);
@@ -270,18 +371,29 @@
   EXPECT_EQ(cache_size * 3 / 4, entry->WriteData(0, 0, buffer,
                 cache_size * 3 / 4, NULL, false));
   entry->Close();
+  FlushQueueForTest();
 
   SetMaxSize(cache_size);
 
   // The cache is 95% full.
 
-  ASSERT_TRUE(cache_->CreateEntry(second, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(second, &entry));
   EXPECT_EQ(cache_size / 10, entry->WriteData(0, 0, buffer, cache_size / 10,
-                                              NULL, false)) << "trim the cache";
-  entry->Close();
+                                              NULL, false));
 
-  EXPECT_FALSE(cache_->OpenEntry(first, &entry));
-  ASSERT_TRUE(cache_->OpenEntry(second, &entry));
+  disk_cache::Entry* entry2;
+  ASSERT_EQ(net::OK, CreateEntry("an extra key", &entry2));
+  EXPECT_EQ(cache_size / 10, entry2->WriteData(0, 0, buffer, cache_size / 10,
+                                               NULL, false));
+  entry2->Close();  // This will trigger the cache trim.
+
+  EXPECT_NE(net::OK, OpenEntry(first, &entry2));
+
+  FlushQueueForTest();  // Make sure that we are done trimming the cache.
+  FlushQueueForTest();  // We may have posted two tasks to evict stuff.
+
+  entry->Close();
+  ASSERT_EQ(net::OK, OpenEntry(second, &entry));
   EXPECT_EQ(cache_size / 10, entry->GetDataSize(0));
   entry->Close();
 }
@@ -308,7 +420,7 @@
   disk_cache::Entry* entries[100];
   for (int i = 0; i < 100; i++) {
     std::string key = GenerateKey(true);
-    ASSERT_TRUE(cache_->CreateEntry(key, &entries[i]));
+    ASSERT_EQ(net::OK, CreateEntry(key, &entries[i]));
   }
   EXPECT_EQ(100, cache_->GetEntryCount());
 
@@ -322,12 +434,13 @@
 
   for (int i = 0; i < 100; i++) {
     disk_cache::Entry* entry;
-    ASSERT_TRUE(cache_->OpenEntry(entries[i]->GetKey(), &entry));
+    ASSERT_EQ(net::OK, OpenEntry(entries[i]->GetKey(), &entry));
     EXPECT_TRUE(entry == entries[i]);
     entry->Close();
     entries[i]->Doom();
     entries[i]->Close();
   }
+  FlushQueueForTest();
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
@@ -360,7 +473,7 @@
 
   std::string key("Some key");
   disk_cache::Entry* entry1;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry1));
 
   const int kSize = 50;
   scoped_refptr<net::IOBuffer> buffer1 = new net::IOBuffer(kSize);
@@ -370,7 +483,7 @@
   entry1->Close();
   SimulateCrash();
 
-  ASSERT_TRUE(cache_->OpenEntry(key, &entry1));
+  ASSERT_EQ(net::OK, OpenEntry(key, &entry1));
 
   scoped_refptr<net::IOBuffer> buffer2 = new net::IOBuffer(kSize);
   memset(buffer2->data(), 0, kSize);
@@ -398,7 +511,7 @@
 
   std::string key("Some key");
   disk_cache::Entry* entry1;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry1));
 
   const int kSize = 50;
   scoped_refptr<net::IOBuffer> buffer1 = new net::IOBuffer(kSize);
@@ -407,7 +520,7 @@
   EXPECT_EQ(kSize, entry1->WriteData(0, 0, buffer1, kSize, NULL, false));
   SimulateCrash();
 
-  EXPECT_FALSE(cache_->OpenEntry(key, &entry1));
+  EXPECT_NE(net::OK, OpenEntry(key, &entry1));
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
@@ -436,7 +549,7 @@
 
   std::string key("Some key");
   disk_cache::Entry* entry1;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry1));
 
   const int kSize = 50;
   scoped_refptr<net::IOBuffer> buffer1 = new net::IOBuffer(kSize);
@@ -444,12 +557,12 @@
   base::strlcpy(buffer1->data(), "And the data to save", kSize);
   EXPECT_EQ(kSize, entry1->WriteData(0, 0, buffer1, kSize, NULL, false));
   entry1->Close();
-  ASSERT_TRUE(cache_->OpenEntry(key, &entry1));
+  ASSERT_EQ(net::OK, OpenEntry(key, &entry1));
   EXPECT_EQ(kSize, entry1->ReadData(0, 0, buffer1, kSize, NULL));
 
   SimulateCrash();
 
-  EXPECT_FALSE(cache_->OpenEntry(key, &entry1));
+  EXPECT_NE(net::OK, OpenEntry(key, &entry1));
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
@@ -478,7 +591,7 @@
   disk_cache::Entry* entries[kNumEntries];
   for (int i = 0; i < kNumEntries; i++) {
     std::string key = GenerateKey(true);
-    ASSERT_TRUE(cache_->CreateEntry(key, &entries[i]));
+    ASSERT_EQ(net::OK, CreateEntry(key, &entries[i]));
   }
   EXPECT_EQ(kNumEntries, cache_->GetEntryCount());
 
@@ -501,12 +614,12 @@
 
   for (int i = kNumEntries / 2; i < kNumEntries; i++) {
     disk_cache::Entry* entry;
-    EXPECT_FALSE(cache_->OpenEntry(keys[i], &entry));
+    EXPECT_NE(net::OK, OpenEntry(keys[i], &entry));
   }
 
   for (int i = 0; i < kNumEntries / 2; i++) {
     disk_cache::Entry* entry;
-    EXPECT_TRUE(cache_->OpenEntry(keys[i], &entry));
+    EXPECT_EQ(net::OK, OpenEntry(keys[i], &entry));
     entry->Close();
   }
 
@@ -536,7 +649,7 @@
   std::string first("some key");
   std::string second("something else");
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry(first, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(first, &entry));
 
   scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kSize);
   memset(buffer->data(), 0, kSize);
@@ -545,19 +658,20 @@
   // Simulate a crash.
   SimulateCrash();
 
-  ASSERT_TRUE(cache_->CreateEntry(second, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(second, &entry));
   EXPECT_EQ(kSize, entry->WriteData(0, 0, buffer, kSize, NULL, false));
 
   EXPECT_EQ(2, cache_->GetEntryCount());
   SetMaxSize(kSize);
   entry->Close();  // Trim the cache.
+  FlushQueueForTest();
 
   // If we evicted the entry in less than 20mS, we have one entry in the cache;
   // if it took more than that, we posted a task and we'll delete the second
   // entry too.
   MessageLoop::current()->RunAllPending();
   EXPECT_GE(1, cache_->GetEntryCount());
-  EXPECT_FALSE(cache_->OpenEntry(first, &entry));
+  EXPECT_NE(net::OK, OpenEntry(first, &entry));
 }
 
 // We'll be leaking memory from this test.
@@ -588,17 +702,17 @@
   // Writing 32 entries to this cache chains most of them.
   for (int i = 0; i < 32; i++) {
     std::string key(StringPrintf("some key %d", i));
-    ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+    ASSERT_EQ(net::OK, CreateEntry(key, &entry));
     EXPECT_EQ(kSize, entry->WriteData(0, 0, buffer, kSize, NULL, false));
     entry->Close();
-    ASSERT_TRUE(cache_->OpenEntry(key, &entry));
+    ASSERT_EQ(net::OK, OpenEntry(key, &entry));
     // Note that we are not closing the entries.
   }
 
   // Simulate a crash.
   SimulateCrash();
 
-  ASSERT_TRUE(cache_->CreateEntry("Something else", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("Something else", &entry));
   EXPECT_EQ(kSize, entry->WriteData(0, 0, buffer, kSize, NULL, false));
 
   EXPECT_EQ(33, cache_->GetEntryCount());
@@ -606,10 +720,12 @@
 
   // For the new eviction code, all corrupt entries are on the second list so
   // they are not going away that easy.
-  if (new_eviction_)
-    cache_->DoomAllEntries();
+  if (new_eviction_) {
+    EXPECT_EQ(net::OK, DoomAllEntries());
+  }
 
   entry->Close();  // Trim the cache.
+  FlushQueueForTest();
 
   // We may abort the eviction before cleaning up everything.
   MessageLoop::current()->RunAllPending();
@@ -637,7 +753,7 @@
   for (int i = 0; i < kNumEntries; i++) {
     std::string key = GenerateKey(true);
     disk_cache::Entry* entry;
-    ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+    ASSERT_EQ(net::OK, CreateEntry(key, &entry));
     entry->Close();
   }
   EXPECT_EQ(kNumEntries, cache_->GetEntryCount());
@@ -648,7 +764,7 @@
   int count = 0;
   Time last_modified[kNumEntries];
   Time last_used[kNumEntries];
-  while (cache_->OpenNextEntry(&iter, &entry)) {
+  while (OpenNextEntry(&iter, &entry) == net::OK) {
     ASSERT_TRUE(NULL != entry);
     if (count < kNumEntries) {
       last_modified[count] = entry->GetLastModified();
@@ -665,7 +781,7 @@
   iter = NULL;
   count = 0;
   // The previous enumeration should not have changed the timestamps.
-  while (cache_->OpenNextEntry(&iter, &entry)) {
+  while (OpenNextEntry(&iter, &entry) == net::OK) {
     ASSERT_TRUE(NULL != entry);
     if (count < kNumEntries) {
       EXPECT_TRUE(last_modified[count] == entry->GetLastModified());
@@ -697,24 +813,24 @@
   const std::string first("first");
   const std::string second("second");
   disk_cache::Entry *entry1, *entry2;
-  ASSERT_TRUE(cache_->CreateEntry(first, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(first, &entry1));
   entry1->Close();
-  ASSERT_TRUE(cache_->CreateEntry(second, &entry2));
+  ASSERT_EQ(net::OK, CreateEntry(second, &entry2));
   entry2->Close();
 
   // Make sure that the timestamp is not the same.
   PlatformThread::Sleep(20);
-  ASSERT_TRUE(cache_->OpenEntry(second, &entry1));
+  ASSERT_EQ(net::OK, OpenEntry(second, &entry1));
   void* iter = NULL;
-  ASSERT_TRUE(cache_->OpenNextEntry(&iter, &entry2));
+  ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry2));
   ASSERT_EQ(entry2->GetKey(), second);
 
   // Two entries and the iterator pointing at "first".
   entry1->Close();
   entry2->Close();
 
-  // The iterator should still be valid, se we should not crash.
-  ASSERT_TRUE(cache_->OpenNextEntry(&iter, &entry2));
+  // The iterator should still be valid, so we should not crash.
+  ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry2));
   ASSERT_EQ(entry2->GetKey(), first);
   entry2->Close();
   cache_->EndEnumeration(&iter);
@@ -744,7 +860,7 @@
 
   std::string key("Some key");
   disk_cache::Entry *entry, *entry1, *entry2;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry1));
 
   const int kSize = 50;
   scoped_refptr<net::IOBuffer> buffer1 = new net::IOBuffer(kSize);
@@ -752,11 +868,11 @@
   base::strlcpy(buffer1->data(), "And the data to save", kSize);
   EXPECT_EQ(kSize, entry1->WriteData(0, 0, buffer1, kSize, NULL, false));
   entry1->Close();
-  ASSERT_TRUE(cache_->OpenEntry(key, &entry1));
+  ASSERT_EQ(net::OK, OpenEntry(key, &entry1));
   EXPECT_EQ(kSize, entry1->ReadData(0, 0, buffer1, kSize, NULL));
 
   std::string key2("Another key");
-  ASSERT_TRUE(cache_->CreateEntry(key2, &entry2));
+  ASSERT_EQ(net::OK, CreateEntry(key2, &entry2));
   entry2->Close();
   ASSERT_EQ(2, cache_->GetEntryCount());
 
@@ -764,7 +880,7 @@
 
   void* iter = NULL;
   int count = 0;
-  while (cache_->OpenNextEntry(&iter, &entry)) {
+  while (OpenNextEntry(&iter, &entry) == net::OK) {
     ASSERT_TRUE(NULL != entry);
     EXPECT_EQ(key2, entry->GetKey());
     entry->Close();
@@ -797,7 +913,7 @@
   for (int i = 0; i < kNumEntries; i++) {
     std::string key = GenerateKey(true);
     disk_cache::Entry* entry;
-    ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+    ASSERT_EQ(net::OK, CreateEntry(key, &entry));
     entry->Close();
   }
   EXPECT_EQ(kNumEntries, cache_->GetEntryCount());
@@ -805,7 +921,7 @@
   disk_cache::Entry *entry1, *entry2;
   void* iter1 = NULL;
   void* iter2 = NULL;
-  ASSERT_TRUE(cache_->OpenNextEntry(&iter1, &entry1));
+  ASSERT_EQ(net::OK, OpenNextEntry(&iter1, &entry1));
   ASSERT_TRUE(NULL != entry1);
   entry1->Close();
   entry1 = NULL;
@@ -814,17 +930,17 @@
   for (int i = 0; i < kNumEntries / 2; i++) {
     if (entry1)
       entry1->Close();
-    ASSERT_TRUE(cache_->OpenNextEntry(&iter1, &entry1));
+    ASSERT_EQ(net::OK, OpenNextEntry(&iter1, &entry1));
     ASSERT_TRUE(NULL != entry1);
 
-    ASSERT_TRUE(cache_->OpenNextEntry(&iter2, &entry2));
+    ASSERT_EQ(net::OK, OpenNextEntry(&iter2, &entry2));
     ASSERT_TRUE(NULL != entry2);
     entry2->Close();
   }
 
   // Messing up with entry1 will modify entry2->next.
   entry1->Doom();
-  ASSERT_TRUE(cache_->OpenNextEntry(&iter2, &entry2));
+  ASSERT_EQ(net::OK, OpenNextEntry(&iter2, &entry2));
   ASSERT_TRUE(NULL != entry2);
 
   // The link entry2->entry1 should be broken.
@@ -833,7 +949,7 @@
   entry2->Close();
 
   // And the second iterator should keep working.
-  ASSERT_TRUE(cache_->OpenNextEntry(&iter2, &entry2));
+  ASSERT_EQ(net::OK, OpenNextEntry(&iter2, &entry2));
   ASSERT_TRUE(NULL != entry2);
   entry2->Close();
 
@@ -855,30 +971,30 @@
   Time initial = Time::Now();
 
   disk_cache::Entry *entry;
-  ASSERT_TRUE(cache_->CreateEntry("first", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("first", &entry));
   entry->Close();
-  ASSERT_TRUE(cache_->CreateEntry("second", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("second", &entry));
   entry->Close();
 
   PlatformThread::Sleep(20);
   Time middle = Time::Now();
 
-  ASSERT_TRUE(cache_->CreateEntry("third", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("third", &entry));
   entry->Close();
-  ASSERT_TRUE(cache_->CreateEntry("fourth", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("fourth", &entry));
   entry->Close();
 
   PlatformThread::Sleep(20);
   Time final = Time::Now();
 
   ASSERT_EQ(4, cache_->GetEntryCount());
-  EXPECT_TRUE(cache_->DoomEntriesSince(final));
+  EXPECT_EQ(net::OK, DoomEntriesSince(final));
   ASSERT_EQ(4, cache_->GetEntryCount());
 
-  EXPECT_TRUE(cache_->DoomEntriesSince(middle));
+  EXPECT_EQ(net::OK, DoomEntriesSince(middle));
   ASSERT_EQ(2, cache_->GetEntryCount());
 
-  ASSERT_TRUE(cache_->OpenEntry("second", &entry));
+  ASSERT_EQ(net::OK, OpenEntry("second", &entry));
   entry->Close();
 }
 
@@ -901,39 +1017,39 @@
   Time initial = Time::Now();
 
   disk_cache::Entry *entry;
-  ASSERT_TRUE(cache_->CreateEntry("first", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("first", &entry));
   entry->Close();
 
   PlatformThread::Sleep(20);
   Time middle_start = Time::Now();
 
-  ASSERT_TRUE(cache_->CreateEntry("second", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("second", &entry));
   entry->Close();
-  ASSERT_TRUE(cache_->CreateEntry("third", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("third", &entry));
   entry->Close();
 
   PlatformThread::Sleep(20);
   Time middle_end = Time::Now();
 
-  ASSERT_TRUE(cache_->CreateEntry("fourth", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("fourth", &entry));
   entry->Close();
-  ASSERT_TRUE(cache_->OpenEntry("fourth", &entry));
+  ASSERT_EQ(net::OK, OpenEntry("fourth", &entry));
   entry->Close();
 
   PlatformThread::Sleep(20);
   Time final = Time::Now();
 
   ASSERT_EQ(4, cache_->GetEntryCount());
-  EXPECT_TRUE(cache_->DoomEntriesBetween(middle_start, middle_end));
+  EXPECT_EQ(net::OK, DoomEntriesBetween(middle_start, middle_end));
   ASSERT_EQ(2, cache_->GetEntryCount());
 
-  ASSERT_TRUE(cache_->OpenEntry("fourth", &entry));
+  ASSERT_EQ(net::OK, OpenEntry("fourth", &entry));
   entry->Close();
 
-  EXPECT_TRUE(cache_->DoomEntriesBetween(middle_start, final));
+  EXPECT_EQ(net::OK, DoomEntriesBetween(middle_start, final));
   ASSERT_EQ(1, cache_->GetEntryCount());
 
-  ASSERT_TRUE(cache_->OpenEntry("first", &entry));
+  ASSERT_EQ(net::OK, OpenEntry("first", &entry));
   entry->Close();
 }
 
@@ -951,7 +1067,7 @@
   BackendDoomBetween();
 }
 
-void DiskCacheBackendTest::BackendTransaction(const std::wstring& name,
+void DiskCacheBackendTest::BackendTransaction(const std::string& name,
                                               int num_entries, bool load) {
   success_ = false;
   ASSERT_TRUE(CopyTestCache(name));
@@ -971,7 +1087,7 @@
 
   std::string key("the first key");
   disk_cache::Entry* entry1;
-  ASSERT_FALSE(cache_->OpenEntry(key, &entry1));
+  ASSERT_NE(net::OK, OpenEntry(key, &entry1));
 
   int actual = cache_->GetEntryCount();
   if (num_entries != actual) {
@@ -991,25 +1107,25 @@
 
 void DiskCacheBackendTest::BackendRecoverInsert() {
   // Tests with an empty cache.
-  BackendTransaction(L"insert_empty1", 0, false);
+  BackendTransaction("insert_empty1", 0, false);
   ASSERT_TRUE(success_) << "insert_empty1";
-  BackendTransaction(L"insert_empty2", 0, false);
+  BackendTransaction("insert_empty2", 0, false);
   ASSERT_TRUE(success_) << "insert_empty2";
-  BackendTransaction(L"insert_empty3", 0, false);
+  BackendTransaction("insert_empty3", 0, false);
   ASSERT_TRUE(success_) << "insert_empty3";
 
   // Tests with one entry on the cache.
-  BackendTransaction(L"insert_one1", 1, false);
+  BackendTransaction("insert_one1", 1, false);
   ASSERT_TRUE(success_) << "insert_one1";
-  BackendTransaction(L"insert_one2", 1, false);
+  BackendTransaction("insert_one2", 1, false);
   ASSERT_TRUE(success_) << "insert_one2";
-  BackendTransaction(L"insert_one3", 1, false);
+  BackendTransaction("insert_one3", 1, false);
   ASSERT_TRUE(success_) << "insert_one3";
 
   // Tests with one hundred entries on the cache, tiny index.
-  BackendTransaction(L"insert_load1", 100, true);
+  BackendTransaction("insert_load1", 100, true);
   ASSERT_TRUE(success_) << "insert_load1";
-  BackendTransaction(L"insert_load2", 100, true);
+  BackendTransaction("insert_load2", 100, true);
   ASSERT_TRUE(success_) << "insert_load2";
 }
 
@@ -1024,42 +1140,42 @@
 
 void DiskCacheBackendTest::BackendRecoverRemove() {
   // Removing the only element.
-  BackendTransaction(L"remove_one1", 0, false);
+  BackendTransaction("remove_one1", 0, false);
   ASSERT_TRUE(success_) << "remove_one1";
-  BackendTransaction(L"remove_one2", 0, false);
+  BackendTransaction("remove_one2", 0, false);
   ASSERT_TRUE(success_) << "remove_one2";
-  BackendTransaction(L"remove_one3", 0, false);
+  BackendTransaction("remove_one3", 0, false);
   ASSERT_TRUE(success_) << "remove_one3";
 
   // Removing the head.
-  BackendTransaction(L"remove_head1", 1, false);
+  BackendTransaction("remove_head1", 1, false);
   ASSERT_TRUE(success_) << "remove_head1";
-  BackendTransaction(L"remove_head2", 1, false);
+  BackendTransaction("remove_head2", 1, false);
   ASSERT_TRUE(success_) << "remove_head2";
-  BackendTransaction(L"remove_head3", 1, false);
+  BackendTransaction("remove_head3", 1, false);
   ASSERT_TRUE(success_) << "remove_head3";
 
   // Removing the tail.
-  BackendTransaction(L"remove_tail1", 1, false);
+  BackendTransaction("remove_tail1", 1, false);
   ASSERT_TRUE(success_) << "remove_tail1";
-  BackendTransaction(L"remove_tail2", 1, false);
+  BackendTransaction("remove_tail2", 1, false);
   ASSERT_TRUE(success_) << "remove_tail2";
-  BackendTransaction(L"remove_tail3", 1, false);
+  BackendTransaction("remove_tail3", 1, false);
   ASSERT_TRUE(success_) << "remove_tail3";
 
   // Removing with one hundred entries on the cache, tiny index.
-  BackendTransaction(L"remove_load1", 100, true);
+  BackendTransaction("remove_load1", 100, true);
   ASSERT_TRUE(success_) << "remove_load1";
-  BackendTransaction(L"remove_load2", 100, true);
+  BackendTransaction("remove_load2", 100, true);
   ASSERT_TRUE(success_) << "remove_load2";
-  BackendTransaction(L"remove_load3", 100, true);
+  BackendTransaction("remove_load3", 100, true);
   ASSERT_TRUE(success_) << "remove_load3";
 
 #ifdef NDEBUG
   // This case cannot be reverted, so it will assert on debug builds.
-  BackendTransaction(L"remove_one4", 0, false);
+  BackendTransaction("remove_one4", 0, false);
   ASSERT_TRUE(success_) << "remove_one4";
-  BackendTransaction(L"remove_head4", 1, false);
+  BackendTransaction("remove_head4", 1, false);
   ASSERT_TRUE(success_) << "remove_head4";
 #endif
 }
@@ -1074,31 +1190,37 @@
 }
 
 // Tests dealing with cache files that cannot be recovered.
-TEST_F(DiskCacheTest, Backend_DeleteOld) {
-  ASSERT_TRUE(CopyTestCache(L"wrong_version"));
+TEST_F(DiskCacheTest, DeleteOld) {
+  ASSERT_TRUE(CopyTestCache("wrong_version"));
   FilePath path = GetCacheFilePath();
-  scoped_ptr<disk_cache::Backend> cache;
-  cache.reset(disk_cache::CreateCacheBackend(path, true, 0, net::DISK_CACHE));
+  base::Thread cache_thread("CacheThread");
+  ASSERT_TRUE(cache_thread.StartWithOptions(
+                  base::Thread::Options(MessageLoop::TYPE_IO, 0)));
+  TestCompletionCallback cb;
+
+  disk_cache::Backend* cache;
+  int rv = disk_cache::BackendImpl::CreateBackend(
+               path, true, 0, net::DISK_CACHE, disk_cache::kNoRandom,
+               cache_thread.message_loop_proxy(), &cache, &cb);
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
 
   MessageLoopHelper helper;
 
-  ASSERT_TRUE(NULL != cache.get());
+  ASSERT_TRUE(NULL != cache);
   ASSERT_EQ(0, cache->GetEntryCount());
 
-  // Wait for a callback that never comes... about 2 secs :). The message loop
-  // has to run to allow destruction of the cleaner thread.
-  helper.WaitUntilCacheIoFinished(1);
+  delete cache;
 }
 
 // We want to be able to deal with messed up entries on disk.
 void DiskCacheBackendTest::BackendInvalidEntry2() {
-  ASSERT_TRUE(CopyTestCache(L"bad_entry"));
+  ASSERT_TRUE(CopyTestCache("bad_entry"));
   DisableFirstCleanup();
   InitCache();
 
   disk_cache::Entry *entry1, *entry2;
-  ASSERT_TRUE(cache_->OpenEntry("the first key", &entry1));
-  EXPECT_FALSE(cache_->OpenEntry("some other key", &entry2));
+  ASSERT_EQ(net::OK, OpenEntry("the first key", &entry1));
+  EXPECT_NE(net::OK, OpenEntry("some other key", &entry2));
   entry1->Close();
 
   // CheckCacheIntegrity will fail at this point.
@@ -1115,45 +1237,45 @@
 }
 
 // We want to be able to deal with abnormal dirty entries.
-void DiskCacheBackendTest::BackendNotMarkedButDirty(const std::wstring& name) {
+void DiskCacheBackendTest::BackendNotMarkedButDirty(const std::string& name) {
   ASSERT_TRUE(CopyTestCache(name));
   DisableFirstCleanup();
   InitCache();
 
   disk_cache::Entry *entry1, *entry2;
-  ASSERT_TRUE(cache_->OpenEntry("the first key", &entry1));
-  EXPECT_FALSE(cache_->OpenEntry("some other key", &entry2));
+  ASSERT_EQ(net::OK, OpenEntry("the first key", &entry1));
+  EXPECT_NE(net::OK, OpenEntry("some other key", &entry2));
   entry1->Close();
 }
 
 TEST_F(DiskCacheBackendTest, NotMarkedButDirty) {
-  BackendNotMarkedButDirty(L"dirty_entry");
+  BackendNotMarkedButDirty("dirty_entry");
 }
 
 TEST_F(DiskCacheBackendTest, NewEvictionNotMarkedButDirty) {
   SetNewEviction();
-  BackendNotMarkedButDirty(L"dirty_entry");
+  BackendNotMarkedButDirty("dirty_entry");
 }
 
 TEST_F(DiskCacheBackendTest, NotMarkedButDirty2) {
-  BackendNotMarkedButDirty(L"dirty_entry2");
+  BackendNotMarkedButDirty("dirty_entry2");
 }
 
 TEST_F(DiskCacheBackendTest, NewEvictionNotMarkedButDirty2) {
   SetNewEviction();
-  BackendNotMarkedButDirty(L"dirty_entry2");
+  BackendNotMarkedButDirty("dirty_entry2");
 }
 
 // We want to be able to deal with messed up entries on disk.
 void DiskCacheBackendTest::BackendInvalidRankings2() {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   FilePath path = GetCacheFilePath();
   DisableFirstCleanup();
   InitCache();
 
   disk_cache::Entry *entry1, *entry2;
-  EXPECT_FALSE(cache_->OpenEntry("the first key", &entry1));
-  ASSERT_TRUE(cache_->OpenEntry("some other key", &entry2));
+  EXPECT_NE(net::OK, OpenEntry("the first key", &entry1));
+  ASSERT_EQ(net::OK, OpenEntry("some other key", &entry2));
   entry2->Close();
 
   // CheckCacheIntegrity will fail at this point.
@@ -1173,17 +1295,17 @@
 void DiskCacheBackendTest::BackendInvalidRankings() {
   disk_cache::Entry* entry;
   void* iter = NULL;
-  ASSERT_TRUE(cache_->OpenNextEntry(&iter, &entry));
+  ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry));
   entry->Close();
   EXPECT_EQ(2, cache_->GetEntryCount());
 
-  EXPECT_FALSE(cache_->OpenNextEntry(&iter, &entry));
-  MessageLoop::current()->RunAllPending();
+  EXPECT_NE(net::OK, OpenNextEntry(&iter, &entry));
+  FlushQueueForTest();  // Allow the restart to finish.
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheBackendTest, InvalidRankingsSuccess) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   DisableFirstCleanup();
   SetDirectMode();
   InitCache();
@@ -1191,7 +1313,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, NewEvictionInvalidRankingsSuccess) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   DisableFirstCleanup();
   SetDirectMode();
   SetNewEviction();
@@ -1200,7 +1322,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, InvalidRankingsFailure) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   DisableFirstCleanup();
   SetDirectMode();
   InitCache();
@@ -1209,7 +1331,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, NewEvictionInvalidRankingsFailure) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   DisableFirstCleanup();
   SetDirectMode();
   SetNewEviction();
@@ -1222,20 +1344,21 @@
 void DiskCacheBackendTest::BackendDisable() {
   disk_cache::Entry *entry1, *entry2;
   void* iter = NULL;
-  ASSERT_TRUE(cache_->OpenNextEntry(&iter, &entry1));
+  ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry1));
 
-  EXPECT_FALSE(cache_->OpenNextEntry(&iter, &entry2));
+  EXPECT_NE(net::OK, OpenNextEntry(&iter, &entry2));
   EXPECT_EQ(2, cache_->GetEntryCount());
-  EXPECT_FALSE(cache_->CreateEntry("Something new", &entry2));
+  EXPECT_NE(net::OK, CreateEntry("Something new", &entry2));
 
   entry1->Close();
-  MessageLoop::current()->RunAllPending();
+  FlushQueueForTest();  // Flushing the Close posts a task to restart the cache.
+  FlushQueueForTest();  // This one actually allows that task to complete.
 
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheBackendTest, DisableSuccess) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   DisableFirstCleanup();
   SetDirectMode();
   InitCache();
@@ -1243,7 +1366,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, NewEvictionDisableSuccess) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   DisableFirstCleanup();
   SetDirectMode();
   SetNewEviction();
@@ -1252,7 +1375,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, DisableFailure) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   DisableFirstCleanup();
   SetDirectMode();
   InitCache();
@@ -1261,7 +1384,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, NewEvictionDisableFailure) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   DisableFirstCleanup();
   SetDirectMode();
   SetNewEviction();
@@ -1277,19 +1400,19 @@
   disk_cache::Entry* entry;
   void* iter = NULL;
   int count = 0;
-  while (cache_->OpenNextEntry(&iter, &entry)) {
+  while (OpenNextEntry(&iter, &entry) == net::OK) {
     ASSERT_TRUE(NULL != entry);
     entry->Close();
     count++;
     ASSERT_LT(count, 9);
   };
 
-  MessageLoop::current()->RunAllPending();
+  FlushQueueForTest();
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheBackendTest, DisableSuccess2) {
-  ASSERT_TRUE(CopyTestCache(L"list_loop"));
+  ASSERT_TRUE(CopyTestCache("list_loop"));
   DisableFirstCleanup();
   SetDirectMode();
   InitCache();
@@ -1297,7 +1420,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, NewEvictionDisableSuccess2) {
-  ASSERT_TRUE(CopyTestCache(L"list_loop"));
+  ASSERT_TRUE(CopyTestCache("list_loop"));
   DisableFirstCleanup();
   SetNewEviction();
   SetDirectMode();
@@ -1306,7 +1429,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, DisableFailure2) {
-  ASSERT_TRUE(CopyTestCache(L"list_loop"));
+  ASSERT_TRUE(CopyTestCache("list_loop"));
   DisableFirstCleanup();
   SetDirectMode();
   InitCache();
@@ -1315,7 +1438,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, NewEvictionDisableFailure2) {
-  ASSERT_TRUE(CopyTestCache(L"list_loop"));
+  ASSERT_TRUE(CopyTestCache("list_loop"));
   DisableFirstCleanup();
   SetDirectMode();
   SetNewEviction();
@@ -1329,20 +1452,20 @@
   disk_cache::Entry *entry1, *entry2;
   void* iter = NULL;
   EXPECT_EQ(2, cache_->GetEntryCount());
-  ASSERT_TRUE(cache_->OpenNextEntry(&iter, &entry1));
+  ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry1));
   entry1->Close();
 
-  EXPECT_FALSE(cache_->OpenNextEntry(&iter, &entry2));
-  MessageLoop::current()->RunAllPending();
+  EXPECT_NE(net::OK, OpenNextEntry(&iter, &entry2));
+  FlushQueueForTest();
 
-  ASSERT_TRUE(cache_->CreateEntry("Something new", &entry2));
+  ASSERT_EQ(net::OK, CreateEntry("Something new", &entry2));
   entry2->Close();
 
   EXPECT_EQ(1, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheBackendTest, DisableSuccess3) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings2"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings2"));
   DisableFirstCleanup();
   SetMaxSize(20 * 1024 * 1024);
   InitCache();
@@ -1350,7 +1473,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, NewEvictionDisableSuccess3) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings2"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings2"));
   DisableFirstCleanup();
   SetMaxSize(20 * 1024 * 1024);
   SetNewEviction();
@@ -1362,7 +1485,7 @@
 void DiskCacheBackendTest::BackendDisable4() {
   disk_cache::Entry *entry1, *entry2, *entry3, *entry4;
   void* iter = NULL;
-  ASSERT_TRUE(cache_->OpenNextEntry(&iter, &entry1));
+  ASSERT_EQ(net::OK, OpenNextEntry(&iter, &entry1));
 
   char key2[2000];
   char key3[20000];
@@ -1370,8 +1493,8 @@
   CacheTestFillBuffer(key3, sizeof(key3), true);
   key2[sizeof(key2) - 1] = '\0';
   key3[sizeof(key3) - 1] = '\0';
-  ASSERT_TRUE(cache_->CreateEntry(key2, &entry2));
-  ASSERT_TRUE(cache_->CreateEntry(key3, &entry3));
+  ASSERT_EQ(net::OK, CreateEntry(key2, &entry2));
+  ASSERT_EQ(net::OK, CreateEntry(key3, &entry3));
 
   const int kBufSize = 20000;
   scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kBufSize);
@@ -1380,10 +1503,10 @@
   EXPECT_EQ(kBufSize, entry3->WriteData(0, 0, buf, kBufSize, NULL, false));
 
   // This line should disable the cache but not delete it.
-  EXPECT_FALSE(cache_->OpenNextEntry(&iter, &entry4));
+  EXPECT_NE(net::OK, OpenNextEntry(&iter, &entry4));
   EXPECT_EQ(4, cache_->GetEntryCount());
 
-  EXPECT_FALSE(cache_->CreateEntry("cache is disabled", &entry4));
+  EXPECT_NE(net::OK, CreateEntry("cache is disabled", &entry4));
 
   EXPECT_EQ(100, entry2->ReadData(0, 0, buf, 100, NULL));
   EXPECT_EQ(100, entry2->WriteData(0, 0, buf, 100, NULL, false));
@@ -1401,23 +1524,22 @@
   entry1->Close();
   entry2->Close();
   entry3->Close();
-  MessageLoop::current()->RunAllPending();
+  FlushQueueForTest();  // Flushing the Close posts a task to restart the cache.
+  FlushQueueForTest();  // This one actually allows that task to complete.
 
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheBackendTest, DisableSuccess4) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   DisableFirstCleanup();
   SetDirectMode();
   InitCache();
   BackendDisable4();
 }
 
-// Flaky, http://crbug.com/21110.
-// TODO(rvargas): Add more debugging code to help identify the root cause.
-TEST_F(DiskCacheBackendTest, FLAKY_NewEvictionDisableSuccess4) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings"));
+TEST_F(DiskCacheBackendTest, NewEvictionDisableSuccess4) {
+  ASSERT_TRUE(CopyTestCache("bad_rankings"));
   DisableFirstCleanup();
   SetDirectMode();
   SetNewEviction();
@@ -1431,10 +1553,11 @@
   FilePath path = GetCacheFilePath();
   ASSERT_TRUE(DeleteCache(path));
   scoped_ptr<disk_cache::BackendImpl> cache;
-  cache.reset(new disk_cache::BackendImpl(path));
+  cache.reset(new disk_cache::BackendImpl(
+                  path, base::MessageLoopProxy::CreateForCurrentThread()));
   ASSERT_TRUE(NULL != cache.get());
   cache->SetUnitTestMode();
-  ASSERT_TRUE(cache->Init());
+  ASSERT_EQ(net::OK, cache->SyncInit());
 
   // Wait for a callback that never comes... about 2 secs :). The message loop
   // has to run to allow invocation of the usage timer.
@@ -1446,23 +1569,26 @@
   Time initial = Time::Now();
 
   disk_cache::Entry *entry1, *entry2;
-  ASSERT_TRUE(cache_->CreateEntry("first", &entry1));
-  ASSERT_TRUE(cache_->CreateEntry("second", &entry2));
+  ASSERT_EQ(net::OK, CreateEntry("first", &entry1));
+  ASSERT_EQ(net::OK, CreateEntry("second", &entry2));
   entry1->Close();
   entry2->Close();
 
-  ASSERT_TRUE(cache_->CreateEntry("third", &entry1));
-  ASSERT_TRUE(cache_->CreateEntry("fourth", &entry2));
+  ASSERT_EQ(net::OK, CreateEntry("third", &entry1));
+  ASSERT_EQ(net::OK, CreateEntry("fourth", &entry2));
 
   ASSERT_EQ(4, cache_->GetEntryCount());
-  EXPECT_TRUE(cache_->DoomAllEntries());
+  EXPECT_EQ(net::OK, DoomAllEntries());
   ASSERT_EQ(0, cache_->GetEntryCount());
 
-  disk_cache::Entry *entry3, *entry4;
-  ASSERT_TRUE(cache_->CreateEntry("third", &entry3));
-  ASSERT_TRUE(cache_->CreateEntry("fourth", &entry4));
+  // We should stop posting tasks at some point (if we post any).
+  MessageLoop::current()->RunAllPending();
 
-  EXPECT_TRUE(cache_->DoomAllEntries());
+  disk_cache::Entry *entry3, *entry4;
+  ASSERT_EQ(net::OK, CreateEntry("third", &entry3));
+  ASSERT_EQ(net::OK, CreateEntry("fourth", &entry4));
+
+  EXPECT_EQ(net::OK, DoomAllEntries());
   ASSERT_EQ(0, cache_->GetEntryCount());
 
   entry1->Close();
@@ -1472,13 +1598,13 @@
   entry4->Close();
 
   // Now try with all references released.
-  ASSERT_TRUE(cache_->CreateEntry("third", &entry1));
-  ASSERT_TRUE(cache_->CreateEntry("fourth", &entry2));
+  ASSERT_EQ(net::OK, CreateEntry("third", &entry1));
+  ASSERT_EQ(net::OK, CreateEntry("fourth", &entry2));
   entry1->Close();
   entry2->Close();
 
   ASSERT_EQ(2, cache_->GetEntryCount());
-  EXPECT_TRUE(cache_->DoomAllEntries());
+  EXPECT_EQ(net::OK, DoomAllEntries());
   ASSERT_EQ(0, cache_->GetEntryCount());
 }
 
@@ -1499,17 +1625,17 @@
 // If the index size changes when we doom the cache, we should not crash.
 void DiskCacheBackendTest::BackendDoomAll2() {
   EXPECT_EQ(2, cache_->GetEntryCount());
-  EXPECT_TRUE(cache_->DoomAllEntries());
+  EXPECT_EQ(net::OK, DoomAllEntries());
 
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry("Something new", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("Something new", &entry));
   entry->Close();
 
   EXPECT_EQ(1, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheBackendTest, DoomAll2) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings2"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings2"));
   DisableFirstCleanup();
   SetMaxSize(20 * 1024 * 1024);
   InitCache();
@@ -1517,7 +1643,7 @@
 }
 
 TEST_F(DiskCacheBackendTest, NewEvictionDoomAll2) {
-  ASSERT_TRUE(CopyTestCache(L"bad_rankings2"));
+  ASSERT_TRUE(CopyTestCache("bad_rankings2"));
   DisableFirstCleanup();
   SetMaxSize(20 * 1024 * 1024);
   SetNewEviction();
@@ -1531,51 +1657,93 @@
   ScopedTestCache store1;
   ScopedTestCache store2("cache_test2");
   ScopedTestCache store3("cache_test3");
+  base::Thread cache_thread("CacheThread");
+  ASSERT_TRUE(cache_thread.StartWithOptions(
+                  base::Thread::Options(MessageLoop::TYPE_IO, 0)));
+  TestCompletionCallback cb;
 
   const int kNumberOfCaches = 2;
-  scoped_ptr<disk_cache::Backend> cache[kNumberOfCaches];
+  disk_cache::Backend* cache[kNumberOfCaches];
 
-  cache[0].reset(disk_cache::CreateCacheBackend(store1.path(), false, 0,
-                                                net::DISK_CACHE));
-  cache[1].reset(disk_cache::CreateCacheBackend(store2.path(), false, 0,
-                                                net::MEDIA_CACHE));
+  int rv = disk_cache::BackendImpl::CreateBackend(
+               store1.path(), false, 0, net::DISK_CACHE, disk_cache::kNone,
+               cache_thread.message_loop_proxy(), &cache[0], &cb);
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
+  rv = disk_cache::BackendImpl::CreateBackend(
+           store2.path(), false, 0, net::MEDIA_CACHE, disk_cache::kNone,
+           cache_thread.message_loop_proxy(), &cache[1], &cb);
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
 
-  ASSERT_TRUE(cache[0].get() != NULL && cache[1].get() != NULL);
+  ASSERT_TRUE(cache[0] != NULL && cache[1] != NULL);
 
   std::string key("the first key");
   disk_cache::Entry* entry;
   for (int i = 0; i < kNumberOfCaches; i++) {
-    ASSERT_TRUE(cache[i]->CreateEntry(key, &entry));
+    rv = cache[i]->CreateEntry(key, &entry, &cb);
+    ASSERT_EQ(net::OK, cb.GetResult(rv));
     entry->Close();
   }
+  delete cache[0];
+  delete cache[1];
 }
 
-// Test the four regions of the curve that determines the max cache size.
+// Test the six regions of the curve that determines the max cache size.
 TEST_F(DiskCacheTest, AutomaticMaxSize) {
   const int kDefaultSize = 80 * 1024 * 1024;
   int64 large_size = kDefaultSize;
+  int64 largest_size = kint32max;
 
-  EXPECT_EQ(kDefaultSize, disk_cache::PreferedCacheSize(large_size));
-  EXPECT_EQ((kDefaultSize / 2) * 8 / 10,
-            disk_cache::PreferedCacheSize(large_size / 2));
+  // Region 1: expected = available * 0.8
+  EXPECT_EQ((kDefaultSize - 1) * 8 / 10,
+            disk_cache::PreferedCacheSize(large_size - 1));
+  EXPECT_EQ(kDefaultSize * 8 / 10,
+            disk_cache::PreferedCacheSize(large_size));
+  EXPECT_EQ(kDefaultSize - 1,
+            disk_cache::PreferedCacheSize(large_size * 10 / 8 - 1));
 
-  EXPECT_EQ(kDefaultSize, disk_cache::PreferedCacheSize(large_size * 2));
-  EXPECT_EQ(kDefaultSize, disk_cache::PreferedCacheSize(large_size * 4));
-  EXPECT_EQ(kDefaultSize, disk_cache::PreferedCacheSize(large_size * 10));
+  // Region 2: expected = default_size
+  EXPECT_EQ(kDefaultSize,
+            disk_cache::PreferedCacheSize(large_size * 10 / 8));
+  EXPECT_EQ(kDefaultSize,
+            disk_cache::PreferedCacheSize(large_size * 10 - 1));
 
-  EXPECT_EQ(kDefaultSize * 2, disk_cache::PreferedCacheSize(large_size * 20));
-  EXPECT_EQ(kDefaultSize * 5 / 2,
-            disk_cache::PreferedCacheSize(large_size * 50 / 2));
+  // Region 3: expected = available * 0.1
+  EXPECT_EQ(kDefaultSize,
+            disk_cache::PreferedCacheSize(large_size * 10));
+  EXPECT_EQ((kDefaultSize * 25 - 1) / 10,
+            disk_cache::PreferedCacheSize(large_size * 25 - 1));
 
-  EXPECT_EQ(kDefaultSize * 5 / 2,
-            disk_cache::PreferedCacheSize(large_size * 51 / 2));
-  EXPECT_EQ(kDefaultSize * 5 / 2,
-            disk_cache::PreferedCacheSize(large_size * 100 / 2));
-  EXPECT_EQ(kDefaultSize * 5 / 2,
-            disk_cache::PreferedCacheSize(large_size * 500 / 2));
+  // Region 4: expected = default_size * 2.5
+  EXPECT_EQ(kDefaultSize * 25 / 10,
+            disk_cache::PreferedCacheSize(large_size * 25));
+  EXPECT_EQ(kDefaultSize * 25 / 10,
+            disk_cache::PreferedCacheSize(large_size * 100 - 1));
+  EXPECT_EQ(kDefaultSize * 25 / 10,
+            disk_cache::PreferedCacheSize(large_size * 100));
+  EXPECT_EQ(kDefaultSize * 25 / 10,
+            disk_cache::PreferedCacheSize(large_size * 250 - 1));
 
-  EXPECT_EQ(kDefaultSize * 6 / 2,
-            disk_cache::PreferedCacheSize(large_size * 600 / 2));
-  EXPECT_EQ(kDefaultSize * 7 / 2,
-            disk_cache::PreferedCacheSize(large_size * 700 / 2));
+  // Region 5: expected = available * 0.1
+  EXPECT_EQ(kDefaultSize * 25 / 10,
+            disk_cache::PreferedCacheSize(large_size * 250));
+  EXPECT_EQ(kint32max - 1,
+            disk_cache::PreferedCacheSize(largest_size * 100 - 1));
+
+  // Region 6: expected = kint32max
+  EXPECT_EQ(kint32max,
+            disk_cache::PreferedCacheSize(largest_size * 100));
+  EXPECT_EQ(kint32max,
+            disk_cache::PreferedCacheSize(largest_size * 10000));
+}
+
+// Tests that we can "migrate" a running instance from one experiment group to
+// another.
+TEST_F(DiskCacheBackendTest, Histograms) {
+  SetDirectMode();
+  InitCache();
+  disk_cache::BackendImpl* backend_ = cache_impl_;  // Needed be the macro.
+
+  for (int i = 1; i < 3; i++) {
+    CACHE_UMA(HOURS, "FillupTime", i, 28);
+  }
 }
diff --git a/net/disk_cache/block_files.cc b/net/disk_cache/block_files.cc
index fe02f67..48e5be3 100644
--- a/net/disk_cache/block_files.cc
+++ b/net/disk_cache/block_files.cc
@@ -11,7 +11,7 @@
 #include "net/disk_cache/cache_util.h"
 #include "net/disk_cache/file_lock.h"
 
-using base::Time;
+using base::TimeTicks;
 
 namespace {
 
@@ -41,7 +41,7 @@
     return false;
   }
 
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
   // We are going to process the map on 32-block chunks (32 bits), and on every
   // chunk, iterate through the 8 nibbles where the new block can be located.
   int current = header->hints[target - 1];
@@ -67,7 +67,7 @@
       if (target != size) {
         header->empty[target - size - 1]++;
       }
-      HISTOGRAM_TIMES("DiskCache.CreateBlock", Time::Now() - start);
+      HISTOGRAM_TIMES("DiskCache.CreateBlock", TimeTicks::Now() - start);
       return true;
     }
   }
@@ -86,7 +86,7 @@
     NOTREACHED();
     return;
   }
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
   int byte_index = index / 8;
   uint8* byte_map = reinterpret_cast<uint8*>(header->allocation_map);
   uint8 map_block = byte_map[byte_index];
@@ -115,7 +115,7 @@
   }
   header->num_entries--;
   DCHECK(header->num_entries >= 0);
-  HISTOGRAM_TIMES("DiskCache.DeleteBlock", Time::Now() - start);
+  HISTOGRAM_TIMES("DiskCache.DeleteBlock", TimeTicks::Now() - start);
 }
 
 // Restores the "empty counters" and allocation hints.
@@ -172,8 +172,8 @@
   if (init_)
     return false;
 
-  block_files_.resize(kFirstAdditionlBlockFile);
-  for (int i = 0; i < kFirstAdditionlBlockFile; i++) {
+  block_files_.resize(kFirstAdditionalBlockFile);
+  for (int i = 0; i < kFirstAdditionalBlockFile; i++) {
     if (create_files)
       if (!CreateBlockFile(i, static_cast<FileType>(i + 1), true))
         return false;
@@ -200,11 +200,21 @@
   block_files_.clear();
 }
 
-FilePath BlockFiles::Name(int index) {
-  // The file format allows for 256 files.
-  DCHECK(index < 256 || index >= 0);
-  std::string tmp = StringPrintf("%s%d", kBlockName, index);
-  return path_.AppendASCII(tmp);
+void BlockFiles::ReportStats() {
+  int used_blocks[kFirstAdditionalBlockFile];
+  int load[kFirstAdditionalBlockFile];
+  for (int i = 0; i < kFirstAdditionalBlockFile; i++) {
+    GetFileStats(i, &used_blocks[i], &load[i]);
+  }
+  UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_0", used_blocks[0]);
+  UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_1", used_blocks[1]);
+  UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_2", used_blocks[2]);
+  UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_3", used_blocks[3]);
+
+  UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_0", load[0], 101);
+  UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_1", load[1], 101);
+  UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_2", load[2], 101);
+  UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_3", load[3], 101);
 }
 
 bool BlockFiles::CreateBlockFile(int index, FileType file_type, bool force) {
@@ -241,7 +251,8 @@
     return false;
   }
 
-  if (file->GetLength() < static_cast<size_t>(kBlockHeaderSize)) {
+  size_t file_len = file->GetLength();
+  if (file_len < static_cast<size_t>(kBlockHeaderSize)) {
     LOG(ERROR) << "File too small " << name.value();
     return false;
   }
@@ -258,6 +269,13 @@
       return false;
   }
 
+  if (index == 0) {
+    // Load the links file into memory with a single read.
+    scoped_array<char> buf(new char[file_len]);
+    if (!file->Read(buf.get(), file_len, 0))
+      return false;
+  }
+
   DCHECK(!block_files_[index]);
   file.swap(&block_files_[index]);
   return true;
@@ -314,7 +332,7 @@
   MappedFile* file = block_files_[block_type - 1];
   BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
 
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
   while (NeedToGrowBlockFile(header, block_count)) {
     if (kMaxBlocks == header->max_entries) {
       file = NextFile(file);
@@ -328,7 +346,7 @@
       return NULL;
     break;
   }
-  HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", Time::Now() - start);
+  HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start);
   return file;
 }
 
@@ -356,7 +374,7 @@
 }
 
 int BlockFiles::CreateNextBlockFile(FileType block_type) {
-  for (int i = kFirstAdditionlBlockFile; i <= kMaxBlockFile; i++) {
+  for (int i = kFirstAdditionalBlockFile; i <= kMaxBlockFile; i++) {
     if (CreateBlockFile(i, block_type, false))
       return i;
   }
@@ -485,4 +503,42 @@
   return true;
 }
 
+// We are interested in the total number of block used by this file type, and
+// the max number of blocks that we can store (reported as the percentage of
+// used blocks). In order to find out the number of used blocks, we have to
+// substract the empty blocks from the total blocks for each file in the chain.
+void BlockFiles::GetFileStats(int index, int* used_count, int* load) {
+  int max_blocks = 0;
+  *used_count = 0;
+  *load = 0;
+  for (;;) {
+    if (!block_files_[index] && !OpenBlockFile(index))
+      return;
+
+    BlockFileHeader* header =
+        reinterpret_cast<BlockFileHeader*>(block_files_[index]->buffer());
+
+    max_blocks += header->max_entries;
+    int used = header->max_entries;
+    for (int i = 0; i < 4; i++) {
+      used -= header->empty[i] * (i + 1);
+      DCHECK_GE(used, 0);
+    }
+    *used_count += used;
+
+    if (!header->next_file)
+      break;
+    index = header->next_file;
+  }
+  if (max_blocks)
+    *load = *used_count * 100 / max_blocks;
+}
+
+FilePath BlockFiles::Name(int index) {
+  // The file format allows for 256 files.
+  DCHECK(index < 256 || index >= 0);
+  std::string tmp = StringPrintf("%s%d", kBlockName, index);
+  return path_.AppendASCII(tmp);
+}
+
 }  // namespace disk_cache
diff --git a/net/disk_cache/block_files.h b/net/disk_cache/block_files.h
index 8503062..3ed19de 100644
--- a/net/disk_cache/block_files.h
+++ b/net/disk_cache/block_files.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -46,6 +46,9 @@
   // cache is being purged.
   void CloseFiles();
 
+  // Sends UMA stats.
+  void ReportStats();
+
  private:
   // Set force to true to overwrite the file if it exists.
   bool CreateBlockFile(int index, FileType file_type, bool force);
@@ -69,6 +72,9 @@
   // Restores the header of a potentially inconsistent file.
   bool FixBlockFileHeader(MappedFile* file);
 
+  // Retrieves stats for the given file index.
+  void GetFileStats(int index, int* used_count, int* load);
+
   // Returns the filename for a given file index.
   FilePath Name(int index);
 
@@ -79,8 +85,9 @@
 
   FRIEND_TEST(DiskCacheTest, BlockFiles_ZeroSizeFile);
   FRIEND_TEST(DiskCacheTest, BlockFiles_InvalidFile);
+  FRIEND_TEST(DiskCacheTest, BlockFiles_Stats);
 
-  DISALLOW_EVIL_CONSTRUCTORS(BlockFiles);
+  DISALLOW_COPY_AND_ASSIGN(BlockFiles);
 };
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/block_files_unittest.cc b/net/disk_cache/block_files_unittest.cc
index 054641c..2c87179 100644
--- a/net/disk_cache/block_files_unittest.cc
+++ b/net/disk_cache/block_files_unittest.cc
@@ -203,4 +203,26 @@
   EXPECT_TRUE(NULL == files.GetFile(addr));
 }
 
+// Tests that we generate the correct file stats.
+TEST_F(DiskCacheTest, BlockFiles_Stats) {
+  ASSERT_TRUE(CopyTestCache("remove_load1"));
+  FilePath path = GetCacheFilePath();
+
+  BlockFiles files(path);
+  ASSERT_TRUE(files.Init(false));
+  int used, load;
+
+  files.GetFileStats(0, &used, &load);
+  EXPECT_EQ(101, used);
+  EXPECT_EQ(9, load);
+
+  files.GetFileStats(1, &used, &load);
+  EXPECT_EQ(203, used);
+  EXPECT_EQ(19, load);
+
+  files.GetFileStats(2, &used, &load);
+  EXPECT_EQ(0, used);
+  EXPECT_EQ(0, load);
+}
+
 }  // namespace disk_cache
diff --git a/net/disk_cache/cache_util_posix.cc b/net/disk_cache/cache_util_posix.cc
index a272cb8..b393747 100644
--- a/net/disk_cache/cache_util_posix.cc
+++ b/net/disk_cache/cache_util_posix.cc
@@ -20,13 +20,17 @@
                                  /* recursive */ false,
                                  file_util::FileEnumerator::FILES);
   for (FilePath file = iter.Next(); !file.value().empty(); file = iter.Next()) {
-    if (!file_util::Delete(file, /* recursive */ false))
-      NOTREACHED();
+    if (!file_util::Delete(file, /* recursive */ false)) {
+      LOG(WARNING) << "Unable to delete cache.";
+      return;
+    }
   }
 
   if (remove_folder) {
-    if (!file_util::Delete(path, /* recursive */ false))
-      NOTREACHED();
+    if (!file_util::Delete(path, /* recursive */ false)) {
+      LOG(WARNING) << "Unable to delete cache folder.";
+      return;
+    }
   }
 }
 
diff --git a/net/disk_cache/disk_cache.h b/net/disk_cache/disk_cache.h
index 3d2793b..b2d76a4 100644
--- a/net/disk_cache/disk_cache.h
+++ b/net/disk_cache/disk_cache.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -18,6 +18,10 @@
 
 class FilePath;
 
+namespace base {
+class MessageLoopProxy;
+}
+
 namespace net {
 class IOBuffer;
 }
@@ -28,27 +32,6 @@
 class Backend;
 typedef net::CompletionCallback CompletionCallback;
 
-// Returns an instance of the Backend. path points to a folder where
-// the cached data will be stored. This cache instance must be the only object
-// that will be reading or writing files to that folder. The returned object
-// should be deleted when not needed anymore. If force is true, and there is
-// a problem with the cache initialization, the files will be deleted and a
-// new set will be created. max_bytes is the maximum size the cache can grow to.
-// If zero is passed in as max_bytes, the cache will determine the value to use
-// based on the available disk space. The returned pointer can be NULL if a
-// fatal error is found.
-// Note: This function is deprecated.
-Backend* CreateCacheBackend(const FilePath& path, bool force,
-                            int max_bytes, net::CacheType type);
-
-// Returns an instance of a Backend implemented only in memory. The returned
-// object should be deleted when not needed anymore. max_bytes is the maximum
-// size the cache can grow to. If zero is passed in as max_bytes, the cache will
-// determine the value to use based on the available memory. The returned
-// pointer can be NULL if a fatal error is found.
-// Note: This function is deprecated.
-Backend* CreateInMemoryCacheBackend(int max_bytes);
-
 // Returns an instance of a Backend of the given |type|. |path| points to a
 // folder where the cached data will be stored (if appropriate). This cache
 // instance must be the only object that will be reading or writing files to
@@ -56,15 +39,17 @@
 // If |force| is true, and there is a problem with the cache initialization, the
 // files will be deleted and a new set will be created. |max_bytes| is the
 // maximum size the cache can grow to. If zero is passed in as |max_bytes|, the
-// cache will determine the value to use. The returned pointer can be NULL if a
-// fatal error is found. The actual return value of the function is a net error
-// code. If this function returns ERR_IO_PENDING, the |callback| will be invoked
-// when a backend is available or a fatal error condition is reached. The
-// pointer to receive the |backend| must remain valid until the operation
-// completes.
+// cache will determine the value to use. |thread| can be used to perform IO
+// operations if a dedicated thread is required; a valid value is expected for
+// any backend that performs operations on a disk. The returned pointer can be
+// NULL if a fatal error is found. The actual return value of the function is a
+// net error code. If this function returns ERR_IO_PENDING, the |callback| will
+// be invoked when a backend is available or a fatal error condition is reached.
+// The pointer to receive the |backend| must remain valid until the operation
+// completes (the callback is notified).
 int CreateCacheBackend(net::CacheType type, const FilePath& path, int max_bytes,
-                       bool force, Backend** backend,
-                       CompletionCallback* callback);
+                       bool force, base::MessageLoopProxy* thread,
+                       Backend** backend, CompletionCallback* callback);
 
 // The root interface for a disk cache instance.
 class Backend {
@@ -80,13 +65,6 @@
   // Returns the number of entries in the cache.
   virtual int32 GetEntryCount() const = 0;
 
-  // Opens an existing entry.  Upon success, the out param holds a pointer
-  // to a Entry object representing the specified disk cache entry.
-  // When the entry pointer is no longer needed, the Close method
-  // should be called.
-  // Note: This method is deprecated.
-  virtual bool OpenEntry(const std::string& key, Entry** entry) = 0;
-
   // Opens an existing entry. Upon success, |entry| holds a pointer to an Entry
   // object representing the specified disk cache entry. When the entry pointer
   // is no longer needed, its Close method should be called. The return value is
@@ -96,13 +74,6 @@
   virtual int OpenEntry(const std::string& key, Entry** entry,
                         CompletionCallback* callback) = 0;
 
-  // Creates a new entry.  Upon success, the out param holds a pointer
-  // to a Entry object representing the newly created disk cache
-  // entry.  When the entry pointer is no longer needed, the Close
-  // method should be called.
-  // Note: This method is deprecated.
-  virtual bool CreateEntry(const std::string& key, Entry** entry) = 0;
-
   // Creates a new entry. Upon success, the out param holds a pointer to an
   // Entry object representing the newly created disk cache entry. When the
   // entry pointer is no longer needed, its Close method should be called. The
@@ -112,32 +83,18 @@
   virtual int CreateEntry(const std::string& key, Entry** entry,
                           CompletionCallback* callback) = 0;
 
-  // Marks the entry, specified by the given key, for deletion.
-  // Note: This method is deprecated.
-  virtual bool DoomEntry(const std::string& key) = 0;
-
   // Marks the entry, specified by the given key, for deletion. The return value
   // is a net error code. If this method returns ERR_IO_PENDING, the |callback|
   // will be invoked after the entry is doomed.
   virtual int DoomEntry(const std::string& key,
                         CompletionCallback* callback) = 0;
 
-  // Marks all entries for deletion.
-  // Note: This method is deprecated.
-  virtual bool DoomAllEntries() = 0;
-
   // Marks all entries for deletion. The return value is a net error code. If
   // this method returns ERR_IO_PENDING, the |callback| will be invoked when the
   // operation completes.
   virtual int DoomAllEntries(CompletionCallback* callback) = 0;
 
   // Marks a range of entries for deletion. This supports unbounded deletes in
-  // either direction by using null Time values for either argument.
-  // Note: This method is deprecated.
-  virtual bool DoomEntriesBetween(const base::Time initial_time,
-                                  const base::Time end_time) = 0;
-
-  // Marks a range of entries for deletion. This supports unbounded deletes in
   // either direction by using null Time values for either argument. The return
   // value is a net error code. If this method returns ERR_IO_PENDING, the
   // |callback| will be invoked when the operation completes.
@@ -145,28 +102,12 @@
                                  const base::Time end_time,
                                  CompletionCallback* callback) = 0;
 
-  // Marks all entries accessed since initial_time for deletion.
-  // Note: This method is deprecated.
-  virtual bool DoomEntriesSince(const base::Time initial_time) = 0;
-
   // Marks all entries accessed since |initial_time| for deletion. The return
   // value is a net error code. If this method returns ERR_IO_PENDING, the
   // |callback| will be invoked when the operation completes.
   virtual int DoomEntriesSince(const base::Time initial_time,
                                CompletionCallback* callback) = 0;
 
-  // Enumerate the cache.  Initialize iter to NULL before calling this method
-  // the first time.  That will cause the enumeration to start at the head of
-  // the cache.  For subsequent calls, pass the same iter pointer again without
-  // changing its value.  This method returns false when there are no more
-  // entries to enumerate.  When the entry pointer is no longer needed, the
-  // Close method should be called.
-  //
-  // NOTE: This method does not modify the last_used field of the entry,
-  // and therefore it does not impact the eviction ranking of the entry.
-  // Note: This method is deprecated.
-  virtual bool OpenNextEntry(void** iter, Entry** next_entry) = 0;
-
   // Enumerates the cache. Initialize |iter| to NULL before calling this method
   // the first time. That will cause the enumeration to start at the head of
   // the cache. For subsequent calls, pass the same |iter| pointer again without
@@ -309,22 +250,22 @@
   // first byte that is stored within this range, and the return value is the
   // minimum number of consecutive stored bytes. Note that it is possible that
   // this entry has stored more than the returned value. This method returns a
-  // net error code whenever the request cannot be completed successfully.
-  // Note: This method is deprecated.
-  virtual int GetAvailableRange(int64 offset, int len, int64* start) = 0;
-
-  // Returns information about the currently stored portion of a sparse entry.
-  // |offset| and |len| describe a particular range that should be scanned to
-  // find out if it is stored or not. |start| will contain the offset of the
-  // first byte that is stored within this range, and the return value is the
-  // minimum number of consecutive stored bytes. Note that it is possible that
-  // this entry has stored more than the returned value. This method returns a
   // net error code whenever the request cannot be completed successfully. If
   // this method returns ERR_IO_PENDING, the |callback| will be invoked when the
   // operation completes, and |start| must remain valid until that point.
   virtual int GetAvailableRange(int64 offset, int len, int64* start,
                                 CompletionCallback* callback) = 0;
 
+  // Returns true if this entry could be a sparse entry or false otherwise. This
+  // is a quick test that may return true even if the entry is not really
+  // sparse. This method doesn't modify the state of this entry (it will not
+  // create sparse tracking data). GetAvailableRange or ReadSparseData can be
+  // used to perfom a definitive test of wether an existing entry is sparse or
+  // not, but that method may modify the current state of the entry (making it
+  // sparse, for instance). The purpose of this method is to test an existing
+  // entry, but without generating actual IO to perform a thorough check.
+  virtual bool CouldBeSparse() const = 0;
+
   // Cancels any pending sparse IO operation (if any). The completion callback
   // of the operation in question will still be called when the operation
   // finishes, but the operation will finish sooner when this method is used.
diff --git a/net/disk_cache/disk_cache_perftest.cc b/net/disk_cache/disk_cache_perftest.cc
index 4064341..1f1514d 100644
--- a/net/disk_cache/disk_cache_perftest.cc
+++ b/net/disk_cache/disk_cache_perftest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -9,10 +9,12 @@
 #include "base/file_util.h"
 #include "base/perftimer.h"
 #include "base/string_util.h"
+#include "base/thread.h"
 #include "base/test/test_file_util.h"
 #include "base/timer.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
 #include "net/disk_cache/block_files.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/disk_cache/disk_cache_test_util.h"
@@ -64,7 +66,9 @@
     entries->push_back(entry);
 
     disk_cache::Entry* cache_entry;
-    if (!cache->CreateEntry(entry.key, &cache_entry))
+    TestCompletionCallback cb;
+    int rv = cache->CreateEntry(entry.key, &cache_entry, &cb);
+    if (net::OK != cb.GetResult(rv))
       break;
     int ret = cache_entry->WriteData(0, 0, buffer1, kSize1, &callback, false);
     if (net::ERR_IO_PENDING == ret)
@@ -110,7 +114,9 @@
 
   for (int i = 0; i < num_entries; i++) {
     disk_cache::Entry* cache_entry;
-    if (!cache->OpenEntry(entries[i].key, &cache_entry))
+    TestCompletionCallback cb;
+    int rv = cache->OpenEntry(entries[i].key, &cache_entry, &cb);
+    if (net::OK != cb.GetResult(rv))
       break;
     int ret = cache_entry->ReadData(0, 0, buffer1, kSize1, &callback);
     if (net::ERR_IO_PENDING == ret)
@@ -154,11 +160,18 @@
 TEST_F(DiskCacheTest, CacheBackendPerformance) {
   MessageLoopForIO message_loop;
 
+  base::Thread cache_thread("CacheThread");
+  ASSERT_TRUE(cache_thread.StartWithOptions(
+                  base::Thread::Options(MessageLoop::TYPE_IO, 0)));
+
   ScopedTestCache test_cache;
-  disk_cache::Backend* cache =
-      disk_cache::CreateCacheBackend(test_cache.path(), false, 0,
-                                     net::DISK_CACHE);
-  ASSERT_TRUE(NULL != cache);
+  TestCompletionCallback cb;
+  disk_cache::Backend* cache;
+  int rv = disk_cache::CreateCacheBackend(
+               net::DISK_CACHE, test_cache.path(), 0, false,
+               cache_thread.message_loop_proxy(), &cache, &cb);
+
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
 
   int seed = static_cast<int>(Time::Now().ToInternalValue());
   srand(seed);
@@ -183,9 +196,10 @@
   ASSERT_TRUE(file_util::EvictFileFromSystemCache(
               test_cache.path().AppendASCII("data_3")));
 
-  cache = disk_cache::CreateCacheBackend(test_cache.path(), false, 0,
-                                         net::DISK_CACHE);
-  ASSERT_TRUE(NULL != cache);
+  rv = disk_cache::CreateCacheBackend(net::DISK_CACHE, test_cache.path(), 0,
+                                      false, cache_thread.message_loop_proxy(),
+                                      &cache, &cb);
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
 
   ret = TimeRead(num_entries, cache, entries, true);
   EXPECT_EQ(ret, g_cache_tests_received);
diff --git a/net/disk_cache/disk_cache_test_base.cc b/net/disk_cache/disk_cache_test_base.cc
index 21352a2..0add3c7 100644
--- a/net/disk_cache/disk_cache_test_base.cc
+++ b/net/disk_cache/disk_cache_test_base.cc
@@ -1,9 +1,11 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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/disk_cache/disk_cache_test_base.h"
 
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
 #include "net/disk_cache/backend_impl.h"
 #include "net/disk_cache/disk_cache_test_util.h"
 #include "net/disk_cache/mem_backend_impl.h"
@@ -37,7 +39,7 @@
 
 void DiskCacheTestWithCache::InitMemoryCache() {
   if (!implementation_) {
-    cache_ = disk_cache::CreateInMemoryCacheBackend(size_);
+    cache_ = disk_cache::MemBackendImpl::CreateBackend(size_);
     return;
   }
 
@@ -56,19 +58,34 @@
   if (first_cleanup_)
     ASSERT_TRUE(DeleteCache(path));
 
+  if (!cache_thread_.IsRunning()) {
+    EXPECT_TRUE(cache_thread_.StartWithOptions(
+                    base::Thread::Options(MessageLoop::TYPE_IO, 0)));
+  }
+  ASSERT_TRUE(cache_thread_.message_loop() != NULL);
+
   if (implementation_)
     return InitDiskCacheImpl(path);
 
-  cache_ = disk_cache::BackendImpl::CreateBackend(path, force_creation_, size_,
-                                                  net::DISK_CACHE,
-                                                  disk_cache::kNoRandom);
+  scoped_refptr<base::MessageLoopProxy> thread =
+      use_current_thread_ ? base::MessageLoopProxy::CreateForCurrentThread() :
+                            cache_thread_.message_loop_proxy();
+
+  TestCompletionCallback cb;
+  int rv = disk_cache::BackendImpl::CreateBackend(
+               path, force_creation_, size_, net::DISK_CACHE,
+               disk_cache::kNoRandom, thread, &cache_, &cb);
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
 }
 
 void DiskCacheTestWithCache::InitDiskCacheImpl(const FilePath& path) {
+  scoped_refptr<base::MessageLoopProxy> thread =
+      use_current_thread_ ? base::MessageLoopProxy::CreateForCurrentThread() :
+                            cache_thread_.message_loop_proxy();
   if (mask_)
-    cache_impl_ = new disk_cache::BackendImpl(path, mask_);
+    cache_impl_ = new disk_cache::BackendImpl(path, mask_, thread);
   else
-    cache_impl_ = new disk_cache::BackendImpl(path);
+    cache_impl_ = new disk_cache::BackendImpl(path, thread);
 
   cache_ = cache_impl_;
   ASSERT_TRUE(NULL != cache_);
@@ -80,12 +97,16 @@
     cache_impl_->SetNewEviction();
 
   cache_impl_->SetFlags(disk_cache::kNoRandom);
-  ASSERT_TRUE(cache_impl_->Init());
+  TestCompletionCallback cb;
+  int rv = cache_impl_->Init(&cb);
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
 }
 
 void DiskCacheTestWithCache::TearDown() {
   MessageLoop::current()->RunAllPending();
   delete cache_;
+  if (cache_thread_.IsRunning())
+    cache_thread_.Stop();
 
   if (!memory_only_ && integrity_) {
     FilePath path = GetCacheFilePath();
@@ -98,6 +119,9 @@
 // We are expected to leak memory when simulating crashes.
 void DiskCacheTestWithCache::SimulateCrash() {
   ASSERT_TRUE(implementation_ && !memory_only_);
+  TestCompletionCallback cb;
+  int rv = cache_impl_->FlushQueueForTest(&cb);
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
   cache_impl_->ClearRefCountForTest();
 
   delete cache_impl_;
@@ -111,3 +135,58 @@
   ASSERT_TRUE(implementation_ && !memory_only_);
   cache_impl_->SetUnitTestMode();
 }
+
+int DiskCacheTestWithCache::OpenEntry(const std::string& key,
+                                      disk_cache::Entry** entry) {
+  TestCompletionCallback cb;
+  int rv = cache_->OpenEntry(key, entry, &cb);
+  return cb.GetResult(rv);
+}
+
+int DiskCacheTestWithCache::CreateEntry(const std::string& key,
+                                        disk_cache::Entry** entry) {
+  TestCompletionCallback cb;
+  int rv = cache_->CreateEntry(key, entry, &cb);
+  return cb.GetResult(rv);
+}
+
+int DiskCacheTestWithCache::DoomEntry(const std::string& key) {
+  TestCompletionCallback cb;
+  int rv = cache_->DoomEntry(key, &cb);
+  return cb.GetResult(rv);
+}
+
+int DiskCacheTestWithCache::DoomAllEntries() {
+  TestCompletionCallback cb;
+  int rv = cache_->DoomAllEntries(&cb);
+  return cb.GetResult(rv);
+}
+
+int DiskCacheTestWithCache::DoomEntriesBetween(const base::Time initial_time,
+                                               const base::Time end_time) {
+  TestCompletionCallback cb;
+  int rv = cache_->DoomEntriesBetween(initial_time, end_time, &cb);
+  return cb.GetResult(rv);
+}
+
+int DiskCacheTestWithCache::DoomEntriesSince(const base::Time initial_time) {
+  TestCompletionCallback cb;
+  int rv = cache_->DoomEntriesSince(initial_time, &cb);
+  return cb.GetResult(rv);
+}
+
+int DiskCacheTestWithCache::OpenNextEntry(void** iter,
+                                          disk_cache::Entry** next_entry) {
+  TestCompletionCallback cb;
+  int rv = cache_->OpenNextEntry(iter, next_entry, &cb);
+  return cb.GetResult(rv);
+}
+
+void DiskCacheTestWithCache::FlushQueueForTest() {
+  if (memory_only_ || !cache_impl_)
+    return;
+
+  TestCompletionCallback cb;
+  int rv = cache_impl_->FlushQueueForTest(&cb);
+  EXPECT_EQ(net::OK, cb.GetResult(rv));
+}
diff --git a/net/disk_cache/disk_cache_test_base.h b/net/disk_cache/disk_cache_test_base.h
index c198e22..6c6b54b 100644
--- a/net/disk_cache/disk_cache_test_base.h
+++ b/net/disk_cache/disk_cache_test_base.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -6,6 +6,7 @@
 #define NET_DISK_CACHE_DISK_CACHE_TEST_BASE_H_
 
 #include "base/basictypes.h"
+#include "base/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
@@ -15,6 +16,7 @@
 
 class Backend;
 class BackendImpl;
+class Entry;
 class MemBackendImpl;
 
 }  // namespace disk_cache
@@ -33,7 +35,8 @@
   DiskCacheTestWithCache()
       : cache_(NULL), cache_impl_(NULL), mem_cache_(NULL), mask_(0), size_(0),
         memory_only_(false), implementation_(false), force_creation_(false),
-        new_eviction_(false), first_cleanup_(true), integrity_(true) {}
+        new_eviction_(false), first_cleanup_(true), integrity_(true),
+        use_current_thread_(false), cache_thread_("CacheThread") {}
 
   void InitCache();
   virtual void TearDown();
@@ -72,6 +75,21 @@
     integrity_ = false;
   }
 
+  void UseCurrentThread() {
+    use_current_thread_ = true;
+  }
+
+  // Utility methods to access the cache and wait for each operation to finish.
+  int OpenEntry(const std::string& key, disk_cache::Entry** entry);
+  int CreateEntry(const std::string& key, disk_cache::Entry** entry);
+  int DoomEntry(const std::string& key);
+  int DoomAllEntries();
+  int DoomEntriesBetween(const base::Time initial_time,
+                         const base::Time end_time);
+  int DoomEntriesSince(const base::Time initial_time);
+  int OpenNextEntry(void** iter, disk_cache::Entry** next_entry);
+  void FlushQueueForTest();
+
   // cache_ will always have a valid object, regardless of how the cache was
   // initialized. The implementation pointers can be NULL.
   disk_cache::Backend* cache_;
@@ -86,6 +104,7 @@
   bool new_eviction_;
   bool first_cleanup_;
   bool integrity_;
+  bool use_current_thread_;
   // This is intentionally left uninitialized, to be used by any test.
   bool success_;
 
@@ -93,6 +112,9 @@
   void InitMemoryCache();
   void InitDiskCache();
   void InitDiskCacheImpl(const FilePath& path);
+
+  base::Thread cache_thread_;
+  DISALLOW_COPY_AND_ASSIGN(DiskCacheTestWithCache);
 };
 
 #endif  // NET_DISK_CACHE_DISK_CACHE_TEST_BASE_H_
diff --git a/net/disk_cache/disk_cache_test_util.cc b/net/disk_cache/disk_cache_test_util.cc
index 36d28d2..46e33db 100644
--- a/net/disk_cache/disk_cache_test_util.cc
+++ b/net/disk_cache/disk_cache_test_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -6,7 +6,9 @@
 
 #include "base/logging.h"
 #include "base/file_util.h"
+#include "base/message_loop_proxy.h"
 #include "base/path_service.h"
+#include "net/base/net_errors.h"
 #include "net/disk_cache/backend_impl.h"
 #include "net/disk_cache/cache_util.h"
 #include "net/disk_cache/file.h"
@@ -76,14 +78,29 @@
   return true;
 }
 
+bool CopyTestCache(const std::string& name) {
+  FilePath path;
+  PathService::Get(base::DIR_SOURCE_ROOT, &path);
+  path = path.AppendASCII("net");
+  path = path.AppendASCII("data");
+  path = path.AppendASCII("cache_tests");
+  path = path.AppendASCII(name);
+
+  FilePath dest = GetCacheFilePath();
+  if (!DeleteCache(dest))
+    return false;
+  return file_util::CopyDirectory(path, dest, false);
+}
+
 bool CheckCacheIntegrity(const FilePath& path, bool new_eviction) {
-  scoped_ptr<disk_cache::BackendImpl> cache(new disk_cache::BackendImpl(path));
+  scoped_ptr<disk_cache::BackendImpl> cache(new disk_cache::BackendImpl(
+      path, base::MessageLoopProxy::CreateForCurrentThread()));
   if (!cache.get())
     return false;
   if (new_eviction)
     cache->SetNewEviction();
   cache->SetFlags(disk_cache::kNoRandom);
-  if (!cache->Init())
+  if (cache->SyncInit() != net::OK)
     return false;
   return cache->SelfCheck() >= 0;
 }
diff --git a/net/disk_cache/disk_cache_test_util.h b/net/disk_cache/disk_cache_test_util.h
index 623810b..7249ee0 100644
--- a/net/disk_cache/disk_cache_test_util.h
+++ b/net/disk_cache/disk_cache_test_util.h
@@ -7,9 +7,9 @@
 
 #include <string>
 
+#include "base/callback.h"
 #include "base/file_path.h"
 #include "base/message_loop.h"
-#include "base/task.h"
 #include "base/timer.h"
 #include "build/build_config.h"
 
@@ -21,6 +21,9 @@
 // Deletes all file son the cache.
 bool DeleteCache(const FilePath& path);
 
+// Copies a set of cache files from the data folder to the test folder.
+bool CopyTestCache(const std::string& name);
+
 // Gets the path to the cache test folder.
 FilePath GetCacheFilePath();
 
diff --git a/net/disk_cache/entry_impl.cc b/net/disk_cache/entry_impl.cc
index e3fda2a..a017549 100644
--- a/net/disk_cache/entry_impl.cc
+++ b/net/disk_cache/entry_impl.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -17,6 +17,7 @@
 
 using base::Time;
 using base::TimeDelta;
+using base::TimeTicks;
 
 namespace {
 
@@ -29,7 +30,8 @@
  public:
   SyncCallback(disk_cache::EntryImpl* entry, net::IOBuffer* buffer,
                net::CompletionCallback* callback )
-      : entry_(entry), callback_(callback), buf_(buffer), start_(Time::Now()) {
+      : entry_(entry), callback_(callback), buf_(buffer),
+        start_(TimeTicks::Now()) {
     entry->AddRef();
     entry->IncrementIoCount();
   }
@@ -41,9 +43,9 @@
   disk_cache::EntryImpl* entry_;
   net::CompletionCallback* callback_;
   scoped_refptr<net::IOBuffer> buf_;
-  Time start_;
+  TimeTicks start_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(SyncCallback);
+  DISALLOW_COPY_AND_ASSIGN(SyncCallback);
 };
 
 void SyncCallback::OnFileIOComplete(int bytes_copied) {
@@ -65,8 +67,8 @@
 // Clears buffer before offset and after valid_len, knowing that the size of
 // buffer is kMaxBlockSize.
 void ClearInvalidData(char* buffer, int offset, int valid_len) {
-  DCHECK(offset >= 0);
-  DCHECK(valid_len >= 0);
+  DCHECK_GE(offset, 0);
+  DCHECK_GE(valid_len, 0);
   DCHECK(disk_cache::kMaxBlockSize >= offset + valid_len);
   if (offset)
     memset(buffer, 0, offset);
@@ -87,7 +89,6 @@
   for (int i = 0; i < kNumStreams; i++) {
     unreported_size_[i] = 0;
   }
-  key_file_ = NULL;
 }
 
 // When an entry is deleted from the cache, we clean up all the data associated
@@ -130,15 +131,11 @@
 }
 
 void EntryImpl::Doom() {
-  if (doomed_)
-    return;
-
-  SetPointerForInvalidEntry(backend_->GetCurrentEntryId());
-  backend_->InternalDoomEntry(this);
+  backend_->background_queue()->DoomEntryImpl(this);
 }
 
 void EntryImpl::Close() {
-  Release();
+  backend_->background_queue()->CloseEntryImpl(this);
 }
 
 std::string EntryImpl::GetKey() const {
@@ -146,26 +143,26 @@
   if (entry->Data()->key_len <= kMaxInternalKeyLength)
     return std::string(entry->Data()->key);
 
+  // We keep a copy of the key so that we can always return it, even if the
+  // backend is disabled.
+  if (!key_.empty())
+    return key_;
+
   Addr address(entry->Data()->long_key);
   DCHECK(address.is_initialized());
   size_t offset = 0;
   if (address.is_block_file())
     offset = address.start_block() * address.BlockSize() + kBlockHeaderSize;
 
-  if (!key_file_) {
-    // We keep a copy of the file needed to access the key so that we can
-    // always return this object's key, even if the backend is disabled.
-    COMPILE_ASSERT(kNumStreams == kKeyFileIndex, invalid_key_index);
-    key_file_ = const_cast<EntryImpl*>(this)->GetBackingFile(address,
-                                                             kKeyFileIndex);
-  }
+  COMPILE_ASSERT(kNumStreams == kKeyFileIndex, invalid_key_index);
+  File* key_file = const_cast<EntryImpl*>(this)->GetBackingFile(address,
+                                                                kKeyFileIndex);
 
-  std::string key;
-  if (!key_file_ ||
-      !key_file_->Read(WriteInto(&key, entry->Data()->key_len + 1),
-                       entry->Data()->key_len + 1, offset))
-    key.clear();
-  return key;
+  if (!key_file ||
+      !key_file->Read(WriteInto(&key_, entry->Data()->key_len + 1),
+                      entry->Data()->key_len + 1, offset))
+    key_.clear();
+  return key_;
 }
 
 Time EntryImpl::GetLastUsed() const {
@@ -187,7 +184,10 @@
 }
 
 int EntryImpl::ReadData(int index, int offset, net::IOBuffer* buf, int buf_len,
-                        net::CompletionCallback* completion_callback) {
+                        net::CompletionCallback* callback) {
+  if (!callback)
+    return ReadDataImpl(index, offset, buf, buf_len, callback);
+
   DCHECK(node_.Data()->dirty);
   if (index < 0 || index >= kNumStreams)
     return net::ERR_INVALID_ARGUMENT;
@@ -199,7 +199,100 @@
   if (buf_len < 0)
     return net::ERR_INVALID_ARGUMENT;
 
-  Time start = Time::Now();
+  backend_->background_queue()->ReadData(this, index, offset, buf, buf_len,
+                                         callback);
+  return net::ERR_IO_PENDING;
+}
+
+int EntryImpl::WriteData(int index, int offset, net::IOBuffer* buf, int buf_len,
+                         CompletionCallback* callback, bool truncate) {
+  if (!callback)
+    return WriteDataImpl(index, offset, buf, buf_len, callback, truncate);
+
+  DCHECK(node_.Data()->dirty);
+  if (index < 0 || index >= kNumStreams)
+    return net::ERR_INVALID_ARGUMENT;
+
+  if (offset < 0 || buf_len < 0)
+    return net::ERR_INVALID_ARGUMENT;
+
+  backend_->background_queue()->WriteData(this, index, offset, buf, buf_len,
+                                          truncate, callback);
+  return net::ERR_IO_PENDING;
+}
+
+int EntryImpl::ReadSparseData(int64 offset, net::IOBuffer* buf, int buf_len,
+                              net::CompletionCallback* callback) {
+  if (!callback)
+    return ReadSparseDataImpl(offset, buf, buf_len, callback);
+
+  backend_->background_queue()->ReadSparseData(this, offset, buf, buf_len,
+                                               callback);
+  return net::ERR_IO_PENDING;
+}
+
+int EntryImpl::WriteSparseData(int64 offset, net::IOBuffer* buf, int buf_len,
+                               net::CompletionCallback* callback) {
+  if (!callback)
+    return WriteSparseDataImpl(offset, buf, buf_len, callback);
+
+  backend_->background_queue()->WriteSparseData(this, offset, buf, buf_len,
+                                                callback);
+  return net::ERR_IO_PENDING;
+}
+
+int EntryImpl::GetAvailableRange(int64 offset, int len, int64* start,
+                                 CompletionCallback* callback) {
+  backend_->background_queue()->GetAvailableRange(this, offset, len, start,
+                                                  callback);
+  return net::ERR_IO_PENDING;
+}
+
+bool EntryImpl::CouldBeSparse() const {
+  if (sparse_.get())
+    return true;
+
+  scoped_ptr<SparseControl> sparse;
+  sparse.reset(new SparseControl(const_cast<EntryImpl*>(this)));
+  return sparse->CouldBeSparse();
+}
+
+void EntryImpl::CancelSparseIO() {
+  backend_->background_queue()->CancelSparseIO(this);
+}
+
+int EntryImpl::ReadyForSparseIO(net::CompletionCallback* callback) {
+  if (!sparse_.get())
+    return net::OK;
+
+  backend_->background_queue()->ReadyForSparseIO(this, callback);
+  return net::ERR_IO_PENDING;
+}
+
+// ------------------------------------------------------------------------
+
+void EntryImpl::DoomImpl() {
+  if (doomed_)
+    return;
+
+  SetPointerForInvalidEntry(backend_->GetCurrentEntryId());
+  backend_->InternalDoomEntry(this);
+}
+
+int EntryImpl::ReadDataImpl(int index, int offset, net::IOBuffer* buf,
+                            int buf_len, CompletionCallback* callback) {
+  DCHECK(node_.Data()->dirty);
+  if (index < 0 || index >= kNumStreams)
+    return net::ERR_INVALID_ARGUMENT;
+
+  int entry_size = entry_.Data()->data_size[index];
+  if (offset >= entry_size || offset < 0 || !buf_len)
+    return 0;
+
+  if (buf_len < 0)
+    return net::ERR_INVALID_ARGUMENT;
+
+  TimeTicks start = TimeTicks::Now();
 
   if (offset + buf_len > entry_size)
     buf_len = entry_size - offset;
@@ -207,6 +300,7 @@
   UpdateRank(false);
 
   backend_->OnEvent(Stats::READ_DATA);
+  backend_->OnRead(buf_len);
 
   if (user_buffers_[index].get()) {
     // Complete the operation locally.
@@ -231,8 +325,8 @@
                    kBlockHeaderSize;
 
   SyncCallback* io_callback = NULL;
-  if (completion_callback)
-    io_callback = new SyncCallback(this, buf, completion_callback);
+  if (callback)
+    io_callback = new SyncCallback(this, buf, callback);
 
   bool completed;
   if (!file->Read(buf->data(), buf_len, file_offset, io_callback, &completed)) {
@@ -245,12 +339,12 @@
     io_callback->Discard();
 
   ReportIOTime(kRead, start);
-  return (completed || !completion_callback) ? buf_len : net::ERR_IO_PENDING;
+  return (completed || !callback) ? buf_len : net::ERR_IO_PENDING;
 }
 
-int EntryImpl::WriteData(int index, int offset, net::IOBuffer* buf, int buf_len,
-                         net::CompletionCallback* completion_callback,
-                         bool truncate) {
+int EntryImpl::WriteDataImpl(int index, int offset, net::IOBuffer* buf,
+                             int buf_len, CompletionCallback* callback,
+                             bool truncate) {
   DCHECK(node_.Data()->dirty);
   if (index < 0 || index >= kNumStreams)
     return net::ERR_INVALID_ARGUMENT;
@@ -260,7 +354,7 @@
 
   int max_file_size = backend_->MaxFileSize();
 
-  // offset of buf_len could be negative numbers.
+  // offset or buf_len could be negative numbers.
   if (offset > max_file_size || buf_len > max_file_size ||
       offset + buf_len > max_file_size) {
     int size = offset + buf_len;
@@ -270,7 +364,7 @@
     return net::ERR_FAILED;
   }
 
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
 
   // Read the size at this point (it may change inside prepare).
   int entry_size = entry_.Data()->data_size[index];
@@ -300,6 +394,7 @@
   UpdateRank(true);
 
   backend_->OnEvent(Stats::WRITE_DATA);
+  backend_->OnWrite(buf_len);
 
   if (user_buffers_[index].get()) {
     // Complete the operation locally.
@@ -330,8 +425,8 @@
     return 0;
 
   SyncCallback* io_callback = NULL;
-  if (completion_callback)
-    io_callback = new SyncCallback(this, buf, completion_callback);
+  if (callback)
+    io_callback = new SyncCallback(this, buf, callback);
 
   bool completed;
   if (!file->Write(buf->data(), buf_len, file_offset, io_callback,
@@ -345,38 +440,38 @@
     io_callback->Discard();
 
   ReportIOTime(kWrite, start);
-  return (completed || !completion_callback) ? buf_len : net::ERR_IO_PENDING;
+  return (completed || !callback) ? buf_len : net::ERR_IO_PENDING;
 }
 
-int EntryImpl::ReadSparseData(int64 offset, net::IOBuffer* buf, int buf_len,
-                              net::CompletionCallback* completion_callback) {
+int EntryImpl::ReadSparseDataImpl(int64 offset, net::IOBuffer* buf, int buf_len,
+                                  CompletionCallback* callback) {
   DCHECK(node_.Data()->dirty);
   int result = InitSparseData();
   if (net::OK != result)
     return result;
 
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
   result = sparse_->StartIO(SparseControl::kReadOperation, offset, buf, buf_len,
-                            completion_callback);
+                            callback);
   ReportIOTime(kSparseRead, start);
   return result;
 }
 
-int EntryImpl::WriteSparseData(int64 offset, net::IOBuffer* buf, int buf_len,
-                               net::CompletionCallback* completion_callback) {
+int EntryImpl::WriteSparseDataImpl(int64 offset, net::IOBuffer* buf,
+                                   int buf_len, CompletionCallback* callback) {
   DCHECK(node_.Data()->dirty);
   int result = InitSparseData();
   if (net::OK != result)
     return result;
 
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
   result = sparse_->StartIO(SparseControl::kWriteOperation, offset, buf,
-                            buf_len, completion_callback);
+                            buf_len, callback);
   ReportIOTime(kSparseWrite, start);
   return result;
 }
 
-int EntryImpl::GetAvailableRange(int64 offset, int len, int64* start) {
+int EntryImpl::GetAvailableRangeImpl(int64 offset, int len, int64* start) {
   int result = InitSparseData();
   if (net::OK != result)
     return result;
@@ -384,23 +479,16 @@
   return sparse_->GetAvailableRange(offset, len, start);
 }
 
-int EntryImpl::GetAvailableRange(int64 offset, int len, int64* start,
-                                 CompletionCallback* callback) {
-  return GetAvailableRange(offset, len, start);
-}
-
-void EntryImpl::CancelSparseIO() {
+void EntryImpl::CancelSparseIOImpl() {
   if (!sparse_.get())
     return;
 
   sparse_->CancelIO();
 }
 
-int EntryImpl::ReadyForSparseIO(net::CompletionCallback* completion_callback) {
-  if (!sparse_.get())
-    return net::OK;
-
-  return sparse_->ReadyToUse(completion_callback);
+int EntryImpl::ReadyForSparseIOImpl(CompletionCallback* callback) {
+  DCHECK(sparse_.get());
+  return sparse_->ReadyToUse(callback);
 }
 
 // ------------------------------------------------------------------------
@@ -431,24 +519,26 @@
       return false;
 
     entry_store->long_key = address.value();
-    key_file_ = GetBackingFile(address, kKeyFileIndex);
+    File* key_file = GetBackingFile(address, kKeyFileIndex);
+    key_ = key;
 
     size_t offset = 0;
     if (address.is_block_file())
       offset = address.start_block() * address.BlockSize() + kBlockHeaderSize;
 
-    if (!key_file_ || !key_file_->Write(key.data(), key.size(), offset)) {
+    if (!key_file || !key_file->Write(key.data(), key.size(), offset)) {
       DeleteData(address, kKeyFileIndex);
       return false;
     }
 
     if (address.is_separate_file())
-      key_file_->SetLength(key.size() + 1);
+      key_file->SetLength(key.size() + 1);
   } else {
     memcpy(entry_store->key, key.data(), key.size());
     entry_store->key[key.size()] = '\0';
   }
   backend_->ModifyStorageSize(0, static_cast<int32>(key.size()));
+  CACHE_UMA(COUNTS, "KeySize", 0, static_cast<int32>(key.size()));
   node->dirty = backend_->GetCurrentEntryId();
   Log("Create Entry ");
   return true;
@@ -596,7 +686,7 @@
   node_.set_modified();
 }
 
-void EntryImpl::ReportIOTime(Operation op, const base::Time& start) {
+void EntryImpl::ReportIOTime(Operation op, const base::TimeTicks& start) {
   int group = backend_->GetSizeGroup();
   switch (op) {
     case kRead:
@@ -859,15 +949,14 @@
   if (async) {
     if (!file->PostWrite(user_buffers_[index].get(), len, offset))
       return false;
+    // The buffer is deleted from the PostWrite operation.
+    ignore_result(user_buffers_[index].release());
   } else {
     if (!file->Write(user_buffers_[index].get(), len, offset, NULL, NULL))
       return false;
     user_buffers_[index].reset(NULL);
   }
 
-  // The buffer is deleted from the PostWrite operation.
-  user_buffers_[index].release();
-
   return true;
 }
 
@@ -875,10 +964,12 @@
   if (sparse_.get())
     return net::OK;
 
-  sparse_.reset(new SparseControl(this));
-  int result = sparse_->Init();
-  if (net::OK != result)
-    sparse_.reset();
+  // Use a local variable so that sparse_ never goes from 'valid' to NULL.
+  scoped_ptr<SparseControl> sparse(new SparseControl(this));
+  int result = sparse->Init();
+  if (net::OK == result)
+    sparse_.swap(sparse);
+
   return result;
 }
 
@@ -893,7 +984,7 @@
 
 void EntryImpl::GetData(int index, char** buffer, Addr* address) {
   if (user_buffers_[index].get()) {
-    // The data is already in memory, just copy it an we're done.
+    // The data is already in memory, just copy it and we're done.
     int data_len = entry_.Data()->data_size[index];
     DCHECK(data_len <= kMaxBlockSize);
     *buffer = new char[data_len];
diff --git a/net/disk_cache/entry_impl.h b/net/disk_cache/entry_impl.h
index 76e4965..9d37fff 100644
--- a/net/disk_cache/entry_impl.h
+++ b/net/disk_cache/entry_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -47,12 +47,26 @@
                              net::CompletionCallback* completion_callback);
   virtual int WriteSparseData(int64 offset, net::IOBuffer* buf, int buf_len,
                               net::CompletionCallback* completion_callback);
-  virtual int GetAvailableRange(int64 offset, int len, int64* start);
   virtual int GetAvailableRange(int64 offset, int len, int64* start,
                                 CompletionCallback* callback);
+  virtual bool CouldBeSparse() const;
   virtual void CancelSparseIO();
   virtual int ReadyForSparseIO(net::CompletionCallback* completion_callback);
 
+  // Background implementation of the Entry interface.
+  void DoomImpl();
+  int ReadDataImpl(int index, int offset, net::IOBuffer* buf, int buf_len,
+                   CompletionCallback* callback);
+  int WriteDataImpl(int index, int offset, net::IOBuffer* buf, int buf_len,
+                    CompletionCallback* callback, bool truncate);
+  int ReadSparseDataImpl(int64 offset, net::IOBuffer* buf, int buf_len,
+                         CompletionCallback* callback);
+  int WriteSparseDataImpl(int64 offset, net::IOBuffer* buf, int buf_len,
+                          CompletionCallback* callback);
+  int GetAvailableRangeImpl(int64 offset, int len, int64* start);
+  void CancelSparseIOImpl();
+  int ReadyForSparseIOImpl(CompletionCallback* callback);
+
   inline CacheEntryBlock* entry() {
     return &entry_;
   }
@@ -112,7 +126,7 @@
   void SetTimes(base::Time last_used, base::Time last_modified);
 
   // Generates a histogram for the time spent working on this operation.
-  void ReportIOTime(Operation op, const base::Time& start);
+  void ReportIOTime(Operation op, const base::TimeTicks& start);
 
  private:
   enum {
@@ -183,13 +197,12 @@
   scoped_array<char> user_buffers_[kNumStreams];  // Store user data.
   // Files to store external user data and key.
   scoped_refptr<File> files_[kNumStreams + 1];
-  // Copy of the file used to store the key. We don't own this object.
-  mutable File* key_file_;
+  mutable std::string key_;           // Copy of the key.
   int unreported_size_[kNumStreams];  // Bytes not reported yet to the backend.
   bool doomed_;               // True if this entry was removed from the cache.
   scoped_ptr<SparseControl> sparse_;  // Support for sparse entries.
 
-  DISALLOW_EVIL_CONSTRUCTORS(EntryImpl);
+  DISALLOW_COPY_AND_ASSIGN(EntryImpl);
 };
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc
index e60a3da..067f260 100644
--- a/net/disk_cache/entry_unittest.cc
+++ b/net/disk_cache/entry_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -34,18 +34,19 @@
   void ZeroLengthIO();
   void ReuseEntry(int size);
   void InvalidData();
-  void DoomEntry();
+  void DoomNormalEntry();
   void DoomedEntry();
   void BasicSparseIO(bool async);
   void HugeSparseIO(bool async);
   void GetAvailableRange();
+  void CouldBeSparse();
   void DoomSparseEntry();
   void PartialSparseEntry();
 };
 
 void DiskCacheEntryTest::InternalSyncIO() {
   disk_cache::Entry *entry1 = NULL;
-  ASSERT_TRUE(cache_->CreateEntry("the first key", &entry1));
+  ASSERT_EQ(net::OK, CreateEntry("the first key", &entry1));
   ASSERT_TRUE(NULL != entry1);
 
   const int kSize1 = 10;
@@ -81,10 +82,12 @@
 
   entry1->Doom();
   entry1->Close();
+  FlushQueueForTest();
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheEntryTest, InternalSyncIO) {
+  SetDirectMode();
   InitCache();
   InternalSyncIO();
 }
@@ -97,7 +100,7 @@
 
 void DiskCacheEntryTest::InternalAsyncIO() {
   disk_cache::Entry *entry1 = NULL;
-  ASSERT_TRUE(cache_->CreateEntry("the first key", &entry1));
+  ASSERT_EQ(net::OK, CreateEntry("the first key", &entry1));
   ASSERT_TRUE(NULL != entry1);
 
   // Avoid using internal buffers for the test. We have to write something to
@@ -108,7 +111,7 @@
   EXPECT_EQ(0, entry1->WriteData(0, 15 * 1024, NULL, 0, NULL, false));
   EXPECT_EQ(0, entry1->WriteData(1, 15 * 1024, NULL, 0, NULL, false));
   entry1->Close();
-  ASSERT_TRUE(cache_->OpenEntry("the first key", &entry1));
+  ASSERT_EQ(net::OK, OpenEntry("the first key", &entry1));
 
   // Let's verify that each IO goes to the right callback object.
   CallbackTest callback1(false);
@@ -222,10 +225,12 @@
 
   entry1->Doom();
   entry1->Close();
+  FlushQueueForTest();
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheEntryTest, InternalAsyncIO) {
+  SetDirectMode();
   InitCache();
   InternalAsyncIO();
 }
@@ -238,7 +243,7 @@
 
 void DiskCacheEntryTest::ExternalSyncIO() {
   disk_cache::Entry *entry1;
-  ASSERT_TRUE(cache_->CreateEntry("the first key", &entry1));
+  ASSERT_EQ(net::OK, CreateEntry("the first key", &entry1));
 
   const int kSize1 = 17000;
   const int kSize2 = 25000;
@@ -268,10 +273,12 @@
 
   entry1->Doom();
   entry1->Close();
+  FlushQueueForTest();
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheEntryTest, ExternalSyncIO) {
+  SetDirectMode();
   InitCache();
   ExternalSyncIO();
 }
@@ -284,7 +291,7 @@
 
 void DiskCacheEntryTest::ExternalAsyncIO() {
   disk_cache::Entry *entry1;
-  ASSERT_TRUE(cache_->CreateEntry("the first key", &entry1));
+  ASSERT_EQ(net::OK, CreateEntry("the first key", &entry1));
 
   // Let's verify that each IO goes to the right callback object.
   CallbackTest callback1(false);
@@ -366,19 +373,21 @@
   EXPECT_TRUE(17000 == ret || net::ERR_IO_PENDING == ret);
   if (net::ERR_IO_PENDING == ret)
     expected++;
-  EXPECT_EQ(37000, entry1->GetDataSize(1));
 
   EXPECT_TRUE(helper.WaitUntilCacheIoFinished(expected));
+  EXPECT_EQ(37000, entry1->GetDataSize(1));
 
   EXPECT_FALSE(g_cache_tests_error);
   EXPECT_EQ(expected, g_cache_tests_received);
 
   entry1->Doom();
   entry1->Close();
+  FlushQueueForTest();
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheEntryTest, ExternalAsyncIO) {
+  SetDirectMode();
   InitCache();
   ExternalAsyncIO();
 }
@@ -391,7 +400,7 @@
 
 void DiskCacheEntryTest::StreamAccess() {
   disk_cache::Entry *entry = NULL;
-  ASSERT_TRUE(cache_->CreateEntry("the first key", &entry));
+  ASSERT_EQ(net::OK, CreateEntry("the first key", &entry));
   ASSERT_TRUE(NULL != entry);
 
   const int kBufferSize = 1024;
@@ -427,7 +436,7 @@
 void DiskCacheEntryTest::GetKey() {
   std::string key1("the first key");
   disk_cache::Entry *entry1;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
   EXPECT_EQ(key1, entry1->GetKey()) << "short key";
   entry1->Close();
 
@@ -439,14 +448,14 @@
   key_buffer[1000] = '\0';
 
   key1 = key_buffer;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
   EXPECT_TRUE(key1 == entry1->GetKey()) << "1000 bytes key";
   entry1->Close();
 
   key_buffer[1000] = 'p';
   key_buffer[3000] = '\0';
   key1 = key_buffer;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
   EXPECT_TRUE(key1 == entry1->GetKey()) << "medium size key";
   entry1->Close();
 
@@ -454,7 +463,7 @@
   key_buffer[19999] = '\0';
 
   key1 = key_buffer;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
   EXPECT_TRUE(key1 == entry1->GetKey()) << "long key";
   entry1->Close();
 }
@@ -473,7 +482,7 @@
 void DiskCacheEntryTest::GrowData() {
   std::string key1("the first key");
   disk_cache::Entry *entry1, *entry2;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
 
   const int kSize = 20000;
   scoped_refptr<net::IOBuffer> buffer1 = new net::IOBuffer(kSize);
@@ -499,13 +508,13 @@
   entry1->Close();
 
   memset(buffer2->data(), 0, kSize);
-  ASSERT_TRUE(cache_->CreateEntry("Second key", &entry2));
+  ASSERT_EQ(net::OK, CreateEntry("Second key", &entry2));
   EXPECT_EQ(10, entry2->WriteData(0, 0, buffer1, 10, NULL, false));
   EXPECT_EQ(10, entry2->GetDataSize(0));
   entry2->Close();
 
   // Go from an internal address to a bigger block size.
-  ASSERT_TRUE(cache_->OpenEntry("Second key", &entry2));
+  ASSERT_EQ(net::OK, OpenEntry("Second key", &entry2));
   EXPECT_EQ(2000, entry2->WriteData(0, 0, buffer1, 2000, NULL, false));
   EXPECT_EQ(2000, entry2->GetDataSize(0));
   EXPECT_EQ(2000, entry2->ReadData(0, 0, buffer2, 2000, NULL));
@@ -514,7 +523,7 @@
   memset(buffer2->data(), 0, kSize);
 
   // Go from an internal address to an external one.
-  ASSERT_TRUE(cache_->OpenEntry("Second key", &entry2));
+  ASSERT_EQ(net::OK, OpenEntry("Second key", &entry2));
   EXPECT_EQ(20000, entry2->WriteData(0, 0, buffer1, kSize, NULL, false));
   EXPECT_EQ(20000, entry2->GetDataSize(0));
   EXPECT_EQ(20000, entry2->ReadData(0, 0, buffer2, kSize, NULL));
@@ -536,7 +545,7 @@
 void DiskCacheEntryTest::TruncateData() {
   std::string key1("the first key");
   disk_cache::Entry *entry1;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
 
   const int kSize1 = 20000;
   const int kSize2 = 20000;
@@ -558,7 +567,7 @@
   EXPECT_EQ(0, entry1->WriteData(0, 0, buffer1, 0, NULL, true));
   EXPECT_EQ(0, entry1->GetDataSize(0));
   entry1->Close();
-  ASSERT_TRUE(cache_->OpenEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, OpenEntry(key1, &entry1));
 
   // Go to an external file.
   EXPECT_EQ(20000, entry1->WriteData(0, 0, buffer1, 20000, NULL, true));
@@ -612,7 +621,7 @@
 void DiskCacheEntryTest::ZeroLengthIO() {
   std::string key1("the first key");
   disk_cache::Entry *entry1;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
 
   EXPECT_EQ(0, entry1->ReadData(0, 0, NULL, 0, NULL));
   EXPECT_EQ(0, entry1->WriteData(0, 0, NULL, 0, NULL, false));
@@ -641,11 +650,11 @@
 void DiskCacheEntryTest::ReuseEntry(int size) {
   std::string key1("the first key");
   disk_cache::Entry *entry;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry));
 
   entry->Close();
   std::string key2("the second key");
-  ASSERT_TRUE(cache_->CreateEntry(key2, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key2, &entry));
 
   scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(size);
   CacheTestFillBuffer(buffer->data(), size, false);
@@ -654,11 +663,11 @@
     EXPECT_EQ(0, entry->WriteData(0, 0, buffer, 0, NULL, true));
     EXPECT_EQ(size, entry->WriteData(0, 0, buffer, size, NULL, false));
     entry->Close();
-    ASSERT_TRUE(cache_->OpenEntry(key2, &entry));
+    ASSERT_EQ(net::OK, OpenEntry(key2, &entry));
   }
 
   entry->Close();
-  ASSERT_TRUE(cache_->OpenEntry(key1, &entry)) << "have not evicted this entry";
+  ASSERT_EQ(net::OK, OpenEntry(key1, &entry)) << "have not evicted this entry";
   entry->Close();
 }
 
@@ -696,7 +705,7 @@
 void DiskCacheEntryTest::InvalidData() {
   std::string key1("the first key");
   disk_cache::Entry *entry1;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
 
   const int kSize1 = 20000;
   const int kSize2 = 20000;
@@ -714,7 +723,7 @@
   EXPECT_EQ(100, entry1->ReadData(0, 300, buffer3, 100, NULL));
   EXPECT_TRUE(!memcmp(buffer3->data(), buffer2->data(), 100));
   entry1->Close();
-  ASSERT_TRUE(cache_->OpenEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, OpenEntry(key1, &entry1));
 
   // The entry is now on disk. Load it and extend it.
   EXPECT_EQ(200, entry1->WriteData(0, 800, buffer1, 200, NULL, false));
@@ -722,7 +731,7 @@
   EXPECT_EQ(100, entry1->ReadData(0, 700, buffer3, 100, NULL));
   EXPECT_TRUE(!memcmp(buffer3->data(), buffer2->data(), 100));
   entry1->Close();
-  ASSERT_TRUE(cache_->OpenEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, OpenEntry(key1, &entry1));
 
   // This time using truncate.
   EXPECT_EQ(200, entry1->WriteData(0, 1800, buffer1, 200, NULL, true));
@@ -768,10 +777,10 @@
   InvalidData();
 }
 
-void DiskCacheEntryTest::DoomEntry() {
+void DiskCacheEntryTest::DoomNormalEntry() {
   std::string key1("the first key");
   disk_cache::Entry *entry1;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
   entry1->Doom();
   entry1->Close();
 
@@ -781,33 +790,36 @@
   buffer->data()[19999] = '\0';
 
   key1 = buffer->data();
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
   EXPECT_EQ(20000, entry1->WriteData(0, 0, buffer, kSize, NULL, false));
   EXPECT_EQ(20000, entry1->WriteData(1, 0, buffer, kSize, NULL, false));
   entry1->Doom();
   entry1->Close();
 
+  FlushQueueForTest();
   EXPECT_EQ(0, cache_->GetEntryCount());
 }
 
 TEST_F(DiskCacheEntryTest, DoomEntry) {
+  SetDirectMode();
   InitCache();
-  DoomEntry();
+  DoomNormalEntry();
 }
 
 TEST_F(DiskCacheEntryTest, MemoryOnlyDoomEntry) {
   SetMemoryOnlyMode();
   InitCache();
-  DoomEntry();
+  DoomNormalEntry();
 }
 
 // Verify that basic operations work as expected with doomed entries.
 void DiskCacheEntryTest::DoomedEntry() {
   std::string key("the first key");
   disk_cache::Entry *entry;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
   entry->Doom();
 
+  FlushQueueForTest();
   EXPECT_EQ(0, cache_->GetEntryCount());
   Time initial = Time::Now();
   PlatformThread::Sleep(20);
@@ -830,14 +842,15 @@
 }
 
 TEST_F(DiskCacheEntryTest, DoomedEntry) {
+  SetDirectMode();
   InitCache();
-  DoomEntry();
+  DoomedEntry();
 }
 
 TEST_F(DiskCacheEntryTest, MemoryOnlyDoomedEntry) {
   SetMemoryOnlyMode();
   InitCache();
-  DoomEntry();
+  DoomedEntry();
 }
 
 // Test that child entries in a memory cache backend are not visible from
@@ -852,7 +865,7 @@
 
   std::string key("the first key");
   disk_cache::Entry* parent_entry;
-  ASSERT_TRUE(cache_->CreateEntry(key, &parent_entry));
+  ASSERT_EQ(net::OK, CreateEntry(key, &parent_entry));
 
   // Writes to the parent entry.
   EXPECT_EQ(kSize, parent_entry->WriteSparseData(0, buf, kSize, NULL));
@@ -866,7 +879,7 @@
   void* iter = NULL;
   disk_cache::Entry* entry = NULL;
   int count = 0;
-  while (cache_->OpenNextEntry(&iter, &entry)) {
+  while (OpenNextEntry(&iter, &entry) == net::OK) {
     ASSERT_TRUE(entry != NULL);
     ++count;
     disk_cache::MemEntryImpl* mem_entry =
@@ -919,7 +932,7 @@
 void DiskCacheEntryTest::BasicSparseIO(bool async) {
   std::string key("the first key");
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
 
   const int kSize = 2048;
   scoped_refptr<net::IOBuffer> buf_1 = new net::IOBuffer(kSize);
@@ -938,7 +951,7 @@
   entry->Close();
 
   // Check everything again.
-  ASSERT_TRUE(cache_->OpenEntry(key, &entry));
+  ASSERT_EQ(net::OK, OpenEntry(key, &entry));
   VerifyContentSparseIO(entry, 0, buf_1->data(), kSize, async);
   VerifyContentSparseIO(entry, 0x400000, buf_1->data(), kSize, async);
   VerifyContentSparseIO(entry, 0x800000000LL, buf_1->data(), kSize, async);
@@ -970,7 +983,7 @@
 void DiskCacheEntryTest::HugeSparseIO(bool async) {
   std::string key("the first key");
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
 
   // Write 1.2 MB so that we cover multiple entries.
   const int kSize = 1200 * 1024;
@@ -983,7 +996,7 @@
   entry->Close();
 
   // Check it again.
-  ASSERT_TRUE(cache_->OpenEntry(key, &entry));
+  ASSERT_EQ(net::OK, OpenEntry(key, &entry));
   VerifyContentSparseIO(entry, 0x20F0000, buf_1->data(), kSize, async);
   entry->Close();
 }
@@ -1013,7 +1026,7 @@
 void DiskCacheEntryTest::GetAvailableRange() {
   std::string key("the first key");
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
 
   const int kSize = 16 * 1024;
   scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSize);
@@ -1025,28 +1038,37 @@
 
   // We stop at the first empty block.
   int64 start;
-  EXPECT_EQ(kSize, entry->GetAvailableRange(0x20F0000, kSize * 2, &start));
+  TestCompletionCallback cb;
+  int rv = entry->GetAvailableRange(0x20F0000, kSize * 2, &start, &cb);
+  EXPECT_EQ(kSize, cb.GetResult(rv));
   EXPECT_EQ(0x20F0000, start);
 
   start = 0;
-  EXPECT_EQ(0, entry->GetAvailableRange(0, kSize, &start));
-  EXPECT_EQ(0, entry->GetAvailableRange(0x20F0000 - kSize, kSize, &start));
-  EXPECT_EQ(kSize, entry->GetAvailableRange(0, 0x2100000, &start));
+  rv = entry->GetAvailableRange(0, kSize, &start, &cb);
+  EXPECT_EQ(0, cb.GetResult(rv));
+  rv = entry->GetAvailableRange(0x20F0000 - kSize, kSize, &start, &cb);
+  EXPECT_EQ(0, cb.GetResult(rv));
+  rv = entry->GetAvailableRange(0, 0x2100000, &start, &cb);
+  EXPECT_EQ(kSize, cb.GetResult(rv));
   EXPECT_EQ(0x20F0000, start);
 
   // We should be able to Read based on the results of GetAvailableRange.
   start = -1;
-  EXPECT_EQ(0, entry->GetAvailableRange(0x2100000, kSize, &start));
-  EXPECT_EQ(0, entry->ReadSparseData(start, buf, kSize, NULL));
+  rv = entry->GetAvailableRange(0x2100000, kSize, &start, &cb);
+  EXPECT_EQ(0, cb.GetResult(rv));
+  rv = entry->ReadSparseData(start, buf, kSize, &cb);
+  EXPECT_EQ(0, cb.GetResult(rv));
 
   start = 0;
-  EXPECT_EQ(0x2000, entry->GetAvailableRange(0x20F2000, kSize, &start));
+  rv = entry->GetAvailableRange(0x20F2000, kSize, &start, &cb);
+  EXPECT_EQ(0x2000, cb.GetResult(rv));
   EXPECT_EQ(0x20F2000, start);
   EXPECT_EQ(0x2000, entry->ReadSparseData(start, buf, kSize, NULL));
 
   // Make sure that we respect the |len| argument.
   start = 0;
-  EXPECT_EQ(1, entry->GetAvailableRange(0x20F0001 - kSize, kSize, &start));
+  rv = entry->GetAvailableRange(0x20F0001 - kSize, kSize, &start, &cb);
+  EXPECT_EQ(1, cb.GetResult(rv));
   EXPECT_EQ(0x20F0000, start);
 
   entry->Close();
@@ -1063,6 +1085,53 @@
   GetAvailableRange();
 }
 
+void DiskCacheEntryTest::CouldBeSparse() {
+  std::string key("the first key");
+  disk_cache::Entry* entry;
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
+
+  const int kSize = 16 * 1024;
+  scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSize);
+  CacheTestFillBuffer(buf->data(), kSize, false);
+
+  // Write at offset 0x20F0000 (33 MB - 64 KB).
+  EXPECT_EQ(kSize, entry->WriteSparseData(0x20F0000, buf, kSize, NULL));
+
+  EXPECT_TRUE(entry->CouldBeSparse());
+  entry->Close();
+
+  ASSERT_EQ(net::OK, OpenEntry(key, &entry));
+  EXPECT_TRUE(entry->CouldBeSparse());
+  entry->Close();
+
+  // Now verify a regular entry.
+  key.assign("another key");
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
+  EXPECT_FALSE(entry->CouldBeSparse());
+
+  EXPECT_EQ(kSize, entry->WriteData(0, 0, buf, kSize, NULL, false));
+  EXPECT_EQ(kSize, entry->WriteData(1, 0, buf, kSize, NULL, false));
+  EXPECT_EQ(kSize, entry->WriteData(2, 0, buf, kSize, NULL, false));
+
+  EXPECT_FALSE(entry->CouldBeSparse());
+  entry->Close();
+
+  ASSERT_EQ(net::OK, OpenEntry(key, &entry));
+  EXPECT_FALSE(entry->CouldBeSparse());
+  entry->Close();
+}
+
+TEST_F(DiskCacheEntryTest, CouldBeSparse) {
+  InitCache();
+  CouldBeSparse();
+}
+
+TEST_F(DiskCacheEntryTest, MemoryCouldBeSparse) {
+  SetMemoryOnlyMode();
+  InitCache();
+  CouldBeSparse();
+}
+
 TEST_F(DiskCacheEntryTest, MemoryOnlyMisalignedSparseIO) {
   SetMemoryOnlyMode();
   InitCache();
@@ -1074,7 +1143,7 @@
 
   std::string key("the first key");
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
 
   // This loop writes back to back starting from offset 0 and 9000.
   for (int i = 0; i < kSize; i += 1024) {
@@ -1104,7 +1173,7 @@
 
   disk_cache::Entry* entry;
   std::string key("the first key");
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
 
   // Writes in the middle of an entry.
   EXPECT_EQ(1024, entry->WriteSparseData(0, buf, 1024, NULL));
@@ -1115,31 +1184,38 @@
   EXPECT_EQ(8192, entry->WriteSparseData(50000, buf, 8192, NULL));
 
   int64 start;
+  TestCompletionCallback cb;
   // Test that we stop at a discontinuous child at the second block.
-  EXPECT_EQ(1024, entry->GetAvailableRange(0, 10000, &start));
+  int rv = entry->GetAvailableRange(0, 10000, &start, &cb);
+  EXPECT_EQ(1024, cb.GetResult(rv));
   EXPECT_EQ(0, start);
 
   // Test that number of bytes is reported correctly when we start from the
   // middle of a filled region.
-  EXPECT_EQ(512, entry->GetAvailableRange(512, 10000, &start));
+  rv = entry->GetAvailableRange(512, 10000, &start, &cb);
+  EXPECT_EQ(512, cb.GetResult(rv));
   EXPECT_EQ(512, start);
 
   // Test that we found bytes in the child of next block.
-  EXPECT_EQ(1024, entry->GetAvailableRange(1024, 10000, &start));
+  rv = entry->GetAvailableRange(1024, 10000, &start, &cb);
+  EXPECT_EQ(1024, cb.GetResult(rv));
   EXPECT_EQ(5120, start);
 
   // Test that the desired length is respected. It starts within a filled
   // region.
-  EXPECT_EQ(512, entry->GetAvailableRange(5500, 512, &start));
+  rv = entry->GetAvailableRange(5500, 512, &start, &cb);
+  EXPECT_EQ(512, cb.GetResult(rv));
   EXPECT_EQ(5500, start);
 
   // Test that the desired length is respected. It starts before a filled
   // region.
-  EXPECT_EQ(500, entry->GetAvailableRange(5000, 620, &start));
+  rv = entry->GetAvailableRange(5000, 620, &start, &cb);
+  EXPECT_EQ(500, cb.GetResult(rv));
   EXPECT_EQ(5120, start);
 
   // Test that multiple blocks are scanned.
-  EXPECT_EQ(8192, entry->GetAvailableRange(40000, 20000, &start));
+  rv = entry->GetAvailableRange(40000, 20000, &start, &cb);
+  EXPECT_EQ(8192, cb.GetResult(rv));
   EXPECT_EQ(50000, start);
 
   entry->Close();
@@ -1149,8 +1225,8 @@
   std::string key1("the first key");
   std::string key2("the second key");
   disk_cache::Entry *entry1, *entry2;
-  ASSERT_TRUE(cache_->CreateEntry(key1, &entry1));
-  ASSERT_TRUE(cache_->CreateEntry(key2, &entry2));
+  ASSERT_EQ(net::OK, CreateEntry(key1, &entry1));
+  ASSERT_EQ(net::OK, CreateEntry(key2, &entry2));
 
   const int kSize = 4 * 1024;
   scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSize);
@@ -1177,7 +1253,7 @@
   entry2->Close();
 
   // Doom the second entry after it's fully saved.
-  EXPECT_TRUE(cache_->DoomEntry(key2));
+  EXPECT_EQ(net::OK, DoomEntry(key2));
 
   // Make sure we do all needed work. This may fail for entry2 if between Close
   // and DoomEntry the system decides to remove all traces of the file from the
@@ -1199,6 +1275,8 @@
 }
 
 TEST_F(DiskCacheEntryTest, DoomSparseEntry) {
+  SetDirectMode();
+  UseCurrentThread();
   InitCache();
   DoomSparseEntry();
 }
@@ -1212,7 +1290,7 @@
 void DiskCacheEntryTest::PartialSparseEntry() {
   std::string key("the first key");
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
 
   // We should be able to deal with IO that is not aligned to the block size
   // of a sparse entry, at least to write a big range without leaving holes.
@@ -1229,7 +1307,7 @@
   EXPECT_EQ(kSmallSize,
             entry->WriteSparseData(1080321, buf1, kSmallSize, NULL));
   entry->Close();
-  ASSERT_TRUE(cache_->OpenEntry(key, &entry));
+  ASSERT_EQ(net::OK, OpenEntry(key, &entry));
 
   scoped_refptr<net::IOBuffer> buf2 = new net::IOBuffer(kSize);
   memset(buf2->data(), 0, kSize);
@@ -1244,37 +1322,48 @@
   EXPECT_EQ(500, entry->ReadSparseData(kSize, buf2, kSize, NULL));
   EXPECT_EQ(0, entry->ReadSparseData(499, buf2, kSize, NULL));
 
+  int rv;
   int64 start;
+  TestCompletionCallback cb;
   if (memory_only_) {
-    EXPECT_EQ(100, entry->GetAvailableRange(0, 600, &start));
+    rv = entry->GetAvailableRange(0, 600, &start, &cb);
+    EXPECT_EQ(100, cb.GetResult(rv));
     EXPECT_EQ(500, start);
   } else {
-    EXPECT_EQ(1024, entry->GetAvailableRange(0, 2048, &start));
+    rv = entry->GetAvailableRange(0, 2048, &start, &cb);
+    EXPECT_EQ(1024, cb.GetResult(rv));
     EXPECT_EQ(1024, start);
   }
-  EXPECT_EQ(500, entry->GetAvailableRange(kSize, kSize, &start));
+  rv = entry->GetAvailableRange(kSize, kSize, &start, &cb);
+  EXPECT_EQ(500, cb.GetResult(rv));
   EXPECT_EQ(kSize, start);
-  EXPECT_EQ(3616, entry->GetAvailableRange(20 * 1024, 10000, &start));
+  rv = entry->GetAvailableRange(20 * 1024, 10000, &start, &cb);
+  EXPECT_EQ(3616, cb.GetResult(rv));
   EXPECT_EQ(20 * 1024, start);
 
   // 1. Query before a filled 1KB block.
   // 2. Query within a filled 1KB block.
   // 3. Query beyond a filled 1KB block.
   if (memory_only_) {
-    EXPECT_EQ(3496, entry->GetAvailableRange(19400, kSize, &start));
+    rv = entry->GetAvailableRange(19400, kSize, &start, &cb);
+    EXPECT_EQ(3496, cb.GetResult(rv));
     EXPECT_EQ(20000, start);
   } else {
-    EXPECT_EQ(3016, entry->GetAvailableRange(19400, kSize, &start));
+    rv = entry->GetAvailableRange(19400, kSize, &start, &cb);
+    EXPECT_EQ(3016, cb.GetResult(rv));
     EXPECT_EQ(20480, start);
   }
-  EXPECT_EQ(1523, entry->GetAvailableRange(3073, kSize, &start));
+  rv = entry->GetAvailableRange(3073, kSize, &start, &cb);
+  EXPECT_EQ(1523, cb.GetResult(rv));
   EXPECT_EQ(3073, start);
-  EXPECT_EQ(0, entry->GetAvailableRange(4600, kSize, &start));
+  rv = entry->GetAvailableRange(4600, kSize, &start, &cb);
+  EXPECT_EQ(0, cb.GetResult(rv));
   EXPECT_EQ(4600, start);
 
   // Now make another write and verify that there is no hole in between.
   EXPECT_EQ(kSize, entry->WriteSparseData(500 + kSize, buf1, kSize, NULL));
-  EXPECT_EQ(7 * 1024 + 500, entry->GetAvailableRange(1024, 10000, &start));
+  rv = entry->GetAvailableRange(1024, 10000, &start, &cb);
+  EXPECT_EQ(7 * 1024 + 500, cb.GetResult(rv));
   EXPECT_EQ(1024, start);
   EXPECT_EQ(kSize, entry->ReadSparseData(kSize, buf2, kSize, NULL));
   EXPECT_EQ(0, memcmp(buf2->data(), buf1->data() + kSize - 500, 500));
@@ -1294,13 +1383,13 @@
   PartialSparseEntry();
 }
 
+// Tests that corrupt sparse children are removed automatically.
 TEST_F(DiskCacheEntryTest, CleanupSparseEntry) {
   InitCache();
   std::string key("the first key");
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
 
-  // Corrupt sparse children should be removed automatically.
   const int kSize = 4 * 1024;
   scoped_refptr<net::IOBuffer> buf1 = new net::IOBuffer(kSize);
   CacheTestFillBuffer(buf1->data(), kSize, false);
@@ -1315,7 +1404,7 @@
   void* iter = NULL;
   int count = 0;
   std::string child_key[2];
-  while (cache_->OpenNextEntry(&iter, &entry)) {
+  while (OpenNextEntry(&iter, &entry) == net::OK) {
     ASSERT_TRUE(entry != NULL);
     // Writing to an entry will alter the LRU list and invalidate the iterator.
     if (entry->GetKey() != key && count < 2)
@@ -1323,14 +1412,14 @@
     entry->Close();
   }
   for (int i = 0; i < 2; i++) {
-    ASSERT_TRUE(cache_->OpenEntry(child_key[i], &entry));
+    ASSERT_EQ(net::OK, OpenEntry(child_key[i], &entry));
     // Overwrite the header's magic and signature.
     EXPECT_EQ(12, entry->WriteData(2, 0, buf1, 12, NULL, false));
     entry->Close();
   }
 
   EXPECT_EQ(4, cache_->GetEntryCount());
-  ASSERT_TRUE(cache_->OpenEntry(key, &entry));
+  ASSERT_EQ(net::OK, OpenEntry(key, &entry));
 
   // Two children should be gone. One while reading and one while writing.
   EXPECT_EQ(0, entry->ReadSparseData(2 * k1Meg + 8192, buf1, kSize, NULL));
@@ -1346,33 +1435,29 @@
 }
 
 TEST_F(DiskCacheEntryTest, CancelSparseIO) {
+  UseCurrentThread();
   InitCache();
   std::string key("the first key");
   disk_cache::Entry* entry;
-  ASSERT_TRUE(cache_->CreateEntry(key, &entry));
+  ASSERT_EQ(net::OK, CreateEntry(key, &entry));
 
   const int kSize = 40 * 1024;
   scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSize);
   CacheTestFillBuffer(buf->data(), kSize, false);
 
-  TestCompletionCallback cb1, cb2, cb3, cb4;
-  int64 offset = 0;
-  int tries = 0;
-  const int maxtries = 100;   // Avoid hang on infinitely fast disks
-  for (int ret = 0; ret != net::ERR_IO_PENDING; offset += kSize * 4) {
-    ret = entry->WriteSparseData(offset, buf, kSize, &cb1);
-    if (++tries > maxtries) {
-       LOG(ERROR) << "Data writes never come back PENDING; skipping test";
-       entry->Close();
-       return;
-    }
-  }
+  // This will open and write two "real" entries.
+  TestCompletionCallback cb1, cb2, cb3, cb4, cb5;
+  int rv = entry->WriteSparseData(1024 * 1024 - 4096, buf, kSize, &cb1);
+  EXPECT_EQ(net::ERR_IO_PENDING, rv);
 
-  // Cannot use the entry at this point.
-  offset = 0;
-  EXPECT_EQ(net::ERR_CACHE_OPERATION_NOT_SUPPORTED,
-            entry->GetAvailableRange(offset, kSize, &offset));
-  EXPECT_EQ(net::OK, entry->ReadyForSparseIO(&cb2));
+  int64 offset = 0;
+  rv = entry->GetAvailableRange(offset, kSize, &offset, &cb5);
+  rv = cb5.GetResult(rv);
+  if (!cb1.have_result()) {
+    // We may or may not have finished writing to the entry. If we have not,
+    // we cannot start another operation at this time.
+    EXPECT_EQ(net::ERR_CACHE_OPERATION_NOT_SUPPORTED, rv);
+  }
 
   // We cancel the pending operation, and register multiple notifications.
   entry->CancelSparseIO();
@@ -1381,21 +1466,22 @@
   entry->CancelSparseIO();  // Should be a no op at this point.
   EXPECT_EQ(net::ERR_IO_PENDING, entry->ReadyForSparseIO(&cb4));
 
-  offset = 0;
-  EXPECT_EQ(net::ERR_CACHE_OPERATION_NOT_SUPPORTED,
-            entry->GetAvailableRange(offset, kSize, &offset));
-  EXPECT_EQ(net::ERR_CACHE_OPERATION_NOT_SUPPORTED,
-            entry->ReadSparseData(offset, buf, kSize, NULL));
-  EXPECT_EQ(net::ERR_CACHE_OPERATION_NOT_SUPPORTED,
-            entry->WriteSparseData(offset, buf, kSize, NULL));
+  if (!cb1.have_result()) {
+    EXPECT_EQ(net::ERR_CACHE_OPERATION_NOT_SUPPORTED,
+              entry->ReadSparseData(offset, buf, kSize, NULL));
+    EXPECT_EQ(net::ERR_CACHE_OPERATION_NOT_SUPPORTED,
+              entry->WriteSparseData(offset, buf, kSize, NULL));
+  }
 
-  // Now see if we receive all notifications.
-  EXPECT_EQ(kSize, cb1.GetResult(net::ERR_IO_PENDING));
-  EXPECT_EQ(net::OK, cb2.GetResult(net::ERR_IO_PENDING));
-  EXPECT_EQ(net::OK, cb3.GetResult(net::ERR_IO_PENDING));
-  EXPECT_EQ(net::OK, cb4.GetResult(net::ERR_IO_PENDING));
+  // Now see if we receive all notifications. Note that we should not be able
+  // to write everything (unless the timing of the system is really weird).
+  rv = cb1.WaitForResult();
+  EXPECT_TRUE(rv == 4096 || rv == kSize);
+  EXPECT_EQ(net::OK, cb2.WaitForResult());
+  EXPECT_EQ(net::OK, cb3.WaitForResult());
+  EXPECT_EQ(net::OK, cb4.WaitForResult());
 
-  EXPECT_EQ(kSize, entry->GetAvailableRange(offset, kSize, &offset));
-  EXPECT_EQ(net::OK, entry->ReadyForSparseIO(&cb2));
+  rv = entry->GetAvailableRange(offset, kSize, &offset, &cb5);
+  EXPECT_EQ(0, cb5.GetResult(rv));
   entry->Close();
 }
diff --git a/net/disk_cache/eviction.cc b/net/disk_cache/eviction.cc
index bdcef6b..91ef9b7 100644
--- a/net/disk_cache/eviction.cc
+++ b/net/disk_cache/eviction.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -38,12 +38,14 @@
 #include "net/disk_cache/trace.h"
 
 using base::Time;
+using base::TimeTicks;
 
 namespace {
 
 const int kCleanUpMargin = 1024 * 1024;
 const int kHighUse = 10;  // Reuse count to be on the HIGH_USE list.
 const int kTargetTime = 24 * 7;  // Time to be evicted (hours since last use).
+const int kMaxDelayedTrims = 60;
 
 int LowWaterAdjust(int high_water) {
   if (high_water < kCleanUpMargin)
@@ -67,21 +69,35 @@
   first_trim_ = true;
   trimming_ = false;
   delay_trim_ = false;
+  trim_delays_ = 0;
+  init_ = true;
+}
+
+void Eviction::Stop() {
+  // It is possible for the backend initialization to fail, in which case this
+  // object was never initialized... and there is nothing to do.
+  if (!init_)
+    return;
+
+  // We want to stop further evictions, so let's pretend that we are busy from
+  // this point on.
+  DCHECK(!trimming_);
+  trimming_ = true;
 }
 
 void Eviction::TrimCache(bool empty) {
-  if (new_eviction_)
-    return TrimCacheV2(empty);
-
   if (backend_->disabled_ || trimming_)
     return;
 
-  if (!empty && backend_->IsLoaded())
+  if (!empty && !ShouldTrim())
     return PostDelayedTrim();
 
+  if (new_eviction_)
+    return TrimCacheV2(empty);
+
   Trace("*** Trim Cache ***");
   trimming_ = true;
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
   Rankings::ScopedRankingsBlock node(rankings_);
   Rankings::ScopedRankingsBlock next(rankings_,
       rankings_->GetPrev(node.get(), Rankings::NO_USE));
@@ -102,7 +118,7 @@
       if (!empty) {
         backend_->OnEvent(Stats::TRIM_ENTRY);
 
-        if ((Time::Now() - start).InMilliseconds() > 20) {
+        if ((TimeTicks::Now() - start).InMilliseconds() > 20) {
           MessageLoop::current()->PostTask(FROM_HERE,
               factory_.NewRunnableMethod(&Eviction::TrimCache, false));
           break;
@@ -111,7 +127,12 @@
     }
   }
 
-  CACHE_UMA(AGE_MS, "TotalTrimTime", backend_->GetSizeGroup(), start);
+  if (empty) {
+    CACHE_UMA(AGE_MS, "TotalClearTimeV1", 0, start);
+  } else {
+    CACHE_UMA(AGE_MS, "TotalTrimTimeV1", backend_->GetSizeGroup(), start);
+  }
+
   trimming_ = false;
   Trace("*** Trim Cache end ***");
   return;
@@ -153,15 +174,28 @@
   if (delay_trim_)
     return;
   delay_trim_ = true;
+  trim_delays_++;
   MessageLoop::current()->PostDelayedTask(FROM_HERE,
       factory_.NewRunnableMethod(&Eviction::DelayedTrim), 1000);
 }
 
 void Eviction::DelayedTrim() {
   delay_trim_ = false;
+  if (trim_delays_ < kMaxDelayedTrims && backend_->IsLoaded())
+    return PostDelayedTrim();
+
   TrimCache(false);
 }
 
+bool Eviction::ShouldTrim() {
+  if (trim_delays_ < kMaxDelayedTrims && backend_->IsLoaded())
+    return false;
+
+  UMA_HISTOGRAM_COUNTS("DiskCache.TrimDelays", trim_delays_);
+  trim_delays_ = 0;
+  return true;
+}
+
 void Eviction::ReportTrimTimes(EntryImpl* entry) {
   if (first_trim_) {
     first_trim_ = false;
@@ -203,7 +237,7 @@
 
   ReportTrimTimes(entry);
   if (empty || !new_eviction_) {
-    entry->Doom();
+    entry->DoomImpl();
   } else {
     entry->DeleteEntryData(false);
     EntryStore* info = entry->entry()->Data();
@@ -223,15 +257,9 @@
 // -----------------------------------------------------------------------
 
 void Eviction::TrimCacheV2(bool empty) {
-  if (backend_->disabled_ || trimming_)
-    return;
-
-  if (!empty && backend_->IsLoaded())
-    return PostDelayedTrim();
-
   Trace("*** Trim Cache ***");
   trimming_ = true;
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
 
   const int kListsToSearch = 3;
   Rankings::ScopedRankingsBlock next[kListsToSearch];
@@ -282,7 +310,7 @@
         if (!EvictEntry(node.get(), empty))
           continue;
 
-        if (!empty && (Time::Now() - start).InMilliseconds() > 20) {
+        if (!empty && (TimeTicks::Now() - start).InMilliseconds() > 20) {
           MessageLoop::current()->PostTask(FROM_HERE,
               factory_.NewRunnableMethod(&Eviction::TrimCache, false));
           break;
@@ -300,7 +328,12 @@
         factory_.NewRunnableMethod(&Eviction::TrimDeleted, empty));
   }
 
-  CACHE_UMA(AGE_MS, "TotalTrimTime", backend_->GetSizeGroup(), start);
+  if (empty) {
+    CACHE_UMA(AGE_MS, "TotalClearTimeV2", 0, start);
+  } else {
+    CACHE_UMA(AGE_MS, "TotalTrimTimeV2", backend_->GetSizeGroup(), start);
+  }
+
   Trace("*** Trim Cache end ***");
   trimming_ = false;
   return;
@@ -396,17 +429,19 @@
   if (backend_->disabled_)
     return;
 
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
   Rankings::ScopedRankingsBlock node(rankings_);
   Rankings::ScopedRankingsBlock next(rankings_,
     rankings_->GetPrev(node.get(), Rankings::DELETED));
+  bool deleted = false;
   for (int i = 0; (i < 4 || empty) && next.get(); i++) {
     node.reset(next.release());
     next.reset(rankings_->GetPrev(node.get(), Rankings::DELETED));
-    RemoveDeletedNode(node.get());
+    deleted |= RemoveDeletedNode(node.get());
   }
 
-  if (header_->lru.sizes[Rankings::DELETED] > header_->num_entries / 4)
+  if (deleted && !empty &&
+      header_->lru.sizes[Rankings::DELETED] > header_->num_entries / 4)
     MessageLoop::current()->PostTask(FROM_HERE,
         factory_.NewRunnableMethod(&Eviction::TrimDeleted, false));
 
@@ -429,10 +464,11 @@
     // We ignore the failure; we're removing the entry anyway.
     entry->Update();
   }
+  bool doomed = (entry->entry()->Data()->state == ENTRY_DOOMED);
   entry->entry()->Data()->state = ENTRY_DOOMED;
-  entry->Doom();
+  entry->DoomImpl();
   entry->Release();
-  return true;
+  return !doomed;
 }
 
 bool Eviction::NodeIsOldEnough(CacheRankingsBlock* node, int list) {
diff --git a/net/disk_cache/eviction.h b/net/disk_cache/eviction.h
index e3c0a72..76ee00b 100644
--- a/net/disk_cache/eviction.h
+++ b/net/disk_cache/eviction.h
@@ -20,10 +20,13 @@
 // integrated with BackendImpl.
 class Eviction {
  public:
-  Eviction() : backend_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {}
+  Eviction()
+      : backend_(NULL), init_(false),
+        ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {}
   ~Eviction() {}
 
   void Init(BackendImpl* backend);
+  void Stop();
 
   // Deletes entries from the cache until the current size is below the limit.
   // If empty is true, the whole cache will be trimmed, regardless of being in
@@ -42,6 +45,7 @@
  private:
   void PostDelayedTrim();
   void DelayedTrim();
+  bool ShouldTrim();
   void ReportTrimTimes(EntryImpl* entry);
   Rankings::List GetListForEntry(EntryImpl* entry);
   bool EvictEntry(CacheRankingsBlock* node, bool empty);
@@ -67,10 +71,12 @@
   Rankings* rankings_;
   IndexHeader* header_;
   int max_size_;
+  int trim_delays_;
   bool new_eviction_;
   bool first_trim_;
   bool trimming_;
   bool delay_trim_;
+  bool init_;
   ScopedRunnableMethodFactory<Eviction> factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Eviction);
diff --git a/net/disk_cache/file_posix.cc b/net/disk_cache/file_posix.cc
index bfaad59..295f744 100644
--- a/net/disk_cache/file_posix.cc
+++ b/net/disk_cache/file_posix.cc
@@ -122,8 +122,8 @@
   void PostRead(disk_cache::File* file, void* buf, size_t buf_len,
                 size_t offset, disk_cache::FileIOCallback* callback);
   void PostWrite(disk_cache::File* file, const void* buf, size_t buf_len,
-                size_t offset, disk_cache::FileIOCallback* callback,
-                bool delete_buffer);
+                 size_t offset, disk_cache::FileIOCallback* callback,
+                 bool delete_buffer);
 
   // Blocks the current thread until all IO operations tracked by this object
   // complete.
@@ -193,6 +193,9 @@
   io_list_.insert(operation.get());
   file->AddRef();  // Balanced on InvokeCallback()
 
+  if (!callback_thread_)
+      callback_thread_ = MessageLoop::current();
+
   WorkerPool::PostTask(FROM_HERE,
                        NewRunnableMethod(operation.get(), &BackgroundIO::Read),
                        true);
@@ -207,6 +210,9 @@
   io_list_.insert(operation.get());
   file->AddRef();  // Balanced on InvokeCallback()
 
+  if (!callback_thread_)
+    callback_thread_ = MessageLoop::current();
+
   WorkerPool::PostTask(FROM_HERE,
                        NewRunnableMethod(operation.get(), &BackgroundIO::Write,
                                          delete_buffer),
@@ -219,6 +225,8 @@
     IOList::iterator it = io_list_.begin();
     InvokeCallback(*it, true);
   }
+  // Unit tests can use different threads.
+  callback_thread_ = NULL;
 }
 
 // Runs on a worker thread.
@@ -372,8 +380,9 @@
 
 // Static.
 void File::WaitForPendingIO(int* num_pending_io) {
-  if (*num_pending_io)
-    Singleton<InFlightIO>::get()->WaitForPendingIO();
+  // We may be running unit tests so we should allow InFlightIO to reset the
+  // message loop.
+  Singleton<InFlightIO>::get()->WaitForPendingIO();
 }
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/histogram_macros.h b/net/disk_cache/histogram_macros.h
index 17cd345..361a9d6 100644
--- a/net/disk_cache/histogram_macros.h
+++ b/net/disk_cache/histogram_macros.h
@@ -11,22 +11,70 @@
 #ifndef NET_DISK_CACHE_HISTOGRAM_MACROS_H_
 #define NET_DISK_CACHE_HISTOGRAM_MACROS_H_
 
+// -----------------------------------------------------------------------------
+
+// These histograms follow the definition of UMA_HISTOGRAMN_XXX except that
+// whenever the name changes (the experiment group changes), the histrogram
+// object is re-created.
+
+#define CACHE_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
+    do { \
+      static scoped_refptr<Histogram> counter; \
+      if (!counter || name != counter->histogram_name()) \
+        counter = Histogram::FactoryGet(name, min, max, bucket_count, \
+                                        Histogram::kUmaTargetedHistogramFlag); \
+      counter->Add(sample); \
+    } while (0)
+
+#define CACHE_HISTOGRAM_COUNTS(name, sample) CACHE_HISTOGRAM_CUSTOM_COUNTS( \
+    name, sample, 1, 1000000, 50)
+
+#define CACHE_HISTOGRAM_COUNTS_10000(name, sample) \
+    CACHE_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 10000, 50)
+
+#define CACHE_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
+    do { \
+      static scoped_refptr<Histogram> counter; \
+      if (!counter || name != counter->histogram_name()) \
+        counter = Histogram::FactoryTimeGet(name, min, max, bucket_count, \
+                      Histogram::kUmaTargetedHistogramFlag); \
+      counter->AddTime(sample); \
+    } while (0)
+
+#define CACHE_HISTOGRAM_TIMES(name, sample) CACHE_HISTOGRAM_CUSTOM_TIMES( \
+    name, sample, base::TimeDelta::FromMilliseconds(1), \
+    base::TimeDelta::FromSeconds(10), 50)
+
+#define CACHE_HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \
+    static scoped_refptr<Histogram> counter; \
+    if (!counter || name != counter->histogram_name()) \
+      counter = LinearHistogram::FactoryGet( \
+                    name, 1, boundary_value, boundary_value + 1, \
+                    Histogram::kUmaTargetedHistogramFlag); \
+    counter->Add(sample); \
+  } while (0)
+
+#define CACHE_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
+    CACHE_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
+
+// -----------------------------------------------------------------------------
+
 // HISTOGRAM_HOURS will collect time related data with a granularity of hours
 // and normal values of a few months.
-#define UMA_HISTOGRAM_HOURS UMA_HISTOGRAM_COUNTS_10000
+#define CACHE_HISTOGRAM_HOURS CACHE_HISTOGRAM_COUNTS_10000
 
 // HISTOGRAM_AGE will collect time elapsed since |initial_time|, with a
 // granularity of hours and normal values of a few months.
-#define UMA_HISTOGRAM_AGE(name, initial_time)\
-    UMA_HISTOGRAM_COUNTS_10000(name, (Time::Now() - initial_time).InHours())
+#define CACHE_HISTOGRAM_AGE(name, initial_time) \
+    CACHE_HISTOGRAM_COUNTS_10000(name, (Time::Now() - initial_time).InHours())
 
 // HISTOGRAM_AGE_MS will collect time elapsed since |initial_time|, with the
 // normal resolution of the UMA_HISTOGRAM_TIMES.
-#define UMA_HISTOGRAM_AGE_MS(name, initial_time)\
-    UMA_HISTOGRAM_TIMES(name, Time::Now() - initial_time)
+#define CACHE_HISTOGRAM_AGE_MS(name, initial_time)\
+    CACHE_HISTOGRAM_TIMES(name, TimeTicks::Now() - initial_time)
 
-#define UMA_HISTOGRAM_CACHE_ERROR(name, sample) \
-    UMA_HISTOGRAM_ENUMERATION(name, sample, 50)
+#define CACHE_HISTOGRAM_CACHE_ERROR(name, sample) \
+    CACHE_HISTOGRAM_ENUMERATION(name, sample, 50)
 
 #ifdef NET_DISK_CACHE_BACKEND_IMPL_CC_
 #define BACKEND_OBJ this
@@ -48,10 +96,13 @@
     const std::string my_name = BACKEND_OBJ->HistogramName(name, experiment);\
     switch (BACKEND_OBJ->cache_type()) {\
       case net::DISK_CACHE:\
-        UMA_HISTOGRAM_##type(my_name.data(), sample);\
+        CACHE_HISTOGRAM_##type(my_name.data(), sample);\
         break;\
       case net::MEDIA_CACHE:\
-        UMA_HISTOGRAM_##type(my_name.data(), sample);\
+        CACHE_HISTOGRAM_##type(my_name.data(), sample);\
+        break;\
+      case net::APP_CACHE:\
+        CACHE_HISTOGRAM_##type(my_name.data(), sample);\
         break;\
       default:\
         NOTREACHED();\
diff --git a/net/disk_cache/in_flight_backend_io.cc b/net/disk_cache/in_flight_backend_io.cc
new file mode 100644
index 0000000..1e2ce9f
--- /dev/null
+++ b/net/disk_cache/in_flight_backend_io.cc
@@ -0,0 +1,448 @@
+// Copyright (c) 2010 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/disk_cache/in_flight_backend_io.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/backend_impl.h"
+#include "net/disk_cache/entry_impl.h"
+
+namespace disk_cache {
+
+BackendIO::BackendIO(InFlightIO* controller, BackendImpl* backend,
+                     net::CompletionCallback* callback)
+    : BackgroundIO(controller), backend_(backend), callback_(callback),
+      operation_(OP_NONE),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          my_callback_(this, &BackendIO::OnIOComplete)) {
+}
+
+// Runs on the background thread.
+void BackendIO::ExecuteOperation() {
+  if (IsEntryOperation())
+    return ExecuteEntryOperation();
+
+  ExecuteBackendOperation();
+}
+
+// Runs on the background thread.
+void BackendIO::OnIOComplete(int result) {
+  DCHECK(IsEntryOperation());
+  DCHECK_NE(result, net::ERR_IO_PENDING);
+  result_ = result;
+  controller_->OnIOComplete(this);
+}
+
+bool BackendIO::IsEntryOperation() {
+  return operation_ > OP_MAX_BACKEND;
+}
+
+void BackendIO::ReleaseEntry() {
+  entry_ = NULL;
+}
+
+void BackendIO::Init() {
+  operation_ = OP_INIT;
+}
+
+void BackendIO::OpenEntry(const std::string& key, Entry** entry) {
+  operation_ = OP_OPEN;
+  key_ = key;
+  entry_ptr_ = entry;
+}
+
+void BackendIO::CreateEntry(const std::string& key, Entry** entry) {
+  operation_ = OP_CREATE;
+  key_ = key;
+  entry_ptr_ = entry;
+}
+
+void BackendIO::DoomEntry(const std::string& key) {
+  operation_ = OP_DOOM;
+  key_ = key;
+}
+
+void BackendIO::DoomAllEntries() {
+  operation_ = OP_DOOM_ALL;
+}
+
+void BackendIO::DoomEntriesBetween(const base::Time initial_time,
+                                   const base::Time end_time) {
+  operation_ = OP_DOOM_BETWEEN;
+  initial_time_ = initial_time;
+  end_time_ = end_time;
+}
+
+void BackendIO::DoomEntriesSince(const base::Time initial_time) {
+  operation_ = OP_DOOM_SINCE;
+  initial_time_ = initial_time;
+}
+
+void BackendIO::OpenNextEntry(void** iter, Entry** next_entry) {
+  operation_ = OP_OPEN_NEXT;
+  iter_ptr_ = iter;
+  entry_ptr_ = next_entry;
+}
+
+void BackendIO::OpenPrevEntry(void** iter, Entry** prev_entry) {
+  operation_ = OP_OPEN_PREV;
+  iter_ptr_ = iter;
+  entry_ptr_ = prev_entry;
+}
+
+void BackendIO::EndEnumeration(void* iterator) {
+  operation_ = OP_END_ENUMERATION;
+  iter_ = iterator;
+}
+
+void BackendIO::CloseEntryImpl(EntryImpl* entry) {
+  operation_ = OP_CLOSE_ENTRY;
+  entry_ = entry;
+}
+
+void BackendIO::DoomEntryImpl(EntryImpl* entry) {
+  operation_ = OP_DOOM_ENTRY;
+  entry_ = entry;
+}
+
+void BackendIO::FlushQueue() {
+  operation_ = OP_FLUSH_QUEUE;
+}
+
+void BackendIO::ReadData(EntryImpl* entry, int index, int offset,
+                         net::IOBuffer* buf, int buf_len) {
+  operation_ = OP_READ;
+  entry_ = entry;
+  index_ = index;
+  offset_ = offset;
+  buf_ = buf;
+  buf_len_ = buf_len;
+}
+
+void BackendIO::WriteData(EntryImpl* entry, int index, int offset,
+                          net::IOBuffer* buf, int buf_len, bool truncate) {
+  operation_ = OP_WRITE;
+  entry_ = entry;
+  index_ = index;
+  offset_ = offset;
+  buf_ = buf;
+  buf_len_ = buf_len;
+  truncate_ = truncate;
+}
+
+void BackendIO::ReadSparseData(EntryImpl* entry, int64 offset,
+                               net::IOBuffer* buf, int buf_len) {
+  operation_ = OP_READ_SPARSE;
+  entry_ = entry;
+  offset64_ = offset;
+  buf_ = buf;
+  buf_len_ = buf_len;
+}
+
+void BackendIO::WriteSparseData(EntryImpl* entry, int64 offset,
+                                net::IOBuffer* buf, int buf_len) {
+  operation_ = OP_WRITE_SPARSE;
+  entry_ = entry;
+  offset64_ = offset;
+  buf_ = buf;
+  buf_len_ = buf_len;
+}
+
+void BackendIO::GetAvailableRange(EntryImpl* entry, int64 offset, int len,
+                                  int64* start) {
+  operation_ = OP_GET_RANGE;
+  entry_ = entry;
+  offset64_ = offset;
+  buf_len_ = len;
+  start_ = start;
+}
+
+void BackendIO::CancelSparseIO(EntryImpl* entry) {
+  operation_ = OP_CANCEL_IO;
+  entry_ = entry;
+}
+
+void BackendIO::ReadyForSparseIO(EntryImpl* entry) {
+  operation_ = OP_IS_READY;
+  entry_ = entry;
+}
+
+// Runs on the background thread.
+void BackendIO::ExecuteBackendOperation() {
+  switch (operation_) {
+    case OP_INIT:
+      result_ = backend_->SyncInit();
+      break;
+    case OP_OPEN:
+      result_ = backend_->SyncOpenEntry(key_, entry_ptr_);
+      break;
+    case OP_CREATE:
+      result_ = backend_->SyncCreateEntry(key_, entry_ptr_);
+      break;
+    case OP_DOOM:
+      result_ = backend_->SyncDoomEntry(key_);
+      break;
+    case OP_DOOM_ALL:
+      result_ = backend_->SyncDoomAllEntries();
+      break;
+    case OP_DOOM_BETWEEN:
+      result_ = backend_->SyncDoomEntriesBetween(initial_time_, end_time_);
+      break;
+    case OP_DOOM_SINCE:
+      result_ = backend_->SyncDoomEntriesSince(initial_time_);
+      break;
+    case OP_OPEN_NEXT:
+      result_ = backend_->SyncOpenNextEntry(iter_ptr_, entry_ptr_);
+      break;
+    case OP_OPEN_PREV:
+      result_ = backend_->SyncOpenPrevEntry(iter_ptr_, entry_ptr_);
+      break;
+    case OP_END_ENUMERATION:
+      backend_->SyncEndEnumeration(iter_);
+      result_ = net::OK;
+      break;
+    case OP_CLOSE_ENTRY:
+      entry_->Release();
+      result_ = net::OK;
+      break;
+    case OP_DOOM_ENTRY:
+      entry_->DoomImpl();
+      result_ = net::OK;
+      break;
+    case OP_FLUSH_QUEUE:
+      result_ = net::OK;
+      break;
+    default:
+      NOTREACHED() << "Invalid Operation";
+      result_ = net::ERR_UNEXPECTED;
+  }
+  DCHECK_NE(net::ERR_IO_PENDING, result_);
+  controller_->OnIOComplete(this);
+}
+
+// Runs on the background thread.
+void BackendIO::ExecuteEntryOperation() {
+  switch (operation_) {
+    case OP_READ:
+      result_ = entry_->ReadDataImpl(index_, offset_, buf_, buf_len_,
+                                     &my_callback_);
+      break;
+    case OP_WRITE:
+      result_ = entry_->WriteDataImpl(index_, offset_, buf_, buf_len_,
+                                      &my_callback_, truncate_);
+      break;
+    case OP_READ_SPARSE:
+      result_ = entry_->ReadSparseDataImpl(offset64_, buf_, buf_len_,
+                                           &my_callback_);
+      break;
+    case OP_WRITE_SPARSE:
+      result_ = entry_->WriteSparseDataImpl(offset64_, buf_, buf_len_,
+                                            &my_callback_);
+      break;
+    case OP_GET_RANGE:
+      result_ = entry_->GetAvailableRangeImpl(offset64_, buf_len_, start_);
+      break;
+    case OP_CANCEL_IO:
+      entry_->CancelSparseIOImpl();
+      result_ = net::OK;
+      break;
+    case OP_IS_READY:
+      result_ = entry_->ReadyForSparseIOImpl(&my_callback_);
+      break;
+    default:
+      NOTREACHED() << "Invalid Operation";
+      result_ = net::ERR_UNEXPECTED;
+  }
+  if (result_ != net::ERR_IO_PENDING)
+    controller_->OnIOComplete(this);
+}
+
+// ---------------------------------------------------------------------------
+
+void InFlightBackendIO::Init(CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->Init();
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::OpenEntry(const std::string& key, Entry** entry,
+                                  CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->OpenEntry(key, entry);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::CreateEntry(const std::string& key, Entry** entry,
+                                    CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->CreateEntry(key, entry);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::DoomEntry(const std::string& key,
+                                  CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->DoomEntry(key);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::DoomAllEntries(CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->DoomAllEntries();
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::DoomEntriesBetween(const base::Time initial_time,
+                        const base::Time end_time,
+                        CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->DoomEntriesBetween(initial_time, end_time);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::DoomEntriesSince(const base::Time initial_time,
+                                         CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->DoomEntriesSince(initial_time);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::OpenNextEntry(void** iter, Entry** next_entry,
+                                      CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->OpenNextEntry(iter, next_entry);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::OpenPrevEntry(void** iter, Entry** prev_entry,
+                                      CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->OpenPrevEntry(iter, prev_entry);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::EndEnumeration(void* iterator) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, NULL);
+  operation->EndEnumeration(iterator);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::CloseEntryImpl(EntryImpl* entry) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, NULL);
+  operation->CloseEntryImpl(entry);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::DoomEntryImpl(EntryImpl* entry) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, NULL);
+  operation->DoomEntryImpl(entry);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::FlushQueue(net::CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->FlushQueue();
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::ReadData(EntryImpl* entry, int index, int offset,
+                                 net::IOBuffer* buf, int buf_len,
+                                 CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->ReadData(entry, index, offset, buf, buf_len);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::WriteData(EntryImpl* entry, int index, int offset,
+                                  net::IOBuffer* buf, int buf_len,
+                                  bool truncate,
+                                  CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->WriteData(entry, index, offset, buf, buf_len, truncate);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::ReadSparseData(EntryImpl* entry, int64 offset,
+                                       net::IOBuffer* buf, int buf_len,
+                                       CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->ReadSparseData(entry, offset, buf, buf_len);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::WriteSparseData(EntryImpl* entry, int64 offset,
+                                        net::IOBuffer* buf, int buf_len,
+                                        CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->WriteSparseData(entry, offset, buf, buf_len);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::GetAvailableRange(EntryImpl* entry, int64 offset,
+                                          int len, int64* start,
+                                          CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->GetAvailableRange(entry, offset, len, start);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::CancelSparseIO(EntryImpl* entry) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, NULL);
+  operation->CancelSparseIO(entry);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::ReadyForSparseIO(EntryImpl* entry,
+                                         CompletionCallback* callback) {
+  scoped_refptr<BackendIO> operation = new BackendIO(this, backend_, callback);
+  operation->ReadyForSparseIO(entry);
+  QueueOperation(operation);
+}
+
+void InFlightBackendIO::WaitForPendingIO() {
+  // We clear the list first so that we don't post more operations after this
+  // point.
+  pending_ops_.clear();
+  InFlightIO::WaitForPendingIO();
+}
+
+void InFlightBackendIO::OnOperationComplete(BackgroundIO* operation,
+                                            bool cancel) {
+  BackendIO* op = static_cast<BackendIO*>(operation);
+
+  if (!op->IsEntryOperation() && !pending_ops_.empty()) {
+    // Process the next request. Note that invoking the callback may result
+    // in the backend destruction (and with it this object), so we should deal
+    // with the next operation before invoking the callback.
+    scoped_refptr<BackendIO> next_op = pending_ops_.front();
+    pending_ops_.pop_front();
+    PostOperation(next_op);
+  }
+
+  if (op->callback() && (!cancel || op->IsEntryOperation()))
+    op->callback()->Run(op->result());
+
+  if (cancel)
+    op->ReleaseEntry();
+}
+
+void InFlightBackendIO::QueueOperation(BackendIO* operation) {
+  if (operation->IsEntryOperation())
+    return PostOperation(operation);
+
+  if (pending_ops_.empty())
+    return PostOperation(operation);
+
+  pending_ops_.push_back(operation);
+}
+
+void InFlightBackendIO::PostOperation(BackendIO* operation) {
+  background_thread_->PostTask(FROM_HERE,
+      NewRunnableMethod(operation, &BackendIO::ExecuteOperation));
+  OnOperationPosted(operation);
+}
+
+}  // namespace
diff --git a/net/disk_cache/in_flight_backend_io.h b/net/disk_cache/in_flight_backend_io.h
new file mode 100644
index 0000000..1cd0d41
--- /dev/null
+++ b/net/disk_cache/in_flight_backend_io.h
@@ -0,0 +1,201 @@
+// Copyright (c) 2010 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_DISK_CACHE_IN_FLIGHT_BACKEND_IO_H_
+#define NET_DISK_CACHE_IN_FLIGHT_BACKEND_IO_H_
+
+#include <list>
+#include <string>
+
+#include "base/message_loop_proxy.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/disk_cache/entry_impl.h"
+#include "net/disk_cache/in_flight_io.h"
+
+namespace disk_cache {
+
+class BackendImpl;
+
+// This class represents a single asynchronous disk cache IO operation while it
+// is being bounced between threads.
+class BackendIO : public BackgroundIO {
+ public:
+  BackendIO(InFlightIO* controller, BackendImpl* backend,
+            net::CompletionCallback* callback);
+
+  // Runs the actual operation on the background thread.
+  void ExecuteOperation();
+
+  // Callback implementation.
+  void OnIOComplete(int result);
+
+  // Returns true if this operation is directed to an entry (vs. the backend).
+  bool IsEntryOperation();
+
+  net::CompletionCallback* callback() { return callback_; }
+
+  void ReleaseEntry();
+
+  // The operations we proxy:
+  void Init();
+  void OpenEntry(const std::string& key, Entry** entry);
+  void CreateEntry(const std::string& key, Entry** entry);
+  void DoomEntry(const std::string& key);
+  void DoomAllEntries();
+  void DoomEntriesBetween(const base::Time initial_time,
+                          const base::Time end_time);
+  void DoomEntriesSince(const base::Time initial_time);
+  void OpenNextEntry(void** iter, Entry** next_entry);
+  void OpenPrevEntry(void** iter, Entry** prev_entry);
+  void EndEnumeration(void* iterator);
+  void CloseEntryImpl(EntryImpl* entry);
+  void DoomEntryImpl(EntryImpl* entry);
+  void FlushQueue();  // Dummy operation.
+  void ReadData(EntryImpl* entry, int index, int offset, net::IOBuffer* buf,
+                int buf_len);
+  void WriteData(EntryImpl* entry, int index, int offset, net::IOBuffer* buf,
+                 int buf_len, bool truncate);
+  void ReadSparseData(EntryImpl* entry, int64 offset, net::IOBuffer* buf,
+                      int buf_len);
+  void WriteSparseData(EntryImpl* entry, int64 offset, net::IOBuffer* buf,
+                       int buf_len);
+  void GetAvailableRange(EntryImpl* entry, int64 offset, int len, int64* start);
+  void CancelSparseIO(EntryImpl* entry);
+  void ReadyForSparseIO(EntryImpl* entry);
+
+ private:
+  // There are two types of operations to proxy: regular backend operations are
+  // queued so that we don't have more than one operation going on at the same
+  // time (for instance opening an entry and creating the same entry). On the
+  // other hand, operations targeted to a given entry can be long lived and
+  // support multiple simultaneous users (multiple reads or writes to the same
+  // entry), so they are not queued, just posted to the worker thread as they
+  // come.
+  enum Operation {
+    OP_NONE = 0,
+    OP_INIT,
+    OP_OPEN,
+    OP_CREATE,
+    OP_DOOM,
+    OP_DOOM_ALL,
+    OP_DOOM_BETWEEN,
+    OP_DOOM_SINCE,
+    OP_OPEN_NEXT,
+    OP_OPEN_PREV,
+    OP_END_ENUMERATION,
+    OP_CLOSE_ENTRY,
+    OP_DOOM_ENTRY,
+    OP_FLUSH_QUEUE,
+    OP_MAX_BACKEND,
+    OP_READ,
+    OP_WRITE,
+    OP_READ_SPARSE,
+    OP_WRITE_SPARSE,
+    OP_GET_RANGE,
+    OP_CANCEL_IO,
+    OP_IS_READY
+  };
+
+  ~BackendIO() {}
+
+  void ExecuteBackendOperation();
+  void ExecuteEntryOperation();
+
+  BackendImpl* backend_;
+  net::CompletionCallback* callback_;
+  Operation operation_;
+  net::CompletionCallbackImpl<BackendIO> my_callback_;
+
+  // The arguments of all the operations we proxy:
+  std::string key_;
+  Entry** entry_ptr_;
+  base::Time initial_time_;
+  base::Time end_time_;
+  void** iter_ptr_;
+  void* iter_;
+  EntryImpl* entry_;
+  int index_;
+  int offset_;
+  scoped_refptr<net::IOBuffer> buf_;
+  int buf_len_;
+  bool truncate_;
+  int64 offset64_;
+  int64* start_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackendIO);
+};
+
+// The specialized controller that keeps track of current operations.
+class InFlightBackendIO : public InFlightIO {
+ public:
+  InFlightBackendIO(BackendImpl* backend,
+                    base::MessageLoopProxy* background_thread)
+      : backend_(backend), background_thread_(background_thread) {}
+  ~InFlightBackendIO() {}
+
+  // The operations we proxy:
+  void Init(net::CompletionCallback* callback);
+  void OpenEntry(const std::string& key, Entry** entry,
+                 net::CompletionCallback* callback);
+  void CreateEntry(const std::string& key, Entry** entry,
+                   net::CompletionCallback* callback);
+  void DoomEntry(const std::string& key, net::CompletionCallback* callback);
+  void DoomAllEntries(net::CompletionCallback* callback);
+  void DoomEntriesBetween(const base::Time initial_time,
+                          const base::Time end_time,
+                          net::CompletionCallback* callback);
+  void DoomEntriesSince(const base::Time initial_time,
+                        net::CompletionCallback* callback);
+  void OpenNextEntry(void** iter, Entry** next_entry,
+                     net::CompletionCallback* callback);
+  void OpenPrevEntry(void** iter, Entry** prev_entry,
+                     net::CompletionCallback* callback);
+  void EndEnumeration(void* iterator);
+  void CloseEntryImpl(EntryImpl* entry);
+  void DoomEntryImpl(EntryImpl* entry);
+  void FlushQueue(net::CompletionCallback* callback);
+  void ReadData(EntryImpl* entry, int index, int offset, net::IOBuffer* buf,
+                int buf_len, net::CompletionCallback* callback);
+  void WriteData(EntryImpl* entry, int index, int offset, net::IOBuffer* buf,
+                 int buf_len, bool truncate, net::CompletionCallback* callback);
+  void ReadSparseData(EntryImpl* entry, int64 offset, net::IOBuffer* buf,
+                      int buf_len, net::CompletionCallback* callback);
+  void WriteSparseData(EntryImpl* entry, int64 offset, net::IOBuffer* buf,
+                       int buf_len, net::CompletionCallback* callback);
+  void GetAvailableRange(EntryImpl* entry, int64 offset, int len, int64* start,
+                         net::CompletionCallback* callback);
+  void CancelSparseIO(EntryImpl* entry);
+  void ReadyForSparseIO(EntryImpl* entry, net::CompletionCallback* callback);
+
+  // Blocks until all operations are cancelled or completed.
+  void WaitForPendingIO();
+
+  scoped_refptr<base::MessageLoopProxy> background_thread() {
+    return background_thread_;
+  }
+
+  // Returns true if the current thread is the background thread.
+  bool BackgroundIsCurrentThread() {
+    return background_thread_->BelongsToCurrentThread();
+  }
+
+ protected:
+  virtual void OnOperationComplete(BackgroundIO* operation, bool cancel);
+
+ private:
+  typedef std::list<scoped_refptr<BackendIO> > OperationList;
+  void QueueOperation(BackendIO* operation);
+  void PostOperation(BackendIO* operation);
+
+  BackendImpl* backend_;
+  scoped_refptr<base::MessageLoopProxy> background_thread_;
+  OperationList pending_ops_;  // The list of operations to be posted.
+
+  DISALLOW_COPY_AND_ASSIGN(InFlightBackendIO);
+};
+
+}  // namespace disk_cache
+
+#endif  // NET_DISK_CACHE_IN_FLIGHT_BACKEND_IO_H_
diff --git a/net/disk_cache/in_flight_io.cc b/net/disk_cache/in_flight_io.cc
new file mode 100644
index 0000000..6112a9c
--- /dev/null
+++ b/net/disk_cache/in_flight_io.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2006-2010 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/disk_cache/in_flight_io.h"
+
+#include "base/logging.h"
+
+namespace disk_cache {
+
+// Runs on the primary thread.
+void BackgroundIO::OnIOSignalled() {
+  if (controller_)
+    controller_->InvokeCallback(this, false);
+}
+
+void BackgroundIO::Cancel() {
+  DCHECK(controller_);
+  controller_ = NULL;
+}
+
+// Runs on the background thread.
+void BackgroundIO::NotifyController() {
+  controller_->OnIOComplete(this);
+}
+
+// ---------------------------------------------------------------------------
+
+void InFlightIO::WaitForPendingIO() {
+  while (!io_list_.empty()) {
+    // Block the current thread until all pending IO completes.
+    IOList::iterator it = io_list_.begin();
+    InvokeCallback(*it, true);
+  }
+}
+
+// Runs on a background thread.
+void InFlightIO::OnIOComplete(BackgroundIO* operation) {
+#ifndef NDEBUG
+  if (callback_thread_->BelongsToCurrentThread()) {
+    DCHECK(single_thread_ || !running_);
+    single_thread_ = true;
+  }
+  running_ = true;
+#endif
+
+  callback_thread_->PostTask(FROM_HERE,
+                             NewRunnableMethod(operation,
+                                               &BackgroundIO::OnIOSignalled));
+  operation->io_completed()->Signal();
+}
+
+// Runs on the primary thread.
+void InFlightIO::InvokeCallback(BackgroundIO* operation, bool cancel_task) {
+  operation->io_completed()->Wait();
+
+  if (cancel_task)
+    operation->Cancel();
+
+  // Make sure that we remove the operation from the list before invoking the
+  // callback (so that a subsequent cancel does not invoke the callback again).
+  DCHECK(io_list_.find(operation) != io_list_.end());
+  io_list_.erase(operation);
+  OnOperationComplete(operation, cancel_task);
+}
+
+// Runs on the primary thread.
+void InFlightIO::OnOperationPosted(BackgroundIO* operation) {
+  DCHECK(callback_thread_->BelongsToCurrentThread());
+  io_list_.insert(operation);
+}
+
+}  // namespace disk_cache
diff --git a/net/disk_cache/in_flight_io.h b/net/disk_cache/in_flight_io.h
new file mode 100644
index 0000000..4486f44
--- /dev/null
+++ b/net/disk_cache/in_flight_io.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2006-2010 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_DISK_CACHE_IN_FLIGHT_IO_H_
+#define NET_DISK_CACHE_IN_FLIGHT_IO_H_
+
+#include <set>
+
+#include "base/message_loop_proxy.h"
+#include "base/waitable_event.h"
+
+namespace disk_cache {
+
+class InFlightIO;
+
+// This class represents a single asynchronous IO operation while it is being
+// bounced between threads.
+class BackgroundIO : public base::RefCountedThreadSafe<BackgroundIO> {
+ public:
+  // Other than the actual parameters for the IO operation (including the
+  // |callback| that must be notified at the end), we need the controller that
+  // is keeping track of all operations. When done, we notify the controller
+  // (we do NOT invoke the callback), in the worker thead that completed the
+  // operation.
+  explicit BackgroundIO(InFlightIO* controller)
+      : controller_(controller), result_(-1), io_completed_(true, false) {}
+
+  // This method signals the controller that this operation is finished, in the
+  // original thread. In practice, this is a RunableMethod that allows
+  // cancellation.
+  void OnIOSignalled();
+
+  // Allows the cancellation of the task to notify the controller (step number 8
+  // in the diagram below). In practice, if the controller waits for the
+  // operation to finish it doesn't have to wait for the final task to be
+  // processed by the message loop so calling this method prevents its delivery.
+  // Note that this method is not intended to cancel the actual IO operation or
+  // to prevent the first notification to take place (OnIOComplete).
+  void Cancel();
+
+  int result() { return result_; }
+
+  base::WaitableEvent* io_completed() {
+    return &io_completed_;
+  }
+
+ protected:
+  virtual ~BackgroundIO() {}
+
+  InFlightIO* controller_;  // The controller that tracks all operations.
+  int result_;  // Final operation result.
+
+ private:
+  friend class base::RefCountedThreadSafe<BackgroundIO>;
+
+  // Notifies the controller about the end of the operation, from the background
+  // thread.
+  void NotifyController();
+
+  // An event to signal when the operation completes.
+  base::WaitableEvent io_completed_;
+
+  DISALLOW_COPY_AND_ASSIGN(BackgroundIO);
+};
+
+// This class keeps track of asynchronous IO operations. A single instance
+// of this class is meant to be used to start an asynchronous operation (using
+// PostXX, exposed by a derived class). This class will post the operation to a
+// worker thread, hanlde the notification when the operation finishes and
+// perform the callback on the same thread that was used to start the operation.
+//
+// The regular sequence of calls is:
+//                 Thread_1                          Worker_thread
+//    1.     DerivedInFlightIO::PostXX()
+//    2.                         -> PostTask ->
+//    3.    InFlightIO::OnOperationPosted()
+//    4.                                        DerivedBackgroundIO::XX()
+//    5.                                         IO operation completes
+//    6.                                       InFlightIO::OnIOComplete()
+//    7.                         <- PostTask <-
+//    8.  BackgroundIO::OnIOSignalled()
+//    9.  InFlightIO::InvokeCallback()
+//   10. DerivedInFlightIO::OnOperationComplete()
+//   11.       invoke callback
+//
+// Shutdown is a special case that is handled though WaitForPendingIO() instead
+// of just waiting for step 7.
+class InFlightIO {
+ public:
+  InFlightIO()
+      : callback_thread_(base::MessageLoopProxy::CreateForCurrentThread()),
+        running_(false), single_thread_(false) {}
+  virtual ~InFlightIO() {}
+
+  // Blocks the current thread until all IO operations tracked by this object
+  // complete.
+  void WaitForPendingIO();
+
+  // Called on a background thread when |operation| completes.
+  void OnIOComplete(BackgroundIO* operation);
+
+  // Invokes the users' completion callback at the end of the IO operation.
+  // |cancel_task| is true if the actual task posted to the thread is still
+  // queued (because we are inside WaitForPendingIO), and false if said task is
+  // the one performing the call.
+  void InvokeCallback(BackgroundIO* operation, bool cancel_task);
+
+ protected:
+  // This method is called to signal the completion of the |operation|. |cancel|
+  // is true if the operation is being cancelled. This method is called on the
+  // thread that created this object.
+  virtual void OnOperationComplete(BackgroundIO* operation, bool cancel) = 0;
+
+  // Signals this object that the derived class just posted the |operation| to
+  // be executed on a background thread. This method must be called on the same
+  // thread used to create this object.
+  void OnOperationPosted(BackgroundIO* operation);
+
+ private:
+  typedef std::set<scoped_refptr<BackgroundIO> > IOList;
+
+  IOList io_list_;  // List of pending, in-flight io operations.
+  scoped_refptr<base::MessageLoopProxy> callback_thread_;
+
+  bool running_;  // True after the first posted operation completes.
+  bool single_thread_;  // True if we only have one thread.
+
+  DISALLOW_COPY_AND_ASSIGN(InFlightIO);
+};
+
+}  // namespace disk_cache
+
+#endif  // NET_DISK_CACHE_IN_FLIGHT_IO_H_
diff --git a/net/disk_cache/mapped_file_unittest.cc b/net/disk_cache/mapped_file_unittest.cc
index ea28f32..8b01a7d 100644
--- a/net/disk_cache/mapped_file_unittest.cc
+++ b/net/disk_cache/mapped_file_unittest.cc
@@ -19,26 +19,18 @@
 // Implementation of FileIOCallback for the tests.
 class FileCallbackTest: public disk_cache::FileIOCallback {
  public:
-  explicit FileCallbackTest(int id) : id_(id), reuse_(0) {}
-  explicit FileCallbackTest(int id, bool reuse)
-      : id_(id), reuse_(reuse_ ? 0 : 1) {}
+  explicit FileCallbackTest(int id) : id_(id) {}
   ~FileCallbackTest() {}
 
   virtual void OnFileIOComplete(int bytes_copied);
  private:
   int id_;
-  int reuse_;
 };
 
 void FileCallbackTest::OnFileIOComplete(int bytes_copied) {
   if (id_ > g_cache_tests_max_id) {
     NOTREACHED();
     g_cache_tests_error = true;
-  } else if (reuse_) {
-    DCHECK(1 == reuse_);
-    if (2 == reuse_)
-      g_cache_tests_error = true;
-    reuse_++;
   }
 
   g_cache_tests_received++;
diff --git a/net/disk_cache/mem_backend_impl.cc b/net/disk_cache/mem_backend_impl.cc
index 875efdc..fe57d7d 100644
--- a/net/disk_cache/mem_backend_impl.cc
+++ b/net/disk_cache/mem_backend_impl.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -28,7 +28,8 @@
 
 namespace disk_cache {
 
-Backend* CreateInMemoryCacheBackend(int max_bytes) {
+// Static.
+Backend* MemBackendImpl::CreateBackend(int max_bytes) {
   MemBackendImpl* cache = new MemBackendImpl();
   cache->SetMaxSize(max_bytes);
   if (cache->Init())
@@ -39,8 +40,6 @@
   return NULL;
 }
 
-// ------------------------------------------------------------------------
-
 bool MemBackendImpl::Init() {
   if (max_size_)
     return true;
diff --git a/net/disk_cache/mem_backend_impl.h b/net/disk_cache/mem_backend_impl.h
index 48d196c..c5e947f 100644
--- a/net/disk_cache/mem_backend_impl.h
+++ b/net/disk_cache/mem_backend_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -23,30 +23,29 @@
   MemBackendImpl() : max_size_(0), current_size_(0) {}
   ~MemBackendImpl();
 
+  // Returns an instance of a Backend implemented only in memory. The returned
+  // object should be deleted when not needed anymore. max_bytes is the maximum
+  // size the cache can grow to. If zero is passed in as max_bytes, the cache
+  // will determine the value to use based on the available memory. The returned
+  // pointer can be NULL if a fatal error is found.
+  static Backend* CreateBackend(int max_bytes);
+
   // Performs general initialization for this current instance of the cache.
   bool Init();
 
   // Backend interface.
   virtual int32 GetEntryCount() const;
-  virtual bool OpenEntry(const std::string& key, Entry** entry);
   virtual int OpenEntry(const std::string& key, Entry** entry,
                         CompletionCallback* callback);
-  virtual bool CreateEntry(const std::string& key, Entry** entry);
   virtual int CreateEntry(const std::string& key, Entry** entry,
                           CompletionCallback* callback);
-  virtual bool DoomEntry(const std::string& key);
   virtual int DoomEntry(const std::string& key, CompletionCallback* callback);
-  virtual bool DoomAllEntries();
   virtual int DoomAllEntries(CompletionCallback* callback);
-  virtual bool DoomEntriesBetween(const base::Time initial_time,
-                                  const base::Time end_time);
   virtual int DoomEntriesBetween(const base::Time initial_time,
                                  const base::Time end_time,
                                  CompletionCallback* callback);
-  virtual bool DoomEntriesSince(const base::Time initial_time);
   virtual int DoomEntriesSince(const base::Time initial_time,
                                CompletionCallback* callback);
-  virtual bool OpenNextEntry(void** iter, Entry** next_entry);
   virtual int OpenNextEntry(void** iter, Entry** next_entry,
                             CompletionCallback* callback);
   virtual void EndEnumeration(void** iter);
@@ -78,6 +77,16 @@
   void RemoveFromRankingList(MemEntryImpl* entry);
 
  private:
+  // Old Backend interface.
+  bool OpenEntry(const std::string& key, Entry** entry);
+  bool CreateEntry(const std::string& key, Entry** entry);
+  bool DoomEntry(const std::string& key);
+  bool DoomAllEntries();
+  bool DoomEntriesBetween(const base::Time initial_time,
+                          const base::Time end_time);
+  bool DoomEntriesSince(const base::Time initial_time);
+  bool OpenNextEntry(void** iter, Entry** next_entry);
+
   // Deletes entries from the cache until the current size is below the limit.
   // If empty is true, the whole cache will be trimmed, regardless of being in
   // use.
@@ -94,7 +103,7 @@
   int32 max_size_;        // Maximum data size for this instance.
   int32 current_size_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(MemBackendImpl);
+  DISALLOW_COPY_AND_ASSIGN(MemBackendImpl);
 };
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/mem_entry_impl.cc b/net/disk_cache/mem_entry_impl.cc
index 002b514..645619e 100644
--- a/net/disk_cache/mem_entry_impl.cc
+++ b/net/disk_cache/mem_entry_impl.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -312,6 +312,11 @@
   return GetAvailableRange(offset, len, start);
 }
 
+bool MemEntryImpl::CouldBeSparse() const {
+  DCHECK_EQ(kParentEntry, type());
+  return (children_.get() != NULL);
+}
+
 int MemEntryImpl::ReadyForSparseIO(
     net::CompletionCallback* completion_callback) {
   return net::OK;
diff --git a/net/disk_cache/mem_entry_impl.h b/net/disk_cache/mem_entry_impl.h
index 4ff7a0f..66535d5 100644
--- a/net/disk_cache/mem_entry_impl.h
+++ b/net/disk_cache/mem_entry_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -67,9 +67,9 @@
                              net::CompletionCallback* completion_callback);
   virtual int WriteSparseData(int64 offset, net::IOBuffer* buf, int buf_len,
                               net::CompletionCallback* completion_callback);
-  virtual int GetAvailableRange(int64 offset, int len, int64* start);
   virtual int GetAvailableRange(int64 offset, int len, int64* start,
                                 CompletionCallback* callback);
+  virtual bool CouldBeSparse() const;
   virtual void CancelSparseIO() {}
   virtual int ReadyForSparseIO(net::CompletionCallback* completion_callback);
 
@@ -112,6 +112,9 @@
 
   ~MemEntryImpl();
 
+  // Old Entry interface.
+  int GetAvailableRange(int64 offset, int len, int64* start);
+
   // Grows and cleans up the data buffer.
   void PrepareTarget(int index, int offset, int buf_len);
 
@@ -159,7 +162,7 @@
   MemBackendImpl* backend_;   // Back pointer to the cache.
   bool doomed_;               // True if this entry was removed from the cache.
 
-  DISALLOW_EVIL_CONSTRUCTORS(MemEntryImpl);
+  DISALLOW_COPY_AND_ASSIGN(MemEntryImpl);
 };
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/mem_rankings.h b/net/disk_cache/mem_rankings.h
index 680be4c..fa90688 100644
--- a/net/disk_cache/mem_rankings.h
+++ b/net/disk_cache/mem_rankings.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -36,7 +36,7 @@
   MemEntryImpl* head_;
   MemEntryImpl* tail_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(MemRankings);
+  DISALLOW_COPY_AND_ASSIGN(MemRankings);
 };
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/rankings.cc b/net/disk_cache/rankings.cc
index d2250df..34c002b 100644
--- a/net/disk_cache/rankings.cc
+++ b/net/disk_cache/rankings.cc
@@ -11,6 +11,7 @@
 #include "net/disk_cache/histogram_macros.h"
 
 using base::Time;
+using base::TimeTicks;
 
 // This is used by crash_cache.exe to generate unit test files.
 disk_cache::RankCrashes g_rankings_crash = disk_cache::NO_CRASH;
@@ -207,7 +208,7 @@
   if (!rankings->address().is_initialized())
     return false;
 
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
   if (!rankings->Load())
     return false;
 
@@ -406,7 +407,7 @@
 // but the net effect is just an assert on debug when attempting to remove the
 // entry. Otherwise we'll need reentrant transactions, which is an overkill.
 void Rankings::UpdateRank(CacheRankingsBlock* node, bool modified, List list) {
-  Time start = Time::Now();
+  TimeTicks start = TimeTicks::Now();
   Remove(node, list);
   Insert(node, modified, list);
   CACHE_UMA(AGE_MS, "UpdateRank", 0, start);
diff --git a/net/disk_cache/rankings.h b/net/disk_cache/rankings.h
index c9fc8d2..f635355 100644
--- a/net/disk_cache/rankings.h
+++ b/net/disk_cache/rankings.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -86,7 +86,7 @@
 
    private:
     Rankings* rankings_;
-    DISALLOW_EVIL_CONSTRUCTORS(ScopedRankingsBlock);
+    DISALLOW_COPY_AND_ASSIGN(ScopedRankingsBlock);
   };
 
   // If we have multiple lists, we have to iterate through all at the same time.
diff --git a/net/disk_cache/sparse_control.cc b/net/disk_cache/sparse_control.cc
index 9f0f537..884a1b7 100644
--- a/net/disk_cache/sparse_control.cc
+++ b/net/disk_cache/sparse_control.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2010 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.
 
@@ -71,7 +71,7 @@
   disk_cache::Bitmap children_map_;
   int64 signature_;
   scoped_array<char> buffer_;
-  DISALLOW_EVIL_CONSTRUCTORS(ChildrenDeleter);
+  DISALLOW_COPY_AND_ASSIGN(ChildrenDeleter);
 };
 
 // This is the callback of the file operation.
@@ -126,7 +126,7 @@
     return Release();
   }
   std::string child_name = GenerateChildName(name_, signature_, child_id);
-  backend_->DoomEntry(child_name);
+  backend_->SyncDoomEntry(child_name);
   children_map_.Set(child_id, false);
 
   // Post a task to delete the next child.
@@ -166,6 +166,16 @@
   return rv;
 }
 
+bool SparseControl::CouldBeSparse() const {
+  DCHECK(!init_);
+
+  if (entry_->GetDataSize(kSparseData))
+    return false;
+
+  // We don't verify the data, just see if it could be there.
+  return (entry_->GetDataSize(kSparseIndex) != 0);
+}
+
 int SparseControl::StartIO(SparseOperation op, int64 offset, net::IOBuffer* buf,
                            int buf_len, net::CompletionCallback* callback) {
   DCHECK(init_);
@@ -363,9 +373,12 @@
     CloseChild();
   }
 
-  // Se if we are tracking this child.
-  bool child_present = ChildPresent();
-  if (!child_present || !entry_->backend_->OpenEntry(key, &child_))
+  // See if we are tracking this child.
+  if (!ChildPresent())
+    return ContinueWithoutChild(key);
+
+  child_ = entry_->backend_->OpenEntryImpl(key);
+  if (!child_)
     return ContinueWithoutChild(key);
 
   EntryImpl* child = static_cast<EntryImpl*>(child_);
@@ -388,7 +401,7 @@
 
   if (child_data_.header.last_block_len < 0 ||
       child_data_.header.last_block_len > kBlockSize) {
-    // Make sure this values are always within range.
+    // Make sure these values are always within range.
     child_data_.header.last_block_len = 0;
     child_data_.header.last_block = -1;
   }
@@ -405,7 +418,7 @@
                              NULL, false);
   if (rv != sizeof(child_data_))
     DLOG(ERROR) << "Failed to save child data";
-  child_->Close();
+  child_->Release();
   child_ = NULL;
 }
 
@@ -417,8 +430,8 @@
 // We are deleting the child because something went wrong.
 bool SparseControl::KillChildAndContinue(const std::string& key, bool fatal) {
   SetChildBit(false);
-  child_->Doom();
-  child_->Close();
+  child_->DoomImpl();
+  child_->Release();
   child_ = NULL;
   if (fatal) {
     result_ = net::ERR_CACHE_READ_FAILURE;
@@ -434,7 +447,8 @@
   if (kGetRangeOperation == operation_)
     return true;
 
-  if (!entry_->backend_->CreateEntry(key, &child_)) {
+  child_ = entry_->backend_->CreateEntryImpl(key);
+  if (!child_) {
     child_ = NULL;
     result_ = net::ERR_CACHE_READ_FAILURE;
     return false;
@@ -603,12 +617,12 @@
   int rv = 0;
   switch (operation_) {
     case kReadOperation:
-      rv = child_->ReadData(kSparseData, child_offset_, user_buf_, child_len_,
-                            callback);
+      rv = child_->ReadDataImpl(kSparseData, child_offset_, user_buf_,
+                                child_len_, callback);
       break;
     case kWriteOperation:
-      rv = child_->WriteData(kSparseData, child_offset_, user_buf_, child_len_,
-                             callback, false);
+      rv = child_->WriteDataImpl(kSparseData, child_offset_, user_buf_,
+                                 child_len_, callback, false);
       break;
     case kGetRangeOperation:
       rv = DoGetAvailableRange();
diff --git a/net/disk_cache/sparse_control.h b/net/disk_cache/sparse_control.h
index 0005f58..b88f258 100644
--- a/net/disk_cache/sparse_control.h
+++ b/net/disk_cache/sparse_control.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2010 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.
 
@@ -52,6 +52,10 @@
   // on disk and returns net::OK. Otherwise it returns a net error code.
   int Init();
 
+  // Performs a quick test to see if the entry is sparse or not, without
+  // generating disk IO (so the answer provided is only a best effort).
+  bool CouldBeSparse() const;
+
   // Performs an actual sparse read or write operation for this entry. |op| is
   // the operation to perform, |offset| is the desired sparse offset, |buf| and
   // |buf_len| specify the actual data to use and |callback| is the callback
@@ -146,7 +150,7 @@
   void DoAbortCallbacks();
 
   EntryImpl* entry_;  // The sparse entry.
-  Entry* child_;  // The current child entry.
+  EntryImpl* child_;  // The current child entry.
   SparseOperation operation_;
   bool pending_;  // True if any child IO operation returned pending.
   bool finished_;
diff --git a/net/disk_cache/storage_block.h b/net/disk_cache/storage_block.h
index 0d94b82..e77c5bb 100644
--- a/net/disk_cache/storage_block.h
+++ b/net/disk_cache/storage_block.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -82,7 +82,7 @@
   bool own_data_;  // Is data_ owned by this object or shared with someone else.
   bool extended_;  // Used to store an entry of more than one block.
 
-  DISALLOW_EVIL_CONSTRUCTORS(StorageBlock);
+  DISALLOW_COPY_AND_ASSIGN(StorageBlock);
 };
 
 typedef StorageBlock<EntryStore> CacheEntryBlock;
diff --git a/net/disk_cache/stress_cache.cc b/net/disk_cache/stress_cache.cc
index 67c396c..b153659 100644
--- a/net/disk_cache/stress_cache.cc
+++ b/net/disk_cache/stress_cache.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -24,6 +24,8 @@
 #include "base/process_util.h"
 #include "base/string_util.h"
 #include "base/thread.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
 #include "net/base/io_buffer.h"
 #include "net/disk_cache/backend_impl.h"
 #include "net/disk_cache/disk_cache.h"
@@ -77,11 +79,20 @@
 void StressTheCache(int iteration) {
   int cache_size = 0x800000;  // 8MB
   FilePath path = GetCacheFilePath().AppendASCII("_stress");
-  disk_cache::BackendImpl* cache = new disk_cache::BackendImpl(path);
-  cache->SetFlags(disk_cache::kNoLoadProtection | disk_cache::kNoRandom);
-  cache->SetMaxSize(cache_size);
-  cache->SetType(net::DISK_CACHE);
-  if (!cache->Init()) {
+
+  base::Thread cache_thread("CacheThread");
+  if (!cache_thread.StartWithOptions(
+          base::Thread::Options(MessageLoop::TYPE_IO, 0)))
+    return;
+
+  TestCompletionCallback cb;
+  disk_cache::Backend* cache;
+  int rv = disk_cache::BackendImpl::CreateBackend(
+               path, false, cache_size, net::DISK_CACHE,
+               disk_cache::kNoLoadProtection | disk_cache::kNoRandom,
+               cache_thread.message_loop_proxy(), &cache, &cb);
+
+  if (cb.GetResult(rv) != net::OK) {
     printf("Unable to initialize cache.\n");
     return;
   }
@@ -111,20 +122,24 @@
     if (entries[slot])
       entries[slot]->Close();
 
-    if (!cache->OpenEntry(keys[key], &entries[slot]))
-      CHECK(cache->CreateEntry(keys[key], &entries[slot]));
+    rv = cache->OpenEntry(keys[key], &entries[slot], &cb);
+    if (cb.GetResult(rv) != net::OK) {
+      rv = cache->CreateEntry(keys[key], &entries[slot], &cb);
+      CHECK_EQ(net::OK, cb.GetResult(rv));
+    }
 
     base::snprintf(buffer->data(), kSize, "%d %d", iteration, i);
-    CHECK(kSize == entries[slot]->WriteData(0, 0, buffer, kSize, NULL, false));
+    rv = entries[slot]->WriteData(0, 0, buffer, kSize, &cb, false);
+    CHECK_EQ(kSize, cb.GetResult(rv));
 
     if (rand() % 100 > 80) {
       key = rand() % kNumKeys;
-      cache->DoomEntry(keys[key]);
+      rv = cache->DoomEntry(keys[key], &cb);
+      cb.GetResult(rv);
     }
 
     if (!(i % 100))
       printf("Entries: %d    \r", i);
-    MessageLoop::current()->RunAllPending();
   }
 }
 
diff --git a/net/disk_cache/trace.cc b/net/disk_cache/trace.cc
index 94d9fad..7a61e20 100644
--- a/net/disk_cache/trace.cc
+++ b/net/disk_cache/trace.cc
@@ -70,14 +70,15 @@
 }
 
 void DestroyTrace(void) {
-  DCHECK(s_trace_buffer);
   delete s_trace_buffer;
   s_trace_buffer = NULL;
   s_trace_object = NULL;
 }
 
 void Trace(const char* format, ...) {
-  DCHECK(s_trace_buffer);
+  if (!s_trace_buffer)
+    return;
+
   va_list ap;
   va_start(ap, format);
 
diff --git a/net/disk_cache/trace.h b/net/disk_cache/trace.h
index be50417..a431890 100644
--- a/net/disk_cache/trace.h
+++ b/net/disk_cache/trace.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -36,7 +36,7 @@
   ~TraceObject() {
     DestroyTrace();
   }
-  DISALLOW_EVIL_CONSTRUCTORS(TraceObject);
+  DISALLOW_COPY_AND_ASSIGN(TraceObject);
 };
 
 // Traces to the internal buffer.
diff --git a/net/fetch_client.target.mk b/net/fetch_client.target.mk
new file mode 100644
index 0000000..114ceb5
--- /dev/null
+++ b/net/fetch_client.target.mk
@@ -0,0 +1,189 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := fetch_client
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I. \
+	-Itesting/gtest/include
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I. \
+	-Itesting/gtest/include
+
+OBJS := $(obj).target/$(TARGET)/net/tools/fetch/fetch_client.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/net/libnet.a $(obj).target/base/libbase.a $(obj).target/testing/libgtest.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-Wl,--gc-sections
+
+LIBS := -lrt \
+	-ldl \
+	-lgtk-x11-2.0 \
+	-lgdk-x11-2.0 \
+	-latk-1.0 \
+	-lgio-2.0 \
+	-lpangoft2-1.0 \
+	-lgdk_pixbuf-2.0 \
+	-lm \
+	-lpangocairo-1.0 \
+	-lcairo \
+	-lpango-1.0 \
+	-lfreetype \
+	-lfontconfig \
+	-lgobject-2.0 \
+	-lgmodule-2.0 \
+	-lgthread-2.0 \
+	-lglib-2.0 \
+	-lnss3 \
+	-lnssutil3 \
+	-lsmime3 \
+	-lplds4 \
+	-lplc4 \
+	-lnspr4 \
+	-lpthread \
+	-lz \
+	-lgconf-2
+
+$(builddir)/fetch_client: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(builddir)/fetch_client: LIBS := $(LIBS)
+$(builddir)/fetch_client: TOOLSET := $(TOOLSET)
+$(builddir)/fetch_client: $(OBJS) $(obj).target/net/libnet.a $(obj).target/base/libbase.a $(obj).target/testing/libgtest.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a FORCE_DO_CMD
+	$(call do_cmd,link)
+
+all_deps += $(builddir)/fetch_client
+# Add target alias
+.PHONY: fetch_client
+fetch_client: $(builddir)/fetch_client
+
+# Add executable to "all" target.
+.PHONY: all
+all: $(builddir)/fetch_client
+
diff --git a/net/fetch_server.target.mk b/net/fetch_server.target.mk
new file mode 100644
index 0000000..7d176e6
--- /dev/null
+++ b/net/fetch_server.target.mk
@@ -0,0 +1,192 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := fetch_server
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I. \
+	-Itesting/gtest/include
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I. \
+	-Itesting/gtest/include
+
+OBJS := $(obj).target/$(TARGET)/net/tools/fetch/fetch_server.o \
+	$(obj).target/$(TARGET)/net/tools/fetch/http_listen_socket.o \
+	$(obj).target/$(TARGET)/net/tools/fetch/http_server.o \
+	$(obj).target/$(TARGET)/net/tools/fetch/http_session.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/net/libnet.a $(obj).target/base/libbase.a $(obj).target/testing/libgtest.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-Wl,--gc-sections
+
+LIBS := -lrt \
+	-ldl \
+	-lgtk-x11-2.0 \
+	-lgdk-x11-2.0 \
+	-latk-1.0 \
+	-lgio-2.0 \
+	-lpangoft2-1.0 \
+	-lgdk_pixbuf-2.0 \
+	-lm \
+	-lpangocairo-1.0 \
+	-lcairo \
+	-lpango-1.0 \
+	-lfreetype \
+	-lfontconfig \
+	-lgobject-2.0 \
+	-lgmodule-2.0 \
+	-lgthread-2.0 \
+	-lglib-2.0 \
+	-lnss3 \
+	-lnssutil3 \
+	-lsmime3 \
+	-lplds4 \
+	-lplc4 \
+	-lnspr4 \
+	-lpthread \
+	-lz \
+	-lgconf-2
+
+$(builddir)/fetch_server: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(builddir)/fetch_server: LIBS := $(LIBS)
+$(builddir)/fetch_server: TOOLSET := $(TOOLSET)
+$(builddir)/fetch_server: $(OBJS) $(obj).target/net/libnet.a $(obj).target/base/libbase.a $(obj).target/testing/libgtest.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a FORCE_DO_CMD
+	$(call do_cmd,link)
+
+all_deps += $(builddir)/fetch_server
+# Add target alias
+.PHONY: fetch_server
+fetch_server: $(builddir)/fetch_server
+
+# Add executable to "all" target.
+.PHONY: all
+all: $(builddir)/fetch_server
+
diff --git a/net/ftp/ftp_directory_listing_buffer.cc b/net/ftp/ftp_directory_listing_buffer.cc
index 2376b8f..8399f13 100644
--- a/net/ftp/ftp_directory_listing_buffer.cc
+++ b/net/ftp/ftp_directory_listing_buffer.cc
@@ -4,6 +4,7 @@
 
 #include "net/ftp/ftp_directory_listing_buffer.h"
 
+#include "base/i18n/icu_encoding_detection.h"
 #include "base/i18n/icu_string_conversions.h"
 #include "base/stl_util-inl.h"
 #include "base/string_util.h"
@@ -13,41 +14,15 @@
 #include "net/ftp/ftp_directory_listing_parser_netware.h"
 #include "net/ftp/ftp_directory_listing_parser_vms.h"
 #include "net/ftp/ftp_directory_listing_parser_windows.h"
-#include "unicode/ucsdet.h"
-
-namespace {
-
-// A very simple-minded character encoding detection.
-// TODO(jungshik): We can apply more heuristics here (e.g. using various hints
-// like TLD, the UI language/default encoding of a client, etc). In that case,
-// this should be pulled out of here and moved somewhere in base because there
-// can be other use cases.
-std::string DetectEncoding(const std::string& text) {
-  if (IsStringASCII(text))
-    return std::string();
-  UErrorCode status = U_ZERO_ERROR;
-  UCharsetDetector* detector = ucsdet_open(&status);
-  ucsdet_setText(detector, text.data(), static_cast<int32_t>(text.length()),
-                 &status);
-  const UCharsetMatch* match = ucsdet_detect(detector, &status);
-  const char* encoding = ucsdet_getName(match, &status);
-  ucsdet_close(detector);
-  // Should we check the quality of the match? A rather arbitrary number is
-  // assigned by ICU and it's hard to come up with a lower limit.
-  if (U_FAILURE(status))
-    return std::string();
-  return encoding;
-}
-
-}  // namespace
 
 namespace net {
 
-FtpDirectoryListingBuffer::FtpDirectoryListingBuffer()
+FtpDirectoryListingBuffer::FtpDirectoryListingBuffer(
+    const base::Time& current_time)
     : current_parser_(NULL) {
-  parsers_.insert(new FtpDirectoryListingParserLs());
+  parsers_.insert(new FtpDirectoryListingParserLs(current_time));
   parsers_.insert(new FtpDirectoryListingParserMlsd());
-  parsers_.insert(new FtpDirectoryListingParserNetware());
+  parsers_.insert(new FtpDirectoryListingParserNetware(current_time));
   parsers_.insert(new FtpDirectoryListingParserVms());
   parsers_.insert(new FtpDirectoryListingParserWindows());
 }
@@ -108,8 +83,10 @@
 }
 
 int FtpDirectoryListingBuffer::ExtractFullLinesFromBuffer() {
-  if (encoding_.empty())
-    encoding_ = DetectEncoding(buffer_);
+  if (encoding_.empty()) {
+    if (!base::DetectEncoding(buffer_, &encoding_))
+      return ERR_ENCODING_DETECTION_FAILED;
+  }
 
   int cut_pos = 0;
   // TODO(phajdan.jr): This code accepts all endlines matching \r*\n. Should it
@@ -172,7 +149,7 @@
   }
 
   if (parsers_.size() != 1) {
-    DCHECK(!current_parser_);
+    current_parser_ = NULL;
 
     // We may hit an ambiguity in case of listings which have no entries. That's
     // fine, as long as all remaining parsers agree that the listing is empty.
diff --git a/net/ftp/ftp_directory_listing_buffer.h b/net/ftp/ftp_directory_listing_buffer.h
index f71685c..4123cf0 100644
--- a/net/ftp/ftp_directory_listing_buffer.h
+++ b/net/ftp/ftp_directory_listing_buffer.h
@@ -21,8 +21,10 @@
 
 class FtpDirectoryListingBuffer {
  public:
-  FtpDirectoryListingBuffer();
-
+  // Constructor. When the current time is needed to guess the year on partial
+  // date strings, |current_time| will be used. This allows passing a specific
+  // date during testing.
+  explicit FtpDirectoryListingBuffer(const base::Time& current_time);
   ~FtpDirectoryListingBuffer();
 
   // Called when data is received from the data socket. Returns network
@@ -43,6 +45,8 @@
   // time, although it will return SERVER_UNKNOWN if it doesn't know the answer.
   FtpServerType GetServerType() const;
 
+  const std::string& encoding() const { return encoding_; }
+
  private:
   typedef std::set<FtpDirectoryListingParser*> ParserSet;
 
diff --git a/net/ftp/ftp_directory_listing_buffer_unittest.cc b/net/ftp/ftp_directory_listing_buffer_unittest.cc
index cb24489..c3c55d0 100644
--- a/net/ftp/ftp_directory_listing_buffer_unittest.cc
+++ b/net/ftp/ftp_directory_listing_buffer_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/path_service.h"
 #include "base/string_tokenizer.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "net/base/net_errors.h"
 #include "net/ftp/ftp_directory_listing_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -34,6 +35,7 @@
     "dir-listing-ls-14",
     "dir-listing-ls-15",
     "dir-listing-ls-16",
+    "dir-listing-ls-17",
     "dir-listing-mlsd-1",
     "dir-listing-mlsd-2",
     "dir-listing-netware-1",
@@ -53,10 +55,14 @@
   test_dir = test_dir.AppendASCII("data");
   test_dir = test_dir.AppendASCII("ftp");
 
+  base::Time mock_current_time;
+  ASSERT_TRUE(base::Time::FromString(L"Tue, 15 Nov 1994 12:45:26 GMT",
+                                     &mock_current_time));
+
   for (size_t i = 0; i < arraysize(test_files); i++) {
     SCOPED_TRACE(StringPrintf("Test[%" PRIuS "]: %s", i, test_files[i]));
 
-    net::FtpDirectoryListingBuffer buffer;
+    net::FtpDirectoryListingBuffer buffer(mock_current_time);
 
     std::string test_listing;
     EXPECT_TRUE(file_util::ReadFileToString(test_dir.AppendASCII(test_files[i]),
@@ -84,14 +90,7 @@
 
       SCOPED_TRACE(StringPrintf("Filename: %s", name.c_str()));
 
-      int year;
-      if (lines[8 * i + 3] == "current") {
-        base::Time::Exploded now_exploded;
-        base::Time::Now().LocalExplode(&now_exploded);
-        year = now_exploded.year;
-      } else {
-        year = StringToInt(lines[8 * i + 3]);
-      }
+      int year = StringToInt(lines[8 * i + 3]);
       int month = StringToInt(lines[8 * i + 4]);
       int day_of_month = StringToInt(lines[8 * i + 5]);
       int hour = StringToInt(lines[8 * i + 6]);
diff --git a/net/ftp/ftp_directory_listing_parser_ls.cc b/net/ftp/ftp_directory_listing_parser_ls.cc
index 1098cda..55f7844 100644
--- a/net/ftp/ftp_directory_listing_parser_ls.cc
+++ b/net/ftp/ftp_directory_listing_parser_ls.cc
@@ -42,12 +42,13 @@
           (text.substr(10).empty() || text.substr(10) == ASCIIToUTF16("+")));
 }
 
-bool DetectColumnOffset(const std::vector<string16>& columns, int* offset) {
+bool DetectColumnOffset(const std::vector<string16>& columns,
+                        const base::Time& current_time, int* offset) {
   base::Time time;
 
   if (columns.size() >= 8 &&
       net::FtpUtil::LsDateListingToTime(columns[5], columns[6], columns[7],
-                                        &time)) {
+                                        current_time, &time)) {
     // Standard listing, exactly like ls -l.
     *offset = 2;
     return true;
@@ -55,7 +56,7 @@
 
   if (columns.size() >= 7 &&
       net::FtpUtil::LsDateListingToTime(columns[4], columns[5], columns[6],
-                                        &time)) {
+                                        current_time, &time)) {
     // wu-ftpd listing, no "number of links" column.
     *offset = 1;
     return true;
@@ -63,7 +64,7 @@
 
   if (columns.size() >= 6 &&
       net::FtpUtil::LsDateListingToTime(columns[3], columns[4], columns[5],
-                                        &time)) {
+                                        current_time, &time)) {
     // Xplain FTP Server listing for folders, like this:
     // drwxr-xr-x               folder        0 Jul 17  2006 online
     *offset = 0;
@@ -78,9 +79,11 @@
 
 namespace net {
 
-FtpDirectoryListingParserLs::FtpDirectoryListingParserLs()
+FtpDirectoryListingParserLs::FtpDirectoryListingParserLs(
+    const base::Time& current_time)
     : received_nonempty_line_(false),
-      received_total_line_(false) {
+      received_total_line_(false),
+      current_time_(current_time) {
 }
 
 bool FtpDirectoryListingParserLs::ConsumeLine(const string16& line) {
@@ -113,7 +116,7 @@
   }
 
   int column_offset;
-  if (!DetectColumnOffset(columns, &column_offset))
+  if (!DetectColumnOffset(columns, current_time_, &column_offset))
     return false;
 
   // We may receive file names containing spaces, which can make the number of
@@ -144,6 +147,7 @@
   if (!FtpUtil::LsDateListingToTime(columns[3 + column_offset],
                                     columns[4 + column_offset],
                                     columns[5 + column_offset],
+                                    current_time_,
                                     &entry.last_modified)) {
     return false;
   }
diff --git a/net/ftp/ftp_directory_listing_parser_ls.h b/net/ftp/ftp_directory_listing_parser_ls.h
index 3fd0d32..0d52791 100644
--- a/net/ftp/ftp_directory_listing_parser_ls.h
+++ b/net/ftp/ftp_directory_listing_parser_ls.h
@@ -7,6 +7,7 @@
 
 #include <queue>
 
+#include "base/time.h"
 #include "net/ftp/ftp_directory_listing_parser.h"
 
 namespace net {
@@ -14,7 +15,10 @@
 // Parser for "ls -l"-style directory listing.
 class FtpDirectoryListingParserLs : public FtpDirectoryListingParser {
  public:
-  FtpDirectoryListingParserLs();
+  // Constructor. When the current time is needed to guess the year on partial
+  // date strings, |current_time| will be used. This allows passing a specific
+  // date during testing.
+  explicit FtpDirectoryListingParserLs(const base::Time& current_time);
 
   // FtpDirectoryListingParser methods:
   virtual FtpServerType GetServerType() const { return SERVER_LS; }
@@ -30,6 +34,9 @@
   // integer. Only one such header is allowed per listing.
   bool received_total_line_;
 
+  // Store the current time. We need it to correctly parse received dates.
+  const base::Time current_time_;
+
   std::queue<FtpDirectoryListingEntry> entries_;
 
   DISALLOW_COPY_AND_ASSIGN(FtpDirectoryListingParserLs);
diff --git a/net/ftp/ftp_directory_listing_parser_ls_unittest.cc b/net/ftp/ftp_directory_listing_parser_ls_unittest.cc
index ad7edc1..608e0e2 100644
--- a/net/ftp/ftp_directory_listing_parser_ls_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_ls_unittest.cc
@@ -5,6 +5,7 @@
 #include "net/ftp/ftp_directory_listing_parser_unittest.h"
 
 #include "base/format_macros.h"
+#include "base/string_util.h"
 #include "net/ftp/ftp_directory_listing_parser_ls.h"
 
 namespace {
@@ -12,8 +13,9 @@
 typedef net::FtpDirectoryListingParserTest FtpDirectoryListingParserLsTest;
 
 TEST_F(FtpDirectoryListingParserLsTest, Good) {
-  base::Time::Exploded now_exploded;
-  base::Time::Now().LocalExplode(&now_exploded);
+  base::Time mock_current_time;
+  ASSERT_TRUE(base::Time::FromString(L"Tue, 15 Nov 1994 12:45:26 GMT",
+                                     &mock_current_time));
 
   const struct SingleLineTestData good_cases[] = {
     { "-rw-r--r--    1 ftp      ftp           528 Nov 01  2007 README",
@@ -21,13 +23,13 @@
       2007, 11, 1, 0, 0 },
     { "drwxr-xr-x    3 ftp      ftp          4096 May 15 18:11 directory",
       net::FtpDirectoryListingEntry::DIRECTORY, "directory", -1,
-      now_exploded.year, 5, 15, 18, 11 },
+      1994, 5, 15, 18, 11 },
     { "lrwxrwxrwx 1 0  0 26 Sep 18 2008 pub -> vol/1/.CLUSTER/var_ftp/pub",
       net::FtpDirectoryListingEntry::SYMLINK, "pub", -1,
       2008, 9, 18, 0, 0 },
     { "lrwxrwxrwx 1 0  0 3 Oct 12 13:37 mirror -> pub",
       net::FtpDirectoryListingEntry::SYMLINK, "mirror", -1,
-      now_exploded.year, 10, 12, 13, 37 },
+      1994, 10, 12, 13, 37 },
     { "drwxrwsr-x    4 501      501          4096 Feb 20  2007 pub",
       net::FtpDirectoryListingEntry::DIRECTORY, "pub", -1,
       2007, 2, 20, 0, 0 },
@@ -36,13 +38,13 @@
       2007, 4, 8, 0, 0 },
     { "drwx-wx-wt  2 root  wheel  512 Jul  1 02:15 incoming",
       net::FtpDirectoryListingEntry::DIRECTORY, "incoming", -1,
-      now_exploded.year, 7, 1, 2, 15 },
+      1994, 7, 1, 2, 15 },
     { "-rw-r--r-- 1 2 3 3447432 May 18  2009 Foo - Manual.pdf",
       net::FtpDirectoryListingEntry::FILE, "Foo - Manual.pdf", 3447432,
       2009, 5, 18, 0, 0 },
     { "d-wx-wx-wt+  4 ftp      989          512 Dec  8 15:54 incoming",
       net::FtpDirectoryListingEntry::DIRECTORY, "incoming", -1,
-      now_exploded.year, 12, 8, 15, 54 },
+      1993, 12, 8, 15, 54 },
 
     // Tests for the wu-ftpd variant:
     { "drwxr-xr-x   2 sys          512 Mar 27  2009 pub",
@@ -64,7 +66,7 @@
       2006, 3, 1, 0, 0 },
     { "dr--r--r--  1 ftp      -----           0 Nov 17 17:08 kernels",
       net::FtpDirectoryListingEntry::DIRECTORY, "kernels", -1,
-      now_exploded.year, 11, 17, 17, 8 },
+      1993, 11, 17, 17, 8 },
 
     // Tests for "ls -l" style listing sent by Xplain FTP Server.
     { "drwxr-xr-x               folder        0 Jul 17  2006 online",
@@ -74,12 +76,16 @@
   for (size_t i = 0; i < arraysize(good_cases); i++) {
     SCOPED_TRACE(StringPrintf("Test[%" PRIuS "]: %s", i, good_cases[i].input));
 
-    net::FtpDirectoryListingParserLs parser;
+    net::FtpDirectoryListingParserLs parser(mock_current_time);
     RunSingleLineTestCase(&parser, good_cases[i]);
   }
 }
 
 TEST_F(FtpDirectoryListingParserLsTest, Bad) {
+  base::Time mock_current_time;
+  ASSERT_TRUE(base::Time::FromString(L"Tue, 15 Nov 1994 12:45:26 GMT",
+                                     &mock_current_time));
+
   const char* bad_cases[] = {
     "garbage",
     "-rw-r--r-- ftp ftp",
@@ -106,7 +112,7 @@
     "drwxr-xr-x   folder     0 May 15 18:11",
   };
   for (size_t i = 0; i < arraysize(bad_cases); i++) {
-    net::FtpDirectoryListingParserLs parser;
+    net::FtpDirectoryListingParserLs parser(mock_current_time);
     EXPECT_FALSE(parser.ConsumeLine(UTF8ToUTF16(bad_cases[i]))) << bad_cases[i];
   }
 }
diff --git a/net/ftp/ftp_directory_listing_parser_mlsd.cc b/net/ftp/ftp_directory_listing_parser_mlsd.cc
index 3929bf2..7715dd3 100644
--- a/net/ftp/ftp_directory_listing_parser_mlsd.cc
+++ b/net/ftp/ftp_directory_listing_parser_mlsd.cc
@@ -9,6 +9,7 @@
 
 #include "base/stl_util-inl.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 
 // You can read the specification of the MLSD format at
 // http://tools.ietf.org/html/rfc3659#page-23.
diff --git a/net/ftp/ftp_directory_listing_parser_mlsd_unittest.cc b/net/ftp/ftp_directory_listing_parser_mlsd_unittest.cc
index 34c2481..9663664 100644
--- a/net/ftp/ftp_directory_listing_parser_mlsd_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_mlsd_unittest.cc
@@ -5,6 +5,7 @@
 #include "net/ftp/ftp_directory_listing_parser_unittest.h"
 
 #include "base/format_macros.h"
+#include "base/string_util.h"
 #include "net/ftp/ftp_directory_listing_parser_mlsd.h"
 
 namespace {
diff --git a/net/ftp/ftp_directory_listing_parser_netware.cc b/net/ftp/ftp_directory_listing_parser_netware.cc
index 20d2b25..60c6c6a 100644
--- a/net/ftp/ftp_directory_listing_parser_netware.cc
+++ b/net/ftp/ftp_directory_listing_parser_netware.cc
@@ -31,8 +31,10 @@
 
 namespace net {
 
-FtpDirectoryListingParserNetware::FtpDirectoryListingParserNetware()
-    : received_first_line_(false) {
+FtpDirectoryListingParserNetware::FtpDirectoryListingParserNetware(
+    const base::Time& current_time)
+    : received_first_line_(false),
+      current_time_(current_time) {
 }
 
 bool FtpDirectoryListingParserNetware::ConsumeLine(const string16& line) {
@@ -75,7 +77,7 @@
 
   // Netware uses the same date listing format as Unix "ls -l".
   if (!FtpUtil::LsDateListingToTime(columns[4], columns[5], columns[6],
-                                    &entry.last_modified)) {
+                                    current_time_, &entry.last_modified)) {
     return false;
   }
 
diff --git a/net/ftp/ftp_directory_listing_parser_netware.h b/net/ftp/ftp_directory_listing_parser_netware.h
index a3b2cef..aef490a 100644
--- a/net/ftp/ftp_directory_listing_parser_netware.h
+++ b/net/ftp/ftp_directory_listing_parser_netware.h
@@ -7,6 +7,7 @@
 
 #include <queue>
 
+#include "base/time.h"
 #include "net/ftp/ftp_directory_listing_parser.h"
 
 namespace net {
@@ -14,7 +15,10 @@
 // Parser for Netware-style directory listing.
 class FtpDirectoryListingParserNetware : public FtpDirectoryListingParser {
  public:
-  FtpDirectoryListingParserNetware();
+  // Constructor. When the current time is needed to guess the year on partial
+  // date strings, |current_time| will be used. This allows passing a specific
+  // date during testing.
+  explicit FtpDirectoryListingParserNetware(const base::Time& current_time);
 
   // FtpDirectoryListingParser methods:
   virtual FtpServerType GetServerType() const { return SERVER_NETWARE; }
@@ -27,6 +31,9 @@
   // True after we have received the first line of input.
   bool received_first_line_;
 
+  // Store the current time. We need it to correctly parse received dates.
+  const base::Time current_time_;
+
   std::queue<FtpDirectoryListingEntry> entries_;
 
   DISALLOW_COPY_AND_ASSIGN(FtpDirectoryListingParserNetware);
diff --git a/net/ftp/ftp_directory_listing_parser_netware_unittest.cc b/net/ftp/ftp_directory_listing_parser_netware_unittest.cc
index 7076a3f..6570846 100644
--- a/net/ftp/ftp_directory_listing_parser_netware_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_netware_unittest.cc
@@ -5,6 +5,8 @@
 #include "net/ftp/ftp_directory_listing_parser_unittest.h"
 
 #include "base/format_macros.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "net/ftp/ftp_directory_listing_parser_netware.h"
 
 namespace {
@@ -12,8 +14,9 @@
 typedef net::FtpDirectoryListingParserTest FtpDirectoryListingParserNetwareTest;
 
 TEST_F(FtpDirectoryListingParserNetwareTest, Good) {
-  base::Time::Exploded now_exploded;
-  base::Time::Now().LocalExplode(&now_exploded);
+  base::Time mock_current_time;
+  ASSERT_TRUE(base::Time::FromString(L"Tue, 15 Nov 1994 12:45:26 GMT",
+                                     &mock_current_time));
 
   const struct SingleLineTestData good_cases[] = {
     { "d [RWCEAFMS] ftpadmin 512 Jan 29  2004 pub",
@@ -21,12 +24,12 @@
       2004, 1, 29, 0, 0 },
     { "- [RW------] ftpadmin 123 Nov 11  18:25 afile",
       net::FtpDirectoryListingEntry::FILE, "afile", 123,
-      now_exploded.year, 11, 11, 18, 25 },
+      1994, 11, 11, 18, 25 },
   };
   for (size_t i = 0; i < arraysize(good_cases); i++) {
     SCOPED_TRACE(StringPrintf("Test[%" PRIuS "]: %s", i, good_cases[i].input));
 
-    net::FtpDirectoryListingParserNetware parser;
+    net::FtpDirectoryListingParserNetware parser(mock_current_time);
     // The parser requires a "total n" like before accepting regular input.
     ASSERT_TRUE(parser.ConsumeLine(UTF8ToUTF16("total 1")));
     RunSingleLineTestCase(&parser, good_cases[i]);
@@ -34,6 +37,10 @@
 }
 
 TEST_F(FtpDirectoryListingParserNetwareTest, Bad) {
+  base::Time mock_current_time;
+  ASSERT_TRUE(base::Time::FromString(L"Tue, 15 Nov 1994 12:45:26 GMT",
+                                     &mock_current_time));
+
   const char* bad_cases[] = {
     "garbage",
     "d [] ftpadmin 512 Jan 29  2004 pub",
@@ -43,7 +50,7 @@
     "l [RW------] ftpadmin 512 Jan 29  2004 pub",
   };
   for (size_t i = 0; i < arraysize(bad_cases); i++) {
-    net::FtpDirectoryListingParserNetware parser;
+    net::FtpDirectoryListingParserNetware parser(mock_current_time);
     // The parser requires a "total n" like before accepting regular input.
     ASSERT_TRUE(parser.ConsumeLine(UTF8ToUTF16("total 1")));
     EXPECT_FALSE(parser.ConsumeLine(UTF8ToUTF16(bad_cases[i]))) << bad_cases[i];
diff --git a/net/ftp/ftp_directory_listing_parser_unittest.h b/net/ftp/ftp_directory_listing_parser_unittest.h
index 7653d21..90670fd 100644
--- a/net/ftp/ftp_directory_listing_parser_unittest.h
+++ b/net/ftp/ftp_directory_listing_parser_unittest.h
@@ -5,7 +5,7 @@
 #ifndef NET_FTP_FTP_DIRECTORY_LISTING_PARSER_UNITTEST_H_
 #define NET_FTP_FTP_DIRECTORY_LISTING_PARSER_UNITTEST_H_
 
-#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "net/ftp/ftp_directory_listing_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/net/ftp/ftp_directory_listing_parser_vms.cc b/net/ftp/ftp_directory_listing_parser_vms.cc
index bfb14d4..f8d3fbb 100644
--- a/net/ftp/ftp_directory_listing_parser_vms.cc
+++ b/net/ftp/ftp_directory_listing_parser_vms.cc
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "net/ftp/ftp_util.h"
 
 namespace {
diff --git a/net/ftp/ftp_directory_listing_parser_vms_unittest.cc b/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
index 723c2ac..363c99c 100644
--- a/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
@@ -5,6 +5,7 @@
 #include "net/ftp/ftp_directory_listing_parser_unittest.h"
 
 #include "base/format_macros.h"
+#include "base/string_util.h"
 #include "net/ftp/ftp_directory_listing_parser_vms.h"
 
 namespace {
diff --git a/net/ftp/ftp_directory_listing_parser_windows_unittest.cc b/net/ftp/ftp_directory_listing_parser_windows_unittest.cc
index bbab699..a768d05 100644
--- a/net/ftp/ftp_directory_listing_parser_windows_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_windows_unittest.cc
@@ -5,6 +5,7 @@
 #include "net/ftp/ftp_directory_listing_parser_unittest.h"
 
 #include "base/format_macros.h"
+#include "base/string_util.h"
 #include "net/ftp/ftp_directory_listing_parser_windows.h"
 
 namespace {
diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc
index 06c9c25..f6b7203 100644
--- a/net/ftp/ftp_network_transaction.cc
+++ b/net/ftp/ftp_network_transaction.cc
@@ -1,16 +1,17 @@
-// Copyright (c) 2008 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.
+// Copyright (c) 2010 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/ftp/ftp_network_transaction.h"
 
 #include "base/compiler_specific.h"
 #include "base/histogram.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "net/base/connection_type_histograms.h"
 #include "net/base/escape.h"
-#include "net/base/load_log.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
 #include "net/base/net_util.h"
 #include "net/ftp/ftp_network_session.h"
 #include "net/ftp/ftp_request_info.h"
@@ -42,6 +43,134 @@
   return true;
 }
 
+enum ErrorClass {
+  // The requested action was initiated. The client should expect another
+  // reply before issuing the next command.
+  ERROR_CLASS_INITIATED,
+
+  // The requested action has been successfully completed.
+  ERROR_CLASS_OK,
+
+  // The command has been accepted, but to complete the operation, more
+  // information must be sent by the client.
+  ERROR_CLASS_INFO_NEEDED,
+
+  // The command was not accepted and the requested action did not take place.
+  // This condition is temporary, and the client is encouraged to restart the
+  // command sequence.
+  ERROR_CLASS_TRANSIENT_ERROR,
+
+  // The command was not accepted and the requested action did not take place.
+  // This condition is rather permanent, and the client is discouraged from
+  // repeating the exact request.
+  ERROR_CLASS_PERMANENT_ERROR,
+};
+
+// Returns the error class for given response code. Caller should ensure
+// that |response_code| is in range 100-599.
+ErrorClass GetErrorClass(int response_code) {
+  if (response_code >= 100 && response_code <= 199)
+    return ERROR_CLASS_INITIATED;
+
+  if (response_code >= 200 && response_code <= 299)
+    return ERROR_CLASS_OK;
+
+  if (response_code >= 300 && response_code <= 399)
+    return ERROR_CLASS_INFO_NEEDED;
+
+  if (response_code >= 400 && response_code <= 499)
+    return ERROR_CLASS_TRANSIENT_ERROR;
+
+  if (response_code >= 500 && response_code <= 599)
+    return ERROR_CLASS_PERMANENT_ERROR;
+
+  // We should not be called on invalid error codes.
+  NOTREACHED() << response_code;
+  return ERROR_CLASS_PERMANENT_ERROR;
+}
+
+// Returns network error code for received FTP |response_code|.
+int GetNetErrorCodeForFtpResponseCode(int response_code) {
+  switch (response_code) {
+    case 421:
+      return net::ERR_FTP_SERVICE_UNAVAILABLE;
+    case 426:
+      return net::ERR_FTP_TRANSFER_ABORTED;
+    case 450:
+      return net::ERR_FTP_FILE_BUSY;
+    case 500:
+    case 501:
+      return net::ERR_FTP_SYNTAX_ERROR;
+    case 502:
+    case 504:
+      return net::ERR_FTP_COMMAND_NOT_SUPPORTED;
+    case 503:
+      return net::ERR_FTP_BAD_COMMAND_SEQUENCE;
+    default:
+      return net::ERR_FTP_FAILED;
+  }
+}
+
+// From RFC 2428 Section 3:
+//   The text returned in response to the EPSV command MUST be:
+//     <some text> (<d><d><d><tcp-port><d>)
+//   <d> is a delimiter character, ideally to be |
+bool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response,
+                                 int* port) {
+  if (response.lines.size() != 1)
+    return false;
+  const char* ptr = response.lines[0].c_str();
+  while (*ptr && *ptr != '(')
+    ++ptr;
+  if (!*ptr)
+    return false;
+  char sep = *(++ptr);
+  if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
+    return false;
+  if (!isdigit(*(++ptr)))
+    return false;
+  *port = *ptr - '0';
+  while (isdigit(*(++ptr))) {
+    *port *= 10;
+    *port += *ptr - '0';
+  }
+  if (*ptr != sep)
+    return false;
+
+  return true;
+}
+
+// There are two way we can receive IP address and port.
+// (127,0,0,1,23,21) IP address and port encapsulated in ().
+// 127,0,0,1,23,21  IP address and port without ().
+//
+// See RFC 959, Section 4.1.2
+bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
+                                 int* port) {
+  if (response.lines.size() != 1)
+    return false;
+  const char* ptr = response.lines[0].c_str();
+  while (*ptr && *ptr != '(')  // Try with bracket.
+    ++ptr;
+  if (*ptr) {
+    ++ptr;
+  } else {
+    ptr = response.lines[0].c_str();  // Try without bracket.
+    while (*ptr && *ptr != ',')
+      ++ptr;
+    while (*ptr && *ptr != ' ')
+      --ptr;
+  }
+  int i0, i1, i2, i3, p0, p1;
+  if (sscanf_s(ptr, "%d,%d,%d,%d,%d,%d", &i0, &i1, &i2, &i3, &p0, &p1) != 6)
+    return false;
+
+  // Ignore the IP address supplied in the response. We are always going
+  // to connect back to the same server to prevent FTP PASV port scanning.
+  *port = (p0 << 8) + p1;
+  return true;
+}
+
 }  // namespace
 
 namespace net {
@@ -59,10 +188,13 @@
       read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
       ctrl_response_buffer_(new FtpCtrlResponseBuffer()),
       read_data_buf_len_(0),
-      file_data_len_(0),
       last_error_(OK),
       system_type_(SYSTEM_TYPE_UNKNOWN),
-      retr_failed_(false),
+      // Use image (binary) transfer by default. It should always work,
+      // whereas the ascii transfer may damage binary data.
+      data_type_(DATA_TYPE_IMAGE),
+      resource_type_(RESOURCE_TYPE_UNKNOWN),
+      use_epsv_(true),
       data_connection_port_(0),
       socket_factory_(socket_factory),
       next_state_(STATE_NONE) {
@@ -73,8 +205,8 @@
 
 int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
                                  CompletionCallback* callback,
-                                 LoadLog* load_log) {
-  load_log_ = load_log;
+                                 const BoundNetLog& net_log) {
+  net_log_ = net_log;
   request_ = request_info;
 
   if (request_->url.has_username()) {
@@ -84,7 +216,9 @@
     password_ = L"chrome@example.com";
   }
 
-  next_state_ = STATE_CTRL_INIT;
+  DetectTypecode();
+
+  next_state_ = STATE_CTRL_RESOLVE_HOST;
   int rv = DoLoop(OK);
   if (rv == ERR_IO_PENDING)
     user_callback_ = callback;
@@ -108,7 +242,7 @@
   username_ = username;
   password_ = password;
 
-  next_state_ = STATE_CTRL_INIT;
+  next_state_ = STATE_CTRL_RESOLVE_HOST;
   int rv = DoLoop(OK);
   if (rv == ERR_IO_PENDING)
     user_callback_ = callback;
@@ -117,7 +251,7 @@
 
 int FtpNetworkTransaction::RestartIgnoringLastError(
     CompletionCallback* callback) {
-  return ERR_FAILED;
+  return ERR_NOT_IMPLEMENTED;
 }
 
 int FtpNetworkTransaction::Read(IOBuffer* buf,
@@ -197,29 +331,6 @@
   return OK;
 }
 
-// static
-FtpNetworkTransaction::ErrorClass FtpNetworkTransaction::GetErrorClass(
-    int response_code) {
-  if (response_code >= 100 && response_code <= 199)
-    return ERROR_CLASS_INITIATED;
-
-  if (response_code >= 200 && response_code <= 299)
-    return ERROR_CLASS_OK;
-
-  if (response_code >= 300 && response_code <= 399)
-    return ERROR_CLASS_INFO_NEEDED;
-
-  if (response_code >= 400 && response_code <= 499)
-    return ERROR_CLASS_TRANSIENT_ERROR;
-
-  if (response_code >= 500 && response_code <= 599)
-    return ERROR_CLASS_PERMANENT_ERROR;
-
-  // We should not be called on invalid error codes.
-  NOTREACHED();
-  return ERROR_CLASS_PERMANENT_ERROR;
-}
-
 int FtpNetworkTransaction::ProcessCtrlResponse() {
   FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
 
@@ -235,9 +346,6 @@
     case COMMAND_PASS:
       rv = ProcessResponsePASS(response);
       break;
-    case COMMAND_ACCT:
-      rv = ProcessResponseACCT(response);
-      break;
     case COMMAND_SYST:
       rv = ProcessResponseSYST(response);
       break;
@@ -247,6 +355,9 @@
     case COMMAND_TYPE:
       rv = ProcessResponseTYPE(response);
       break;
+    case COMMAND_EPSV:
+      rv = ProcessResponseEPSV(response);
+      break;
     case COMMAND_PASV:
       rv = ProcessResponsePASV(response);
       break;
@@ -265,9 +376,6 @@
     case COMMAND_LIST:
       rv = ProcessResponseLIST(response);
       break;
-    case COMMAND_MDTM:
-      rv = ProcessResponseMDTM(response);
-      break;
     case COMMAND_QUIT:
       rv = ProcessResponseQUIT(response);
       break;
@@ -308,11 +416,9 @@
   ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer());
   read_data_buf_ = NULL;
   read_data_buf_len_ = 0;
-  file_data_len_ = 0;
   if (write_buf_)
     write_buf_->SetOffset(0);
   last_error_ = OK;
-  retr_failed_ = false;
   data_connection_port_ = 0;
   ctrl_socket_.reset();
   data_socket_.reset();
@@ -338,8 +444,16 @@
 std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
     bool is_directory) const {
   std::string path(current_remote_directory_);
-  if (request_->url.has_path())
-    path.append(request_->url.path());
+  if (request_->url.has_path()) {
+    std::string gurl_path(request_->url.path());
+
+    // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
+    std::string::size_type pos = gurl_path.rfind(';');
+    if (pos != std::string::npos)
+      gurl_path.resize(pos);
+
+    path.append(gurl_path);
+  }
   // Make sure that if the path is expected to be a file, it won't end
   // with a trailing slash.
   if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
@@ -361,6 +475,27 @@
   return path;
 }
 
+void FtpNetworkTransaction::DetectTypecode() {
+  if (!request_->url.has_path())
+    return;
+  std::string gurl_path(request_->url.path());
+
+  // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
+  std::string::size_type pos = gurl_path.rfind(';');
+  if (pos == std::string::npos)
+    return;
+  std::string typecode_string(gurl_path.substr(pos));
+  if (typecode_string == ";type=a") {
+    data_type_ = DATA_TYPE_ASCII;
+    resource_type_ = RESOURCE_TYPE_FILE;
+  } else if (typecode_string == ";type=i") {
+    data_type_ = DATA_TYPE_IMAGE;
+    resource_type_ = RESOURCE_TYPE_FILE;
+  } else if (typecode_string == ";type=d") {
+    resource_type_ = RESOURCE_TYPE_DIRECTORY;
+  }
+}
+
 int FtpNetworkTransaction::DoLoop(int result) {
   DCHECK(next_state_ != STATE_NONE);
 
@@ -369,13 +504,6 @@
     State state = next_state_;
     next_state_ = STATE_NONE;
     switch (state) {
-      case STATE_CTRL_INIT:
-        DCHECK(rv == OK);
-        rv = DoCtrlInit();
-        break;
-      case STATE_CTRL_INIT_COMPLETE:
-        rv = DoCtrlInitComplete(rv);
-        break;
       case STATE_CTRL_RESOLVE_HOST:
         DCHECK(rv == OK);
         rv = DoCtrlResolveHost();
@@ -416,10 +544,6 @@
         DCHECK(rv == OK);
         rv = DoCtrlWriteSYST();
         break;
-      case STATE_CTRL_WRITE_ACCT:
-        DCHECK(rv == OK);
-        rv = DoCtrlWriteACCT();
-        break;
       case STATE_CTRL_WRITE_PWD:
         DCHECK(rv == OK);
         rv = DoCtrlWritePWD();
@@ -428,6 +552,10 @@
         DCHECK(rv == OK);
         rv = DoCtrlWriteTYPE();
         break;
+      case STATE_CTRL_WRITE_EPSV:
+        DCHECK(rv == OK);
+        rv = DoCtrlWriteEPSV();
+        break;
       case STATE_CTRL_WRITE_PASV:
         DCHECK(rv == OK);
         rv = DoCtrlWritePASV();
@@ -452,15 +580,10 @@
         DCHECK(rv == OK);
         rv = DoCtrlWriteLIST();
         break;
-      case STATE_CTRL_WRITE_MDTM:
-        DCHECK(rv == OK);
-        rv = DoCtrlWriteMDTM();
-        break;
       case STATE_CTRL_WRITE_QUIT:
         DCHECK(rv == OK);
         rv = DoCtrlWriteQUIT();
         break;
-
       case STATE_DATA_CONNECT:
         DCHECK(rv == OK);
         rv = DoDataConnect();
@@ -484,29 +607,18 @@
   return rv;
 }
 
-// TODO(ibrar): Yet to see if we need any intialization
-int FtpNetworkTransaction::DoCtrlInit() {
-  next_state_ = STATE_CTRL_INIT_COMPLETE;
-  return OK;
-}
-
-int FtpNetworkTransaction::DoCtrlInitComplete(int result) {
-  next_state_ = STATE_CTRL_RESOLVE_HOST;
-  return OK;
-}
-
 int FtpNetworkTransaction::DoCtrlResolveHost() {
   next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
 
   std::string host;
   int port;
 
-  host = request_->url.host();
+  host = request_->url.HostNoBrackets();
   port = request_->url.EffectiveIntPort();
 
   HostResolver::RequestInfo info(host, port);
   // No known referrer.
-  return resolver_.Resolve(info, &addresses_, &io_callback_, load_log_);
+  return resolver_.Resolve(info, &addresses_, &io_callback_, net_log_);
 }
 
 int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
@@ -517,8 +629,9 @@
 
 int FtpNetworkTransaction::DoCtrlConnect() {
   next_state_ = STATE_CTRL_CONNECT_COMPLETE;
-  ctrl_socket_.reset(socket_factory_->CreateTCPClientSocket(addresses_));
-  return ctrl_socket_->Connect(&io_callback_, load_log_);
+  ctrl_socket_.reset(socket_factory_->CreateTCPClientSocket(
+        addresses_, net_log_.net_log()));
+  return ctrl_socket_->Connect(&io_callback_);
 }
 
 int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
@@ -606,11 +719,10 @@
       next_state_ = STATE_CTRL_WRITE_PASS;
       break;
     case ERROR_CLASS_TRANSIENT_ERROR:
-      if (response.status_code == 421)
-        return Stop(ERR_FAILED);
-      break;
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_PERMANENT_ERROR:
-      return Stop(ERR_FAILED);
+      response_.needs_auth = true;
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     default:
       NOTREACHED();
       return Stop(ERR_UNEXPECTED);
@@ -636,21 +748,12 @@
       next_state_ = STATE_CTRL_WRITE_SYST;
       break;
     case ERROR_CLASS_INFO_NEEDED:
-      next_state_ = STATE_CTRL_WRITE_ACCT;
-      break;
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_TRANSIENT_ERROR:
-      if (response.status_code == 421) {
-        // TODO(ibrar): Retry here.
-      }
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_PERMANENT_ERROR:
-      if (response.status_code == 503) {
-        next_state_ = STATE_CTRL_WRITE_USER;
-      } else {
-        response_.needs_auth = true;
-        return Stop(ERR_FAILED);
-      }
-      break;
+      response_.needs_auth = true;
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     default:
       NOTREACHED();
       return Stop(ERR_UNEXPECTED);
@@ -699,7 +802,7 @@
     case ERROR_CLASS_INFO_NEEDED:
       return Stop(ERR_INVALID_RESPONSE);
     case ERROR_CLASS_TRANSIENT_ERROR:
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_PERMANENT_ERROR:
       // Server does not recognize the SYST command so proceed.
       next_state_ = STATE_CTRL_WRITE_PWD;
@@ -737,7 +840,7 @@
       }
       if (system_type_ == SYSTEM_TYPE_VMS)
         line = FtpUtil::VMSPathToUnix(line);
-      if (line[line.length() - 1] == '/')
+      if (line.length() && line[line.length() - 1] == '/')
         line.erase(line.length() - 1);
       current_remote_directory_ = line;
       next_state_ = STATE_CTRL_WRITE_TYPE;
@@ -746,9 +849,9 @@
     case ERROR_CLASS_INFO_NEEDED:
       return Stop(ERR_INVALID_RESPONSE);
     case ERROR_CLASS_TRANSIENT_ERROR:
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_PERMANENT_ERROR:
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     default:
       NOTREACHED();
       return Stop(ERR_UNEXPECTED);
@@ -758,7 +861,15 @@
 
 // TYPE command.
 int FtpNetworkTransaction::DoCtrlWriteTYPE() {
-  std::string command = "TYPE I";
+  std::string command = "TYPE ";
+  if (data_type_ == DATA_TYPE_ASCII) {
+    command += "A";
+  } else if (data_type_ == DATA_TYPE_IMAGE) {
+    command += "I";
+  } else {
+    NOTREACHED();
+    return Stop(ERR_UNEXPECTED);
+  }
   next_state_ = STATE_CTRL_READ;
   return SendFtpCommand(command, COMMAND_TYPE);
 }
@@ -769,14 +880,14 @@
     case ERROR_CLASS_INITIATED:
       return Stop(ERR_INVALID_RESPONSE);
     case ERROR_CLASS_OK:
-      next_state_ = STATE_CTRL_WRITE_PASV;
+      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
       break;
     case ERROR_CLASS_INFO_NEEDED:
       return Stop(ERR_INVALID_RESPONSE);
     case ERROR_CLASS_TRANSIENT_ERROR:
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_PERMANENT_ERROR:
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     default:
       NOTREACHED();
       return Stop(ERR_UNEXPECTED);
@@ -784,27 +895,33 @@
   return OK;
 }
 
-// ACCT command.
-int FtpNetworkTransaction::DoCtrlWriteACCT() {
-  std::string command = "ACCT noaccount";
+// EPSV command
+int FtpNetworkTransaction::DoCtrlWriteEPSV() {
+  const std::string command = "EPSV";
   next_state_ = STATE_CTRL_READ;
-  return SendFtpCommand(command, COMMAND_ACCT);
+  return SendFtpCommand(command, COMMAND_EPSV);
 }
 
-int FtpNetworkTransaction::ProcessResponseACCT(
+int FtpNetworkTransaction::ProcessResponseEPSV(
     const FtpCtrlResponse& response) {
   switch (GetErrorClass(response.status_code)) {
     case ERROR_CLASS_INITIATED:
       return Stop(ERR_INVALID_RESPONSE);
     case ERROR_CLASS_OK:
-      next_state_ = STATE_CTRL_WRITE_SYST;
+      if (!ExtractPortFromEPSVResponse( response, &data_connection_port_))
+        return Stop(ERR_INVALID_RESPONSE);
+      if (data_connection_port_ < 1024 ||
+          !IsPortAllowedByFtp(data_connection_port_))
+        return Stop(ERR_UNSAFE_PORT);
+      next_state_ = STATE_DATA_CONNECT;
       break;
     case ERROR_CLASS_INFO_NEEDED:
       return Stop(ERR_INVALID_RESPONSE);
     case ERROR_CLASS_TRANSIENT_ERROR:
-      return Stop(ERR_FAILED);
     case ERROR_CLASS_PERMANENT_ERROR:
-      return Stop(ERR_FAILED);
+      use_epsv_ = false;
+      next_state_ = STATE_CTRL_WRITE_PASV;
+      return OK;
     default:
       NOTREACHED();
       return Stop(ERR_UNEXPECTED);
@@ -819,54 +936,25 @@
   return SendFtpCommand(command, COMMAND_PASV);
 }
 
-// There are two way we can receive IP address and port.
-// TODO(phajdan.jr): Figure out how this should work for IPv6.
-// (127,0,0,1,23,21) IP address and port encapsulated in ().
-// 127,0,0,1,23,21  IP address and port without ().
 int FtpNetworkTransaction::ProcessResponsePASV(
     const FtpCtrlResponse& response) {
   switch (GetErrorClass(response.status_code)) {
     case ERROR_CLASS_INITIATED:
       return Stop(ERR_INVALID_RESPONSE);
     case ERROR_CLASS_OK:
-      const char* ptr;
-      int i0, i1, i2, i3, p0, p1;
-      if (response.lines.size() != 1)
+      if (!ExtractPortFromPASVResponse(response, &data_connection_port_))
         return Stop(ERR_INVALID_RESPONSE);
-      ptr = response.lines[0].c_str();  // Try with bracket.
-      while (*ptr && *ptr != '(')
-        ++ptr;
-      if (*ptr) {
-        ++ptr;
-      } else {
-        ptr = response.lines[0].c_str();  // Try without bracket.
-        while (*ptr && *ptr != ',')
-          ++ptr;
-        while (*ptr && *ptr != ' ')
-          --ptr;
-      }
-      if (sscanf_s(ptr, "%d,%d,%d,%d,%d,%d",
-                   &i0, &i1, &i2, &i3, &p0, &p1) == 6) {
-        // Ignore the IP address supplied in the response. We are always going
-        // to connect back to the same server to prevent FTP PASV port scanning.
-
-        data_connection_port_ = (p0 << 8) + p1;
-
-        if (data_connection_port_ < 1024 ||
-            !IsPortAllowedByFtp(data_connection_port_))
-          return Stop(ERR_UNSAFE_PORT);
-
-        next_state_ = STATE_DATA_CONNECT;
-      } else {
-        return Stop(ERR_INVALID_RESPONSE);
-      }
+      if (data_connection_port_ < 1024 ||
+          !IsPortAllowedByFtp(data_connection_port_))
+        return Stop(ERR_UNSAFE_PORT);
+      next_state_ = STATE_DATA_CONNECT;
       break;
     case ERROR_CLASS_INFO_NEEDED:
       return Stop(ERR_INVALID_RESPONSE);
     case ERROR_CLASS_TRANSIENT_ERROR:
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_PERMANENT_ERROR:
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     default:
       NOTREACHED();
       return Stop(ERR_UNEXPECTED);
@@ -889,22 +977,36 @@
     case ERROR_CLASS_OK:
       if (response.lines.size() != 1)
         return Stop(ERR_INVALID_RESPONSE);
-      if (!StringToInt(response.lines[0], &file_data_len_))
+      int64 size;
+      if (!StringToInt64(response.lines[0], &size))
         return Stop(ERR_INVALID_RESPONSE);
-      if (file_data_len_ < 0)
+      if (size < 0)
         return Stop(ERR_INVALID_RESPONSE);
+      response_.expected_content_size = size;
       break;
     case ERROR_CLASS_INFO_NEEDED:
       break;
     case ERROR_CLASS_TRANSIENT_ERROR:
       break;
     case ERROR_CLASS_PERMANENT_ERROR:
+      // It's possible that SIZE failed because the path is a directory.
+      if (response.status_code == 550 &&
+          resource_type_ == RESOURCE_TYPE_UNKNOWN) {
+        resource_type_ = RESOURCE_TYPE_DIRECTORY;
+      } else if (resource_type_ != RESOURCE_TYPE_DIRECTORY) {
+        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
+      }
       break;
     default:
       NOTREACHED();
       return Stop(ERR_UNEXPECTED);
   }
-  next_state_ = STATE_CTRL_WRITE_MDTM;
+
+  if (resource_type_ == RESOURCE_TYPE_DIRECTORY)
+    next_state_ = STATE_CTRL_WRITE_CWD;
+  else
+    next_state_ = STATE_CTRL_WRITE_RETR;
+
   return OK;
 }
 
@@ -928,27 +1030,22 @@
       next_state_ = STATE_CTRL_WRITE_QUIT;
       break;
     case ERROR_CLASS_INFO_NEEDED:
-      next_state_ = STATE_CTRL_WRITE_PASV;
-      break;
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_TRANSIENT_ERROR:
-      if (response.status_code == 421 || response.status_code == 425 ||
-          response.status_code == 426)
-        return Stop(ERR_FAILED);
-      return ERR_FAILED;  // TODO(ibrar): Retry here.
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_PERMANENT_ERROR:
       // Code 550 means "Failed to open file". Other codes are unrelated,
       // like "Not logged in" etc.
       if (response.status_code != 550)
-        return Stop(ERR_FAILED);
-
-      DCHECK(!retr_failed_);  // Should not get here twice.
-      retr_failed_ = true;
+        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
 
       // It's possible that RETR failed because the path is a directory.
+      resource_type_ = RESOURCE_TYPE_DIRECTORY;
+
       // We're going to try CWD next, but first send a PASV one more time,
       // because some FTP servers, including FileZilla, require that.
       // See http://crbug.com/25316.
-      next_state_ = STATE_CTRL_WRITE_PASV;
+      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
       break;
     default:
       NOTREACHED();
@@ -957,36 +1054,6 @@
   return OK;
 }
 
-// MDMT command
-int FtpNetworkTransaction::DoCtrlWriteMDTM() {
-  std::string command = "MDTM " + GetRequestPathForFtpCommand(false);
-  next_state_ = STATE_CTRL_READ;
-  return SendFtpCommand(command, COMMAND_MDTM);
-}
-
-int FtpNetworkTransaction::ProcessResponseMDTM(
-    const FtpCtrlResponse& response) {
-  switch (GetErrorClass(response.status_code)) {
-    case ERROR_CLASS_INITIATED:
-      return Stop(ERR_FAILED);
-    case ERROR_CLASS_OK:
-      next_state_ = STATE_CTRL_WRITE_RETR;
-      break;
-    case ERROR_CLASS_INFO_NEEDED:
-      return Stop(ERR_FAILED);
-    case ERROR_CLASS_TRANSIENT_ERROR:
-      return Stop(ERR_FAILED);
-    case ERROR_CLASS_PERMANENT_ERROR:
-      next_state_ = STATE_CTRL_WRITE_RETR;
-      break;
-    default:
-      NOTREACHED();
-      return Stop(ERR_UNEXPECTED);
-  }
-  return OK;
-}
-
-
 // CWD command
 int FtpNetworkTransaction::DoCtrlWriteCWD() {
   std::string command = "CWD " + GetRequestPathForFtpCommand(true);
@@ -1004,16 +1071,16 @@
     case ERROR_CLASS_INFO_NEEDED:
       return Stop(ERR_INVALID_RESPONSE);
     case ERROR_CLASS_TRANSIENT_ERROR:
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_PERMANENT_ERROR:
-      if (retr_failed_ && response.status_code == 550) {
-        // Both RETR and CWD failed with codes 550. That means that the path
-        // we're trying to access is not a file, and not a directory. The most
-        // probable interpretation is that it doesn't exist (with FTP we can't
-        // be sure).
+      if (resource_type_ == RESOURCE_TYPE_DIRECTORY &&
+          response.status_code == 550) {
+        // We're assuming that the resource is a directory, but the server says
+        // it's not true. The most probable interpretation is that it doesn't
+        // exist (with FTP we can't be sure).
         return Stop(ERR_FILE_NOT_FOUND);
       }
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     default:
       NOTREACHED();
       return Stop(ERR_UNEXPECTED);
@@ -1031,8 +1098,11 @@
     const FtpCtrlResponse& response) {
   switch (GetErrorClass(response.status_code)) {
     case ERROR_CLASS_INITIATED:
+      // We want the client to start reading the response at this point.
+      // It got here either through Start or RestartWithAuth. We want that
+      // method to complete. Not setting next state here will make DoLoop exit
+      // and in turn make Start/RestartWithAuth complete.
       response_.is_directory_listing = true;
-      next_state_ = STATE_CTRL_READ;
       break;
     case ERROR_CLASS_OK:
       response_.is_directory_listing = true;
@@ -1064,8 +1134,11 @@
     const FtpCtrlResponse& response) {
   switch (GetErrorClass(response.status_code)) {
     case ERROR_CLASS_INITIATED:
+      // We want the client to start reading the response at this point.
+      // It got here either through Start or RestartWithAuth. We want that
+      // method to complete. Not setting next state here will make DoLoop exit
+      // and in turn make Start/RestartWithAuth complete.
       response_.is_directory_listing = true;
-      next_state_ = STATE_CTRL_READ;
       break;
     case ERROR_CLASS_OK:
       response_.is_directory_listing = true;
@@ -1074,9 +1147,9 @@
     case ERROR_CLASS_INFO_NEEDED:
       return Stop(ERR_INVALID_RESPONSE);
     case ERROR_CLASS_TRANSIENT_ERROR:
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     case ERROR_CLASS_PERMANENT_ERROR:
-      return Stop(ERR_FAILED);
+      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
     default:
       NOTREACHED();
       return Stop(ERR_UNEXPECTED);
@@ -1101,24 +1174,21 @@
 
 int FtpNetworkTransaction::DoDataConnect() {
   next_state_ = STATE_DATA_CONNECT_COMPLETE;
-  AddressList data_addresses;
-  // TODO(phajdan.jr): Use exactly same IP address as the control socket.
-  // If the DNS name resolves to several different IPs, and they are different
-  // physical servers, this will break. However, that configuration is very rare
-  // in practice.
-  data_addresses.Copy(addresses_.head());
-  data_addresses.SetPort(data_connection_port_);
-  data_socket_.reset(socket_factory_->CreateTCPClientSocket(data_addresses));
-  return data_socket_->Connect(&io_callback_, load_log_);
+  AddressList data_address;
+  // Connect to the same host as the control socket to prevent PASV port
+  // scanning attacks.
+  int rv = ctrl_socket_->GetPeerAddress(&data_address);
+  if (rv != OK)
+    return Stop(rv);
+  data_address.SetPort(data_connection_port_);
+  data_socket_.reset(socket_factory_->CreateTCPClientSocket(
+        data_address, net_log_.net_log()));
+  return data_socket_->Connect(&io_callback_);
 }
 
 int FtpNetworkTransaction::DoDataConnectComplete(int result) {
   RecordDataConnectionError(result);
-  if (retr_failed_) {
-    next_state_ = STATE_CTRL_WRITE_CWD;
-  } else {
-    next_state_ = STATE_CTRL_WRITE_SIZE;
-  }
+  next_state_ = STATE_CTRL_WRITE_SIZE;
   return OK;
 }
 
@@ -1132,7 +1202,13 @@
     // to be closed on our side too.
     data_socket_.reset();
 
-    // No more data so send QUIT Command now and wait for response.
+    if (ctrl_socket_->IsConnected()) {
+      // Wait for the server's response, we should get it before sending QUIT.
+      next_state_ = STATE_CTRL_READ;
+      return OK;
+    }
+
+    // We are no longer connected to the server, so just finish the transaction.
     return Stop(OK);
   }
 
diff --git a/net/ftp/ftp_network_transaction.h b/net/ftp/ftp_network_transaction.h
index e8140f4..31c38c7 100644
--- a/net/ftp/ftp_network_transaction.h
+++ b/net/ftp/ftp_network_transaction.h
@@ -1,6 +1,6 @@
-// Copyright (c) 2008 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.
+// Copyright (c) 2010 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_FTP_FTP_NETWORK_TRANSACTION_H_
 #define NET_FTP_FTP_NETWORK_TRANSACTION_H_
@@ -14,6 +14,7 @@
 #include "base/scoped_ptr.h"
 #include "net/base/address_list.h"
 #include "net/base/host_resolver.h"
+#include "net/base/net_log.h"
 #include "net/ftp/ftp_ctrl_response_buffer.h"
 #include "net/ftp/ftp_response_info.h"
 #include "net/ftp/ftp_transaction.h"
@@ -33,7 +34,7 @@
   // FtpTransaction methods:
   virtual int Start(const FtpRequestInfo* request_info,
                     CompletionCallback* callback,
-                    LoadLog* load_log);
+                    const BoundNetLog& net_log);
   virtual int Stop(int error);
   virtual int RestartWithAuth(const std::wstring& username,
                               const std::wstring& password,
@@ -49,9 +50,9 @@
     COMMAND_NONE,
     COMMAND_USER,
     COMMAND_PASS,
-    COMMAND_ACCT,
     COMMAND_SYST,
     COMMAND_TYPE,
+    COMMAND_EPSV,
     COMMAND_PASV,
     COMMAND_PWD,
     COMMAND_SIZE,
@@ -59,31 +60,7 @@
     COMMAND_CWD,
     COMMAND_MLSD,
     COMMAND_LIST,
-    COMMAND_MDTM,
-    COMMAND_QUIT
-  };
-
-  enum ErrorClass {
-    // The requested action was initiated. The client should expect another
-    // reply before issuing the next command.
-    ERROR_CLASS_INITIATED,
-
-    // The requested action has been successfully completed.
-    ERROR_CLASS_OK,
-
-    // The command has been accepted, but to complete the operation, more
-    // information must be sent by the client.
-    ERROR_CLASS_INFO_NEEDED,
-
-    // The command was not accepted and the requested action did not take place.
-    // This condition is temporary, and the client is encouraged to restart the
-    // command sequence.
-    ERROR_CLASS_TRANSIENT_ERROR,
-
-    // The command was not accepted and the requested action did not take place.
-    // This condition is rather permanent, and the client is discouraged from
-    // repeating the exact request.
-    ERROR_CLASS_PERMANENT_ERROR,
+    COMMAND_QUIT,
   };
 
   // Major categories of remote system types, as returned by SYST command.
@@ -95,6 +72,22 @@
     SYSTEM_TYPE_VMS,
   };
 
+  // Data representation type, see RFC 959 section 3.1.1. Data Types.
+  // We only support the two most popular data types.
+  enum DataType {
+    DATA_TYPE_ASCII,
+    DATA_TYPE_IMAGE,
+  };
+
+  // In FTP we need to issue different commands depending on whether a resource
+  // is a file or directory. If we don't know that, we're going to autodetect
+  // it.
+  enum ResourceType {
+    RESOURCE_TYPE_UNKNOWN,
+    RESOURCE_TYPE_FILE,
+    RESOURCE_TYPE_DIRECTORY,
+  };
+
   // Resets the members of the transaction so it can be restarted.
   void ResetStateForRestart();
 
@@ -107,14 +100,13 @@
 
   int SendFtpCommand(const std::string& command, Command cmd);
 
-  // Return the error class for given response code. You should validate the
-  // code to be in range 100-599.
-  static ErrorClass GetErrorClass(int response_code);
-
   // Returns request path suitable to be included in an FTP command. If the path
   // will be used as a directory, |is_directory| should be true.
   std::string GetRequestPathForFtpCommand(bool is_directory) const;
 
+  // See if the request URL contains a typecode and make us respect it.
+  void DetectTypecode();
+
   // Runs the state transition loop.
   int DoLoop(int result);
 
@@ -122,8 +114,6 @@
   // argument receive the result from the previous state.  If a method returns
   // ERR_IO_PENDING, then the result from OnIOComplete will be passed to the
   // next state method as the result arg.
-  int DoCtrlInit();
-  int DoCtrlInitComplete(int result);
   int DoCtrlResolveHost();
   int DoCtrlResolveHostComplete(int result);
   int DoCtrlConnect();
@@ -136,14 +126,14 @@
   int ProcessResponseUSER(const FtpCtrlResponse& response);
   int DoCtrlWritePASS();
   int ProcessResponsePASS(const FtpCtrlResponse& response);
-  int DoCtrlWriteACCT();
-  int ProcessResponseACCT(const FtpCtrlResponse& response);
   int DoCtrlWriteSYST();
   int ProcessResponseSYST(const FtpCtrlResponse& response);
   int DoCtrlWritePWD();
   int ProcessResponsePWD(const FtpCtrlResponse& response);
   int DoCtrlWriteTYPE();
   int ProcessResponseTYPE(const FtpCtrlResponse& response);
+  int DoCtrlWriteEPSV();
+  int ProcessResponseEPSV(const FtpCtrlResponse& response);
   int DoCtrlWritePASV();
   int ProcessResponsePASV(const FtpCtrlResponse& response);
   int DoCtrlWriteRETR();
@@ -156,8 +146,6 @@
   int ProcessResponseMLSD(const FtpCtrlResponse& response);
   int DoCtrlWriteLIST();
   int ProcessResponseLIST(const FtpCtrlResponse& response);
-  int DoCtrlWriteMDTM();
-  int ProcessResponseMDTM(const FtpCtrlResponse& response);
   int DoCtrlWriteQUIT();
   int ProcessResponseQUIT(const FtpCtrlResponse& response);
 
@@ -175,7 +163,7 @@
 
   scoped_refptr<FtpNetworkSession> session_;
 
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
   const FtpRequestInfo* request_;
   FtpResponseInfo response_;
 
@@ -190,7 +178,6 @@
 
   scoped_refptr<IOBuffer> read_data_buf_;
   int read_data_buf_len_;
-  int file_data_len_;
 
   // Buffer holding the command line to be written to the control socket.
   scoped_refptr<IOBufferWithSize> write_command_buf_;
@@ -203,6 +190,16 @@
 
   SystemType system_type_;
 
+  // Data type to be used for the TYPE command.
+  DataType data_type_;
+
+  // Detected resource type (file or directory).
+  ResourceType resource_type_;
+
+  // Initially we favour EPSV over PASV for transfers but should any
+  // EPSV fail, we fall back to PASV for the duration of connection.
+  bool use_epsv_;
+
   // We get username and password as wstrings in RestartWithAuth, so they are
   // also kept as wstrings here.
   std::wstring username_;
@@ -212,8 +209,6 @@
   // with any trailing slash removed.
   std::string current_remote_directory_;
 
-  bool retr_failed_;
-
   int data_connection_port_;
 
   ClientSocketFactory* socket_factory_;
@@ -223,8 +218,6 @@
 
   enum State {
     // Control connection states:
-    STATE_CTRL_INIT,
-    STATE_CTRL_INIT_COMPLETE,
     STATE_CTRL_RESOLVE_HOST,
     STATE_CTRL_RESOLVE_HOST_COMPLETE,
     STATE_CTRL_CONNECT,
@@ -235,9 +228,9 @@
     STATE_CTRL_WRITE_COMPLETE,
     STATE_CTRL_WRITE_USER,
     STATE_CTRL_WRITE_PASS,
-    STATE_CTRL_WRITE_ACCT,
     STATE_CTRL_WRITE_SYST,
     STATE_CTRL_WRITE_TYPE,
+    STATE_CTRL_WRITE_EPSV,
     STATE_CTRL_WRITE_PASV,
     STATE_CTRL_WRITE_PWD,
     STATE_CTRL_WRITE_RETR,
@@ -245,7 +238,6 @@
     STATE_CTRL_WRITE_CWD,
     STATE_CTRL_WRITE_MLSD,
     STATE_CTRL_WRITE_LIST,
-    STATE_CTRL_WRITE_MDTM,
     STATE_CTRL_WRITE_QUIT,
     // Data connection states:
     STATE_DATA_CONNECT,
diff --git a/net/ftp/ftp_network_transaction_unittest.cc b/net/ftp/ftp_network_transaction_unittest.cc
index d8479aa..b7cc29d 100644
--- a/net/ftp/ftp_network_transaction_unittest.cc
+++ b/net/ftp/ftp_network_transaction_unittest.cc
@@ -36,21 +36,22 @@
     PRE_SYST,
     PRE_PWD,
     PRE_TYPE,
-    PRE_PASV,
     PRE_SIZE,
-    PRE_MDTM,
+    PRE_EPSV,
+    PRE_PASV,
     PRE_MLSD,
     PRE_LIST,
     PRE_RETR,
-    PRE_PASV2,
     PRE_CWD,
     PRE_QUIT,
+    PRE_NOPASV,
     QUIT
   };
 
   FtpSocketDataProvider()
       : failure_injection_state_(NONE),
-        multiline_welcome_(false) {
+        multiline_welcome_(false),
+        data_type_('I') {
     Init();
   }
 
@@ -74,15 +75,14 @@
         return Verify("PWD\r\n", data, PRE_TYPE,
                       "257 \"/\" is your current location\r\n");
       case PRE_TYPE:
-        return Verify("TYPE I\r\n", data, PRE_PASV,
-                      "200 TYPE is now 8-bit binary\r\n");
-      case PRE_PASV:
-        return Verify("PASV\r\n", data, PRE_SIZE,
-                      "227 Entering Passive Mode 127,0,0,1,123,456\r\n");
-      case PRE_PASV2:
-        // Parser should also accept format without parentheses.
-        return Verify("PASV\r\n", data, PRE_CWD,
-                      "227 Entering Passive Mode 127,0,0,1,123,456\r\n");
+        return Verify(std::string("TYPE ") + data_type_ + "\r\n", data,
+                      PRE_EPSV, "200 TYPE set successfully\r\n");
+      case PRE_EPSV:
+        return Verify("EPSV\r\n", data, PRE_SIZE,
+                      "227 Entering Extended Passive Mode (|||31744|)\r\n");
+      case PRE_NOPASV:
+        return Verify("PASV\r\n", data, PRE_QUIT,
+                      "599 fail\r\n");
       case PRE_QUIT:
         return Verify("QUIT\r\n", data, QUIT, "221 Goodbye.\r\n");
       default:
@@ -114,6 +114,10 @@
     multiline_welcome_ = multiline;
   }
 
+  void set_data_type(char data_type) {
+    data_type_ = data_type;
+  }
+
  protected:
   void Init() {
     state_ = PRE_USER;
@@ -133,16 +137,26 @@
   MockWriteResult Verify(const std::string& expected,
                          const std::string& data,
                          State next_state,
-                         const char* next_read) {
+                         const char* next_read,
+                         const size_t next_read_length) {
     EXPECT_EQ(expected, data);
     if (expected == data) {
       state_ = next_state;
-      SimulateRead(next_read);
+      SimulateRead(next_read, next_read_length);
       return MockWriteResult(true, data.length());
     }
     return MockWriteResult(true, ERR_UNEXPECTED);
   }
 
+  MockWriteResult Verify(const std::string& expected,
+                         const std::string& data,
+                         State next_state,
+                         const char* next_read) {
+    return Verify(expected, data, next_state,
+                  next_read, std::strlen(next_read));
+  }
+
+
  private:
   State state_;
   State failure_injection_state_;
@@ -152,6 +166,9 @@
   // If true, we will send multiple 230 lines as response after PASS.
   bool multiline_welcome_;
 
+  // Data type to be used for TYPE command.
+  char data_type_;
+
   DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProvider);
 };
 
@@ -165,15 +182,8 @@
       return MockWriteResult(true, data.length());
     switch (state()) {
       case PRE_SIZE:
-        return Verify("SIZE /\r\n", data, PRE_MDTM,
+        return Verify("SIZE /\r\n", data, PRE_CWD,
                       "550 I can only retrieve regular files\r\n");
-      case PRE_MDTM:
-        return Verify("MDTM /\r\n", data, PRE_RETR,
-                      "213 20070221112533\r\n");
-      case PRE_RETR:
-        return Verify("RETR /\r\n", data, PRE_PASV2,
-                      "550 Can't download directory\r\n");
-
       case PRE_CWD:
         return Verify("CWD /\r\n", data, PRE_MLSD, "200 OK\r\n");
       case PRE_MLSD:
@@ -191,6 +201,32 @@
   DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderDirectoryListing);
 };
 
+class FtpSocketDataProviderDirectoryListingWithPasvFallback
+    : public FtpSocketDataProviderDirectoryListing {
+ public:
+  FtpSocketDataProviderDirectoryListingWithPasvFallback() {
+  }
+
+  virtual MockWriteResult OnWrite(const std::string& data) {
+    if (InjectFault())
+      return MockWriteResult(true, data.length());
+    switch (state()) {
+      case PRE_EPSV:
+        return Verify("EPSV\r\n", data, PRE_PASV,
+                      "500 no EPSV for you\r\n");
+      case PRE_PASV:
+        return Verify("PASV\r\n", data, PRE_SIZE,
+                      "227 Entering Passive Mode 127,0,0,1,123,456\r\n");
+      default:
+        return FtpSocketDataProviderDirectoryListing::OnWrite(data);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(
+      FtpSocketDataProviderDirectoryListingWithPasvFallback);
+};
+
 class FtpSocketDataProviderVMSDirectoryListing : public FtpSocketDataProvider {
  public:
   FtpSocketDataProviderVMSDirectoryListing() {
@@ -205,15 +241,14 @@
       case PRE_PWD:
         return Verify("PWD\r\n", data, PRE_TYPE,
                       "257 \"ANONYMOUS_ROOT:[000000]\"\r\n");
+      case PRE_EPSV:
+        return Verify("EPSV\r\n", data, PRE_PASV, "500 Invalid command\r\n");
+      case PRE_PASV:
+        return Verify("PASV\r\n", data, PRE_SIZE,
+                      "227 Entering Passive Mode 127,0,0,1,123,456\r\n");
       case PRE_SIZE:
-        return Verify("SIZE ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_MDTM,
+        return Verify("SIZE ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_CWD,
                       "550 I can only retrieve regular files\r\n");
-      case PRE_MDTM:
-        return Verify("MDTM ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_RETR,
-                      "213 20070221112533\r\n");
-      case PRE_RETR:
-        return Verify("RETR ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_PASV2,
-                      "550 Can't download directory\r\n");
       case PRE_CWD:
         return Verify("CWD ANONYMOUS_ROOT:[dir]\r\n", data, PRE_MLSD,
                       "200 OK\r\n");
@@ -245,15 +280,15 @@
       case PRE_PWD:
         return Verify("PWD\r\n", data, PRE_TYPE,
                       "257 \"ANONYMOUS_ROOT:[000000]\"\r\n");
+      case PRE_EPSV:
+        return Verify("EPSV\r\n", data, PRE_PASV,
+                      "500 EPSV command unknown\r\n");
+      case PRE_PASV:
+        return Verify("PASV\r\n", data, PRE_SIZE,
+                      "227 Entering Passive Mode 127,0,0,1,123,456\r\n");
       case PRE_SIZE:
-        return Verify("SIZE ANONYMOUS_ROOT\r\n", data, PRE_MDTM,
+        return Verify("SIZE ANONYMOUS_ROOT\r\n", data, PRE_CWD,
                       "550 I can only retrieve regular files\r\n");
-      case PRE_MDTM:
-        return Verify("MDTM ANONYMOUS_ROOT\r\n", data, PRE_RETR,
-                      "213 20070221112533\r\n");
-      case PRE_RETR:
-        return Verify("RETR ANONYMOUS_ROOT\r\n", data, PRE_PASV2,
-                      "550 Can't download directory\r\n");
       case PRE_CWD:
         return Verify("CWD ANONYMOUS_ROOT:[000000]\r\n", data, PRE_MLSD,
                       "200 OK\r\n");
@@ -281,11 +316,8 @@
       return MockWriteResult(true, data.length());
     switch (state()) {
       case PRE_SIZE:
-        return Verify("SIZE /file\r\n", data, PRE_MDTM,
+        return Verify("SIZE /file\r\n", data, PRE_RETR,
                       "213 18\r\n");
-      case PRE_MDTM:
-        return Verify("MDTM /file\r\n", data, PRE_RETR,
-                      "213 20070221112533\r\n");
       case PRE_RETR:
         return Verify("RETR /file\r\n", data, PRE_QUIT, "200 OK\r\n");
       default:
@@ -297,6 +329,31 @@
   DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownload);
 };
 
+class FtpSocketDataProviderFileDownloadWithPasvFallback
+    : public FtpSocketDataProviderFileDownload {
+ public:
+  FtpSocketDataProviderFileDownloadWithPasvFallback() {
+  }
+
+  virtual MockWriteResult OnWrite(const std::string& data) {
+    if (InjectFault())
+      return MockWriteResult(true, data.length());
+    switch (state()) {
+      case PRE_EPSV:
+        return Verify("EPSV\r\n", data, PRE_PASV,
+                      "500 No can do\r\n");
+      case PRE_PASV:
+        return Verify("PASV\r\n", data, PRE_SIZE,
+                      "227 Entering Passive Mode 127,0,0,1,123,456\r\n");
+      default:
+        return FtpSocketDataProviderFileDownload::OnWrite(data);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownloadWithPasvFallback);
+};
+
 class FtpSocketDataProviderVMSFileDownload : public FtpSocketDataProvider {
  public:
   FtpSocketDataProviderVMSFileDownload() {
@@ -311,12 +368,15 @@
       case PRE_PWD:
         return Verify("PWD\r\n", data, PRE_TYPE,
                       "257 \"ANONYMOUS_ROOT:[000000]\"\r\n");
+      case PRE_EPSV:
+        return Verify("EPSV\r\n", data, PRE_PASV,
+                      "500 EPSV command unknown\r\n");
+      case PRE_PASV:
+        return Verify("PASV\r\n", data, PRE_SIZE,
+                      "227 Entering Passive Mode 127,0,0,1,123,456\r\n");
       case PRE_SIZE:
-        return Verify("SIZE ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_MDTM,
+        return Verify("SIZE ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_RETR,
                       "213 18\r\n");
-      case PRE_MDTM:
-        return Verify("MDTM ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_RETR,
-                      "213 20070221112533\r\n");
       case PRE_RETR:
         return Verify("RETR ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_QUIT,
                       "200 OK\r\n");
@@ -329,7 +389,7 @@
   DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderVMSFileDownload);
 };
 
-class FtpSocketDataProviderEscaping : public FtpSocketDataProvider {
+class FtpSocketDataProviderEscaping : public FtpSocketDataProviderFileDownload {
  public:
   FtpSocketDataProviderEscaping() {
   }
@@ -339,44 +399,18 @@
       return MockWriteResult(true, data.length());
     switch (state()) {
       case PRE_SIZE:
-        return Verify("SIZE / !\"#$%y\200\201\r\n", data, PRE_MDTM,
+        return Verify("SIZE / !\"#$%y\200\201\r\n", data, PRE_RETR,
                       "213 18\r\n");
-      case PRE_MDTM:
-        return Verify("MDTM / !\"#$%y\200\201\r\n", data, PRE_RETR,
-                      "213 20070221112533\r\n");
       case PRE_RETR:
         return Verify("RETR / !\"#$%y\200\201\r\n", data, PRE_QUIT,
                       "200 OK\r\n");
       default:
-        return FtpSocketDataProvider::OnWrite(data);
-    }
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEscaping);
-};
-
-class FtpSocketDataProviderFileDownloadAcceptedDataConnection
-    : public FtpSocketDataProviderFileDownload {
- public:
-  FtpSocketDataProviderFileDownloadAcceptedDataConnection() {
-  }
-
-  virtual MockWriteResult OnWrite(const std::string& data) {
-    if (InjectFault())
-      return MockWriteResult(true, data.length());
-    switch (state()) {
-      case PRE_RETR:
-        return Verify("RETR /file\r\n", data, PRE_QUIT,
-                      "150 Accepted Data Connection\r\n");
-      default:
         return FtpSocketDataProviderFileDownload::OnWrite(data);
     }
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(
-      FtpSocketDataProviderFileDownloadAcceptedDataConnection);
+  DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEscaping);
 };
 
 class FtpSocketDataProviderFileDownloadTransferStarting
@@ -440,8 +474,8 @@
     switch (state()) {
       case PRE_SIZE:
         return Verify("SIZE /file\r\n", data, PRE_QUIT,
-                      "500 Evil Response\r\n"
-                      "500 More Evil\r\n");
+                      "599 Evil Response\r\n"
+                      "599 More Evil\r\n");
       default:
         return FtpSocketDataProviderFileDownload::OnWrite(data);
     }
@@ -451,32 +485,46 @@
   DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownloadInvalidResponse);
 };
 
-class FtpSocketDataProviderFileDownloadRetrFail
-    : public FtpSocketDataProviderFileDownload {
+class FtpSocketDataProviderEvilEpsv : public FtpSocketDataProviderFileDownload {
  public:
-  FtpSocketDataProviderFileDownloadRetrFail() {
-  }
+  FtpSocketDataProviderEvilEpsv(const char* epsv_response,
+                                State expected_state)
+      : epsv_response_(epsv_response),
+        epsv_response_length_(std::strlen(epsv_response)),
+        expected_state_(expected_state) {}
+
+  FtpSocketDataProviderEvilEpsv(const char* epsv_response,
+                               size_t epsv_response_length,
+                               State expected_state)
+      : epsv_response_(epsv_response),
+        epsv_response_length_(epsv_response_length),
+        expected_state_(expected_state) {}
 
   virtual MockWriteResult OnWrite(const std::string& data) {
     if (InjectFault())
       return MockWriteResult(true, data.length());
     switch (state()) {
-      case PRE_CWD:
-        return Verify("CWD /file\r\n", data, PRE_QUIT,
-                      "550 file is a directory\r\n");
+      case PRE_EPSV:
+        return Verify("EPSV\r\n", data, expected_state_,
+                      epsv_response_, epsv_response_length_);
       default:
         return FtpSocketDataProviderFileDownload::OnWrite(data);
     }
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderFileDownloadRetrFail);
+  const char* epsv_response_;
+  const size_t epsv_response_length_;
+  const State expected_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEvilEpsv);
 };
 
-class FtpSocketDataProviderEvilPasv : public FtpSocketDataProviderFileDownload {
+class FtpSocketDataProviderEvilPasv
+    : public FtpSocketDataProviderFileDownloadWithPasvFallback {
  public:
   explicit FtpSocketDataProviderEvilPasv(const char* pasv_response,
-                                        State expected_state)
+                                         State expected_state)
       : pasv_response_(pasv_response),
         expected_state_(expected_state) {
   }
@@ -488,7 +536,7 @@
       case PRE_PASV:
         return Verify("PASV\r\n", data, expected_state_, pasv_response_);
       default:
-        return FtpSocketDataProviderFileDownload::OnWrite(data);
+        return FtpSocketDataProviderFileDownloadWithPasvFallback::OnWrite(data);
     }
   }
 
@@ -499,7 +547,33 @@
   DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEvilPasv);
 };
 
-class FtpSocketDataProviderEvilLogin : public FtpSocketDataProviderFileDownload {
+class FtpSocketDataProviderEvilSize : public FtpSocketDataProviderFileDownload {
+ public:
+  FtpSocketDataProviderEvilSize(const char* size_response, State expected_state)
+      : size_response_(size_response),
+        expected_state_(expected_state) {
+  }
+
+  virtual MockWriteResult OnWrite(const std::string& data) {
+    if (InjectFault())
+      return MockWriteResult(true, data.length());
+    switch (state()) {
+      case PRE_SIZE:
+        return Verify("SIZE /file\r\n", data, expected_state_, size_response_);
+      default:
+        return FtpSocketDataProviderFileDownload::OnWrite(data);
+    }
+  }
+
+ private:
+  const char* size_response_;
+  const State expected_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(FtpSocketDataProviderEvilSize);
+};
+
+class FtpSocketDataProviderEvilLogin
+    : public FtpSocketDataProviderFileDownload {
  public:
   FtpSocketDataProviderEvilLogin(const char* expected_user,
                                 const char* expected_password)
@@ -570,31 +644,42 @@
                           int expected_result) {
     std::string mock_data("mock-data");
     MockRead data_reads[] = {
+      // Usually FTP servers close the data connection after the entire data has
+      // been received.
+      MockRead(false, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
       MockRead(mock_data.c_str()),
     };
     // For compatibility with FileZilla, the transaction code will use two data
     // sockets for directory requests. For more info see http://crbug.com/25316.
-    StaticSocketDataProvider data1(data_reads, NULL);
-    StaticSocketDataProvider data2(data_reads, NULL);
+    StaticSocketDataProvider data1(data_reads, arraysize(data_reads), NULL, 0);
+    StaticSocketDataProvider data2(data_reads, arraysize(data_reads), NULL, 0);
     mock_socket_factory_.AddSocketDataProvider(ctrl_socket);
     mock_socket_factory_.AddSocketDataProvider(&data1);
     mock_socket_factory_.AddSocketDataProvider(&data2);
     FtpRequestInfo request_info = GetRequestInfo(request);
     EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
     ASSERT_EQ(ERR_IO_PENDING,
-              transaction_.Start(&request_info, &callback_, NULL));
+              transaction_.Start(&request_info, &callback_, BoundNetLog()));
     EXPECT_NE(LOAD_STATE_IDLE, transaction_.GetLoadState());
-    EXPECT_EQ(expected_result, callback_.WaitForResult());
-    EXPECT_EQ(FtpSocketDataProvider::QUIT, ctrl_socket->state());
+    ASSERT_EQ(expected_result, callback_.WaitForResult());
     if (expected_result == OK) {
       scoped_refptr<IOBuffer> io_buffer(new IOBuffer(kBufferSize));
       memset(io_buffer->data(), 0, kBufferSize);
       ASSERT_EQ(ERR_IO_PENDING,
                 transaction_.Read(io_buffer.get(), kBufferSize, &callback_));
-      EXPECT_EQ(static_cast<int>(mock_data.length()),
+      ASSERT_EQ(static_cast<int>(mock_data.length()),
                 callback_.WaitForResult());
       EXPECT_EQ(mock_data, std::string(io_buffer->data(), mock_data.length()));
+
+      // Do another Read to detect that the data socket is now closed.
+      int rv = transaction_.Read(io_buffer.get(), kBufferSize, &callback_);
+      if (rv == ERR_IO_PENDING) {
+        EXPECT_EQ(0, callback_.WaitForResult());
+      } else {
+        EXPECT_EQ(0, rv);
+      }
     }
+    EXPECT_EQ(FtpSocketDataProvider::QUIT, ctrl_socket->state());
     EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
   }
 
@@ -620,14 +705,48 @@
   host_resolver_->rules()->AddSimulatedFailure("badhost");
   EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
   ASSERT_EQ(ERR_IO_PENDING,
-            transaction_.Start(&request_info, &callback_, NULL));
-  EXPECT_EQ(ERR_NAME_NOT_RESOLVED, callback_.WaitForResult());
+            transaction_.Start(&request_info, &callback_, BoundNetLog()));
+  ASSERT_EQ(ERR_NAME_NOT_RESOLVED, callback_.WaitForResult());
   EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
 }
 
+// Check that when determining the host, the square brackets decorating IPv6
+// literals in URLs are stripped.
+TEST_F(FtpNetworkTransactionTest, StripBracketsFromIPv6Literals) {
+  host_resolver_->rules()->AddSimulatedFailure("[::1]");
+
+  // We start a transaction that is expected to fail with ERR_INVALID_RESPONSE.
+  // The important part of this test is to make sure that we don't fail with
+  // ERR_NAME_NOT_RESOLVED, since that would mean the decorated hostname
+  // was used.
+  FtpSocketDataProviderEvilSize ctrl_socket(
+      "213 99999999999999999999999999999999\r\n",
+      FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://[::1]/file", ERR_INVALID_RESPONSE);
+}
+
 TEST_F(FtpNetworkTransactionTest, DirectoryTransaction) {
   FtpSocketDataProviderDirectoryListing ctrl_socket;
   ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
+
+  EXPECT_TRUE(transaction_.GetResponseInfo()->is_directory_listing);
+  EXPECT_EQ(-1, transaction_.GetResponseInfo()->expected_content_size);
+}
+
+TEST_F(FtpNetworkTransactionTest, DirectoryTransactionWithPasvFallback) {
+  FtpSocketDataProviderDirectoryListingWithPasvFallback ctrl_socket;
+  ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
+
+  EXPECT_TRUE(transaction_.GetResponseInfo()->is_directory_listing);
+  EXPECT_EQ(-1, transaction_.GetResponseInfo()->expected_content_size);
+}
+
+TEST_F(FtpNetworkTransactionTest, DirectoryTransactionWithTypecode) {
+  FtpSocketDataProviderDirectoryListing ctrl_socket;
+  ExecuteTransaction(&ctrl_socket, "ftp://host;type=d", OK);
+
+  EXPECT_TRUE(transaction_.GetResponseInfo()->is_directory_listing);
+  EXPECT_EQ(-1, transaction_.GetResponseInfo()->expected_content_size);
 }
 
 TEST_F(FtpNetworkTransactionTest, DirectoryTransactionMultilineWelcome) {
@@ -676,6 +795,34 @@
 TEST_F(FtpNetworkTransactionTest, DownloadTransaction) {
   FtpSocketDataProviderFileDownload ctrl_socket;
   ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
+
+  // We pass an artificial value of 18 as a response to the SIZE command.
+  EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionWithPasvFallback) {
+  FtpSocketDataProviderFileDownloadWithPasvFallback ctrl_socket;
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
+
+  // We pass an artificial value of 18 as a response to the SIZE command.
+  EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeA) {
+  FtpSocketDataProviderFileDownload ctrl_socket;
+  ctrl_socket.set_data_type('A');
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file;type=a", OK);
+
+  // We pass an artificial value of 18 as a response to the SIZE command.
+  EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionWithTypecodeI) {
+  FtpSocketDataProviderFileDownload ctrl_socket;
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file;type=i", OK);
+
+  // We pass an artificial value of 18 as a response to the SIZE command.
+  EXPECT_EQ(18, transaction_.GetResponseInfo()->expected_content_size);
 }
 
 TEST_F(FtpNetworkTransactionTest, DownloadTransactionMultilineWelcome) {
@@ -701,52 +848,6 @@
   ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
 }
 
-TEST_F(FtpNetworkTransactionTest, DownloadTransactionAcceptedDataConnection) {
-  FtpSocketDataProviderFileDownloadAcceptedDataConnection ctrl_socket;
-  std::string mock_data("mock-data");
-  MockRead data_reads[] = {
-    MockRead(mock_data.c_str()),
-  };
-  StaticSocketDataProvider data_socket1(data_reads, NULL);
-  mock_socket_factory_.AddSocketDataProvider(&ctrl_socket);
-  mock_socket_factory_.AddSocketDataProvider(&data_socket1);
-  FtpRequestInfo request_info = GetRequestInfo("ftp://host/file");
-
-  // Start the transaction.
-  ASSERT_EQ(ERR_IO_PENDING,
-            transaction_.Start(&request_info, &callback_, NULL));
-  EXPECT_EQ(OK, callback_.WaitForResult());
-
-  // The transaction fires the callback when we can start reading data.
-  EXPECT_EQ(FtpSocketDataProvider::PRE_QUIT, ctrl_socket.state());
-  EXPECT_EQ(LOAD_STATE_SENDING_REQUEST, transaction_.GetLoadState());
-  scoped_refptr<IOBuffer> io_buffer(new IOBuffer(kBufferSize));
-  memset(io_buffer->data(), 0, kBufferSize);
-  ASSERT_EQ(ERR_IO_PENDING,
-            transaction_.Read(io_buffer.get(), kBufferSize, &callback_));
-  EXPECT_EQ(LOAD_STATE_READING_RESPONSE, transaction_.GetLoadState());
-  EXPECT_EQ(static_cast<int>(mock_data.length()),
-            callback_.WaitForResult());
-  EXPECT_EQ(LOAD_STATE_READING_RESPONSE, transaction_.GetLoadState());
-  EXPECT_EQ(mock_data, std::string(io_buffer->data(), mock_data.length()));
-
-  // FTP server should disconnect the data socket. It is also a signal for the
-  // FtpNetworkTransaction that the data transfer is finished.
-  ClientSocket* data_socket = mock_socket_factory_.GetMockTCPClientSocket(1);
-  ASSERT_TRUE(data_socket);
-  data_socket->Disconnect();
-
-  // We should issue Reads until one returns EOF...
-  ASSERT_EQ(ERR_IO_PENDING,
-            transaction_.Read(io_buffer.get(), kBufferSize, &callback_));
-
-  // Make sure the transaction finishes cleanly.
-  EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
-  EXPECT_EQ(OK, callback_.WaitForResult());
-  EXPECT_EQ(FtpSocketDataProvider::QUIT, ctrl_socket.state());
-  EXPECT_EQ(LOAD_STATE_IDLE, transaction_.GetLoadState());
-}
-
 TEST_F(FtpNetworkTransactionTest, DownloadTransactionTransferStarting) {
   FtpSocketDataProviderFileDownloadTransferStarting ctrl_socket;
   ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
@@ -757,6 +858,12 @@
   ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE);
 }
 
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilPasvReallyBadFormat) {
+  FtpSocketDataProviderEvilPasv ctrl_socket("227 Portscan (127,0,0,\r\n",
+                                           FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE);
+}
+
 TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilPasvUnsafePort1) {
   FtpSocketDataProviderEvilPasv ctrl_socket("227 Portscan (127,0,0,1,0,22)\r\n",
                                            FtpSocketDataProvider::PRE_QUIT);
@@ -791,15 +898,16 @@
   MockRead data_reads[] = {
     MockRead(mock_data.c_str()),
   };
-  StaticSocketDataProvider data_socket1(data_reads, NULL);
+  StaticSocketDataProvider data_socket1(data_reads, arraysize(data_reads),
+                                        NULL, 0);
   mock_socket_factory_.AddSocketDataProvider(&ctrl_socket);
   mock_socket_factory_.AddSocketDataProvider(&data_socket1);
   FtpRequestInfo request_info = GetRequestInfo("ftp://host/file");
 
   // Start the transaction.
   ASSERT_EQ(ERR_IO_PENDING,
-            transaction_.Start(&request_info, &callback_, NULL));
-  EXPECT_EQ(OK, callback_.WaitForResult());
+            transaction_.Start(&request_info, &callback_, BoundNetLog()));
+  ASSERT_EQ(OK, callback_.WaitForResult());
 
   // The transaction fires the callback when we can start reading data. That
   // means that the data socket should be open.
@@ -809,11 +917,87 @@
   ASSERT_TRUE(data_socket->IsConnected());
 
   // Even if the PASV response specified some other address, we connect
-  // to the address we used for control connection.
-  EXPECT_EQ("127.0.0.1", NetAddressToString(data_socket->addresses().head()));
+  // to the address we used for control connection (which could be 127.0.0.1
+  // or ::1 depending on whether we use IPv6).
+  const struct addrinfo* addrinfo = data_socket->addresses().head();
+  while (addrinfo) {
+    EXPECT_NE("1.2.3.4", NetAddressToString(addrinfo));
+    addrinfo = addrinfo->ai_next;
+  }
+}
 
-  // Make sure we have only one host entry in the AddressList.
-  EXPECT_FALSE(data_socket->addresses().head()->ai_next);
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvReallyBadFormat1) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||22)\r\n",
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvReallyBadFormat2) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (||\r\n",
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvReallyBadFormat3) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan\r\n",
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvReallyBadFormat4) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (||||)\r\n",
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvReallyBadFormat5) {
+  const char response[] = "227 Portscan (\0\0\031773\0)\r\n";
+  FtpSocketDataProviderEvilEpsv ctrl_socket(response, sizeof(response)-1,
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvUnsafePort1) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||22|)\r\n",
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvUnsafePort2) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||258|)\r\n",
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvUnsafePort3) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||772|)\r\n",
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvUnsafePort4) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|||2049|)\r\n",
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvWeirdSep) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan ($$$31744$)\r\n",
+                                            FtpSocketDataProvider::PRE_SIZE);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
+}
+
+TEST_F(FtpNetworkTransactionTest,
+       DownloadTransactionEvilEpsvWeirdSepUnsafePort) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan ($$$317$)\r\n",
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_UNSAFE_PORT);
+}
+
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilEpsvIllegalHost) {
+  FtpSocketDataProviderEvilEpsv ctrl_socket("227 Portscan (|2|::1|31744|)\r\n",
+                                            FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE);
 }
 
 TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilLoginBadUsername) {
@@ -846,8 +1030,8 @@
   FtpRequestInfo request_info = GetRequestInfo("ftp://host/file");
 
   ASSERT_EQ(ERR_IO_PENDING,
-            transaction_.Start(&request_info, &callback_, NULL));
-  EXPECT_EQ(ERR_FAILED, callback_.WaitForResult());
+            transaction_.Start(&request_info, &callback_, BoundNetLog()));
+  ASSERT_EQ(ERR_FTP_FAILED, callback_.WaitForResult());
 
   MockRead ctrl_reads[] = {
     MockRead("220 host TestFTPd\r\n"),
@@ -857,7 +1041,8 @@
   MockWrite ctrl_writes[] = {
     MockWrite("QUIT\r\n"),
   };
-  StaticSocketDataProvider ctrl_socket2(ctrl_reads, ctrl_writes);
+  StaticSocketDataProvider ctrl_socket2(ctrl_reads, arraysize(ctrl_reads),
+                                        ctrl_writes, arraysize(ctrl_writes));
   mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2);
   ASSERT_EQ(ERR_IO_PENDING, transaction_.RestartWithAuth(L"foo\nownz0red",
                                                          L"innocent",
@@ -875,8 +1060,8 @@
   FtpRequestInfo request_info = GetRequestInfo("ftp://host/file");
 
   ASSERT_EQ(ERR_IO_PENDING,
-            transaction_.Start(&request_info, &callback_, NULL));
-  EXPECT_EQ(ERR_FAILED, callback_.WaitForResult());
+            transaction_.Start(&request_info, &callback_, BoundNetLog()));
+  ASSERT_EQ(ERR_FTP_FAILED, callback_.WaitForResult());
 
   MockRead ctrl_reads[] = {
     MockRead("220 host TestFTPd\r\n"),
@@ -888,7 +1073,8 @@
     MockWrite("USER innocent\r\n"),
     MockWrite("QUIT\r\n"),
   };
-  StaticSocketDataProvider ctrl_socket2(ctrl_reads, ctrl_writes);
+  StaticSocketDataProvider ctrl_socket2(ctrl_reads, arraysize(ctrl_reads),
+                                        ctrl_writes, arraysize(ctrl_writes));
   mock_socket_factory_.AddSocketDataProvider(&ctrl_socket2);
   ASSERT_EQ(ERR_IO_PENDING, transaction_.RestartWithAuth(L"innocent",
                                                          L"foo\nownz0red",
@@ -902,6 +1088,26 @@
                      OK);
 }
 
+// Test for http://crbug.com/23794.
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionEvilSize) {
+  // Try to overflow int64 in the response.
+  FtpSocketDataProviderEvilSize ctrl_socket(
+      "213 99999999999999999999999999999999\r\n",
+      FtpSocketDataProvider::PRE_QUIT);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", ERR_INVALID_RESPONSE);
+}
+
+// Test for http://crbug.com/36360.
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionBigSize) {
+  // Pass a valid, but large file size. The transaction should not fail.
+  FtpSocketDataProviderEvilSize ctrl_socket(
+      "213 3204427776\r\n",
+      FtpSocketDataProvider::PRE_RETR);
+  ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
+  EXPECT_EQ(3204427776LL,
+            transaction_.GetResponseInfo()->expected_content_size);
+}
+
 // Regression test for http://crbug.com/25023.
 TEST_F(FtpNetworkTransactionTest, CloseConnection) {
   FtpSocketDataProviderCloseConnection ctrl_socket;
@@ -914,8 +1120,8 @@
                         "ftp://host",
                         FtpSocketDataProvider::PRE_USER,
                         FtpSocketDataProvider::PRE_QUIT,
-                        "500 no such user\r\n",
-                        ERR_FAILED);
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
 TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPass) {
@@ -925,7 +1131,18 @@
                         FtpSocketDataProvider::PRE_PASSWD,
                         FtpSocketDataProvider::PRE_QUIT,
                         "530 Login authentication failed\r\n",
-                        ERR_FAILED);
+                        ERR_FTP_FAILED);
+}
+
+// Regression test for http://crbug.com/38707.
+TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPass503) {
+  FtpSocketDataProviderDirectoryListing ctrl_socket;
+  TransactionFailHelper(&ctrl_socket,
+                        "ftp://host",
+                        FtpSocketDataProvider::PRE_PASSWD,
+                        FtpSocketDataProvider::PRE_QUIT,
+                        "503 Bad sequence of commands\r\n",
+                        ERR_FTP_BAD_COMMAND_SEQUENCE);
 }
 
 TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailSyst) {
@@ -934,7 +1151,7 @@
                         "ftp://host",
                         FtpSocketDataProvider::PRE_SYST,
                         FtpSocketDataProvider::PRE_PWD,
-                        "500 failed syst\r\n",
+                        "599 fail\r\n",
                         OK);
 }
 
@@ -944,8 +1161,8 @@
                         "ftp://host",
                         FtpSocketDataProvider::PRE_PWD,
                         FtpSocketDataProvider::PRE_QUIT,
-                        "500 failed pwd\r\n",
-                        ERR_FAILED);
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
 TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailType) {
@@ -954,48 +1171,18 @@
                         "ftp://host",
                         FtpSocketDataProvider::PRE_TYPE,
                         FtpSocketDataProvider::PRE_QUIT,
-                        "500 failed type\r\n",
-                        ERR_FAILED);
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
-TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPasv) {
+TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailEpsv) {
   FtpSocketDataProviderDirectoryListing ctrl_socket;
   TransactionFailHelper(&ctrl_socket,
                         "ftp://host",
-                        FtpSocketDataProvider::PRE_PASV,
-                        FtpSocketDataProvider::PRE_QUIT,
-                        "500 failed pasv\r\n",
-                        ERR_FAILED);
-}
-
-TEST_F(FtpNetworkTransactionTest, DirectoryTransactionMalformedMdtm) {
-  FtpSocketDataProviderDirectoryListing ctrl_socket;
-  TransactionFailHelper(&ctrl_socket,
-                        "ftp://host",
-                        FtpSocketDataProvider::PRE_MDTM,
-                        FtpSocketDataProvider::PRE_RETR,
-                        "213 foobar\r\n",
-                        OK);
-}
-
-TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailMdtm) {
-  FtpSocketDataProviderDirectoryListing ctrl_socket;
-  TransactionFailHelper(&ctrl_socket,
-                        "ftp://host",
-                        FtpSocketDataProvider::PRE_MDTM,
-                        FtpSocketDataProvider::PRE_RETR,
-                        "500 failed mdtm\r\n",
-                        OK);
-}
-
-TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailPasv2) {
-  FtpSocketDataProviderDirectoryListing ctrl_socket;
-  TransactionFailHelper(&ctrl_socket,
-                        "ftp://host",
-                        FtpSocketDataProvider::PRE_PASV2,
-                        FtpSocketDataProvider::PRE_QUIT,
-                        "500 failed pasv2\r\n",
-                        ERR_FAILED);
+                        FtpSocketDataProvider::PRE_EPSV,
+                        FtpSocketDataProvider::PRE_NOPASV,
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
 TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFailCwd) {
@@ -1004,8 +1191,8 @@
                         "ftp://host",
                         FtpSocketDataProvider::PRE_CWD,
                         FtpSocketDataProvider::PRE_QUIT,
-                        "500 failed cwd\r\n",
-                        ERR_FAILED);
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
 TEST_F(FtpNetworkTransactionTest, DirectoryTransactionFileNotFound) {
@@ -1034,8 +1221,8 @@
                         "ftp://host/dir",
                         FtpSocketDataProvider::PRE_LIST,
                         FtpSocketDataProvider::PRE_QUIT,
-                        "500 failed list\r\n",
-                        ERR_FAILED);
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
 TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailUser) {
@@ -1044,8 +1231,8 @@
                         "ftp://host/file",
                         FtpSocketDataProvider::PRE_USER,
                         FtpSocketDataProvider::PRE_QUIT,
-                        "500 no such user\r\n",
-                        ERR_FAILED);
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
 TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailPass) {
@@ -1055,7 +1242,7 @@
                         FtpSocketDataProvider::PRE_PASSWD,
                         FtpSocketDataProvider::PRE_QUIT,
                         "530 Login authentication failed\r\n",
-                        ERR_FAILED);
+                        ERR_FTP_FAILED);
 }
 
 TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailSyst) {
@@ -1064,7 +1251,7 @@
                         "ftp://host/file",
                         FtpSocketDataProvider::PRE_SYST,
                         FtpSocketDataProvider::PRE_PWD,
-                        "500 failed syst\r\n",
+                        "599 fail\r\n",
                         OK);
 }
 
@@ -1074,8 +1261,8 @@
                         "ftp://host/file",
                         FtpSocketDataProvider::PRE_PWD,
                         FtpSocketDataProvider::PRE_QUIT,
-                        "500 failed pwd\r\n",
-                        ERR_FAILED);
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
 TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailType) {
@@ -1084,48 +1271,49 @@
                         "ftp://host/file",
                         FtpSocketDataProvider::PRE_TYPE,
                         FtpSocketDataProvider::PRE_QUIT,
-                        "500 failed type\r\n",
-                        ERR_FAILED);
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
-TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailPasv) {
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailEpsv) {
   FtpSocketDataProviderFileDownload ctrl_socket;
   TransactionFailHelper(&ctrl_socket,
                         "ftp://host/file",
-                        FtpSocketDataProvider::PRE_PASV,
-                        FtpSocketDataProvider::PRE_QUIT,
-                        "500 failed pasv\r\n",
-                        ERR_FAILED);
-}
-
-TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailMdtm) {
-  FtpSocketDataProviderFileDownload ctrl_socket;
-  TransactionFailHelper(&ctrl_socket,
-                        "ftp://host/file",
-                        FtpSocketDataProvider::PRE_MDTM,
-                        FtpSocketDataProvider::PRE_RETR,
-                        "500 failed mdtm\r\n",
-                        OK);
+                        FtpSocketDataProvider::PRE_EPSV,
+                        FtpSocketDataProvider::PRE_NOPASV,
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
 TEST_F(FtpNetworkTransactionTest, DownloadTransactionFailRetr) {
-  FtpSocketDataProviderFileDownloadRetrFail ctrl_socket;
+  FtpSocketDataProviderFileDownload ctrl_socket;
   TransactionFailHelper(&ctrl_socket,
                         "ftp://host/file",
                         FtpSocketDataProvider::PRE_RETR,
                         FtpSocketDataProvider::PRE_QUIT,
-                        "500 failed retr\r\n",
-                        ERR_FAILED);
+                        "599 fail\r\n",
+                        ERR_FTP_FAILED);
 }
 
 TEST_F(FtpNetworkTransactionTest, DownloadTransactionFileNotFound) {
-  FtpSocketDataProviderFileDownloadRetrFail ctrl_socket;
+  FtpSocketDataProviderFileDownload ctrl_socket;
+  TransactionFailHelper(&ctrl_socket,
+                        "ftp://host/file;type=i",
+                        FtpSocketDataProvider::PRE_SIZE,
+                        FtpSocketDataProvider::PRE_QUIT,
+                        "550 File Not Found\r\n",
+                        ERR_FTP_FAILED);
+}
+
+// Test for http://crbug.com/38845.
+TEST_F(FtpNetworkTransactionTest, ZeroLengthDirInPWD) {
+  FtpSocketDataProviderFileDownload ctrl_socket;
   TransactionFailHelper(&ctrl_socket,
                         "ftp://host/file",
-                        FtpSocketDataProvider::PRE_RETR,
-                        FtpSocketDataProvider::PRE_PASV2,
-                        "550 cannot open file\r\n",
-                        ERR_FILE_NOT_FOUND);
+                        FtpSocketDataProvider::PRE_PWD,
+                        FtpSocketDataProvider::PRE_TYPE,
+                        "257 \"\"\r\n",
+                        OK);
 }
 
 }  // namespace net
diff --git a/net/ftp/ftp_response_info.h b/net/ftp/ftp_response_info.h
index 9c94064..daec6c2 100644
--- a/net/ftp/ftp_response_info.h
+++ b/net/ftp/ftp_response_info.h
@@ -11,7 +11,10 @@
 
 class FtpResponseInfo {
  public:
-  FtpResponseInfo() : needs_auth(false), is_directory_listing(false) {
+  FtpResponseInfo()
+      : needs_auth(false),
+        expected_content_size(-1),
+        is_directory_listing(false) {
   }
 
   // True if authentication failed and valid authentication credentials are
@@ -26,6 +29,10 @@
   // responses, this time could be "far" in the past.
   base::Time response_time;
 
+  // Expected content size, in bytes, as reported by SIZE command. Only valid
+  // for file downloads. -1 means unknown size.
+  int64 expected_content_size;
+
   // True if the response data is of a directory listing.
   bool is_directory_listing;
 };
diff --git a/net/ftp/ftp_transaction.h b/net/ftp/ftp_transaction.h
index da7f73e..881a977 100644
--- a/net/ftp/ftp_transaction.h
+++ b/net/ftp/ftp_transaction.h
@@ -13,7 +13,7 @@
 
 class FtpResponseInfo;
 class FtpRequestInfo;
-class LoadLog;
+class BoundNetLog;
 
 // Represents a single FTP transaction.
 class FtpTransaction {
@@ -35,10 +35,10 @@
   //
   // NOTE: The transaction is not responsible for deleting the callback object.
   //
-  // Profiling information for the request is saved to |load_log| if non-NULL.
+  // Profiling information for the request is saved to |net_log| if non-NULL.
   virtual int Start(const FtpRequestInfo* request_info,
                     CompletionCallback* callback,
-                    LoadLog* load_log) = 0;
+                    const BoundNetLog& net_log) = 0;
 
   // Restarts the FTP transaction with authentication credentials.
   virtual int RestartWithAuth(const std::wstring& username,
diff --git a/net/ftp/ftp_util.cc b/net/ftp/ftp_util.cc
index e76adc0..79e6b71 100644
--- a/net/ftp/ftp_util.cc
+++ b/net/ftp/ftp_util.cc
@@ -10,6 +10,7 @@
 #include "base/string_tokenizer.h"
 #include "base/string_util.h"
 #include "base/time.h"
+#include "base/utf_string_conversions.h"
 
 // For examples of Unix<->VMS path conversions, see the unit test file. On VMS
 // a path looks differently depending on whether it's a file or directory.
@@ -99,7 +100,7 @@
   std::replace(result.begin(), result.end(), ']', '/');
 
   // Make sure the result doesn't end with a slash.
-  if (result[result.length() - 1] == '/')
+  if (result.length() && result[result.length() - 1] == '/')
     result = result.substr(0, result.length() - 1);
 
   return result;
@@ -146,7 +147,9 @@
 
 // static
 bool FtpUtil::LsDateListingToTime(const string16& month, const string16& day,
-                                  const string16& rest, base::Time* time) {
+                                  const string16& rest,
+                                  const base::Time& current_time,
+                                  base::Time* result) {
   base::Time::Exploded time_exploded = { 0 };
 
   if (!ThreeLetterMonthToNumber(month, &time_exploded.month))
@@ -166,14 +169,23 @@
     if (!StringToInt(rest.substr(3, 2), &time_exploded.minute))
       return false;
 
-    // Use current year.
-    base::Time::Exploded now_exploded;
-    base::Time::Now().LocalExplode(&now_exploded);
-    time_exploded.year = now_exploded.year;
+    // Guess the year.
+    base::Time::Exploded current_exploded;
+    current_time.LocalExplode(&current_exploded);
+
+    // If it's not possible for the parsed date to be in the current year,
+    // use the previous year.
+    if (time_exploded.month > current_exploded.month ||
+        (time_exploded.month == current_exploded.month &&
+         time_exploded.day_of_month > current_exploded.day_of_month)) {
+      time_exploded.year = current_exploded.year - 1;
+    } else {
+      time_exploded.year = current_exploded.year;
+    }
   }
 
   // We don't know the time zone of the listing, so just use local time.
-  *time = base::Time::FromLocalExploded(time_exploded);
+  *result = base::Time::FromLocalExploded(time_exploded);
   return true;
 }
 
diff --git a/net/ftp/ftp_util.h b/net/ftp/ftp_util.h
index 5643606..37a51c2 100644
--- a/net/ftp/ftp_util.h
+++ b/net/ftp/ftp_util.h
@@ -32,12 +32,13 @@
 
   // Convert a "ls -l" date listing to time. The listing comes in three columns.
   // The first one contains month, the second one contains day of month.
-  // The first one is either a time (and then the current year is assumed),
-  // or is a year (and then we don't know the time).
+  // The first one is either a time (and then we guess the year based
+  // on |current_time|), or is a year (and then we don't know the time).
   static bool LsDateListingToTime(const string16& month,
                                   const string16& day,
                                   const string16& rest,
-                                  base::Time* time);
+                                  const base::Time& current_time,
+                                  base::Time* result);
 
   // Skip |columns| columns from |text| (whitespace-delimited), and return the
   // remaining part, without leading/trailing whitespace.
diff --git a/net/ftp/ftp_util_unittest.cc b/net/ftp/ftp_util_unittest.cc
index e929aed..20450c2 100644
--- a/net/ftp/ftp_util_unittest.cc
+++ b/net/ftp/ftp_util_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/basictypes.h"
 #include "base/format_macros.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "base/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -93,6 +94,7 @@
     { "[.a.b.c]",    "a/b/c"      },
     { "[.a.b.c]d",   "a/b/c/d"    },
     { "[.a.b.c.d]",  "a/b/c/d"    },
+    { "[.",          ""           },
   };
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); i++) {
     EXPECT_EQ(kTestCases[i].expected_output,
@@ -102,8 +104,9 @@
 }
 
 TEST(FtpUtilTest, LsDateListingToTime) {
-  base::Time::Exploded now_exploded;
-  base::Time::Now().LocalExplode(&now_exploded);
+  base::Time mock_current_time;
+  ASSERT_TRUE(base::Time::FromString(L"Tue, 15 Nov 1994 12:45:26 GMT",
+                                     &mock_current_time));
 
   const struct {
     // Input.
@@ -119,14 +122,22 @@
     int expected_minute;
   } kTestCases[] = {
     { "Nov", "01", "2007", 2007, 11, 1, 0, 0 },
-    { "Jul", "25", "13:37", now_exploded.year, 7, 25, 13, 37 },
+    { "Jul", "25", "13:37", 1994, 7, 25, 13, 37 },
 
     // Test date listings in German, we should support them for FTP servers
     // giving localized listings.
     { "M\xc3\xa4r", "13", "2009", 2009, 3, 13, 0, 0 },
-    { "Mai", "1", "10:10", now_exploded.year, 5, 1, 10, 10 },
-    { "Okt", "14", "21:18", now_exploded.year, 10, 14, 21, 18 },
+    { "Mai", "1", "10:10", 1994, 5, 1, 10, 10 },
+    { "Okt", "14", "21:18", 1994, 10, 14, 21, 18 },
     { "Dez", "25", "2008", 2008, 12, 25, 0, 0 },
+
+    // Test current year detection.
+    { "Nov", "01", "12:00", 1994, 11, 1, 12, 0 },
+    { "Nov", "15", "12:00", 1994, 11, 15, 12, 0 },
+    { "Nov", "16", "12:00", 1993, 11, 16, 12, 0 },
+    { "Jan", "01", "08:30", 1994, 1, 1, 8, 30 },
+    { "Sep", "02", "09:00", 1994, 9, 2, 9, 0 },
+    { "Dec", "06", "21:00", 1993, 12, 6, 21, 0 },
   };
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); i++) {
     SCOPED_TRACE(StringPrintf("Test[%" PRIuS "]: %s %s %s", i,
@@ -136,7 +147,7 @@
     base::Time time;
     ASSERT_TRUE(net::FtpUtil::LsDateListingToTime(
         UTF8ToUTF16(kTestCases[i].month), UTF8ToUTF16(kTestCases[i].day),
-        UTF8ToUTF16(kTestCases[i].rest), &time));
+        UTF8ToUTF16(kTestCases[i].rest), mock_current_time, &time));
 
     base::Time::Exploded time_exploded;
     time.LocalExplode(&time_exploded);
diff --git a/net/hresolv.scons b/net/hresolv.scons
new file mode 100644
index 0000000..ce95aa0
--- /dev/null
+++ b/net/hresolv.scons
@@ -0,0 +1,449 @@
+# This file is generated; do not edit.
+
+import os
+
+Import("env")
+
+env = env.Clone(COMPONENT_NAME='net',
+                TARGET_NAME='hresolv')
+
+configurations = {
+    'Release' : {
+        'Append' : dict(
+            CCFLAGS = [
+                '-Werror',
+                '-pthread',
+                '-fno-exceptions',
+                '-Wall',
+                '-D_FILE_OFFSET_BITS=64',
+                '-fvisibility=hidden',
+                '-fno-strict-aliasing',
+                '-pthread',
+                '-D_REENTRANT',
+                '-I/usr/include/gtk-2.0',
+                '-I/usr/lib/gtk-2.0/include',
+                '-I/usr/include/atk-1.0',
+                '-I/usr/include/cairo',
+                '-I/usr/include/pango-1.0',
+                '-I/usr/include/gio-unix-2.0/',
+                '-I/usr/include/glib-2.0',
+                '-I/usr/lib/glib-2.0/include',
+                '-I/usr/include/pixman-1',
+                '-I/usr/include/freetype2',
+                '-I/usr/include/directfb',
+                '-I/usr/include/libpng12',
+                '-O2',
+                '-fno-ident',
+                '-fdata-sections',
+                '-ffunction-sections',
+                '-fno-asynchronous-unwind-tables'
+            ],
+            CPPDEFINES = [
+                '__STDC_FORMAT_MACROS',
+                'CHROMIUM_BUILD',
+                'ENABLE_GPU=1',
+                'NDEBUG',
+                'NVALGRIND'
+            ],
+            CPPPATH = [
+                env.Dir('$SRC_DIR/net/..')
+            ],
+            CXXFLAGS = [
+                '-fno-rtti',
+                '-fno-threadsafe-statics',
+                '-fvisibility-inlines-hidden'
+            ],
+            LINKFLAGS = [
+                '-pthread',
+                '-Wl,--gc-sections'
+            ],
+            LIBS = [
+                '-lrt',
+                '-lgtk-x11-2.0',
+                '-lgdk-x11-2.0',
+                '-latk-1.0',
+                '-lgio-2.0',
+                '-lpangoft2-1.0',
+                '-lgdk_pixbuf-2.0',
+                '-lm',
+                '-lpangocairo-1.0',
+                '-lcairo',
+                '-lpango-1.0',
+                '-lfreetype',
+                '-lfontconfig',
+                '-lgobject-2.0',
+                '-lgmodule-2.0',
+                '-lgthread-2.0',
+                '-lrt',
+                '-lglib-2.0',
+                '-lnss3',
+                '-lnssutil3',
+                '-lsmime3',
+                '-lplds4',
+                '-lplc4',
+                '-lnspr4',
+                '-lpthread',
+                '-ldl',
+                '-lz',
+                '-lrt',
+                '-lgconf-2',
+                '-lglib-2.0',
+                '-lgdk-x11-2.0',
+                '-lgdk_pixbuf-2.0',
+                '-lm',
+                '-lpangocairo-1.0',
+                '-lpango-1.0',
+                '-lcairo',
+                '-lgobject-2.0',
+                '-lgmodule-2.0',
+                '-lgthread-2.0',
+                '-lrt',
+                '-lglib-2.0',
+                'libnet_base',
+                'libbase',
+                'libmodp_b64',
+                'libssl',
+                'libzlib',
+                'libsymbolize',
+                'libxdg_mime',
+                'libevent',
+                'libbase_i18n',
+                'libicui18n',
+                'libicuuc',
+                'libicudata',
+                'libgoogleurl',
+                'libsdch'
+            ],
+        ),
+        'FilterOut' : dict(
+        ),
+        'Replace' : dict(
+             FLOCK_LDMODULE = ['flock', '$TOP_BUILDDIR/linker.lock', '$LDMODULE'],
+             FLOCK_LINK = ['flock', '$TOP_BUILDDIR/linker.lock', '$LINK'],
+             FLOCK_SHLINK = ['flock', '$TOP_BUILDDIR/linker.lock', '$SHLINK'],
+             IMPLICIT_COMMAND_DEPENDENCIES = '0',
+             LDMODULECOM = [['$FLOCK_LDMODULE',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$LDMODULEFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+             LIBPATH = ['$LIB_DIR'],
+             LINKCOM = [['$FLOCK_LINK',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$LINKFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+             SHLINKCOM = [['$FLOCK_SHLINK',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$SHLINKFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+        ),
+        'ImportExternal' : [
+             'AS',
+             'CC',
+             'CXX',
+             'LINK',
+        ],
+        'PropagateExternal' : [
+             'AS',
+             'CC',
+             'CCACHE_DIR',
+             'CXX',
+             'DISTCC_DIR',
+             'DISTCC_HOSTS',
+             'HOME',
+             'INCLUDE_SERVER_ARGS',
+             'INCLUDE_SERVER_PORT',
+             'LINK',
+             'CHROME_BUILD_TYPE',
+             'CHROMIUM_BUILD',
+             'OFFICIAL_BUILD',
+        ],
+    },
+    'Debug' : {
+        'Append' : dict(
+            CCFLAGS = [
+                '-Werror',
+                '-pthread',
+                '-fno-exceptions',
+                '-Wall',
+                '-D_FILE_OFFSET_BITS=64',
+                '-fvisibility=hidden',
+                '-fno-strict-aliasing',
+                '-pthread',
+                '-D_REENTRANT',
+                '-I/usr/include/gtk-2.0',
+                '-I/usr/lib/gtk-2.0/include',
+                '-I/usr/include/atk-1.0',
+                '-I/usr/include/cairo',
+                '-I/usr/include/pango-1.0',
+                '-I/usr/include/gio-unix-2.0/',
+                '-I/usr/include/glib-2.0',
+                '-I/usr/lib/glib-2.0/include',
+                '-I/usr/include/pixman-1',
+                '-I/usr/include/freetype2',
+                '-I/usr/include/directfb',
+                '-I/usr/include/libpng12',
+                '-O0',
+                '-g'
+            ],
+            CPPDEFINES = [
+                '__STDC_FORMAT_MACROS',
+                'CHROMIUM_BUILD',
+                'ENABLE_GPU=1',
+                '_DEBUG'
+            ],
+            CPPPATH = [
+                env.Dir('$SRC_DIR/net/..')
+            ],
+            CXXFLAGS = [
+                '-fno-rtti',
+                '-fno-threadsafe-statics',
+                '-fvisibility-inlines-hidden'
+            ],
+            LINKFLAGS = [
+                '-pthread',
+                '-rdynamic'
+            ],
+            LIBS = [
+                '-lrt',
+                '-lgtk-x11-2.0',
+                '-lgdk-x11-2.0',
+                '-latk-1.0',
+                '-lgio-2.0',
+                '-lpangoft2-1.0',
+                '-lgdk_pixbuf-2.0',
+                '-lm',
+                '-lpangocairo-1.0',
+                '-lcairo',
+                '-lpango-1.0',
+                '-lfreetype',
+                '-lfontconfig',
+                '-lgobject-2.0',
+                '-lgmodule-2.0',
+                '-lgthread-2.0',
+                '-lrt',
+                '-lglib-2.0',
+                '-lnss3',
+                '-lnssutil3',
+                '-lsmime3',
+                '-lplds4',
+                '-lplc4',
+                '-lnspr4',
+                '-lpthread',
+                '-ldl',
+                '-lz',
+                '-lrt',
+                '-lgconf-2',
+                '-lglib-2.0',
+                '-lgdk-x11-2.0',
+                '-lgdk_pixbuf-2.0',
+                '-lm',
+                '-lpangocairo-1.0',
+                '-lpango-1.0',
+                '-lcairo',
+                '-lgobject-2.0',
+                '-lgmodule-2.0',
+                '-lgthread-2.0',
+                '-lrt',
+                '-lglib-2.0',
+                'libnet_base',
+                'libbase',
+                'libmodp_b64',
+                'libssl',
+                'libzlib',
+                'libsymbolize',
+                'libxdg_mime',
+                'libevent',
+                'libbase_i18n',
+                'libicui18n',
+                'libicuuc',
+                'libicudata',
+                'libgoogleurl',
+                'libsdch'
+            ],
+        ),
+        'FilterOut' : dict(
+        ),
+        'Replace' : dict(
+             FLOCK_LDMODULE = ['flock', '$TOP_BUILDDIR/linker.lock', '$LDMODULE'],
+             FLOCK_LINK = ['flock', '$TOP_BUILDDIR/linker.lock', '$LINK'],
+             FLOCK_SHLINK = ['flock', '$TOP_BUILDDIR/linker.lock', '$SHLINK'],
+             IMPLICIT_COMMAND_DEPENDENCIES = '0',
+             LDMODULECOM = [['$FLOCK_LDMODULE',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$LDMODULEFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+             LIBPATH = ['$LIB_DIR'],
+             LINKCOM = [['$FLOCK_LINK',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$LINKFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+             SHLINKCOM = [['$FLOCK_SHLINK',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$SHLINKFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+        ),
+        'ImportExternal' : [
+             'AS',
+             'CC',
+             'CXX',
+             'LINK',
+        ],
+        'PropagateExternal' : [
+             'AS',
+             'CC',
+             'CCACHE_DIR',
+             'CXX',
+             'DISTCC_DIR',
+             'DISTCC_HOSTS',
+             'HOME',
+             'INCLUDE_SERVER_ARGS',
+             'INCLUDE_SERVER_PORT',
+             'LINK',
+             'CHROME_BUILD_TYPE',
+             'CHROMIUM_BUILD',
+             'OFFICIAL_BUILD',
+        ],
+    },
+}
+
+config = configurations[env['CONFIG_NAME']]
+env.Append(**config['Append'])
+env.FilterOut(**config['FilterOut'])
+env.Replace(**config['Replace'])
+
+# Scons forces -fPIC for SHCCFLAGS on some platforms.
+# Disable that so we can control it from cflags in gyp.
+# Note that Scons itself is inconsistent with its -fPIC
+# setting. SHCCFLAGS forces -fPIC, and SHCFLAGS does not.
+# This will make SHCCFLAGS consistent with SHCFLAGS.
+env['SHCCFLAGS'] = ['$CCFLAGS']
+
+for _var in config['ImportExternal']:
+  if _var in ARGUMENTS:
+    env[_var] = ARGUMENTS[_var]
+  elif _var in os.environ:
+    env[_var] = os.environ[_var]
+for _var in config['PropagateExternal']:
+  if _var in ARGUMENTS:
+    env[_var] = ARGUMENTS[_var]
+  elif _var in os.environ:
+    env['ENV'][_var] = os.environ[_var]
+
+env['ENV']['LD_LIBRARY_PATH'] = env.subst('$LIB_DIR')
+
+if ARGUMENTS.get('COVERAGE') not in (None, '0'):
+  env.AppendUnique(
+            CCFLAGS = [
+                '-fprofile-arcs',
+                '-ftest-coverage'
+            ],
+            LINKFLAGS = [
+                '-fprofile-arcs'
+            ],
+  )
+
+if ARGUMENTS.get('PROFILE') not in (None, '0'):
+  env.AppendUnique(
+            CCFLAGS = [
+                '-pg',
+                '-g'
+            ],
+            LINKFLAGS = [
+                '-pg'
+            ],
+  )
+
+if ARGUMENTS.get('SYMBOLS') not in (None, '0'):
+  env.AppendUnique(
+            CCFLAGS = [
+                '-g'
+            ],
+  )
+
+input_files = [
+    'tools/hresolv/hresolv.cc',
+]
+
+target_files = []
+prerequisites = []
+
+_result = []
+for infile in input_files:
+  if env.compilable(infile):
+    if (type(infile) == type('')
+        and (infile.startswith('$SRC_DIR/net/')
+             or not os.path.isabs(env.subst(infile)))):
+      # Force files below the build directory by replacing all '..'
+      # elements in the path with '__':
+      base, ext = os.path.splitext(os.path.normpath(infile))
+      base = [d == '..' and '__' or d for d in base.split('/')]
+      base = os.path.join(*base)
+      object = '${OBJ_DIR}/${COMPONENT_NAME}/${TARGET_NAME}/' + base
+      if not infile.startswith('$SRC_DIR/net/'):
+        infile = '$SRC_DIR/net/' + infile
+      infile = env.StaticObject(object, infile)[0]
+    else:
+      infile = env.StaticObject(infile)[0]
+  _result.append(infile)
+input_files = _result
+
+_outputs = env.GypProgram(env.File('${TOP_BUILDDIR}/${PROGPREFIX}hresolv${PROGSUFFIX}'), input_files)
+target_files.extend(_outputs)
+
+gyp_target = env.Alias('hresolv', target_files)
+dependencies = [
+    Alias('net_base'),
+    Alias('base'),
+    Alias('modp_b64'),
+    Alias('gtk'),
+    Alias('nss'),
+    Alias('ssl'),
+    Alias('zlib'),
+    Alias('symbolize'),
+    Alias('xdg_mime'),
+    Alias('libevent'),
+    Alias('base_i18n'),
+    Alias('icui18n'),
+    Alias('icuuc'),
+    Alias('icudata'),
+    Alias('googleurl'),
+    Alias('sdch'),
+    Alias('gconf'),
+    Alias('gdk')
+]
+env.Requires(target_files, dependencies)
+env.Requires(gyp_target, dependencies)
+for prerequisite in prerequisites:
+  env.Requires(prerequisite, dependencies)
+env.Requires(gyp_target, prerequisites)
+Return("gyp_target")
diff --git a/net/hresolv.target.mk b/net/hresolv.target.mk
new file mode 100644
index 0000000..95b02a8
--- /dev/null
+++ b/net/hresolv.target.mk
@@ -0,0 +1,183 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := hresolv
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I.
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I.
+
+OBJS := $(obj).target/$(TARGET)/net/tools/hresolv/hresolv.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/net/libnet_base.a $(obj).target/base/libbase.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-Wl,--gc-sections
+
+LIBS := -lrt \
+	-ldl \
+	-lgtk-x11-2.0 \
+	-lgdk-x11-2.0 \
+	-latk-1.0 \
+	-lgio-2.0 \
+	-lpangoft2-1.0 \
+	-lgdk_pixbuf-2.0 \
+	-lm \
+	-lpangocairo-1.0 \
+	-lcairo \
+	-lpango-1.0 \
+	-lfreetype \
+	-lfontconfig \
+	-lgobject-2.0 \
+	-lgmodule-2.0 \
+	-lgthread-2.0 \
+	-lglib-2.0 \
+	-lnss3 \
+	-lnssutil3 \
+	-lsmime3 \
+	-lplds4 \
+	-lplc4 \
+	-lnspr4 \
+	-lpthread \
+	-lz \
+	-lgconf-2
+
+$(builddir)/hresolv: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(builddir)/hresolv: LIBS := $(LIBS)
+$(builddir)/hresolv: TOOLSET := $(TOOLSET)
+$(builddir)/hresolv: $(OBJS) $(obj).target/net/libnet_base.a $(obj).target/base/libbase.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a FORCE_DO_CMD
+	$(call do_cmd,link)
+
+all_deps += $(builddir)/hresolv
+# Add target alias
+.PHONY: hresolv
+hresolv: $(builddir)/hresolv
+
+# Add executable to "all" target.
+.PHONY: all
+all: $(builddir)/hresolv
+
diff --git a/net/http/http_alternate_protocols.cc b/net/http/http_alternate_protocols.cc
new file mode 100644
index 0000000..cd42ed5
--- /dev/null
+++ b/net/http/http_alternate_protocols.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2010 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/http/http_alternate_protocols.h"
+
+#include "base/logging.h"
+#include "base/stl_util-inl.h"
+
+namespace net {
+
+const char HttpAlternateProtocols::kHeader[] = "Alternate-Protocol";
+const char* const HttpAlternateProtocols::kProtocolStrings[] = {
+  "npn-spdy/1",
+};
+
+HttpAlternateProtocols::HttpAlternateProtocols() {}
+HttpAlternateProtocols::~HttpAlternateProtocols() {}
+
+bool HttpAlternateProtocols::HasAlternateProtocolFor(
+    const HostPortPair& http_host_port_pair) const {
+  return ContainsKey(protocol_map_, http_host_port_pair);
+}
+
+bool HttpAlternateProtocols::HasAlternateProtocolFor(
+    const std::string& host, uint16 port) const {
+  struct HostPortPair http_host_port_pair;
+  http_host_port_pair.host = host;
+  http_host_port_pair.port = port;
+  return HasAlternateProtocolFor(http_host_port_pair);
+}
+
+HttpAlternateProtocols::PortProtocolPair
+HttpAlternateProtocols::GetAlternateProtocolFor(
+    const HostPortPair& http_host_port_pair) const {
+  DCHECK(ContainsKey(protocol_map_, http_host_port_pair));
+  return protocol_map_.find(http_host_port_pair)->second;
+}
+
+HttpAlternateProtocols::PortProtocolPair
+HttpAlternateProtocols::GetAlternateProtocolFor(
+    const std::string& host, uint16 port) const {
+  struct HostPortPair http_host_port_pair;
+  http_host_port_pair.host = host;
+  http_host_port_pair.port = port;
+  return GetAlternateProtocolFor(http_host_port_pair);
+}
+
+void HttpAlternateProtocols::SetAlternateProtocolFor(
+    const HostPortPair& http_host_port_pair,
+    uint16 alternate_port,
+    Protocol alternate_protocol) {
+  if (alternate_protocol == BROKEN) {
+    LOG(DFATAL) << "Call MarkBrokenAlternateProtocolFor() instead.";
+    return;
+  }
+
+  PortProtocolPair alternate;
+  alternate.port = alternate_port;
+  alternate.protocol = alternate_protocol;
+  if (HasAlternateProtocolFor(http_host_port_pair)) {
+    const PortProtocolPair existing_alternate =
+        GetAlternateProtocolFor(http_host_port_pair);
+
+    if (existing_alternate.protocol == BROKEN) {
+      DLOG(INFO) << "Ignore alternate protocol since it's known to be broken.";
+      return;
+    }
+
+    if (alternate_protocol != BROKEN && !existing_alternate.Equals(alternate)) {
+      LOG(WARNING) << "Changing the alternate protocol for: "
+                   << http_host_port_pair.ToString()
+                   << " from [Port: " << existing_alternate.port
+                   << ", Protocol: " << existing_alternate.protocol
+                   << "] to [Port: " << alternate_port
+                   << ", Protocol: " << alternate_protocol
+                   << "].";
+    }
+  }
+
+  protocol_map_[http_host_port_pair] = alternate;
+}
+
+void HttpAlternateProtocols::MarkBrokenAlternateProtocolFor(
+    const HostPortPair& http_host_port_pair) {
+  protocol_map_[http_host_port_pair].protocol = BROKEN;
+}
+
+}  // namespace net
diff --git a/net/http/http_alternate_protocols.h b/net/http/http_alternate_protocols.h
new file mode 100644
index 0000000..56fb49a
--- /dev/null
+++ b/net/http/http_alternate_protocols.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2010 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.
+//
+// HttpAlternateProtocols is an in-memory data structure used for keeping track
+// of which HTTP HostPortPairs have an alternate protocol that can be used
+// instead of HTTP on a different port.
+
+#ifndef NET_HTTP_HTTP_ALTERNATE_PROTOCOLS_H_
+#define NET_HTTP_HTTP_ALTERNATE_PROTOCOLS_H_
+
+#include <map>
+#include <utility>
+#include "base/basictypes.h"
+#include "net/base/host_port_pair.h"
+
+namespace net {
+
+class HttpAlternateProtocols {
+ public:
+  enum Protocol {
+    NPN_SPDY_1,
+    NUM_ALTERNATE_PROTOCOLS,
+    BROKEN,  // The alternate protocol is known to be broken.
+  };
+
+  struct PortProtocolPair {
+    bool Equals(const PortProtocolPair& other) const {
+      return port == other.port && protocol == other.protocol;
+    }
+
+    uint16 port;
+    Protocol protocol;
+  };
+
+  static const char kHeader[];
+  static const char* const kProtocolStrings[NUM_ALTERNATE_PROTOCOLS];
+
+  HttpAlternateProtocols();
+  ~HttpAlternateProtocols();
+
+  // Reports whether or not we have received Alternate-Protocol for
+  // |http_host_port_pair|.
+  bool HasAlternateProtocolFor(const HostPortPair& http_host_port_pair) const;
+  bool HasAlternateProtocolFor(const std::string& host, uint16 port) const;
+
+  PortProtocolPair GetAlternateProtocolFor(
+      const HostPortPair& http_host_port_pair) const;
+  PortProtocolPair GetAlternateProtocolFor(
+      const std::string& host, uint16 port) const;
+
+  // SetAlternateProtocolFor() will ignore the request if the alternate protocol
+  // has already been marked broken via MarkBrokenAlternateProtocolFor().
+  void SetAlternateProtocolFor(const HostPortPair& http_host_port_pair,
+                               uint16 alternate_port,
+                               Protocol alternate_protocol);
+
+  // Marks the alternate protocol as broken.  Once marked broken, any further
+  // attempts to set the alternate protocol for |http_host_port_pair| will fail.
+  void MarkBrokenAlternateProtocolFor(const HostPortPair& http_host_port_pair);
+
+ private:
+  typedef std::map<HostPortPair, PortProtocolPair> ProtocolMap;
+
+  ProtocolMap protocol_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpAlternateProtocols);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_ALTERNATE_PROTOCOLS_H_
diff --git a/net/http/http_alternate_protocols_unittest.cc b/net/http/http_alternate_protocols_unittest.cc
new file mode 100644
index 0000000..d82f850
--- /dev/null
+++ b/net/http/http_alternate_protocols_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2010 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.
+//
+// HttpAlternateProtocols is an in-memory data structure used for keeping track
+// of which HTTP HostPortPairs have an alternate protocol that can be used
+// instead of HTTP on a different port.
+
+#include "net/http/http_alternate_protocols.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+TEST(HttpAlternateProtocols, Basic) {
+  HttpAlternateProtocols alternate_protocols;
+  HostPortPair test_host_port_pair;
+  test_host_port_pair.host = "foo";
+  test_host_port_pair.port = 80;
+  EXPECT_FALSE(
+      alternate_protocols.HasAlternateProtocolFor(test_host_port_pair));
+  alternate_protocols.SetAlternateProtocolFor(
+      test_host_port_pair, 443, HttpAlternateProtocols::NPN_SPDY_1);
+  ASSERT_TRUE(alternate_protocols.HasAlternateProtocolFor(test_host_port_pair));
+  const HttpAlternateProtocols::PortProtocolPair alternate =
+      alternate_protocols.GetAlternateProtocolFor(test_host_port_pair);
+  EXPECT_EQ(443, alternate.port);
+  EXPECT_EQ(HttpAlternateProtocols::NPN_SPDY_1, alternate.protocol);
+}
+
+TEST(HttpAlternateProtocols, SetBroken) {
+  HttpAlternateProtocols alternate_protocols;
+  HostPortPair test_host_port_pair;
+  test_host_port_pair.host = "foo";
+  test_host_port_pair.port = 80;
+  alternate_protocols.MarkBrokenAlternateProtocolFor(test_host_port_pair);
+  ASSERT_TRUE(alternate_protocols.HasAlternateProtocolFor(test_host_port_pair));
+  HttpAlternateProtocols::PortProtocolPair alternate =
+      alternate_protocols.GetAlternateProtocolFor(test_host_port_pair);
+  EXPECT_EQ(HttpAlternateProtocols::BROKEN, alternate.protocol);
+
+  alternate_protocols.SetAlternateProtocolFor(
+      test_host_port_pair,
+      1234,
+      HttpAlternateProtocols::NPN_SPDY_1);
+  alternate = alternate_protocols.GetAlternateProtocolFor(test_host_port_pair);
+  EXPECT_EQ(HttpAlternateProtocols::BROKEN, alternate.protocol)
+      << "Second attempt should be ignored.";
+}
+
+}  // namespace
+}  // namespace net
diff --git a/net/http/http_auth.cc b/net/http/http_auth.cc
index aa97640..ff2ac4f 100644
--- a/net/http/http_auth.cc
+++ b/net/http/http_auth.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -8,6 +8,7 @@
 
 #include "base/basictypes.h"
 #include "base/string_util.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_auth_handler_basic.h"
 #include "net/http/http_auth_handler_digest.h"
 #include "net/http/http_auth_handler_negotiate.h"
@@ -18,69 +19,50 @@
 namespace net {
 
 // static
-void HttpAuth::ChooseBestChallenge(const HttpResponseHeaders* headers,
-                                   Target target,
-                                   const GURL& origin,
-                                   scoped_refptr<HttpAuthHandler>* handler) {
+void HttpAuth::ChooseBestChallenge(
+    HttpAuthHandlerFactory* http_auth_handler_factory,
+    const HttpResponseHeaders* headers,
+    Target target,
+    const GURL& origin,
+    const std::set<std::string>& disabled_schemes,
+    const BoundNetLog& net_log,
+    scoped_ptr<HttpAuthHandler>* handler) {
+  DCHECK(http_auth_handler_factory);
+
   // A connection-based authentication scheme must continue to use the
   // existing handler object in |*handler|.
-  if (*handler && (*handler)->is_connection_based()) {
+  if (handler->get() && (*handler)->is_connection_based()) {
     const std::string header_name = GetChallengeHeaderName(target);
     std::string challenge;
     void* iter = NULL;
     while (headers->EnumerateHeader(&iter, header_name, &challenge)) {
       ChallengeTokenizer props(challenge.begin(), challenge.end());
       if (LowerCaseEqualsASCII(props.scheme(), (*handler)->scheme().c_str()) &&
-          (*handler)->InitFromChallenge(challenge.begin(), challenge.end(),
-                                        target, origin))
+          (*handler)->InitFromChallenge(&props, target, origin, net_log))
         return;
     }
   }
 
   // Choose the challenge whose authentication handler gives the maximum score.
-  scoped_refptr<HttpAuthHandler> best;
+  scoped_ptr<HttpAuthHandler> best;
   const std::string header_name = GetChallengeHeaderName(target);
   std::string cur_challenge;
   void* iter = NULL;
   while (headers->EnumerateHeader(&iter, header_name, &cur_challenge)) {
-    scoped_refptr<HttpAuthHandler> cur;
-    CreateAuthHandler(cur_challenge, target, origin, &cur);
-    if (cur && (!best || best->score() < cur->score()))
-      best.swap(cur);
-  }
-  handler->swap(best);
-}
-
-// static
-void HttpAuth::CreateAuthHandler(const std::string& challenge,
-                                 Target target,
-                                 const GURL& origin,
-                                 scoped_refptr<HttpAuthHandler>* handler) {
-  // Find the right auth handler for the challenge's scheme.
-  ChallengeTokenizer props(challenge.begin(), challenge.end());
-  if (!props.valid()) {
-    *handler = NULL;
-    return;
-  }
-
-  scoped_refptr<HttpAuthHandler> tmp_handler;
-  if (LowerCaseEqualsASCII(props.scheme(), "basic")) {
-    tmp_handler = new HttpAuthHandlerBasic();
-  } else if (LowerCaseEqualsASCII(props.scheme(), "digest")) {
-    tmp_handler = new HttpAuthHandlerDigest();
-  } else if (LowerCaseEqualsASCII(props.scheme(), "negotiate")) {
-    tmp_handler = new HttpAuthHandlerNegotiate();
-  } else if (LowerCaseEqualsASCII(props.scheme(), "ntlm")) {
-    tmp_handler = new HttpAuthHandlerNTLM();
-  }
-  if (tmp_handler) {
-    if (!tmp_handler->InitFromChallenge(challenge.begin(), challenge.end(),
-                                        target, origin)) {
-      // Invalid/unsupported challenge.
-      tmp_handler = NULL;
+    scoped_ptr<HttpAuthHandler> cur;
+    int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
+        cur_challenge, target, origin, net_log, &cur);
+    if (rv != OK) {
+      LOG(WARNING) << "Unable to create AuthHandler. Status: "
+                   << ErrorToString(rv) << " Challenge: " << cur_challenge;
+      continue;
+    }
+    if (cur.get() && (!best.get() || best->score() < cur->score())) {
+      if (disabled_schemes.find(cur->scheme()) == disabled_schemes.end())
+        best.swap(cur);
     }
   }
-  handler->swap(tmp_handler);
+  handler->swap(best);
 }
 
 void HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin,
@@ -106,6 +88,9 @@
 //   name="value"
 //   name=value
 //   name=
+// Due to buggy implementations found in some embedded devices, we also
+// accept values with missing close quotemark (http://crbug.com/39836):
+//   name="value
 bool HttpAuth::ChallengeTokenizer::GetNext() {
   if (!props_.GetNext())
     return false;
@@ -115,6 +100,21 @@
   value_end_ = props_.value_end();
   name_begin_ = name_end_ = value_end_;
 
+  if (expect_base64_token_) {
+    expect_base64_token_ = false;
+    // Strip off any padding.
+    // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.)
+    //
+    // Our base64 decoder requires that the length be a multiple of 4.
+    int encoded_length = value_end_ - value_begin_;
+    while (encoded_length > 0 && encoded_length % 4 != 0 &&
+           value_begin_[encoded_length - 1] == '=') {
+      --encoded_length;
+      --value_end_;
+    }
+    return true;
+  }
+
   // Scan for the equals sign.
   std::string::const_iterator equals = std::find(value_begin_, value_end_, '=');
   if (equals == value_end_ || equals == value_begin_)
@@ -130,13 +130,13 @@
   name_end_ = equals;
   value_begin_ = equals + 1;
 
+  value_is_quoted_ = false;
   if (value_begin_ != value_end_ && HttpUtil::IsQuote(*value_begin_)) {
     // Trim surrounding quotemarks off the value
-    if (*value_begin_ != *(value_end_ - 1))
-      return valid_ = false;  // Malformed -- mismatching quotes.
-    value_is_quoted_ = true;
-  } else {
-    value_is_quoted_ = false;
+    if (*value_begin_ != *(value_end_ - 1) || value_begin_ + 1 == value_end_)
+      value_begin_ = equals + 2;  // Gracefully recover from mismatching quotes.
+    else
+      value_is_quoted_ = true;
   }
   return true;
 }
@@ -172,4 +172,10 @@
   }
 }
 
+// static
+std::string HttpAuth::GetAuthTargetString(
+    HttpAuth::Target target) {
+  return target == HttpAuth::AUTH_PROXY ? "proxy" : "server";
+}
+
 }  // namespace net
diff --git a/net/http/http_auth.h b/net/http/http_auth.h
index 38c9918..01afcc0 100644
--- a/net/http/http_auth.h
+++ b/net/http/http_auth.h
@@ -1,66 +1,76 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_HTTP_HTTP_AUTH_H_
 #define NET_HTTP_HTTP_AUTH_H_
 
+#include <set>
+
+#include "base/scoped_ptr.h"
 #include "net/http/http_util.h"
 
 template <class T> class scoped_refptr;
 
 namespace net {
 
+class BoundNetLog;
 class HttpAuthHandler;
+class HttpAuthHandlerFactory;
 class HttpResponseHeaders;
 
 // Utility class for http authentication.
 class HttpAuth {
  public:
 
-   // Http authentication can be done the the proxy server, origin server,
-   // or both. This enum tracks who the target is.
-   enum Target {
-     AUTH_NONE = -1,
-     // We depend on the valid targets (!= AUTH_NONE) being usable as indexes
-     // in an array, so start from 0.
-     AUTH_PROXY = 0,
-     AUTH_SERVER = 1,
-   };
+  // Http authentication can be done the the proxy server, origin server,
+  // or both. This enum tracks who the target is.
+  enum Target {
+    AUTH_NONE = -1,
+    // We depend on the valid targets (!= AUTH_NONE) being usable as indexes
+    // in an array, so start from 0.
+    AUTH_PROXY = 0,
+    AUTH_SERVER = 1,
+    AUTH_NUM_TARGETS = 2,
+  };
 
-   // Describes where the identity used for authentication came from.
-   enum IdentitySource {
-     // Came from nowhere -- the identity is not initialized.
-     IDENT_SRC_NONE,
+  // Describes where the identity used for authentication came from.
+  enum IdentitySource {
+    // Came from nowhere -- the identity is not initialized.
+    IDENT_SRC_NONE,
 
-     // The identity came from the auth cache, by doing a path-based
-     // lookup (premptive authorization).
-     IDENT_SRC_PATH_LOOKUP,
+    // The identity came from the auth cache, by doing a path-based
+    // lookup (premptive authorization).
+    IDENT_SRC_PATH_LOOKUP,
 
-     // The identity was extracted from a URL of the form:
-     // http://<username>:<password>@host:port
-     IDENT_SRC_URL,
+    // The identity was extracted from a URL of the form:
+    // http://<username>:<password>@host:port
+    IDENT_SRC_URL,
 
-     // The identity was retrieved from the auth cache, by doing a
-     // realm lookup.
-     IDENT_SRC_REALM_LOOKUP,
+    // The identity was retrieved from the auth cache, by doing a
+    // realm lookup.
+    IDENT_SRC_REALM_LOOKUP,
 
-     // The identity was provided by RestartWithAuth -- it likely
-     // came from a prompt (or maybe the password manager).
-     IDENT_SRC_EXTERNAL,
-   };
+    // The identity was provided by RestartWithAuth -- it likely
+    // came from a prompt (or maybe the password manager).
+    IDENT_SRC_EXTERNAL,
 
-   // Helper structure used by HttpNetworkTransaction to track
-   // the current identity being used for authorization.
-   struct Identity {
-     Identity() : source(IDENT_SRC_NONE), invalid(true) { }
+    // The identity used the default credentials for the computer,
+    // on schemes that support single sign-on.
+    IDENT_SRC_DEFAULT_CREDENTIALS,
+  };
 
-     IdentitySource source;
-     bool invalid;
-     // TODO(wtc): |username| and |password| should be string16.
-     std::wstring username;
-     std::wstring password;
-   };
+  // Helper structure used by HttpNetworkTransaction to track
+  // the current identity being used for authorization.
+  struct Identity {
+    Identity() : source(IDENT_SRC_NONE), invalid(true) { }
+
+    IdentitySource source;
+    bool invalid;
+    // TODO(wtc): |username| and |password| should be string16.
+    std::wstring username;
+    std::wstring password;
+  };
 
   // Get the name of the header containing the auth challenge
   // (either WWW-Authenticate or Proxy-Authenticate).
@@ -70,13 +80,9 @@
   // (either Authorization or Proxy-Authorization).
   static std::string GetAuthorizationHeaderName(Target target);
 
-  // Create a handler to generate credentials for the challenge, and pass
-  // it back in |*handler|. If the challenge is unsupported or invalid
-  // |*handler| is set to NULL.
-  static void CreateAuthHandler(const std::string& challenge,
-                                Target target,
-                                const GURL& origin,
-                                scoped_refptr<HttpAuthHandler>* handler);
+  // Returns a string representation of a Target value that can be used in log
+  // messages.
+  static std::string GetAuthTargetString(Target target);
 
   // Iterate through the challenge headers, and pick the best one that
   // we support. Obtains the implementation class for handling the challenge,
@@ -85,16 +91,22 @@
   // |*handler| is unchanged. If no supported challenge was found, |*handler|
   // is set to NULL.
   //
+  // |disabled_schemes| is the set of schemes that we should not use.
+  //
   // |origin| is used by the NTLM authentication scheme to construct the
   // service principal name.  It is ignored by other schemes.
   //
   // TODO(wtc): Continuing to use the existing handler in |*handler| (for
   // NTLM) is new behavior.  Rename ChooseBestChallenge to fully encompass
   // what it does now.
-  static void ChooseBestChallenge(const HttpResponseHeaders* headers,
-                                  Target target,
-                                  const GURL& origin,
-                                  scoped_refptr<HttpAuthHandler>* handler);
+  static void ChooseBestChallenge(
+      HttpAuthHandlerFactory* http_auth_handler_factory,
+      const HttpResponseHeaders* headers,
+      Target target,
+      const GURL& origin,
+      const std::set<std::string>& disabled_schemes,
+      const BoundNetLog& net_log,
+      scoped_ptr<HttpAuthHandler>* handler);
 
   // ChallengeTokenizer breaks up a challenge string into the the auth scheme
   // and parameter list, according to RFC 2617 Sec 1.2:
@@ -107,10 +119,16 @@
    public:
     ChallengeTokenizer(std::string::const_iterator begin,
                        std::string::const_iterator end)
-        : props_(begin, end, ','), valid_(true) {
+        : props_(begin, end, ','), valid_(true), begin_(begin), end_(end),
+          expect_base64_token_(false) {
       Init(begin, end);
     }
 
+    // Get the original text.
+    std::string challenge_text() const {
+      return std::string(begin_, end_);
+    }
+
     // Get the auth scheme of the challenge.
     std::string::const_iterator scheme_begin() const { return scheme_begin_; }
     std::string::const_iterator scheme_end() const { return scheme_end_; }
@@ -127,6 +145,17 @@
     // Returns true if there is none to consume.
     bool GetNext();
 
+    // Inform the tokenizer whether the next token should be treated as a base64
+    // encoded value. If |expect_base64_token| is true, |GetNext| will treat the
+    // next token as a base64 encoded value, and will include the trailing '='
+    // padding rather than attempt to split the token into a name/value pair.
+    // In this case, |name| will be empty, and |value| will contain the token.
+    // Subsequent calls to |GetNext()| will not treat the token like a base64
+    // encoded token unless the caller again calls |set_expect_base64_token|.
+    void set_expect_base64_token(bool expect_base64_token) {
+      expect_base64_token_ = expect_base64_token;
+    }
+
     // The name of the current name-value pair.
     std::string::const_iterator name_begin() const { return name_begin_; }
     std::string::const_iterator name_end() const { return name_end_; }
@@ -154,6 +183,9 @@
     HttpUtil::ValuesIterator props_;
     bool valid_;
 
+    std::string::const_iterator begin_;
+    std::string::const_iterator end_;
+
     std::string::const_iterator scheme_begin_;
     std::string::const_iterator scheme_end_;
 
@@ -164,6 +196,7 @@
     std::string::const_iterator value_end_;
 
     bool value_is_quoted_;
+    bool expect_base64_token_;
   };
 };
 
diff --git a/net/http/http_auth_cache.cc b/net/http/http_auth_cache.cc
index 3076a89..21db55d 100644
--- a/net/http/http_auth_cache.cc
+++ b/net/http/http_auth_cache.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -35,8 +35,8 @@
 // |container| an ancestor of |path|?
 bool IsEnclosingPath(const std::string& container, const std::string& path) {
   DCHECK(container.empty() || *(container.end() - 1) == '/');
-  return (container.empty() && path.empty()) ||
-         (!container.empty() && StartsWithASCII(path, container, true));
+  return ((container.empty() && path.empty()) ||
+          (!container.empty() && StartsWithASCII(path, container, true)));
 }
 
 // Debug helper to check that |origin| arguments are properly formed.
@@ -60,13 +60,15 @@
 namespace net {
 
 // Performance: O(n), where n is the number of realm entries.
-HttpAuthCache::Entry* HttpAuthCache::LookupByRealm(const GURL& origin,
-                                                   const std::string& realm) {
+HttpAuthCache::Entry* HttpAuthCache::Lookup(const GURL& origin,
+                                            const std::string& realm,
+                                            const std::string& scheme) {
   CheckOriginIsValid(origin);
 
   // Linear scan through the realm entries.
   for (EntryList::iterator it = entries_.begin(); it != entries_.end(); ++it) {
-    if (it->origin() == origin && it->realm() == realm)
+    if (it->origin() == origin && it->realm() == realm &&
+        it->scheme() == scheme)
       return &(*it);
   }
   return NULL; // No realm entry found.
@@ -95,7 +97,9 @@
 }
 
 HttpAuthCache::Entry* HttpAuthCache::Add(const GURL& origin,
-                                         HttpAuthHandler* handler,
+                                         const std::string& realm,
+                                         const std::string& scheme,
+                                         const std::string& auth_challenge,
                                          const std::wstring& username,
                                          const std::wstring& password,
                                          const std::string& path) {
@@ -103,8 +107,7 @@
   CheckPathIsValid(path);
 
   // Check for existing entry (we will re-use it if present).
-  HttpAuthCache::Entry* entry = LookupByRealm(origin, handler->realm());
-
+  HttpAuthCache::Entry* entry = Lookup(origin, realm, scheme);
   if (!entry) {
     // Failsafe to prevent unbounded memory growth of the cache.
     if (entries_.size() >= kMaxNumRealmEntries) {
@@ -115,11 +118,17 @@
     entries_.push_front(Entry());
     entry = &entries_.front();
     entry->origin_ = origin;
+    entry->realm_ = realm;
+    entry->scheme_ = scheme;
   }
+  DCHECK_EQ(origin, entry->origin_);
+  DCHECK_EQ(realm, entry->realm_);
+  DCHECK_EQ(scheme, entry->scheme_);
 
+  entry->auth_challenge_ = auth_challenge;
   entry->username_ = username;
   entry->password_ = password;
-  entry->handler_ = handler;
+  entry->nonce_count_ = 1;
   entry->AddPath(path);
 
   return entry;
@@ -155,10 +164,12 @@
 
 bool HttpAuthCache::Remove(const GURL& origin,
                            const std::string& realm,
+                           const std::string& scheme,
                            const std::wstring& username,
                            const std::wstring& password) {
   for (EntryList::iterator it = entries_.begin(); it != entries_.end(); ++it) {
-    if (it->origin() == origin && it->realm() == realm) {
+    if (it->origin() == origin && it->realm() == realm &&
+        it->scheme() == scheme) {
       if (username == it->username() && password == it->password()) {
         entries_.erase(it);
         return true;
diff --git a/net/http/http_auth_cache.h b/net/http/http_auth_cache.h
index 62c09e9..1d238af 100644
--- a/net/http/http_auth_cache.h
+++ b/net/http/http_auth_cache.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -10,34 +10,33 @@
 
 #include "base/ref_counted.h"
 #include "googleurl/src/gurl.h"
-#include "net/http/http_auth_handler.h"
 // This is needed for the FRIEND_TEST() macro.
 #include "testing/gtest/include/gtest/gtest_prod.h"
 
 namespace net {
 
-// TODO(eroman): Can we change the key from (origin, realm) to
-// (origin, realm, auth_scheme)?
-
 // HttpAuthCache stores HTTP authentication identities and challenge info.
-// For each realm the cache stores a HttpAuthCache::Entry, which holds:
-//   - the realm name
-//   - the origin server {scheme, host, port}
+// For each (origin, realm, scheme) triple the cache stores a
+// HttpAuthCache::Entry, which holds:
+//   - the origin server {protocol scheme, host, port}
 //   - the last identity used (username/password)
-//   - the last auth handler used
+//   - the last auth handler used (contains realm and authentication scheme)
 //   - the list of paths which used this realm
-// Entries can be looked up by either (origin, realm) or (origin, path).
+// Entries can be looked up by either (origin, realm, scheme) or (origin, path).
 class HttpAuthCache {
  public:
   class Entry;
 
-  // Find the realm entry on server |origin| for realm |realm|.
+  // Find the realm entry on server |origin| for realm |realm| and
+  // scheme |scheme|.
   //   |origin| - the {scheme, host, port} of the server.
   //   |realm|  - case sensitive realm string.
+  //   |scheme| - case sensitive authentication scheme, should be lower-case.
   //   returns  - the matched entry or NULL.
-  Entry* LookupByRealm(const GURL& origin, const std::string& realm);
+  Entry* Lookup(const GURL& origin, const std::string& realm,
+                const std::string& scheme);
 
-  // Find the realm entry on server |origin| whose protection space includes
+  // Find the entry on server |origin| whose protection space includes
   // |path|. This uses the assumption in RFC 2617 section 2 that deeper
   // paths lie in the same protection space.
   //   |origin| - the {scheme, host, port} of the server.
@@ -46,31 +45,37 @@
   //   returns  - the matched entry or NULL.
   Entry* LookupByPath(const GURL& origin, const std::string& path);
 
-  // Add a realm entry on server |origin| for realm |handler->realm()|, If an
-  // entry for this realm already exists, update it rather than replace it --
-  // this  preserves the realm's paths list.
+  // Add an entry on server |origin| for realm |handler->realm()| and
+  // scheme |handler->scheme()|.  If an entry for this (realm,scheme)
+  // already exists, update it rather than replace it -- this  preserves the
+  // paths list.
   //   |origin|   - the {scheme, host, port} of the server.
-  //   |handler|  - handler for the challenge.
+  //   |realm|    - the auth realm for the challenge.
+  //   |scheme|   - the authentication scheme for the challenge.
   //   |username| - login information for the realm.
   //   |password| - login information for the realm.
   //   |path|     - absolute path for a resource contained in the protection
   //                space; this will be added to the list of known paths.
   //   returns    - the entry that was just added/updated.
   Entry* Add(const GURL& origin,
-             HttpAuthHandler* handler,
+             const std::string& realm,
+             const std::string& scheme,
+             const std::string& auth_challenge,
              const std::wstring& username,
              const std::wstring& password,
              const std::string& path);
 
-  // Remove realm entry on server |origin| for realm |realm| if one exists
-  // AND if the cached identity matches (|username|, |password|).
+  // Remove entry on server |origin| for realm |realm| and scheme |scheme|
+  // if one exists AND if the cached identity matches (|username|, |password|).
   //   |origin|   - the {scheme, host, port} of the server.
   //   |realm|    - case sensitive realm string.
+  //   |scheme|   - authentication scheme
   //   |username| - condition to match.
   //   |password| - condition to match.
   //   returns    - true if an entry was removed.
   bool Remove(const GURL& origin,
               const std::string& realm,
+              const std::string& scheme,
               const std::wstring& username,
               const std::wstring& password);
 
@@ -95,24 +100,33 @@
 
   // The case-sensitive realm string of the challenge.
   const std::string realm() const {
-    return handler_->realm();
+    return realm_;
   }
 
-  // The handler for the challenge.
-  HttpAuthHandler* handler() const {
-    return handler_.get();
+  // The authentication scheme string of the challenge
+  const std::string scheme() const {
+    return scheme_;
+  }
+
+  // The authentication challenge.
+  const std::string auth_challenge() const {
+    return auth_challenge_;
   }
 
   // The login username.
-  const std::wstring& username() const {
+  const std::wstring username() const {
     return username_;
   }
 
   // The login password.
-  const std::wstring& password() const {
+  const std::wstring password() const {
     return password_;
   }
 
+  int IncrementNonceCount() {
+    return ++nonce_count_;
+  }
+
  private:
   friend class HttpAuthCache;
   FRIEND_TEST(HttpAuthCacheTest, AddPath);
@@ -129,13 +143,15 @@
 
   // |origin_| contains the {scheme, host, port} of the server.
   GURL origin_;
+  std::string realm_;
+  std::string scheme_;
 
   // Identity.
+  std::string auth_challenge_;
   std::wstring username_;
   std::wstring password_;
 
-  // Auth handler for the challenge.
-  scoped_refptr<HttpAuthHandler> handler_;
+  int nonce_count_;
 
   // List of paths that define the realm's protection space.
   typedef std::list<std::string> PathList;
diff --git a/net/http/http_auth_cache_unittest.cc b/net/http/http_auth_cache_unittest.cc
index 52fa5cd..4e2af9d 100644
--- a/net/http/http_auth_cache_unittest.cc
+++ b/net/http/http_auth_cache_unittest.cc
@@ -1,9 +1,11 @@
-// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/string_util.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_auth_cache.h"
+#include "net/http/http_auth_handler.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -23,18 +25,21 @@
     properties_ = 0;
   }
 
- virtual std::string GenerateCredentials(const std::wstring&,
-                                          const std::wstring&,
-                                          const HttpRequestInfo*,
-                                          const ProxyInfo*) {
-    return "mock-credentials";  // Unused.
-  }
-
  protected:
-  virtual bool Init(std::string::const_iterator, std::string::const_iterator) {
+  virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) {
     return false;  // Unused.
   }
 
+  virtual int GenerateAuthTokenImpl(const std::wstring*,
+                                    const std::wstring*,
+                                    const HttpRequestInfo*,
+                                    CompletionCallback* callback,
+                                    std::string* auth_token) {
+    *auth_token = "mock-credentials";
+    return OK;
+  }
+
+
  private:
   ~MockAuthHandler() {}
 };
@@ -49,68 +54,121 @@
 
   // Add cache entries for 3 realms: "Realm1", "Realm2", "Realm3"
 
-  scoped_refptr<HttpAuthHandler> realm1_handler =
-      new MockAuthHandler("basic", "Realm1", HttpAuth::AUTH_SERVER);
-  cache.Add(origin, realm1_handler, L"realm1-user", L"realm1-password",
-      "/foo/bar/index.html");
+  scoped_ptr<HttpAuthHandler> realm1_handler(
+      new MockAuthHandler("basic", "Realm1", HttpAuth::AUTH_SERVER));
+  cache.Add(origin, realm1_handler->realm(), realm1_handler->scheme(),
+            "Basic realm=Realm1", L"realm1-user", L"realm1-password",
+            "/foo/bar/index.html");
 
-  scoped_refptr<HttpAuthHandler> realm2_handler =
-      new MockAuthHandler("basic", "Realm2", HttpAuth::AUTH_SERVER);
-  cache.Add(origin, realm2_handler, L"realm2-user", L"realm2-password",
-      "/foo2/index.html");
+  scoped_ptr<HttpAuthHandler> realm2_handler(
+      new MockAuthHandler("basic", "Realm2", HttpAuth::AUTH_SERVER));
+  cache.Add(origin, realm2_handler->realm(), realm2_handler->scheme(),
+            "Basic realm=Realm2", L"realm2-user", L"realm2-password",
+            "/foo2/index.html");
 
-  scoped_refptr<HttpAuthHandler> realm3_handler =
-      new MockAuthHandler("basic", "Realm3", HttpAuth::AUTH_PROXY);
-  cache.Add(origin, realm3_handler, L"realm3-user", L"realm3-password", "");
+  scoped_ptr<HttpAuthHandler> realm3_basic_handler(
+      new MockAuthHandler("basic", "Realm3", HttpAuth::AUTH_PROXY));
+  cache.Add(origin, realm3_basic_handler->realm(),
+            realm3_basic_handler->scheme(), "Basic realm=Realm3",
+            L"realm3-basic-user", L"realm3-basic-password", "");
+
+  scoped_ptr<HttpAuthHandler> realm3_digest_handler(
+      new MockAuthHandler("digest", "Realm3", HttpAuth::AUTH_PROXY));
+  cache.Add(origin, realm3_digest_handler->realm(),
+            realm3_digest_handler->scheme(), "Digest realm=Realm3",
+            L"realm3-digest-user", L"realm3-digest-password",
+            "/baz/index.html");
 
   // There is no Realm4
-  entry = cache.LookupByRealm(origin, "Realm4");
+  entry = cache.Lookup(origin, "Realm4", "basic");
   EXPECT_TRUE(NULL == entry);
 
   // While Realm3 does exist, the origin scheme is wrong.
-  entry = cache.LookupByRealm(GURL("https://www.google.com"), "Realm3");
+  entry = cache.Lookup(GURL("https://www.google.com"), "Realm3",
+                       "basic");
   EXPECT_TRUE(NULL == entry);
 
-  // Valid lookup by realm.
-  entry = cache.LookupByRealm(GURL("http://www.google.com:80"), "Realm3");
-  EXPECT_FALSE(NULL == entry);
-  EXPECT_TRUE(entry->handler() == realm3_handler.get());
-  EXPECT_EQ(L"realm3-user", entry->username());
-  EXPECT_EQ(L"realm3-password", entry->password());
+  // Realm, origin scheme ok, authentication scheme wrong
+  entry = cache.Lookup(GURL("http://www.google.com"), "Realm1", "digest");
+  EXPECT_TRUE(NULL == entry);
+
+  // Valid lookup by origin, realm, scheme.
+  entry = cache.Lookup(GURL("http://www.google.com:80"), "Realm3", "basic");
+  ASSERT_FALSE(NULL == entry);
+  EXPECT_EQ("basic", entry->scheme());
+  EXPECT_EQ("Realm3", entry->realm());
+  EXPECT_EQ("Basic realm=Realm3", entry->auth_challenge());
+  EXPECT_EQ(L"realm3-basic-user", entry->username());
+  EXPECT_EQ(L"realm3-basic-password", entry->password());
+
+  // Valid lookup by origin, realm, scheme when there's a duplicate
+  // origin, realm in the cache
+  entry = cache.Lookup(GURL("http://www.google.com:80"), "Realm3", "digest");
+  ASSERT_FALSE(NULL == entry);
+  EXPECT_EQ("digest", entry->scheme());
+  EXPECT_EQ("Realm3", entry->realm());
+  EXPECT_EQ("Digest realm=Realm3", entry->auth_challenge());
+  EXPECT_EQ(L"realm3-digest-user", entry->username());
+  EXPECT_EQ(L"realm3-digest-password", entry->password());
 
   // Valid lookup by realm.
-  entry = cache.LookupByRealm(origin, "Realm2");
-  EXPECT_FALSE(NULL == entry);
-  EXPECT_TRUE(entry->handler() == realm2_handler.get());
+  entry = cache.Lookup(origin, "Realm2", "basic");
+  ASSERT_FALSE(NULL == entry);
+  EXPECT_EQ("basic", entry->scheme());
+  EXPECT_EQ("Realm2", entry->realm());
+  EXPECT_EQ("Basic realm=Realm2", entry->auth_challenge());
   EXPECT_EQ(L"realm2-user", entry->username());
   EXPECT_EQ(L"realm2-password", entry->password());
 
   // Check that subpaths are recognized.
-  HttpAuthCache::Entry* realm2Entry = cache.LookupByRealm(origin, "Realm2");
-  EXPECT_FALSE(NULL == realm2Entry);
+  HttpAuthCache::Entry* realm2_entry = cache.Lookup(origin, "Realm2", "basic");
+  EXPECT_FALSE(NULL == realm2_entry);
   // Positive tests:
   entry = cache.LookupByPath(origin, "/foo2/index.html");
-  EXPECT_TRUE(realm2Entry == entry);
+  EXPECT_TRUE(realm2_entry == entry);
   entry = cache.LookupByPath(origin, "/foo2/foobar.html");
-  EXPECT_TRUE(realm2Entry == entry);
+  EXPECT_TRUE(realm2_entry == entry);
   entry = cache.LookupByPath(origin, "/foo2/bar/index.html");
-  EXPECT_TRUE(realm2Entry == entry);
+  EXPECT_TRUE(realm2_entry == entry);
   entry = cache.LookupByPath(origin, "/foo2/");
-  EXPECT_TRUE(realm2Entry == entry);
+  EXPECT_TRUE(realm2_entry == entry);
+
   // Negative tests:
   entry = cache.LookupByPath(origin, "/foo2");
-  EXPECT_FALSE(realm2Entry == entry);
+  EXPECT_FALSE(realm2_entry == entry);
   entry = cache.LookupByPath(origin, "/foo3/index.html");
-  EXPECT_FALSE(realm2Entry == entry);
+  EXPECT_FALSE(realm2_entry == entry);
   entry = cache.LookupByPath(origin, "");
-  EXPECT_FALSE(realm2Entry == entry);
+  EXPECT_FALSE(realm2_entry == entry);
   entry = cache.LookupByPath(origin, "/");
-  EXPECT_FALSE(realm2Entry == entry);
+  EXPECT_FALSE(realm2_entry == entry);
+
+  // Confirm we find the same realm, different auth scheme by path lookup
+  HttpAuthCache::Entry* realm3_digest_entry =
+      cache.Lookup(origin, "Realm3", "digest");
+  EXPECT_FALSE(NULL == realm3_digest_entry);
+  entry = cache.LookupByPath(origin, "/baz/index.html");
+  EXPECT_TRUE(realm3_digest_entry == entry);
+  entry = cache.LookupByPath(origin, "/baz/");
+  EXPECT_TRUE(realm3_digest_entry == entry);
+  entry = cache.LookupByPath(origin, "/baz");
+  EXPECT_FALSE(realm3_digest_entry == entry);
+
+  // Confirm we find the same realm, different auth scheme by path lookup
+  HttpAuthCache::Entry* realm3DigestEntry =
+      cache.Lookup(origin, "Realm3", "digest");
+  EXPECT_FALSE(NULL == realm3DigestEntry);
+  entry = cache.LookupByPath(origin, "/baz/index.html");
+  EXPECT_TRUE(realm3DigestEntry == entry);
+  entry = cache.LookupByPath(origin, "/baz/");
+  EXPECT_TRUE(realm3DigestEntry == entry);
+  entry = cache.LookupByPath(origin, "/baz");
+  EXPECT_FALSE(realm3DigestEntry == entry);
 
   // Lookup using empty path (may be used for proxy).
   entry = cache.LookupByPath(origin, "");
   EXPECT_FALSE(NULL == entry);
-  EXPECT_TRUE(entry->handler() == realm3_handler.get());
+  EXPECT_EQ("basic", entry->scheme());
   EXPECT_EQ("Realm3", entry->realm());
 }
 
@@ -151,16 +209,20 @@
 TEST(HttpAuthCacheTest, AddToExistingEntry) {
   HttpAuthCache cache;
   GURL origin("http://www.foobar.com:70");
+  const std::string auth_challenge = "Basic realm=MyRealm";
 
-  scoped_refptr<HttpAuthHandler> handler =
-      new MockAuthHandler("basic", "MyRealm", HttpAuth::AUTH_SERVER);
+  scoped_ptr<HttpAuthHandler> handler(
+      new MockAuthHandler("basic", "MyRealm", HttpAuth::AUTH_SERVER));
 
   HttpAuthCache::Entry* orig_entry = cache.Add(
-      origin, handler, L"user1", L"password1", "/x/y/z/");
-  cache.Add(origin, handler, L"user2", L"password2", "/z/y/x/");
-  cache.Add(origin, handler, L"user3", L"password3", "/z/y");
+      origin, handler->realm(), handler->scheme(), auth_challenge,
+      L"user1", L"password1", "/x/y/z/");
+  cache.Add(origin, handler->realm(), handler->scheme(), auth_challenge,
+            L"user2", L"password2", "/z/y/x/");
+  cache.Add(origin, handler->realm(), handler->scheme(), auth_challenge,
+            L"user3", L"password3", "/z/y");
 
-  HttpAuthCache::Entry* entry = cache.LookupByRealm(origin, "MyRealm");
+  HttpAuthCache::Entry* entry = cache.Lookup(origin, "MyRealm", "basic");
 
   EXPECT_TRUE(entry == orig_entry);
   EXPECT_EQ(L"user3", entry->username());
@@ -174,38 +236,65 @@
 TEST(HttpAuthCacheTest, Remove) {
   GURL origin("http://foobar2.com");
 
-  scoped_refptr<HttpAuthHandler> realm1_handler =
-      new MockAuthHandler("basic", "Realm1", HttpAuth::AUTH_SERVER);
+  scoped_ptr<HttpAuthHandler> realm1_handler(
+      new MockAuthHandler("basic", "Realm1", HttpAuth::AUTH_SERVER));
 
-  scoped_refptr<HttpAuthHandler> realm2_handler =
-      new MockAuthHandler("basic", "Realm2", HttpAuth::AUTH_SERVER);
+  scoped_ptr<HttpAuthHandler> realm2_handler(
+      new MockAuthHandler("basic", "Realm2", HttpAuth::AUTH_SERVER));
 
-  scoped_refptr<HttpAuthHandler> realm3_handler =
-      new MockAuthHandler("basic", "Realm3", HttpAuth::AUTH_SERVER);
+  scoped_ptr<HttpAuthHandler> realm3_basic_handler(
+      new MockAuthHandler("basic", "Realm3", HttpAuth::AUTH_SERVER));
+
+  scoped_ptr<HttpAuthHandler> realm3_digest_handler(
+      new MockAuthHandler("digest", "Realm3", HttpAuth::AUTH_SERVER));
 
   HttpAuthCache cache;
-  cache.Add(origin, realm1_handler, L"alice", L"123", "/");
-  cache.Add(origin, realm2_handler, L"bob", L"princess", "/");
-  cache.Add(origin, realm3_handler, L"admin", L"password", "/");
+  cache.Add(origin, realm1_handler->realm(), realm1_handler->scheme(),
+            "basic realm=Realm1", L"alice", L"123", "/");
+  cache.Add(origin, realm2_handler->realm(), realm2_handler->scheme(),
+            "basic realm=Realm2", L"bob", L"princess", "/");
+  cache.Add(origin, realm3_basic_handler->realm(),
+            realm3_basic_handler->scheme(), "basic realm=Realm3", L"admin",
+            L"password", "/");
+  cache.Add(origin, realm3_digest_handler->realm(),
+            realm3_digest_handler->scheme(), "digest realm=Realm3", L"root",
+            L"wilecoyote", "/");
 
   // Fails, because there is no realm "Realm4".
-  EXPECT_FALSE(cache.Remove(origin, "Realm4", L"alice", L"123"));
+  EXPECT_FALSE(cache.Remove(origin, "Realm4", "basic", L"alice", L"123"));
 
   // Fails because the origin is wrong.
   EXPECT_FALSE(cache.Remove(
-      GURL("http://foobar2.com:100"), "Realm1", L"alice", L"123"));
+      GURL("http://foobar2.com:100"), "Realm1", "basic", L"alice", L"123"));
 
   // Fails because the username is wrong.
-  EXPECT_FALSE(cache.Remove(origin, "Realm1", L"alice2", L"123"));
+  EXPECT_FALSE(cache.Remove(origin, "Realm1", "basic", L"alice2", L"123"));
 
   // Fails because the password is wrong.
-  EXPECT_FALSE(cache.Remove(origin, "Realm1", L"alice", L"1234"));
+  EXPECT_FALSE(cache.Remove(origin, "Realm1", "basic", L"alice", L"1234"));
+
+  // Fails because the authentication type is wrong.
+  EXPECT_FALSE(cache.Remove(origin, "Realm1", "digest", L"alice", L"123"));
 
   // Succeeds.
-  EXPECT_TRUE(cache.Remove(origin, "Realm1", L"alice", L"123"));
+  EXPECT_TRUE(cache.Remove(origin, "Realm1", "basic", L"alice", L"123"));
 
   // Fails because we just deleted the entry!
-  EXPECT_FALSE(cache.Remove(origin, "Realm1", L"alice", L"123"));
+  EXPECT_FALSE(cache.Remove(origin, "Realm1", "basic", L"alice", L"123"));
+
+  // Succeed when there are two authentication types for the same origin,realm.
+  EXPECT_TRUE(cache.Remove(origin, "Realm3", "digest", L"root", L"wilecoyote"));
+
+  // Succeed as above, but when entries were added in opposite order
+  cache.Add(origin, realm3_digest_handler->realm(),
+            realm3_digest_handler->scheme(), "digest realm=Realm3", L"root",
+            L"wilecoyote", "/");
+  EXPECT_TRUE(cache.Remove(origin, "Realm3", "basic", L"admin", L"password"));
+
+  // Make sure that removing one entry still leaves the other available for
+  // lookup.
+  HttpAuthCache::Entry* entry = cache.Lookup(origin, "Realm3", "digest");
+  EXPECT_FALSE(NULL == entry);
 }
 
 // Test fixture class for eviction tests (contains helpers for bulk
@@ -227,15 +316,13 @@
   }
 
   void AddPathToRealm(int realm_i, int path_i) {
-    scoped_refptr<HttpAuthHandler> handler = new MockAuthHandler("basic",
-        GenerateRealm(realm_i), HttpAuth::AUTH_SERVER);
-    std::string path = GeneratePath(realm_i, path_i);
-    cache_.Add(origin_, handler, L"username", L"password", path);
+    cache_.Add(origin_, GenerateRealm(realm_i), "basic", "",
+               L"username", L"password", GeneratePath(realm_i, path_i));
   }
 
   void CheckRealmExistence(int realm_i, bool exists) {
     const HttpAuthCache::Entry* entry =
-        cache_.LookupByRealm(origin_, GenerateRealm(realm_i));
+        cache_.Lookup(origin_, GenerateRealm(realm_i), "basic");
     if (exists) {
       EXPECT_FALSE(entry == NULL);
       EXPECT_EQ(GenerateRealm(realm_i), entry->realm());
diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc
new file mode 100644
index 0000000..c077ee8
--- /dev/null
+++ b/net/http/http_auth_controller.cc
@@ -0,0 +1,363 @@
+// Copyright (c) 2010 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/http/http_auth_controller.h"
+
+#include "base/string_util.h"
+#include "net/base/host_resolver.h"
+#include "net/base/net_util.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_request_info.h"
+
+namespace net {
+
+namespace {
+
+// Returns a log message for all the response headers related to the auth
+// challenge.
+std::string AuthChallengeLogMessage(HttpResponseHeaders* headers) {
+  std::string msg;
+  std::string header_val;
+  void* iter = NULL;
+  while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) {
+    msg.append("\n  Has header Proxy-Authenticate: ");
+    msg.append(header_val);
+  }
+
+  iter = NULL;
+  while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) {
+    msg.append("\n  Has header WWW-Authenticate: ");
+    msg.append(header_val);
+  }
+
+  // RFC 4559 requires that a proxy indicate its support of NTLM/Negotiate
+  // authentication with a "Proxy-Support: Session-Based-Authentication"
+  // response header.
+  iter = NULL;
+  while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) {
+    msg.append("\n  Has header Proxy-Support: ");
+    msg.append(header_val);
+  }
+
+  return msg;
+}
+
+}  // namespace
+
+HttpAuthController::HttpAuthController(
+    HttpAuth::Target target,
+    const GURL& auth_url,
+    scoped_refptr<HttpNetworkSession> session)
+    : target_(target),
+      auth_url_(auth_url),
+      auth_origin_(auth_url.GetOrigin()),
+      auth_path_(HttpAuth::AUTH_PROXY ? std::string() : auth_url.path()),
+      embedded_identity_used_(false),
+      default_credentials_used_(false),
+      session_(session),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          io_callback_(this, &HttpAuthController::OnIOComplete)),
+      user_callback_(NULL) {
+}
+
+HttpAuthController::~HttpAuthController() {
+  user_callback_ = NULL;
+}
+
+int HttpAuthController::MaybeGenerateAuthToken(const HttpRequestInfo* request,
+                                               CompletionCallback* callback,
+                                               const BoundNetLog& net_log) {
+  bool needs_auth = HaveAuth() || SelectPreemptiveAuth(net_log);
+  if (!needs_auth)
+    return OK;
+  const std::wstring* username = NULL;
+  const std::wstring* password = NULL;
+  if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS) {
+    username = &identity_.username;
+    password = &identity_.password;
+  }
+  DCHECK(auth_token_.empty());
+  DCHECK(NULL == user_callback_);
+  int rv = handler_->GenerateAuthToken(username,
+                                       password,
+                                       request,
+                                       &io_callback_,
+                                       &auth_token_);
+  if (rv == ERR_IO_PENDING)
+    user_callback_ = callback;
+  else
+    OnIOComplete(rv);
+  // This error occurs with GSSAPI, if the user has not already logged in.
+  if (rv == ERR_MISSING_AUTH_CREDENTIALS)
+    rv = OK;
+  return rv;
+}
+
+bool HttpAuthController::SelectPreemptiveAuth(const BoundNetLog& net_log) {
+  DCHECK(!HaveAuth());
+  DCHECK(identity_.invalid);
+
+  // Don't do preemptive authorization if the URL contains a username/password,
+  // since we must first be challenged in order to use the URL's identity.
+  if (auth_url_.has_username())
+    return false;
+
+  // SelectPreemptiveAuth() is on the critical path for each request, so it
+  // is expected to be fast. LookupByPath() is fast in the common case, since
+  // the number of http auth cache entries is expected to be very small.
+  // (For most users in fact, it will be 0.)
+  HttpAuthCache::Entry* entry = session_->auth_cache()->LookupByPath(
+      auth_origin_, auth_path_);
+  if (!entry)
+    return false;
+
+  // Try to create a handler using the previous auth challenge.
+  scoped_ptr<HttpAuthHandler> handler_preemptive;
+  int rv_create = session_->http_auth_handler_factory()->
+      CreatePreemptiveAuthHandlerFromString(entry->auth_challenge(), target_,
+                                            auth_origin_,
+                                            entry->IncrementNonceCount(),
+                                            net_log, &handler_preemptive);
+  if (rv_create != OK)
+    return false;
+
+  // Set the state
+  identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
+  identity_.invalid = false;
+  identity_.username = entry->username();
+  identity_.password = entry->password();
+  handler_.swap(handler_preemptive);
+  return true;
+}
+
+void HttpAuthController::AddAuthorizationHeader(
+    HttpRequestHeaders* authorization_headers) {
+  DCHECK(HaveAuth());
+  authorization_headers->SetHeader(
+      HttpAuth::GetAuthorizationHeaderName(target_), auth_token_);
+  auth_token_.clear();
+}
+
+int HttpAuthController::HandleAuthChallenge(
+    scoped_refptr<HttpResponseHeaders> headers,
+    bool do_not_send_server_auth,
+    bool establishing_tunnel,
+    const BoundNetLog& net_log) {
+  DCHECK(headers);
+  DCHECK(auth_origin_.is_valid());
+
+  LOG(INFO) << "The " << HttpAuth::GetAuthTargetString(target_) << " "
+            << auth_origin_ << " requested auth"
+            << AuthChallengeLogMessage(headers.get());
+
+  // The auth we tried just failed, hence it can't be valid. Remove it from
+  // the cache so it won't be used again.
+  // TODO(wtc): IsFinalRound is not the right condition.  In a multi-round
+  // auth sequence, the server may fail the auth in round 1 if our first
+  // authorization header is broken.  We should inspect response_.headers to
+  // determine if the server already failed the auth or wants us to continue.
+  // See http://crbug.com/21015.
+  if (HaveAuth() && handler_->IsFinalRound()) {
+    InvalidateRejectedAuthFromCache();
+    handler_.reset();
+    identity_ = HttpAuth::Identity();
+  }
+
+  identity_.invalid = true;
+
+  if (target_ != HttpAuth::AUTH_SERVER || !do_not_send_server_auth) {
+    // Find the best authentication challenge that we support.
+    HttpAuth::ChooseBestChallenge(session_->http_auth_handler_factory(),
+                                  headers, target_, auth_origin_,
+                                  disabled_schemes_, net_log,
+                                  &handler_);
+  }
+
+  if (!handler_.get()) {
+    if (establishing_tunnel) {
+      LOG(ERROR) << "Can't perform auth to the "
+                 << HttpAuth::GetAuthTargetString(target_) << " "
+                 << auth_origin_ << " when establishing a tunnel"
+                 << AuthChallengeLogMessage(headers.get());
+
+      // We are establishing a tunnel, we can't show the error page because an
+      // active network attacker could control its contents.  Instead, we just
+      // fail to establish the tunnel.
+      DCHECK(target_ == HttpAuth::AUTH_PROXY);
+      return ERR_PROXY_AUTH_UNSUPPORTED;
+    }
+    // We found no supported challenge -- let the transaction continue
+    // so we end up displaying the error page.
+    return OK;
+  }
+
+  if (handler_->NeedsIdentity()) {
+    // Pick a new auth identity to try, by looking to the URL and auth cache.
+    // If an identity to try is found, it is saved to identity_.
+    SelectNextAuthIdentityToTry();
+  } else {
+    // Proceed with the existing identity or a null identity.
+    //
+    // TODO(wtc): Add a safeguard against infinite transaction restarts, if
+    // the server keeps returning "NTLM".
+    identity_.invalid = false;
+  }
+
+  // From this point on, we are restartable.
+
+  if (identity_.invalid) {
+    // We have exhausted all identity possibilities, all we can do now is
+    // pass the challenge information back to the client.
+    PopulateAuthChallenge();
+  } else {
+    auth_info_ = NULL;
+  }
+
+  return OK;
+}
+
+void HttpAuthController::ResetAuth(const std::wstring& username,
+                                   const std::wstring& password) {
+  DCHECK(identity_.invalid || (username.empty() && password.empty()));
+
+  if (identity_.invalid) {
+    // Update the username/password.
+    identity_.source = HttpAuth::IDENT_SRC_EXTERNAL;
+    identity_.invalid = false;
+    identity_.username = username;
+    identity_.password = password;
+  }
+
+  DCHECK(identity_.source != HttpAuth::IDENT_SRC_PATH_LOOKUP);
+
+  // Add the auth entry to the cache before restarting. We don't know whether
+  // the identity is valid yet, but if it is valid we want other transactions
+  // to know about it. If an entry for (origin, handler->realm()) already
+  // exists, we update it.
+  //
+  // If identity_.source is HttpAuth::IDENT_SRC_NONE or
+  // HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS, identity_ contains no
+  // identity because identity is not required yet or we're using default
+  // credentials.
+  //
+  // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in
+  // round 1 and round 2, which is redundant but correct.  It would be nice
+  // to add an auth entry to the cache only once, preferrably in round 1.
+  // See http://crbug.com/21015.
+  switch (identity_.source) {
+    case HttpAuth::IDENT_SRC_NONE:
+    case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
+      break;
+    default:
+      session_->auth_cache()->Add(auth_origin_, handler_->realm(),
+                                  handler_->scheme(), handler_->challenge(),
+                                  identity_.username, identity_.password,
+                                  auth_path_);
+      break;
+  }
+}
+
+void HttpAuthController::InvalidateRejectedAuthFromCache() {
+  DCHECK(HaveAuth());
+
+  // TODO(eroman): this short-circuit can be relaxed. If the realm of
+  // the preemptively used auth entry matches the realm of the subsequent
+  // challenge, then we can invalidate the preemptively used entry.
+  // Otherwise as-is we may send the failed credentials one extra time.
+  if (identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP)
+    return;
+
+  // Clear the cache entry for the identity we just failed on.
+  // Note: we require the username/password to match before invalidating
+  // since the entry in the cache may be newer than what we used last time.
+  session_->auth_cache()->Remove(auth_origin_, handler_->realm(),
+                                 handler_->scheme(), identity_.username,
+                                 identity_.password);
+}
+
+bool HttpAuthController::SelectNextAuthIdentityToTry() {
+  DCHECK(handler_.get());
+  DCHECK(identity_.invalid);
+
+  // Try to use the username/password encoded into the URL first.
+  if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() &&
+      !embedded_identity_used_) {
+    identity_.source = HttpAuth::IDENT_SRC_URL;
+    identity_.invalid = false;
+    // Extract the username:password from the URL.
+    GetIdentityFromURL(auth_url_,
+                       &identity_.username,
+                       &identity_.password);
+    embedded_identity_used_ = true;
+    // TODO(eroman): If the password is blank, should we also try combining
+    // with a password from the cache?
+    return true;
+  }
+
+  // Check the auth cache for a realm entry.
+  HttpAuthCache::Entry* entry =
+    session_->auth_cache()->Lookup(auth_origin_, handler_->realm(),
+                                   handler_->scheme());
+
+  if (entry) {
+    identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
+    identity_.invalid = false;
+    identity_.username = entry->username();
+    identity_.password = entry->password();
+    return true;
+  }
+
+  // Use default credentials (single sign on) if this is the first attempt
+  // at identity.  Do not allow multiple times as it will infinite loop.
+  // We use default credentials after checking the auth cache so that if
+  // single sign-on doesn't work, we won't try default credentials for future
+  // transactions.
+  if (!default_credentials_used_ && handler_->AllowsDefaultCredentials()) {
+    identity_.source = HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS;
+    identity_.invalid = false;
+    default_credentials_used_ = true;
+    return true;
+  }
+
+  return false;
+}
+
+void HttpAuthController::PopulateAuthChallenge() {
+  // Populates response_.auth_challenge with the authentication challenge info.
+  // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo().
+
+  auth_info_ = new AuthChallengeInfo;
+  auth_info_->is_proxy = target_ == HttpAuth::AUTH_PROXY;
+  auth_info_->host_and_port = ASCIIToWide(GetHostAndPort(auth_origin_));
+  auth_info_->scheme = ASCIIToWide(handler_->scheme());
+  // TODO(eroman): decode realm according to RFC 2047.
+  auth_info_->realm = ASCIIToWide(handler_->realm());
+}
+
+void HttpAuthController::OnIOComplete(int result) {
+  // This error occurs with GSSAPI, if the user has not already logged in.
+  // In that case, disable the current scheme as it cannot succeed.
+  if (result == ERR_MISSING_AUTH_CREDENTIALS) {
+    DisableAuthScheme(handler_->scheme());
+    auth_token_.clear();
+    result = OK;
+  }
+  if (user_callback_) {
+    CompletionCallback* c = user_callback_;
+    user_callback_ = NULL;
+    c->Run(result);
+  }
+}
+
+bool HttpAuthController::IsAuthSchemeDisabled(const std::string& scheme) const {
+  return disabled_schemes_.find(scheme) != disabled_schemes_.end();
+}
+
+void HttpAuthController::DisableAuthScheme(const std::string& scheme) {
+  disabled_schemes_.insert(scheme);
+}
+
+}  // namespace net
diff --git a/net/http/http_auth_controller.h b/net/http/http_auth_controller.h
new file mode 100644
index 0000000..9bc8d59
--- /dev/null
+++ b/net/http/http_auth_controller.h
@@ -0,0 +1,149 @@
+// Copyright (c) 2010 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_HTTP_HTTP_AUTH_CONTROLLER_H_
+#define NET_HTTP_HTTP_AUTH_CONTROLLER_H_
+
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_log.h"
+#include "net/http/http_auth.h"
+
+namespace net {
+
+class AuthChallengeInfo;
+class HostResolver;
+class HttpAuthHandler;
+class HttpNetworkSession;
+class HttpRequestHeaders;
+struct HttpRequestInfo;
+
+class HttpAuthController : public base::RefCounted<HttpAuthController> {
+ public:
+  // The arguments are self explanatory except possibly for |auth_url|, which
+  // should be both the auth target and auth path in a single url argument.
+  HttpAuthController(HttpAuth::Target target, const GURL& auth_url,
+                     scoped_refptr<HttpNetworkSession> session);
+
+  // Generate an authentication token for |target| if necessary. The return
+  // value is a net error code. |OK| will be returned both in the case that
+  // a token is correctly generated synchronously, as well as when no tokens
+  // were necessary.
+  virtual int MaybeGenerateAuthToken(const HttpRequestInfo* request,
+                                     CompletionCallback* callback,
+                                     const BoundNetLog& net_log);
+
+  // Adds either the proxy auth header, or the origin server auth header,
+  // as specified by |target_|.
+  virtual void AddAuthorizationHeader(
+      HttpRequestHeaders* authorization_headers);
+
+  // Checks for and handles HTTP status code 401 or 407.
+  // |HandleAuthChallenge()| returns OK on success, or a network error code
+  // otherwise. It may also populate |auth_info_|.
+  virtual int HandleAuthChallenge(scoped_refptr<HttpResponseHeaders> headers,
+                                  bool do_not_send_server_auth,
+                                  bool establishing_tunnel,
+                                  const BoundNetLog& net_log);
+
+  // Store the supplied credentials and prepare to restart the auth.
+  virtual void ResetAuth(const std::wstring& username,
+                         const std::wstring& password);
+
+  virtual bool HaveAuthHandler() const {
+    return handler_.get() != NULL;
+  }
+
+  virtual bool HaveAuth() const {
+    return handler_.get() && !identity_.invalid;
+  }
+
+  virtual scoped_refptr<AuthChallengeInfo> auth_info() {
+    return auth_info_;
+  }
+
+  virtual bool IsAuthSchemeDisabled(const std::string& scheme) const;
+  virtual void DisableAuthScheme(const std::string& scheme);
+
+ protected:  // So that we can mock this object.
+  friend class base::RefCounted<HttpAuthController>;
+  virtual ~HttpAuthController();
+
+ private:
+  // Searches the auth cache for an entry that encompasses the request's path.
+  // If such an entry is found, updates |identity_| and |handler_| with the
+  // cache entry's data and returns true.
+  bool SelectPreemptiveAuth(const BoundNetLog& net_log);
+
+  // Invalidates any auth cache entries after authentication has failed.
+  // The identity that was rejected is |identity_|.
+  void InvalidateRejectedAuthFromCache();
+
+  // Sets |identity_| to the next identity that the transaction should try. It
+  // chooses candidates by searching the auth cache and the URL for a
+  // username:password. Returns true if an identity was found.
+  bool SelectNextAuthIdentityToTry();
+
+  // Populates auth_info_ with the challenge information, so that
+  // URLRequestHttpJob can prompt for a username/password.
+  void PopulateAuthChallenge();
+
+  void OnIOComplete(int result);
+
+  // Indicates if this handler is for Proxy auth or Server auth.
+  HttpAuth::Target target_;
+
+  // Holds the {scheme, host, path, port} for the authentication target.
+  const GURL auth_url_;
+
+  // Holds the {scheme, host, port} for the authentication target.
+  const GURL auth_origin_;
+
+  // The absolute path of the resource needing authentication.
+  // For proxy authentication the path is empty.
+  const std::string auth_path_;
+
+  // |handler_| encapsulates the logic for the particular auth-scheme.
+  // This includes the challenge's parameters. If NULL, then there is no
+  // associated auth handler.
+  scoped_ptr<HttpAuthHandler> handler_;
+
+  // |identity_| holds the (username/password) that should be used by
+  // the handler_ to generate credentials. This identity can come from
+  // a number of places (url, cache, prompt).
+  HttpAuth::Identity identity_;
+
+  // |auth_token_| contains the opaque string to pass to the proxy or
+  // server to authenticate the client.
+  std::string auth_token_;
+
+  // Contains information about the auth challenge.
+  scoped_refptr<AuthChallengeInfo> auth_info_;
+
+  // True if we've used the username/password embedded in the URL.  This
+  // makes sure we use the embedded identity only once for the transaction,
+  // preventing an infinite auth restart loop.
+  bool embedded_identity_used_;
+
+  // True if default credentials have already been tried for this transaction
+  // in response to an HTTP authentication challenge.
+  bool default_credentials_used_;
+
+  scoped_refptr<HttpNetworkSession> session_;
+
+  std::set<std::string> disabled_schemes_;
+
+  CompletionCallbackImpl<HttpAuthController> io_callback_;
+  CompletionCallback* user_callback_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_AUTH_CONTROLLER_H_
diff --git a/net/http/http_auth_filter.cc b/net/http/http_auth_filter.cc
new file mode 100644
index 0000000..80d6e0c
--- /dev/null
+++ b/net/http/http_auth_filter.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 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/http/http_auth_filter.h"
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+
+namespace net {
+
+// Using a std::set<> has the benefit of removing duplicates automatically.
+typedef std::set<string16> RegistryWhitelist;
+
+// TODO(ahendrickson) -- Determine if we want separate whitelists for HTTP and
+// HTTPS, one for both, or only an HTTP one.  My understanding is that the HTTPS
+// entries in the registry mean that you are only allowed to connect to the site
+// via HTTPS and still be considered 'safe'.
+
+HttpAuthFilterWhitelist::HttpAuthFilterWhitelist() {
+}
+
+HttpAuthFilterWhitelist::~HttpAuthFilterWhitelist() {
+}
+
+void HttpAuthFilterWhitelist::SetWhitelist(
+    const std::string& server_whitelist) {
+  rules_.ParseFromString(server_whitelist);
+}
+
+bool HttpAuthFilterWhitelist::IsValid(const GURL& url,
+                                      HttpAuth::Target target) const {
+  if ((target != HttpAuth::AUTH_SERVER) && (target != HttpAuth::AUTH_PROXY))
+    return false;
+  // All proxies pass
+  if (target == HttpAuth::AUTH_PROXY)
+    return true;
+  return rules_.Matches(url);
+}
+
+// Add a new domain |filter| to the whitelist, if it's not already there
+bool HttpAuthFilterWhitelist::AddFilter(const std::string& filter,
+                                        HttpAuth::Target target) {
+  if ((target != HttpAuth::AUTH_SERVER) && (target != HttpAuth::AUTH_PROXY))
+    return false;
+  // All proxies pass
+  if (target == HttpAuth::AUTH_PROXY)
+    return true;
+  rules_.AddRuleFromString(filter);
+  return true;
+}
+
+void HttpAuthFilterWhitelist::AddRuleToBypassLocal() {
+  rules_.AddRuleToBypassLocal();
+}
+
+}  // namespace net
diff --git a/net/http/http_auth_filter.h b/net/http/http_auth_filter.h
new file mode 100644
index 0000000..8a2524c
--- /dev/null
+++ b/net/http/http_auth_filter.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2010 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_HTTP_HTTP_AUTH_FILTER_H_
+#define NET_HTTP_HTTP_AUTH_FILTER_H_
+
+#include <list>
+#include <set>
+#include <string>
+
+#include "base/string_util.h"
+#include "net/http/http_auth.h"
+#include "net/proxy/proxy_bypass_rules.h"
+
+class GURL;
+
+namespace net {
+
+// |HttpAuthFilter|s determine whether an authentication scheme should be
+// allowed for a particular peer.
+class HttpAuthFilter {
+ public:
+  virtual ~HttpAuthFilter() {}
+
+  // Checks if (|url|, |target|) is supported by the authentication scheme.
+  // Only the host of |url| is examined.
+  virtual bool IsValid(const GURL& url, HttpAuth::Target target) const = 0;
+};
+
+// Whitelist HTTP authentication filter.
+// Explicit whitelists of domains are set via SetWhitelist().
+//
+// Uses the ProxyBypassRules class to do whitelisting for servers.
+// All proxies are allowed.
+class HttpAuthFilterWhitelist : public HttpAuthFilter {
+ public:
+  HttpAuthFilterWhitelist();
+  virtual ~HttpAuthFilterWhitelist();
+
+  // HttpAuthFilter methods:
+  virtual bool IsValid(const GURL& url, HttpAuth::Target target) const;
+
+  // Installs the whitelist.
+  // |server_whitelist| is parsed by ProxyBypassRules.
+  void SetWhitelist(const std::string& server_whitelist);
+
+  // Adds an individual URL |filter| to the list, of the specified |target|.
+  bool AddFilter(const std::string& filter, HttpAuth::Target target);
+
+  // Adds a rule that bypasses all "local" hostnames.
+  void AddRuleToBypassLocal();
+
+  const ProxyBypassRules& rules() const { return rules_; }
+
+ private:
+  // We are using ProxyBypassRules because they have the functionality that we
+  // want, but we are not using it for proxy bypass.
+  ProxyBypassRules rules_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpAuthFilterWhitelist);
+};
+
+}   // namespace net
+
+#endif  // NET_HTTP_HTTP_AUTH_FILTER_H_
diff --git a/net/http/http_auth_filter_unittest.cc b/net/http/http_auth_filter_unittest.cc
new file mode 100644
index 0000000..df61e14
--- /dev/null
+++ b/net/http/http_auth_filter_unittest.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2010 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 <iostream>
+
+#include "base/logging.h"
+
+#include "base/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
+#include "net/http/http_auth_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+static const char* const server_whitelist_array[] = {
+  "google.com",
+  "linkedin.com",
+  "book.com",
+  ".chromium.org",
+  ".gag",
+  "gog"
+};
+
+enum {
+  ALL_SERVERS_MATCH = (1 << arraysize(server_whitelist_array)) - 1
+};
+
+struct UrlData {
+  GURL url;
+  HttpAuth::Target target;
+  bool matches;
+  int match_bits;
+};
+
+static const UrlData urls[] = {
+  { GURL(""),
+    HttpAuth::AUTH_NONE, false, 0 },
+  { GURL("http://foo.cn"),
+    HttpAuth::AUTH_PROXY, true, ALL_SERVERS_MATCH },
+  { GURL("http://foo.cn"),
+    HttpAuth::AUTH_SERVER, false, 0 },
+  { GURL("http://slashdot.org"),
+    HttpAuth::AUTH_NONE, false, 0 },
+  { GURL("http://www.google.com"),
+    HttpAuth::AUTH_SERVER, true, 1 << 0 },
+  { GURL("http://www.google.com"),
+    HttpAuth::AUTH_PROXY, true, ALL_SERVERS_MATCH },
+  { GURL("https://login.facebook.com/login.php?login_attempt=1"),
+    HttpAuth::AUTH_NONE, false, 0 },
+  { GURL("http://codereview.chromium.org/634002/show"),
+    HttpAuth::AUTH_SERVER, true, 1 << 3 },
+  { GURL("http://code.google.com/p/chromium/issues/detail?id=34505"),
+    HttpAuth::AUTH_SERVER, true, 1 << 0 },
+  { GURL("http://code.google.com/p/chromium/issues/list?can=2&q=label:"
+         "spdy&sort=owner&colspec=ID%20Stars%20Pri%20Area%20Type%20Status%20"
+         "Summary%20Modified%20Owner%20Mstone%20OS"),
+    HttpAuth::AUTH_SERVER, true, 1 << 3 },
+  { GURL("https://www.linkedin.com/secure/login?trk=hb_signin"),
+    HttpAuth::AUTH_SERVER, true, 1 << 1 },
+  { GURL("http://www.linkedin.com/mbox?displayMBoxItem=&"
+         "itemID=I1717980652_2&trk=COMM_HP_MSGVW_MEBC_MEBC&goback=.hom"),
+    HttpAuth::AUTH_SERVER, true, 1 << 1 },
+  { GURL("http://news.slashdot.org/story/10/02/18/190236/"
+         "New-Plan-Lets-Top-HS-Students-Graduate-2-Years-Early"),
+    HttpAuth::AUTH_PROXY, true, ALL_SERVERS_MATCH },
+  { GURL("http://codereview.chromium.org/646068/diff/4001/5003"),
+    HttpAuth::AUTH_SERVER, true, 1 << 3 },
+  { GURL("http://codereview.chromium.gag/646068/diff/4001/5003"),
+    HttpAuth::AUTH_SERVER, true, 1 << 4 },
+  { GURL("http://codereview.chromium.gog/646068/diff/4001/5003"),
+    HttpAuth::AUTH_SERVER, true, 1 << 5 },
+};
+
+}   // namespace
+
+TEST(HttpAuthFilterTest, EmptyFilter) {
+  // Create an empty filter
+  HttpAuthFilterWhitelist filter;
+  for (size_t i = 0; i < arraysize(urls); i++) {
+    EXPECT_EQ(urls[i].target == HttpAuth::AUTH_PROXY,
+              filter.IsValid(urls[i].url, urls[i].target))
+        << " " << i << ": " << urls[i].url;
+  }
+}
+
+TEST(HttpAuthFilterTest, NonEmptyFilter) {
+  // Create an non-empty filter
+  HttpAuthFilterWhitelist filter;
+  std::string server_whitelist_filter_string;
+  for (size_t i = 0; i < arraysize(server_whitelist_array); ++i) {
+    if (!server_whitelist_filter_string.empty())
+      server_whitelist_filter_string += ",";
+    server_whitelist_filter_string += "*";
+    server_whitelist_filter_string += server_whitelist_array[i];
+  }
+  filter.SetWhitelist(server_whitelist_filter_string);
+  for (size_t i = 0; i < arraysize(urls); i++) {
+    EXPECT_EQ(urls[i].matches, filter.IsValid(urls[i].url, urls[i].target))
+        << " " << i << ": " << urls[i].url;
+  }
+}
+
+}   // namespace net
diff --git a/net/http/http_auth_filter_win.h b/net/http/http_auth_filter_win.h
new file mode 100644
index 0000000..f819523
--- /dev/null
+++ b/net/http/http_auth_filter_win.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 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_HTTP_HTTP_AUTH_FILTER_WIN_H_
+#define NET_HTTP_HTTP_AUTH_FILTER_WIN_H_
+
+#include <string>
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/string_util.h"
+
+namespace net {
+
+enum RegistryHiveType {
+  CURRENT_USER,
+  LOCAL_MACHINE
+};
+
+namespace http_auth {
+
+// The common path to all the registry keys containing domain zone information.
+extern const char16 kRegistryInternetSettings[];
+extern const char16 kSettingsMachineOnly[];
+extern const char16* kRegistryEntries[3];       // L"http", L"https", and L"*"
+
+extern const char16* GetRegistryWhitelistKey();
+// Override the whitelist key.  Passing in NULL restores the default value.
+extern void SetRegistryWhitelistKey(const char16* new_whitelist_key);
+extern bool UseOnlyMachineSettings();
+
+}  // namespace http_auth
+
+}  // namespace net
+#endif  // OS_WIN
+
+#endif  // NET_HTTP_HTTP_AUTH_FILTER_WIN_H_
diff --git a/net/http/http_auth_gssapi_posix.cc b/net/http/http_auth_gssapi_posix.cc
new file mode 100644
index 0000000..5c88375
--- /dev/null
+++ b/net/http/http_auth_gssapi_posix.cc
@@ -0,0 +1,831 @@
+// Copyright (c) 2010 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/http/http_auth_gssapi_posix.h"
+
+#include <limits>
+#include <string>
+
+#include "base/base64.h"
+#include "base/file_path.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+
+// These are defined for the GSSAPI library:
+// Paraphrasing the comments from gssapi.h:
+// "The implementation must reserve static storage for a
+// gss_OID_desc object for each constant.  That constant
+// should be initialized to point to that gss_OID_desc."
+namespace {
+
+static gss_OID_desc GSS_C_NT_USER_NAME_VAL = {
+  10,
+  const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01")
+};
+static gss_OID_desc GSS_C_NT_MACHINE_UID_NAME_VAL = {
+  10,
+  const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02")
+};
+static gss_OID_desc GSS_C_NT_STRING_UID_NAME_VAL = {
+  10,
+  const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03")
+};
+static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_VAL = {
+  6,
+  const_cast<char*>("\x2b\x06\x01\x05\x06\x02")
+};
+static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_VAL = {
+  10,
+  const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")
+};
+static gss_OID_desc GSS_C_NT_ANONYMOUS_VAL = {
+  6,
+  const_cast<char*>("\x2b\x06\01\x05\x06\x03")
+};
+static gss_OID_desc GSS_C_NT_EXPORT_NAME_VAL = {
+  6,
+  const_cast<char*>("\x2b\x06\x01\x05\x06\x04")
+};
+
+}  // namespace
+
+gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_VAL;
+gss_OID GSS_C_NT_MACHINE_UID_NAME = &GSS_C_NT_MACHINE_UID_NAME_VAL;
+gss_OID GSS_C_NT_STRING_UID_NAME = &GSS_C_NT_STRING_UID_NAME_VAL;
+gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = &GSS_C_NT_HOSTBASED_SERVICE_X_VAL;
+gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_VAL;
+gss_OID GSS_C_NT_ANONYMOUS = &GSS_C_NT_ANONYMOUS_VAL;
+gss_OID GSS_C_NT_EXPORT_NAME = &GSS_C_NT_EXPORT_NAME_VAL;
+
+namespace net {
+
+// These are encoded using ASN.1 BER encoding.
+
+// This one is used by Firefox's nsAuthGSSAPI class.
+gss_OID_desc CHROME_GSS_KRB5_MECH_OID_DESC_VAL = {
+  9,
+  const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")
+};
+
+gss_OID_desc CHROME_GSS_C_NT_HOSTBASED_SERVICE_X_VAL = {
+  6,
+  const_cast<char*>("\x2b\x06\x01\x05\x06\x02")
+};
+
+gss_OID_desc CHROME_GSS_C_NT_HOSTBASED_SERVICE_VAL = {
+  10,
+  const_cast<char*>("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")
+};
+
+gss_OID CHROME_GSS_C_NT_HOSTBASED_SERVICE_X =
+    &CHROME_GSS_C_NT_HOSTBASED_SERVICE_X_VAL;
+gss_OID CHROME_GSS_C_NT_HOSTBASED_SERVICE =
+    &CHROME_GSS_C_NT_HOSTBASED_SERVICE_VAL;
+gss_OID CHROME_GSS_KRB5_MECH_OID_DESC =
+    &CHROME_GSS_KRB5_MECH_OID_DESC_VAL;
+
+// Debugging helpers.
+namespace {
+
+std::string DisplayStatus(OM_uint32 major_status,
+                          OM_uint32 minor_status) {
+  if (major_status == GSS_S_COMPLETE)
+    return "OK";
+  return StringPrintf("0x%08X 0x%08X", major_status, minor_status);
+}
+
+std::string DisplayCode(GSSAPILibrary* gssapi_lib,
+                        OM_uint32 status,
+                        OM_uint32 status_code_type) {
+  const int kMaxDisplayIterations = 8;
+  const size_t kMaxMsgLength = 4096;
+  // msg_ctx needs to be outside the loop because it is invoked multiple times.
+  OM_uint32 msg_ctx = 0;
+  std::string rv = StringPrintf("(0x%08X)", status);
+
+  // This loop should continue iterating until msg_ctx is 0 after the first
+  // iteration. To be cautious and prevent an infinite loop, it stops after
+  // a finite number of iterations as well. As an added sanity check, no
+  // individual message may exceed |kMaxMsgLength|, and the final result
+  // will not exceed |kMaxMsgLength|*2-1.
+  for (int i = 0; i < kMaxDisplayIterations && rv.size() < kMaxMsgLength;
+      ++i) {
+    OM_uint32 min_stat;
+    gss_buffer_desc_struct msg = GSS_C_EMPTY_BUFFER;
+    OM_uint32 maj_stat =
+        gssapi_lib->display_status(&min_stat, status, status_code_type,
+                                   GSS_C_NULL_OID, &msg_ctx, &msg);
+    if (maj_stat == GSS_S_COMPLETE) {
+      int msg_len = (msg.length > kMaxMsgLength) ?
+          static_cast<int>(kMaxMsgLength) :
+          static_cast<int>(msg.length);
+      if (msg_len > 0 && msg.value != NULL) {
+        rv += StringPrintf(" %.*s", msg_len,
+                           static_cast<char*>(msg.value));
+      }
+    }
+    gssapi_lib->release_buffer(&min_stat, &msg);
+    if (!msg_ctx)
+      break;
+  }
+  return rv;
+}
+
+std::string DisplayExtendedStatus(GSSAPILibrary* gssapi_lib,
+                                  OM_uint32 major_status,
+                                  OM_uint32 minor_status) {
+  if (major_status == GSS_S_COMPLETE)
+    return "OK";
+  std::string major = DisplayCode(gssapi_lib, major_status, GSS_C_GSS_CODE);
+  std::string minor = DisplayCode(gssapi_lib, minor_status, GSS_C_MECH_CODE);
+  return StringPrintf("Major: %s | Minor: %s", major.c_str(), minor.c_str());
+}
+
+// ScopedName releases a gss_name_t when it goes out of scope.
+class ScopedName {
+ public:
+  ScopedName(gss_name_t name,
+             GSSAPILibrary* gssapi_lib)
+      : name_(name),
+        gssapi_lib_(gssapi_lib) {
+    DCHECK(gssapi_lib_);
+  }
+
+  ~ScopedName() {
+    if (name_ != GSS_C_NO_NAME) {
+      OM_uint32 minor_status = 0;
+      OM_uint32 major_status =
+          gssapi_lib_->release_name(&minor_status, &name_);
+      if (major_status != GSS_S_COMPLETE) {
+        LOG(WARNING) << "Problem releasing name. "
+                     << DisplayStatus(major_status, minor_status);
+      }
+      name_ = GSS_C_NO_NAME;
+    }
+  }
+
+ private:
+  gss_name_t name_;
+  GSSAPILibrary* gssapi_lib_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedName);
+};
+
+// ScopedBuffer releases a gss_buffer_t when it goes out of scope.
+class ScopedBuffer {
+ public:
+  ScopedBuffer(gss_buffer_t buffer,
+               GSSAPILibrary* gssapi_lib)
+      : buffer_(buffer),
+        gssapi_lib_(gssapi_lib) {
+    DCHECK(gssapi_lib_);
+  }
+
+  ~ScopedBuffer() {
+    if (buffer_ != GSS_C_NO_BUFFER) {
+      OM_uint32 minor_status = 0;
+      OM_uint32 major_status =
+          gssapi_lib_->release_buffer(&minor_status, buffer_);
+      if (major_status != GSS_S_COMPLETE) {
+        LOG(WARNING) << "Problem releasing buffer. "
+                     << DisplayStatus(major_status, minor_status);
+      }
+      buffer_ = GSS_C_NO_BUFFER;
+    }
+  }
+
+ private:
+  gss_buffer_t buffer_;
+  GSSAPILibrary* gssapi_lib_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedBuffer);
+};
+
+namespace {
+
+std::string AppendIfPredefinedValue(gss_OID oid,
+                                    gss_OID predefined_oid,
+                                    const char* predefined_oid_name) {
+  DCHECK(oid);
+  DCHECK(predefined_oid);
+  DCHECK(predefined_oid_name);
+  std::string output;
+  if (oid->length != predefined_oid->length)
+    return output;
+  if (0 != memcmp(oid->elements,
+                  predefined_oid->elements,
+                  predefined_oid->length))
+    return output;
+
+  output += " (";
+  output += predefined_oid_name;
+  output += ")";
+  return output;
+}
+
+}  // namespace
+
+std::string DescribeOid(GSSAPILibrary* gssapi_lib, const gss_OID oid) {
+  if (!oid)
+    return "<NULL>";
+  std::string output;
+  const size_t kMaxCharsToPrint = 1024;
+  OM_uint32 byte_length = oid->length;
+  size_t char_length = byte_length / sizeof(char);
+  if (char_length > kMaxCharsToPrint) {
+    // This might be a plain ASCII string.
+    // Check if the first |kMaxCharsToPrint| characters
+    // contain only printable characters and are NULL terminated.
+    const char* str = reinterpret_cast<const char*>(oid);
+    bool is_printable = true;
+    size_t str_length = 0;
+    for ( ; str_length < kMaxCharsToPrint; ++str_length) {
+      if (!str[str_length])
+        break;
+      if (!isprint(str[str_length])) {
+        is_printable = false;
+        break;
+      }
+    }
+    if (!str[str_length]) {
+      output += StringPrintf("\"%s\"", str);
+      return output;
+    }
+  }
+  output = StringPrintf("(%u) \"", byte_length);
+  if (!oid->elements) {
+    output += "<NULL>";
+    return output;
+  }
+  const unsigned char* elements =
+      reinterpret_cast<const unsigned char*>(oid->elements);
+  // Don't print more than |kMaxCharsToPrint| characters.
+  size_t i = 0;
+  for ( ; (i < byte_length) && (i < kMaxCharsToPrint); ++i) {
+    output += StringPrintf("\\x%02X", elements[i]);
+  }
+  if (i >= kMaxCharsToPrint)
+    output += "...";
+  output += "\"";
+
+  // Check if the OID is one of the predefined values.
+  output += AppendIfPredefinedValue(oid,
+                                    GSS_C_NT_USER_NAME,
+                                    "GSS_C_NT_USER_NAME");
+  output += AppendIfPredefinedValue(oid,
+                                    GSS_C_NT_MACHINE_UID_NAME,
+                                    "GSS_C_NT_MACHINE_UID_NAME");
+  output += AppendIfPredefinedValue(oid,
+                                    GSS_C_NT_STRING_UID_NAME,
+                                    "GSS_C_NT_STRING_UID_NAME");
+  output += AppendIfPredefinedValue(oid,
+                                    GSS_C_NT_HOSTBASED_SERVICE_X,
+                                    "GSS_C_NT_HOSTBASED_SERVICE_X");
+  output += AppendIfPredefinedValue(oid,
+                                    GSS_C_NT_HOSTBASED_SERVICE,
+                                    "GSS_C_NT_HOSTBASED_SERVICE");
+  output += AppendIfPredefinedValue(oid,
+                                    GSS_C_NT_ANONYMOUS,
+                                    "GSS_C_NT_ANONYMOUS");
+  output += AppendIfPredefinedValue(oid,
+                                    GSS_C_NT_EXPORT_NAME,
+                                    "GSS_C_NT_EXPORT_NAME");
+
+  return output;
+}
+
+std::string DescribeBuffer(const gss_buffer_t buffer) {
+  if (!buffer)
+    return "<NULL>";
+  size_t length = buffer->length;
+  std::string output(StringPrintf("(%" PRIuS ") ", length));
+  if (!buffer->value) {
+    output += "<NULL>";
+    return output;
+  }
+  const char* value =
+      reinterpret_cast<const char*>(buffer->value);
+  bool is_printable = true;
+  for (size_t i = 0; i < length; ++i) {
+    if (!isprint(value[i])) {
+      // Allow the last character to be a '0'.
+      if ((i < (length - 1)) && !value[i])
+        continue;
+      is_printable = false;
+      break;
+    }
+  }
+  if (is_printable) {
+    output += "\"";
+    output += value;
+    output += "\"";
+  } else {
+    output += "[";
+    for (size_t i = 0; i < buffer->length; ++i) {
+      output += StringPrintf("\\x%02X", value[i] & 0x0FF);
+    }
+    output += "]";
+  }
+
+  return output;
+}
+
+std::string DescribeName(GSSAPILibrary* gssapi_lib, const gss_name_t name) {
+  OM_uint32 major_status = 0;
+  OM_uint32 minor_status = 0;
+  gss_buffer_desc_struct output_name_buffer = GSS_C_EMPTY_BUFFER;
+  gss_OID_desc output_name_type_desc = GSS_C_EMPTY_BUFFER;
+  gss_OID output_name_type = &output_name_type_desc;
+  major_status = gssapi_lib->display_name(&minor_status,
+                                          name,
+                                          &output_name_buffer,
+                                          &output_name_type);
+  ScopedBuffer scoped_output_name(&output_name_buffer, gssapi_lib);
+  if (major_status != GSS_S_COMPLETE) {
+    std::string error =
+        StringPrintf("Unable to describe name 0x%p, %s",
+                     name,
+                     DisplayExtendedStatus(gssapi_lib,
+                                           major_status,
+                                           minor_status).c_str());
+    return error;
+  }
+  int len = output_name_buffer.length;
+  std::string description =
+      StringPrintf("%*s (Type %s)",
+                   len,
+                   reinterpret_cast<const char*>(output_name_buffer.value),
+                   DescribeOid(gssapi_lib, output_name_type).c_str());
+  return description;
+}
+
+std::string DescribeContext(GSSAPILibrary* gssapi_lib,
+                            const gss_ctx_id_t context_handle) {
+  OM_uint32 major_status = 0;
+  OM_uint32 minor_status = 0;
+  gss_name_t src_name = GSS_C_NO_NAME;
+  gss_name_t targ_name = GSS_C_NO_NAME;
+  OM_uint32 lifetime_rec = 0;
+  gss_OID mech_type = GSS_C_NO_OID;
+  OM_uint32 ctx_flags = 0;
+  int locally_initiated = 0;
+  int open = 0;
+  major_status = gssapi_lib->inquire_context(&minor_status,
+                                             context_handle,
+                                             &src_name,
+                                             &targ_name,
+                                             &lifetime_rec,
+                                             &mech_type,
+                                             &ctx_flags,
+                                             &locally_initiated,
+                                             &open);
+  ScopedName(src_name, gssapi_lib);
+  ScopedName(targ_name, gssapi_lib);
+  if (major_status != GSS_S_COMPLETE) {
+    std::string error =
+        StringPrintf("Unable to describe context 0x%p, %s",
+                     context_handle,
+                     DisplayExtendedStatus(gssapi_lib,
+                                           major_status,
+                                           minor_status).c_str());
+    return error;
+  }
+  std::string source(DescribeName(gssapi_lib, src_name));
+  std::string target(DescribeName(gssapi_lib, targ_name));
+  std::string description = StringPrintf("Context 0x%p: "
+                                         "Source \"%s\", "
+                                         "Target \"%s\", "
+                                         "lifetime %d, "
+                                         "mechanism %s, "
+                                         "flags 0x%08X, "
+                                         "local %d, "
+                                         "open %d",
+                                         context_handle,
+                                         source.c_str(),
+                                         target.c_str(),
+                                         lifetime_rec,
+                                         DescribeOid(gssapi_lib,
+                                                     mech_type).c_str(),
+                                         ctx_flags,
+                                         locally_initiated,
+                                         open);
+  return description;
+}
+
+}  // namespace
+
+GSSAPISharedLibrary::GSSAPISharedLibrary()
+    : initialized_(false),
+      gssapi_library_(NULL),
+      import_name_(NULL),
+      release_name_(NULL),
+      release_buffer_(NULL),
+      display_name_(NULL),
+      display_status_(NULL),
+      init_sec_context_(NULL),
+      wrap_size_limit_(NULL),
+      delete_sec_context_(NULL),
+      inquire_context_(NULL) {
+}
+
+GSSAPISharedLibrary::~GSSAPISharedLibrary() {
+  if (gssapi_library_) {
+    base::UnloadNativeLibrary(gssapi_library_);
+    gssapi_library_ = NULL;
+  }
+}
+
+bool GSSAPISharedLibrary::Init() {
+  if (!initialized_)
+    InitImpl();
+  return initialized_;
+}
+
+bool GSSAPISharedLibrary::InitImpl() {
+  DCHECK(!initialized_);
+  gssapi_library_ = LoadSharedLibrary();
+  if (gssapi_library_ == NULL)
+    return false;
+  initialized_ = true;
+  return true;
+}
+
+base::NativeLibrary GSSAPISharedLibrary::LoadSharedLibrary() {
+  static const char* kLibraryNames[] = {
+#if defined(OS_MACOSX)
+    "libgssapi_krb5.dylib"  // MIT Kerberos
+#else
+    "libgssapi_krb5.so.2",  // MIT Kerberos - FC, Suse10, Debian
+    "libgssapi.so.4",       // Heimdal - Suse10, MDK
+    "libgssapi.so.1"        // Heimdal - Suse9, CITI - FC, MDK, Suse10
+#endif
+  };
+  static size_t num_lib_names = arraysize(kLibraryNames);
+
+  for (size_t i = 0; i < num_lib_names; ++i) {
+    const char* library_name = kLibraryNames[i];
+    FilePath file_path(library_name);
+    base::NativeLibrary lib = base::LoadNativeLibrary(file_path);
+    if (lib) {
+      // Only return this library if we can bind the functions we need.
+      if (BindMethods(lib))
+        return lib;
+      base::UnloadNativeLibrary(lib);
+    }
+  }
+  LOG(WARNING) << "Unable to find a compatible GSSAPI library";
+  return NULL;
+}
+
+#define BIND(lib, x) \
+  gss_##x##_type x = reinterpret_cast<gss_##x##_type>( \
+      base::GetFunctionPointerFromNativeLibrary(lib, "gss_" #x)); \
+  if (x == NULL) { \
+    LOG(WARNING) << "Unable to bind function \"" << "gss_" #x << "\""; \
+    return false; \
+  }
+
+bool GSSAPISharedLibrary::BindMethods(base::NativeLibrary lib) {
+  DCHECK(lib != NULL);
+
+  BIND(lib, import_name)
+  BIND(lib, release_name)
+  BIND(lib, release_buffer)
+  BIND(lib, display_name)
+  BIND(lib, display_status)
+  BIND(lib, init_sec_context)
+  BIND(lib, wrap_size_limit)
+  BIND(lib, delete_sec_context)
+  BIND(lib, inquire_context)
+
+  import_name_ = import_name;
+  release_name_ = release_name;
+  release_buffer_ = release_buffer;
+  display_name_ = display_name;
+  display_status_ = display_status;
+  init_sec_context_ = init_sec_context;
+  wrap_size_limit_ = wrap_size_limit;
+  delete_sec_context_ = delete_sec_context;
+  inquire_context_ = inquire_context;
+
+  return true;
+}
+
+#undef BIND
+
+OM_uint32 GSSAPISharedLibrary::import_name(
+    OM_uint32* minor_status,
+    const gss_buffer_t input_name_buffer,
+    const gss_OID input_name_type,
+    gss_name_t* output_name) {
+  DCHECK(initialized_);
+  return import_name_(minor_status, input_name_buffer, input_name_type,
+                      output_name);
+}
+
+OM_uint32 GSSAPISharedLibrary::release_name(
+    OM_uint32* minor_status,
+    gss_name_t* input_name) {
+  DCHECK(initialized_);
+  return release_name_(minor_status, input_name);
+}
+
+OM_uint32 GSSAPISharedLibrary::release_buffer(
+    OM_uint32* minor_status,
+    gss_buffer_t buffer) {
+  DCHECK(initialized_);
+  return release_buffer_(minor_status, buffer);
+}
+
+OM_uint32 GSSAPISharedLibrary::display_name(
+    OM_uint32* minor_status,
+    const gss_name_t input_name,
+    gss_buffer_t output_name_buffer,
+    gss_OID* output_name_type) {
+  DCHECK(initialized_);
+  return display_name_(minor_status,
+                       input_name,
+                       output_name_buffer,
+                       output_name_type);
+}
+
+OM_uint32 GSSAPISharedLibrary::display_status(
+    OM_uint32* minor_status,
+    OM_uint32 status_value,
+    int status_type,
+    const gss_OID mech_type,
+    OM_uint32* message_context,
+    gss_buffer_t status_string) {
+  DCHECK(initialized_);
+  return display_status_(minor_status, status_value, status_type, mech_type,
+                         message_context, status_string);
+}
+
+OM_uint32 GSSAPISharedLibrary::init_sec_context(
+    OM_uint32* minor_status,
+    const gss_cred_id_t initiator_cred_handle,
+    gss_ctx_id_t* context_handle,
+    const gss_name_t target_name,
+    const gss_OID mech_type,
+    OM_uint32 req_flags,
+    OM_uint32 time_req,
+    const gss_channel_bindings_t input_chan_bindings,
+    const gss_buffer_t input_token,
+    gss_OID* actual_mech_type,
+    gss_buffer_t output_token,
+    OM_uint32* ret_flags,
+    OM_uint32* time_rec) {
+  DCHECK(initialized_);
+  return init_sec_context_(minor_status,
+                           initiator_cred_handle,
+                           context_handle,
+                           target_name,
+                           mech_type,
+                           req_flags,
+                           time_req,
+                           input_chan_bindings,
+                           input_token,
+                           actual_mech_type,
+                           output_token,
+                           ret_flags,
+                           time_rec);
+}
+
+OM_uint32 GSSAPISharedLibrary::wrap_size_limit(
+    OM_uint32* minor_status,
+    const gss_ctx_id_t context_handle,
+    int conf_req_flag,
+    gss_qop_t qop_req,
+    OM_uint32 req_output_size,
+    OM_uint32* max_input_size) {
+  DCHECK(initialized_);
+  return wrap_size_limit_(minor_status,
+                          context_handle,
+                          conf_req_flag,
+                          qop_req,
+                          req_output_size,
+                          max_input_size);
+}
+
+OM_uint32 GSSAPISharedLibrary::delete_sec_context(
+    OM_uint32* minor_status,
+    gss_ctx_id_t* context_handle,
+    gss_buffer_t output_token) {
+  // This is called from the owner class' destructor, even if
+  // Init() is not called, so we can't assume |initialized_|
+  // is set.
+  if (!initialized_)
+    return 0;
+  return delete_sec_context_(minor_status,
+                             context_handle,
+                             output_token);
+}
+
+OM_uint32 GSSAPISharedLibrary::inquire_context(
+    OM_uint32* minor_status,
+    const gss_ctx_id_t context_handle,
+    gss_name_t* src_name,
+    gss_name_t* targ_name,
+    OM_uint32* lifetime_rec,
+    gss_OID* mech_type,
+    OM_uint32* ctx_flags,
+    int* locally_initiated,
+    int* open) {
+  DCHECK(initialized_);
+  return inquire_context_(minor_status,
+                         context_handle,
+                         src_name,
+                         targ_name,
+                         lifetime_rec,
+                         mech_type,
+                         ctx_flags,
+                         locally_initiated,
+                         open);
+}
+GSSAPILibrary* GSSAPILibrary::GetDefault() {
+  return Singleton<GSSAPISharedLibrary>::get();
+}
+
+ScopedSecurityContext::ScopedSecurityContext(GSSAPILibrary* gssapi_lib)
+    : security_context_(GSS_C_NO_CONTEXT),
+      gssapi_lib_(gssapi_lib) {
+  DCHECK(gssapi_lib_);
+}
+
+ScopedSecurityContext::~ScopedSecurityContext() {
+  if (security_context_ != GSS_C_NO_CONTEXT) {
+    gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+    OM_uint32 minor_status = 0;
+    OM_uint32 major_status = gssapi_lib_->delete_sec_context(
+        &minor_status, &security_context_, &output_token);
+    if (major_status != GSS_S_COMPLETE) {
+      LOG(WARNING) << "Problem releasing security_context. "
+                   << DisplayStatus(major_status, minor_status);
+    }
+    security_context_ = GSS_C_NO_CONTEXT;
+  }
+}
+
+HttpAuthGSSAPI::HttpAuthGSSAPI(GSSAPILibrary* library,
+                               const std::string& scheme,
+                               gss_OID gss_oid)
+    : scheme_(scheme),
+      gss_oid_(gss_oid),
+      library_(library),
+      scoped_sec_context_(library) {
+  DCHECK(library_);
+}
+
+HttpAuthGSSAPI::~HttpAuthGSSAPI() {
+}
+
+bool HttpAuthGSSAPI::Init() {
+  if (!library_)
+    return false;
+  return library_->Init();
+}
+
+bool HttpAuthGSSAPI::NeedsIdentity() const {
+  return decoded_server_auth_token_.empty();
+}
+
+bool HttpAuthGSSAPI::IsFinalRound() const {
+  return !NeedsIdentity();
+}
+
+bool HttpAuthGSSAPI::ParseChallenge(HttpAuth::ChallengeTokenizer* tok) {
+  // Verify the challenge's auth-scheme.
+  if (!tok->valid() ||
+      !LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
+    return false;
+
+  tok->set_expect_base64_token(true);
+  if (!tok->GetNext()) {
+    decoded_server_auth_token_.clear();
+    return true;
+  }
+
+  std::string encoded_auth_token = tok->value();
+  std::string decoded_auth_token;
+  bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
+  if (!base64_rv) {
+    LOG(ERROR) << "Base64 decoding of auth token failed.";
+    return false;
+  }
+  decoded_server_auth_token_ = decoded_auth_token;
+  return true;
+}
+
+int HttpAuthGSSAPI::GenerateAuthToken(const std::wstring* username,
+                                      const std::wstring* password,
+                                      const std::wstring& spn,
+                                      std::string* auth_token) {
+  DCHECK(auth_token);
+  DCHECK((username == NULL) == (password == NULL));
+
+  if (!IsFinalRound()) {
+    int rv = OnFirstRound(username, password);
+    if (rv != OK)
+      return rv;
+  }
+
+  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+  input_token.length = decoded_server_auth_token_.length();
+  input_token.value =
+      (input_token.length > 0) ?
+          const_cast<char*>(decoded_server_auth_token_.data()) :
+          NULL;
+  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+  ScopedBuffer scoped_output_token(&output_token, library_);
+  int rv = GetNextSecurityToken(spn, &input_token, &output_token);
+  if (rv != OK)
+    return rv;
+
+  // Base64 encode data in output buffer and prepend the scheme.
+  std::string encode_input(static_cast<char*>(output_token.value),
+                           output_token.length);
+  std::string encode_output;
+  bool ok = base::Base64Encode(encode_input, &encode_output);
+  if (!ok)
+    return ERR_UNEXPECTED;
+  *auth_token = scheme_ + " " + encode_output;
+  return OK;
+}
+
+int HttpAuthGSSAPI::OnFirstRound(const std::wstring* username,
+                                 const std::wstring* password) {
+  // TODO(cbentzel): Acquire credentials?
+  DCHECK((username == NULL) == (password == NULL));
+  username_.clear();
+  password_.clear();
+  if (username) {
+    username_ = *username;
+    password_ = *password;
+  }
+  return OK;
+}
+
+int HttpAuthGSSAPI::GetNextSecurityToken(const std::wstring& spn,
+                                         gss_buffer_t in_token,
+                                         gss_buffer_t out_token) {
+  // Create a name for the principal
+  // TODO(cbentzel): Just do this on the first pass?
+  std::string spn_principal = WideToASCII(spn);
+  gss_buffer_desc spn_buffer = GSS_C_EMPTY_BUFFER;
+  spn_buffer.value = const_cast<char*>(spn_principal.c_str());
+  spn_buffer.length = spn_principal.size() + 1;
+  OM_uint32 minor_status = 0;
+  gss_name_t principal_name = GSS_C_NO_NAME;
+  OM_uint32 major_status = library_->import_name(
+      &minor_status,
+      &spn_buffer,
+      CHROME_GSS_C_NT_HOSTBASED_SERVICE,
+      &principal_name);
+  if (major_status != GSS_S_COMPLETE) {
+    LOG(ERROR) << "Problem importing name from "
+               << "spn \"" << spn_principal << "\""
+               << std::endl
+               << DisplayExtendedStatus(library_,
+                                        major_status,
+                                        minor_status);
+    return ERR_UNEXPECTED;
+  }
+  ScopedName scoped_name(principal_name, library_);
+
+  // Continue creating a security context.
+  OM_uint32 req_flags = 0;
+  major_status = library_->init_sec_context(
+      &minor_status,
+      GSS_C_NO_CREDENTIAL,
+      scoped_sec_context_.receive(),
+      principal_name,
+      gss_oid_,
+      req_flags,
+      GSS_C_INDEFINITE,
+      GSS_C_NO_CHANNEL_BINDINGS,
+      in_token,
+      NULL,  // actual_mech_type
+      out_token,
+      NULL,  // ret flags
+      NULL);
+  if (major_status != GSS_S_COMPLETE &&
+      major_status != GSS_S_CONTINUE_NEEDED) {
+    LOG(ERROR) << "Problem initializing context. "
+               << std::endl
+               << DisplayExtendedStatus(library_,
+                                        major_status,
+                                        minor_status)
+               << std::endl
+               << DescribeContext(library_, scoped_sec_context_.get());
+    return ERR_MISSING_AUTH_CREDENTIALS;
+  }
+
+  return OK;
+}
+
+}  // namespace net
diff --git a/net/http/http_auth_gssapi_posix.h b/net/http/http_auth_gssapi_posix.h
new file mode 100644
index 0000000..f0642ea
--- /dev/null
+++ b/net/http/http_auth_gssapi_posix.h
@@ -0,0 +1,263 @@
+// Copyright (c) 2010 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_HTTP_HTTP_AUTH_GSSAPI_POSIX_H_
+#define NET_HTTP_HTTP_AUTH_GSSAPI_POSIX_H_
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/native_library.h"
+#include "net/http/http_auth.h"
+
+#define GSS_USE_FUNCTION_POINTERS
+#include "net/third_party/gssapi/gssapi.h"
+
+class GURL;
+
+namespace net {
+
+class HttpRequestInfo;
+
+extern gss_OID CHROME_GSS_C_NT_HOSTBASED_SERVICE_X;
+extern gss_OID CHROME_GSS_C_NT_HOSTBASED_SERVICE;
+extern gss_OID CHROME_GSS_KRB5_MECH_OID_DESC;
+
+// GSSAPILibrary is introduced so unit tests can mock the calls to the GSSAPI
+// library. The default implementation attempts to load one of the standard
+// GSSAPI library implementations, then simply passes the arguments on to
+// that implementation.
+class GSSAPILibrary {
+ public:
+  virtual ~GSSAPILibrary() {}
+
+  // Initializes the library, including any necessary dynamic libraries.
+  // This is done separately from construction (which happens at startup time)
+  // in order to delay work until the class is actually needed.
+  virtual bool Init() = 0;
+
+  // These methods match the ones in the GSSAPI library.
+  virtual OM_uint32 import_name(
+      OM_uint32* minor_status,
+      const gss_buffer_t input_name_buffer,
+      const gss_OID input_name_type,
+      gss_name_t* output_name) = 0;
+  virtual OM_uint32 release_name(
+      OM_uint32* minor_status,
+      gss_name_t* input_name) = 0;
+  virtual OM_uint32 release_buffer(
+      OM_uint32* minor_status,
+      gss_buffer_t buffer) = 0;
+  virtual OM_uint32 display_name(
+      OM_uint32* minor_status,
+      const gss_name_t input_name,
+      gss_buffer_t output_name_buffer,
+      gss_OID* output_name_type) = 0;
+  virtual OM_uint32 display_status(
+      OM_uint32* minor_status,
+      OM_uint32 status_value,
+      int status_type,
+      const gss_OID mech_type,
+      OM_uint32* message_contex,
+      gss_buffer_t status_string) = 0;
+  virtual OM_uint32 init_sec_context(
+      OM_uint32* minor_status,
+      const gss_cred_id_t initiator_cred_handle,
+      gss_ctx_id_t* context_handle,
+      const gss_name_t target_name,
+      const gss_OID mech_type,
+      OM_uint32 req_flags,
+      OM_uint32 time_req,
+      const gss_channel_bindings_t input_chan_bindings,
+      const gss_buffer_t input_token,
+      gss_OID* actual_mech_type,
+      gss_buffer_t output_token,
+      OM_uint32* ret_flags,
+      OM_uint32* time_rec) = 0;
+  virtual OM_uint32 wrap_size_limit(
+      OM_uint32* minor_status,
+      const gss_ctx_id_t context_handle,
+      int conf_req_flag,
+      gss_qop_t qop_req,
+      OM_uint32 req_output_size,
+      OM_uint32* max_input_size) = 0;
+  virtual OM_uint32 delete_sec_context(
+      OM_uint32* minor_status,
+      gss_ctx_id_t* context_handle,
+      gss_buffer_t output_token) = 0;
+  virtual OM_uint32 inquire_context(
+      OM_uint32* minor_status,
+      const gss_ctx_id_t context_handle,
+      gss_name_t* src_name,
+      gss_name_t* targ_name,
+      OM_uint32* lifetime_rec,
+      gss_OID* mech_type,
+      OM_uint32* ctx_flags,
+      int* locally_initiated,
+      int* open) = 0;
+
+  // Get the default GSSPILibrary instance. The object returned is a singleton
+  // instance, and the caller should not delete it.
+  static GSSAPILibrary* GetDefault();
+};
+
+// GSSAPISharedLibrary class is defined here so that unit tests can access it.
+class GSSAPISharedLibrary : public GSSAPILibrary {
+ public:
+  GSSAPISharedLibrary();
+  virtual ~GSSAPISharedLibrary();
+
+  // GSSAPILibrary methods:
+  virtual bool Init();
+  virtual OM_uint32 import_name(
+      OM_uint32* minor_status,
+      const gss_buffer_t input_name_buffer,
+      const gss_OID input_name_type,
+      gss_name_t* output_name);
+  virtual OM_uint32 release_name(
+      OM_uint32* minor_status,
+      gss_name_t* input_name);
+  virtual OM_uint32 release_buffer(
+      OM_uint32* minor_status,
+      gss_buffer_t buffer);
+  virtual OM_uint32 display_name(
+      OM_uint32* minor_status,
+      const gss_name_t input_name,
+      gss_buffer_t output_name_buffer,
+      gss_OID* output_name_type);
+  virtual OM_uint32 display_status(
+      OM_uint32* minor_status,
+      OM_uint32 status_value,
+      int status_type,
+      const gss_OID mech_type,
+      OM_uint32* message_contex,
+      gss_buffer_t status_string);
+  virtual OM_uint32 init_sec_context(
+      OM_uint32* minor_status,
+      const gss_cred_id_t initiator_cred_handle,
+      gss_ctx_id_t* context_handle,
+      const gss_name_t target_name,
+      const gss_OID mech_type,
+      OM_uint32 req_flags,
+      OM_uint32 time_req,
+      const gss_channel_bindings_t input_chan_bindings,
+      const gss_buffer_t input_token,
+      gss_OID* actual_mech_type,
+      gss_buffer_t output_token,
+      OM_uint32* ret_flags,
+      OM_uint32* time_rec);
+  virtual OM_uint32 wrap_size_limit(
+      OM_uint32* minor_status,
+      const gss_ctx_id_t context_handle,
+      int conf_req_flag,
+      gss_qop_t qop_req,
+      OM_uint32 req_output_size,
+      OM_uint32* max_input_size);
+  virtual OM_uint32 delete_sec_context(
+      OM_uint32* minor_status,
+      gss_ctx_id_t* context_handle,
+      gss_buffer_t output_token);
+  virtual OM_uint32 inquire_context(
+      OM_uint32* minor_status,
+      const gss_ctx_id_t context_handle,
+      gss_name_t* src_name,
+      gss_name_t* targ_name,
+      OM_uint32* lifetime_rec,
+      gss_OID* mech_type,
+      OM_uint32* ctx_flags,
+      int* locally_initiated,
+      int* open);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(HttpAuthGSSAPIPOSIXTest, GSSAPIStartup);
+
+  bool InitImpl();
+  // Finds a usable dynamic library for GSSAPI and loads it.  The criteria are:
+  //   1. The library must exist.
+  //   2. The library must export the functions we need.
+  base::NativeLibrary LoadSharedLibrary();
+  bool BindMethods(base::NativeLibrary lib);
+
+  bool initialized_;
+
+  // Need some way to invalidate the library.
+  base::NativeLibrary gssapi_library_;
+
+  // Function pointers
+  gss_import_name_type import_name_;
+  gss_release_name_type release_name_;
+  gss_release_buffer_type release_buffer_;
+  gss_display_name_type display_name_;
+  gss_display_status_type display_status_;
+  gss_init_sec_context_type init_sec_context_;
+  gss_wrap_size_limit_type wrap_size_limit_;
+  gss_delete_sec_context_type delete_sec_context_;
+  gss_inquire_context_type inquire_context_;
+};
+
+// ScopedSecurityContext releases a gss_ctx_id_t when it goes out of
+// scope.
+class ScopedSecurityContext {
+ public:
+  ScopedSecurityContext(GSSAPILibrary* gssapi_lib);
+  ~ScopedSecurityContext();
+
+  const gss_ctx_id_t get() const { return security_context_; }
+  gss_ctx_id_t* receive() { return &security_context_; }
+
+ private:
+  gss_ctx_id_t security_context_;
+  GSSAPILibrary* gssapi_lib_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedSecurityContext);
+};
+
+
+// TODO(ahendrickson): Share code with HttpAuthSSPI.
+class HttpAuthGSSAPI {
+ public:
+  HttpAuthGSSAPI(GSSAPILibrary* library,
+                 const std::string& scheme,
+                 const gss_OID gss_oid);
+  ~HttpAuthGSSAPI();
+
+  bool Init();
+
+  bool NeedsIdentity() const;
+  bool IsFinalRound() const;
+
+  bool ParseChallenge(HttpAuth::ChallengeTokenizer* tok);
+
+  // Generates an authentication token.
+  // The return value is an error code. If it's not |OK|, the value of
+  // |*auth_token| is unspecified.
+  // |spn| is the Service Principal Name of the server that the token is
+  // being generated for.
+  // If this is the first round of a multiple round scheme, credentials are
+  // obtained using |*username| and |*password|. If |username| and |password|
+  // are NULL, the default credentials are used instead.
+  int GenerateAuthToken(const std::wstring* username,
+                        const std::wstring* password,
+                        const std::wstring& spn,
+                        std::string* auth_token);
+
+ private:
+  int OnFirstRound(const std::wstring* username,
+                   const std::wstring* password);
+  int GetNextSecurityToken(const std::wstring& spn,
+                           gss_buffer_t in_token,
+                           gss_buffer_t out_token);
+
+  std::string scheme_;
+  std::wstring username_;
+  std::wstring password_;
+  gss_OID gss_oid_;
+  GSSAPILibrary* library_;
+  std::string decoded_server_auth_token_;
+  ScopedSecurityContext scoped_sec_context_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_AUTH_GSSAPI_POSIX_H_
diff --git a/net/http/http_auth_gssapi_posix_unittest.cc b/net/http/http_auth_gssapi_posix_unittest.cc
new file mode 100644
index 0000000..e66bf85
--- /dev/null
+++ b/net/http/http_auth_gssapi_posix_unittest.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2010 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/http/http_auth_gssapi_posix.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/native_library.h"
+#include "base/scoped_ptr.h"
+#include "net/http/mock_gssapi_library_posix.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+// gss_buffer_t helpers.
+void ClearBuffer(gss_buffer_t dest) {
+  if (!dest)
+    return;
+  dest->length = 0;
+  delete [] reinterpret_cast<char*>(dest->value);
+  dest->value = NULL;
+}
+
+void SetBuffer(gss_buffer_t dest, const void* src, size_t length) {
+  if (!dest)
+    return;
+  ClearBuffer(dest);
+  if (!src)
+    return;
+  dest->length = length;
+  if (length) {
+    dest->value = new char[length];
+    memcpy(dest->value, src, length);
+  }
+}
+
+void CopyBuffer(gss_buffer_t dest, const gss_buffer_t src) {
+  if (!dest)
+    return;
+  ClearBuffer(dest);
+  if (!src)
+    return;
+  SetBuffer(dest, src->value, src->length);
+}
+
+}  // namespace
+
+TEST(HttpAuthGSSAPIPOSIXTest, GSSAPIStartup) {
+  // TODO(ahendrickson): Manipulate the libraries and paths to test each of the
+  // libraries we expect, and also whether or not they have the interface
+  // functions we want.
+  GSSAPILibrary* gssapi = GSSAPILibrary::GetDefault();
+  DCHECK(gssapi);
+  gssapi->Init();
+}
+
+TEST(HttpAuthGSSAPIPOSIXTest, GSSAPICycle) {
+  scoped_ptr<test::MockGSSAPILibrary> mock_library(new test::MockGSSAPILibrary);
+  DCHECK(mock_library.get());
+  mock_library->Init();
+  const char kAuthResponse[] = "Mary had a little lamb";
+  test::GssContextMockImpl context1(
+      "localhost",                    // Source name
+      "example.com",                  // Target name
+      23,                             // Lifetime
+      *GSS_C_NT_HOSTBASED_SERVICE,    // Mechanism
+      0,                              // Context flags
+      1,                              // Locally initiated
+      0);                             // Open
+  test::GssContextMockImpl context2(
+      "localhost",                    // Source name
+      "example.com",                  // Target name
+      23,                             // Lifetime
+      *GSS_C_NT_HOSTBASED_SERVICE,    // Mechanism
+      0,                              // Context flags
+      1,                              // Locally initiated
+      1);                             // Open
+  test::MockGSSAPILibrary::SecurityContextQuery queries[] = {
+    { "Negotiate",                    // Package name
+      GSS_S_CONTINUE_NEEDED,          // Major response code
+      0,                              // Minor response code
+      context1,                       // Context
+      { 0, NULL },                           // Expected input token
+      { arraysize(kAuthResponse),
+        const_cast<char*>(kAuthResponse) }   // Output token
+    },
+    { "Negotiate",                    // Package name
+      GSS_S_COMPLETE,                 // Major response code
+      0,                              // Minor response code
+      context2,                       // Context
+      { arraysize(kAuthResponse),
+        const_cast<char*>(kAuthResponse) },  // Expected input token
+      { arraysize(kAuthResponse),
+        const_cast<char*>(kAuthResponse) }   // Output token
+    },
+  };
+
+  for (size_t i = 0; i < arraysize(queries); ++i) {
+    mock_library->ExpectSecurityContext(queries[i].expected_package,
+                                        queries[i].response_code,
+                                        queries[i].minor_response_code,
+                                        queries[i].context_info,
+                                        queries[i].expected_input_token,
+                                        queries[i].output_token);
+  }
+
+  OM_uint32 major_status = 0;
+  OM_uint32 minor_status = 0;
+  gss_cred_id_t initiator_cred_handle = NULL;
+  gss_ctx_id_t context_handle = NULL;
+  gss_name_t target_name = NULL;
+  gss_OID mech_type = NULL;
+  OM_uint32 req_flags = 0;
+  OM_uint32 time_req = 25;
+  gss_channel_bindings_t input_chan_bindings = NULL;
+  gss_buffer_desc input_token = { 0, NULL };
+  gss_OID actual_mech_type= NULL;
+  gss_buffer_desc output_token = { 0, NULL };
+  OM_uint32 ret_flags = 0;
+  OM_uint32 time_rec = 0;
+  for (size_t i = 0; i < arraysize(queries); ++i) {
+    major_status = mock_library->init_sec_context(&minor_status,
+                                                  initiator_cred_handle,
+                                                  &context_handle,
+                                                  target_name,
+                                                  mech_type,
+                                                  req_flags,
+                                                  time_req,
+                                                  input_chan_bindings,
+                                                  &input_token,
+                                                  &actual_mech_type,
+                                                  &output_token,
+                                                  &ret_flags,
+                                                  &time_rec);
+    CopyBuffer(&input_token, &output_token);
+    ClearBuffer(&output_token);
+  }
+  ClearBuffer(&input_token);
+  major_status = mock_library->delete_sec_context(&minor_status,
+                                                  &context_handle,
+                                                  GSS_C_NO_BUFFER);
+}
+
+}  // namespace net
diff --git a/net/http/http_auth_handler.cc b/net/http/http_auth_handler.cc
index 9abdd9a..0bb017b 100644
--- a/net/http/http_auth_handler.cc
+++ b/net/http/http_auth_handler.cc
@@ -1,22 +1,48 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/logging.h"
 #include "net/http/http_auth_handler.h"
 
+#include "base/histogram.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+
 namespace net {
 
-bool HttpAuthHandler::InitFromChallenge(std::string::const_iterator begin,
-                                        std::string::const_iterator end,
-                                        HttpAuth::Target target,
-                                        const GURL& origin) {
+HttpAuthHandler::HttpAuthHandler()
+    : score_(-1),
+      target_(HttpAuth::AUTH_NONE),
+      properties_(-1),
+      original_callback_(NULL),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          wrapper_callback_(
+              this, &HttpAuthHandler::OnGenerateAuthTokenComplete)) {
+}
+
+HttpAuthHandler::~HttpAuthHandler() {
+}
+
+//static
+std::string HttpAuthHandler::GenerateHistogramNameFromScheme(
+    const std::string& scheme) {
+  return StringPrintf("Net.AuthGenerateToken_%s", scheme.c_str());
+}
+
+bool HttpAuthHandler::InitFromChallenge(
+    HttpAuth::ChallengeTokenizer* challenge,
+    HttpAuth::Target target,
+    const GURL& origin,
+    const BoundNetLog& net_log) {
   origin_ = origin;
   target_ = target;
   score_ = -1;
   properties_ = -1;
+  net_log_ = net_log;
 
-  bool ok = Init(begin, end);
+  auth_challenge_ = challenge->challenge_text();
+  bool ok = Init(challenge);
 
   // Init() is expected to set the scheme, realm, score, and properties.  The
   // realm may be empty.
@@ -24,7 +50,69 @@
   DCHECK(!ok || score_ != -1);
   DCHECK(!ok || properties_ != -1);
 
+  if (ok)
+    histogram_ = Histogram::FactoryTimeGet(
+        GenerateHistogramNameFromScheme(scheme()),
+        base::TimeDelta::FromMilliseconds(1),
+        base::TimeDelta::FromSeconds(10), 50,
+        Histogram::kUmaTargetedHistogramFlag);
+
   return ok;
 }
 
+namespace {
+
+NetLog::EventType EventTypeFromAuthTarget(HttpAuth::Target target) {
+  switch (target) {
+    case HttpAuth::AUTH_PROXY:
+      return NetLog::TYPE_AUTH_PROXY;
+    case HttpAuth::AUTH_SERVER:
+      return NetLog::TYPE_AUTH_SERVER;
+    default:
+      NOTREACHED();
+      return NetLog::TYPE_CANCELLED;
+  }
+}
+
+}  // namespace
+
+int HttpAuthHandler::GenerateAuthToken(const std::wstring* username,
+                                       const std::wstring* password,
+                                       const HttpRequestInfo* request,
+                                       CompletionCallback* callback,
+                                       std::string* auth_token) {
+  // TODO(cbentzel): Enforce non-NULL callback after cleaning up SocketStream.
+  DCHECK(request);
+  DCHECK((username == NULL) == (password == NULL));
+  DCHECK(username != NULL || AllowsDefaultCredentials());
+  DCHECK(auth_token != NULL);
+  DCHECK(original_callback_ == NULL);
+  DCHECK(histogram_.get());
+  original_callback_ = callback;
+  net_log_.BeginEvent(EventTypeFromAuthTarget(target_), NULL);
+  generate_auth_token_start_ =  base::TimeTicks::Now();
+  int rv = GenerateAuthTokenImpl(username, password, request,
+                                 &wrapper_callback_, auth_token);
+  if (rv != ERR_IO_PENDING)
+    FinishGenerateAuthToken();
+  return rv;
+}
+
+void HttpAuthHandler::OnGenerateAuthTokenComplete(int rv) {
+  CompletionCallback* callback = original_callback_;
+  FinishGenerateAuthToken();
+  if (callback)
+    callback->Run(rv);
+}
+
+void HttpAuthHandler::FinishGenerateAuthToken() {
+  // TOOD(cbentzel): Should this be done in OK case only?
+  DCHECK(histogram_.get());
+  base::TimeDelta generate_auth_token_duration =
+      base::TimeTicks::Now() - generate_auth_token_start_;
+  histogram_->AddTime(generate_auth_token_duration);
+  net_log_.EndEvent(EventTypeFromAuthTarget(target_), NULL);
+  original_callback_ = NULL;
+}
+
 }  // namespace net
diff --git a/net/http/http_auth_handler.h b/net/http/http_auth_handler.h
index 02a410e..ad8c939 100644
--- a/net/http/http_auth_handler.h
+++ b/net/http/http_auth_handler.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -7,25 +7,60 @@
 
 #include <string>
 
-#include "base/ref_counted.h"
+#include "base/time.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_log.h"
 #include "net/http/http_auth.h"
 
+class Histogram;
+
 namespace net {
 
-class HttpRequestInfo;
+class HostResolver;
 class ProxyInfo;
+struct HttpRequestInfo;
 
 // HttpAuthHandler is the interface for the authentication schemes
-// (basic, digest, ...)
-// The registry mapping auth-schemes to implementations is hardcoded in
-// HttpAuth::CreateAuthHandler().
-class HttpAuthHandler : public base::RefCounted<HttpAuthHandler> {
+// (basic, digest, NTLM, Negotiate).
+// HttpAuthHandler objects are typically created by an HttpAuthHandlerFactory.
+class HttpAuthHandler {
  public:
-  // Initialize the handler by parsing a challenge string.
-  bool InitFromChallenge(std::string::const_iterator begin,
-                         std::string::const_iterator end,
+  HttpAuthHandler();
+  virtual ~HttpAuthHandler();
+
+  // Initializes the handler using a challenge issued by a server.
+  // |challenge| must be non-NULL and have already tokenized the
+  // authentication scheme, but none of the tokens occuring after the
+  // authentication scheme. |target| and |origin| are both stored
+  // for later use, and are not part of the initial challenge.
+  bool InitFromChallenge(HttpAuth::ChallengeTokenizer* challenge,
                          HttpAuth::Target target,
-                         const GURL& origin);
+                         const GURL& origin,
+                         const BoundNetLog& net_log);
+
+  // Generates an authentication token, potentially asynchronously.
+  //
+  // When |username| and |password| are NULL, the default credentials for
+  // the currently logged in user are used. |AllowsDefaultCredentials()| MUST be
+  // true in this case.
+  //
+  // |request|, |callback|, and |auth_token| must be non-NULL.
+  //
+  // The return value is a net error code.
+  // If |OK| is returned, |*auth_token| is filled in with an authentication
+  // token which can be inserted in the HTTP request.
+  // If |ERR_IO_PENDING| is returned, |*auth_token| will be filled in
+  // asynchronously and |callback| will be invoked. The lifetime of
+  // |request|, |callback|, and |auth_token| must last until |callback| is
+  // invoked, but |username| and |password| are only used during the initial
+  // call.
+  // Otherwise, there was a problem generating a token synchronously, and the
+  // value of |*auth_token| is unspecified.
+  int GenerateAuthToken(const std::wstring* username,
+                        const std::wstring* password,
+                        const HttpRequestInfo* request,
+                        CompletionCallback* callback,
+                        std::string* auth_token);
 
   // Lowercase name of the auth scheme
   const std::string& scheme() const {
@@ -37,6 +72,11 @@
     return realm_;
   }
 
+  // The challenge which was issued when creating the handler.
+  const std::string challenge() const {
+    return auth_challenge_;
+  }
+
   // Numeric rank based on the challenge's security level. Higher
   // numbers are better. Used by HttpAuth::ChooseBestChallenge().
   int score() const {
@@ -72,11 +112,12 @@
   // single-round schemes.
   virtual bool IsFinalRound() { return true; }
 
-  // Generate the Authorization header value.
-  virtual std::string GenerateCredentials(const std::wstring& username,
-                                          const std::wstring& password,
-                                          const HttpRequestInfo* request,
-                                          const ProxyInfo* proxy) = 0;
+  // Returns whether the default credentials may be used for the |origin| passed
+  // into |InitFromChallenge|. If true, the user does not need to be prompted
+  // for username and password to establish credentials.
+  // NOTE: SSO is a potential security risk.
+  // TODO(cbentzel): Add a pointer to Firefox documentation about risk.
+  virtual bool AllowsDefaultCredentials() { return false; }
 
  protected:
   enum Property {
@@ -84,24 +125,34 @@
     IS_CONNECTION_BASED = 1 << 1,
   };
 
-  friend class base::RefCounted<HttpAuthHandler>;
-
-  virtual ~HttpAuthHandler() { }
-
-  // Initialize the handler by parsing a challenge string.
+  // Initializes the handler using a challenge issued by a server.
+  // |challenge| must be non-NULL and have already tokenized the
+  // authentication scheme, but none of the tokens occuring after the
+  // authentication scheme.
   // Implementations are expcted to initialize the following members:
   // scheme_, realm_, score_, properties_
-  virtual bool Init(std::string::const_iterator challenge_begin,
-                    std::string::const_iterator challenge_end) = 0;
+  virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) = 0;
 
-  // The lowercase auth-scheme {"basic", "digest", "ntlm", ...}
+  // |GenerateAuthTokenImpl()} is the auth-scheme specific implementation
+  // of generating the next auth token. Callers sohuld use |GenerateAuthToken()|
+  // which will in turn call |GenerateAuthTokenImpl()|
+  virtual int GenerateAuthTokenImpl(const std::wstring* username,
+                                    const std::wstring* password,
+                                    const HttpRequestInfo* request,
+                                    CompletionCallback* callback,
+                                    std::string* auth_token) = 0;
+
+  // The lowercase auth-scheme {"basic", "digest", "ntlm", "negotiate"}
   std::string scheme_;
 
   // The realm.  Used by "basic" and "digest".
   std::string realm_;
 
+  // The auth challenge.
+  std::string auth_challenge_;
+
   // The {scheme, host, port} for the authentication target.  Used by "ntlm"
-  // to construct the service principal name.
+  // and "negotiate" to construct the service principal name.
   GURL origin_;
 
   // The score for this challenge. Higher numbers are better.
@@ -113,6 +164,19 @@
 
   // A bitmask of the properties of the authentication scheme.
   int properties_;
+
+  BoundNetLog net_log_;
+
+ private:
+  void OnGenerateAuthTokenComplete(int rv);
+  void FinishGenerateAuthToken();
+  static std::string GenerateHistogramNameFromScheme(const std::string& scheme);
+
+  CompletionCallback* original_callback_;
+  CompletionCallbackImpl<HttpAuthHandler> wrapper_callback_;
+  // When GenerateAuthToken was called.
+  base::TimeTicks generate_auth_token_start_;
+  scoped_refptr<Histogram> histogram_;
 };
 
 }  // namespace net
diff --git a/net/http/http_auth_handler_basic.cc b/net/http/http_auth_handler_basic.cc
index 71c310c..efd8c5e 100644
--- a/net/http/http_auth_handler_basic.cc
+++ b/net/http/http_auth_handler_basic.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -8,6 +8,8 @@
 
 #include "base/base64.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_auth.h"
 
 namespace net {
@@ -20,38 +22,63 @@
 //
 // We allow it to be compatibility with certain embedded webservers that don't
 // include a realm (see http://crbug.com/20984.)
-bool HttpAuthHandlerBasic::Init(std::string::const_iterator challenge_begin,
-                                std::string::const_iterator challenge_end) {
+bool HttpAuthHandlerBasic::Init(HttpAuth::ChallengeTokenizer* challenge) {
   scheme_ = "basic";
   score_ = 1;
   properties_ = 0;
 
   // Verify the challenge's auth-scheme.
-  HttpAuth::ChallengeTokenizer challenge_tok(challenge_begin, challenge_end);
-  if (!challenge_tok.valid() ||
-      !LowerCaseEqualsASCII(challenge_tok.scheme(), "basic"))
+  if (!challenge->valid() ||
+      !LowerCaseEqualsASCII(challenge->scheme(), "basic"))
     return false;
 
   // Extract the realm (may be missing).
-  while (challenge_tok.GetNext()) {
-    if (LowerCaseEqualsASCII(challenge_tok.name(), "realm"))
-      realm_ = challenge_tok.unquoted_value();
+  while (challenge->GetNext()) {
+    if (LowerCaseEqualsASCII(challenge->name(), "realm"))
+      realm_ = challenge->unquoted_value();
   }
 
-  return challenge_tok.valid();
+  return challenge->valid();
 }
 
-std::string HttpAuthHandlerBasic::GenerateCredentials(
-    const std::wstring& username,
-    const std::wstring& password,
+int HttpAuthHandlerBasic::GenerateAuthTokenImpl(
+    const std::wstring* username,
+    const std::wstring* password,
     const HttpRequestInfo*,
-    const ProxyInfo*) {
+    CompletionCallback*,
+    std::string* auth_token) {
   // TODO(eroman): is this the right encoding of username/password?
   std::string base64_username_password;
-  if (!base::Base64Encode(WideToUTF8(username) + ":" + WideToUTF8(password),
-                          &base64_username_password))
-    return std::string();  // FAIL
-  return std::string("Basic ") + base64_username_password;
+  if (!base::Base64Encode(WideToUTF8(*username) + ":" + WideToUTF8(*password),
+                          &base64_username_password)) {
+    LOG(ERROR) << "Unexpected problem Base64 encoding.";
+    return ERR_UNEXPECTED;
+  }
+  *auth_token = "Basic " + base64_username_password;
+  return OK;
+}
+
+HttpAuthHandlerBasic::Factory::Factory() {
+}
+
+HttpAuthHandlerBasic::Factory::~Factory() {
+}
+
+int HttpAuthHandlerBasic::Factory::CreateAuthHandler(
+    HttpAuth::ChallengeTokenizer* challenge,
+    HttpAuth::Target target,
+    const GURL& origin,
+    CreateReason reason,
+    int digest_nonce_count,
+    const BoundNetLog& net_log,
+    scoped_ptr<HttpAuthHandler>* handler) {
+  // TODO(cbentzel): Move towards model of parsing in the factory
+  //                 method and only constructing when valid.
+  scoped_ptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerBasic());
+  if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
+    return ERR_INVALID_RESPONSE;
+  handler->swap(tmp_handler);
+  return OK;
 }
 
 }  // namespace net
diff --git a/net/http/http_auth_handler_basic.h b/net/http/http_auth_handler_basic.h
index 679c91a..6ecb80a 100644
--- a/net/http/http_auth_handler_basic.h
+++ b/net/http/http_auth_handler_basic.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -6,19 +6,35 @@
 #define NET_HTTP_HTTP_AUTH_HANDLER_BASIC_H_
 
 #include "net/http/http_auth_handler.h"
+#include "net/http/http_auth_handler_factory.h"
 
 namespace net {
 
 // Code for handling http basic authentication.
 class HttpAuthHandlerBasic : public HttpAuthHandler {
  public:
-  virtual std::string GenerateCredentials(const std::wstring& username,
-                                          const std::wstring& password,
-                                          const HttpRequestInfo*,
-                                          const ProxyInfo*);
+  class Factory : public HttpAuthHandlerFactory {
+   public:
+    Factory();
+    virtual ~Factory();
+
+    virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+                                  HttpAuth::Target target,
+                                  const GURL& origin,
+                                  CreateReason reason,
+                                  int digest_nonce_count,
+                                  const BoundNetLog& net_log,
+                                  scoped_ptr<HttpAuthHandler>* handler);
+  };
+
  protected:
-  virtual bool Init(std::string::const_iterator challenge_begin,
-                    std::string::const_iterator challenge_end);
+  virtual bool Init(HttpAuth::ChallengeTokenizer* challenge);
+
+  virtual int GenerateAuthTokenImpl(const std::wstring* username,
+                                    const std::wstring* password,
+                                    const HttpRequestInfo* request,
+                                    CompletionCallback* callback,
+                                    std::string* auth_token);
 
  private:
   ~HttpAuthHandlerBasic() {}
diff --git a/net/http/http_auth_handler_basic_unittest.cc b/net/http/http_auth_handler_basic_unittest.cc
index d7a1437..12e8830 100644
--- a/net/http/http_auth_handler_basic_unittest.cc
+++ b/net/http/http_auth_handler_basic_unittest.cc
@@ -1,15 +1,17 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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 "testing/gtest/include/gtest/gtest.h"
 
 #include "base/basictypes.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_auth_handler_basic.h"
+#include "net/http/http_request_info.h"
 
 namespace net {
 
-TEST(HttpAuthHandlerBasicTest, GenerateCredentials) {
+TEST(HttpAuthHandlerBasicTest, GenerateAuthToken) {
   static const struct {
     const wchar_t* username;
     const wchar_t* password;
@@ -24,48 +26,53 @@
     { L"", L"", "Basic Og==" },
   };
   GURL origin("http://www.example.com");
+  HttpAuthHandlerBasic::Factory factory;
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
     std::string challenge = "Basic realm=\"Atlantis\"";
-    scoped_refptr<HttpAuthHandlerBasic> basic = new HttpAuthHandlerBasic;
-    bool ok = basic->InitFromChallenge(challenge.begin(), challenge.end(),
-                                       HttpAuth::AUTH_SERVER, origin);
-    EXPECT_TRUE(ok);
-    std::string credentials = basic->GenerateCredentials(tests[i].username,
-                                                         tests[i].password,
-                                                         NULL, NULL);
-    EXPECT_STREQ(tests[i].expected_credentials, credentials.c_str());
+    scoped_ptr<HttpAuthHandler> basic;
+    EXPECT_EQ(OK, factory.CreateAuthHandlerFromString(
+        challenge, HttpAuth::AUTH_SERVER, origin, BoundNetLog(), &basic));
+    std::wstring username(tests[i].username);
+    std::wstring password(tests[i].password);
+    HttpRequestInfo request_info;
+    std::string auth_token;
+    int rv = basic->GenerateAuthToken(&username, &password, &request_info,
+                                      NULL, &auth_token);
+    EXPECT_EQ(OK, rv);
+    EXPECT_STREQ(tests[i].expected_credentials, auth_token.c_str());
   }
 }
 
 TEST(HttpAuthHandlerBasicTest, InitFromChallenge) {
   static const struct {
     const char* challenge;
-    bool expected_success;
+    int expected_rv;
     const char* expected_realm;
   } tests[] = {
     // No realm (we allow this even though realm is supposed to be required
     // according to RFC 2617.)
     {
       "Basic",
-      true,
+      OK,
       "",
     },
 
     // Realm is empty string.
     {
       "Basic realm=\"\"",
-      true,
+      OK,
       "",
     },
   };
+  HttpAuthHandlerBasic::Factory factory;
   GURL origin("http://www.example.com");
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
     std::string challenge = tests[i].challenge;
-    scoped_refptr<HttpAuthHandlerBasic> basic = new HttpAuthHandlerBasic;
-    bool ok = basic->InitFromChallenge(challenge.begin(), challenge.end(),
-                                       HttpAuth::AUTH_SERVER, origin);
-    EXPECT_EQ(tests[i].expected_success, ok);
-    if (ok)
+    scoped_ptr<HttpAuthHandler> basic;
+    int rv = factory.CreateAuthHandlerFromString(
+        challenge, HttpAuth::AUTH_SERVER, origin, BoundNetLog(), &basic);
+    EXPECT_EQ(tests[i].expected_rv, rv);
+    if (rv == OK)
       EXPECT_EQ(tests[i].expected_realm, basic->realm());
   }
 }
diff --git a/net/http/http_auth_handler_digest.cc b/net/http/http_auth_handler_digest.cc
index a0c443c..90090a6 100644
--- a/net/http/http_auth_handler_digest.cc
+++ b/net/http/http_auth_handler_digest.cc
@@ -1,12 +1,15 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/http/http_auth_handler_digest.h"
 
+#include "base/logging.h"
 #include "base/md5.h"
 #include "base/rand_util.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/net_errors.h"
 #include "net/base/net_util.h"
 #include "net/http/http_auth.h"
 #include "net/http/http_request_info.h"
@@ -42,10 +45,15 @@
 //=====================+==========================================+
 
 
+//static
+bool HttpAuthHandlerDigest::fixed_cnonce_ = false;
+
 // static
 std::string HttpAuthHandlerDigest::GenerateNonce() {
   // This is how mozilla generates their cnonce -- a 16 digit hex string.
   static const char domain[] = "0123456789abcdef";
+  if (fixed_cnonce_)
+    return std::string(domain);
   std::string cnonce;
   cnonce.reserve(16);
   for (int i = 0; i < 16; ++i)
@@ -77,40 +85,34 @@
   }
 }
 
-std::string HttpAuthHandlerDigest::GenerateCredentials(
-    const std::wstring& username,
-    const std::wstring& password,
+int HttpAuthHandlerDigest::GenerateAuthTokenImpl(
+    const std::wstring* username,
+    const std::wstring* password,
     const HttpRequestInfo* request,
-    const ProxyInfo* proxy) {
+    CompletionCallback* callback,
+    std::string* auth_token) {
   // Generate a random client nonce.
   std::string cnonce = GenerateNonce();
 
-  // The nonce-count should be incremented after re-use per the spec.
-  // This may not be possible when there are multiple connections to the
-  // server though:
-  // https://bugzilla.mozilla.org/show_bug.cgi?id=114451
-  int nonce_count = ++nonce_count_;
-
   // Extract the request method and path -- the meaning of 'path' is overloaded
   // in certain cases, to be a hostname.
   std::string method;
   std::string path;
-  GetRequestMethodAndPath(request, proxy, &method, &path);
+  GetRequestMethodAndPath(request, &method, &path);
 
-  return AssembleCredentials(method, path,
-                             // TODO(eroman): is this the right encoding?
-                             WideToUTF8(username),
-                             WideToUTF8(password),
-                             cnonce, nonce_count);
+  *auth_token = AssembleCredentials(method, path,
+                                    // TODO(eroman): is this the right encoding?
+                                    WideToUTF8(*username),
+                                    WideToUTF8(*password),
+                                    cnonce, nonce_count_);
+  return OK;
 }
 
 void HttpAuthHandlerDigest::GetRequestMethodAndPath(
     const HttpRequestInfo* request,
-    const ProxyInfo* proxy,
     std::string* method,
     std::string* path) const {
   DCHECK(request);
-  DCHECK(proxy);
 
   const GURL& url = request->url;
 
@@ -158,7 +160,7 @@
   std::string nc = StringPrintf("%08x", nonce_count);
 
   std::string authorization = std::string("Digest username=") +
-      HttpUtil::Quote(username);
+                              HttpUtil::Quote(username);
   authorization += ", realm=" + HttpUtil::Quote(realm_);
   authorization += ", nonce=" + HttpUtil::Quote(nonce_);
   authorization += ", uri=" + HttpUtil::Quote(path);
@@ -204,8 +206,7 @@
 // send the realm (See http://crbug.com/20984 for an instance where a
 // webserver was not sending the realm with a BASIC challenge).
 bool HttpAuthHandlerDigest::ParseChallenge(
-    std::string::const_iterator challenge_begin,
-    std::string::const_iterator challenge_end) {
+    HttpAuth::ChallengeTokenizer* challenge) {
   scheme_ = "digest";
   score_ = 2;
   properties_ = ENCRYPTS_IDENTITY;
@@ -216,24 +217,23 @@
   qop_ = QOP_UNSPECIFIED;
   realm_ = nonce_ = domain_ = opaque_ = std::string();
 
-  HttpAuth::ChallengeTokenizer props(challenge_begin, challenge_end);
-
-  if (!props.valid() || !LowerCaseEqualsASCII(props.scheme(), "digest"))
+  if (!challenge->valid() ||
+      !LowerCaseEqualsASCII(challenge->scheme(), "digest"))
     return false; // FAIL -- Couldn't match auth-scheme.
 
   // Loop through all the properties.
-  while (props.GetNext()) {
-    if (props.value().empty()) {
+  while (challenge->GetNext()) {
+    if (challenge->value().empty()) {
       DLOG(INFO) << "Invalid digest property";
       return false;
     }
 
-    if (!ParseChallengeProperty(props.name(), props.unquoted_value()))
+    if (!ParseChallengeProperty(challenge->name(), challenge->unquoted_value()))
       return false; // FAIL -- couldn't parse a property.
   }
 
   // Check if tokenizer failed.
-  if (!props.valid())
+  if (!challenge->valid())
     return false; // FAIL
 
   // Check that a minimum set of properties were provided.
@@ -283,4 +283,28 @@
   return true;
 }
 
+HttpAuthHandlerDigest::Factory::Factory() {
+}
+
+HttpAuthHandlerDigest::Factory::~Factory() {
+}
+
+int HttpAuthHandlerDigest::Factory::CreateAuthHandler(
+    HttpAuth::ChallengeTokenizer* challenge,
+    HttpAuth::Target target,
+    const GURL& origin,
+    CreateReason reason,
+    int digest_nonce_count,
+    const BoundNetLog& net_log,
+    scoped_ptr<HttpAuthHandler>* handler) {
+  // TODO(cbentzel): Move towards model of parsing in the factory
+  //                 method and only constructing when valid.
+  scoped_ptr<HttpAuthHandler> tmp_handler(
+      new HttpAuthHandlerDigest(digest_nonce_count));
+  if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
+    return ERR_INVALID_RESPONSE;
+  handler->swap(tmp_handler);
+  return OK;
+}
+
 }  // namespace net
diff --git a/net/http/http_auth_handler_digest.h b/net/http/http_auth_handler_digest.h
index b4c8687..2aa9028 100644
--- a/net/http/http_auth_handler_digest.h
+++ b/net/http/http_auth_handler_digest.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -6,6 +6,7 @@
 #define NET_HTTP_HTTP_AUTH_HANDLER_DIGEST_H_
 
 #include "net/http/http_auth_handler.h"
+#include "net/http/http_auth_handler_factory.h"
 
 // This is needed for the FRIEND_TEST() macro.
 #include "testing/gtest/include/gtest/gtest_prod.h"
@@ -15,21 +16,35 @@
 // Code for handling http digest authentication.
 class HttpAuthHandlerDigest : public HttpAuthHandler {
  public:
-  virtual std::string GenerateCredentials(const std::wstring& username,
-                                          const std::wstring& password,
-                                          const HttpRequestInfo* request,
-                                          const ProxyInfo* proxy);
+  class Factory : public HttpAuthHandlerFactory {
+   public:
+    Factory();
+    virtual ~Factory();
+
+    virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+                                  HttpAuth::Target target,
+                                  const GURL& origin,
+                                  CreateReason reason,
+                                  int digest_nonce_count,
+                                  const BoundNetLog& net_log,
+                                  scoped_ptr<HttpAuthHandler>* handler);
+  };
 
  protected:
-  virtual bool Init(std::string::const_iterator challenge_begin,
-                    std::string::const_iterator challenge_end) {
-    nonce_count_ = 0;
-    return ParseChallenge(challenge_begin, challenge_end);
+  virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) {
+    return ParseChallenge(challenge);
   }
 
+  virtual int GenerateAuthTokenImpl(const std::wstring* username,
+                                    const std::wstring* password,
+                                    const HttpRequestInfo* request,
+                                    CompletionCallback* callback,
+                                    std::string* auth_token);
+
  private:
   FRIEND_TEST(HttpAuthHandlerDigestTest, ParseChallenge);
   FRIEND_TEST(HttpAuthHandlerDigestTest, AssembleCredentials);
+  FRIEND_TEST(HttpNetworkTransactionTest, DigestPreAuthNonceCount);
 
   // Possible values for the "algorithm" property.
   enum DigestAlgorithm {
@@ -53,12 +68,13 @@
     QOP_AUTH_INT = 1 << 1,
   };
 
+  explicit HttpAuthHandlerDigest(int nonce_count) : nonce_count_(nonce_count) {}
+
   ~HttpAuthHandlerDigest() {}
 
   // Parse the challenge, saving the results into this instance.
   // Returns true on success.
-  bool ParseChallenge(std::string::const_iterator challenge_begin,
-                      std::string::const_iterator challenge_end);
+  bool ParseChallenge(HttpAuth::ChallengeTokenizer* challenge);
 
   // Parse an individual property. Returns true on success.
   bool ParseChallengeProperty(const std::string& name,
@@ -74,7 +90,6 @@
   // Extract the method and path of the request, as needed by
   // the 'A2' production. (path may be a hostname for proxy).
   void GetRequestMethodAndPath(const HttpRequestInfo* request,
-                               const ProxyInfo* proxy,
                                std::string* method,
                                std::string* path) const;
 
@@ -94,6 +109,11 @@
                                   const std::string& cnonce,
                                   int nonce_count) const;
 
+  // Forces cnonce to be the same each time. This is used for unit tests.
+  static void SetFixedCnonce(bool fixed_cnonce) {
+    fixed_cnonce_ = fixed_cnonce;
+  }
+
   // Information parsed from the challenge.
   std::string nonce_;
   std::string domain_;
@@ -103,6 +123,9 @@
   int qop_; // Bitfield of QualityOfProtection
 
   int nonce_count_;
+
+  // Forces the cnonce to be the same each time, for unit tests.
+  static bool fixed_cnonce_;
 };
 
 }  // namespace net
diff --git a/net/http/http_auth_handler_digest_unittest.cc b/net/http/http_auth_handler_digest_unittest.cc
index eb5649b..b1782f6 100644
--- a/net/http/http_auth_handler_digest_unittest.cc
+++ b/net/http/http_auth_handler_digest_unittest.cc
@@ -1,10 +1,11 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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 "testing/gtest/include/gtest/gtest.h"
 
 #include "base/basictypes.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_auth_handler_digest.h"
 
 namespace net {
@@ -63,7 +64,7 @@
 
     { // Check that md5-sess is recognized, as is single QOP
       "Digest nonce=\"xyz\", algorithm=\"md5-sess\", "
-          "realm=\"Oblivion\", qop=\"auth\"",
+      "realm=\"Oblivion\", qop=\"auth\"",
       true,
       "Oblivion",
       "xyz",
@@ -100,13 +101,25 @@
     }
   };
 
+  GURL origin("http://www.example.com");
+  scoped_ptr<HttpAuthHandlerDigest::Factory> factory(
+      new HttpAuthHandlerDigest::Factory());
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
-    std::string challenge(tests[i].challenge);
-
-    scoped_refptr<HttpAuthHandlerDigest> digest = new HttpAuthHandlerDigest;
-    bool ok = digest->ParseChallenge(challenge.begin(), challenge.end());
-
-    EXPECT_EQ(tests[i].parsed_success, ok);
+    scoped_ptr<HttpAuthHandler> handler;
+    int rv = factory->CreateAuthHandlerFromString(tests[i].challenge,
+                                                  HttpAuth::AUTH_SERVER,
+                                                  origin,
+                                                  BoundNetLog(),
+                                                  &handler);
+    if (tests[i].parsed_success) {
+      EXPECT_EQ(OK, rv);
+    } else {
+      EXPECT_NE(OK, rv);
+      continue;
+    }
+    ASSERT_TRUE(handler != NULL);
+    HttpAuthHandlerDigest* digest =
+        static_cast<HttpAuthHandlerDigest*>(handler.get());
     EXPECT_STREQ(tests[i].parsed_realm, digest->realm_.c_str());
     EXPECT_STREQ(tests[i].parsed_nonce, digest->nonce_.c_str());
     EXPECT_STREQ(tests[i].parsed_domain, digest->domain_.c_str());
@@ -249,15 +262,26 @@
     }
   };
   GURL origin("http://www.example.com");
+  scoped_ptr<HttpAuthHandlerDigest::Factory> factory(
+      new HttpAuthHandlerDigest::Factory());
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
-    scoped_refptr<HttpAuthHandlerDigest> digest = new HttpAuthHandlerDigest;
-    std::string challenge = tests[i].challenge;
-    EXPECT_TRUE(digest->InitFromChallenge(
-        challenge.begin(), challenge.end(), HttpAuth::AUTH_SERVER, origin));
+    scoped_ptr<HttpAuthHandler> handler;
+    int rv = factory->CreateAuthHandlerFromString(tests[i].challenge,
+                                                  HttpAuth::AUTH_SERVER,
+                                                  origin,
+                                                  BoundNetLog(),
+                                                  &handler);
+    EXPECT_EQ(OK, rv);
+    ASSERT_TRUE(handler != NULL);
 
+    HttpAuthHandlerDigest* digest =
+        static_cast<HttpAuthHandlerDigest*>(handler.get());
     std::string creds = digest->AssembleCredentials(tests[i].req_method,
-        tests[i].req_path, tests[i].username, tests[i].password,
-        tests[i].cnonce, tests[i].nonce_count);
+                                                    tests[i].req_path,
+                                                    tests[i].username,
+                                                    tests[i].password,
+                                                    tests[i].cnonce,
+                                                    tests[i].nonce_count);
 
     EXPECT_STREQ(tests[i].expected_creds, creds.c_str());
   }
diff --git a/net/http/http_auth_handler_factory.cc b/net/http/http_auth_handler_factory.cc
new file mode 100644
index 0000000..c2d011b
--- /dev/null
+++ b/net/http/http_auth_handler_factory.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2010 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/http/http_auth_handler_factory.h"
+
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_auth_filter.h"
+#include "net/http/http_auth_handler_basic.h"
+#include "net/http/http_auth_handler_digest.h"
+#include "net/http/http_auth_handler_negotiate.h"
+#include "net/http/http_auth_handler_ntlm.h"
+
+namespace net {
+
+int HttpAuthHandlerFactory::CreateAuthHandlerFromString(
+    const std::string& challenge,
+    HttpAuth::Target target,
+    const GURL& origin,
+    const BoundNetLog& net_log,
+    scoped_ptr<HttpAuthHandler>* handler) {
+  HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end());
+  return CreateAuthHandler(&props, target, origin, CREATE_CHALLENGE, 1,
+                           net_log, handler);
+}
+
+int HttpAuthHandlerFactory::CreatePreemptiveAuthHandlerFromString(
+    const std::string& challenge,
+    HttpAuth::Target target,
+    const GURL& origin,
+    int digest_nonce_count,
+    const BoundNetLog& net_log,
+    scoped_ptr<HttpAuthHandler>* handler) {
+  HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end());
+  return CreateAuthHandler(&props, target, origin, CREATE_PREEMPTIVE,
+                           digest_nonce_count, net_log, handler);
+}
+
+// static
+HttpAuthHandlerRegistryFactory* HttpAuthHandlerFactory::CreateDefault() {
+  HttpAuthHandlerRegistryFactory* registry_factory =
+      new HttpAuthHandlerRegistryFactory();
+  registry_factory->RegisterSchemeFactory(
+      "basic", new HttpAuthHandlerBasic::Factory());
+  registry_factory->RegisterSchemeFactory(
+      "digest", new HttpAuthHandlerDigest::Factory());
+  registry_factory->RegisterSchemeFactory(
+      "negotiate", new HttpAuthHandlerNegotiate::Factory());
+  registry_factory->RegisterSchemeFactory(
+      "ntlm", new HttpAuthHandlerNTLM::Factory());
+  return registry_factory;
+}
+
+HttpAuthHandlerRegistryFactory::HttpAuthHandlerRegistryFactory() {
+}
+
+HttpAuthHandlerRegistryFactory::~HttpAuthHandlerRegistryFactory() {
+  STLDeleteContainerPairSecondPointers(factory_map_.begin(),
+                                       factory_map_.end());
+}
+
+void HttpAuthHandlerRegistryFactory::SetURLSecurityManager(
+    const std::string& scheme,
+    URLSecurityManager* security_manager) {
+  HttpAuthHandlerFactory* factory = GetSchemeFactory(scheme);
+  if (factory)
+    factory->set_url_security_manager(security_manager);
+}
+
+void HttpAuthHandlerRegistryFactory::RegisterSchemeFactory(
+    const std::string& scheme,
+    HttpAuthHandlerFactory* factory) {
+  std::string lower_scheme = StringToLowerASCII(scheme);
+  FactoryMap::iterator it = factory_map_.find(lower_scheme);
+  if (it != factory_map_.end()) {
+    delete it->second;
+  }
+  if (factory)
+    factory_map_[lower_scheme] = factory;
+  else
+    factory_map_.erase(it);
+}
+
+int HttpAuthHandlerRegistryFactory::CreateAuthHandler(
+    HttpAuth::ChallengeTokenizer* challenge,
+    HttpAuth::Target target,
+    const GURL& origin,
+    CreateReason reason,
+    int digest_nonce_count,
+    const BoundNetLog& net_log,
+    scoped_ptr<HttpAuthHandler>* handler) {
+  if (!challenge->valid()) {
+    handler->reset();
+    return ERR_INVALID_RESPONSE;
+  }
+  std::string lower_scheme = StringToLowerASCII(challenge->scheme());
+  FactoryMap::iterator it = factory_map_.find(lower_scheme);
+  if (it == factory_map_.end()) {
+    handler->reset();
+    return ERR_UNSUPPORTED_AUTH_SCHEME;
+  }
+  DCHECK(it->second);
+  return it->second->CreateAuthHandler(challenge, target, origin, reason,
+                                       digest_nonce_count, net_log, handler);
+}
+
+HttpAuthHandlerFactory* HttpAuthHandlerRegistryFactory::GetSchemeFactory(
+    const std::string& scheme) const {
+  std::string lower_scheme = StringToLowerASCII(scheme);
+  FactoryMap::const_iterator it = factory_map_.find(lower_scheme);
+  if (it == factory_map_.end()) {
+    return NULL;                  // |scheme| is not registered.
+  }
+  return it->second;
+}
+
+}  // namespace net
diff --git a/net/http/http_auth_handler_factory.h b/net/http/http_auth_handler_factory.h
new file mode 100644
index 0000000..9e55350
--- /dev/null
+++ b/net/http/http_auth_handler_factory.h
@@ -0,0 +1,163 @@
+// Copyright (c) 2010 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_HTTP_HTTP_AUTH_HANDLER_FACTORY_H_
+#define NET_HTTP_HTTP_AUTH_HANDLER_FACTORY_H_
+
+#include <map>
+#include <string>
+
+#include "base/scoped_ptr.h"
+#include "net/http/http_auth.h"
+#include "net/http/url_security_manager.h"
+
+class GURL;
+
+namespace net {
+
+class BoundNetLog;
+class HttpAuthHandler;
+class HttpAuthHandlerRegistryFactory;
+
+// An HttpAuthHandlerFactory is used to create HttpAuthHandler objects.
+class HttpAuthHandlerFactory {
+ public:
+  HttpAuthHandlerFactory() : url_security_manager_(NULL) {}
+  virtual ~HttpAuthHandlerFactory() {}
+
+  // Sets an URL security manager.  HttpAuthHandlerFactory doesn't own the URL
+  // security manager, and the URL security manager should outlive this object.
+  void set_url_security_manager(URLSecurityManager* url_security_manager) {
+    url_security_manager_ = url_security_manager;
+  }
+
+  // Retrieves the associated URL security manager.
+  URLSecurityManager* url_security_manager() {
+    return url_security_manager_;
+  }
+
+  enum CreateReason {
+    CREATE_CHALLENGE,  // Create a handler in response to a challenge.
+    CREATE_PREEMPTIVE,    // Create a handler preemptively.
+  };
+
+  // Creates an HttpAuthHandler object based on the authentication
+  // challenge specified by |*challenge|. |challenge| must point to a valid
+  // non-NULL tokenizer.
+  //
+  // If an HttpAuthHandler object  is successfully created it is passed back to
+  // the caller through |*handler| and OK is returned.
+  //
+  // If |*challenge| specifies an unsupported authentication scheme, |*handler|
+  // is set to NULL and ERR_UNSUPPORTED_AUTH_SCHEME is returned.
+  //
+  // If |*challenge| is improperly formed, |*handler| is set to NULL and
+  // ERR_INVALID_RESPONSE is returned.
+  //
+  // |create_reason| indicates why the handler is being created. This is used
+  // since NTLM and Negotiate schemes do not support preemptive creation.
+  //
+  // |digest_nonce_count| is specifically intended for the Digest authentication
+  // scheme, and indicates the number of handlers generated for a particular
+  // server nonce challenge.
+  //
+  // For the NTLM and Negotiate handlers:
+  // If |origin| does not match the authentication method's filters for
+  // the specified |target|, ERR_INVALID_AUTH_CREDENTIALS is returned.
+  // NOTE: This will apply to ALL |origin| values if the filters are empty.
+  //
+  // |*challenge| should not be reused after a call to |CreateAuthHandler()|,
+  virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+                                HttpAuth::Target target,
+                                const GURL& origin,
+                                CreateReason create_reason,
+                                int digest_nonce_count,
+                                const BoundNetLog& net_log,
+                                scoped_ptr<HttpAuthHandler>* handler) = 0;
+
+  // Creates an HTTP authentication handler based on the authentication
+  // challenge string |challenge|.
+  // This is a convenience function which creates a ChallengeTokenizer for
+  // |challenge| and calls |CreateAuthHandler|. See |CreateAuthHandler| for
+  // more details on return values.
+  int CreateAuthHandlerFromString(const std::string& challenge,
+                                  HttpAuth::Target target,
+                                  const GURL& origin,
+                                  const BoundNetLog& net_log,
+                                  scoped_ptr<HttpAuthHandler>* handler);
+
+  // Creates an HTTP authentication handler based on the authentication
+  // challenge string |challenge|.
+  // This is a convenience function which creates a ChallengeTokenizer for
+  // |challenge| and calls |CreateAuthHandler|. See |CreateAuthHandler| for
+  // more details on return values.
+  int CreatePreemptiveAuthHandlerFromString(
+      const std::string& challenge,
+      HttpAuth::Target target,
+      const GURL& origin,
+      int digest_nonce_count,
+      const BoundNetLog& net_log,
+      scoped_ptr<HttpAuthHandler>* handler);
+
+  // Creates a standard HttpAuthHandlerRegistryFactory. The caller is
+  // responsible for deleting the factory.
+  // The default factory supports Basic, Digest, NTLM, and Negotiate schemes.
+  static HttpAuthHandlerRegistryFactory* CreateDefault();
+
+ private:
+  // The URL security manager
+  URLSecurityManager* url_security_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpAuthHandlerFactory);
+};
+
+// The HttpAuthHandlerRegistryFactory dispatches create requests out
+// to other factories based on the auth scheme.
+class HttpAuthHandlerRegistryFactory : public HttpAuthHandlerFactory {
+ public:
+  HttpAuthHandlerRegistryFactory();
+  virtual ~HttpAuthHandlerRegistryFactory();
+
+  // Sets an URL security manager into the factory associated with |scheme|.
+  void SetURLSecurityManager(const std::string& scheme,
+                             URLSecurityManager* url_security_manager);
+
+  // Registers a |factory| that will be used for a particular HTTP
+  // authentication scheme such as Basic, Digest, or Negotiate.
+  // The |*factory| object is assumed to be new-allocated, and its lifetime
+  // will be managed by this HttpAuthHandlerRegistryFactory object (including
+  // deleting it when it is no longer used.
+  // A NULL |factory| value means that HttpAuthHandlers's will not be created
+  // for |scheme|. If a factory object used to exist for |scheme|, it will be
+  // deleted.
+  void RegisterSchemeFactory(const std::string& scheme,
+                             HttpAuthHandlerFactory* factory);
+
+  // Retrieve the factory for the specified |scheme|. If no factory exists
+  // for the |scheme|, NULL is returned. The returned factory must not be
+  // deleted by the caller, and it is guaranteed to be valid until either
+  // a new factory is registered for the same scheme, or until this
+  // registry factory is destroyed.
+  HttpAuthHandlerFactory* GetSchemeFactory(const std::string& scheme) const;
+
+  // Creates an auth handler by dispatching out to the registered factories
+  // based on the first token in |challenge|.
+  virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+                                HttpAuth::Target target,
+                                const GURL& origin,
+                                CreateReason reason,
+                                int digest_nonce_count,
+                                const BoundNetLog& net_log,
+                                scoped_ptr<HttpAuthHandler>* handler);
+
+ private:
+  typedef std::map<std::string, HttpAuthHandlerFactory*> FactoryMap;
+
+  FactoryMap factory_map_;
+  DISALLOW_COPY_AND_ASSIGN(HttpAuthHandlerRegistryFactory);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_AUTH_HANDLER_FACTORY_H_
diff --git a/net/http/http_auth_handler_factory_unittest.cc b/net/http/http_auth_handler_factory_unittest.cc
new file mode 100644
index 0000000..8dd37f0
--- /dev/null
+++ b/net/http/http_auth_handler_factory_unittest.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2010 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/scoped_ptr.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_auth_handler.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/url_security_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+class MockHttpAuthHandlerFactory : public HttpAuthHandlerFactory {
+ public:
+  explicit MockHttpAuthHandlerFactory(int return_code) :
+      return_code_(return_code) {}
+  virtual ~MockHttpAuthHandlerFactory() {}
+
+  virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+                                HttpAuth::Target target,
+                                const GURL& origin,
+                                CreateReason reason,
+                                int nonce_count,
+                                const BoundNetLog& net_log,
+                                scoped_ptr<HttpAuthHandler>* handler) {
+    handler->reset();
+    return return_code_;
+  }
+
+ private:
+  int return_code_;
+};
+
+}  // namespace
+
+TEST(HttpAuthHandlerFactoryTest, RegistryFactory) {
+  HttpAuthHandlerRegistryFactory registry_factory;
+  GURL gurl("www.google.com");
+  const int kBasicReturnCode = ERR_INVALID_SPDY_STREAM;
+  MockHttpAuthHandlerFactory* mock_factory_basic =
+      new MockHttpAuthHandlerFactory(kBasicReturnCode);
+
+  const int kDigestReturnCode = ERR_PAC_SCRIPT_FAILED;
+  MockHttpAuthHandlerFactory* mock_factory_digest =
+      new MockHttpAuthHandlerFactory(kDigestReturnCode);
+
+  const int kDigestReturnCodeReplace = ERR_SYN_REPLY_NOT_RECEIVED;
+  MockHttpAuthHandlerFactory* mock_factory_digest_replace =
+      new MockHttpAuthHandlerFactory(kDigestReturnCodeReplace);
+
+  scoped_ptr<HttpAuthHandler> handler;
+
+  // No schemes should be supported in the beginning.
+  EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME,
+            registry_factory.CreateAuthHandlerFromString(
+                "Basic", HttpAuth::AUTH_SERVER, gurl, BoundNetLog(), &handler));
+
+  // Test what happens with a single scheme.
+  registry_factory.RegisterSchemeFactory("Basic", mock_factory_basic);
+  EXPECT_EQ(kBasicReturnCode,
+            registry_factory.CreateAuthHandlerFromString(
+                "Basic", HttpAuth::AUTH_SERVER, gurl, BoundNetLog(), &handler));
+  EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME,
+            registry_factory.CreateAuthHandlerFromString(
+                "Digest", HttpAuth::AUTH_SERVER, gurl, BoundNetLog(),
+                &handler));
+
+  // Test multiple schemes
+  registry_factory.RegisterSchemeFactory("Digest", mock_factory_digest);
+  EXPECT_EQ(kBasicReturnCode,
+            registry_factory.CreateAuthHandlerFromString(
+                "Basic", HttpAuth::AUTH_SERVER, gurl, BoundNetLog(), &handler));
+  EXPECT_EQ(kDigestReturnCode,
+            registry_factory.CreateAuthHandlerFromString(
+                "Digest", HttpAuth::AUTH_SERVER, gurl, BoundNetLog(),
+                &handler));
+
+  // Test case-insensitivity
+  EXPECT_EQ(kBasicReturnCode,
+            registry_factory.CreateAuthHandlerFromString(
+                "basic", HttpAuth::AUTH_SERVER, gurl, BoundNetLog(), &handler));
+
+  // Test replacement of existing auth scheme
+  registry_factory.RegisterSchemeFactory("Digest", mock_factory_digest_replace);
+  EXPECT_EQ(kBasicReturnCode,
+            registry_factory.CreateAuthHandlerFromString(
+                "Basic", HttpAuth::AUTH_SERVER, gurl, BoundNetLog(), &handler));
+  EXPECT_EQ(kDigestReturnCodeReplace,
+            registry_factory.CreateAuthHandlerFromString(
+                "Digest", HttpAuth::AUTH_SERVER, gurl, BoundNetLog(),
+                &handler));
+}
+
+TEST(HttpAuthHandlerFactoryTest, DefaultFactory) {
+  URLSecurityManagerAllow url_security_manager;
+  scoped_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory(
+      HttpAuthHandlerFactory::CreateDefault());
+  http_auth_handler_factory->SetURLSecurityManager(
+      "negotiate", &url_security_manager);
+  GURL server_origin("http://www.example.com");
+  GURL proxy_origin("http://cache.example.com:3128");
+  {
+    scoped_ptr<HttpAuthHandler> handler;
+    int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
+        "Basic realm=\"FooBar\"",
+        HttpAuth::AUTH_SERVER,
+        server_origin,
+        BoundNetLog(),
+        &handler);
+    EXPECT_EQ(OK, rv);
+    EXPECT_FALSE(handler.get() == NULL);
+    EXPECT_STREQ("basic", handler->scheme().c_str());
+    EXPECT_STREQ("FooBar", handler->realm().c_str());
+    EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target());
+    EXPECT_FALSE(handler->encrypts_identity());
+    EXPECT_FALSE(handler->is_connection_based());
+  }
+  {
+    scoped_ptr<HttpAuthHandler> handler;
+    int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
+        "UNSUPPORTED realm=\"FooBar\"",
+        HttpAuth::AUTH_SERVER,
+        server_origin,
+        BoundNetLog(),
+        &handler);
+    EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME, rv);
+    EXPECT_TRUE(handler.get() == NULL);
+  }
+  {
+    scoped_ptr<HttpAuthHandler> handler;
+    int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
+        "Digest realm=\"FooBar\", nonce=\"xyz\"",
+        HttpAuth::AUTH_PROXY,
+        proxy_origin,
+        BoundNetLog(),
+        &handler);
+    EXPECT_EQ(OK, rv);
+    EXPECT_FALSE(handler.get() == NULL);
+    EXPECT_STREQ("digest", handler->scheme().c_str());
+    EXPECT_STREQ("FooBar", handler->realm().c_str());
+    EXPECT_EQ(HttpAuth::AUTH_PROXY, handler->target());
+    EXPECT_TRUE(handler->encrypts_identity());
+    EXPECT_FALSE(handler->is_connection_based());
+  }
+  {
+    scoped_ptr<HttpAuthHandler> handler;
+    int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
+        "NTLM",
+        HttpAuth::AUTH_SERVER,
+        server_origin,
+        BoundNetLog(),
+        &handler);
+    EXPECT_EQ(OK, rv);
+    ASSERT_FALSE(handler.get() == NULL);
+    EXPECT_STREQ("ntlm", handler->scheme().c_str());
+    EXPECT_STREQ("", handler->realm().c_str());
+    EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target());
+    EXPECT_TRUE(handler->encrypts_identity());
+    EXPECT_TRUE(handler->is_connection_based());
+  }
+  {
+    scoped_ptr<HttpAuthHandler> handler;
+    int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
+        "Negotiate",
+        HttpAuth::AUTH_SERVER,
+        server_origin,
+        BoundNetLog(),
+        &handler);
+    EXPECT_EQ(OK, rv);
+    EXPECT_FALSE(handler.get() == NULL);
+    EXPECT_STREQ("negotiate", handler->scheme().c_str());
+    EXPECT_STREQ("", handler->realm().c_str());
+    EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target());
+    EXPECT_TRUE(handler->encrypts_identity());
+    EXPECT_TRUE(handler->is_connection_based());
+  }
+}
+
+}  // namespace net
diff --git a/net/http/http_auth_handler_mock.cc b/net/http/http_auth_handler_mock.cc
new file mode 100644
index 0000000..09a0356
--- /dev/null
+++ b/net/http/http_auth_handler_mock.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2010 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/http/http_auth_handler_mock.h"
+
+#include "base/message_loop.h"
+#include "net/base/net_errors.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+HttpAuthHandlerMock::HttpAuthHandlerMock()
+  : resolve_(RESOLVE_INIT), user_callback_(NULL),
+    ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+    generate_async_(false), generate_rv_(OK),
+    auth_token_(NULL),
+    first_round_(true),
+    connection_based_(false) {
+}
+
+HttpAuthHandlerMock::~HttpAuthHandlerMock() {
+}
+
+void HttpAuthHandlerMock::SetResolveExpectation(Resolve resolve) {
+  EXPECT_EQ(RESOLVE_INIT, resolve_);
+  resolve_ = resolve;
+}
+
+bool HttpAuthHandlerMock::NeedsCanonicalName() {
+  switch (resolve_) {
+    case RESOLVE_SYNC:
+    case RESOLVE_ASYNC:
+      return true;
+    case RESOLVE_SKIP:
+      resolve_ = RESOLVE_TESTED;
+      return false;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+int HttpAuthHandlerMock::ResolveCanonicalName(HostResolver* host_resolver,
+                                              CompletionCallback* callback) {
+  EXPECT_NE(RESOLVE_TESTED, resolve_);
+  int rv = OK;
+  switch (resolve_) {
+    case RESOLVE_SYNC:
+      resolve_ = RESOLVE_TESTED;
+      break;
+    case RESOLVE_ASYNC:
+      EXPECT_TRUE(user_callback_ == NULL);
+      rv = ERR_IO_PENDING;
+      user_callback_ = callback;
+      MessageLoop::current()->PostTask(
+          FROM_HERE, method_factory_.NewRunnableMethod(
+              &HttpAuthHandlerMock::OnResolveCanonicalName));
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+  return rv;
+}
+
+void HttpAuthHandlerMock::SetGenerateExpectation(bool async, int rv) {
+  generate_async_ = async;
+  generate_rv_ = rv;
+}
+
+bool HttpAuthHandlerMock::Init(HttpAuth::ChallengeTokenizer* challenge) {
+  scheme_ = "mock";
+  score_ = 1;
+  properties_ = connection_based_ ? IS_CONNECTION_BASED : 0;
+  return true;
+}
+
+int HttpAuthHandlerMock::GenerateAuthTokenImpl(const std::wstring* username,
+                                               const std::wstring* password,
+                                               const HttpRequestInfo* request,
+                                               CompletionCallback* callback,
+                                               std::string* auth_token) {
+  first_round_ = false;
+  if (generate_async_) {
+    EXPECT_TRUE(user_callback_ == NULL);
+    EXPECT_TRUE(auth_token_ == NULL);
+    user_callback_ = callback;
+    auth_token_ = auth_token;
+    MessageLoop::current()->PostTask(
+        FROM_HERE, method_factory_.NewRunnableMethod(
+            &HttpAuthHandlerMock::OnGenerateAuthToken));
+    return ERR_IO_PENDING;
+  } else {
+    if (generate_rv_ == OK)
+      *auth_token = "auth_token";
+    return generate_rv_;
+  }
+}
+
+void HttpAuthHandlerMock::OnResolveCanonicalName() {
+  EXPECT_EQ(RESOLVE_ASYNC, resolve_);
+  EXPECT_TRUE(user_callback_ != NULL);
+  resolve_ = RESOLVE_TESTED;
+  CompletionCallback* callback = user_callback_;
+  user_callback_ = NULL;
+  callback->Run(OK);
+}
+
+void HttpAuthHandlerMock::OnGenerateAuthToken() {
+  EXPECT_TRUE(generate_async_);
+  EXPECT_TRUE(user_callback_ != NULL);
+  if (generate_rv_ == OK)
+    *auth_token_ = "auth_token";
+  auth_token_ = NULL;
+  CompletionCallback* callback = user_callback_;
+  user_callback_ = NULL;
+  callback->Run(generate_rv_);
+}
+
+void HttpAuthHandlerMock::Factory::set_mock_handler(
+    HttpAuthHandler* handler, HttpAuth::Target target) {
+  EXPECT_TRUE(handlers_[target].get() == NULL);
+  handlers_[target].reset(handler);
+}
+
+int HttpAuthHandlerMock::Factory::CreateAuthHandler(
+    HttpAuth::ChallengeTokenizer* challenge,
+    HttpAuth::Target target,
+    const GURL& origin,
+    CreateReason reason,
+    int nonce_count,
+    const BoundNetLog& net_log,
+    scoped_ptr<HttpAuthHandler>* handler) {
+  if (!handlers_[target].get())
+    return ERR_UNEXPECTED;
+  handler->swap(handlers_[target]);
+  return OK;
+}
+
+}  // namespace net
diff --git a/net/http/http_auth_handler_mock.h b/net/http/http_auth_handler_mock.h
new file mode 100644
index 0000000..a0ef4f0
--- /dev/null
+++ b/net/http/http_auth_handler_mock.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2010 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_HTTP_HTTP_AUTH_HANDLER_MOCK_H_
+#define NET_HTTP_HTTP_AUTH_HANDLER_MOCK_H_
+
+#include <string>
+
+#include "base/task.h"
+#include "net/http/http_auth_handler.h"
+#include "net/http/http_auth_handler_factory.h"
+
+namespace net {
+
+// MockAuthHandler is used in tests to reliably trigger edge cases.
+class HttpAuthHandlerMock : public HttpAuthHandler {
+ public:
+  enum Resolve {
+    RESOLVE_INIT,
+    RESOLVE_SKIP,
+    RESOLVE_SYNC,
+    RESOLVE_ASYNC,
+    RESOLVE_TESTED,
+  };
+
+  HttpAuthHandlerMock();
+
+  virtual ~HttpAuthHandlerMock();
+
+  void SetResolveExpectation(Resolve resolve);
+
+  virtual bool NeedsCanonicalName();
+
+  virtual int ResolveCanonicalName(HostResolver* host_resolver,
+                                   CompletionCallback* callback);
+
+  virtual bool NeedsIdentity() { return first_round_; }
+  virtual bool IsFinalRound() { return false; }
+
+  void SetGenerateExpectation(bool async, int rv);
+
+  void set_connection_based(bool connection_based) {
+    connection_based_ = connection_based;
+  }
+
+  // The Factory class simply returns the same handler each time
+  // CreateAuthHandler is called.
+  class Factory : public HttpAuthHandlerFactory {
+   public:
+    Factory() {}
+    virtual ~Factory() {}
+
+    void set_mock_handler(HttpAuthHandler* handler, HttpAuth::Target target);
+
+    virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+                                  HttpAuth::Target target,
+                                  const GURL& origin,
+                                  CreateReason reason,
+                                  int nonce_count,
+                                  const BoundNetLog& net_log,
+                                  scoped_ptr<HttpAuthHandler>* handler);
+
+   private:
+    scoped_ptr<HttpAuthHandler> handlers_[HttpAuth::AUTH_NUM_TARGETS];
+  };
+
+ protected:
+  virtual bool Init(HttpAuth::ChallengeTokenizer* challenge);
+
+  virtual int GenerateAuthTokenImpl(const std::wstring* username,
+                                    const std::wstring* password,
+                                    const HttpRequestInfo* request,
+                                    CompletionCallback* callback,
+                                    std::string* auth_token);
+
+ private:
+  void OnResolveCanonicalName();
+
+  void OnGenerateAuthToken();
+
+  Resolve resolve_;
+  CompletionCallback* user_callback_;
+  ScopedRunnableMethodFactory<HttpAuthHandlerMock> method_factory_;
+  bool generate_async_;
+  int generate_rv_;
+  std::string* auth_token_;
+  bool first_round_;
+  bool connection_based_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_AUTH_HANDLER_MOCK_H_
diff --git a/net/http/http_auth_handler_negotiate.cc b/net/http/http_auth_handler_negotiate.cc
new file mode 100644
index 0000000..d7a9c50
--- /dev/null
+++ b/net/http/http_auth_handler_negotiate.cc
@@ -0,0 +1,316 @@
+// Copyright (c) 2010 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/http/http_auth_handler_negotiate.h"
+
+#include "base/logging.h"
+#include "net/base/address_family.h"
+#include "net/base/host_resolver.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_auth_filter.h"
+#include "net/http/url_security_manager.h"
+
+namespace net {
+
+HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate(
+    AuthLibrary* auth_library,
+#if defined(OS_WIN)
+    ULONG max_token_length,
+#endif
+    URLSecurityManager* url_security_manager,
+    HostResolver* resolver,
+    bool disable_cname_lookup,
+    bool use_port)
+#if defined(OS_WIN)
+    : auth_system_(auth_library, "Negotiate", NEGOSSP_NAME, max_token_length),
+#elif defined(OS_POSIX)
+    : auth_system_(auth_library, "Negotiate", CHROME_GSS_KRB5_MECH_OID_DESC),
+#endif
+      disable_cname_lookup_(disable_cname_lookup),
+      use_port_(use_port),
+      ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(
+          this, &HttpAuthHandlerNegotiate::OnIOComplete)),
+      resolver_(resolver),
+      already_called_(false),
+      has_username_and_password_(false),
+      user_callback_(NULL),
+      auth_token_(NULL),
+      next_state_(STATE_NONE),
+      url_security_manager_(url_security_manager) {
+}
+
+HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() {
+}
+
+int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl(
+    const std::wstring* username,
+    const std::wstring* password,
+    const HttpRequestInfo* request,
+    CompletionCallback* callback,
+    std::string* auth_token) {
+  DCHECK(user_callback_ == NULL);
+  DCHECK((username == NULL) == (password == NULL));
+  DCHECK(auth_token_ == NULL);
+  auth_token_ = auth_token;
+  if (already_called_) {
+    DCHECK((!has_username_and_password_ && username == NULL) ||
+           (has_username_and_password_ && *username == username_ &&
+            *password == password_));
+    next_state_ = STATE_GENERATE_AUTH_TOKEN;
+  } else {
+    already_called_ = true;
+    if (username) {
+      has_username_and_password_ = true;
+      username_ = *username;
+      password_ = *password;
+    }
+    next_state_ = STATE_RESOLVE_CANONICAL_NAME;
+  }
+  int rv = DoLoop(OK);
+  if (rv == ERR_IO_PENDING)
+    user_callback_ = callback;
+  return rv;
+}
+
+// The Negotiate challenge header looks like:
+//   WWW-Authenticate: NEGOTIATE auth-data
+bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) {
+#if defined(OS_POSIX)
+  if (!auth_system_.Init()) {
+    LOG(INFO) << "can't initialize GSSAPI library";
+    return false;
+  }
+  // GSSAPI does not provide a way to enter username/password to
+  // obtain a TGT. If the default credentials are not allowed for
+  // a particular site (based on whitelist), fall back to a
+  // different scheme.
+  if (!AllowsDefaultCredentials())
+    return false;
+#endif
+  scheme_ = "negotiate";
+  score_ = 4;
+  properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED;
+  return auth_system_.ParseChallenge(challenge);
+}
+
+// Require identity on first pass instead of second.
+bool HttpAuthHandlerNegotiate::NeedsIdentity() {
+  return auth_system_.NeedsIdentity();
+}
+
+bool HttpAuthHandlerNegotiate::IsFinalRound() {
+  return auth_system_.IsFinalRound();
+}
+
+bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() {
+  if (target_ == HttpAuth::AUTH_PROXY)
+    return true;
+  if (!url_security_manager_)
+    return false;
+  return url_security_manager_->CanUseDefaultCredentials(origin_);
+}
+
+std::wstring HttpAuthHandlerNegotiate::CreateSPN(
+    const AddressList& address_list, const GURL& origin) {
+  // Kerberos Web Server SPNs are in the form HTTP/<host>:<port> through SSPI,
+  // and in the form HTTP@<host>:<port> through GSSAPI
+  //   http://msdn.microsoft.com/en-us/library/ms677601%28VS.85%29.aspx
+  //
+  // However, reality differs from the specification. A good description of
+  // the problems can be found here:
+  //   http://blog.michelbarneveld.nl/michel/archive/2009/11/14/the-reason-why-kb911149-and-kb908209-are-not-the-soluton.aspx
+  //
+  // Typically the <host> portion should be the canonical FQDN for the service.
+  // If this could not be resolved, the original hostname in the URL will be
+  // attempted instead. However, some intranets register SPNs using aliases
+  // for the same canonical DNS name to allow multiple web services to reside
+  // on the same host machine without requiring different ports. IE6 and IE7
+  // have hotpatches that allow the default behavior to be overridden.
+  //   http://support.microsoft.com/kb/911149
+  //   http://support.microsoft.com/kb/938305
+  //
+  // According to the spec, the <port> option should be included if it is a
+  // non-standard port (i.e. not 80 or 443 in the HTTP case). However,
+  // historically browsers have not included the port, even on non-standard
+  // ports. IE6 required a hotpatch and a registry setting to enable
+  // including non-standard ports, and IE7 and IE8 also require the same
+  // registry setting, but no hotpatch. Firefox does not appear to have an
+  // option to include non-standard ports as of 3.6.
+  //   http://support.microsoft.com/kb/908209
+  //
+  // Without any command-line flags, Chrome matches the behavior of Firefox
+  // and IE. Users can override the behavior so aliases are allowed and
+  // non-standard ports are included.
+  int port = origin.EffectiveIntPort();
+  std::string server;
+  if (!address_list.GetCanonicalName(&server))
+    server = origin.host();
+#if defined(OS_WIN)
+  static const char kSpnSeparator = '/';
+#elif defined(OS_POSIX)
+  static const char kSpnSeparator = '@';
+#endif
+  if (port != 80 && port != 443 && use_port_) {
+    return ASCIIToWide(StringPrintf("HTTP%c%s:%d", kSpnSeparator,
+                                    server.c_str(), port));
+  } else {
+    return ASCIIToWide(StringPrintf("HTTP%c%s", kSpnSeparator, server.c_str()));
+  }
+}
+
+int HttpAuthHandlerNegotiate::DoLoop(int result) {
+  DCHECK(next_state_ != STATE_NONE);
+
+  int rv = result;
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_RESOLVE_CANONICAL_NAME:
+        DCHECK_EQ(OK, rv);
+        rv = DoResolveCanonicalName();
+        break;
+      case STATE_RESOLVE_CANONICAL_NAME_COMPLETE:
+        rv = DoResolveCanonicalNameComplete(rv);
+        break;
+      case STATE_GENERATE_AUTH_TOKEN:
+        DCHECK_EQ(OK, rv);
+        rv = DoGenerateAuthToken();
+        break;
+      case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
+        rv = DoGenerateAuthTokenComplete(rv);
+        break;
+      default:
+        NOTREACHED() << "bad state";
+        rv = ERR_FAILED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+
+  return rv;
+}
+
+int HttpAuthHandlerNegotiate::DoResolveCanonicalName() {
+  next_state_ = STATE_RESOLVE_CANONICAL_NAME_COMPLETE;
+  if (disable_cname_lookup_)
+    return OK;
+
+  // TODO(cbentzel): Add reverse DNS lookup for numeric addresses.
+  DCHECK(!single_resolve_.get());
+  HostResolver::RequestInfo info(origin_.host(), 0);
+  info.set_host_resolver_flags(HOST_RESOLVER_CANONNAME);
+  single_resolve_.reset(new SingleRequestHostResolver(resolver_));
+  return single_resolve_->Resolve(info, &address_list_, &io_callback_,
+                                  net_log_);
+}
+
+int HttpAuthHandlerNegotiate::DoResolveCanonicalNameComplete(int rv) {
+  DCHECK_NE(ERR_IO_PENDING, rv);
+  if (rv != OK) {
+    // Even in the error case, try to use origin_.host instead of
+    // passing the failure on to the caller.
+    LOG(INFO) << "Problem finding canonical name for SPN for host "
+              << origin_.host() << ": " << ErrorToString(rv);
+    rv = OK;
+  }
+
+  next_state_ = STATE_GENERATE_AUTH_TOKEN;
+  spn_ = CreateSPN(address_list_, origin_);
+  address_list_.Reset();
+  return rv;
+}
+
+int HttpAuthHandlerNegotiate::DoGenerateAuthToken() {
+  next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
+  std::wstring* username = has_username_and_password_ ? &username_ : NULL;
+  std::wstring* password = has_username_and_password_ ? &password_ : NULL;
+  // TODO(cbentzel): This should possibly be done async.
+  return auth_system_.GenerateAuthToken(username, password, spn_, auth_token_);
+}
+
+int HttpAuthHandlerNegotiate::DoGenerateAuthTokenComplete(int rv) {
+  DCHECK_NE(ERR_IO_PENDING, rv);
+  auth_token_ = NULL;
+  return rv;
+}
+
+void HttpAuthHandlerNegotiate::OnIOComplete(int result) {
+  int rv = DoLoop(result);
+  if (rv != ERR_IO_PENDING)
+    DoCallback(rv);
+}
+
+void HttpAuthHandlerNegotiate::DoCallback(int rv) {
+  DCHECK(rv != ERR_IO_PENDING);
+  DCHECK(user_callback_);
+  CompletionCallback* callback = user_callback_;
+  user_callback_ = NULL;
+  callback->Run(rv);
+}
+
+HttpAuthHandlerNegotiate::Factory::Factory()
+    : disable_cname_lookup_(false),
+      use_port_(false),
+#if defined(OS_WIN)
+      max_token_length_(0),
+      first_creation_(true),
+      is_unsupported_(false),
+      auth_library_(SSPILibrary::GetDefault()) {
+#elif defined(OS_POSIX)
+      auth_library_(GSSAPILibrary::GetDefault()) {
+#endif
+}
+
+HttpAuthHandlerNegotiate::Factory::~Factory() {
+}
+
+void HttpAuthHandlerNegotiate::Factory::set_host_resolver(
+    HostResolver* resolver) {
+  resolver_ = resolver;
+}
+
+int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler(
+    HttpAuth::ChallengeTokenizer* challenge,
+    HttpAuth::Target target,
+    const GURL& origin,
+    CreateReason reason,
+    int digest_nonce_count,
+    const BoundNetLog& net_log,
+    scoped_ptr<HttpAuthHandler>* handler) {
+#if defined(OS_WIN)
+  if (is_unsupported_ || reason == CREATE_PREEMPTIVE)
+    return ERR_UNSUPPORTED_AUTH_SCHEME;
+  if (max_token_length_ == 0) {
+    int rv = DetermineMaxTokenLength(auth_library_, NEGOSSP_NAME,
+                                     &max_token_length_);
+    if (rv == ERR_UNSUPPORTED_AUTH_SCHEME)
+      is_unsupported_ = true;
+    if (rv != OK)
+      return rv;
+  }
+  // TODO(cbentzel): Move towards model of parsing in the factory
+  //                 method and only constructing when valid.
+  scoped_ptr<HttpAuthHandler> tmp_handler(
+      new HttpAuthHandlerNegotiate(auth_library_, max_token_length_,
+                                   url_security_manager(), resolver_,
+                                   disable_cname_lookup_, use_port_));
+  if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
+    return ERR_INVALID_RESPONSE;
+  handler->swap(tmp_handler);
+  return OK;
+#elif defined(OS_POSIX)
+  // TODO(ahendrickson): Move towards model of parsing in the factory
+  //                     method and only constructing when valid.
+  scoped_ptr<HttpAuthHandler> tmp_handler(
+      new HttpAuthHandlerNegotiate(auth_library_, url_security_manager(),
+                                   resolver_, disable_cname_lookup_,
+                                   use_port_));
+  if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
+    return ERR_INVALID_RESPONSE;
+  handler->swap(tmp_handler);
+  return OK;
+#endif
+}
+
+}  // namespace net
diff --git a/net/http/http_auth_handler_negotiate.h b/net/http/http_auth_handler_negotiate.h
index eb4d11f..ec1a194 100644
--- a/net/http/http_auth_handler_negotiate.h
+++ b/net/http/http_auth_handler_negotiate.h
@@ -5,18 +5,27 @@
 #ifndef NET_HTTP_HTTP_AUTH_HANDLER_NEGOTIATE_H_
 #define NET_HTTP_HTTP_AUTH_HANDLER_NEGOTIATE_H_
 
-#include "build/build_config.h"
-
 #include <string>
 
+#include "build/build_config.h"
+
+#include "build/build_config.h"
+#include "net/base/address_list.h"
 #include "net/http/http_auth_handler.h"
+#include "net/http/http_auth_handler_factory.h"
 
 #if defined(OS_WIN)
 #include "net/http/http_auth_sspi_win.h"
+#elif defined(OS_POSIX)
+#include "net/http/http_auth_gssapi_posix.h"
 #endif
 
 namespace net {
 
+class HostResolver;
+class SingleRequestHostResolver;
+class URLSecurityManager;
+
 // Handler for WWW-Authenticate: Negotiate protocol.
 //
 // See http://tools.ietf.org/html/rfc4178 and http://tools.ietf.org/html/rfc4559
@@ -24,28 +33,139 @@
 
 class HttpAuthHandlerNegotiate : public HttpAuthHandler {
  public:
-  HttpAuthHandlerNegotiate();
+#if defined(OS_WIN)
+  typedef SSPILibrary AuthLibrary;
+  typedef HttpAuthSSPI AuthSystem;
+#elif defined(OS_POSIX)
+  typedef GSSAPILibrary AuthLibrary;
+  typedef HttpAuthGSSAPI AuthSystem;
+#endif
+
+  class Factory : public HttpAuthHandlerFactory {
+   public:
+    Factory();
+    virtual ~Factory();
+
+    // |disable_cname_lookup()| and |set_disable_cname_lookup()| get/set whether
+    // the auth handlers generated by this factory should skip looking up the
+    // canonical DNS name of the the host that they are authenticating to when
+    // generating the SPN. The default value is false.
+    bool disable_cname_lookup() const { return disable_cname_lookup_; }
+    void set_disable_cname_lookup(bool disable_cname_lookup) {
+      disable_cname_lookup_ = disable_cname_lookup;
+    }
+
+    // |use_port()| and |set_use_port()| get/set whether the auth handlers
+    // generated by this factory should include the port number of the server
+    // they are authenticating to when constructing a Kerberos SPN. The default
+    // value is false.
+    bool use_port() const { return use_port_; }
+    void set_use_port(bool use_port) { use_port_ = use_port; }
+
+    void set_host_resolver(HostResolver* host_resolver);
+
+    virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+                                  HttpAuth::Target target,
+                                  const GURL& origin,
+                                  CreateReason reason,
+                                  int digest_nonce_count,
+                                  const BoundNetLog& net_log,
+                                  scoped_ptr<HttpAuthHandler>* handler);
+
+    // Set the system library to use. Typically the only callers which need to
+    // use this are unit tests which pass in a mocked-out version of the
+    // system library.
+    // The caller is responsible for managing the lifetime of |*auth_library|,
+    // and the lifetime must exceed that of this Factory object and all
+    // HttpAuthHandler's that this Factory object creates.
+    void set_library(AuthLibrary* auth_library) {
+      auth_library_ = auth_library;
+    }
+
+   private:
+    bool disable_cname_lookup_;
+    bool use_port_;
+    scoped_refptr<HostResolver> resolver_;
+#if defined(OS_WIN)
+    ULONG max_token_length_;
+    bool first_creation_;
+    bool is_unsupported_;
+#endif
+    AuthLibrary* auth_library_;
+  };
+
+  HttpAuthHandlerNegotiate(AuthLibrary* sspi_library,
+#if defined(OS_WIN)
+                           ULONG max_token_length,
+#endif
+                           URLSecurityManager* url_security_manager,
+                           HostResolver* host_resolver,
+                           bool disable_cname_lookup,
+                           bool use_port);
+
+  virtual ~HttpAuthHandlerNegotiate();
 
   virtual bool NeedsIdentity();
 
   virtual bool IsFinalRound();
 
-  virtual std::string GenerateCredentials(const std::wstring& username,
-                                          const std::wstring& password,
-                                          const HttpRequestInfo* request,
-                                          const ProxyInfo* proxy);
+  virtual bool AllowsDefaultCredentials();
 
+  // These are public for unit tests
+  std::wstring CreateSPN(const AddressList& address_list, const GURL& orign);
+  const std::wstring& spn() const { return spn_; }
 
  protected:
-  virtual bool Init(std::string::const_iterator challenge_begin,
-                    std::string::const_iterator challenge_end);
+  virtual bool Init(HttpAuth::ChallengeTokenizer* challenge);
+
+  virtual int GenerateAuthTokenImpl(const std::wstring* username,
+                                    const std::wstring* password,
+                                    const HttpRequestInfo* request,
+                                    CompletionCallback* callback,
+                                    std::string* auth_token);
 
  private:
-  ~HttpAuthHandlerNegotiate();
+  enum State {
+    STATE_RESOLVE_CANONICAL_NAME,
+    STATE_RESOLVE_CANONICAL_NAME_COMPLETE,
+    STATE_GENERATE_AUTH_TOKEN,
+    STATE_GENERATE_AUTH_TOKEN_COMPLETE,
+    STATE_NONE,
+  };
 
-#if defined(OS_WIN)
-  HttpAuthSSPI auth_sspi_;
-#endif
+  void OnIOComplete(int result);
+  void DoCallback(int result);
+  int DoLoop(int result);
+
+  int DoResolveCanonicalName();
+  int DoResolveCanonicalNameComplete(int rv);
+  int DoGenerateAuthToken();
+  int DoGenerateAuthTokenComplete(int rv);
+
+  AuthSystem auth_system_;
+  bool disable_cname_lookup_;
+  bool use_port_;
+  CompletionCallbackImpl<HttpAuthHandlerNegotiate> io_callback_;
+  scoped_refptr<HostResolver> resolver_;
+
+  // Members which are needed for DNS lookup + SPN.
+  AddressList address_list_;
+  scoped_ptr<SingleRequestHostResolver> single_resolve_;
+
+  // Things which should be consistent after first call to GenerateAuthToken.
+  bool already_called_;
+  bool has_username_and_password_;
+  std::wstring username_;
+  std::wstring password_;
+  std::wstring spn_;
+
+  // Things which vary each round.
+  CompletionCallback* user_callback_;
+  std::string* auth_token_;
+
+  State next_state_;
+
+  URLSecurityManager* url_security_manager_;
 };
 
 }  // namespace net
diff --git a/net/http/http_auth_handler_negotiate_unittest.cc b/net/http/http_auth_handler_negotiate_unittest.cc
new file mode 100644
index 0000000..4fcdad1
--- /dev/null
+++ b/net/http/http_auth_handler_negotiate_unittest.cc
@@ -0,0 +1,359 @@
+// Copyright (c) 2010 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/http/http_auth_handler_negotiate.h"
+
+#include "net/base/mock_host_resolver.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/http/http_request_info.h"
+#if defined(OS_WIN)
+#include "net/http/mock_sspi_library_win.h"
+#elif defined(OS_POSIX)
+#include "net/http/mock_gssapi_library_posix.h"
+#include "net/third_party/gssapi/gssapi.h"
+#endif
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if defined(OS_WIN)
+typedef net::MockSSPILibrary MockAuthLibrary;
+#elif defined(OS_POSIX)
+typedef net::test::MockGSSAPILibrary MockAuthLibrary;
+#endif
+
+
+namespace net {
+
+class HttpAuthHandlerNegotiateTest : public PlatformTest {
+ public:
+  virtual void SetUp() {
+    auth_library_.reset(new MockAuthLibrary());
+    resolver_ = new MockHostResolver();
+    resolver_->rules()->AddIPLiteralRule("alias", "10.0.0.2",
+                                           "canonical.example.com");
+
+    url_security_manager_.reset(new URLSecurityManagerAllow());
+    factory_.reset(new HttpAuthHandlerNegotiate::Factory());
+    factory_->set_url_security_manager(url_security_manager_.get());
+    factory_->set_library(auth_library_.get());
+    factory_->set_host_resolver(resolver_);
+  }
+
+  void SetupMocks(MockAuthLibrary* mock_library) {
+#if defined(OS_WIN)
+    security_package_.reset(new SecPkgInfoW);
+    memset(security_package_.get(), 0x0, sizeof(SecPkgInfoW));
+    security_package_->cbMaxToken = 1337;
+    mock_library->ExpectQuerySecurityPackageInfo(
+        L"Negotiate", SEC_E_OK, security_package_.get());
+#elif defined(OS_POSIX)
+    // Copied from an actual transaction!
+    const char kAuthResponse[] =
+        "\x60\x82\x02\xCA\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x01"
+        "\x00\x6E\x82\x02\xB9\x30\x82\x02\xB5\xA0\x03\x02\x01\x05\xA1\x03"
+        "\x02\x01\x0E\xA2\x07\x03\x05\x00\x00\x00\x00\x00\xA3\x82\x01\xC1"
+        "\x61\x82\x01\xBD\x30\x82\x01\xB9\xA0\x03\x02\x01\x05\xA1\x16\x1B"
+        "\x14\x55\x4E\x49\x58\x2E\x43\x4F\x52\x50\x2E\x47\x4F\x4F\x47\x4C"
+        "\x45\x2E\x43\x4F\x4D\xA2\x2C\x30\x2A\xA0\x03\x02\x01\x01\xA1\x23"
+        "\x30\x21\x1B\x04\x68\x6F\x73\x74\x1B\x19\x6E\x69\x6E\x6A\x61\x2E"
+        "\x63\x61\x6D\x2E\x63\x6F\x72\x70\x2E\x67\x6F\x6F\x67\x6C\x65\x2E"
+        "\x63\x6F\x6D\xA3\x82\x01\x6A\x30\x82\x01\x66\xA0\x03\x02\x01\x10"
+        "\xA1\x03\x02\x01\x01\xA2\x82\x01\x58\x04\x82\x01\x54\x2C\xB1\x2B"
+        "\x0A\xA5\xFF\x6F\xEC\xDE\xB0\x19\x6E\x15\x20\x18\x0C\x42\xB3\x2C"
+        "\x4B\xB0\x37\x02\xDE\xD3\x2F\xB4\xBF\xCA\xEC\x0E\xF9\xF3\x45\x6A"
+        "\x43\xF3\x8D\x79\xBD\xCB\xCD\xB2\x2B\xB8\xFC\xD6\xB4\x7F\x09\x48"
+        "\x14\xA7\x4F\xD2\xEE\xBC\x1B\x2F\x18\x3B\x81\x97\x7B\x28\xA4\xAF"
+        "\xA8\xA3\x7A\x31\x1B\xFC\x97\xB6\xBA\x8A\x50\x50\xD7\x44\xB8\x30"
+        "\xA4\x51\x4C\x3A\x95\x6C\xA1\xED\xE2\xEF\x17\xFE\xAB\xD2\xE4\x70"
+        "\xDE\xEB\x7E\x86\x48\xC5\x3E\x19\x5B\x83\x17\xBB\x52\x26\xC0\xF3"
+        "\x38\x0F\xB0\x8C\x72\xC9\xB0\x8B\x99\x96\x18\xE1\x9E\x67\x9D\xDC"
+        "\xF5\x39\x80\x70\x35\x3F\x98\x72\x16\x44\xA2\xC0\x10\xAA\x70\xBD"
+        "\x06\x6F\x83\xB1\xF4\x67\xA4\xBD\xDA\xF7\x79\x1D\x96\xB5\x7E\xF8"
+        "\xC6\xCF\xB4\xD9\x51\xC9\xBB\xB4\x20\x3C\xDD\xB9\x2C\x38\xEA\x40"
+        "\xFB\x02\x6C\xCB\x48\x71\xE8\xF4\x34\x5B\x63\x5D\x13\x57\xBD\xD1"
+        "\x3D\xDE\xE8\x4A\x51\x6E\xBE\x4C\xF5\xA3\x84\xF7\x4C\x4E\x58\x04"
+        "\xBE\xD1\xCC\x22\xA0\x43\xB0\x65\x99\x6A\xE0\x78\x0D\xFC\xE1\x42"
+        "\xA9\x18\xCF\x55\x4D\x23\xBD\x5C\x0D\xB5\x48\x25\x47\xCC\x01\x54"
+        "\x36\x4D\x0C\x6F\xAC\xCD\x33\x21\xC5\x63\x18\x91\x68\x96\xE9\xD1"
+        "\xD8\x23\x1F\x21\xAE\x96\xA3\xBD\x27\xF7\x4B\xEF\x4C\x43\xFF\xF8"
+        "\x22\x57\xCF\x68\x6C\x35\xD5\x21\x48\x5B\x5F\x8F\xA5\xB9\x6F\x99"
+        "\xA6\xE0\x6E\xF0\xC5\x7C\x91\xC8\x0B\x8A\x4B\x4E\x80\x59\x02\xE9"
+        "\xE8\x3F\x87\x04\xA6\xD1\xCA\x26\x3C\xF0\xDA\x57\xFA\xE6\xAF\x25"
+        "\x43\x34\xE1\xA4\x06\x1A\x1C\xF4\xF5\x21\x9C\x00\x98\xDD\xF0\xB4"
+        "\x8E\xA4\x81\xDA\x30\x81\xD7\xA0\x03\x02\x01\x10\xA2\x81\xCF\x04"
+        "\x81\xCC\x20\x39\x34\x60\x19\xF9\x4C\x26\x36\x46\x99\x7A\xFD\x2B"
+        "\x50\x8B\x2D\x47\x72\x38\x20\x43\x0E\x6E\x28\xB3\xA7\x4F\x26\xF1"
+        "\xF1\x7B\x02\x63\x58\x5A\x7F\xC8\xD0\x6E\xF5\xD1\xDA\x28\x43\x1B"
+        "\x6D\x9F\x59\x64\xDE\x90\xEA\x6C\x8C\xA9\x1B\x1E\x92\x29\x24\x23"
+        "\x2C\xE3\xEA\x64\xEF\x91\xA5\x4E\x94\xE1\xDC\x56\x3A\xAF\xD5\xBC"
+        "\xC9\xD3\x9B\x6B\x1F\xBE\x40\xE5\x40\xFF\x5E\x21\xEA\xCE\xFC\xD5"
+        "\xB0\xE5\xBA\x10\x94\xAE\x16\x54\xFC\xEB\xAB\xF1\xD4\x20\x31\xCC"
+        "\x26\xFE\xBE\xFE\x22\xB6\x9B\x1A\xE5\x55\x2C\x93\xB7\x3B\xD6\x4C"
+        "\x35\x35\xC1\x59\x61\xD4\x1F\x2E\x4C\xE1\x72\x8F\x71\x4B\x0C\x39"
+        "\x80\x79\xFA\xCD\xEA\x71\x1B\xAE\x35\x41\xED\xF9\x65\x0C\x59\xF8"
+        "\xE1\x27\xDA\xD6\xD1\x20\x32\xCD\xBF\xD1\xEF\xE2\xED\xAD\x5D\xA7"
+        "\x69\xE3\x55\xF9\x30\xD3\xD4\x08\xC8\xCA\x62\xF8\x64\xEC\x9B\x92"
+        "\x1A\xF1\x03\x2E\xCC\xDC\xEB\x17\xDE\x09\xAC\xA9\x58\x86";
+    test::GssContextMockImpl context1(
+        "localhost",                    // Source name
+        "example.com",                  // Target name
+        23,                             // Lifetime
+        *GSS_C_NT_HOSTBASED_SERVICE,    // Mechanism
+        0,                              // Context flags
+        1,                              // Locally initiated
+        0);                             // Open
+    test::GssContextMockImpl context2(
+        "localhost",                    // Source name
+        "example.com",                  // Target name
+        23,                             // Lifetime
+        *GSS_C_NT_HOSTBASED_SERVICE,    // Mechanism
+        0,                              // Context flags
+        1,                              // Locally initiated
+        1);                             // Open
+    test::MockGSSAPILibrary::SecurityContextQuery queries[] = {
+      { "Negotiate",                    // Package name
+        GSS_S_CONTINUE_NEEDED,          // Major response code
+        0,                              // Minor response code
+        context1,                       // Context
+        { 0, NULL },                           // Expected input token
+        { arraysize(kAuthResponse),
+          const_cast<char*>(kAuthResponse) }   // Output token
+      },
+      { "Negotiate",                    // Package name
+        GSS_S_COMPLETE,                 // Major response code
+        0,                              // Minor response code
+        context2,                       // Context
+        { arraysize(kAuthResponse),
+          const_cast<char*>(kAuthResponse) },  // Expected input token
+        { arraysize(kAuthResponse),
+          const_cast<char*>(kAuthResponse) }   // Output token
+      },
+    };
+
+    for (size_t i = 0; i < arraysize(queries); ++i) {
+      mock_library->ExpectSecurityContext(queries[i].expected_package,
+                                          queries[i].response_code,
+                                          queries[i].minor_response_code,
+                                          queries[i].context_info,
+                                          queries[i].expected_input_token,
+                                          queries[i].output_token);
+    }
+#endif  // defined(OS_POSIX)
+  }
+
+#if defined(OS_POSIX)
+  void SetupErrorMocks(MockAuthLibrary* mock_library,
+                       int major_status,
+                       int minor_status) {
+    const gss_OID_desc kDefaultMech = { 0, NULL };
+    test::GssContextMockImpl context(
+        "localhost",                    // Source name
+        "example.com",                  // Target name
+        0,                              // Lifetime
+        kDefaultMech,                   // Mechanism
+        0,                              // Context flags
+        1,                              // Locally initiated
+        0);                             // Open
+    test::MockGSSAPILibrary::SecurityContextQuery query = {
+      "Negotiate",                    // Package name
+      major_status,                   // Major response code
+      minor_status,                   // Minor response code
+      context,                        // Context
+      { 0, NULL },                    // Expected input token
+      { 0, NULL }                     // Output token
+    };
+
+    mock_library->ExpectSecurityContext(query.expected_package,
+                                        query.response_code,
+                                        query.minor_response_code,
+                                        query.context_info,
+                                        query.expected_input_token,
+                                        query.output_token);
+  }
+
+#endif  // defined(OS_POSIX)
+
+  int CreateHandler(bool disable_cname_lookup, bool use_port,
+                     bool synchronous_resolve_mode,
+                     const std::string& url_string,
+                     scoped_ptr<HttpAuthHandlerNegotiate>* handler) {
+    factory_->set_disable_cname_lookup(disable_cname_lookup);
+    factory_->set_use_port(use_port);
+    resolver_->set_synchronous_mode(synchronous_resolve_mode);
+    GURL gurl(url_string);
+
+    // Note: This is a little tricky because CreateAuthHandlerFromString
+    // expects a scoped_ptr<HttpAuthHandler>* rather than a
+    // scoped_ptr<HttpAuthHandlerNegotiate>*. This needs to do the cast
+    // after creating the handler, and make sure that generic_handler
+    // no longer holds on to the HttpAuthHandlerNegotiate object.
+    scoped_ptr<HttpAuthHandler> generic_handler;
+    int rv = factory_->CreateAuthHandlerFromString("Negotiate",
+                                                   HttpAuth::AUTH_SERVER,
+                                                   gurl,
+                                                   BoundNetLog(),
+                                                   &generic_handler);
+    if (rv != OK)
+      return rv;
+    HttpAuthHandlerNegotiate* negotiate_handler =
+        static_cast<HttpAuthHandlerNegotiate*>(generic_handler.release());
+    handler->reset(negotiate_handler);
+    return rv;
+  }
+
+  MockAuthLibrary* AuthLibrary() { return auth_library_.get(); }
+
+ private:
+#if defined(OS_WIN)
+  scoped_ptr<SecPkgInfoW> security_package_;
+#endif
+  scoped_ptr<MockAuthLibrary> auth_library_;
+  scoped_refptr<MockHostResolver> resolver_;
+  scoped_ptr<URLSecurityManager> url_security_manager_;
+  scoped_ptr<HttpAuthHandlerNegotiate::Factory> factory_;
+};
+
+TEST_F(HttpAuthHandlerNegotiateTest, DisableCname) {
+  SetupMocks(AuthLibrary());
+  scoped_ptr<HttpAuthHandlerNegotiate> auth_handler;
+  EXPECT_EQ(OK, CreateHandler(
+      true, false, true, "http://alias:500", &auth_handler));
+
+  ASSERT_TRUE(auth_handler.get() != NULL);
+  TestCompletionCallback callback;
+  HttpRequestInfo request_info;
+  std::string token;
+  std::wstring username = L"foo";
+  std::wstring password = L"bar";
+  EXPECT_EQ(OK, auth_handler->GenerateAuthToken(&username, &password,
+                                                &request_info,
+                                                &callback, &token));
+#if defined(OS_WIN)
+  EXPECT_EQ(L"HTTP/alias", auth_handler->spn());
+#elif defined(OS_POSIX)
+  EXPECT_EQ(L"HTTP@alias", auth_handler->spn());
+#endif
+}
+
+TEST_F(HttpAuthHandlerNegotiateTest, DisableCnameStandardPort) {
+  SetupMocks(AuthLibrary());
+  scoped_ptr<HttpAuthHandlerNegotiate> auth_handler;
+  EXPECT_EQ(OK, CreateHandler(
+      true, true, true, "http://alias:80", &auth_handler));
+  ASSERT_TRUE(auth_handler.get() != NULL);
+  TestCompletionCallback callback;
+  HttpRequestInfo request_info;
+  std::string token;
+  std::wstring username = L"foo";
+  std::wstring password = L"bar";
+  EXPECT_EQ(OK, auth_handler->GenerateAuthToken(&username, &password,
+                                                &request_info,
+                                                &callback, &token));
+#if defined(OS_WIN)
+  EXPECT_EQ(L"HTTP/alias", auth_handler->spn());
+#elif defined(OS_POSIX)
+  EXPECT_EQ(L"HTTP@alias", auth_handler->spn());
+#endif
+}
+
+TEST_F(HttpAuthHandlerNegotiateTest, DisableCnameNonstandardPort) {
+  SetupMocks(AuthLibrary());
+  scoped_ptr<HttpAuthHandlerNegotiate> auth_handler;
+  EXPECT_EQ(OK, CreateHandler(
+      true, true, true, "http://alias:500", &auth_handler));
+  ASSERT_TRUE(auth_handler.get() != NULL);
+  TestCompletionCallback callback;
+  HttpRequestInfo request_info;
+  std::string token;
+  std::wstring username = L"foo";
+  std::wstring password = L"bar";
+  EXPECT_EQ(OK, auth_handler->GenerateAuthToken(&username, &password,
+                                                &request_info,
+                                                &callback, &token));
+#if defined(OS_WIN)
+  EXPECT_EQ(L"HTTP/alias:500", auth_handler->spn());
+#elif defined(OS_POSIX)
+  EXPECT_EQ(L"HTTP@alias:500", auth_handler->spn());
+#endif
+}
+
+TEST_F(HttpAuthHandlerNegotiateTest, CnameSync) {
+  SetupMocks(AuthLibrary());
+  scoped_ptr<HttpAuthHandlerNegotiate> auth_handler;
+  EXPECT_EQ(OK, CreateHandler(
+      false, false, true, "http://alias:500", &auth_handler));
+  ASSERT_TRUE(auth_handler.get() != NULL);
+  TestCompletionCallback callback;
+  HttpRequestInfo request_info;
+  std::string token;
+  std::wstring username = L"foo";
+  std::wstring password = L"bar";
+  EXPECT_EQ(OK, auth_handler->GenerateAuthToken(&username, &password,
+                                                &request_info,
+                                                &callback, &token));
+#if defined(OS_WIN)
+  EXPECT_EQ(L"HTTP/canonical.example.com", auth_handler->spn());
+#elif defined(OS_POSIX)
+  EXPECT_EQ(L"HTTP@canonical.example.com", auth_handler->spn());
+#endif
+}
+
+TEST_F(HttpAuthHandlerNegotiateTest, CnameAsync) {
+  SetupMocks(AuthLibrary());
+  scoped_ptr<HttpAuthHandlerNegotiate> auth_handler;
+  EXPECT_EQ(OK, CreateHandler(
+      false, false, false, "http://alias:500", &auth_handler));
+  ASSERT_TRUE(auth_handler.get() != NULL);
+  TestCompletionCallback callback;
+  HttpRequestInfo request_info;
+  std::string token;
+  std::wstring username = L"foo";
+  std::wstring password = L"bar";
+  EXPECT_EQ(ERR_IO_PENDING, auth_handler->GenerateAuthToken(
+      &username, &password, &request_info, &callback, &token));
+  EXPECT_EQ(OK, callback.WaitForResult());
+#if defined(OS_WIN)
+  EXPECT_EQ(L"HTTP/canonical.example.com", auth_handler->spn());
+#elif defined(OS_POSIX)
+  EXPECT_EQ(L"HTTP@canonical.example.com", auth_handler->spn());
+#endif
+}
+
+#if defined(OS_POSIX)
+
+// These tests are only for GSSAPI, as we can't use explicit credentials with
+// that library.
+
+TEST_F(HttpAuthHandlerNegotiateTest, ServerNotInKerberosDatabase) {
+  SetupErrorMocks(AuthLibrary(), GSS_S_FAILURE, 0x96C73A07);  // No server
+  scoped_ptr<HttpAuthHandlerNegotiate> auth_handler;
+  EXPECT_EQ(OK, CreateHandler(
+      false, false, false, "http://alias:500", &auth_handler));
+  ASSERT_TRUE(auth_handler.get() != NULL);
+  TestCompletionCallback callback;
+  HttpRequestInfo request_info;
+  std::string token;
+  EXPECT_EQ(ERR_IO_PENDING, auth_handler->GenerateAuthToken(
+      NULL, NULL, &request_info, &callback, &token));
+  EXPECT_EQ(ERR_MISSING_AUTH_CREDENTIALS, callback.WaitForResult());
+}
+
+TEST_F(HttpAuthHandlerNegotiateTest, NoKerberosCredentials) {
+  SetupErrorMocks(AuthLibrary(), GSS_S_FAILURE, 0x96C73AC3);  // No credentials
+  scoped_ptr<HttpAuthHandlerNegotiate> auth_handler;
+  EXPECT_EQ(OK, CreateHandler(
+      false, false, false, "http://alias:500", &auth_handler));
+  ASSERT_TRUE(auth_handler.get() != NULL);
+  TestCompletionCallback callback;
+  HttpRequestInfo request_info;
+  std::string token;
+  EXPECT_EQ(ERR_IO_PENDING, auth_handler->GenerateAuthToken(
+      NULL, NULL, &request_info, &callback, &token));
+  EXPECT_EQ(ERR_MISSING_AUTH_CREDENTIALS, callback.WaitForResult());
+}
+
+#endif  // defined(OS_POSIX)
+
+}  // namespace net
diff --git a/net/http/http_auth_handler_ntlm.cc b/net/http/http_auth_handler_ntlm.cc
old mode 100755
new mode 100644
index 6e10e79..d8e8a75
--- a/net/http/http_auth_handler_ntlm.cc
+++ b/net/http/http_auth_handler_ntlm.cc
@@ -1,33 +1,30 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/http/http_auth_handler_ntlm.h"
 
 #include "base/base64.h"
+#include "base/logging.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_util.h"
 
 namespace net {
 
-std::string HttpAuthHandlerNTLM::GenerateCredentials(
-    const std::wstring& username,
-    const std::wstring& password,
+int HttpAuthHandlerNTLM::GenerateAuthTokenImpl(
+    const std::wstring* username,
+    const std::wstring* password,
     const HttpRequestInfo* request,
-    const ProxyInfo* proxy) {
+    CompletionCallback* callback,
+    std::string* auth_token) {
 #if defined(NTLM_SSPI)
-  std::string auth_credentials;
-
-  int rv = auth_sspi_.GenerateCredentials(
+  return auth_sspi_.GenerateAuthToken(
       username,
       password,
-      origin_,
-      request,
-      proxy,
-      &auth_credentials);
-  if (rv == OK)
-    return auth_credentials;
-  return std::string();
+      CreateSPN(origin_),
+      auth_token);
 #else  // !defined(NTLM_SSPI)
   // TODO(wtc): See if we can use char* instead of void* for in_buf and
   // out_buf.  This change will need to propagate to GetNextToken,
@@ -41,16 +38,16 @@
   // components.
   std::wstring domain;
   std::wstring user;
-  size_t backslash_idx = username.find(L'\\');
+  size_t backslash_idx = username->find(L'\\');
   if (backslash_idx == std::wstring::npos) {
-    user = username;
+    user = *username;
   } else {
-    domain = username.substr(0, backslash_idx);
-    user = username.substr(backslash_idx + 1);
+    domain = username->substr(0, backslash_idx);
+    user = username->substr(backslash_idx + 1);
   }
   domain_ = WideToUTF16(domain);
   username_ = WideToUTF16(user);
-  password_ = WideToUTF16(password);
+  password_ = WideToUTF16(*password);
 
   // Initial challenge.
   if (auth_data_.empty()) {
@@ -58,71 +55,66 @@
     in_buf = NULL;
     int rv = InitializeBeforeFirstChallenge();
     if (rv != OK)
-      return std::string();
+      return rv;
   } else {
-    // Decode |auth_data_| into the input buffer.
-    int len = auth_data_.length();
-
-    // Strip off any padding.
-    // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.)
-    //
-    // Our base64 decoder requires that the length be a multiple of 4.
-    while (len > 0 && len % 4 != 0 && auth_data_[len - 1] == '=')
-      len--;
-    auth_data_.erase(len);
-
-    if (!base::Base64Decode(auth_data_, &decoded_auth_data))
-      return std::string();  // Improper base64 encoding
+    if (!base::Base64Decode(auth_data_, &decoded_auth_data)) {
+      LOG(ERROR) << "Unexpected problem Base64 decoding.";
+      return ERR_UNEXPECTED;
+    }
     in_buf_len = decoded_auth_data.length();
     in_buf = decoded_auth_data.data();
   }
 
   int rv = GetNextToken(in_buf, in_buf_len, &out_buf, &out_buf_len);
   if (rv != OK)
-    return std::string();
+    return rv;
 
   // Base64 encode data in output buffer and prepend "NTLM ".
   std::string encode_input(static_cast<char*>(out_buf), out_buf_len);
   std::string encode_output;
-  bool ok = base::Base64Encode(encode_input, &encode_output);
+  bool base64_rv = base::Base64Encode(encode_input, &encode_output);
   // OK, we are done with |out_buf|
   free(out_buf);
-  if (!ok)
-    return std::string();
-  return std::string("NTLM ") + encode_output;
+  if (!base64_rv) {
+    LOG(ERROR) << "Unexpected problem Base64 encoding.";
+    return ERR_UNEXPECTED;
+  }
+  *auth_token = std::string("NTLM ") + encode_output;
+  return OK;
 #endif
 }
 
 // The NTLM challenge header looks like:
 //   WWW-Authenticate: NTLM auth-data
 bool HttpAuthHandlerNTLM::ParseChallenge(
-    std::string::const_iterator challenge_begin,
-    std::string::const_iterator challenge_end) {
+    HttpAuth::ChallengeTokenizer* tok) {
   scheme_ = "ntlm";
   score_ = 3;
   properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED;
 
 #if defined(NTLM_SSPI)
-  return auth_sspi_.ParseChallenge(challenge_begin, challenge_end);
+  return auth_sspi_.ParseChallenge(tok);
 #else
   auth_data_.clear();
 
   // Verify the challenge's auth-scheme.
-  HttpAuth::ChallengeTokenizer challenge_tok(challenge_begin, challenge_end);
-  if (!challenge_tok.valid() ||
-      !LowerCaseEqualsASCII(challenge_tok.scheme(), "ntlm"))
+  if (!tok->valid() || !LowerCaseEqualsASCII(tok->scheme(), "ntlm"))
     return false;
 
-  // Extract the auth-data.  We can't use challenge_tok.GetNext() because
-  // auth-data is base64-encoded and may contain '=' padding at the end,
-  // which would be mistaken for a name=value pair.
-  challenge_begin += 4;  // Skip over "NTLM".
-  HttpUtil::TrimLWS(&challenge_begin, &challenge_end);
-
-  auth_data_.assign(challenge_begin, challenge_end);
-
+  tok->set_expect_base64_token(true);
+  if (tok->GetNext())
+    auth_data_.assign(tok->value_begin(), tok->value_end());
   return true;
-#endif
+#endif  // defined(NTLM_SSPI)
+}
+
+// static
+std::wstring HttpAuthHandlerNTLM::CreateSPN(const GURL& origin) {
+  // The service principal name of the destination server.  See
+  // http://msdn.microsoft.com/en-us/library/ms677949%28VS.85%29.aspx
+  std::wstring target(L"HTTP/");
+  target.append(ASCIIToWide(GetHostAndPort(origin)));
+  return target;
 }
 
 }  // namespace net
diff --git a/net/http/http_auth_handler_ntlm.h b/net/http/http_auth_handler_ntlm.h
index c3bda62..f22a2b5 100644
--- a/net/http/http_auth_handler_ntlm.h
+++ b/net/http/http_auth_handler_ntlm.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -27,12 +27,47 @@
 #include "base/basictypes.h"
 #include "base/string16.h"
 #include "net/http/http_auth_handler.h"
+#include "net/http/http_auth_handler_factory.h"
 
 namespace net {
 
+class URLSecurityManager;
+
 // Code for handling HTTP NTLM authentication.
 class HttpAuthHandlerNTLM : public HttpAuthHandler {
  public:
+  class Factory : public HttpAuthHandlerFactory {
+   public:
+    Factory();
+    virtual ~Factory();
+
+    virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge,
+                                  HttpAuth::Target target,
+                                  const GURL& origin,
+                                  CreateReason reason,
+                                  int digest_nonce_count,
+                                  const BoundNetLog& net_log,
+                                  scoped_ptr<HttpAuthHandler>* handler);
+#if defined(NTLM_SSPI)
+    // Set the SSPILibrary to use. Typically the only callers which need to
+    // use this are unit tests which pass in a mocked-out version of the
+    // SSPI library.
+    // The caller is responsible for managing the lifetime of |*sspi_library|,
+    // and the lifetime must exceed that of this Factory object and all
+    // HttpAuthHandler's that this Factory object creates.
+    void set_sspi_library(SSPILibrary* sspi_library) {
+      sspi_library_ = sspi_library;
+    }
+#endif  // defined(NTLM_SSPI)
+   private:
+#if defined(NTLM_SSPI)
+    ULONG max_token_length_;
+    bool first_creation_;
+    bool is_unsupported_;
+    SSPILibrary* sspi_library_;
+#endif  // defined(NTLM_SSPI)
+  };
+
 #if defined(NTLM_PORTABLE)
   // A function that generates n random bytes in the output buffer.
   typedef void (*GenerateRandomProc)(uint8* output, size_t n);
@@ -62,23 +97,31 @@
   };
 #endif
 
+#if defined(NTLM_PORTABLE)
   HttpAuthHandlerNTLM();
+#endif
+#if defined(NTLM_SSPI)
+  HttpAuthHandlerNTLM(SSPILibrary* sspi_library, ULONG max_token_length,
+                      URLSecurityManager* url_security_manager);
+#endif
 
   virtual bool NeedsIdentity();
 
   virtual bool IsFinalRound();
 
-  virtual std::string GenerateCredentials(const std::wstring& username,
-                                          const std::wstring& password,
-                                          const HttpRequestInfo* request,
-                                          const ProxyInfo* proxy);
+  virtual bool AllowsDefaultCredentials();
 
  protected:
-  virtual bool Init(std::string::const_iterator challenge_begin,
-                    std::string::const_iterator challenge_end) {
-    return ParseChallenge(challenge_begin, challenge_end);
+  virtual bool Init(HttpAuth::ChallengeTokenizer* tok) {
+    return ParseChallenge(tok);
   }
 
+  virtual int GenerateAuthTokenImpl(const std::wstring* username,
+                                    const std::wstring* password,
+                                    const HttpRequestInfo* request,
+                                    CompletionCallback* callback,
+                                    std::string* auth_token);
+
   // This function acquires a credentials handle in the SSPI implementation.
   // It does nothing in the portable implementation.
   int InitializeBeforeFirstChallenge();
@@ -95,8 +138,7 @@
 
   // Parse the challenge, saving the results into this instance.
   // Returns true on success.
-  bool ParseChallenge(std::string::const_iterator challenge_begin,
-                      std::string::const_iterator challenge_end);
+  bool ParseChallenge(HttpAuth::ChallengeTokenizer* tok);
 
   // Given an input token received from the server, generate the next output
   // token to be sent to the server.
@@ -105,6 +147,9 @@
                    void** out_token,
                    uint32* out_token_len);
 
+  // Create an NTLM SPN to identify the |origin| server.
+  static std::wstring CreateSPN(const GURL& origin);
+
 #if defined(NTLM_SSPI)
   HttpAuthSSPI auth_sspi_;
 #endif
@@ -121,6 +166,10 @@
   // The base64-encoded string following "NTLM" in the "WWW-Authenticate" or
   // "Proxy-Authenticate" response header.
   std::string auth_data_;
+
+#if defined(NTLM_SSPI)
+  URLSecurityManager* url_security_manager_;
+#endif
 };
 
 }  // namespace net
diff --git a/net/http/http_auth_handler_ntlm_portable.cc b/net/http/http_auth_handler_ntlm_portable.cc
index 7976878..e16d32c 100644
--- a/net/http/http_auth_handler_ntlm_portable.cc
+++ b/net/http/http_auth_handler_ntlm_portable.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -16,6 +16,7 @@
 #include "base/rand_util.h"
 #include "base/string_util.h"
 #include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
 #include "net/http/des.h"
@@ -120,13 +121,12 @@
 
 // We send these flags with our type 1 message.
 enum {
-  NTLM_TYPE1_FLAGS =
-      NTLM_NegotiateUnicode |
-      NTLM_NegotiateOEM |
-      NTLM_RequestTarget |
-      NTLM_NegotiateNTLMKey |
-      NTLM_NegotiateAlwaysSign |
-      NTLM_NegotiateNTLM2Key
+  NTLM_TYPE1_FLAGS = (NTLM_NegotiateUnicode |
+                      NTLM_NegotiateOEM |
+                      NTLM_RequestTarget |
+                      NTLM_NegotiateNTLMKey |
+                      NTLM_NegotiateAlwaysSign |
+                      NTLM_NegotiateNTLM2Key)
 };
 
 static const char NTLM_SIGNATURE[] = "NTLMSSP";
@@ -657,6 +657,12 @@
   return !auth_data_.empty();
 }
 
+bool HttpAuthHandlerNTLM::AllowsDefaultCredentials() {
+  // Default credentials are not supported in the portable implementation of
+  // NTLM, but are supported in the SSPI implementation.
+  return false;
+}
+
 // static
 HttpAuthHandlerNTLM::GenerateRandomProc
 HttpAuthHandlerNTLM::SetGenerateRandomProc(
@@ -704,4 +710,31 @@
   return OK;
 }
 
+HttpAuthHandlerNTLM::Factory::Factory() {
+}
+
+HttpAuthHandlerNTLM::Factory::~Factory() {
+}
+
+int HttpAuthHandlerNTLM::Factory::CreateAuthHandler(
+    HttpAuth::ChallengeTokenizer* challenge,
+    HttpAuth::Target target,
+    const GURL& origin,
+    CreateReason reason,
+    int digest_nonce_count,
+    const BoundNetLog& net_log,
+    scoped_ptr<HttpAuthHandler>* handler) {
+  if (reason == CREATE_PREEMPTIVE)
+    return ERR_UNSUPPORTED_AUTH_SCHEME;
+  // TODO(cbentzel): Move towards model of parsing in the factory
+  //                 method and only constructing when valid.
+  // NOTE: Default credentials are not supported for the portable implementation
+  // of NTLM.
+  scoped_ptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerNTLM);
+  if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
+    return ERR_INVALID_RESPONSE;
+  handler->swap(tmp_handler);
+  return OK;
+}
+
 }  // namespace net
diff --git a/net/http/http_auth_handler_ntlm_win.cc b/net/http/http_auth_handler_ntlm_win.cc
index fba9c1b..0f67c68 100644
--- a/net/http/http_auth_handler_ntlm_win.cc
+++ b/net/http/http_auth_handler_ntlm_win.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -14,12 +14,17 @@
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
 #include "net/http/http_auth_sspi_win.h"
+#include "net/http/url_security_manager.h"
 
 #pragma comment(lib, "secur32.lib")
 
 namespace net {
 
-HttpAuthHandlerNTLM::HttpAuthHandlerNTLM() :  auth_sspi_("NTLM", NTLMSP_NAME) {
+HttpAuthHandlerNTLM::HttpAuthHandlerNTLM(
+    SSPILibrary* sspi_library, ULONG max_token_length,
+    URLSecurityManager* url_security_manager)
+    : auth_sspi_(sspi_library, "NTLM", NTLMSP_NAME, max_token_length),
+      url_security_manager_(url_security_manager) {
 }
 
 HttpAuthHandlerNTLM::~HttpAuthHandlerNTLM() {
@@ -34,6 +39,51 @@
   return auth_sspi_.IsFinalRound();
 }
 
+bool HttpAuthHandlerNTLM::AllowsDefaultCredentials() {
+  if (target_ == HttpAuth::AUTH_PROXY)
+    return true;
+  if (!url_security_manager_)
+    return false;
+  return url_security_manager_->CanUseDefaultCredentials(origin_);
+}
+
+HttpAuthHandlerNTLM::Factory::Factory()
+    : max_token_length_(0),
+      first_creation_(true),
+      is_unsupported_(false),
+      sspi_library_(SSPILibrary::GetDefault()) {
+}
+
+HttpAuthHandlerNTLM::Factory::~Factory() {
+}
+
+int HttpAuthHandlerNTLM::Factory::CreateAuthHandler(
+    HttpAuth::ChallengeTokenizer* challenge,
+    HttpAuth::Target target,
+    const GURL& origin,
+    CreateReason reason,
+    int digest_nonce_count,
+    const BoundNetLog& net_log,
+    scoped_ptr<HttpAuthHandler>* handler) {
+  if (is_unsupported_ || reason == CREATE_PREEMPTIVE)
+    return ERR_UNSUPPORTED_AUTH_SCHEME;
+  if (max_token_length_ == 0) {
+    int rv = DetermineMaxTokenLength(sspi_library_, NTLMSP_NAME,
+                                     &max_token_length_);
+    if (rv == ERR_UNSUPPORTED_AUTH_SCHEME)
+      is_unsupported_ = true;
+    if (rv != OK)
+      return rv;
+  }
+  // TODO(cbentzel): Move towards model of parsing in the factory
+  //                 method and only constructing when valid.
+  scoped_ptr<HttpAuthHandler> tmp_handler(
+      new HttpAuthHandlerNTLM(sspi_library_, max_token_length_,
+                              url_security_manager()));
+  if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
+    return ERR_INVALID_RESPONSE;
+  handler->swap(tmp_handler);
+  return OK;
+}
 
 }  // namespace net
-
diff --git a/net/http/http_auth_handler_unittest.cc b/net/http/http_auth_handler_unittest.cc
new file mode 100644
index 0000000..2516745
--- /dev/null
+++ b/net/http/http_auth_handler_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2010 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/http/http_auth_handler.h"
+
+#include "net/base/capturing_net_log.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log_unittest.h"
+#include "net/base/test_completion_callback.h"
+#include "net/http/http_auth_handler_mock.h"
+#include "net/http/http_request_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+TEST(HttpAuthHandlerTest, NetLog) {
+  NetLog::Source source;
+  GURL origin("http://www.example.com");
+  std::string challenge = "Mock asdf";
+  std::wstring username = L"user";
+  std::wstring password = L"pass";
+  std::string auth_token;
+  HttpRequestInfo request;
+
+  for (int i = 0; i < 2; ++i) {
+    bool async = (i == 0);
+    for (int j = 0; j < 2; ++j) {
+      int rv = (j == 0) ? OK : ERR_UNEXPECTED;
+      for (int k = 0; k < 2; ++k) {
+        TestCompletionCallback test_callback;
+        HttpAuth::Target target =
+            (k == 0) ? HttpAuth::AUTH_PROXY : HttpAuth::AUTH_SERVER;
+        NetLog::EventType event_type =
+            (k == 0) ? NetLog::TYPE_AUTH_PROXY : NetLog::TYPE_AUTH_SERVER;
+        HttpAuth::ChallengeTokenizer tokenizer(
+            challenge.begin(), challenge.end());
+        HttpAuthHandlerMock mock_handler;
+        CapturingNetLog capturing_net_log(CapturingNetLog::kUnbounded);
+        BoundNetLog bound_net_log(source, &capturing_net_log);
+
+        mock_handler.InitFromChallenge(&tokenizer, target,
+                                       origin, bound_net_log);
+        mock_handler.SetGenerateExpectation(async, rv);
+        mock_handler.GenerateAuthToken(&username, &password, &request,
+                                       &test_callback, &auth_token);
+        if (async)
+          test_callback.WaitForResult();
+
+        EXPECT_EQ(2u, capturing_net_log.entries().size());
+        EXPECT_TRUE(LogContainsBeginEvent(capturing_net_log.entries(),
+                                          0, event_type));
+        EXPECT_TRUE(LogContainsEndEvent(capturing_net_log.entries(),
+                                        1, event_type));
+      }
+    }
+  }
+}
+
+}  // namespace net
diff --git a/net/http/http_auth_sspi_win.cc b/net/http/http_auth_sspi_win.cc
index 00861e5..221acf1 100644
--- a/net/http/http_auth_sspi_win.cc
+++ b/net/http/http_auth_sspi_win.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -9,18 +9,106 @@
 
 #include "base/base64.h"
 #include "base/logging.h"
+#include "base/singleton.h"
 #include "base/string_util.h"
 #include "net/base/net_errors.h"
-#include "net/base/net_util.h"
 #include "net/http/http_auth.h"
 
 namespace net {
 
-HttpAuthSSPI::HttpAuthSSPI(const std::string& scheme,
-                           SEC_WCHAR* security_package)
-    : scheme_(scheme),
+namespace {
+
+int MapAcquireCredentialsStatusToError(SECURITY_STATUS status,
+                                       const SEC_WCHAR* package) {
+  switch (status) {
+    case SEC_E_OK:
+      return OK;
+    case SEC_E_INSUFFICIENT_MEMORY:
+      return ERR_OUT_OF_MEMORY;
+    case SEC_E_INTERNAL_ERROR:
+      return ERR_UNEXPECTED;
+    case SEC_E_NO_CREDENTIALS:
+    case SEC_E_NOT_OWNER:
+    case SEC_E_UNKNOWN_CREDENTIALS:
+      return ERR_INVALID_AUTH_CREDENTIALS;
+    case SEC_E_SECPKG_NOT_FOUND:
+      // This indicates that the SSPI configuration does not match expectations
+      LOG(ERROR) << "Received SEC_E_SECPKG_NOT_FOUND for " << package;
+      return ERR_UNSUPPORTED_AUTH_SCHEME;
+    default:
+      LOG(ERROR) << "Unexpected SECURITY_STATUS " << status;
+      return ERR_UNEXPECTED;
+  }
+}
+
+int AcquireExplicitCredentials(SSPILibrary* library,
+                               const SEC_WCHAR* package,
+                               const std::wstring& domain,
+                               const std::wstring& user,
+                               const std::wstring& password,
+                               CredHandle* cred) {
+  SEC_WINNT_AUTH_IDENTITY identity;
+  identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+  identity.User =
+      reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(user.c_str()));
+  identity.UserLength = user.size();
+  identity.Domain =
+      reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(domain.c_str()));
+  identity.DomainLength = domain.size();
+  identity.Password =
+      reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(password.c_str()));
+  identity.PasswordLength = password.size();
+
+  TimeStamp expiry;
+
+  // Pass the username/password to get the credentials handle.
+  SECURITY_STATUS status = library->AcquireCredentialsHandle(
+      NULL,  // pszPrincipal
+      const_cast<SEC_WCHAR*>(package),  // pszPackage
+      SECPKG_CRED_OUTBOUND,  // fCredentialUse
+      NULL,  // pvLogonID
+      &identity,  // pAuthData
+      NULL,  // pGetKeyFn (not used)
+      NULL,  // pvGetKeyArgument (not used)
+      cred,  // phCredential
+      &expiry);  // ptsExpiry
+
+  return MapAcquireCredentialsStatusToError(status, package);
+}
+
+int AcquireDefaultCredentials(SSPILibrary* library, const SEC_WCHAR* package,
+                              CredHandle* cred) {
+  TimeStamp expiry;
+
+  // Pass the username/password to get the credentials handle.
+  // Note: Since the 5th argument is NULL, it uses the default
+  // cached credentials for the logged in user, which can be used
+  // for a single sign-on.
+  SECURITY_STATUS status = library->AcquireCredentialsHandle(
+      NULL,  // pszPrincipal
+      const_cast<SEC_WCHAR*>(package),  // pszPackage
+      SECPKG_CRED_OUTBOUND,  // fCredentialUse
+      NULL,  // pvLogonID
+      NULL,  // pAuthData
+      NULL,  // pGetKeyFn (not used)
+      NULL,  // pvGetKeyArgument (not used)
+      cred,  // phCredential
+      &expiry);  // ptsExpiry
+
+  return MapAcquireCredentialsStatusToError(status, package);
+}
+
+}  // anonymous namespace
+
+HttpAuthSSPI::HttpAuthSSPI(SSPILibrary* library,
+                           const std::string& scheme,
+                           SEC_WCHAR* security_package,
+                           ULONG max_token_length)
+    : library_(library),
+      scheme_(scheme),
       security_package_(security_package),
-      max_token_length_(0) {
+      max_token_length_(max_token_length) {
+  DCHECK(library_);
   SecInvalidateHandle(&cred_);
   SecInvalidateHandle(&ctxt_);
 }
@@ -28,7 +116,7 @@
 HttpAuthSSPI::~HttpAuthSSPI() {
   ResetSecurityContext();
   if (SecIsValidHandle(&cred_)) {
-    FreeCredentialsHandle(&cred_);
+    library_->FreeCredentialsHandle(&cred_);
     SecInvalidateHandle(&cred_);
   }
 }
@@ -43,66 +131,52 @@
 
 void HttpAuthSSPI::ResetSecurityContext() {
   if (SecIsValidHandle(&ctxt_)) {
-    DeleteSecurityContext(&ctxt_);
+    library_->DeleteSecurityContext(&ctxt_);
     SecInvalidateHandle(&ctxt_);
   }
 }
 
-bool HttpAuthSSPI::ParseChallenge(std::string::const_iterator challenge_begin,
-                                  std::string::const_iterator challenge_end) {
+bool HttpAuthSSPI::ParseChallenge(HttpAuth::ChallengeTokenizer* tok) {
   // Verify the challenge's auth-scheme.
-  HttpAuth::ChallengeTokenizer challenge_tok(challenge_begin, challenge_end);
-  if (!challenge_tok.valid() ||
-      !LowerCaseEqualsASCII(challenge_tok.scheme(),
-                            StringToLowerASCII(scheme_).c_str()))
+  if (!tok->valid() ||
+      !LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
     return false;
-  // Extract the auth-data.  We can't use challenge_tok.GetNext() because
-  // auth-data is base64-encoded and may contain '=' padding at the end,
-  // which would be mistaken for a name=value pair.
-  challenge_begin += scheme_.length();  // Skip over scheme name.
-  HttpUtil::TrimLWS(&challenge_begin, &challenge_end);
-  std::string encoded_auth_token(challenge_begin, challenge_end);
-  int encoded_length = encoded_auth_token.length();
-  // Strip off any padding.
-  // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.)
-  //
-  // Our base64 decoder requires that the length be a multiple of 4.
-  while (encoded_length > 0 && encoded_length % 4 != 0 &&
-         encoded_auth_token[encoded_length - 1] == '=')
-    encoded_length--;
-  encoded_auth_token.erase(encoded_length);
 
-  std::string decoded_auth_token;
-  bool rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
-  if (rv) {
-    decoded_server_auth_token_ = decoded_auth_token;
+  tok->set_expect_base64_token(true);
+  if (!tok->GetNext()) {
+    decoded_server_auth_token_.clear();
+    return true;
   }
-  return rv;
+
+  std::string encoded_auth_token = tok->value();
+  std::string decoded_auth_token;
+  bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
+  if (!base64_rv) {
+    LOG(ERROR) << "Base64 decoding of auth token failed.";
+    return false;
+  }
+  decoded_server_auth_token_ = decoded_auth_token;
+  return true;
 }
 
-int HttpAuthSSPI::GenerateCredentials(const std::wstring& username,
-                                      const std::wstring& password,
-                                      const GURL& origin,
-                                      const HttpRequestInfo* request,
-                                      const ProxyInfo* proxy,
-                                      std::string* out_credentials) {
-  // |username| may be in the form "DOMAIN\user".  Parse it into the two
-  // components.
-  std::wstring domain;
-  std::wstring user;
-  SplitDomainAndUser(username, &domain, &user);
+int HttpAuthSSPI::GenerateAuthToken(const std::wstring* username,
+                                    const std::wstring* password,
+                                    const std::wstring& spn,
+                                    std::string* auth_token) {
+  DCHECK((username == NULL) == (password == NULL));
 
   // Initial challenge.
   if (!IsFinalRound()) {
-    int rv = OnFirstRound(domain, user, password);
+    int rv = OnFirstRound(username, password);
     if (rv != OK)
       return rv;
   }
 
+  DCHECK(SecIsValidHandle(&cred_));
   void* out_buf;
   int out_buf_len;
   int rv = GetNextSecurityToken(
-      origin,
+      spn,
       static_cast<void *>(const_cast<char *>(
           decoded_server_auth_token_.c_str())),
       decoded_server_auth_token_.length(),
@@ -114,28 +188,41 @@
   // Base64 encode data in output buffer and prepend the scheme.
   std::string encode_input(static_cast<char*>(out_buf), out_buf_len);
   std::string encode_output;
-  bool ok = base::Base64Encode(encode_input, &encode_output);
+  bool base64_rv = base::Base64Encode(encode_input, &encode_output);
   // OK, we are done with |out_buf|
   free(out_buf);
-  if (!ok)
-    return rv;
-  *out_credentials = scheme_ + " " + encode_output;
+  if (!base64_rv) {
+    LOG(ERROR) << "Base64 encoding of auth token failed.";
+    return ERR_UNEXPECTED;
+  }
+  *auth_token = scheme_ + " " + encode_output;
   return OK;
 }
 
-int HttpAuthSSPI::OnFirstRound(const std::wstring& domain,
-                               const std::wstring& user,
-                               const std::wstring& password) {
-  int rv = DetermineMaxTokenLength(security_package_, &max_token_length_);
-  if (rv != OK) {
-    return rv;
+int HttpAuthSSPI::OnFirstRound(const std::wstring* username,
+                               const std::wstring* password) {
+  DCHECK((username == NULL) == (password == NULL));
+  DCHECK(!SecIsValidHandle(&cred_));
+  int rv = OK;
+  if (username) {
+    std::wstring domain;
+    std::wstring user;
+    SplitDomainAndUser(*username, &domain, &user);
+    rv = AcquireExplicitCredentials(library_, security_package_, domain,
+                                    user, *password, &cred_);
+    if (rv != OK)
+      return rv;
+  } else {
+    rv = AcquireDefaultCredentials(library_, security_package_, &cred_);
+    if (rv != OK)
+      return rv;
   }
-  rv = AcquireCredentials(security_package_, domain, user, password, &cred_);
+
   return rv;
 }
 
 int HttpAuthSSPI::GetNextSecurityToken(
-    const GURL& origin,
+    const std::wstring& spn,
     const void * in_token,
     int in_token_len,
     void** out_token,
@@ -181,30 +268,25 @@
   if (!out_buffer.pvBuffer)
     return ERR_OUT_OF_MEMORY;
 
-  // The service principal name of the destination server.  See
-  // http://msdn.microsoft.com/en-us/library/ms677949%28VS.85%29.aspx
-  std::wstring target(L"HTTP/");
-  target.append(ASCIIToWide(GetHostAndPort(origin)));
-  wchar_t* target_name = const_cast<wchar_t*>(target.c_str());
-
   // This returns a token that is passed to the remote server.
-  status = InitializeSecurityContext(&cred_,  // phCredential
-                                     ctxt_ptr,  // phContext
-                                     target_name,  // pszTargetName
-                                     0,  // fContextReq
-                                     0,  // Reserved1 (must be 0)
-                                     SECURITY_NATIVE_DREP,  // TargetDataRep
-                                     in_buffer_desc_ptr,  // pInput
-                                     0,  // Reserved2 (must be 0)
-                                     &ctxt_,  // phNewContext
-                                     &out_buffer_desc,  // pOutput
-                                     &ctxt_attr,  // pfContextAttr
-                                     &expiry);  // ptsExpiry
+  status = library_->InitializeSecurityContext(
+      &cred_,  // phCredential
+      ctxt_ptr,  // phContext
+      const_cast<wchar_t *>(spn.c_str()),  // pszTargetName
+      0,  // fContextReq
+      0,  // Reserved1 (must be 0)
+      SECURITY_NATIVE_DREP,  // TargetDataRep
+      in_buffer_desc_ptr,  // pInput
+      0,  // Reserved2 (must be 0)
+      &ctxt_,  // phNewContext
+      &out_buffer_desc,  // pOutput
+      &ctxt_attr,  // pfContextAttr
+      &expiry);  // ptsExpiry
   // On success, the function returns SEC_I_CONTINUE_NEEDED on the first call
   // and SEC_E_OK on the second call.  On failure, the function returns an
   // error code.
   if (status != SEC_I_CONTINUE_NEEDED && status != SEC_E_OK) {
-    LOG(ERROR) << "InitializeSecurityContext failed: " << status;
+    LOG(ERROR) << "InitializeSecurityContext failed " << status;
     ResetSecurityContext();
     free(out_buffer.pvBuffer);
     return ERR_UNEXPECTED;  // TODO(wtc): map error code.
@@ -221,6 +303,9 @@
 void SplitDomainAndUser(const std::wstring& combined,
                         std::wstring* domain,
                         std::wstring* user) {
+  // |combined| may be in the form "user" or "DOMAIN\user".
+  // Separatethe two parts if they exist.
+  // TODO(cbentzel): I believe user@domain is also a valid form.
   size_t backslash_idx = combined.find(L'\\');
   if (backslash_idx == std::wstring::npos) {
     domain->clear();
@@ -231,56 +316,105 @@
   }
 }
 
-int DetermineMaxTokenLength(const std::wstring& package,
+int DetermineMaxTokenLength(SSPILibrary* library,
+                            const std::wstring& package,
                             ULONG* max_token_length) {
-  PSecPkgInfo pkg_info;
-  SECURITY_STATUS status = QuerySecurityPackageInfo(
+  DCHECK(library);
+  DCHECK(max_token_length);
+  PSecPkgInfo pkg_info = NULL;
+  SECURITY_STATUS status = library->QuerySecurityPackageInfo(
       const_cast<wchar_t *>(package.c_str()), &pkg_info);
   if (status != SEC_E_OK) {
-    LOG(ERROR) << "Security package " << package << " not found";
+    // The documentation at
+    // http://msdn.microsoft.com/en-us/library/aa379359(VS.85).aspx
+    // only mentions that a non-zero (or non-SEC_E_OK) value is returned
+    // if the function fails. In practice, it appears to return
+    // SEC_E_SECPKG_NOT_FOUND for invalid/unknown packages.
+    LOG(ERROR) << "Security package " << package << " not found."
+               << " Status code: " << status;
+    if (status == SEC_E_SECPKG_NOT_FOUND)
+      return ERR_UNSUPPORTED_AUTH_SCHEME;
+    else
+      return ERR_UNEXPECTED;
+  }
+  int token_length = pkg_info->cbMaxToken;
+  status = library->FreeContextBuffer(pkg_info);
+  if (status != SEC_E_OK) {
+    // The documentation at
+    // http://msdn.microsoft.com/en-us/library/aa375416(VS.85).aspx
+    // only mentions that a non-zero (or non-SEC_E_OK) value is returned
+    // if the function fails, and does not indicate what the failure conditions
+    // are.
+    LOG(ERROR) << "Unexpected problem freeing context buffer. Status code: "
+               << status;
     return ERR_UNEXPECTED;
   }
-  *max_token_length = pkg_info->cbMaxToken;
-  FreeContextBuffer(pkg_info);
+  *max_token_length = token_length;
   return OK;
 }
 
-int AcquireCredentials(const SEC_WCHAR* package,
-                       const std::wstring& domain,
-                       const std::wstring& user,
-                       const std::wstring& password,
-                       CredHandle* cred) {
-  SEC_WINNT_AUTH_IDENTITY identity;
-  identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
-  identity.User =
-      reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(user.c_str()));
-  identity.UserLength = user.size();
-  identity.Domain =
-      reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(domain.c_str()));
-  identity.DomainLength = domain.size();
-  identity.Password =
-      reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(password.c_str()));
-  identity.PasswordLength = password.size();
+class SSPILibraryDefault : public SSPILibrary {
+ public:
+  SSPILibraryDefault() {}
+  virtual ~SSPILibraryDefault() {}
 
-  TimeStamp expiry;
+  virtual SECURITY_STATUS AcquireCredentialsHandle(LPWSTR pszPrincipal,
+                                                   LPWSTR pszPackage,
+                                                   unsigned long fCredentialUse,
+                                                   void* pvLogonId,
+                                                   void* pvAuthData,
+                                                   SEC_GET_KEY_FN pGetKeyFn,
+                                                   void* pvGetKeyArgument,
+                                                   PCredHandle phCredential,
+                                                   PTimeStamp ptsExpiry) {
+    return ::AcquireCredentialsHandle(pszPrincipal, pszPackage, fCredentialUse,
+                                      pvLogonId, pvAuthData, pGetKeyFn,
+                                      pvGetKeyArgument, phCredential,
+                                      ptsExpiry);
+  }
 
-  // Pass the username/password to get the credentials handle.
-  // Note: If the 5th argument is NULL, it uses the default cached credentials
-  // for the logged in user, which can be used for single sign-on.
-  SECURITY_STATUS status = AcquireCredentialsHandle(
-      NULL,  // pszPrincipal
-      const_cast<SEC_WCHAR*>(package),  // pszPackage
-      SECPKG_CRED_OUTBOUND,  // fCredentialUse
-      NULL,  // pvLogonID
-      &identity,  // pAuthData
-      NULL,  // pGetKeyFn (not used)
-      NULL,  // pvGetKeyArgument (not used)
-      cred,  // phCredential
-      &expiry);  // ptsExpiry
+  virtual SECURITY_STATUS InitializeSecurityContext(PCredHandle phCredential,
+                                                    PCtxtHandle phContext,
+                                                    SEC_WCHAR* pszTargetName,
+                                                    unsigned long fContextReq,
+                                                    unsigned long Reserved1,
+                                                    unsigned long TargetDataRep,
+                                                    PSecBufferDesc pInput,
+                                                    unsigned long Reserved2,
+                                                    PCtxtHandle phNewContext,
+                                                    PSecBufferDesc pOutput,
+                                                    unsigned long* contextAttr,
+                                                    PTimeStamp ptsExpiry) {
+    return ::InitializeSecurityContext(phCredential, phContext, pszTargetName,
+                                       fContextReq, Reserved1, TargetDataRep,
+                                       pInput, Reserved2, phNewContext, pOutput,
+                                       contextAttr, ptsExpiry);
+  }
 
-  if (status != SEC_E_OK)
-    return ERR_UNEXPECTED;
-  return OK;
+  virtual SECURITY_STATUS QuerySecurityPackageInfo(LPWSTR pszPackageName,
+                                                   PSecPkgInfoW *pkgInfo) {
+    return ::QuerySecurityPackageInfo(pszPackageName, pkgInfo);
+  }
+
+  virtual SECURITY_STATUS FreeCredentialsHandle(PCredHandle phCredential) {
+    return ::FreeCredentialsHandle(phCredential);
+  }
+
+  virtual SECURITY_STATUS DeleteSecurityContext(PCtxtHandle phContext) {
+    return ::DeleteSecurityContext(phContext);
+  }
+
+  virtual SECURITY_STATUS FreeContextBuffer(PVOID pvContextBuffer) {
+    return ::FreeContextBuffer(pvContextBuffer);
+  }
+
+ private:
+  friend struct DefaultSingletonTraits<SSPILibraryDefault>;
+};
+
+// static
+SSPILibrary* SSPILibrary::GetDefault() {
+  return Singleton<SSPILibraryDefault>::get();
 }
 
 }  // namespace net
diff --git a/net/http/http_auth_sspi_win.h b/net/http/http_auth_sspi_win.h
old mode 100755
new mode 100644
index 4d340aa..14d158a
--- a/net/http/http_auth_sspi_win.h
+++ b/net/http/http_auth_sspi_win.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -16,45 +16,101 @@
 
 #include <string>
 
-class GURL;
+#include "net/http/http_auth.h"
 
 namespace net {
 
-class HttpRequestInfo;
+struct HttpRequestInfo;
 class ProxyInfo;
 
+// SSPILibrary is introduced so unit tests can mock the calls to Windows' SSPI
+// implementation. The default implementation simply passes the arguments on to
+// the SSPI implementation provided by Secur32.dll.
+// NOTE(cbentzel): I considered replacing the Secur32.dll with a mock DLL, but
+// decided that it wasn't worth the effort as this is unlikely to be performance
+// sensitive code.
+class SSPILibrary {
+ public:
+  virtual ~SSPILibrary() {}
+
+  virtual SECURITY_STATUS AcquireCredentialsHandle(LPWSTR pszPrincipal,
+                                                   LPWSTR pszPackage,
+                                                   unsigned long fCredentialUse,
+                                                   void* pvLogonId,
+                                                   void* pvAuthData,
+                                                   SEC_GET_KEY_FN pGetKeyFn,
+                                                   void* pvGetKeyArgument,
+                                                   PCredHandle phCredential,
+                                                   PTimeStamp ptsExpiry) = 0;
+
+  virtual SECURITY_STATUS InitializeSecurityContext(PCredHandle phCredential,
+                                                    PCtxtHandle phContext,
+                                                    SEC_WCHAR* pszTargetName,
+                                                    unsigned long fContextReq,
+                                                    unsigned long Reserved1,
+                                                    unsigned long TargetDataRep,
+                                                    PSecBufferDesc pInput,
+                                                    unsigned long Reserved2,
+                                                    PCtxtHandle phNewContext,
+                                                    PSecBufferDesc pOutput,
+                                                    unsigned long* contextAttr,
+                                                    PTimeStamp ptsExpiry) = 0;
+
+  virtual SECURITY_STATUS QuerySecurityPackageInfo(LPWSTR pszPackageName,
+                                                   PSecPkgInfoW *pkgInfo) = 0;
+
+  virtual SECURITY_STATUS FreeCredentialsHandle(PCredHandle phCredential) = 0;
+
+  virtual SECURITY_STATUS DeleteSecurityContext(PCtxtHandle phContext) = 0;
+
+  virtual SECURITY_STATUS FreeContextBuffer(PVOID pvContextBuffer) = 0;
+
+  // Get the default SSPILibrary instance, which simply acts as a passthrough
+  // to the Windows SSPI implementation. The object returned is a singleton
+  // instance, and the caller should not delete it.
+  static SSPILibrary* GetDefault();
+};
+
 class HttpAuthSSPI {
  public:
-  HttpAuthSSPI(const std::string& scheme,
-               SEC_WCHAR* security_package);
+  HttpAuthSSPI(SSPILibrary* sspi_library,
+               const std::string& scheme,
+               SEC_WCHAR* security_package,
+               ULONG max_token_length);
   ~HttpAuthSSPI();
 
   bool NeedsIdentity() const;
   bool IsFinalRound() const;
 
-  bool ParseChallenge(std::string::const_iterator challenge_begin,
-                      std::string::const_iterator challenge_end);
+  bool ParseChallenge(HttpAuth::ChallengeTokenizer* tok);
 
-  int GenerateCredentials(const std::wstring& username,
-                          const std::wstring& password,
-                          const GURL& origin,
-                          const HttpRequestInfo* request,
-                          const ProxyInfo* proxy,
-                          std::string* out_credentials);
+  // Generates an authentication token for the service specified by the
+  // Service Principal Name |spn| and stores the value in |*auth_token|.
+  // If the return value is not |OK|, then the value of |*auth_token| is
+  // unspecified. ERR_IO_PENDING is not a valid return code.
+  // If this is the first round of a multiple round scheme, credentials are
+  // obtained using |*username| and |*password|. If |username| and |password|
+  // are both NULL, the credentials for the currently logged in user are used
+  // instead.
+  int GenerateAuthToken(const std::wstring* username,
+                        const std::wstring* password,
+                        const std::wstring& spn,
+                        std::string* auth_token);
 
  private:
-  int OnFirstRound(const std::wstring& domain,
-                   const std::wstring& user,
-                   const std::wstring& password);
+  int OnFirstRound(const std::wstring* username,
+                   const std::wstring* password);
 
   int GetNextSecurityToken(
-      const GURL& origin,
+      const std::wstring& spn,
       const void* in_token,
       int in_token_len,
       void** out_token,
       int* out_token_len);
 
   void ResetSecurityContext();
+
+  SSPILibrary* library_;
   std::string scheme_;
   SEC_WCHAR* security_package_;
   std::string decoded_server_auth_token_;
@@ -73,20 +129,24 @@
                         std::wstring* domain,
                         std::wstring* user);
 
-// Determines the max token length for a particular SSPI package.
-// If the return value is not OK, than the value of max_token_length
-// is undefined.
-// |max_token_length| must be non-NULL.
-int DetermineMaxTokenLength(const std::wstring& package,
+// Determines the maximum token length in bytes for a particular SSPI package.
+//
+// |library| and |max_token_length| must be non-NULL pointers to valid objects.
+//
+// If the return value is OK, |*max_token_length| contains the maximum token
+// length in bytes.
+//
+// If the return value is ERR_UNSUPPORTED_AUTH_SCHEME, |package| is not an
+// known SSPI authentication scheme on this system. |*max_token_length| is not
+// changed.
+//
+// If the return value is ERR_UNEXPECTED, there was an unanticipated problem
+// in the underlying SSPI call. The details are logged, and |*max_token_length|
+// is not changed.
+int DetermineMaxTokenLength(SSPILibrary* library,
+                            const std::wstring& package,
                             ULONG* max_token_length);
 
-// Acquire credentials for a user.
-int AcquireCredentials(const SEC_WCHAR* package,
-                       const std::wstring& domain,
-                       const std::wstring& user,
-                       const std::wstring& password,
-                       CredHandle* cred);
-
 }  // namespace net
-#endif  // NET_HTTP_HTTP_AUTH_SSPI_WIN_H_
 
+#endif  // NET_HTTP_HTTP_AUTH_SSPI_WIN_H_
diff --git a/net/http/http_auth_sspi_win_unittest.cc b/net/http/http_auth_sspi_win_unittest.cc
index b421ca9..fdef793 100644
--- a/net/http/http_auth_sspi_win_unittest.cc
+++ b/net/http/http_auth_sspi_win_unittest.cc
@@ -1,13 +1,17 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "base/basictypes.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_auth_sspi_win.h"
+#include "net/http/mock_sspi_library_win.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
 
+namespace {
+
 void MatchDomainUserAfterSplit(const std::wstring& combined,
                                const std::wstring& expected_domain,
                                const std::wstring& expected_user) {
@@ -18,9 +22,36 @@
   EXPECT_EQ(expected_user, actual_user);
 }
 
-TEST(HttpAuthHandlerSspiWinTest, SplitUserAndDomain) {
+}  // namespace
+
+TEST(HttpAuthSSPITest, SplitUserAndDomain) {
   MatchDomainUserAfterSplit(L"foobar", L"", L"foobar");
   MatchDomainUserAfterSplit(L"FOO\\bar", L"FOO", L"bar");
 }
 
+TEST(HttpAuthSSPITest, DetermineMaxTokenLength_Normal) {
+  SecPkgInfoW package_info;
+  memset(&package_info, 0x0, sizeof(package_info));
+  package_info.cbMaxToken = 1337;
+
+  MockSSPILibrary mock_library;
+  mock_library.ExpectQuerySecurityPackageInfo(L"NTLM", SEC_E_OK, &package_info);
+  ULONG max_token_length = 100;
+  int rv = DetermineMaxTokenLength(&mock_library, L"NTLM", &max_token_length);
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ(1337, max_token_length);
+}
+
+TEST(HttpAuthSSPITest, DetermineMaxTokenLength_InvalidPackage) {
+  MockSSPILibrary mock_library;
+  mock_library.ExpectQuerySecurityPackageInfo(L"Foo", SEC_E_SECPKG_NOT_FOUND,
+                                              NULL);
+  ULONG max_token_length = 100;
+  int rv = DetermineMaxTokenLength(&mock_library, L"Foo", &max_token_length);
+  EXPECT_EQ(ERR_UNSUPPORTED_AUTH_SCHEME, rv);
+  // |DetermineMaxTokenLength()| interface states that |max_token_length| should
+  // not change on failure.
+  EXPECT_EQ(100, max_token_length);
+}
+
 }  // namespace net
diff --git a/net/http/http_auth_unittest.cc b/net/http/http_auth_unittest.cc
old mode 100755
new mode 100644
index a960d70..cdc7e16
--- a/net/http/http_auth_unittest.cc
+++ b/net/http/http_auth_unittest.cc
@@ -1,14 +1,21 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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 "testing/gtest/include/gtest/gtest.h"
+#include <set>
+#include <string>
 
 #include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_auth.h"
+#include "net/http/http_auth_filter.h"
 #include "net/http/http_auth_handler.h"
+#include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
 
@@ -56,18 +63,20 @@
       "WWW-Authenticate: Negotiate\n"
       "WWW-Authenticate: NTLM\n",
 
-      // Negotiate is not currently support on non-Windows platforms, so
-      // the choice varies depending on platform.
-#if defined(OS_WIN)
+      // TODO(ahendrickson): This may be flaky on Linux and OSX as it
+      // relies on being able to load one of the known .so files
+      // for gssapi.
       "negotiate",
       "",
-#else
-      "ntlm",
-      "",
-#endif
     }
   };
   GURL origin("http://www.example.com");
+  std::set<std::string> disabled_schemes;
+  URLSecurityManagerAllow url_security_manager;
+  scoped_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory(
+      HttpAuthHandlerFactory::CreateDefault());
+  http_auth_handler_factory->SetURLSecurityManager(
+      "negotiate", &url_security_manager);
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
     // Make a HttpResponseHeaders object.
@@ -79,13 +88,16 @@
                 headers_with_status_line.c_str(),
                 headers_with_status_line.length())));
 
-    scoped_refptr<HttpAuthHandler> handler;
-    HttpAuth::ChooseBestChallenge(headers.get(),
+    scoped_ptr<HttpAuthHandler> handler;
+    HttpAuth::ChooseBestChallenge(http_auth_handler_factory.get(),
+                                  headers.get(),
                                   HttpAuth::AUTH_SERVER,
                                   origin,
+                                  disabled_schemes,
+                                  BoundNetLog(),
                                   &handler);
 
-    if (handler) {
+    if (handler.get()) {
       EXPECT_STREQ(tests[i].challenge_scheme, handler->scheme().c_str());
       EXPECT_STREQ(tests[i].challenge_realm, handler->realm().c_str());
     } else {
@@ -95,7 +107,7 @@
   }
 }
 
-TEST(HttpAuthTest, ChooseBestChallengeConnectionBased) {
+TEST(HttpAuthTest, ChooseBestChallengeConnectionBasedNTLM) {
   static const struct {
     const char* headers;
     const char* challenge_realm;
@@ -120,8 +132,12 @@
     }
   };
   GURL origin("http://www.example.com");
+  std::set<std::string> disabled_schemes;
 
-  scoped_refptr<HttpAuthHandler> handler;
+  scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory(
+      HttpAuthHandlerFactory::CreateDefault());
+  scoped_ptr<HttpAuthHandler> handler;
+
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
     // Make a HttpResponseHeaders object.
     std::string headers_with_status_line("HTTP/1.1 401 Unauthorized\n");
@@ -132,17 +148,86 @@
                 headers_with_status_line.c_str(),
                 headers_with_status_line.length())));
 
-    scoped_refptr<HttpAuthHandler> old_handler = handler;
-    HttpAuth::ChooseBestChallenge(headers.get(),
+    // possibly_deleted_old_handler may point to deleted memory
+    // after ChooseBestChallenge has been called, and should not
+    // be dereferenced.
+    HttpAuthHandler* possibly_deleted_old_handler = handler.get();
+    HttpAuth::ChooseBestChallenge(http_auth_handler_factory.get(),
+                                  headers.get(),
                                   HttpAuth::AUTH_SERVER,
                                   origin,
+                                  disabled_schemes,
+                                  BoundNetLog(),
                                   &handler);
-
     EXPECT_TRUE(handler != NULL);
     // Since NTLM is connection-based, we should continue to use the existing
     // handler rather than creating a new one.
     if (i != 0)
-      EXPECT_EQ(old_handler, handler);
+      EXPECT_EQ(possibly_deleted_old_handler, handler.get());
+    ASSERT_NE(reinterpret_cast<net::HttpAuthHandler *>(NULL), handler.get());
+    EXPECT_STREQ(tests[i].challenge_realm, handler->realm().c_str());
+  }
+}
+
+TEST(HttpAuthTest, ChooseBestChallengeConnectionBasedNegotiate) {
+  static const struct {
+    const char* headers;
+    const char* challenge_realm;
+  } tests[] = {
+    {
+      "WWW-Authenticate: Negotiate\r\n",
+
+      "",
+    },
+    {
+      "WWW-Authenticate: Negotiate "
+      "TlRMTVNTUAACAAAADAAMADgAAAAFgokCTroKF1e/DRcAAAAAAAAAALo"
+      "AugBEAAAABQEoCgAAAA9HAE8ATwBHAEwARQACAAwARwBPAE8ARwBMAE"
+      "UAAQAaAEEASwBFAEUAUwBBAFIAQQAtAEMATwBSAFAABAAeAGMAbwByA"
+      "HAALgBnAG8AbwBnAGwAZQAuAGMAbwBtAAMAQABhAGsAZQBlAHMAYQBy"
+      "AGEALQBjAG8AcgBwAC4AYQBkAC4AYwBvAHIAcAAuAGcAbwBvAGcAbAB"
+      "lAC4AYwBvAG0ABQAeAGMAbwByAHAALgBnAG8AbwBnAGwAZQAuAGMAbw"
+      "BtAAAAAAA=\r\n",
+
+      // Realm is empty.
+      "",
+    }
+  };
+  GURL origin("http://www.example.com");
+  std::set<std::string> disabled_schemes;
+  URLSecurityManagerAllow url_security_manager;
+  scoped_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory(
+      HttpAuthHandlerFactory::CreateDefault());
+  http_auth_handler_factory->SetURLSecurityManager(
+      "negotiate", &url_security_manager);
+
+  scoped_ptr<HttpAuthHandler> handler;
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+    // Make a HttpResponseHeaders object.
+    std::string headers_with_status_line("HTTP/1.1 401 Unauthorized\n");
+    headers_with_status_line += tests[i].headers;
+    scoped_refptr<net::HttpResponseHeaders> headers(
+        new net::HttpResponseHeaders(
+            net::HttpUtil::AssembleRawHeaders(
+                headers_with_status_line.c_str(),
+                headers_with_status_line.length())));
+
+    HttpAuthHandler* old_handler = handler.get();
+    HttpAuth::ChooseBestChallenge(http_auth_handler_factory.get(),
+                                  headers.get(),
+                                  HttpAuth::AUTH_SERVER,
+                                  origin,
+                                  disabled_schemes,
+                                  BoundNetLog(),
+                                  &handler);
+
+    EXPECT_TRUE(handler != NULL);
+    // Since Negotiate is connection-based, we should continue to use the
+    // existing handler rather than creating a new one.
+    if (i != 0)
+      EXPECT_EQ(old_handler, handler.get());
+
+    ASSERT_NE(reinterpret_cast<net::HttpAuthHandler *>(NULL), handler.get());
 
     EXPECT_STREQ(tests[i].challenge_realm, handler->realm().c_str());
   }
@@ -179,6 +264,82 @@
   EXPECT_FALSE(challenge.GetNext());
 }
 
+// Use a name=value property with mismatching quote marks.
+TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotes) {
+  std::string challenge_str = "Basic realm=\"foobar@baz.com";
+  HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
+                                         challenge_str.end());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("Basic"), challenge.scheme());
+  EXPECT_TRUE(challenge.GetNext());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("realm"), challenge.name());
+  EXPECT_EQ(std::string("foobar@baz.com"), challenge.value());
+  EXPECT_EQ(std::string("foobar@baz.com"), challenge.unquoted_value());
+  EXPECT_FALSE(challenge.value_is_quoted());
+  EXPECT_FALSE(challenge.GetNext());
+}
+
+// Use a name= property without a value and with mismatching quote marks.
+TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesNoValue) {
+  std::string challenge_str = "Basic realm=\"";
+  HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
+                                         challenge_str.end());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("Basic"), challenge.scheme());
+  EXPECT_TRUE(challenge.GetNext());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("realm"), challenge.name());
+  EXPECT_EQ(std::string(""), challenge.value());
+  EXPECT_FALSE(challenge.value_is_quoted());
+  EXPECT_FALSE(challenge.GetNext());
+}
+
+// Use a name=value property with mismatching quote marks and spaces in the
+// value.
+TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesSpaces) {
+  std::string challenge_str = "Basic realm=\"foo bar";
+  HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
+                                         challenge_str.end());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("Basic"), challenge.scheme());
+  EXPECT_TRUE(challenge.GetNext());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("realm"), challenge.name());
+  EXPECT_EQ(std::string("foo bar"), challenge.value());
+  EXPECT_EQ(std::string("foo bar"), challenge.unquoted_value());
+  EXPECT_FALSE(challenge.value_is_quoted());
+  EXPECT_FALSE(challenge.GetNext());
+}
+
+// Use multiple name=value properties with mismatching quote marks in the last
+// value.
+TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesMultiple) {
+  std::string challenge_str = "Digest qop=, algorithm=md5, realm=\"foo";
+  HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
+                                         challenge_str.end());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("Digest"), challenge.scheme());
+  EXPECT_TRUE(challenge.GetNext());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("qop"), challenge.name());
+  EXPECT_EQ(std::string(""), challenge.value());
+  EXPECT_FALSE(challenge.value_is_quoted());
+  EXPECT_TRUE(challenge.GetNext());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("algorithm"), challenge.name());
+  EXPECT_EQ(std::string("md5"), challenge.value());
+  EXPECT_EQ(std::string("md5"), challenge.unquoted_value());
+  EXPECT_FALSE(challenge.value_is_quoted());
+  EXPECT_TRUE(challenge.GetNext());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("realm"), challenge.name());
+  EXPECT_EQ(std::string("foo"), challenge.value());
+  EXPECT_EQ(std::string("foo"), challenge.unquoted_value());
+  EXPECT_FALSE(challenge.value_is_quoted());
+  EXPECT_FALSE(challenge.GetNext());
+}
+
 // Use a name= property which has no value.
 TEST(HttpAuthTest, ChallengeTokenizerNoValue) {
   std::string challenge_str = "Digest qop=";
@@ -230,6 +391,20 @@
   EXPECT_FALSE(challenge.GetNext());
 }
 
+// Use a challenge with Base64 encoded token.
+TEST(HttpAuthTest, ChallengeTokenizerBase64) {
+  std::string challenge_str = "NTLM  SGVsbG8sIFdvcmxkCg===";
+  HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(),
+                                         challenge_str.end());
+  EXPECT_TRUE(challenge.valid());
+  EXPECT_EQ(std::string("NTLM"), challenge.scheme());
+  challenge.set_expect_base64_token(true);
+  EXPECT_TRUE(challenge.GetNext());
+  // Notice the two equal statements below due to padding removal.
+  EXPECT_EQ(std::string("SGVsbG8sIFdvcmxkCg=="), challenge.value());
+  EXPECT_FALSE(challenge.GetNext());
+}
+
 TEST(HttpAuthTest, GetChallengeHeaderName) {
   std::string name;
 
@@ -250,56 +425,4 @@
   EXPECT_STREQ("Proxy-Authorization", name.c_str());
 }
 
-TEST(HttpAuthTest, CreateAuthHandler) {
-  GURL server_origin("http://www.example.com");
-  GURL proxy_origin("http://cache.example.com:3128");
-  {
-    scoped_refptr<HttpAuthHandler> handler;
-    HttpAuth::CreateAuthHandler("Basic realm=\"FooBar\"",
-                                HttpAuth::AUTH_SERVER,
-                                server_origin,
-                                &handler);
-    EXPECT_FALSE(handler.get() == NULL);
-    EXPECT_STREQ("basic", handler->scheme().c_str());
-    EXPECT_STREQ("FooBar", handler->realm().c_str());
-    EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target());
-    EXPECT_FALSE(handler->encrypts_identity());
-    EXPECT_FALSE(handler->is_connection_based());
-  }
-  {
-    scoped_refptr<HttpAuthHandler> handler;
-    HttpAuth::CreateAuthHandler("UNSUPPORTED realm=\"FooBar\"",
-                                HttpAuth::AUTH_SERVER,
-                                server_origin,
-                                &handler);
-    EXPECT_TRUE(handler.get() == NULL);
-  }
-  {
-    scoped_refptr<HttpAuthHandler> handler;
-    HttpAuth::CreateAuthHandler("Digest realm=\"FooBar\", nonce=\"xyz\"",
-                                HttpAuth::AUTH_PROXY,
-                                proxy_origin,
-                                &handler);
-    EXPECT_FALSE(handler.get() == NULL);
-    EXPECT_STREQ("digest", handler->scheme().c_str());
-    EXPECT_STREQ("FooBar", handler->realm().c_str());
-    EXPECT_EQ(HttpAuth::AUTH_PROXY, handler->target());
-    EXPECT_TRUE(handler->encrypts_identity());
-    EXPECT_FALSE(handler->is_connection_based());
-  }
-  {
-    scoped_refptr<HttpAuthHandler> handler;
-    HttpAuth::CreateAuthHandler("NTLM",
-                                HttpAuth::AUTH_SERVER,
-                                server_origin,
-                                &handler);
-    EXPECT_FALSE(handler.get() == NULL);
-    EXPECT_STREQ("ntlm", handler->scheme().c_str());
-    EXPECT_STREQ("", handler->realm().c_str());
-    EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target());
-    EXPECT_TRUE(handler->encrypts_identity());
-    EXPECT_TRUE(handler->is_connection_based());
-  }
-}
-
 }  // namespace net
diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc
index f587ab4..8303357 100644
--- a/net/http/http_basic_stream.cc
+++ b/net/http/http_basic_stream.cc
@@ -6,9 +6,10 @@
 
 namespace net {
 
-HttpBasicStream::HttpBasicStream(ClientSocketHandle* handle, LoadLog* load_log)
+HttpBasicStream::HttpBasicStream(ClientSocketHandle* handle,
+                                 const BoundNetLog& net_log)
     : read_buf_(new GrowableIOBuffer()),
-      parser_(new HttpStreamParser(handle, read_buf_, load_log)) {
+      parser_(new HttpStreamParser(handle, read_buf_, net_log)) {
 }
 
 int HttpBasicStream::SendRequest(const HttpRequestInfo* request,
@@ -20,6 +21,8 @@
       request, headers, request_body, response, callback);
 }
 
+HttpBasicStream::~HttpBasicStream() {}
+
 uint64 HttpBasicStream::GetUploadProgress() const {
   return parser_->GetUploadProgress();
 }
diff --git a/net/http/http_basic_stream.h b/net/http/http_basic_stream.h
index 0a12baf..ff3bcf2 100644
--- a/net/http/http_basic_stream.h
+++ b/net/http/http_basic_stream.h
@@ -18,16 +18,16 @@
 
 namespace net {
 
+class BoundNetLog;
 class ClientSocketHandle;
-class HttpRequestInfo;
+struct HttpRequestInfo;
 class HttpResponseInfo;
-class LoadLog;
 class UploadDataStream;
 
 class HttpBasicStream : public HttpStream {
  public:
-  HttpBasicStream(ClientSocketHandle* handle, LoadLog* load_log);
-  virtual ~HttpBasicStream() {}
+  HttpBasicStream(ClientSocketHandle* handle, const BoundNetLog& net_log);
+  virtual ~HttpBasicStream();
 
   // HttpStream methods:
   virtual int SendRequest(const HttpRequestInfo* request,
diff --git a/net/http/http_byte_range.cc b/net/http/http_byte_range.cc
index 8978d9a..60683c5 100644
--- a/net/http/http_byte_range.cc
+++ b/net/http/http_byte_range.cc
@@ -36,9 +36,9 @@
 bool HttpByteRange::IsValid() const {
   if (suffix_length_ > 0)
     return true;
-  return first_byte_position_ >= 0 &&
-         (last_byte_position_ == kPositionNotSpecified ||
-          last_byte_position_ >= first_byte_position_);
+  return (first_byte_position_ >= 0 &&
+          (last_byte_position_ == kPositionNotSpecified ||
+           last_byte_position_ >= first_byte_position_));
 }
 
 bool HttpByteRange::ComputeBounds(int64 size) {
@@ -52,9 +52,9 @@
   if (!HasFirstBytePosition() &&
       !HasLastBytePosition() &&
       !IsSuffixByteRange()) {
-      first_byte_position_ = 0;
-      last_byte_position_ = size - 1;
-      return true;
+    first_byte_position_ = 0;
+    last_byte_position_ = size - 1;
+    return true;
   }
   if (!IsValid())
     return false;
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index b7989d1..dbd6bf1 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -12,29 +12,34 @@
 #include <unistd.h>
 #endif
 
+#include "base/callback.h"
 #include "base/format_macros.h"
 #include "base/message_loop.h"
 #include "base/pickle.h"
 #include "base/ref_counted.h"
+#include "base/stl_util-inl.h"
 #include "base/string_util.h"
 #include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/disk_cache/disk_cache.h"
-#include "net/flip/flip_session_pool.h"
 #include "net/http/http_cache_transaction.h"
 #include "net/http/http_network_layer.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
+#include "net/http/http_util.h"
+#include "net/spdy/spdy_session_pool.h"
 
 namespace net {
 
-// disk cache entry data indices.
-enum {
-  kResponseInfoIndex,
-  kResponseContentIndex
-};
+int HttpCache::DefaultBackend::CreateBackend(disk_cache::Backend** backend,
+                                             CompletionCallback* callback) {
+  DCHECK_GE(max_bytes_, 0);
+  return disk_cache::CreateCacheBackend(type_, path_, max_bytes_, true,
+                                        thread_, backend, callback);
+}
 
 //-----------------------------------------------------------------------------
 
@@ -46,20 +51,23 @@
 }
 
 HttpCache::ActiveEntry::~ActiveEntry() {
-  if (disk_entry)
+  if (disk_entry) {
     disk_entry->Close();
+    disk_entry = NULL;
+  }
 }
 
 //-----------------------------------------------------------------------------
 
 // This structure keeps track of work items that are attempting to create or
-// open cache entries.
-struct HttpCache::NewEntry {
-  NewEntry() : disk_entry(NULL), writer(NULL) {}
-  ~NewEntry() {}
+// open cache entries or the backend itself.
+struct HttpCache::PendingOp {
+  PendingOp() : disk_entry(NULL), writer(NULL), callback(NULL) {}
+  ~PendingOp() {}
 
   disk_cache::Entry* disk_entry;
   WorkItem* writer;
+  CompletionCallback* callback;  // BackendCallback.
   WorkItemList pending_queue;
 };
 
@@ -67,38 +75,56 @@
 
 // The type of operation represented by a work item.
 enum WorkItemOperation {
+  WI_CREATE_BACKEND,
   WI_OPEN_ENTRY,
   WI_CREATE_ENTRY,
   WI_DOOM_ENTRY
 };
 
-// A work item encapsulates a single request for cache entry with all the
+// A work item encapsulates a single request to the backend with all the
 // information needed to complete that request.
 class HttpCache::WorkItem {
  public:
-  WorkItem(ActiveEntry** entry, CompletionCallback* callback,
-           WorkItemOperation operation)
-      : entry_(entry), callback_(callback), operation_(operation) {}
+  WorkItem(WorkItemOperation operation, Transaction* trans, ActiveEntry** entry)
+      : operation_(operation), trans_(trans), entry_(entry), callback_(NULL),
+        backend_(NULL) {}
+  WorkItem(WorkItemOperation operation, Transaction* trans,
+           CompletionCallback* cb, disk_cache::Backend** backend)
+      : operation_(operation), trans_(trans), entry_(NULL), callback_(cb),
+        backend_(backend) {}
   ~WorkItem() {}
-  WorkItemOperation operation() { return operation_; }
 
   // Calls back the transaction with the result of the operation.
   void NotifyTransaction(int result, ActiveEntry* entry) {
+    // TODO(rvargas): convert to DCHECK after fixing bug 47895.
+    CHECK(!entry || entry->disk_entry);
     if (entry_)
       *entry_ = entry;
+    if (trans_)
+      trans_->io_callback()->Run(result);
+  }
+
+  // Notifies the caller about the operation completion.
+  void DoCallback(int result, disk_cache::Backend* backend) {
+    if (backend_)
+      *backend_ = backend;
     if (callback_)
       callback_->Run(result);
   }
 
-  void ClearCallback() { callback_ = NULL; }
+  WorkItemOperation operation() { return operation_; }
+  void ClearTransaction() { trans_ = NULL; }
   void ClearEntry() { entry_ = NULL; }
-  bool Matches(CompletionCallback* cb) const { return cb == callback_; }
-  bool IsValid() const { return callback_ || entry_; }
+  void ClearCallback() { callback_ = NULL; }
+  bool Matches(Transaction* trans) const { return trans == trans_; }
+  bool IsValid() const { return trans_ || entry_ || callback_; }
 
  private:
-  ActiveEntry** entry_;
-  CompletionCallback* callback_;
   WorkItemOperation operation_;
+  Transaction* trans_;
+  ActiveEntry** entry_;
+  CompletionCallback* callback_;  // User callback.
+  disk_cache::Backend** backend_;
 };
 
 //-----------------------------------------------------------------------------
@@ -107,76 +133,141 @@
 // pass multiple arguments to the completion routine.
 class HttpCache::BackendCallback : public CallbackRunner<Tuple1<int> > {
  public:
-  BackendCallback(HttpCache* cache, NewEntry* entry)
-      : cache_(cache), entry_(entry) {}
+  BackendCallback(HttpCache* cache, PendingOp* pending_op)
+      : cache_(cache), pending_op_(pending_op) {}
   ~BackendCallback() {}
 
   virtual void RunWithParams(const Tuple1<int>& params) {
-    cache_->OnIOComplete(params.a, entry_);
+    if (cache_)
+      cache_->OnIOComplete(params.a, pending_op_);
     delete this;
   }
 
+  void Cancel() {
+    cache_ = NULL;
+  }
+
  private:
   HttpCache* cache_;
-  NewEntry* entry_;
+  PendingOp* pending_op_;
   DISALLOW_COPY_AND_ASSIGN(BackendCallback);
 };
 
 //-----------------------------------------------------------------------------
 
-HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
-                     HostResolver* host_resolver,
-                     ProxyService* proxy_service,
+// This class encapsulates a transaction whose only purpose is to write metadata
+// to a given entry.
+class HttpCache::MetadataWriter {
+ public:
+  explicit MetadataWriter(HttpCache::Transaction* trans)
+      : transaction_(trans),
+        ALLOW_THIS_IN_INITIALIZER_LIST(
+            callback_(this, &MetadataWriter::OnIOComplete)) {}
+  ~MetadataWriter() {}
+
+  // Implementes the bulk of HttpCache::WriteMetadata.
+  void Write(const GURL& url, base::Time expected_response_time, IOBuffer* buf,
+             int buf_len);
+
+ private:
+  void VerifyResponse(int result);
+  void SelfDestroy();
+  void OnIOComplete(int result);
+
+  scoped_ptr<HttpCache::Transaction> transaction_;
+  bool verified_;
+  scoped_refptr<IOBuffer> buf_;
+  int buf_len_;
+  base::Time expected_response_time_;
+  CompletionCallbackImpl<MetadataWriter> callback_;
+  HttpRequestInfo request_info_;
+  DISALLOW_COPY_AND_ASSIGN(MetadataWriter);
+};
+
+void HttpCache::MetadataWriter::Write(const GURL& url,
+                                      base::Time expected_response_time,
+                                      IOBuffer* buf, int buf_len) {
+  DCHECK_GT(buf_len, 0);
+  DCHECK(buf);
+  DCHECK(buf->data());
+  request_info_.url = url;
+  request_info_.method = "GET";
+  request_info_.load_flags = LOAD_ONLY_FROM_CACHE;
+
+  expected_response_time_ = expected_response_time;
+  buf_ = buf;
+  buf_len_ = buf_len;
+  verified_ = false;
+
+  int rv = transaction_->Start(&request_info_, &callback_, BoundNetLog());
+  if (rv != ERR_IO_PENDING)
+    VerifyResponse(rv);
+}
+
+void HttpCache::MetadataWriter::VerifyResponse(int result) {
+  verified_ = true;
+  if (result != OK)
+    return SelfDestroy();
+
+  const HttpResponseInfo* response_info = transaction_->GetResponseInfo();
+  DCHECK(response_info->was_cached);
+  if (response_info->response_time != expected_response_time_)
+    return SelfDestroy();
+
+  result = transaction_->WriteMetadata(buf_, buf_len_, &callback_);
+  if (result != ERR_IO_PENDING)
+    SelfDestroy();
+}
+
+void HttpCache::MetadataWriter::SelfDestroy() {
+  delete this;
+}
+
+void HttpCache::MetadataWriter::OnIOComplete(int result) {
+  if (!verified_)
+    return VerifyResponse(result);
+  SelfDestroy();
+}
+
+//-----------------------------------------------------------------------------
+
+HttpCache::HttpCache(HostResolver* host_resolver, ProxyService* proxy_service,
                      SSLConfigService* ssl_config_service,
-                     const FilePath& cache_dir,
-                     int cache_size)
-    : disk_cache_dir_(cache_dir),
+                     HttpAuthHandlerFactory* http_auth_handler_factory,
+                     HttpNetworkDelegate* network_delegate,
+                     NetLog* net_log,
+                     BackendFactory* backend_factory)
+    : backend_factory_(backend_factory),
+      temp_backend_(NULL),
+      building_backend_(false),
       mode_(NORMAL),
-      type_(DISK_CACHE),
-      network_layer_(HttpNetworkLayer::CreateFactory(
-          network_change_notifier, host_resolver, proxy_service,
-          ssl_config_service)),
+      network_layer_(HttpNetworkLayer::CreateFactory(host_resolver,
+          proxy_service, ssl_config_service, http_auth_handler_factory,
+          network_delegate, net_log)),
       ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
-      enable_range_support_(true),
-      cache_size_(cache_size) {
+      enable_range_support_(true) {
 }
 
 HttpCache::HttpCache(HttpNetworkSession* session,
-                     const FilePath& cache_dir,
-                     int cache_size)
-    : disk_cache_dir_(cache_dir),
+                     BackendFactory* backend_factory)
+    : backend_factory_(backend_factory),
+      temp_backend_(NULL),
+      building_backend_(false),
       mode_(NORMAL),
-      type_(DISK_CACHE),
       network_layer_(HttpNetworkLayer::CreateFactory(session)),
       ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
-      enable_range_support_(true),
-      cache_size_(cache_size) {
-}
-
-HttpCache::HttpCache(NetworkChangeNotifier* network_change_notifier,
-                     HostResolver* host_resolver,
-                     ProxyService* proxy_service,
-                     SSLConfigService* ssl_config_service,
-                     int cache_size)
-    : mode_(NORMAL),
-      type_(MEMORY_CACHE),
-      network_layer_(HttpNetworkLayer::CreateFactory(
-          network_change_notifier, host_resolver, proxy_service,
-          ssl_config_service)),
-      ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
-      enable_range_support_(true),
-      cache_size_(cache_size) {
+      enable_range_support_(true) {
 }
 
 HttpCache::HttpCache(HttpTransactionFactory* network_layer,
-                     disk_cache::Backend* disk_cache)
-    : mode_(NORMAL),
-      type_(DISK_CACHE),
+                     BackendFactory* backend_factory)
+    : backend_factory_(backend_factory),
+      temp_backend_(NULL),
+      building_backend_(false),
+      mode_(NORMAL),
       network_layer_(network_layer),
-      disk_cache_(disk_cache),
       ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
-      enable_range_support_(true),
-      cache_size_(0) {
+      enable_range_support_(true) {
 }
 
 HttpCache::~HttpCache() {
@@ -193,32 +284,50 @@
     DeactivateEntry(entry);
   }
 
-  ActiveEntriesSet::iterator it = doomed_entries_.begin();
-  for (; it != doomed_entries_.end(); ++it)
-    delete *it;
+  STLDeleteElements(&doomed_entries_);
+
+  PendingOpsMap::iterator pending_it = pending_ops_.begin();
+  for (; pending_it != pending_ops_.end(); ++pending_it) {
+    // We are not notifying the transactions about the cache going away, even
+    // though they are waiting for a callback that will never fire.
+    PendingOp* pending_op = pending_it->second;
+    delete pending_op->writer;
+    if (building_backend_) {
+      // If we don't have a backend, when its construction finishes it will
+      // deliver the callbacks.
+      BackendCallback* callback =
+          static_cast<BackendCallback*>(pending_op->callback);
+      callback->Cancel();
+    } else {
+      delete pending_op->callback;
+    }
+
+    STLDeleteElements(&pending_op->pending_queue);
+    delete pending_op;
+  }
 }
 
-disk_cache::Backend* HttpCache::GetBackend() {
-  if (disk_cache_.get())
-    return disk_cache_.get();
+int HttpCache::GetBackend(disk_cache::Backend** backend,
+                          CompletionCallback* callback) {
+  DCHECK(callback != NULL);
 
-  DCHECK_GE(cache_size_, 0);
-  if (type_ == MEMORY_CACHE) {
-    // We may end up with no folder name and no cache if the initialization
-    // of the disk cache fails. We want to be sure that what we wanted to have
-    // was an in-memory cache.
-    disk_cache_.reset(disk_cache::CreateInMemoryCacheBackend(cache_size_));
-  } else if (!disk_cache_dir_.empty()) {
-    disk_cache_.reset(disk_cache::CreateCacheBackend(disk_cache_dir_, true,
-        cache_size_, type_));
-    disk_cache_dir_ = FilePath();  // Reclaim memory.
+  if (disk_cache_.get()) {
+    *backend = disk_cache_.get();
+    return OK;
   }
+
+  return CreateBackend(backend, callback);
+}
+
+disk_cache::Backend* HttpCache::GetCurrentBackend() {
   return disk_cache_.get();
 }
 
 int HttpCache::CreateTransaction(scoped_ptr<HttpTransaction>* trans) {
   // Do lazy initialization of disk cache if needed.
-  GetBackend();
+  if (!disk_cache_.get())
+    CreateBackend(NULL, NULL);  // We don't care about the result.
+
   trans->reset(new HttpCache::Transaction(this, enable_range_support_));
   return OK;
 }
@@ -245,38 +354,83 @@
   return response_info->InitFromPickle(pickle, response_truncated);
 }
 
-// static
-bool HttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry,
-                                 HttpResponseInfo* response_info,
-                                 bool* response_truncated) {
-  int size = disk_entry->GetDataSize(kResponseInfoIndex);
+void HttpCache::WriteMetadata(const GURL& url,
+                              base::Time expected_response_time, IOBuffer* buf,
+                              int buf_len) {
+  if (!buf_len)
+    return;
 
-  scoped_refptr<IOBuffer> buffer = new IOBuffer(size);
-  int rv = disk_entry->ReadData(kResponseInfoIndex, 0, buffer, size, NULL);
-  if (rv != size) {
-    DLOG(ERROR) << "ReadData failed: " << rv;
-    return false;
-  }
+  // Do lazy initialization of disk cache if needed.
+  if (!disk_cache_.get())
+    CreateBackend(NULL, NULL);  // We don't care about the result.
 
-  return ParseResponseInfo(buffer->data(), size, response_info,
-                           response_truncated);
+  HttpCache::Transaction* trans =
+      new HttpCache::Transaction(this, enable_range_support_);
+  MetadataWriter* writer = new MetadataWriter(trans);
+
+  // The writer will self destruct when done.
+  writer->Write(url, expected_response_time, buf, buf_len);
 }
 
-// static
-bool HttpCache::WriteResponseInfo(disk_cache::Entry* disk_entry,
-                                  const HttpResponseInfo* response_info,
-                                  bool skip_transient_headers,
-                                  bool response_truncated) {
-  Pickle pickle;
-  response_info->Persist(
-      &pickle, skip_transient_headers, response_truncated);
+void HttpCache::CloseCurrentConnections() {
+  net::HttpNetworkLayer* network =
+      static_cast<net::HttpNetworkLayer*>(network_layer_.get());
+  HttpNetworkSession* session = network->GetSession();
+  if (session) {
+    session->tcp_socket_pool()->Flush();
+    if (session->spdy_session_pool())
+      session->spdy_session_pool()->CloseAllSessions();
+  }
+}
 
-  scoped_refptr<WrappedIOBuffer> data = new WrappedIOBuffer(
-      reinterpret_cast<const char*>(pickle.data()));
-  int len = static_cast<int>(pickle.size());
+//-----------------------------------------------------------------------------
 
-  return disk_entry->WriteData(kResponseInfoIndex, 0, data, len, NULL,
-                               true) == len;
+int HttpCache::CreateBackend(disk_cache::Backend** backend,
+                             CompletionCallback* callback) {
+  if (!backend_factory_.get())
+    return ERR_FAILED;
+
+  building_backend_ = true;
+
+  scoped_ptr<WorkItem> item(new WorkItem(WI_CREATE_BACKEND, NULL, callback,
+                                         backend));
+
+  // This is the only operation that we can do that is not related to any given
+  // entry, so we use an empty key for it.
+  PendingOp* pending_op = GetPendingOp("");
+  if (pending_op->writer) {
+    if (callback)
+      pending_op->pending_queue.push_back(item.release());
+    return ERR_IO_PENDING;
+  }
+
+  DCHECK(pending_op->pending_queue.empty());
+
+  pending_op->writer = item.release();
+  BackendCallback* my_callback = new BackendCallback(this, pending_op);
+  pending_op->callback = my_callback;
+
+  int rv = backend_factory_->CreateBackend(&temp_backend_, my_callback);
+  if (rv != ERR_IO_PENDING) {
+    pending_op->writer->ClearCallback();
+    my_callback->Run(rv);
+  }
+
+  return rv;
+}
+
+int HttpCache::GetBackendForTransaction(Transaction* trans) {
+  if (disk_cache_.get())
+    return OK;
+
+  if (!building_backend_)
+    return ERR_FAILED;
+
+  WorkItem* item = new WorkItem(WI_CREATE_BACKEND, trans, NULL, NULL);
+  PendingOp* pending_op = GetPendingOp("");
+  DCHECK(pending_op->writer);
+  pending_op->pending_queue.push_back(item);
+  return ERR_IO_PENDING;
 }
 
 // Generate a key that can be used inside the cache.
@@ -318,14 +472,14 @@
   return result;
 }
 
-int HttpCache::DoomEntry(const std::string& key, CompletionCallback* callback) {
+int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
   // Need to abandon the ActiveEntry, but any transaction attached to the entry
   // should not be impacted.  Dooming an entry only means that it will no
   // longer be returned by FindActiveEntry (and it will also be destroyed once
   // all consumers are finished with the entry).
   ActiveEntriesMap::iterator it = active_entries_.find(key);
   if (it == active_entries_.end()) {
-    return AsyncDoomEntry(key, callback);
+    return AsyncDoomEntry(key, trans);
   }
 
   ActiveEntry* entry = it->second;
@@ -342,24 +496,24 @@
   return OK;
 }
 
-int HttpCache::AsyncDoomEntry(const std::string& key,
-                              CompletionCallback* callback) {
-  DCHECK(callback);
-  WorkItem* item = new WorkItem(NULL, callback, WI_DOOM_ENTRY);
-  NewEntry* new_entry = GetNewEntry(key);
-  if (new_entry->writer) {
-    new_entry->pending_queue.push_back(item);
+int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) {
+  DCHECK(trans);
+  WorkItem* item = new WorkItem(WI_DOOM_ENTRY, trans, NULL);
+  PendingOp* pending_op = GetPendingOp(key);
+  if (pending_op->writer) {
+    pending_op->pending_queue.push_back(item);
     return ERR_IO_PENDING;
   }
 
-  DCHECK(new_entry->pending_queue.empty());
+  DCHECK(pending_op->pending_queue.empty());
 
-  new_entry->writer = item;
-  BackendCallback* my_callback = new BackendCallback(this, new_entry);
+  pending_op->writer = item;
+  BackendCallback* my_callback = new BackendCallback(this, pending_op);
+  pending_op->callback = my_callback;
 
   int rv = disk_cache_->DoomEntry(key, my_callback);
   if (rv != ERR_IO_PENDING) {
-    item->ClearCallback();
+    item->ClearTransaction();
     my_callback->Run(rv);
   }
 
@@ -384,102 +538,6 @@
   return it != active_entries_.end() ? it->second : NULL;
 }
 
-HttpCache::NewEntry* HttpCache::GetNewEntry(const std::string& key) {
-  DCHECK(!FindActiveEntry(key));
-
-  NewEntriesMap::const_iterator it = new_entries_.find(key);
-  if (it != new_entries_.end())
-    return it->second;
-
-  NewEntry* entry = new NewEntry();
-  new_entries_[key] = entry;
-  return entry;
-}
-
-void HttpCache::DeleteNewEntry(NewEntry* entry) {
-  std::string key;
-  if (entry->disk_entry)
-    key = entry->disk_entry->GetKey();
-
-  if (!key.empty()) {
-    NewEntriesMap::iterator it = new_entries_.find(key);
-    DCHECK(it != new_entries_.end());
-    new_entries_.erase(it);
-  } else {
-    for (NewEntriesMap::iterator it = new_entries_.begin();
-         it != new_entries_.end(); ++it) {
-      if (it->second == entry) {
-        new_entries_.erase(it);
-        break;
-      }
-    }
-  }
-
-  delete entry;
-}
-
-int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
-                         CompletionCallback* callback) {
-  ActiveEntry* active_entry = FindActiveEntry(key);
-  if (active_entry) {
-    *entry = active_entry;
-    return OK;
-  }
-
-  WorkItem* item = new WorkItem(entry, callback, WI_OPEN_ENTRY);
-  NewEntry* new_entry = GetNewEntry(key);
-  if (new_entry->writer) {
-    new_entry->pending_queue.push_back(item);
-    return ERR_IO_PENDING;
-  }
-
-  DCHECK(new_entry->pending_queue.empty());
-
-  new_entry->writer = item;
-  BackendCallback* my_callback = new BackendCallback(this, new_entry);
-
-  int rv = disk_cache_->OpenEntry(key, &(new_entry->disk_entry), my_callback);
-  if (rv != ERR_IO_PENDING) {
-    item->ClearCallback();
-    my_callback->Run(rv);
-  }
-
-  return rv;
-}
-
-int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
-                           CompletionCallback* callback) {
-  DCHECK(!FindActiveEntry(key));
-
-  WorkItem* item = new WorkItem(entry, callback, WI_CREATE_ENTRY);
-  NewEntry* new_entry = GetNewEntry(key);
-  if (new_entry->writer) {
-    new_entry->pending_queue.push_back(item);
-    return ERR_IO_PENDING;
-  }
-
-  DCHECK(new_entry->pending_queue.empty());
-
-  new_entry->writer = item;
-  BackendCallback* my_callback = new BackendCallback(this, new_entry);
-
-  int rv = disk_cache_->CreateEntry(key, &(new_entry->disk_entry), my_callback);
-  if (rv != ERR_IO_PENDING) {
-    item->ClearCallback();
-    my_callback->Run(rv);
-  }
-
-  return rv;
-}
-
-void HttpCache::DestroyEntry(ActiveEntry* entry) {
-  if (entry->doomed) {
-    FinalizeDoomedEntry(entry);
-  } else {
-    DeactivateEntry(entry);
-  }
-}
-
 HttpCache::ActiveEntry* HttpCache::ActivateEntry(
     const std::string& key,
     disk_cache::Entry* disk_entry) {
@@ -490,19 +548,21 @@
 }
 
 void HttpCache::DeactivateEntry(ActiveEntry* entry) {
-  DCHECK(!entry->will_process_pending_queue);
-  DCHECK(!entry->doomed);
-  DCHECK(!entry->writer);
-  DCHECK(entry->readers.empty());
-  DCHECK(entry->pending_queue.empty());
+  // TODO(rvargas): convert to DCHECKs after fixing bug 47895.
+  CHECK(!entry->will_process_pending_queue);
+  CHECK(!entry->doomed);
+  CHECK(!entry->writer);
+  CHECK(entry->disk_entry);
+  CHECK(entry->readers.empty());
+  CHECK(entry->pending_queue.empty());
 
   std::string key = entry->disk_entry->GetKey();
   if (key.empty())
     return SlowDeactivateEntry(entry);
 
   ActiveEntriesMap::iterator it = active_entries_.find(key);
-  DCHECK(it != active_entries_.end());
-  DCHECK(it->second == entry);
+  CHECK(it != active_entries_.end());
+  CHECK(it->second == entry);
 
   active_entries_.erase(it);
   delete entry;
@@ -520,8 +580,110 @@
   }
 }
 
+HttpCache::PendingOp* HttpCache::GetPendingOp(const std::string& key) {
+  DCHECK(!FindActiveEntry(key));
+
+  PendingOpsMap::const_iterator it = pending_ops_.find(key);
+  if (it != pending_ops_.end())
+    return it->second;
+
+  PendingOp* operation = new PendingOp();
+  pending_ops_[key] = operation;
+  return operation;
+}
+
+void HttpCache::DeletePendingOp(PendingOp* pending_op) {
+  std::string key;
+  if (pending_op->disk_entry)
+    key = pending_op->disk_entry->GetKey();
+
+  if (!key.empty()) {
+    PendingOpsMap::iterator it = pending_ops_.find(key);
+    DCHECK(it != pending_ops_.end());
+    pending_ops_.erase(it);
+  } else {
+    for (PendingOpsMap::iterator it = pending_ops_.begin();
+         it != pending_ops_.end(); ++it) {
+      if (it->second == pending_op) {
+        pending_ops_.erase(it);
+        break;
+      }
+    }
+  }
+  DCHECK(pending_op->pending_queue.empty());
+
+  delete pending_op;
+}
+
+int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry,
+                         Transaction* trans) {
+  ActiveEntry* active_entry = FindActiveEntry(key);
+  if (active_entry) {
+    *entry = active_entry;
+    return OK;
+  }
+
+  WorkItem* item = new WorkItem(WI_OPEN_ENTRY, trans, entry);
+  PendingOp* pending_op = GetPendingOp(key);
+  if (pending_op->writer) {
+    pending_op->pending_queue.push_back(item);
+    return ERR_IO_PENDING;
+  }
+
+  DCHECK(pending_op->pending_queue.empty());
+
+  pending_op->writer = item;
+  BackendCallback* my_callback = new BackendCallback(this, pending_op);
+  pending_op->callback = my_callback;
+
+  int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry), my_callback);
+  if (rv != ERR_IO_PENDING) {
+    item->ClearTransaction();
+    my_callback->Run(rv);
+  }
+
+  return rv;
+}
+
+int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry,
+                           Transaction* trans) {
+  DCHECK(!FindActiveEntry(key));
+
+  WorkItem* item = new WorkItem(WI_CREATE_ENTRY, trans, entry);
+  PendingOp* pending_op = GetPendingOp(key);
+  if (pending_op->writer) {
+    pending_op->pending_queue.push_back(item);
+    return ERR_IO_PENDING;
+  }
+
+  DCHECK(pending_op->pending_queue.empty());
+
+  pending_op->writer = item;
+  BackendCallback* my_callback = new BackendCallback(this, pending_op);
+  pending_op->callback = my_callback;
+
+  int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry),
+                                    my_callback);
+  if (rv != ERR_IO_PENDING) {
+    item->ClearTransaction();
+    my_callback->Run(rv);
+  }
+
+  return rv;
+}
+
+void HttpCache::DestroyEntry(ActiveEntry* entry) {
+  if (entry->doomed) {
+    FinalizeDoomedEntry(entry);
+  } else {
+    DeactivateEntry(entry);
+  }
+}
+
 int HttpCache::AddTransactionToEntry(ActiveEntry* entry, Transaction* trans) {
-  DCHECK(entry);
+  // TODO(rvargas): convert to DCHECKs after fixing bug 47895.
+  CHECK(entry);
+  CHECK(entry->disk_entry);
 
   // We implement a basic reader/writer lock for the disk cache entry.  If
   // there is already a writer, then everyone has to wait for the writer to
@@ -555,7 +717,7 @@
   if (!entry->writer && !entry->pending_queue.empty())
     ProcessPendingQueue(entry);
 
-  return trans->EntryAvailable(entry);
+  return OK;
 }
 
 void HttpCache::DoneWithEntry(ActiveEntry* entry, Transaction* trans,
@@ -602,7 +764,8 @@
     // We need to do something about these pending entries, which now need to
     // be added to a new entry.
     while (!pending_queue.empty()) {
-      pending_queue.front()->AddToEntry();
+      // ERR_CACHE_RACE causes the transaction to restart the whole process.
+      pending_queue.front()->io_callback()->Run(ERR_CACHE_RACE);
       pending_queue.pop_front();
     }
   }
@@ -633,8 +796,20 @@
   ProcessPendingQueue(entry);
 }
 
-void HttpCache::RemovePendingTransaction(Transaction* trans,
-                                         CompletionCallback* cb) {
+LoadState HttpCache::GetLoadStateForPendingTransaction(
+      const Transaction* trans) {
+  ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
+  if (i == active_entries_.end()) {
+    // If this is really a pending transaction, and it is not part of
+    // active_entries_, we should be creating the backend or the entry.
+    return LOAD_STATE_WAITING_FOR_CACHE;
+  }
+
+  Transaction* writer = i->second->writer;
+  return writer ? writer->GetWriterLoadState() : LOAD_STATE_WAITING_FOR_CACHE;
+}
+
+void HttpCache::RemovePendingTransaction(Transaction* trans) {
   ActiveEntriesMap::const_iterator i = active_entries_.find(trans->key());
   bool found = false;
   if (i != active_entries_.end())
@@ -643,9 +818,21 @@
   if (found)
     return;
 
-  NewEntriesMap::const_iterator j = new_entries_.find(trans->key());
-  if (j != new_entries_.end())
-    found = RemovePendingCallbackFromNewEntry(j->second, cb);
+  if (building_backend_) {
+    PendingOpsMap::const_iterator j = pending_ops_.find("");
+    if (j != pending_ops_.end())
+      found = RemovePendingTransactionFromPendingOp(j->second, trans);
+
+    if (found)
+      return;
+  }
+
+  PendingOpsMap::const_iterator j = pending_ops_.find(trans->key());
+  if (j != pending_ops_.end())
+    found = RemovePendingTransactionFromPendingOp(j->second, trans);
+
+  if (found)
+    return;
 
   ActiveEntriesSet::iterator k = doomed_entries_.begin();
   for (; k != doomed_entries_.end() && !found; ++k)
@@ -667,18 +854,18 @@
   return true;
 }
 
-bool HttpCache::RemovePendingCallbackFromNewEntry(NewEntry* entry,
-                                                  CompletionCallback* cb) {
-  if (entry->writer->Matches(cb)) {
-    entry->writer->ClearCallback();
-    entry->writer->ClearEntry();
+bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
+                                                      Transaction* trans) {
+  if (pending_op->writer->Matches(trans)) {
+    pending_op->writer->ClearTransaction();
+    pending_op->writer->ClearEntry();
     return true;
   }
-  WorkItemList& pending_queue = entry->pending_queue;
+  WorkItemList& pending_queue = pending_op->pending_queue;
 
   WorkItemList::iterator it = pending_queue.begin();
   for (; it != pending_queue.end(); ++it) {
-    if ((*it)->Matches(cb)) {
+    if ((*it)->Matches(trans)) {
       delete *it;
       pending_queue.erase(it);
       return true;
@@ -695,14 +882,16 @@
     return;
   entry->will_process_pending_queue = true;
 
-  MessageLoop::current()->PostTask(FROM_HERE,
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
       task_factory_.NewRunnableMethod(&HttpCache::OnProcessPendingQueue,
                                       entry));
 }
 
 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
   entry->will_process_pending_queue = false;
-  DCHECK(!entry->writer);
+  // TODO(rvargas): convert to DCHECK after fixing bug 47895.
+  CHECK(!entry->writer);
 
   // If no one is interested in this entry, then we can de-activate it.
   if (entry->pending_queue.empty()) {
@@ -718,12 +907,20 @@
 
   entry->pending_queue.erase(entry->pending_queue.begin());
 
-  AddTransactionToEntry(entry, next);
+  int rv = AddTransactionToEntry(entry, next);
+  if (rv != ERR_IO_PENDING) {
+    next->io_callback()->Run(rv);
+  }
 }
 
-void HttpCache::OnIOComplete(int result, NewEntry* new_entry) {
-  scoped_ptr<WorkItem> item(new_entry->writer);
-  WorkItemOperation op = item->operation();
+void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
+  WorkItemOperation op = pending_op->writer->operation();
+
+  // Completing the creation of the backend is simpler than the other cases.
+  if (op == WI_CREATE_BACKEND)
+    return OnBackendCreated(result, pending_op);
+
+  scoped_ptr<WorkItem> item(pending_op->writer);
   bool fail_requests = false;
 
   ActiveEntry* entry = NULL;
@@ -733,31 +930,32 @@
       // Anything after a Doom has to be restarted.
       fail_requests = true;
     } else if (item->IsValid()) {
-      key = new_entry->disk_entry->GetKey();
-      entry = ActivateEntry(key, new_entry->disk_entry);
+      key = pending_op->disk_entry->GetKey();
+      entry = ActivateEntry(key, pending_op->disk_entry);
     } else {
       // The writer transaction is gone.
       if (op == WI_CREATE_ENTRY)
-        new_entry->disk_entry->Doom();
-      new_entry->disk_entry->Close();
+        pending_op->disk_entry->Doom();
+      pending_op->disk_entry->Close();
+      pending_op->disk_entry = NULL;
       fail_requests = true;
     }
   }
 
   // We are about to notify a bunch of transactions, and they may decide to
-  // re-issue a request (or send a different one). If we don't delete new_entry,
-  // the new request will be appended to the end of the list, and we'll see it
-  // again from this point before it has a chance to complete (and we'll be
-  // messing out the request order). The down side is that if for some reason
-  // notifying request A ends up cancelling request B (for the same key), we
-  // won't find request B anywhere (because it would be in a local variable
+  // re-issue a request (or send a different one). If we don't delete
+  // pending_op, the new request will be appended to the end of the list, and
+  // we'll see it again from this point before it has a chance to complete (and
+  // we'll be messing out the request order). The down side is that if for some
+  // reason notifying request A ends up cancelling request B (for the same key),
+  // we won't find request B anywhere (because it would be in a local variable
   // here) and that's bad. If there is a chance for that to happen, we'll have
   // to move the callback used to be a CancelableCallback. By the way, for this
   // to happen the action (to cancel B) has to be synchronous to the
   // notification for request A.
   WorkItemList pending_items;
-  pending_items.swap(new_entry->pending_queue);
-  DeleteNewEntry(new_entry);
+  pending_items.swap(pending_op->pending_queue);
+  DeletePendingOp(pending_op);
 
   item->NotifyTransaction(result, entry);
 
@@ -793,7 +991,7 @@
         }
       }
     } else {
-       if (op == WI_CREATE_ENTRY && result != OK) {
+      if (op == WI_CREATE_ENTRY && result != OK) {
         // Failed Create followed by an Open.
         item->NotifyTransaction(ERR_CACHE_RACE, NULL);
         fail_requests = true;
@@ -804,18 +1002,31 @@
   }
 }
 
-void HttpCache::CloseCurrentConnections() {
-  net::HttpNetworkLayer* network =
-      static_cast<net::HttpNetworkLayer*>(network_layer_.get());
-  HttpNetworkSession* session = network->GetSession();
-  if (session) {
-    session->tcp_socket_pool()->CloseIdleSockets();
-    if (session->flip_session_pool())
-      session->flip_session_pool()->CloseAllSessions();
-    session->ReplaceTCPSocketPool();
-  }
-}
+void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) {
+  scoped_ptr<WorkItem> item(pending_op->writer);
+  WorkItemOperation op = item->operation();
+  DCHECK_EQ(WI_CREATE_BACKEND, op);
 
-//-----------------------------------------------------------------------------
+  backend_factory_.reset();  // Reclaim memory.
+
+  if (result == OK)
+    disk_cache_.reset(temp_backend_);
+
+  item->DoCallback(result, temp_backend_);
+
+  // Notify all callers and delete all pending work items.
+  while (!pending_op->pending_queue.empty()) {
+    scoped_ptr<WorkItem> pending_item(pending_op->pending_queue.front());
+    pending_op->pending_queue.pop_front();
+    DCHECK_EQ(WI_CREATE_BACKEND, pending_item->operation());
+
+    // This could be an external caller or a transaction waiting on Start().
+    pending_item->DoCallback(result, temp_backend_);
+    pending_item->NotifyTransaction(result, NULL);
+  }
+
+  DeletePendingOp(pending_op);
+  building_backend_ = false;
+}
 
 }  // namespace net
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index 1985f9b..daa6d28 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -16,17 +16,23 @@
 
 #include <list>
 #include <set>
+#include <string>
 
 #include "base/basictypes.h"
 #include "base/file_path.h"
 #include "base/hash_tables.h"
+#include "base/message_loop_proxy.h"
+#include "base/non_thread_safe.h"
 #include "base/scoped_ptr.h"
 #include "base/task.h"
 #include "base/weak_ptr.h"
 #include "net/base/cache_type.h"
 #include "net/base/completion_callback.h"
+#include "net/base/load_states.h"
 #include "net/http/http_transaction_factory.h"
 
+class GURL;
+
 namespace disk_cache {
 class Backend;
 class Entry;
@@ -35,15 +41,20 @@
 namespace net {
 
 class HostResolver;
+class HttpAuthHandlerFactory;
+class HttpNetworkDelegate;
 class HttpNetworkSession;
-class HttpRequestInfo;
+struct HttpRequestInfo;
 class HttpResponseInfo;
-class NetworkChangeNotifier;
+class IOBuffer;
+class NetLog;
 class ProxyService;
 class SSLConfigService;
+class ViewCacheHelper;
 
 class HttpCache : public HttpTransactionFactory,
-                  public base::SupportsWeakPtr<HttpCache> {
+                  public base::SupportsWeakPtr<HttpCache>,
+                  public NonThreadSafe {
  public:
   ~HttpCache();
 
@@ -61,47 +72,82 @@
     DISABLE
   };
 
-  // Initialize the cache from the directory where its data is stored. The
-  // disk cache is initialized lazily (by CreateTransaction) in this case. If
-  // |cache_size| is zero, a default value will be calculated automatically.
-  HttpCache(NetworkChangeNotifier* network_change_notifier,
-            HostResolver* host_resolver,
+  // A BackendFactory creates a backend object to be used by the HttpCache.
+  class BackendFactory {
+   public:
+    virtual ~BackendFactory() {}
+
+    // The actual method to build the backend. Returns a net error code. If
+    // ERR_IO_PENDING is returned, the |callback| will be notified when the
+    // operation completes, and |backend| must remain valid until the
+    // notification arrives.
+    // The implementation must not access the factory object after invoking the
+    // |callback| because the object can be deleted from within the callback.
+    virtual int CreateBackend(disk_cache::Backend** backend,
+                              CompletionCallback* callback) = 0;
+  };
+
+  // A default backend factory for the common use cases.
+  class DefaultBackend : public BackendFactory {
+   public:
+    // |path| is the destination for any files used by the backend, and
+    // |cache_thread| is the thread where disk operations should take place. If
+    // |max_bytes| is  zero, a default value will be calculated automatically.
+    DefaultBackend(CacheType type, const FilePath& path, int max_bytes,
+                   base::MessageLoopProxy* thread)
+        : type_(type), path_(path), max_bytes_(max_bytes), thread_(thread) {}
+
+    // Returns a factory for an in-memory cache.
+    static BackendFactory* InMemory(int max_bytes)  {
+      return new DefaultBackend(MEMORY_CACHE, FilePath(), max_bytes, NULL);
+    }
+
+    // BackendFactory implementation.
+    virtual int CreateBackend(disk_cache::Backend** backend,
+                              CompletionCallback* callback);
+
+   private:
+    CacheType type_;
+    const FilePath path_;
+    int max_bytes_;
+    scoped_refptr<base::MessageLoopProxy> thread_;
+  };
+
+  // The disk cache is initialized lazily (by CreateTransaction) in this case.
+  // The  HttpCache takes ownership of the |backend_factory|.
+  HttpCache(HostResolver* host_resolver,
             ProxyService* proxy_service,
             SSLConfigService* ssl_config_service,
-            const FilePath& cache_dir,
-            int cache_size);
+            HttpAuthHandlerFactory* http_auth_handler_factory,
+            HttpNetworkDelegate* network_delegate,
+            NetLog* net_log,
+            BackendFactory* backend_factory);
 
-  // Initialize the cache from the directory where its data is stored. The
-  // disk cache is initialized lazily (by CreateTransaction) in  this case. If
-  // |cache_size| is zero, a default value will be calculated automatically.
+  // The disk cache is initialized lazily (by CreateTransaction) in  this case.
   // Provide an existing HttpNetworkSession, the cache can construct a
   // network layer with a shared HttpNetworkSession in order for multiple
-  // network layers to share information (e.g. authenication data).
-  HttpCache(HttpNetworkSession* session, const FilePath& cache_dir,
-            int cache_size);
-
-  // Initialize using an in-memory cache. The cache is initialized lazily
-  // (by CreateTransaction) in this case. If |cache_size| is zero, a default
-  // value will be calculated automatically.
-  HttpCache(NetworkChangeNotifier* network_change_notifier,
-            HostResolver* host_resolver,
-            ProxyService* proxy_service,
-            SSLConfigService* ssl_config_service,
-            int cache_size);
+  // network layers to share information (e.g. authenication data). The
+  // HttpCache takes ownership of the |backend_factory|.
+  HttpCache(HttpNetworkSession* session, BackendFactory* backend_factory);
 
   // Initialize the cache from its component parts, which is useful for
-  // testing.  The lifetime of the network_layer and disk_cache are managed by
-  // the HttpCache and will be destroyed using |delete| when the HttpCache is
+  // testing.  The lifetime of the network_layer and backend_factory are managed
+  // by the HttpCache and will be destroyed using |delete| when the HttpCache is
   // destroyed.
   HttpCache(HttpTransactionFactory* network_layer,
-            disk_cache::Backend* disk_cache);
+            BackendFactory* backend_factory);
 
   HttpTransactionFactory* network_layer() { return network_layer_.get(); }
 
-  // Returns the cache backend for this HttpCache instance. If the backend
-  // is not initialized yet, this method will initialize it. If the return
-  // value is NULL then the backend cannot be initialized.
-  disk_cache::Backend* GetBackend();
+  // Retrieves the cache backend for this HttpCache instance. If the backend
+  // is not initialized yet, this method will initialize it. The return value is
+  // a network error code, and it could be ERR_IO_PENDING, in which case the
+  // |callback| will be notified when the operation completes. The pointer that
+  // receives the |backend| must remain valid until the operation completes.
+  int GetBackend(disk_cache::Backend** backend, CompletionCallback* callback);
+
+  // Returns the current backend (can be NULL).
+  disk_cache::Backend* GetCurrentBackend();
 
   // HttpTransactionFactory implementation:
   virtual int CreateTransaction(scoped_ptr<HttpTransaction>* trans);
@@ -109,33 +155,22 @@
   virtual HttpNetworkSession* GetSession();
   virtual void Suspend(bool suspend);
 
-  // Helper function for reading response info from the disk cache.  If the
-  // cache doesn't have the whole resource *|request_truncated| is set to true.
-  // Avoid this function for performance critical paths as it uses blocking IO.
-  static bool ReadResponseInfo(disk_cache::Entry* disk_entry,
-                               HttpResponseInfo* response_info,
-                               bool* response_truncated);
-
-  // Helper function for writing response info into the disk cache.  If the
-  // cache doesn't have the whole resource |request_truncated| should be true.
-  // Avoid this function for performance critical paths as it uses blocking IO.
-  static bool WriteResponseInfo(disk_cache::Entry* disk_entry,
-                                const HttpResponseInfo* response_info,
-                                bool skip_transient_headers,
-                                bool response_truncated);
-
   // Given a header data blob, convert it to a response info object.
   static bool ParseResponseInfo(const char* data, int len,
                                 HttpResponseInfo* response_info,
                                 bool* response_truncated);
 
+  // Writes |buf_len| bytes of metadata stored in |buf| to the cache entry
+  // referenced by |url|, as long as the entry's |expected_response_time| has
+  // not changed. This method returns without blocking, and the operation will
+  // be performed asynchronously without any completion notification.
+  void WriteMetadata(const GURL& url, base::Time expected_response_time,
+                     IOBuffer* buf, int buf_len);
+
   // Get/Set the cache's mode.
   void set_mode(Mode value) { mode_ = value; }
   Mode mode() { return mode_; }
 
-  void set_type(CacheType type) { type_ = type; }
-  CacheType type() { return type_; }
-
   // Close currently active sockets so that fresh page loads will not use any
   // recycled connections.  For sockets currently in use, they may not close
   // immediately, but they will not be reusable. This is for debugging.
@@ -145,15 +180,27 @@
     enable_range_support_ = value;
   }
 
- private:
+ protected:
+  // Disk cache entry data indices.
+  enum {
+    kResponseInfoIndex = 0,
+    kResponseContentIndex,
+    kMetadataIndex,
 
+    // Must remain at the end of the enum.
+    kNumCacheEntryDataIndices
+  };
+  friend class ViewCacheHelper;
+
+ private:
   // Types --------------------------------------------------------------------
 
   class BackendCallback;
+  class MetadataWriter;
   class Transaction;
   class WorkItem;
   friend class Transaction;
-  struct NewEntry;  // Info for an entry under construction.
+  struct PendingOp;  // Info for an entry under construction.
 
   typedef std::list<Transaction*> TransactionList;
   typedef std::list<WorkItem*> WorkItemList;
@@ -171,24 +218,35 @@
   };
 
   typedef base::hash_map<std::string, ActiveEntry*> ActiveEntriesMap;
-  typedef base::hash_map<std::string, NewEntry*> NewEntriesMap;
+  typedef base::hash_map<std::string, PendingOp*> PendingOpsMap;
   typedef std::set<ActiveEntry*> ActiveEntriesSet;
 
-
   // Methods ------------------------------------------------------------------
 
+  // Creates the |backend| object and notifies the |callback| when the operation
+  // completes. Returns an error code.
+  int CreateBackend(disk_cache::Backend** backend,
+                    CompletionCallback* callback);
+
+  // Makes sure that the backend creation is complete before allowing the
+  // provided transaction to use the object. Returns an error code.  |trans|
+  // will be notified via its IO callback if this method returns ERR_IO_PENDING.
+  // The transaction is free to use the backend directly at any time after
+  // receiving the notification.
+  int GetBackendForTransaction(Transaction* trans);
+
   // Generates the cache key for this request.
   std::string GenerateCacheKey(const HttpRequestInfo*);
 
-  // Dooms the entry selected by |key|. |callback| is used for completion
-  // notification if this function returns ERR_IO_PENDING. The entry can be
+  // Dooms the entry selected by |key|. |trans| will be notified via its IO
+  // callback if this method returns ERR_IO_PENDING. The entry can be
   // currently in use or not.
-  int DoomEntry(const std::string& key, CompletionCallback* callback);
+  int DoomEntry(const std::string& key, Transaction* trans);
 
-  // Dooms the entry selected by |key|. |callback| is used for completion
-  // notification if this function returns ERR_IO_PENDING. The entry should not
+  // Dooms the entry selected by |key|. |trans| will be notified via its IO
+  // callback if this method returns ERR_IO_PENDING. The entry should not
   // be currently in use.
-  int AsyncDoomEntry(const std::string& key, CompletionCallback* callback);
+  int AsyncDoomEntry(const std::string& key, Transaction* trans);
 
   // Closes a previously doomed entry.
   void FinalizeDoomedEntry(ActiveEntry* entry);
@@ -208,29 +266,33 @@
   // Deletes an ActiveEntry using an exhaustive search.
   void SlowDeactivateEntry(ActiveEntry* entry);
 
-  // Returns the NewEntry for the desired |key|. If an entry is not under
-  // construction already, a new NewEntry structure is created.
-  NewEntry* GetNewEntry(const std::string& key);
+  // Returns the PendingOp for the desired |key|. If an entry is not under
+  // construction already, a new PendingOp structure is created.
+  PendingOp* GetPendingOp(const std::string& key);
 
-  // Deletes a NewEntry.
-  void DeleteNewEntry(NewEntry* entry);
+  // Deletes a PendingOp.
+  void DeletePendingOp(PendingOp* pending_op);
 
   // Opens the disk cache entry associated with |key|, returning an ActiveEntry
-  // in |*entry|. |callback| is used for completion notification if this
-  // function returns ERR_IO_PENDING.
+  // in |*entry|. |trans| will be notified via its IO callback if this method
+  // returns ERR_IO_PENDING.
   int OpenEntry(const std::string& key, ActiveEntry** entry,
-                CompletionCallback* callback);
+                Transaction* trans);
 
   // Creates the disk cache entry associated with |key|, returning an
-  // ActiveEntry in |*entry|. |callback| is used for completion notification if
-  // this function returns ERR_IO_PENDING.
+  // ActiveEntry in |*entry|. |trans| will be notified via its IO callback if
+  // this method returns ERR_IO_PENDING.
   int CreateEntry(const std::string& key, ActiveEntry** entry,
-                  CompletionCallback* callback);
+                  Transaction* trans);
 
   // Destroys an ActiveEntry (active or doomed).
   void DestroyEntry(ActiveEntry* entry);
 
-  // Adds a transaction to an ActiveEntry.
+  // Adds a transaction to an ActiveEntry. If this method returns ERR_IO_PENDING
+  // the transaction will be notified about completion via its IO callback. This
+  // method returns ERR_CACHE_RACE to signal the transaction that it cannot be
+  // added to the provided entry, and it should retry the process with another
+  // one (in this case, the entry is no longer valid).
   int AddTransactionToEntry(ActiveEntry* entry, Transaction* trans);
 
   // Called when the transaction has finished working with this entry. |cancel|
@@ -249,17 +311,20 @@
   // transactions can start reading from this entry.
   void ConvertWriterToReader(ActiveEntry* entry);
 
-  // Removes the transaction |trans|, waiting for |callback|, from the pending
-  // list of an entry (NewEntry, active or doomed entry).
-  void RemovePendingTransaction(Transaction* trans, CompletionCallback* cb);
+  // Returns the LoadState of the provided pending transaction.
+  LoadState GetLoadStateForPendingTransaction(const Transaction* trans);
+
+  // Removes the transaction |trans|, from the pending list of an entry
+  // (PendingOp, active or doomed entry).
+  void RemovePendingTransaction(Transaction* trans);
 
   // Removes the transaction |trans|, from the pending list of |entry|.
   bool RemovePendingTransactionFromEntry(ActiveEntry* entry,
                                          Transaction* trans);
 
-  // Removes the callback |cb|, from the pending list of |entry|.
-  bool RemovePendingCallbackFromNewEntry(NewEntry* entry,
-                                         CompletionCallback* cb);
+  // Removes the transaction |trans|, from the pending list of |pending_op|.
+  bool RemovePendingTransactionFromPendingOp(PendingOp* pending_op,
+                                             Transaction* trans);
 
   // Resumes processing the pending list of |entry|.
   void ProcessPendingQueue(ActiveEntry* entry);
@@ -271,16 +336,20 @@
   // Callbacks ----------------------------------------------------------------
 
   // Processes BackendCallback notifications.
-  void OnIOComplete(int result, NewEntry* entry);
+  void OnIOComplete(int result, PendingOp* entry);
+
+  // Processes the backend creation notification.
+  void OnBackendCreated(int result, PendingOp* pending_op);
 
 
   // Variables ----------------------------------------------------------------
 
   // Used when lazily constructing the disk_cache_.
-  FilePath disk_cache_dir_;
+  scoped_ptr<BackendFactory> backend_factory_;
+  disk_cache::Backend* temp_backend_;
+  bool building_backend_;
 
   Mode mode_;
-  CacheType type_;
 
   scoped_ptr<HttpTransactionFactory> network_layer_;
   scoped_ptr<disk_cache::Backend> disk_cache_;
@@ -292,12 +361,11 @@
   ActiveEntriesSet doomed_entries_;
 
   // The set of entries "under construction".
-  NewEntriesMap new_entries_;
+  PendingOpsMap pending_ops_;
 
   ScopedRunnableMethodFactory<HttpCache> task_factory_;
 
   bool enable_range_support_;
-  int cache_size_;
 
   typedef base::hash_map<std::string, int> PlaybackCacheMap;
   scoped_ptr<PlaybackCacheMap> playback_cache_map_;
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index 3be313f..2ea08b8 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -16,8 +16,8 @@
 #include "base/time.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
-#include "net/base/load_log.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
 #include "net/base/ssl_cert_request_info.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/http_request_info.h"
@@ -30,14 +30,6 @@
 
 namespace net {
 
-// disk cache entry data indices.
-enum {
-  kResponseInfoIndex,
-  kResponseContentIndex
-};
-
-//-----------------------------------------------------------------------------
-
 struct HeaderNameAndValue {
   const char* name;
   const char* value;
@@ -77,16 +69,17 @@
   { NULL, NULL }
 };
 
-static bool HeaderMatches(const HttpUtil::HeadersIterator& h,
+static bool HeaderMatches(const HttpRequestHeaders& headers,
                           const HeaderNameAndValue* search) {
   for (; search->name; ++search) {
-    if (!LowerCaseEqualsASCII(h.name_begin(), h.name_end(), search->name))
+    std::string header_value;
+    if (!headers.GetHeader(search->name, &header_value))
       continue;
 
     if (!search->value)
       return true;
 
-    HttpUtil::ValuesIterator v(h.values_begin(), h.values_end(), ',');
+    HttpUtil::ValuesIterator v(header_value.begin(), header_value.end(), ',');
     while (v.GetNext()) {
       if (LowerCaseEqualsASCII(v.value_begin(), v.value_end(), search->value))
         return true;
@@ -116,28 +109,26 @@
       cache_pending_(false),
       read_offset_(0),
       effective_load_flags_(0),
+      write_len_(0),
       final_upload_progress_(0),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           io_callback_(this, &Transaction::OnIOComplete)),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           cache_callback_(new CancelableCompletionCallback<Transaction>(
-                this, &Transaction::OnIOComplete))),
+              this, &Transaction::OnIOComplete))),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           write_headers_callback_(new CancelableCompletionCallback<Transaction>(
-                this, &Transaction::OnIOComplete))) {
+              this, &Transaction::OnIOComplete))) {
   COMPILE_ASSERT(HttpCache::Transaction::kNumValidationHeaders ==
-                     ARRAYSIZE_UNSAFE(kValidationHeaders),
+                 arraysize(kValidationHeaders),
                  Invalid_number_of_validation_headers);
 }
 
-#if defined(OS_WIN)
-#pragma optimize("", off)
-#pragma warning(disable:4748)
-#endif
 HttpCache::Transaction::~Transaction() {
-  // TODO(rvargas): remove this after finding the cause for bug 31723.
-  char local_obj[sizeof(*this)];
-  memcpy(local_obj, this, sizeof(local_obj));
+  // We may have to issue another IO, but we should never invoke the callback_
+  // after this point.
+  callback_ = NULL;
+
   if (cache_) {
     if (entry_) {
       bool cancel_request = reading_ && enable_range_support_;
@@ -151,7 +142,7 @@
 
       cache_->DoneWithEntry(entry_, this, cancel_request);
     } else if (cache_pending_) {
-      cache_->RemovePendingTransaction(this, &io_callback_);
+      cache_->RemovePendingTransaction(this);
     }
   }
 
@@ -164,14 +155,10 @@
   // cache_ pointer to signal that we are dead.  See DoCacheReadCompleted.
   cache_.reset();
 }
-#if defined(OS_WIN)
-#pragma warning(default:4748)
-#pragma optimize("", on)
-#endif
 
 int HttpCache::Transaction::Start(const HttpRequestInfo* request,
                                   CompletionCallback* callback,
-                                  LoadLog* load_log) {
+                                  const BoundNetLog& net_log) {
   DCHECK(request);
   DCHECK(callback);
 
@@ -184,48 +171,13 @@
   if (!cache_)
     return ERR_UNEXPECTED;
 
-  SetRequest(load_log, request);
+  SetRequest(net_log, request);
 
-  int rv;
+  // We have to wait until the backend is initialized so we start the SM.
+  next_state_ = STATE_GET_BACKEND;
+  int rv = DoLoop(OK);
 
-  if (!ShouldPassThrough()) {
-    cache_key_ = cache_->GenerateCacheKey(request);
-
-    // Requested cache access mode.
-    if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
-      mode_ = READ;
-    } else if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
-      mode_ = WRITE;
-    } else {
-      mode_ = READ_WRITE;
-    }
-
-    // Downgrade to UPDATE if the request has been externally conditionalized.
-    if (external_validation_.initialized) {
-      if (mode_ & WRITE) {
-        // Strip off the READ_DATA bit (and maybe add back a READ_META bit
-        // in case READ was off).
-        mode_ = UPDATE;
-      } else {
-        mode_ = NONE;
-      }
-    }
-  }
-
-  // If must use cache, then we must fail.  This can happen for back/forward
-  // navigations to a page generated via a form post.
-  if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE)
-    return ERR_CACHE_MISS;
-
-  if (mode_ == NONE) {
-    if (partial_.get())
-      partial_->RestoreHeaders(&custom_request_->extra_headers);
-    rv = BeginNetworkRequest();
-  } else {
-    rv = AddToEntry();
-  }
-
-  // Setting this here allows us to check for the existance of a callback_ to
+  // Setting this here allows us to check for the existence of a callback_ to
   // determine if we are still inside Start.
   if (rv == ERR_IO_PENDING)
     callback_ = callback;
@@ -351,6 +303,9 @@
   return rv;
 }
 
+void HttpCache::Transaction::StopCaching() {
+}
+
 const HttpResponseInfo* HttpCache::Transaction::GetResponseInfo() const {
   // Null headers means we encountered an error or haven't a response yet
   if (auth_response_.headers)
@@ -360,11 +315,14 @@
 }
 
 LoadState HttpCache::Transaction::GetLoadState() const {
-  if (network_trans_.get())
-    return network_trans_->GetLoadState();
-  if (entry_ || !request_)
-    return LOAD_STATE_IDLE;
-  return LOAD_STATE_WAITING_FOR_CACHE;
+  LoadState state = GetWriterLoadState();
+  if (state != LOAD_STATE_WAITING_FOR_CACHE)
+    return state;
+
+  if (cache_)
+    return cache_->GetLoadStateForPendingTransaction(this);
+
+  return LOAD_STATE_IDLE;
 }
 
 uint64 HttpCache::Transaction::GetUploadProgress() const {
@@ -373,197 +331,20 @@
   return final_upload_progress_;
 }
 
-int HttpCache::Transaction::AddToEntry() {
-  next_state_ = STATE_INIT_ENTRY;
-  cache_pending_ = false;
-  return DoLoop(OK);
-}
-
-int HttpCache::Transaction::DoInitEntry() {
-  DCHECK(!new_entry_);
-
-  if (!cache_)
+int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len,
+                                          CompletionCallback* callback) {
+  DCHECK(buf);
+  DCHECK_GT(buf_len, 0);
+  DCHECK(callback);
+  if (!cache_ || !entry_)
     return ERR_UNEXPECTED;
 
-  if (mode_ == WRITE) {
-    next_state_ = STATE_DOOM_ENTRY;
-    return OK;
-  }
-
-  next_state_ = STATE_OPEN_ENTRY;
-  return OK;
-}
-
-int HttpCache::Transaction::DoDoomEntry() {
-  next_state_ = STATE_DOOM_ENTRY_COMPLETE;
-  cache_pending_ = true;
-  // TODO(rvargas): Add a LoadLog event.
-  return cache_->DoomEntry(cache_key_, &io_callback_);
-}
-
-int HttpCache::Transaction::DoDoomEntryComplete(int result) {
-  next_state_ = STATE_CREATE_ENTRY;
-  cache_pending_ = false;
-  if (result == ERR_CACHE_RACE)
-    next_state_ = STATE_INIT_ENTRY;
-
-  return OK;
-}
-
-int HttpCache::Transaction::DoOpenEntry() {
-  DCHECK(!new_entry_);
-  next_state_ = STATE_OPEN_ENTRY_COMPLETE;
-  cache_pending_ = true;
-  LoadLog::BeginEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_OPEN_ENTRY);
-  return cache_->OpenEntry(cache_key_, &new_entry_, &io_callback_);
-}
-
-int HttpCache::Transaction::DoOpenEntryComplete(int result) {
-  // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
-  // OK, otherwise the cache will end up with an active entry without any
-  // transaction attached.
-  LoadLog::EndEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_OPEN_ENTRY);
-  cache_pending_ = false;
-  if (result == OK) {
-    next_state_ = STATE_ADD_TO_ENTRY;
-    return OK;
-  }
-
-  if (result == ERR_CACHE_RACE) {
-    next_state_ = STATE_INIT_ENTRY;
-    return OK;
-  }
-
-  if (mode_ == READ_WRITE) {
-    mode_ = WRITE;
-    next_state_ = STATE_CREATE_ENTRY;
-    return OK;
-  }
-  if (mode_ == UPDATE) {
-    // There is no cache entry to update; proceed without caching.
-    mode_ = NONE;
-    next_state_ = STATE_SEND_REQUEST;
-    return OK;
-  }
-  if (cache_->mode() == PLAYBACK)
-    DLOG(INFO) << "Playback Cache Miss: " << request_->url;
-
-  // The entry does not exist, and we are not permitted to create a new entry,
-  // so we must fail.
-  return ERR_CACHE_MISS;
-}
-
-int HttpCache::Transaction::DoCreateEntry() {
-  DCHECK(!new_entry_);
-  next_state_ = STATE_CREATE_ENTRY_COMPLETE;
-  cache_pending_ = true;
-  LoadLog::BeginEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_CREATE_ENTRY);
-  return cache_->CreateEntry(cache_key_, &new_entry_, &io_callback_);
-}
-
-int HttpCache::Transaction::DoCreateEntryComplete(int result) {
-  // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
-  // OK, otherwise the cache will end up with an active entry without any
-  // transaction attached.
-  LoadLog::EndEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_CREATE_ENTRY);
-  cache_pending_ = false;
-  next_state_ = STATE_ADD_TO_ENTRY;
-
-  if (result == ERR_CACHE_RACE) {
-    next_state_ = STATE_INIT_ENTRY;
-    return OK;
-  }
-
-  if (result != OK) {
-    // We have a race here: Maybe we failed to open the entry and decided to
-    // create one, but by the time we called create, another transaction already
-    // created the entry. If we want to eliminate this issue, we need an atomic
-    // OpenOrCreate() method exposed by the disk cache.
-    DLOG(WARNING) << "Unable to create cache entry";
-    mode_ = NONE;
-    if (partial_.get())
-      partial_->RestoreHeaders(&custom_request_->extra_headers);
-    next_state_ = STATE_SEND_REQUEST;
-  }
-  return OK;
-}
-
-int HttpCache::Transaction::DoAddToEntry() {
-  DCHECK(new_entry_);
-  cache_pending_ = true;
-  LoadLog::BeginEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_WAITING);
-  int rv = cache_->AddTransactionToEntry(new_entry_, this);
-  new_entry_ = NULL;
-  return rv;
-}
-
-int HttpCache::Transaction::EntryAvailable(ActiveEntry* entry) {
-  LoadLog::EndEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_WAITING);
-  cache_pending_ = false;
-  entry_ = entry;
-
-  next_state_ = STATE_ENTRY_AVAILABLE;
-  if (new_entry_) {
-    // We are inside AddTransactionToEntry() so avoid reentering DoLoop().
-    DCHECK_EQ(new_entry_, entry);
-    return OK;
-  }
-  return DoLoop(OK);
-}
-
-int HttpCache::Transaction::DoEntryAvailable() {
-  DCHECK(!new_entry_);
-  if (mode_ == WRITE) {
-    if (partial_.get())
-      partial_->RestoreHeaders(&custom_request_->extra_headers);
-    next_state_ = STATE_SEND_REQUEST;
-  } else {
-    // We have to read the headers from the cached entry.
-    DCHECK(mode_ & READ_META);
-    next_state_ = STATE_CACHE_READ_RESPONSE;
-  }
-  return OK;
-}
-
-int HttpCache::Transaction::DoCacheReadResponseComplete(int result) {
-  cache_callback_->Release();  // Balance the AddRef from DoCacheReadResponse.
-  LoadLog::EndEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_READ_INFO);
-  if (result != io_buf_len_ ||
-      !HttpCache::ParseResponseInfo(read_buf_->data(), io_buf_len_,
-                                    &response_, &truncated_)) {
-    DLOG(ERROR) << "ReadData failed: " << result;
-    return ERR_CACHE_READ_FAILURE;
-  }
-
-  // We now have access to the cache entry.
-  //
-  //  o if we are a reader for the transaction, then we can start reading the
-  //    cache entry.
-  //
-  //  o if we can read or write, then we should check if the cache entry needs
-  //    to be validated and then issue a network request if needed or just read
-  //    from the cache if the cache entry is already valid.
-  //
-  //  o if we are set to UPDATE, then we are handling an externally
-  //    conditionalized request (if-modified-since / if-none-match). We check
-  //    if the request headers define a validation request.
-  //
-  switch (mode_) {
-    case READ:
-      result = BeginCacheRead();
-      break;
-    case READ_WRITE:
-      result = BeginPartialCacheValidation();
-      break;
-    case UPDATE:
-      result = BeginExternallyConditionalizedRequest();
-      break;
-    case WRITE:
-    default:
-      NOTREACHED();
-      result = ERR_FAILED;
-  }
-  return result;
+  // We don't need to track this operation for anything.
+  // It could be possible to check if there is something already written and
+  // avoid writing again (it should be the same, right?), but let's allow the
+  // caller to "update" the contents with something new.
+  return entry_->disk_entry->WriteData(kMetadataIndex, 0, buf, buf_len,
+                                       callback, true);
 }
 
 bool HttpCache::Transaction::AddTruncatedFlag() {
@@ -589,6 +370,16 @@
   return true;
 }
 
+LoadState HttpCache::Transaction::GetWriterLoadState() const {
+  if (network_trans_.get())
+    return network_trans_->GetLoadState();
+  if (entry_ || !request_)
+    return LOAD_STATE_IDLE;
+  return LOAD_STATE_WAITING_FOR_CACHE;
+}
+
+//-----------------------------------------------------------------------------
+
 void HttpCache::Transaction::DoCallback(int rv) {
   DCHECK(rv != ERR_IO_PENDING);
   DCHECK(callback_);
@@ -614,6 +405,13 @@
     State state = next_state_;
     next_state_ = STATE_NONE;
     switch (state) {
+      case STATE_GET_BACKEND:
+        DCHECK_EQ(OK, rv);
+        rv = DoGetBackend();
+        break;
+      case STATE_GET_BACKEND_COMPLETE:
+        rv = DoGetBackendComplete(rv);
+        break;
       case STATE_SEND_REQUEST:
         DCHECK_EQ(OK, rv);
         rv = DoSendRequest();
@@ -661,13 +459,15 @@
         DCHECK_EQ(OK, rv);
         rv = DoAddToEntry();
         break;
-      case STATE_ENTRY_AVAILABLE:
-        DCHECK_EQ(OK, rv);
-        rv = DoEntryAvailable();
+      case STATE_ADD_TO_ENTRY_COMPLETE:
+        rv = DoAddToEntryComplete(rv);
         break;
-      case STATE_PARTIAL_CACHE_VALIDATION:
+      case STATE_START_PARTIAL_CACHE_VALIDATION:
         DCHECK_EQ(OK, rv);
-        rv = DoPartialCacheValidation();
+        rv = DoStartPartialCacheValidation();
+        break;
+      case STATE_COMPLETE_PARTIAL_CACHE_VALIDATION:
+        rv = DoCompletePartialCacheValidation(rv);
         break;
       case STATE_UPDATE_CACHED_RESPONSE:
         DCHECK_EQ(OK, rv);
@@ -687,6 +487,13 @@
       case STATE_TRUNCATE_CACHED_DATA_COMPLETE:
         rv = DoTruncateCachedDataComplete(rv);
         break;
+      case STATE_TRUNCATE_CACHED_METADATA:
+        DCHECK_EQ(OK, rv);
+        rv = DoTruncateCachedMetadata();
+        break;
+      case STATE_TRUNCATE_CACHED_METADATA_COMPLETE:
+        rv = DoTruncateCachedMetadataComplete(rv);
+        break;
       case STATE_PARTIAL_HEADERS_RECEIVED:
         DCHECK_EQ(OK, rv);
         rv = DoPartialHeadersReceived();
@@ -709,6 +516,13 @@
       case STATE_CACHE_WRITE_RESPONSE_COMPLETE:
         rv = DoCacheWriteResponseComplete(rv);
         break;
+      case STATE_CACHE_READ_METADATA:
+        DCHECK_EQ(OK, rv);
+        rv = DoCacheReadMetadata();
+        break;
+      case STATE_CACHE_READ_METADATA_COMPLETE:
+        rv = DoCacheReadMetadataComplete(rv);
+        break;
       case STATE_CACHE_QUERY_DATA:
         DCHECK_EQ(OK, rv);
         rv = DoCacheQueryData();
@@ -742,9 +556,664 @@
   return rv;
 }
 
-void HttpCache::Transaction::SetRequest(LoadLog* load_log,
+int HttpCache::Transaction::DoGetBackend() {
+  cache_pending_ = true;
+  next_state_ = STATE_GET_BACKEND_COMPLETE;
+  net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WAITING, NULL);
+  return cache_->GetBackendForTransaction(this);
+}
+
+int HttpCache::Transaction::DoGetBackendComplete(int result) {
+  DCHECK(result == OK || result == ERR_FAILED);
+  net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_WAITING, NULL);
+  cache_pending_ = false;
+
+  if (!ShouldPassThrough()) {
+    cache_key_ = cache_->GenerateCacheKey(request_);
+
+    // Requested cache access mode.
+    if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) {
+      mode_ = READ;
+    } else if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
+      mode_ = WRITE;
+    } else {
+      mode_ = READ_WRITE;
+    }
+
+    // Downgrade to UPDATE if the request has been externally conditionalized.
+    if (external_validation_.initialized) {
+      if (mode_ & WRITE) {
+        // Strip off the READ_DATA bit (and maybe add back a READ_META bit
+        // in case READ was off).
+        mode_ = UPDATE;
+      } else {
+        mode_ = NONE;
+      }
+    }
+  }
+
+  // If must use cache, then we must fail.  This can happen for back/forward
+  // navigations to a page generated via a form post.
+  if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE)
+    return ERR_CACHE_MISS;
+
+  if (mode_ == NONE) {
+    if (partial_.get())
+      partial_->RestoreHeaders(&custom_request_->extra_headers);
+    next_state_ = STATE_SEND_REQUEST;
+  } else {
+    next_state_ = STATE_INIT_ENTRY;
+  }
+
+  return OK;
+}
+
+int HttpCache::Transaction::DoSendRequest() {
+  DCHECK(mode_ & WRITE || mode_ == NONE);
+  DCHECK(!network_trans_.get());
+
+  // Create a network transaction.
+  int rv = cache_->network_layer_->CreateTransaction(&network_trans_);
+  if (rv != OK)
+    return rv;
+
+  next_state_ = STATE_SEND_REQUEST_COMPLETE;
+  rv = network_trans_->Start(request_, &io_callback_, net_log_);
+  return rv;
+}
+
+int HttpCache::Transaction::DoSendRequestComplete(int result) {
+  if (!cache_)
+    return ERR_UNEXPECTED;
+
+  if (result == OK) {
+    next_state_ = STATE_SUCCESSFUL_SEND_REQUEST;
+    return OK;
+  }
+
+  if (IsCertificateError(result)) {
+    const HttpResponseInfo* response = network_trans_->GetResponseInfo();
+    // If we get a certificate error, then there is a certificate in ssl_info,
+    // so GetResponseInfo() should never returns NULL here.
+    DCHECK(response);
+    response_.ssl_info = response->ssl_info;
+  } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
+    const HttpResponseInfo* response = network_trans_->GetResponseInfo();
+    DCHECK(response);
+    response_.cert_request_info = response->cert_request_info;
+  }
+  return result;
+}
+
+// We received the response headers and there is no error.
+int HttpCache::Transaction::DoSuccessfulSendRequest() {
+  DCHECK(!new_response_);
+  const HttpResponseInfo* new_response = network_trans_->GetResponseInfo();
+  if (new_response->headers->response_code() == 401 ||
+      new_response->headers->response_code() == 407) {
+    auth_response_ = *new_response;
+    return OK;
+  }
+
+  if (!ValidatePartialResponse(new_response->headers, &server_responded_206_) &&
+      !auth_response_.headers) {
+    // Something went wrong with this request and we have to restart it.
+    // If we have an authentication response, we are exposed to weird things
+    // hapenning if the user cancels the authentication before we receive
+    // the new response.
+    response_ = HttpResponseInfo();
+    network_trans_.reset();
+    next_state_ = STATE_SEND_REQUEST;
+    return OK;
+  }
+  if (server_responded_206_ && mode_ == READ_WRITE && !truncated_ &&
+      response_.headers->response_code() == 200) {
+    // We have stored the full entry, but it changed and the server is
+    // sending a range. We have to delete the old entry.
+    DoneWritingToEntry(false);
+  }
+
+  HistogramHeaders(new_response->headers);
+
+  new_response_ = new_response;
+  // Are we expecting a response to a conditional query?
+  if (mode_ == READ_WRITE || mode_ == UPDATE) {
+    if (new_response->headers->response_code() == 304 ||
+        server_responded_206_) {
+      next_state_ = STATE_UPDATE_CACHED_RESPONSE;
+      return OK;
+    }
+    mode_ = WRITE;
+  }
+
+  next_state_ = STATE_OVERWRITE_CACHED_RESPONSE;
+  return OK;
+}
+
+int HttpCache::Transaction::DoNetworkRead() {
+  next_state_ = STATE_NETWORK_READ_COMPLETE;
+  return network_trans_->Read(read_buf_, io_buf_len_, &io_callback_);
+}
+
+int HttpCache::Transaction::DoNetworkReadComplete(int result) {
+  DCHECK(mode_ & WRITE || mode_ == NONE);
+
+  if (!cache_)
+    return ERR_UNEXPECTED;
+
+  next_state_ = STATE_CACHE_WRITE_DATA;
+  return result;
+}
+
+int HttpCache::Transaction::DoInitEntry() {
+  DCHECK(!new_entry_);
+
+  if (!cache_)
+    return ERR_UNEXPECTED;
+
+  if (mode_ == WRITE) {
+    next_state_ = STATE_DOOM_ENTRY;
+    return OK;
+  }
+
+  next_state_ = STATE_OPEN_ENTRY;
+  return OK;
+}
+
+int HttpCache::Transaction::DoOpenEntry() {
+  DCHECK(!new_entry_);
+  next_state_ = STATE_OPEN_ENTRY_COMPLETE;
+  cache_pending_ = true;
+  net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY, NULL);
+  return cache_->OpenEntry(cache_key_, &new_entry_, this);
+}
+
+int HttpCache::Transaction::DoOpenEntryComplete(int result) {
+  // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
+  // OK, otherwise the cache will end up with an active entry without any
+  // transaction attached.
+  net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY, NULL);
+  cache_pending_ = false;
+  if (result == OK) {
+    next_state_ = STATE_ADD_TO_ENTRY;
+    return OK;
+  }
+
+  if (result == ERR_CACHE_RACE) {
+    next_state_ = STATE_INIT_ENTRY;
+    return OK;
+  }
+
+  if (mode_ == READ_WRITE) {
+    mode_ = WRITE;
+    next_state_ = STATE_CREATE_ENTRY;
+    return OK;
+  }
+  if (mode_ == UPDATE) {
+    // There is no cache entry to update; proceed without caching.
+    mode_ = NONE;
+    next_state_ = STATE_SEND_REQUEST;
+    return OK;
+  }
+  if (cache_->mode() == PLAYBACK)
+    DLOG(INFO) << "Playback Cache Miss: " << request_->url;
+
+  // The entry does not exist, and we are not permitted to create a new entry,
+  // so we must fail.
+  return ERR_CACHE_MISS;
+}
+
+int HttpCache::Transaction::DoCreateEntry() {
+  DCHECK(!new_entry_);
+  next_state_ = STATE_CREATE_ENTRY_COMPLETE;
+  cache_pending_ = true;
+  net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY, NULL);
+  return cache_->CreateEntry(cache_key_, &new_entry_, this);
+}
+
+int HttpCache::Transaction::DoCreateEntryComplete(int result) {
+  // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
+  // OK, otherwise the cache will end up with an active entry without any
+  // transaction attached.
+  net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY, NULL);
+  cache_pending_ = false;
+  next_state_ = STATE_ADD_TO_ENTRY;
+
+  if (result == ERR_CACHE_RACE) {
+    next_state_ = STATE_INIT_ENTRY;
+    return OK;
+  }
+
+  if (result != OK) {
+    // We have a race here: Maybe we failed to open the entry and decided to
+    // create one, but by the time we called create, another transaction already
+    // created the entry. If we want to eliminate this issue, we need an atomic
+    // OpenOrCreate() method exposed by the disk cache.
+    DLOG(WARNING) << "Unable to create cache entry";
+    mode_ = NONE;
+    if (partial_.get())
+      partial_->RestoreHeaders(&custom_request_->extra_headers);
+    next_state_ = STATE_SEND_REQUEST;
+  }
+  return OK;
+}
+
+int HttpCache::Transaction::DoDoomEntry() {
+  next_state_ = STATE_DOOM_ENTRY_COMPLETE;
+  cache_pending_ = true;
+  net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY, NULL);
+  return cache_->DoomEntry(cache_key_, this);
+}
+
+int HttpCache::Transaction::DoDoomEntryComplete(int result) {
+  net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY, NULL);
+  next_state_ = STATE_CREATE_ENTRY;
+  cache_pending_ = false;
+  if (result == ERR_CACHE_RACE)
+    next_state_ = STATE_INIT_ENTRY;
+
+  return OK;
+}
+
+int HttpCache::Transaction::DoAddToEntry() {
+  DCHECK(new_entry_);
+  cache_pending_ = true;
+  next_state_ = STATE_ADD_TO_ENTRY_COMPLETE;
+  net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WAITING, NULL);
+  DCHECK(entry_lock_waiting_since_.is_null());
+  entry_lock_waiting_since_ = base::TimeTicks::Now();
+  return cache_->AddTransactionToEntry(new_entry_, this);
+}
+
+int HttpCache::Transaction::DoAddToEntryComplete(int result) {
+  net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_WAITING, NULL);
+  UMA_HISTOGRAM_TIMES("HttpCache.EntryLockWait",
+                      base::TimeTicks::Now() - entry_lock_waiting_since_);
+  entry_lock_waiting_since_ = base::TimeTicks();
+  DCHECK(new_entry_);
+  cache_pending_ = false;
+
+  if (result == ERR_CACHE_RACE) {
+    new_entry_ = NULL;
+    next_state_ = STATE_INIT_ENTRY;
+    return OK;
+  }
+
+  if (result != OK) {
+    // If there is a failure, the cache should have taken care of new_entry_.
+    NOTREACHED();
+    new_entry_ = NULL;
+    return result;
+  }
+
+  entry_ = new_entry_;
+  new_entry_ = NULL;
+
+  if (mode_ == WRITE) {
+    if (partial_.get())
+      partial_->RestoreHeaders(&custom_request_->extra_headers);
+    next_state_ = STATE_SEND_REQUEST;
+  } else {
+    // We have to read the headers from the cached entry.
+    DCHECK(mode_ & READ_META);
+    next_state_ = STATE_CACHE_READ_RESPONSE;
+  }
+  return OK;
+}
+
+int HttpCache::Transaction::DoStartPartialCacheValidation() {
+  if (mode_ == NONE)
+    return OK;
+
+  next_state_ = STATE_COMPLETE_PARTIAL_CACHE_VALIDATION;
+  return partial_->ShouldValidateCache(entry_->disk_entry, &io_callback_);
+}
+
+int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) {
+  if (!result) {
+    // This is the end of the request.
+    if (mode_ & WRITE) {
+      DoneWritingToEntry(true);
+    } else {
+      cache_->DoneReadingFromEntry(entry_, this);
+      entry_ = NULL;
+    }
+    return result;
+  }
+
+  if (result < 0)
+    return result;
+
+  partial_->PrepareCacheValidation(entry_->disk_entry,
+                                   &custom_request_->extra_headers);
+
+  if (reading_ && partial_->IsCurrentRangeCached()) {
+    next_state_ = STATE_CACHE_READ_DATA;
+    return OK;
+  }
+
+  return BeginCacheValidation();
+}
+
+// We received 304 or 206 and we want to update the cached response headers.
+int HttpCache::Transaction::DoUpdateCachedResponse() {
+  next_state_ = STATE_UPDATE_CACHED_RESPONSE_COMPLETE;
+  int rv = OK;
+  // Update cached response based on headers in new_response.
+  // TODO(wtc): should we update cached certificate (response_.ssl_info), too?
+  response_.headers->Update(*new_response_->headers);
+  response_.response_time = new_response_->response_time;
+  response_.request_time = new_response_->request_time;
+
+  if (response_.headers->HasHeaderValue("cache-control", "no-store")) {
+    int ret = cache_->DoomEntry(cache_key_, NULL);
+    DCHECK_EQ(OK, ret);
+  } else {
+    // If we are already reading, we already updated the headers for this
+    // request; doing it again will change Content-Length.
+    if (!reading_) {
+      target_state_ = STATE_UPDATE_CACHED_RESPONSE_COMPLETE;
+      next_state_ = STATE_CACHE_WRITE_RESPONSE;
+      rv = OK;
+    }
+  }
+  return rv;
+}
+
+int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) {
+  if (mode_ == UPDATE) {
+    DCHECK(!server_responded_206_);
+    // We got a "not modified" response and already updated the corresponding
+    // cache entry above.
+    //
+    // By closing the cached entry now, we make sure that the 304 rather than
+    // the cached 200 response, is what will be returned to the user.
+    DoneWritingToEntry(true);
+  } else if (entry_ && !server_responded_206_) {
+    DCHECK_EQ(READ_WRITE, mode_);
+    if (!partial_.get() || partial_->IsLastRange()) {
+      cache_->ConvertWriterToReader(entry_);
+      mode_ = READ;
+    }
+    // We no longer need the network transaction, so destroy it.
+    final_upload_progress_ = network_trans_->GetUploadProgress();
+    network_trans_.reset();
+  }
+  next_state_ = STATE_OVERWRITE_CACHED_RESPONSE;
+  return OK;
+}
+
+int HttpCache::Transaction::DoOverwriteCachedResponse() {
+  if (mode_ & READ) {
+    next_state_ = STATE_PARTIAL_HEADERS_RECEIVED;
+    return OK;
+  }
+
+  // We change the value of Content-Length for partial content.
+  if (server_responded_206_ && partial_.get())
+    partial_->FixContentLength(new_response_->headers);
+
+  response_ = *new_response_;
+  target_state_ = STATE_TRUNCATE_CACHED_DATA;
+  next_state_ = truncated_ ? STATE_CACHE_WRITE_TRUNCATED_RESPONSE :
+                             STATE_CACHE_WRITE_RESPONSE;
+  return OK;
+}
+
+int HttpCache::Transaction::DoTruncateCachedData() {
+  next_state_ = STATE_TRUNCATE_CACHED_DATA_COMPLETE;
+  cache_callback_->AddRef();  // Balanced in DoTruncateCachedDataComplete.
+  if (!entry_)
+    return OK;
+
+  // Truncate the stream.
+  return WriteToEntry(kResponseContentIndex, 0, NULL, 0, cache_callback_);
+}
+
+int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) {
+  // Balance the AddRef from DoTruncateCachedData.
+  cache_callback_->Release();
+  next_state_ = STATE_TRUNCATE_CACHED_METADATA;
+  return OK;
+}
+
+int HttpCache::Transaction::DoTruncateCachedMetadata() {
+  next_state_ = STATE_TRUNCATE_CACHED_METADATA_COMPLETE;
+  cache_callback_->AddRef();  // Balanced in DoTruncateCachedMetadataComplete.
+  if (!entry_)
+    return OK;
+
+  return WriteToEntry(kMetadataIndex, 0, NULL, 0, cache_callback_);
+}
+
+int HttpCache::Transaction::DoTruncateCachedMetadataComplete(int result) {
+  // Balance the AddRef from DoTruncateCachedMetadata.
+  cache_callback_->Release();
+
+  // If this response is a redirect, then we can stop writing now.  (We don't
+  // need to cache the response body of a redirect.)
+  if (response_.headers->IsRedirect(NULL))
+    DoneWritingToEntry(true);
+  next_state_ = STATE_PARTIAL_HEADERS_RECEIVED;
+  return OK;
+}
+
+int HttpCache::Transaction::DoPartialHeadersReceived() {
+  new_response_ = NULL;
+  if (entry_ && !partial_.get() &&
+      entry_->disk_entry->GetDataSize(kMetadataIndex))
+    next_state_ = STATE_CACHE_READ_METADATA;
+
+  if (!partial_.get())
+    return OK;
+
+  if (reading_) {
+    if (network_trans_.get()) {
+      next_state_ = STATE_NETWORK_READ;
+    } else {
+      next_state_ = STATE_CACHE_READ_DATA;
+    }
+  } else if (mode_ != NONE) {
+    // We are about to return the headers for a byte-range request to the user,
+    // so let's fix them.
+    partial_->FixResponseHeaders(response_.headers);
+  }
+  return OK;
+}
+
+int HttpCache::Transaction::DoCacheReadResponse() {
+  DCHECK(entry_);
+  next_state_ = STATE_CACHE_READ_RESPONSE_COMPLETE;
+
+  io_buf_len_ = entry_->disk_entry->GetDataSize(kResponseInfoIndex);
+  read_buf_ = new IOBuffer(io_buf_len_);
+
+  net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO, NULL);
+  cache_callback_->AddRef();  // Balanced in DoCacheReadResponseComplete.
+  return entry_->disk_entry->ReadData(kResponseInfoIndex, 0, read_buf_,
+                                      io_buf_len_, cache_callback_);
+}
+
+int HttpCache::Transaction::DoCacheReadResponseComplete(int result) {
+  cache_callback_->Release();  // Balance the AddRef from DoCacheReadResponse.
+  net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO, NULL);
+  if (result != io_buf_len_ ||
+      !HttpCache::ParseResponseInfo(read_buf_->data(), io_buf_len_,
+                                    &response_, &truncated_)) {
+    DLOG(ERROR) << "ReadData failed: " << result;
+    return ERR_CACHE_READ_FAILURE;
+  }
+
+  // We now have access to the cache entry.
+  //
+  //  o if we are a reader for the transaction, then we can start reading the
+  //    cache entry.
+  //
+  //  o if we can read or write, then we should check if the cache entry needs
+  //    to be validated and then issue a network request if needed or just read
+  //    from the cache if the cache entry is already valid.
+  //
+  //  o if we are set to UPDATE, then we are handling an externally
+  //    conditionalized request (if-modified-since / if-none-match). We check
+  //    if the request headers define a validation request.
+  //
+  switch (mode_) {
+    case READ:
+      result = BeginCacheRead();
+      break;
+    case READ_WRITE:
+      result = BeginPartialCacheValidation();
+      break;
+    case UPDATE:
+      result = BeginExternallyConditionalizedRequest();
+      break;
+    case WRITE:
+    default:
+      NOTREACHED();
+      result = ERR_FAILED;
+  }
+  return result;
+}
+
+int HttpCache::Transaction::DoCacheWriteResponse() {
+  return WriteResponseInfoToEntry(false);
+}
+
+int HttpCache::Transaction::DoCacheWriteTruncatedResponse() {
+  return WriteResponseInfoToEntry(true);
+}
+
+int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) {
+  next_state_ = target_state_;
+  target_state_ = STATE_NONE;
+  if (!entry_)
+    return OK;
+
+  // Balance the AddRef from WriteResponseInfoToEntry.
+  write_headers_callback_->Release();
+  if (result != io_buf_len_) {
+    DLOG(ERROR) << "failed to write response info to cache";
+    DoneWritingToEntry(false);
+  }
+  return OK;
+}
+
+int HttpCache::Transaction::DoCacheReadMetadata() {
+  DCHECK(entry_);
+  DCHECK(!response_.metadata);
+  next_state_ = STATE_CACHE_READ_METADATA_COMPLETE;
+
+  response_.metadata =
+      new IOBufferWithSize(entry_->disk_entry->GetDataSize(kMetadataIndex));
+
+  net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO, NULL);
+  cache_callback_->AddRef();  // Balanced in DoCacheReadMetadataComplete.
+  return entry_->disk_entry->ReadData(kMetadataIndex, 0, response_.metadata,
+                                      response_.metadata->size(),
+                                      cache_callback_);
+}
+
+int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) {
+  cache_callback_->Release();  // Balance the AddRef from DoCacheReadMetadata.
+  net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO, NULL);
+  if (result != response_.metadata->size()) {
+    DLOG(ERROR) << "ReadData failed: " << result;
+    return ERR_CACHE_READ_FAILURE;
+  }
+
+  return OK;
+}
+
+int HttpCache::Transaction::DoCacheQueryData() {
+  next_state_ = STATE_CACHE_QUERY_DATA_COMPLETE;
+
+  // Balanced in ValidateEntryHeadersAndContinue.
+  cache_callback_->AddRef();
+  return entry_->disk_entry->ReadyForSparseIO(cache_callback_);
+}
+
+int HttpCache::Transaction::DoCacheQueryDataComplete(int result) {
+  DCHECK_EQ(OK, result);
+  // Balance the AddRef from BeginPartialCacheValidation.
+  cache_callback_->Release();
+  if (!cache_)
+    return ERR_UNEXPECTED;
+
+  return ValidateEntryHeadersAndContinue(true);
+}
+
+int HttpCache::Transaction::DoCacheReadData() {
+  DCHECK(entry_);
+  next_state_ = STATE_CACHE_READ_DATA_COMPLETE;
+  cache_callback_->AddRef();  // Balanced in DoCacheReadDataComplete.
+  if (partial_.get()) {
+    return partial_->CacheRead(entry_->disk_entry, read_buf_, io_buf_len_,
+                               cache_callback_);
+  }
+
+  return entry_->disk_entry->ReadData(kResponseContentIndex, read_offset_,
+                                      read_buf_, io_buf_len_, cache_callback_);
+}
+
+int HttpCache::Transaction::DoCacheReadDataComplete(int result) {
+  cache_callback_->Release();  // Balance the AddRef from DoCacheReadData.
+
+  if (!cache_)
+    return ERR_UNEXPECTED;
+
+  if (partial_.get())
+    return DoPartialCacheReadCompleted(result);
+
+  if (result > 0) {
+    read_offset_ += result;
+  } else if (result == 0) {  // End of file.
+    cache_->DoneReadingFromEntry(entry_, this);
+    entry_ = NULL;
+  }
+  return result;
+}
+
+int HttpCache::Transaction::DoCacheWriteData(int num_bytes) {
+  next_state_ = STATE_CACHE_WRITE_DATA_COMPLETE;
+  write_len_ = num_bytes;
+  cache_callback_->AddRef();  // Balanced in DoCacheWriteDataComplete.
+
+  return AppendResponseDataToEntry(read_buf_, num_bytes, cache_callback_);
+}
+
+int HttpCache::Transaction::DoCacheWriteDataComplete(int result) {
+  // Balance the AddRef from DoCacheWriteData.
+  cache_callback_->Release();
+  if (!cache_)
+    return ERR_UNEXPECTED;
+
+  if (result != write_len_) {
+    DLOG(ERROR) << "failed to write response data to cache";
+    DoneWritingToEntry(false);
+
+    // We want to ignore errors writing to disk and just keep reading from
+    // the network.
+    result = write_len_;
+  }
+
+  if (partial_.get()) {
+    // This may be the last request.
+    if (!(result == 0 && !truncated_ &&
+          (partial_->IsLastRange() || mode_ == WRITE)))
+      return DoPartialNetworkReadCompleted(result);
+  }
+
+  if (result == 0)  // End of file.
+    DoneWritingToEntry(true);
+
+  return result;
+}
+
+//-----------------------------------------------------------------------------
+
+void HttpCache::Transaction::SetRequest(const BoundNetLog& net_log,
                                         const HttpRequestInfo* request) {
-  load_log_ = load_log;
+  net_log_ = net_log;
   request_ = request;
   effective_load_flags_ = request_->load_flags;
 
@@ -785,45 +1254,37 @@
     { kForceValidateHeaders, LOAD_VALIDATE_CACHE },
   };
 
-  std::string new_extra_headers;
   bool range_found = false;
   bool external_validation_error = false;
 
-  // scan request headers to see if we have any that would impact our load flags
-  HttpUtil::HeadersIterator it(request_->extra_headers.begin(),
-                               request_->extra_headers.end(),
-                               "\r\n");
-  while (it.GetNext()) {
-    if (!LowerCaseEqualsASCII(it.name(), "range")) {
-      new_extra_headers.append(it.name_begin(), it.values_end());
-      new_extra_headers.append("\r\n");
+  if (request_->extra_headers.HasHeader(HttpRequestHeaders::kRange)) {
+    if (enable_range_support_) {
+      range_found = true;
     } else {
-      if (enable_range_support_) {
-        range_found = true;
-      } else {
-        effective_load_flags_ |= LOAD_DISABLE_CACHE;
-        continue;
-      }
+      effective_load_flags_ |= LOAD_DISABLE_CACHE;
     }
-    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSpecialHeaders); ++i) {
-      if (HeaderMatches(it, kSpecialHeaders[i].search)) {
-        effective_load_flags_ |= kSpecialHeaders[i].load_flag;
-        break;
-      }
-    }
+  }
 
-    // Check for conditionalization headers which may correspond with a
-    // cache validation request.
-    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kValidationHeaders); ++i) {
-      const ValidationHeaderInfo& info = kValidationHeaders[i];
-      if (LowerCaseEqualsASCII(it.name_begin(), it.name_end(),
-                               info.request_header_name)) {
-        if (!external_validation_.values[i].empty() || it.values().empty())
-          external_validation_error = true;
-        external_validation_.values[i] = it.values();
-        external_validation_.initialized = true;
-        break;
-      }
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSpecialHeaders); ++i) {
+    if (HeaderMatches(request_->extra_headers, kSpecialHeaders[i].search)) {
+      effective_load_flags_ |= kSpecialHeaders[i].load_flag;
+      break;
+    }
+  }
+
+  // Check for conditionalization headers which may correspond with a
+  // cache validation request.
+  for (size_t i = 0; i < arraysize(kValidationHeaders); ++i) {
+    const ValidationHeaderInfo& info = kValidationHeaders[i];
+    std::string validation_value;
+    if (request_->extra_headers.GetHeader(
+            info.request_header_name, &validation_value)) {
+      if (!external_validation_.values[i].empty() ||
+          validation_value.empty())
+        external_validation_error = true;
+      external_validation_.values[i] = validation_value;
+      external_validation_.initialized = true;
+      break;
     }
   }
 
@@ -847,9 +1308,9 @@
       // We will be modifying the actual range requested to the server, so
       // let's remove the header here.
       custom_request_.reset(new HttpRequestInfo(*request_));
+      custom_request_->extra_headers.RemoveHeader(HttpRequestHeaders::kRange);
       request_ = custom_request_.get();
-      custom_request_->extra_headers = new_extra_headers;
-      partial_->SetHeaders(new_extra_headers);
+      partial_->SetHeaders(custom_request_->extra_headers);
     } else {
       // The range is invalid or we cannot handle it properly.
       LOG(INFO) << "Invalid byte range found.";
@@ -895,16 +1356,33 @@
   if (truncated_)
     return ERR_CACHE_MISS;
 
+  if (entry_->disk_entry->GetDataSize(kMetadataIndex))
+    next_state_ = STATE_CACHE_READ_METADATA;
+
   return OK;
 }
 
 int HttpCache::Transaction::BeginCacheValidation() {
   DCHECK(mode_ == READ_WRITE);
 
-  if ((effective_load_flags_ & LOAD_PREFERRING_CACHE ||
-       !RequiresValidation()) && !partial_.get()) {
+  bool skip_validation = effective_load_flags_ & LOAD_PREFERRING_CACHE ||
+                         !RequiresValidation();
+
+  if (partial_.get() && !partial_->IsCurrentRangeCached())
+    skip_validation = false;
+
+  if (skip_validation) {
+    if (partial_.get()) {
+      // We are going to return the saved response headers to the caller, so
+      // we may need to adjust them first.
+      next_state_ = STATE_PARTIAL_HEADERS_RECEIVED;
+      return OK;
+    }
     cache_->ConvertWriterToReader(entry_);
     mode_ = READ;
+
+    if (entry_ && entry_->disk_entry->GetDataSize(kMetadataIndex))
+      next_state_ = STATE_CACHE_READ_METADATA;
   } else {
     // Make the network request conditional, to see if we may reuse our cached
     // response.  If we cannot do so, then we just resort to a normal fetch.
@@ -943,24 +1421,6 @@
   return ValidateEntryHeadersAndContinue(false);
 }
 
-int HttpCache::Transaction::DoCacheQueryData() {
-  next_state_ = STATE_CACHE_QUERY_DATA_COMPLETE;
-
-  // Balanced in ValidateEntryHeadersAndContinue.
-  cache_callback_->AddRef();
-  return entry_->disk_entry->ReadyForSparseIO(cache_callback_);
-}
-
-int HttpCache::Transaction::DoCacheQueryDataComplete(int result) {
-  DCHECK_EQ(OK, result);
-  // Balance the AddRef from BeginPartialCacheValidation.
-  cache_callback_->Release();
-  if (!cache_)
-    return ERR_UNEXPECTED;
-
-  return ValidateEntryHeadersAndContinue(true);
-}
-
 int HttpCache::Transaction::ValidateEntryHeadersAndContinue(
     bool byte_range_requested) {
   DCHECK(mode_ == READ_WRITE);
@@ -981,46 +1441,15 @@
     invalid_range_ = true;
   }
 
-  next_state_ = STATE_PARTIAL_CACHE_VALIDATION;
+  next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION;
   return OK;
 }
 
-int HttpCache::Transaction::DoPartialCacheValidation() {
-  if (mode_ == NONE)
-    return OK;
-
-  int rv = partial_->PrepareCacheValidation(entry_->disk_entry,
-                                            &custom_request_->extra_headers);
-
-  if (!rv) {
-    // This is the end of the request.
-    if (mode_ & WRITE) {
-      DoneWritingToEntry(true);
-    } else {
-      cache_->DoneReadingFromEntry(entry_, this);
-      entry_ = NULL;
-    }
-    return rv;
-  }
-
-  if (rv < 0) {
-    DCHECK(rv != ERR_IO_PENDING);
-    return rv;
-  }
-
-  if (reading_ && partial_->IsCurrentRangeCached()) {
-    next_state_ = STATE_CACHE_READ_DATA;
-    return OK;
-  }
-
-  return BeginCacheValidation();
-}
-
 int HttpCache::Transaction::BeginExternallyConditionalizedRequest() {
   DCHECK_EQ(UPDATE, mode_);
   DCHECK(external_validation_.initialized);
 
-  for (size_t i = 0;  i < ARRAYSIZE_UNSAFE(kValidationHeaders); i++) {
+  for (size_t i = 0;  i < arraysize(kValidationHeaders); i++) {
     if (external_validation_.values[i].empty())
       continue;
     // Retrieve either the cached response's "etag" or "last-modified" header.
@@ -1042,25 +1471,6 @@
   return OK;
 }
 
-int HttpCache::Transaction::BeginNetworkRequest() {
-  next_state_ = STATE_SEND_REQUEST;
-  return DoLoop(OK);
-}
-
-int HttpCache::Transaction::DoSendRequest() {
-  DCHECK(mode_ & WRITE || mode_ == NONE);
-  DCHECK(!network_trans_.get());
-
-  // Create a network transaction.
-  int rv = cache_->network_layer_->CreateTransaction(&network_trans_);
-  if (rv != OK)
-    return rv;
-
-  next_state_ = STATE_SEND_REQUEST_COMPLETE;
-  rv = network_trans_->Start(request_, &io_callback_, load_log_);
-  return rv;
-}
-
 int HttpCache::Transaction::RestartNetworkRequest() {
   DCHECK(mode_ & WRITE || mode_ == NONE);
   DCHECK(network_trans_.get());
@@ -1120,7 +1530,7 @@
 
   // Since Vary header computation is fairly expensive, we save it for last.
   if (response_.vary_data.is_valid() &&
-          !response_.vary_data.MatchesRequest(*request_, *response_.headers))
+      !response_.vary_data.MatchesRequest(*request_, *response_.headers))
     return true;
 
   return false;
@@ -1163,12 +1573,12 @@
     if (partial_.get() && !partial_->IsCurrentRangeCached()) {
       // We don't want to switch to WRITE mode if we don't have this block of a
       // byte-range request because we may have other parts cached.
-      custom_request_->extra_headers.append("If-Range: ");
+      custom_request_->extra_headers.SetHeader(
+          HttpRequestHeaders::kIfRange, etag_value);
     } else {
-      custom_request_->extra_headers.append("If-None-Match: ");
+      custom_request_->extra_headers.SetHeader(
+          HttpRequestHeaders::kIfNoneMatch, etag_value);
     }
-    custom_request_->extra_headers.append(etag_value);
-    custom_request_->extra_headers.append("\r\n");
     // For byte-range requests, make sure that we use only one way to validate
     // the request.
     if (partial_.get() && !partial_->IsCurrentRangeCached())
@@ -1177,12 +1587,12 @@
 
   if (!last_modified_value.empty()) {
     if (partial_.get() && !partial_->IsCurrentRangeCached()) {
-      custom_request_->extra_headers.append("If-Range: ");
+      custom_request_->extra_headers.SetHeader(
+          HttpRequestHeaders::kIfRange, last_modified_value);
     } else {
-      custom_request_->extra_headers.append("If-Modified-Since: ");
+      custom_request_->extra_headers.SetHeader(
+          HttpRequestHeaders::kIfModifiedSince, last_modified_value);
     }
-    custom_request_->extra_headers.append(last_modified_value);
-    custom_request_->extra_headers.append("\r\n");
   }
 
   return true;
@@ -1303,11 +1713,6 @@
   return DoLoop(OK);
 }
 
-int HttpCache::Transaction::DoNetworkRead() {
-  next_state_ = STATE_NETWORK_READ_COMPLETE;
-  return network_trans_->Read(read_buf_, io_buf_len_, &io_callback_);
-}
-
 int HttpCache::Transaction::ReadFromEntry(IOBuffer* data, int data_len) {
   read_buf_ = data;
   io_buf_len_ = data_len;
@@ -1315,32 +1720,6 @@
   return DoLoop(OK);
 }
 
-int HttpCache::Transaction::DoCacheReadData() {
-  DCHECK(entry_);
-  next_state_ = STATE_CACHE_READ_DATA_COMPLETE;
-  cache_callback_->AddRef();  // Balanced in DoCacheReadDataComplete.
-  if (partial_.get()) {
-    return partial_->CacheRead(entry_->disk_entry, read_buf_, io_buf_len_,
-                               cache_callback_);
-  }
-
-  return entry_->disk_entry->ReadData(kResponseContentIndex, read_offset_,
-                                      read_buf_, io_buf_len_, cache_callback_);
-}
-
-int HttpCache::Transaction::DoCacheReadResponse() {
-  DCHECK(entry_);
-  next_state_ = STATE_CACHE_READ_RESPONSE_COMPLETE;
-
-  io_buf_len_ = entry_->disk_entry->GetDataSize(kResponseInfoIndex);
-  read_buf_ = new IOBuffer(io_buf_len_);
-
-  LoadLog::BeginEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_READ_INFO);
-  cache_callback_->AddRef();  // Balanced in DoCacheReadResponseComplete.
-  return entry_->disk_entry->ReadData(kResponseInfoIndex, 0, read_buf_,
-                                      io_buf_len_, cache_callback_);
-}
-
 int HttpCache::Transaction::WriteToEntry(int index, int offset,
                                          IOBuffer* data, int data_len,
                                          CompletionCallback* callback) {
@@ -1354,26 +1733,9 @@
   } else {
     rv = partial_->CacheWrite(entry_->disk_entry, data, data_len, callback);
   }
-
-  if (rv != ERR_IO_PENDING && rv != data_len) {
-    DLOG(ERROR) << "failed to write response data to cache";
-    DoneWritingToEntry(false);
-
-    // We want to ignore errors writing to disk and just keep reading from
-    // the network.
-    rv = data_len;
-  }
   return rv;
 }
 
-int HttpCache::Transaction::DoCacheWriteResponse() {
-  return WriteResponseInfoToEntry(false);
-}
-
-int HttpCache::Transaction::DoCacheWriteTruncatedResponse() {
-  return WriteResponseInfoToEntry(true);
-}
-
 int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) {
   next_state_ = STATE_CACHE_WRITE_RESPONSE_COMPLETE;
   if (!entry_)
@@ -1389,7 +1751,7 @@
   // errors) and no SSL blocking page is shown.  An alternative would be to
   // reverse-map the cert status to a net error and replay the net error.
   if ((cache_->mode() != RECORD &&
-      response_.headers->HasHeaderValue("cache-control", "no-store")) ||
+       response_.headers->HasHeaderValue("cache-control", "no-store")) ||
       net::IsCertStatusError(response_.ssl_info.cert_status)) {
     DoneWritingToEntry(false);
     return OK;
@@ -1415,21 +1777,6 @@
                                        write_headers_callback_, true);
 }
 
-int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) {
-  next_state_ = target_state_;
-  target_state_ = STATE_NONE;
-  if (!entry_)
-    return OK;
-
-  // Balance the AddRef from WriteResponseInfoToEntry.
-  write_headers_callback_->Release();
-  if (result != io_buf_len_) {
-    DLOG(ERROR) << "failed to write response info to cache";
-    DoneWritingToEntry(false);
-  }
-  return OK;
-}
-
 int HttpCache::Transaction::AppendResponseDataToEntry(
     IOBuffer* data, int data_len, CompletionCallback* callback) {
   if (!entry_ || !data_len)
@@ -1440,17 +1787,6 @@
                       callback);
 }
 
-int HttpCache::Transaction::DoTruncateCachedData() {
-  next_state_ = STATE_TRUNCATE_CACHED_DATA_COMPLETE;
-  if (!entry_)
-    return OK;
-
-  // Truncate the stream.
-  int rv = WriteToEntry(kResponseContentIndex, 0, NULL, 0, NULL);
-  DCHECK(rv != ERR_IO_PENDING);
-  return OK;
-}
-
 void HttpCache::Transaction::DoneWritingToEntry(bool success) {
   if (!entry_)
     return;
@@ -1473,48 +1809,13 @@
     partial_.reset(NULL);
 }
 
-int HttpCache::Transaction::DoNetworkReadComplete(int result) {
-  DCHECK(mode_ & WRITE || mode_ == NONE);
-
-  if (!cache_)
-    return ERR_UNEXPECTED;
-
-  next_state_ = STATE_CACHE_WRITE_DATA;
-  return result;
-}
-
-int HttpCache::Transaction::DoCacheWriteData(int num_bytes) {
-  next_state_ = STATE_CACHE_WRITE_DATA_COMPLETE;
-  cache_callback_->AddRef();  // Balanced in DoCacheWriteDataComplete.
-
-  return AppendResponseDataToEntry(read_buf_, num_bytes, cache_callback_);
-}
-
 int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) {
   partial_->OnNetworkReadCompleted(result);
 
   if (result == 0) {
     // We need to move on to the next range.
     network_trans_.reset();
-    next_state_ = STATE_PARTIAL_CACHE_VALIDATION;
-  }
-  return result;
-}
-
-int HttpCache::Transaction::DoCacheReadDataComplete(int result) {
-  cache_callback_->Release();  // Balance the AddRef from DoCacheReadData.
-
-  if (!cache_)
-    return ERR_UNEXPECTED;
-
-  if (partial_.get())
-    return DoPartialCacheReadCompleted(result);
-
-  if (result > 0) {
-    read_offset_ += result;
-  } else if (result == 0) {  // End of file.
-    cache_->DoneReadingFromEntry(entry_, this);
-    entry_ = NULL;
+    next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION;
   }
   return result;
 }
@@ -1524,190 +1825,7 @@
 
   if (result == 0 && mode_ == READ_WRITE) {
     // We need to move on to the next range.
-    next_state_ = STATE_PARTIAL_CACHE_VALIDATION;
-  }
-  return result;
-}
-
-int HttpCache::Transaction::DoCacheWriteDataComplete(int result) {
-  // Balance the AddRef from DoCacheWriteData.
-  cache_callback_->Release();
-  if (!cache_)
-    return ERR_UNEXPECTED;
-
-  if (result < 0)
-    return result;
-
-  if (partial_.get()) {
-    // This may be the last request.
-    if (!(result == 0 && !truncated_ &&
-         (partial_->IsLastRange() || mode_ == WRITE)))
-      return DoPartialNetworkReadCompleted(result);
-  }
-
-  if (result == 0)  // End of file.
-    DoneWritingToEntry(true);
-
-  return result;
-}
-
-// We received the response headers and there is no error.
-int HttpCache::Transaction::DoSuccessfulSendRequest() {
-  DCHECK(!new_response_);
-  const HttpResponseInfo* new_response = network_trans_->GetResponseInfo();
-  if (new_response->headers->response_code() == 401 ||
-      new_response->headers->response_code() == 407) {
-    auth_response_ = *new_response;
-    return OK;
-  }
-
-  if (!ValidatePartialResponse(new_response->headers, &server_responded_206_) &&
-      !auth_response_.headers) {
-    // Something went wrong with this request and we have to restart it.
-    // If we have an authentication response, we are exposed to weird things
-    // hapenning if the user cancels the authentication before we receive
-    // the new response.
-    response_ = HttpResponseInfo();
-    network_trans_.reset();
-    next_state_ = STATE_SEND_REQUEST;
-    return OK;
-  }
-  if (server_responded_206_ && mode_ == READ_WRITE && !truncated_ &&
-      response_.headers->response_code() == 200) {
-    // We have stored the full entry, but it changed and the server is
-    // sending a range. We have to delete the old entry.
-    DoneWritingToEntry(false);
-  }
-
-  HistogramHeaders(new_response->headers);
-
-  new_response_ = new_response;
-  // Are we expecting a response to a conditional query?
-  if (mode_ == READ_WRITE || mode_ == UPDATE) {
-    if (new_response->headers->response_code() == 304 ||
-        server_responded_206_) {
-      next_state_ = STATE_UPDATE_CACHED_RESPONSE;
-      return OK;
-    }
-    mode_ = WRITE;
-  }
-
-  next_state_ = STATE_OVERWRITE_CACHED_RESPONSE;
-  return OK;
-}
-
-// We received 304 or 206 and we want to update the cached response headers.
-int HttpCache::Transaction::DoUpdateCachedResponse() {
-  next_state_ = STATE_UPDATE_CACHED_RESPONSE_COMPLETE;
-  int rv = OK;
-  // Update cached response based on headers in new_response.
-  // TODO(wtc): should we update cached certificate (response_.ssl_info), too?
-  response_.headers->Update(*new_response_->headers);
-  response_.response_time = new_response_->response_time;
-  response_.request_time = new_response_->request_time;
-
-  if (response_.headers->HasHeaderValue("cache-control", "no-store")) {
-    int ret = cache_->DoomEntry(cache_key_, NULL);
-    DCHECK_EQ(OK, ret);
-  } else {
-    // If we are already reading, we already updated the headers for this
-    // request; doing it again will change Content-Length.
-    if (!reading_) {
-      target_state_ = STATE_UPDATE_CACHED_RESPONSE_COMPLETE;
-      next_state_ = STATE_CACHE_WRITE_RESPONSE;
-      rv = OK;
-    }
-  }
-  return rv;
-}
-
-int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) {
-  if (mode_ == UPDATE) {
-    DCHECK(!server_responded_206_);
-    // We got a "not modified" response and already updated the corresponding
-    // cache entry above.
-    //
-    // By closing the cached entry now, we make sure that the 304 rather than
-    // the cached 200 response, is what will be returned to the user.
-    DoneWritingToEntry(true);
-  } else if (entry_ && !server_responded_206_) {
-    DCHECK_EQ(READ_WRITE, mode_);
-    if (!partial_.get() || partial_->IsLastRange()) {
-      cache_->ConvertWriterToReader(entry_);
-      mode_ = READ;
-    }
-    // We no longer need the network transaction, so destroy it.
-    final_upload_progress_ = network_trans_->GetUploadProgress();
-    network_trans_.reset();
-  }
-  next_state_ = STATE_OVERWRITE_CACHED_RESPONSE;
-  return OK;
-}
-
-int HttpCache::Transaction::DoOverwriteCachedResponse() {
-  if (mode_ & READ) {
-    next_state_ = STATE_PARTIAL_HEADERS_RECEIVED;
-    return OK;
-  }
-
-  // We change the value of Content-Length for partial content.
-  if (server_responded_206_ && partial_.get())
-    partial_->FixContentLength(new_response_->headers);
-
-  response_ = *new_response_;
-  target_state_ = STATE_TRUNCATE_CACHED_DATA;
-  next_state_ = truncated_ ? STATE_CACHE_WRITE_TRUNCATED_RESPONSE :
-                             STATE_CACHE_WRITE_RESPONSE;
-  return OK;
-}
-
-int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) {
-  // If this response is a redirect, then we can stop writing now.  (We don't
-  // need to cache the response body of a redirect.)
-  if (response_.headers->IsRedirect(NULL))
-    DoneWritingToEntry(true);
-  next_state_ = STATE_PARTIAL_HEADERS_RECEIVED;
-  return OK;
-}
-
-int HttpCache::Transaction::DoPartialHeadersReceived() {
-  new_response_ = NULL;
-  if (!partial_.get())
-    return OK;
-
-  if (reading_) {
-    if (network_trans_.get()) {
-      next_state_ = STATE_NETWORK_READ;
-    } else {
-      next_state_ = STATE_CACHE_READ_DATA;
-    }
-  } else if (mode_ != NONE) {
-    // We are about to return the headers for a byte-range request to the user,
-    // so let's fix them.
-    partial_->FixResponseHeaders(response_.headers);
-  }
-  return OK;
-}
-
-int HttpCache::Transaction::DoSendRequestComplete(int result) {
-  if (!cache_)
-    return ERR_UNEXPECTED;
-
-  if (result == OK) {
-    next_state_ = STATE_SUCCESSFUL_SEND_REQUEST;
-    return OK;
-  }
-
-  if (IsCertificateError(result)) {
-    const HttpResponseInfo* response = network_trans_->GetResponseInfo();
-    // If we get a certificate error, then there is a certificate in ssl_info,
-    // so GetResponseInfo() should never returns NULL here.
-    DCHECK(response);
-    response_.ssl_info = response->ssl_info;
-  } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
-    const HttpResponseInfo* response = network_trans_->GetResponseInfo();
-    DCHECK(response);
-    response_.cert_request_info = response->cert_request_info;
+    next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION;
   }
   return result;
 }
diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h
index f417378..ae143e9 100644
--- a/net/http/http_cache_transaction.h
+++ b/net/http/http_cache_transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -8,6 +8,8 @@
 #ifndef NET_HTTP_HTTP_CACHE_TRANSACTION_H_
 #define NET_HTTP_HTTP_CACHE_TRANSACTION_H_
 
+#include "net/base/net_log.h"
+#include "base/time.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_response_info.h"
 #include "net/http/http_transaction.h"
@@ -25,7 +27,8 @@
   virtual ~Transaction();
 
   // HttpTransaction methods:
-  virtual int Start(const HttpRequestInfo*, CompletionCallback*, LoadLog*);
+  virtual int Start(const HttpRequestInfo*, CompletionCallback*,
+                    const BoundNetLog&);
   virtual int RestartIgnoringLastError(CompletionCallback* callback);
   virtual int RestartWithCertificate(X509Certificate* client_cert,
                                      CompletionCallback* callback);
@@ -34,6 +37,7 @@
                               CompletionCallback* callback);
   virtual bool IsReadyToRestartForAuth();
   virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
+  virtual void StopCaching();
   virtual const HttpResponseInfo* GetResponseInfo() const;
   virtual LoadState GetLoadState() const;
   virtual uint64 GetUploadProgress(void) const;
@@ -71,18 +75,36 @@
 
   const std::string& key() const { return cache_key_; }
 
-  // Associates this transaction with a cache entry.
-  int AddToEntry();
-
-  // Called by the HttpCache when the given disk cache entry becomes accessible
-  // to the transaction.  Returns network error code.
-  int EntryAvailable(ActiveEntry* entry);
+  // Writes |buf_len| bytes of meta-data from the provided buffer |buf|. to the
+  // HTTP cache entry that backs this transaction (if any).
+  // Returns the number of bytes actually written, or a net error code. If the
+  // operation cannot complete immediately, returns ERR_IO_PENDING, grabs a
+  // reference to the buffer (until completion), and notifies the caller using
+  // the provided |callback| when the operatiopn finishes.
+  //
+  // The first time this method is called for a given transaction, previous
+  // meta-data will be overwritten with the provided data, and subsequent
+  // invocations will keep appending to the cached entry.
+  //
+  // In order to guarantee that the metadata is set to the correct entry, the
+  // response (or response info) must be evaluated by the caller, for instance
+  // to make sure that the response_time is as expected, before calling this
+  // method.
+  int WriteMetadata(IOBuffer* buf, int buf_len, CompletionCallback* callback);
 
   // This transaction is being deleted and we are not done writing to the cache.
   // We need to indicate that the response data was truncated.  Returns true on
   // success.
   bool AddTruncatedFlag();
 
+  // Returns the LoadState of the writer transaction of a given ActiveEntry. In
+  // other words, returns the LoadState of this transaction without asking the
+  // http cache, because this transaction should be the one currently writing
+  // to the cache entry.
+  LoadState GetWriterLoadState() const;
+
+  CompletionCallback* io_callback() { return &io_callback_; }
+
  private:
   static const size_t kNumValidationHeaders = 2;
   // Helper struct to pair a header name with its value, for
@@ -96,6 +118,8 @@
 
   enum State {
     STATE_NONE,
+    STATE_GET_BACKEND,
+    STATE_GET_BACKEND_COMPLETE,
     STATE_SEND_REQUEST,
     STATE_SEND_REQUEST_COMPLETE,
     STATE_SUCCESSFUL_SEND_REQUEST,
@@ -109,19 +133,24 @@
     STATE_DOOM_ENTRY,
     STATE_DOOM_ENTRY_COMPLETE,
     STATE_ADD_TO_ENTRY,
-    STATE_ENTRY_AVAILABLE,
-    STATE_PARTIAL_CACHE_VALIDATION,
+    STATE_ADD_TO_ENTRY_COMPLETE,
+    STATE_START_PARTIAL_CACHE_VALIDATION,
+    STATE_COMPLETE_PARTIAL_CACHE_VALIDATION,
     STATE_UPDATE_CACHED_RESPONSE,
     STATE_UPDATE_CACHED_RESPONSE_COMPLETE,
     STATE_OVERWRITE_CACHED_RESPONSE,
     STATE_TRUNCATE_CACHED_DATA,
     STATE_TRUNCATE_CACHED_DATA_COMPLETE,
+    STATE_TRUNCATE_CACHED_METADATA,
+    STATE_TRUNCATE_CACHED_METADATA_COMPLETE,
     STATE_PARTIAL_HEADERS_RECEIVED,
     STATE_CACHE_READ_RESPONSE,
     STATE_CACHE_READ_RESPONSE_COMPLETE,
     STATE_CACHE_WRITE_RESPONSE,
     STATE_CACHE_WRITE_TRUNCATED_RESPONSE,
     STATE_CACHE_WRITE_RESPONSE_COMPLETE,
+    STATE_CACHE_READ_METADATA,
+    STATE_CACHE_READ_METADATA_COMPLETE,
     STATE_CACHE_QUERY_DATA,
     STATE_CACHE_QUERY_DATA_COMPLETE,
     STATE_CACHE_READ_DATA,
@@ -143,6 +172,8 @@
   // Each of these methods corresponds to a State value.  If there is an
   // argument, the value corresponds to the return of the previous state or
   // corresponding callback.
+  int DoGetBackend();
+  int DoGetBackendComplete(int result);
   int DoSendRequest();
   int DoSendRequestComplete(int result);
   int DoSuccessfulSendRequest();
@@ -156,19 +187,24 @@
   int DoDoomEntry();
   int DoDoomEntryComplete(int result);
   int DoAddToEntry();
-  int DoEntryAvailable();
-  int DoPartialCacheValidation();
+  int DoAddToEntryComplete(int result);
+  int DoStartPartialCacheValidation();
+  int DoCompletePartialCacheValidation(int result);
   int DoUpdateCachedResponse();
   int DoUpdateCachedResponseComplete(int result);
   int DoOverwriteCachedResponse();
   int DoTruncateCachedData();
   int DoTruncateCachedDataComplete(int result);
+  int DoTruncateCachedMetadata();
+  int DoTruncateCachedMetadataComplete(int result);
   int DoPartialHeadersReceived();
   int DoCacheReadResponse();
   int DoCacheReadResponseComplete(int result);
   int DoCacheWriteResponse();
   int DoCacheWriteTruncatedResponse();
   int DoCacheWriteResponseComplete(int result);
+  int DoCacheReadMetadata();
+  int DoCacheReadMetadataComplete(int result);
   int DoCacheQueryData();
   int DoCacheQueryDataComplete(int result);
   int DoCacheReadData();
@@ -177,7 +213,7 @@
   int DoCacheWriteDataComplete(int result);
 
   // Sets request_ and fields derived from it.
-  void SetRequest(LoadLog* load_log, const HttpRequestInfo* request);
+  void SetRequest(const BoundNetLog& net_log, const HttpRequestInfo* request);
 
   // Returns true if the request should be handled exclusively by the network
   // layer (skipping the cache entirely).
@@ -203,9 +239,6 @@
   // Returns a network error code.
   int BeginExternallyConditionalizedRequest();
 
-  // Called to begin a network transaction.  Returns network error code.
-  int BeginNetworkRequest();
-
   // Called to restart a network transaction after an error.  Returns network
   // error code.
   int RestartNetworkRequest();
@@ -271,9 +304,6 @@
   // working with range requests.
   int DoPartialCacheReadCompleted(int result);
 
-  // Performs the needed work after writing data to the cache.
-  int DoCacheWriteCompleted(int result);
-
   // Sends a histogram with info about the response headers.
   void HistogramHeaders(const HttpResponseHeaders* headers);
 
@@ -282,13 +312,14 @@
 
   State next_state_;
   const HttpRequestInfo* request_;
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
   scoped_ptr<HttpRequestInfo> custom_request_;
   // If extra_headers specified a "if-modified-since" or "if-none-match",
   // |external_validation_| contains the value of those headers.
   ValidationHeaders external_validation_;
   base::WeakPtr<HttpCache> cache_;
   HttpCache::ActiveEntry* entry_;
+  base::TimeTicks entry_lock_waiting_since_;
   HttpCache::ActiveEntry* new_entry_;
   scoped_ptr<HttpTransaction> network_trans_;
   CompletionCallback* callback_;  // Consumer's callback.
@@ -308,6 +339,7 @@
   int io_buf_len_;
   int read_offset_;
   int effective_load_flags_;
+  int write_len_;
   scoped_ptr<PartialData> partial_;  // We are dealing with range requests.
   uint64 final_upload_progress_;
   CompletionCallbackImpl<Transaction> io_callback_;
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 15ac1aa..8b2f7cc 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -11,10 +11,11 @@
 #include "net/base/cache_type.h"
 #include "net/base/net_errors.h"
 #include "net/base/load_flags.h"
-#include "net/base/load_log_unittest.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/ssl_cert_request_info.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/http_byte_range.h"
+#include "net/http/http_request_headers.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
@@ -47,9 +48,23 @@
   return t->test_mode;
 }
 
+// We can override the test mode for a given operation by setting this global
+// variable. Just remember to reset it after the test!.
+int g_test_mode = 0;
+
+// Returns the test mode after considering the global override.
+int GetEffectiveTestMode(int test_mode) {
+  if (!g_test_mode)
+    return test_mode;
+
+  return g_test_mode;
+}
+
 //-----------------------------------------------------------------------------
 // mock disk cache (a very basic memory cache implementation)
 
+static const int kNumCacheEntryDataIndices = 3;
+
 class MockDiskEntry : public disk_cache::Entry,
                       public base::RefCounted<MockDiskEntry> {
  public:
@@ -89,13 +104,14 @@
   }
 
   virtual int32 GetDataSize(int index) const {
-    DCHECK(index >= 0 && index < 2);
+    DCHECK(index >= 0 && index < kNumCacheEntryDataIndices);
     return static_cast<int32>(data_[index].size());
   }
 
   virtual int ReadData(int index, int offset, net::IOBuffer* buf, int buf_len,
                        net::CompletionCallback* callback) {
-    DCHECK(index >= 0 && index < 2);
+    DCHECK(index >= 0 && index < kNumCacheEntryDataIndices);
+    DCHECK(callback);
 
     if (fail_requests_)
       return net::ERR_CACHE_READ_FAILURE;
@@ -108,7 +124,7 @@
     int num = std::min(buf_len, static_cast<int>(data_[index].size()) - offset);
     memcpy(buf->data(), &data_[index][offset], num);
 
-    if (!callback || (test_mode_ & TEST_MODE_SYNC_CACHE_READ))
+    if (GetEffectiveTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ)
       return num;
 
     CallbackLater(callback, num);
@@ -117,11 +133,14 @@
 
   virtual int WriteData(int index, int offset, net::IOBuffer* buf, int buf_len,
                         net::CompletionCallback* callback, bool truncate) {
-    DCHECK(index >= 0 && index < 2);
+    DCHECK(index >= 0 && index < kNumCacheEntryDataIndices);
+    DCHECK(callback);
     DCHECK(truncate);
 
-    if (fail_requests_)
-      return net::ERR_CACHE_READ_FAILURE;
+    if (fail_requests_) {
+      CallbackLater(callback, net::ERR_CACHE_READ_FAILURE);
+      return net::ERR_IO_PENDING;
+    }
 
     if (offset < 0 || offset > static_cast<int>(data_[index].size()))
       return net::ERR_FAILED;
@@ -130,7 +149,7 @@
     if (buf_len)
       memcpy(&data_[index][offset], buf->data(), buf_len);
 
-    if (!callback || (test_mode_ & TEST_MODE_SYNC_CACHE_WRITE))
+    if (GetEffectiveTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE)
       return buf_len;
 
     CallbackLater(callback, buf_len);
@@ -138,7 +157,8 @@
   }
 
   virtual int ReadSparseData(int64 offset, net::IOBuffer* buf, int buf_len,
-                             net::CompletionCallback* completion_callback) {
+                             net::CompletionCallback* callback) {
+    DCHECK(callback);
     if (!sparse_ || busy_)
       return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
     if (offset < 0)
@@ -156,17 +176,18 @@
                        buf_len);
     memcpy(buf->data(), &data_[1][real_offset], num);
 
-    if (!completion_callback || (test_mode_ & TEST_MODE_SYNC_CACHE_READ))
+    if (GetEffectiveTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ)
       return num;
 
-    CallbackLater(completion_callback, num);
+    CallbackLater(callback, num);
     busy_ = true;
     delayed_ = false;
     return net::ERR_IO_PENDING;
   }
 
   virtual int WriteSparseData(int64 offset, net::IOBuffer* buf, int buf_len,
-                              net::CompletionCallback* completion_callback) {
+                              net::CompletionCallback* callback) {
+    DCHECK(callback);
     if (busy_)
       return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
     if (!sparse_) {
@@ -189,14 +210,16 @@
       data_[1].resize(real_offset + buf_len);
 
     memcpy(&data_[1][real_offset], buf->data(), buf_len);
-    if (!completion_callback || (test_mode_ & TEST_MODE_SYNC_CACHE_WRITE))
+    if (GetEffectiveTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE)
       return buf_len;
 
-    CallbackLater(completion_callback, buf_len);
+    CallbackLater(callback, buf_len);
     return net::ERR_IO_PENDING;
   }
 
-  virtual int GetAvailableRange(int64 offset, int len, int64* start) {
+  virtual int GetAvailableRange(int64 offset, int len, int64* start,
+                                net::CompletionCallback* callback) {
+    DCHECK(callback);
     if (!sparse_ || busy_)
       return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
     if (offset < 0)
@@ -225,12 +248,15 @@
         count++;
       }
     }
-    return count;
+    if (GetEffectiveTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE)
+      return count;
+
+    CallbackLater(callback, count);
+    return net::ERR_IO_PENDING;
   }
 
-  virtual int GetAvailableRange(int64 offset, int len, int64* start,
-                                net::CompletionCallback* callback) {
-    return net::ERR_NOT_IMPLEMENTED;
+  virtual bool CouldBeSparse() const {
+    return sparse_;
   }
 
   virtual void CancelSparseIO() { cancel_ = true; }
@@ -241,7 +267,7 @@
 
     cancel_ = false;
     DCHECK(completion_callback);
-    if (test_mode_ & TEST_MODE_SYNC_CACHE_READ)
+    if (GetEffectiveTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ)
       return net::OK;
 
     // The pending operation is already in the message loop (and hopefuly
@@ -281,8 +307,8 @@
   void CallbackLater(net::CompletionCallback* callback, int result) {
     if (ignore_callbacks_)
       return StoreAndDeliverCallbacks(true, this, callback, result);
-    MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this,
-        &MockDiskEntry::RunCallback, callback, result));
+    MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+        this, &MockDiskEntry::RunCallback, callback, result));
   }
   void RunCallback(net::CompletionCallback* callback, int result) {
     if (busy_) {
@@ -322,7 +348,7 @@
   }
 
   std::string key_;
-  std::vector<char> data_[2];
+  std::vector<char> data_[kNumCacheEntryDataIndices];
   int test_mode_;
   bool doomed_;
   bool sparse_;
@@ -345,22 +371,16 @@
   }
 
   ~MockDiskCache() {
-    EntryMap::iterator it = entries_.begin();
-    for (; it != entries_.end(); ++it)
-      it->second->Release();
+    ReleaseAll();
   }
 
   virtual int32 GetEntryCount() const {
     return static_cast<int32>(entries_.size());
   }
 
-  virtual bool OpenEntry(const std::string& key, disk_cache::Entry** entry) {
-    NOTREACHED();
-    return false;
-  }
-
   virtual int OpenEntry(const std::string& key, disk_cache::Entry** entry,
                         net::CompletionCallback* callback) {
+    DCHECK(callback);
     if (fail_requests_)
       return net::ERR_CACHE_OPEN_FAILURE;
 
@@ -382,20 +402,16 @@
     if (soft_failures_)
       it->second->set_fail_requests();
 
-    if (!callback || (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START))
+    if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START)
       return net::OK;
 
     CallbackLater(callback, net::OK);
     return net::ERR_IO_PENDING;
   }
 
-  virtual bool CreateEntry(const std::string& key, disk_cache::Entry** entry) {
-    NOTREACHED();
-    return false;
-  }
-
   virtual int CreateEntry(const std::string& key, disk_cache::Entry** entry,
                           net::CompletionCallback* callback) {
+    DCHECK(callback);
     if (fail_requests_)
       return net::ERR_CACHE_CREATE_FAILURE;
 
@@ -419,19 +435,16 @@
     if (soft_failures_)
       new_entry->set_fail_requests();
 
-    if (!callback || (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START))
+    if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START)
       return net::OK;
 
     CallbackLater(callback, net::OK);
     return net::ERR_IO_PENDING;
   }
 
-  virtual bool DoomEntry(const std::string& key) {
-    return false;
-  }
-
   virtual int DoomEntry(const std::string& key,
                         net::CompletionCallback* callback) {
+    DCHECK(callback);
     EntryMap::iterator it = entries_.find(key);
     if (it != entries_.end()) {
       it->second->Release();
@@ -445,38 +458,21 @@
     return net::ERR_IO_PENDING;
   }
 
-  virtual bool DoomAllEntries() {
-    return false;
-  }
-
   virtual int DoomAllEntries(net::CompletionCallback* callback) {
     return net::ERR_NOT_IMPLEMENTED;
   }
 
-  virtual bool DoomEntriesBetween(const Time initial_time,
-                                  const Time end_time) {
-    return false;
-  }
-
   virtual int DoomEntriesBetween(const base::Time initial_time,
                                  const base::Time end_time,
                                  net::CompletionCallback* callback) {
     return net::ERR_NOT_IMPLEMENTED;
   }
 
-  virtual bool DoomEntriesSince(const Time initial_time) {
-    return false;
-  }
-
   virtual int DoomEntriesSince(const base::Time initial_time,
                                net::CompletionCallback* callback) {
     return net::ERR_NOT_IMPLEMENTED;
   }
 
-  virtual bool OpenNextEntry(void** iter, disk_cache::Entry** next_entry) {
-    return false;
-  }
-
   virtual int OpenNextEntry(void** iter, disk_cache::Entry** next_entry,
                             net::CompletionCallback* callback) {
     return net::ERR_NOT_IMPLEMENTED;
@@ -500,6 +496,13 @@
   // Return entries that fail some of their requests.
   void set_soft_failures(bool value) { soft_failures_ = value; }
 
+  void ReleaseAll() {
+    EntryMap::iterator it = entries_.begin();
+    for (; it != entries_.end(); ++it)
+      it->second->Release();
+    entries_.clear();
+  }
+
  private:
   typedef base::hash_map<std::string, MockDiskEntry*> EntryMap;
 
@@ -512,7 +515,7 @@
     }
 
    private:
-     net::CompletionCallback* callback_;
+    net::CompletionCallback* callback_;
     int result_;
     DISALLOW_COPY_AND_ASSIGN(CallbackRunner);
   };
@@ -529,13 +532,23 @@
   bool soft_failures_;
 };
 
+class MockBackendFactory : public net::HttpCache::BackendFactory {
+ public:
+  virtual int CreateBackend(disk_cache::Backend** backend,
+                            net::CompletionCallback* callback) {
+    *backend = new MockDiskCache();
+    return net::OK;
+  }
+};
+
 class MockHttpCache {
  public:
-  MockHttpCache() : http_cache_(new MockNetworkLayer(), new MockDiskCache()) {
+  MockHttpCache()
+      : http_cache_(new MockNetworkLayer(), new MockBackendFactory()) {
   }
 
-  explicit MockHttpCache(disk_cache::Backend* disk_cache)
-      : http_cache_(new MockNetworkLayer(), disk_cache) {
+  explicit MockHttpCache(net::HttpCache::BackendFactory* disk_cache_factory)
+      : http_cache_(new MockNetworkLayer(), disk_cache_factory) {
   }
 
   net::HttpCache* http_cache() { return &http_cache_; }
@@ -544,13 +557,128 @@
     return static_cast<MockNetworkLayer*>(http_cache_.network_layer());
   }
   MockDiskCache* disk_cache() {
-    return static_cast<MockDiskCache*>(http_cache_.GetBackend());
+    TestCompletionCallback cb;
+    disk_cache::Backend* backend;
+    int rv = http_cache_.GetBackend(&backend, &cb);
+    rv = cb.GetResult(rv);
+    return (rv == net::OK) ? static_cast<MockDiskCache*>(backend) : NULL;
+  }
+
+  // Helper function for reading response info from the disk cache.
+  static bool ReadResponseInfo(disk_cache::Entry* disk_entry,
+                               net::HttpResponseInfo* response_info,
+                               bool* response_truncated) {
+    int size = disk_entry->GetDataSize(0);
+
+    TestCompletionCallback cb;
+    scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(size);
+    int rv = disk_entry->ReadData(0, 0, buffer, size, &cb);
+    rv = cb.GetResult(rv);
+    EXPECT_EQ(size, rv);
+
+    return net::HttpCache::ParseResponseInfo(buffer->data(), size,
+                                             response_info,
+                                             response_truncated);
+  }
+
+  // Helper function for writing response info into the disk cache.
+  static bool WriteResponseInfo(disk_cache::Entry* disk_entry,
+                                const net::HttpResponseInfo* response_info,
+                                bool skip_transient_headers,
+                                bool response_truncated) {
+    Pickle pickle;
+    response_info->Persist(
+        &pickle, skip_transient_headers, response_truncated);
+
+    TestCompletionCallback cb;
+    scoped_refptr<net::WrappedIOBuffer> data = new net::WrappedIOBuffer(
+        reinterpret_cast<const char*>(pickle.data()));
+    int len = static_cast<int>(pickle.size());
+
+    int rv =  disk_entry->WriteData(0, 0, data, len, &cb, true);
+    rv = cb.GetResult(rv);
+    return (rv == len);
+  }
+
+  // Helper function to synchronously open a backend entry.
+  bool OpenBackendEntry(const std::string& key, disk_cache::Entry** entry) {
+    TestCompletionCallback cb;
+    int rv = disk_cache()->OpenEntry(key, entry, &cb);
+    return (cb.GetResult(rv) == net::OK);
+  }
+
+  // Helper function to synchronously create a backend entry.
+  bool CreateBackendEntry(const std::string& key, disk_cache::Entry** entry) {
+    TestCompletionCallback cb;
+    int rv = disk_cache()->CreateEntry(key, entry, &cb);
+    return (cb.GetResult(rv) == net::OK);
   }
 
  private:
   net::HttpCache http_cache_;
 };
 
+// This version of the disk cache doesn't invoke CreateEntry callbacks.
+class MockDiskCacheNoCB : public MockDiskCache {
+  virtual int CreateEntry(const std::string& key, disk_cache::Entry** entry,
+                          net::CompletionCallback* callback) {
+    return net::ERR_IO_PENDING;
+  }
+};
+
+class MockBackendNoCbFactory : public net::HttpCache::BackendFactory {
+ public:
+  virtual int CreateBackend(disk_cache::Backend** backend,
+                            net::CompletionCallback* callback) {
+    *backend = new MockDiskCacheNoCB();
+    return net::OK;
+  }
+};
+
+// This backend factory allows us to control the backend instantiation.
+class MockBlockingBackendFactory : public net::HttpCache::BackendFactory {
+ public:
+  MockBlockingBackendFactory()
+      : backend_(NULL), callback_(NULL), block_(true), fail_(false) {}
+
+  virtual int CreateBackend(disk_cache::Backend** backend,
+                            net::CompletionCallback* callback) {
+    if (!block_) {
+      if (!fail_)
+        *backend = new MockDiskCache();
+      return Result();
+    }
+
+    backend_ =  backend;
+    callback_ = callback;
+    return net::ERR_IO_PENDING;
+  }
+
+  // Completes the backend creation. Any blocked call will be notified via the
+  // provided callback.
+  void FinishCreation() {
+    block_ = false;
+    if (callback_) {
+      if (!fail_)
+        *backend_ = new MockDiskCache();
+      net::CompletionCallback* cb = callback_;
+      callback_ = NULL;
+      cb->Run(Result());  // This object can be deleted here.
+    }
+  }
+
+  void set_fail(bool fail) { fail_ = fail; }
+
+  net::CompletionCallback* callback() { return callback_; }
+
+ private:
+  int Result() { return fail_ ? net::ERR_FAILED : net::OK; }
+
+  disk_cache::Backend** backend_;
+  net::CompletionCallback* callback_;
+  bool block_;
+  bool fail_;
+};
 
 //-----------------------------------------------------------------------------
 // helpers
@@ -569,7 +697,7 @@
                                          const MockTransaction& trans_info,
                                          const MockHttpRequest& request,
                                          net::HttpResponseInfo* response_info,
-                                         net::LoadLog* load_log) {
+                                         const net::BoundNetLog& net_log) {
   TestCompletionCallback callback;
 
   // write to the cache
@@ -579,7 +707,7 @@
   EXPECT_EQ(net::OK, rv);
   ASSERT_TRUE(trans.get());
 
-  rv = trans->Start(&request, &callback, load_log);
+  rv = trans->Start(&request, &callback, net_log);
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   ASSERT_EQ(net::OK, rv);
@@ -598,19 +726,19 @@
                                    const MockHttpRequest& request,
                                    net::HttpResponseInfo* response_info) {
   RunTransactionTestWithRequestAndLog(cache, trans_info, request,
-                                      response_info, NULL);
+                                      response_info, net::BoundNetLog());
 }
 
 void RunTransactionTestWithLog(net::HttpCache* cache,
                                const MockTransaction& trans_info,
-                               net::LoadLog* log) {
+                               const net::BoundNetLog& log) {
   RunTransactionTestWithRequestAndLog(
       cache, trans_info, MockHttpRequest(trans_info), NULL, log);
 }
 
 void RunTransactionTest(net::HttpCache* cache,
                         const MockTransaction& trans_info) {
-  RunTransactionTestWithLog(cache, trans_info, NULL);
+  RunTransactionTestWithLog(cache, trans_info, net::BoundNetLog());
 }
 
 void RunTransactionTestWithResponseInfo(net::HttpCache* cache,
@@ -708,20 +836,21 @@
 bool RangeTransactionServer::bad_200_ = false;
 
 // A dummy extra header that must be preserved on a given request.
-#define EXTRA_HEADER "Extra: header\r\n"
+#define EXTRA_HEADER "Extra: header"
+static const char kExtraHeaderKey[] = "Extra";
 
 // Static.
 void RangeTransactionServer::RangeHandler(const net::HttpRequestInfo* request,
                                           std::string* response_status,
                                           std::string* response_headers,
                                           std::string* response_data) {
-  if (request->extra_headers.empty()) {
+  if (request->extra_headers.IsEmpty()) {
     response_status->assign("HTTP/1.1 416 Requested Range Not Satisfiable");
     return;
   }
 
   // We want to make sure we don't delete extra headers.
-  EXPECT_TRUE(request->extra_headers.find(EXTRA_HEADER) != std::string::npos);
+  EXPECT_TRUE(request->extra_headers.HasHeader(kExtraHeaderKey));
 
   if (not_modified_) {
     response_status->assign("HTTP/1.1 304 Not Modified");
@@ -729,7 +858,10 @@
   }
 
   std::vector<net::HttpByteRange> ranges;
-  if (!net::HttpUtil::ParseRanges(request->extra_headers, &ranges) ||
+  std::string range_header;
+  if (!request->extra_headers.GetHeader(
+          net::HttpRequestHeaders::kRange, &range_header) ||
+      !net::HttpUtil::ParseRangeHeader(range_header, &ranges) ||
       ranges.size() != 1)
     return;
   // We can handle this range request.
@@ -745,19 +877,11 @@
 
   EXPECT_LT(end, 80);
 
-  size_t if_range_header = request->extra_headers.find("If-Range");
-  if (std::string::npos != if_range_header) {
-    // Check that If-Range isn't specified twice.
-    EXPECT_EQ(std::string::npos,
-              request->extra_headers.find("If-Range", if_range_header + 1));
-  }
-
   std::string content_range = StringPrintf("Content-Range: bytes %d-%d/80\n",
                                            start, end);
   response_headers->append(content_range);
 
-  if (request->extra_headers.find("If-None-Match") == std::string::npos ||
-      modified_) {
+  if (!request->extra_headers.HasHeader("If-None-Match") || modified_) {
     EXPECT_EQ(9, (end - start) % 10);
     std::string data;
     for (int block_start = start; block_start < end; block_start += 10)
@@ -801,33 +925,25 @@
   0
 };
 
-// Returns true if the response headers (|response|) match a partial content
+// Verifies the response headers (|response|) match a partial content
 // response for the range starting at |start| and ending at |end|.
-bool Verify206Response(std::string response, int start, int end) {
+void Verify206Response(std::string response, int start, int end) {
   std::string raw_headers(net::HttpUtil::AssembleRawHeaders(response.data(),
                                                             response.size()));
   scoped_refptr<net::HttpResponseHeaders> headers =
       new net::HttpResponseHeaders(raw_headers);
 
-  if (206 != headers->response_code())
-    return false;
+  ASSERT_EQ(206, headers->response_code());
 
   int64 range_start, range_end, object_size;
-  if (!headers->GetContentRange(&range_start, &range_end, &object_size))
-    return false;
+  ASSERT_TRUE(
+      headers->GetContentRange(&range_start, &range_end, &object_size));
   int64 content_length = headers->GetContentLength();
 
   int length = end - start + 1;
-  if (content_length != length)
-    return false;
-
-  if (range_start != start)
-    return false;
-
-  if (range_end != end)
-    return false;
-
-  return true;
+  ASSERT_EQ(length, content_length);
+  ASSERT_EQ(start, range_start);
+  ASSERT_EQ(end, range_end);
 }
 
 // Helper to represent a network HTTP response.
@@ -862,7 +978,6 @@
 //-----------------------------------------------------------------------------
 // tests
 
-
 TEST(HttpCache, CreateThenDestroy) {
   MockHttpCache cache;
 
@@ -873,12 +988,13 @@
 }
 
 TEST(HttpCache, GetBackend) {
-  // This will initialize a cache object with NULL backend.
-  MockHttpCache cache(NULL);
+  MockHttpCache cache(net::HttpCache::DefaultBackend::InMemory(0));
 
+  disk_cache::Backend* backend;
+  TestCompletionCallback cb;
   // This will lazily initialize the backend.
-  cache.http_cache()->set_type(net::MEMORY_CACHE);
-  EXPECT_TRUE(cache.http_cache()->GetBackend());
+  int rv = cache.http_cache()->GetBackend(&backend, &cb);
+  EXPECT_EQ(net::OK, cb.GetResult(rv));
 }
 
 TEST(HttpCache, SimpleGET) {
@@ -897,28 +1013,47 @@
 
   cache.disk_cache()->set_fail_requests();
 
-  scoped_refptr<net::LoadLog> log(new net::LoadLog(net::LoadLog::kUnbounded));
+  net::CapturingBoundNetLog log(net::CapturingNetLog::kUnbounded);
 
   // Read from the network, and don't use the cache.
-  RunTransactionTestWithLog(cache.http_cache(), kSimpleGET_Transaction, log);
+  RunTransactionTestWithLog(cache.http_cache(), kSimpleGET_Transaction,
+                            log.bound());
 
-  // Check that the LoadLog was filled as expected.
+  // Check that the NetLog was filled as expected.
   // (We attempted to both Open and Create entries, but both failed).
-  EXPECT_EQ(4u, log->entries().size());
+  EXPECT_EQ(6u, log.entries().size());
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 0, net::LoadLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+      log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_WAITING));
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, 1, net::LoadLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+      log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_WAITING));
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 2, net::LoadLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+      log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, 3, net::LoadLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+      log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+  EXPECT_TRUE(net::LogContainsBeginEvent(
+      log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+  EXPECT_TRUE(net::LogContainsEndEvent(
+      log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
 
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(0, cache.disk_cache()->create_count());
 }
 
+TEST(HttpCache, SimpleGETNoDiskCache2) {
+  // This will initialize a cache object with NULL backend.
+  MockBlockingBackendFactory* factory = new MockBlockingBackendFactory();
+  factory->set_fail(true);
+  factory->FinishCreation();  // We'll complete synchronously.
+  MockHttpCache cache(factory);
+
+  // Read from the network, and don't use the cache.
+  RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_FALSE(cache.http_cache()->GetCurrentBackend());
+}
+
 TEST(HttpCache, SimpleGETWithDiskFailures) {
   MockHttpCache cache;
 
@@ -950,7 +1085,7 @@
   int rv = cache.http_cache()->CreateTransaction(&c->trans);
   EXPECT_EQ(net::OK, rv);
 
-  rv = c->trans->Start(&request, &c->callback, NULL);
+  rv = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   EXPECT_EQ(net::ERR_IO_PENDING, rv);
   rv = c->callback.WaitForResult();
 
@@ -959,8 +1094,7 @@
 
   // We have to open the entry again to propagate the failure flag.
   disk_cache::Entry* en;
-  ASSERT_EQ(net::OK, cache.disk_cache()->OpenEntry(kSimpleGET_Transaction.url,
-                                                   &en, NULL));
+  ASSERT_TRUE(cache.OpenBackendEntry(kSimpleGET_Transaction.url, &en));
   en->Close();
 
   ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
@@ -981,48 +1115,57 @@
 TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit) {
   MockHttpCache cache;
 
-  scoped_refptr<net::LoadLog> log(new net::LoadLog(net::LoadLog::kUnbounded));
+  net::CapturingBoundNetLog log(net::CapturingNetLog::kUnbounded);
 
   // write to the cache
-  RunTransactionTestWithLog(cache.http_cache(), kSimpleGET_Transaction, log);
+  RunTransactionTestWithLog(cache.http_cache(), kSimpleGET_Transaction,
+                            log.bound());
 
-  // Check that the LoadLog was filled as expected.
-  EXPECT_EQ(6u, log->entries().size());
+  // Check that the NetLog was filled as expected.
+  EXPECT_EQ(8u, log.entries().size());
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 0, net::LoadLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+      log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_WAITING));
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, 1, net::LoadLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+      log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_WAITING));
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 2, net::LoadLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+      log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, 3, net::LoadLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+      log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 4, net::LoadLog::TYPE_HTTP_CACHE_WAITING));
+      log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, 5, net::LoadLog::TYPE_HTTP_CACHE_WAITING));
+      log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+  EXPECT_TRUE(net::LogContainsBeginEvent(
+      log.entries(), 6, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+  EXPECT_TRUE(net::LogContainsEndEvent(
+      log.entries(), 7, net::NetLog::TYPE_HTTP_CACHE_WAITING));
 
   // force this transaction to read from the cache
   MockTransaction transaction(kSimpleGET_Transaction);
   transaction.load_flags |= net::LOAD_ONLY_FROM_CACHE;
 
-  log = new net::LoadLog(net::LoadLog::kUnbounded);
+  log.Clear();
 
-  RunTransactionTestWithLog(cache.http_cache(), transaction, log);
+  RunTransactionTestWithLog(cache.http_cache(), transaction, log.bound());
 
-  // Check that the LoadLog was filled as expected.
-  EXPECT_EQ(6u, log->entries().size());
+  // Check that the NetLog was filled as expected.
+  EXPECT_EQ(8u, log.entries().size());
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 0, net::LoadLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+      log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_WAITING));
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, 1, net::LoadLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
+      log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_WAITING));
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 2, net::LoadLog::TYPE_HTTP_CACHE_WAITING));
+      log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, 3, net::LoadLog::TYPE_HTTP_CACHE_WAITING));
+      log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY));
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 4, net::LoadLog::TYPE_HTTP_CACHE_READ_INFO));
+      log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_WAITING));
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, 5, net::LoadLog::TYPE_HTTP_CACHE_READ_INFO));
+      log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+  EXPECT_TRUE(net::LogContainsBeginEvent(
+      log.entries(), 6, net::NetLog::TYPE_HTTP_CACHE_READ_INFO));
+  EXPECT_TRUE(net::LogContainsEndEvent(
+      log.entries(), 7, net::NetLog::TYPE_HTTP_CACHE_READ_INFO));
 
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
@@ -1044,7 +1187,7 @@
   EXPECT_EQ(net::OK, rv);
   ASSERT_TRUE(trans.get());
 
-  rv = trans->Start(&request, &callback, NULL);
+  rv = trans->Start(&request, &callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   ASSERT_EQ(net::ERR_CACHE_MISS, rv);
@@ -1090,14 +1233,35 @@
 TEST(HttpCache, SimpleGET_LoadBypassCache) {
   MockHttpCache cache;
 
-  // write to the cache
+  // Write to the cache.
   RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction);
 
-  // force this transaction to write to the cache again
+  // Force this transaction to write to the cache again.
   MockTransaction transaction(kSimpleGET_Transaction);
   transaction.load_flags |= net::LOAD_BYPASS_CACHE;
 
-  RunTransactionTest(cache.http_cache(), transaction);
+  net::CapturingBoundNetLog log(net::CapturingNetLog::kUnbounded);
+
+  RunTransactionTestWithLog(cache.http_cache(), transaction, log.bound());
+
+  // Check that the NetLog was filled as expected.
+  EXPECT_EQ(8u, log.entries().size());
+  EXPECT_TRUE(net::LogContainsBeginEvent(
+      log.entries(), 0, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+  EXPECT_TRUE(net::LogContainsEndEvent(
+      log.entries(), 1, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+  EXPECT_TRUE(net::LogContainsBeginEvent(
+      log.entries(), 2, net::NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY));
+  EXPECT_TRUE(net::LogContainsEndEvent(
+      log.entries(), 3, net::NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY));
+  EXPECT_TRUE(net::LogContainsBeginEvent(
+      log.entries(), 4, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+  EXPECT_TRUE(net::LogContainsEndEvent(
+      log.entries(), 5, net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY));
+  EXPECT_TRUE(net::LogContainsBeginEvent(
+      log.entries(), 6, net::NetLog::TYPE_HTTP_CACHE_WAITING));
+  EXPECT_TRUE(net::LogContainsEndEvent(
+      log.entries(), 7, net::NetLog::TYPE_HTTP_CACHE_WAITING));
 
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -1183,7 +1347,7 @@
     std::string* response_status,
     std::string* response_headers,
     std::string* response_data) {
-  EXPECT_TRUE(request->extra_headers.find(EXTRA_HEADER) != std::string::npos);
+  EXPECT_TRUE(request->extra_headers.HasHeader(kExtraHeaderKey));
 }
 
 // Tests that we don't remove extra headers for simple requests.
@@ -1215,7 +1379,7 @@
 
   MockTransaction transaction(kETagGET_Transaction);
   transaction.handler = PreserveRequestHeaders_Handler;
-  transaction.request_headers = "If-None-Match: \"foopy\"\n"
+  transaction.request_headers = "If-None-Match: \"foopy\"\r\n"
                                 EXTRA_HEADER;
   AddMockTransaction(&transaction);
 
@@ -1241,8 +1405,15 @@
 
     c->result = cache.http_cache()->CreateTransaction(&c->trans);
     EXPECT_EQ(net::OK, c->result);
+    EXPECT_EQ(net::LOAD_STATE_IDLE, c->trans->GetLoadState());
 
-    c->result = c->trans->Start(&request, &c->callback, NULL);
+    c->result = c->trans->Start(&request, &c->callback, net::BoundNetLog());
+  }
+
+  // All requests are waiting for the active entry.
+  for (int i = 0; i < kNumTransactions; ++i) {
+    Context* c = context_list[i];
+    EXPECT_EQ(net::LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
   }
 
   // Allow all requests to move from the Create queue to the active entry.
@@ -1255,6 +1426,13 @@
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
+  // All requests depend on the writer, and the writer is between Start and
+  // Read, i.e. idle.
+  for (int i = 0; i < kNumTransactions; ++i) {
+    Context* c = context_list[i];
+    EXPECT_EQ(net::LOAD_STATE_IDLE, c->trans->GetLoadState());
+  }
+
   for (int i = 0; i < kNumTransactions; ++i) {
     Context* c = context_list[i];
     if (c->result == net::ERR_IO_PENDING)
@@ -1299,7 +1477,7 @@
     if (i == 1 || i == 2)
       this_request = &reader_request;
 
-    c->result = c->trans->Start(this_request, &c->callback, NULL);
+    c->result = c->trans->Start(this_request, &c->callback, net::BoundNetLog());
   }
 
   // Allow all requests to move from the Create queue to the active entry.
@@ -1319,6 +1497,11 @@
 
   // Now we have 2 active readers and two queued transactions.
 
+  EXPECT_EQ(net::LOAD_STATE_IDLE,
+            context_list[2]->trans->GetLoadState());
+  EXPECT_EQ(net::LOAD_STATE_WAITING_FOR_CACHE,
+            context_list[3]->trans->GetLoadState());
+
   c = context_list[1];
   ASSERT_EQ(net::ERR_IO_PENDING, c->result);
   c->result = c->callback.WaitForResult();
@@ -1358,9 +1541,7 @@
 // See http://code.google.com/p/chromium/issues/detail?id=25588
 TEST(HttpCache, SimpleGET_DoomWithPending) {
   // We need simultaneous doomed / not_doomed entries so let's use a real cache.
-  disk_cache::Backend* disk_cache =
-      disk_cache::CreateInMemoryCacheBackend(1024 * 1024);
-  MockHttpCache cache(disk_cache);
+  MockHttpCache cache(net::HttpCache::DefaultBackend::InMemory(1024 * 1024));
 
   MockHttpRequest request(kSimpleGET_Transaction);
   MockHttpRequest writer_request(kSimpleGET_Transaction);
@@ -1380,7 +1561,7 @@
     if (i == 3)
       this_request = &writer_request;
 
-    c->result = c->trans->Start(this_request, &c->callback, NULL);
+    c->result = c->trans->Start(this_request, &c->callback, net::BoundNetLog());
   }
 
   // The first request should be a writer at this point, and the two subsequent
@@ -1423,7 +1604,7 @@
     c->result = cache.http_cache()->CreateTransaction(&c->trans);
     EXPECT_EQ(net::OK, c->result);
 
-    c->result = c->trans->Start(&request, &c->callback, NULL);
+    c->result = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   }
 
   // Allow all requests to move from the Create queue to the active entry.
@@ -1469,7 +1650,7 @@
     c->result = cache.http_cache()->CreateTransaction(&c->trans);
     EXPECT_EQ(net::OK, c->result);
 
-    c->result = c->trans->Start(&request, &c->callback, NULL);
+    c->result = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   }
 
   // Allow all requests to move from the Create queue to the active entry.
@@ -1528,7 +1709,7 @@
     c->result = cache.http_cache()->CreateTransaction(&c->trans);
     EXPECT_EQ(net::OK, c->result);
 
-    c->result = c->trans->Start(&request, &c->callback, NULL);
+    c->result = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   }
 
   // The first request should be creating the disk cache entry and the others
@@ -1567,6 +1748,29 @@
   }
 }
 
+// Tests that we can cancel a single request to open a disk cache entry.
+TEST(HttpCache, SimpleGET_CancelCreate) {
+  MockHttpCache cache;
+
+  MockHttpRequest request(kSimpleGET_Transaction);
+
+  Context* c = new Context();
+
+  c->result = cache.http_cache()->CreateTransaction(&c->trans);
+  EXPECT_EQ(net::OK, c->result);
+
+  c->result = c->trans->Start(&request, &c->callback, net::BoundNetLog());
+  EXPECT_EQ(net::ERR_IO_PENDING, c->result);
+
+  // Release the reference that the mock disk cache keeps for this entry, so
+  // that we test that the http cache handles the cancelation correctly.
+  cache.disk_cache()->ReleaseAll();
+  delete c;
+
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
 // Tests that we delete/create entries even if multiple requests are queued.
 TEST(HttpCache, SimpleGET_ManyWriters_BypassCache) {
   MockHttpCache cache;
@@ -1584,7 +1788,7 @@
     c->result = cache.http_cache()->CreateTransaction(&c->trans);
     EXPECT_EQ(net::OK, c->result);
 
-    c->result = c->trans->Start(&request, &c->callback, NULL);
+    c->result = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   }
 
   // The first request should be deleting the disk cache entry and the others
@@ -1624,7 +1828,7 @@
   scoped_ptr<net::HttpTransaction> trans;
   int rv = cache.http_cache()->CreateTransaction(&trans);
   EXPECT_EQ(net::OK, rv);
-  rv = trans->Start(&request, &callback, NULL);
+  rv = trans->Start(&request, &callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   ASSERT_EQ(net::OK, rv);
@@ -1642,6 +1846,172 @@
   MessageLoop::current()->RunAllPending();
 }
 
+// Tests that we can delete the HttpCache and deal with queued transactions
+// ("waiting for the backend" as opposed to Active or Doomed entries).
+TEST(HttpCache, SimpleGET_ManyWriters_DeleteCache) {
+  scoped_ptr<MockHttpCache> cache(new MockHttpCache(
+                                      new MockBackendNoCbFactory()));
+
+  MockHttpRequest request(kSimpleGET_Transaction);
+
+  std::vector<Context*> context_list;
+  const int kNumTransactions = 5;
+
+  for (int i = 0; i < kNumTransactions; i++) {
+    context_list.push_back(new Context());
+    Context* c = context_list[i];
+
+    c->result = cache->http_cache()->CreateTransaction(&c->trans);
+    EXPECT_EQ(net::OK, c->result);
+
+    c->result = c->trans->Start(&request, &c->callback, net::BoundNetLog());
+  }
+
+  // The first request should be creating the disk cache entry and the others
+  // should be pending.
+
+  EXPECT_EQ(0, cache->network_layer()->transaction_count());
+  EXPECT_EQ(0, cache->disk_cache()->open_count());
+  EXPECT_EQ(0, cache->disk_cache()->create_count());
+
+  cache.reset();
+
+  // There is not much to do with the transactions at this point... they are
+  // waiting for a callback that will not fire.
+  for (int i = 0; i < kNumTransactions; ++i) {
+    delete context_list[i];
+  }
+}
+
+// Tests that we queue requests when initializing the backend.
+TEST(HttpCache, SimpleGET_WaitForBackend) {
+  MockBlockingBackendFactory* factory = new MockBlockingBackendFactory();
+  MockHttpCache cache(factory);
+
+  MockHttpRequest request0(kSimpleGET_Transaction);
+  MockHttpRequest request1(kTypicalGET_Transaction);
+  MockHttpRequest request2(kETagGET_Transaction);
+
+  std::vector<Context*> context_list;
+  const int kNumTransactions = 3;
+
+  for (int i = 0; i < kNumTransactions; i++) {
+    context_list.push_back(new Context());
+    Context* c = context_list[i];
+
+    c->result = cache.http_cache()->CreateTransaction(&c->trans);
+    EXPECT_EQ(net::OK, c->result);
+  }
+
+  context_list[0]->result = context_list[0]->trans->Start(
+      &request0, &context_list[0]->callback, net::BoundNetLog());
+  context_list[1]->result = context_list[1]->trans->Start(
+      &request1, &context_list[1]->callback, net::BoundNetLog());
+  context_list[2]->result = context_list[2]->trans->Start(
+      &request2, &context_list[2]->callback, net::BoundNetLog());
+
+  // Just to make sure that everything is still pending.
+  MessageLoop::current()->RunAllPending();
+
+  // The first request should be creating the disk cache.
+  EXPECT_FALSE(context_list[0]->callback.have_result());
+
+  factory->FinishCreation();
+
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(3, cache.network_layer()->transaction_count());
+  EXPECT_EQ(3, cache.disk_cache()->create_count());
+
+  for (int i = 0; i < kNumTransactions; ++i) {
+    EXPECT_TRUE(context_list[i]->callback.have_result());
+    delete context_list[i];
+  }
+}
+
+// Tests that we can cancel requests that are queued waiting for the backend
+// to be initialized.
+TEST(HttpCache, SimpleGET_WaitForBackend_CancelCreate) {
+  MockBlockingBackendFactory* factory = new MockBlockingBackendFactory();
+  MockHttpCache cache(factory);
+
+  MockHttpRequest request0(kSimpleGET_Transaction);
+  MockHttpRequest request1(kTypicalGET_Transaction);
+  MockHttpRequest request2(kETagGET_Transaction);
+
+  std::vector<Context*> context_list;
+  const int kNumTransactions = 3;
+
+  for (int i = 0; i < kNumTransactions; i++) {
+    context_list.push_back(new Context());
+    Context* c = context_list[i];
+
+    c->result = cache.http_cache()->CreateTransaction(&c->trans);
+    EXPECT_EQ(net::OK, c->result);
+  }
+
+  context_list[0]->result = context_list[0]->trans->Start(
+      &request0, &context_list[0]->callback, net::BoundNetLog());
+  context_list[1]->result = context_list[1]->trans->Start(
+      &request1, &context_list[1]->callback, net::BoundNetLog());
+  context_list[2]->result = context_list[2]->trans->Start(
+      &request2, &context_list[2]->callback, net::BoundNetLog());
+
+  // Just to make sure that everything is still pending.
+  MessageLoop::current()->RunAllPending();
+
+  // The first request should be creating the disk cache.
+  EXPECT_FALSE(context_list[0]->callback.have_result());
+
+  // Cancel a request from the pending queue.
+  delete context_list[1];
+  context_list[1] = NULL;
+
+  // Cancel the request that is creating the entry.
+  delete context_list[0];
+  context_list[0] = NULL;
+
+  // Complete the last transaction.
+  factory->FinishCreation();
+
+  context_list[2]->result =
+      context_list[2]->callback.GetResult(context_list[2]->result);
+  ReadAndVerifyTransaction(context_list[2]->trans.get(), kETagGET_Transaction);
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  delete context_list[2];
+}
+
+// Tests that we can delete the cache while creating the backend.
+TEST(HttpCache, DeleteCacheWaitingForBackend) {
+  MockBlockingBackendFactory* factory = new MockBlockingBackendFactory();
+  scoped_ptr<MockHttpCache> cache(new MockHttpCache(factory));
+
+  MockHttpRequest request(kSimpleGET_Transaction);
+
+  scoped_ptr<Context> c(new Context());
+  c->result = cache->http_cache()->CreateTransaction(&c->trans);
+  EXPECT_EQ(net::OK, c->result);
+
+  c->trans->Start(&request, &c->callback, net::BoundNetLog());
+
+  // Just to make sure that everything is still pending.
+  MessageLoop::current()->RunAllPending();
+
+  // The request should be creating the disk cache.
+  EXPECT_FALSE(c->callback.have_result());
+
+  // We cannot call FinishCreation because the factory itself will go away with
+  // the cache, so grab the callback and attempt to use it.
+  net::CompletionCallback* callback = factory->callback();
+
+  cache.reset();
+  MessageLoop::current()->RunAllPending();
+
+  callback->Run(net::ERR_ABORTED);
+}
+
 TEST(HttpCache, TypicalGET_ConditionalRequest) {
   MockHttpCache cache;
 
@@ -1666,8 +2036,8 @@
     std::string* response_status,
     std::string* response_headers,
     std::string* response_data) {
-  EXPECT_TRUE(request->extra_headers.find("If-None-Match") !=
-                  std::string::npos);
+  EXPECT_TRUE(
+      request->extra_headers.HasHeader(net::HttpRequestHeaders::kIfNoneMatch));
   response_status->assign("HTTP/1.1 304 Not Modified");
   response_headers->assign(kETagGET_Transaction.response_headers);
   response_data->clear();
@@ -1701,8 +2071,8 @@
     std::string* response_status,
     std::string* response_headers,
     std::string* response_data) {
-  EXPECT_TRUE(request->extra_headers.find("If-None-Match") !=
-              std::string::npos);
+  EXPECT_TRUE(
+      request->extra_headers.HasHeader(net::HttpRequestHeaders::kIfNoneMatch));
   response_status->assign("HTTP/1.1 304 Not Modified");
   response_headers->assign("Cache-Control: no-store\n");
   response_data->clear();
@@ -1967,7 +2337,7 @@
   };
 
   const char* kExtraRequestHeaders =
-      "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT\n";
+      "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT";
 
   // We will control the network layer's responses for |kUrl| using
   // |mock_network_response|.
@@ -2011,7 +2381,7 @@
   };
 
   const char* kExtraRequestHeaders =
-      "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT\n";
+      "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT";
 
   // We will control the network layer's responses for |kUrl| using
   // |mock_network_response|.
@@ -2119,8 +2489,8 @@
   };
 
   const char* kExtraRequestHeaders =
-    "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT\n"
-    "If-None-Match: \"Foo1\"\n";
+      "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT\r\n"
+      "If-None-Match: \"Foo1\"\r\n";
 
   ConditionalizedRequestUpdatesCacheHelper(
       kNetResponse1, kNetResponse2, kNetResponse2, kExtraRequestHeaders);
@@ -2148,8 +2518,8 @@
 
   // The etag doesn't match what we have stored.
   const char* kExtraRequestHeaders =
-    "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT\n"
-    "If-None-Match: \"Foo2\"\n";
+      "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT\n"
+      "If-None-Match: \"Foo2\"\n";
 
   ConditionalizedRequestUpdatesCacheHelper(
       kNetResponse1, kNetResponse2, kNetResponse1, kExtraRequestHeaders);
@@ -2177,8 +2547,8 @@
 
   // The modification date doesn't match what we have stored.
   const char* kExtraRequestHeaders =
-    "If-Modified-Since: Fri, 08 Feb 2008 22:38:21 GMT\n"
-    "If-None-Match: \"Foo1\"\n";
+      "If-Modified-Since: Fri, 08 Feb 2008 22:38:21 GMT\n"
+      "If-None-Match: \"Foo1\"\n";
 
   ConditionalizedRequestUpdatesCacheHelper(
       kNetResponse1, kNetResponse2, kNetResponse1, kExtraRequestHeaders);
@@ -2206,8 +2576,8 @@
 
   // Two dates, the second matches what we have stored.
   const char* kExtraRequestHeaders =
-    "If-Modified-Since: Mon, 04 Feb 2008 22:38:21 GMT\n"
-    "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT\n";
+      "If-Modified-Since: Mon, 04 Feb 2008 22:38:21 GMT\n"
+      "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT\n";
 
   ConditionalizedRequestUpdatesCacheHelper(
       kNetResponse1, kNetResponse2, kNetResponse1, kExtraRequestHeaders);
@@ -2255,7 +2625,7 @@
   EXPECT_EQ(net::OK, rv);
   ASSERT_TRUE(trans.get());
 
-  rv = trans->Start(&request, &callback, NULL);
+  rv = trans->Start(&request, &callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   ASSERT_EQ(net::ERR_CACHE_MISS, rv);
@@ -2334,9 +2704,9 @@
   cache.http_cache()->set_enable_range_support(true);
 
   MockTransaction transaction(kRangeGET_Transaction);
-  transaction.request_headers = "If-None-Match: foo\n"
+  transaction.request_headers = "If-None-Match: foo\r\n"
                                 EXTRA_HEADER
-                                "Range: bytes = 40-49\n";
+                                "\r\nRange: bytes = 40-49";
   RunTransactionTest(cache.http_cache(), transaction);
 
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
@@ -2344,18 +2714,18 @@
   EXPECT_EQ(0, cache.disk_cache()->create_count());
 
   transaction.request_headers =
-      "If-Modified-Since: Wed, 28 Nov 2007 00:45:20 GMT\n"
+      "If-Modified-Since: Wed, 28 Nov 2007 00:45:20 GMT\r\n"
       EXTRA_HEADER
-      "Range: bytes = 40-49\n";
+      "\r\nRange: bytes = 40-49";
   RunTransactionTest(cache.http_cache(), transaction);
 
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(0, cache.disk_cache()->create_count());
 
-  transaction.request_headers = "If-Range: bla\n"
+  transaction.request_headers = "If-Range: bla\r\n"
                                 EXTRA_HEADER
-                                "Range: bytes = 40-49\n";
+                                "\r\nRange: bytes = 40-49\n";
   RunTransactionTest(cache.http_cache(), transaction);
 
   EXPECT_EQ(3, cache.network_layer()->transaction_count());
@@ -2400,7 +2770,7 @@
   RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
                                  &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2409,8 +2779,8 @@
   RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
                                  &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
-  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  Verify206Response(headers, 40, 49);
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
@@ -2423,8 +2793,8 @@
   transaction.data = "rg: 30-39 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 30, 39));
-  EXPECT_EQ(3, cache.network_layer()->transaction_count());
+  Verify206Response(headers, 30, 39);
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(2, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
@@ -2436,8 +2806,8 @@
   transaction.data = "rg: 20-29 rg: 30-39 rg: 40-49 rg: 50-59 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 20, 59));
-  EXPECT_EQ(5, cache.network_layer()->transaction_count());
+  Verify206Response(headers, 20, 59);
+  EXPECT_EQ(4, cache.network_layer()->transaction_count());
   EXPECT_EQ(3, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
@@ -2458,7 +2828,7 @@
   std::string headers;
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2466,8 +2836,8 @@
   // Read from the cache (40-49).
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
-  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  Verify206Response(headers, 40, 49);
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
@@ -2479,8 +2849,8 @@
   transaction.data = "rg: 30-39 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 30, 39));
-  EXPECT_EQ(3, cache.network_layer()->transaction_count());
+  Verify206Response(headers, 30, 39);
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
@@ -2492,14 +2862,89 @@
   transaction.data = "rg: 20-29 rg: 30-39 rg: 40-49 rg: 50-59 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 20, 59));
-  EXPECT_EQ(5, cache.network_layer()->transaction_count());
+  Verify206Response(headers, 20, 59);
+  EXPECT_EQ(4, cache.network_layer()->transaction_count());
   EXPECT_EQ(2, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   RemoveMockTransaction(&transaction);
 }
 
+// Tests that we don't revalidate an entry unless we are required to do so.
+TEST(HttpCache, RangeGET_Revalidate1) {
+  MockHttpCache cache;
+  cache.http_cache()->set_enable_range_support(true);
+  std::string headers;
+
+  // Write to the cache (40-49).
+  MockTransaction transaction(kRangeGET_TransactionOK);
+  transaction.response_headers =
+      "Last-Modified: Sat, 18 Apr 2009 01:10:43 GMT\n"
+      "Expires: Wed, 7 Sep 2033 21:46:42 GMT\n"  // Should never expire.
+      "ETag: \"foo\"\n"
+      "Accept-Ranges: bytes\n"
+      "Content-Length: 10\n";
+  AddMockTransaction(&transaction);
+  RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+
+  Verify206Response(headers, 40, 49);
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Read from the cache (40-49).
+  RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+  Verify206Response(headers, 40, 49);
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Read again forcing the revalidation.
+  transaction.load_flags |= net::LOAD_VALIDATE_CACHE;
+  RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+
+  Verify206Response(headers, 40, 49);
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  RemoveMockTransaction(&transaction);
+}
+
+// Checks that we revalidate an entry when the headers say so.
+TEST(HttpCache, RangeGET_Revalidate2) {
+  MockHttpCache cache;
+  cache.http_cache()->set_enable_range_support(true);
+  std::string headers;
+
+  // Write to the cache (40-49).
+  MockTransaction transaction(kRangeGET_TransactionOK);
+  transaction.response_headers =
+      "Last-Modified: Sat, 18 Apr 2009 01:10:43 GMT\n"
+      "Expires: Sat, 18 Apr 2009 01:10:43 GMT\n"  // Expired.
+      "ETag: \"foo\"\n"
+      "Accept-Ranges: bytes\n"
+      "Content-Length: 10\n";
+  AddMockTransaction(&transaction);
+  RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+
+  Verify206Response(headers, 40, 49);
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(0, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  // Read from the cache (40-49).
+  RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
+  Verify206Response(headers, 40, 49);
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(1, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+  RemoveMockTransaction(&transaction);
+}
+
 // Tests that we deal with 304s for range requests.
 TEST(HttpCache, RangeGET_304) {
   MockHttpCache cache;
@@ -2511,7 +2956,7 @@
   RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
                                  &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2519,10 +2964,11 @@
   // Read from the cache (40-49).
   RangeTransactionServer handler;
   handler.set_not_modified(true);
-  RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
-                                 &headers);
+  MockTransaction transaction(kRangeGET_TransactionOK);
+  transaction.load_flags |= net::LOAD_VALIDATE_CACHE;
+  RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2541,7 +2987,7 @@
   RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
                                  &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2549,10 +2995,11 @@
   // Attempt to read from the cache (40-49).
   RangeTransactionServer handler;
   handler.set_modified(true);
-  RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
-                                 &headers);
+  MockTransaction transaction(kRangeGET_TransactionOK);
+  transaction.load_flags |= net::LOAD_VALIDATE_CACHE;
+  RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2580,7 +3027,7 @@
   transaction.data = "rg: 70-79 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 70, 79));
+  Verify206Response(headers, 70, 79);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2593,7 +3040,7 @@
   transaction.data = "rg: 60-69 rg: 70-79 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 60, 79));
+  Verify206Response(headers, 60, 79);
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2620,7 +3067,7 @@
   transaction.data = "rg: 70-79 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 70, 79));
+  Verify206Response(headers, 70, 79);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2633,7 +3080,7 @@
   transaction.data = "rg: 60-69 rg: 70-79 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 60, 79));
+  Verify206Response(headers, 60, 79);
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2682,7 +3129,7 @@
   RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
                                  &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2717,13 +3164,14 @@
   // Write to the cache (0-9).
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 0, 9));
+  Verify206Response(headers, 0, 9);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   // Read from the cache (0-9), write and read from cache (10 - 79),
   MockTransaction transaction2(kRangeGET_TransactionOK);
+  transaction2.load_flags |= net::LOAD_VALIDATE_CACHE;
   transaction2.request_headers = "Foo: bar\r\n" EXTRA_HEADER;
   transaction2.data = "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 rg: 40-49 "
                       "rg: 50-59 rg: 60-69 rg: 70-79 ";
@@ -2751,7 +3199,7 @@
   transaction.data = "rg: 00-09 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 0, 9));
+  Verify206Response(headers, 0, 9);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2762,6 +3210,7 @@
   // real server will answer with 200.
   MockTransaction transaction2(kRangeGET_TransactionOK);
   transaction2.request_headers = EXTRA_HEADER;
+  transaction2.load_flags |= net::LOAD_VALIDATE_CACHE;
   transaction2.data = "rg: 40-49 ";
   RangeTransactionServer handler;
   handler.set_modified(true);
@@ -2786,8 +3235,7 @@
 
   // Create a disk cache entry that stores 206 headers while not being sparse.
   disk_cache::Entry* entry;
-  ASSERT_EQ(net::OK, cache.disk_cache()->CreateEntry(kSimpleGET_Transaction.url,
-                                                     &entry, NULL));
+  ASSERT_TRUE(cache.CreateBackendEntry(kSimpleGET_Transaction.url, &entry));
 
   std::string raw_headers(kRangeGET_TransactionOK.status);
   raw_headers.append("\n");
@@ -2797,12 +3245,14 @@
 
   net::HttpResponseInfo response;
   response.headers = new net::HttpResponseHeaders(raw_headers);
-  EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, false));
+  EXPECT_TRUE(MockHttpCache::WriteResponseInfo(entry, &response, true, false));
 
   scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(500));
   int len = static_cast<int>(base::strlcpy(buf->data(),
                                            kRangeGET_TransactionOK.data, 500));
-  EXPECT_EQ(len, entry->WriteData(1, 0, buf, len, NULL, true));
+  TestCompletionCallback cb;
+  int rv = entry->WriteData(1, 0, buf, len, &cb, true);
+  EXPECT_EQ(len, cb.GetResult(rv));
   entry->Close();
 
   // Now see that we don't use the stored entry.
@@ -2829,9 +3279,7 @@
 
   // Create a disk cache entry that stores 206 headers while not being sparse.
   disk_cache::Entry* entry;
-  ASSERT_EQ(net::OK,
-            cache.disk_cache()->CreateEntry(kRangeGET_TransactionOK.url,
-                                            &entry, NULL));
+  ASSERT_TRUE(cache.CreateBackendEntry(kRangeGET_TransactionOK.url, &entry));
 
   std::string raw_headers(kRangeGET_TransactionOK.status);
   raw_headers.append("\n");
@@ -2841,12 +3289,14 @@
 
   net::HttpResponseInfo response;
   response.headers = new net::HttpResponseHeaders(raw_headers);
-  EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, false));
+  EXPECT_TRUE(MockHttpCache::WriteResponseInfo(entry, &response, true, false));
 
   scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(500));
   int len = static_cast<int>(base::strlcpy(buf->data(),
                                            kRangeGET_TransactionOK.data, 500));
-  EXPECT_EQ(len, entry->WriteData(1, 0, buf, len, NULL, true));
+  TestCompletionCallback cb;
+  int rv = entry->WriteData(1, 0, buf, len, &cb, true);
+  EXPECT_EQ(len, cb.GetResult(rv));
   entry->Close();
 
   // Now see that we don't use the stored entry.
@@ -2855,7 +3305,7 @@
                                  &headers);
 
   // We are expecting a 206.
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(2, cache.disk_cache()->create_count());
@@ -2890,7 +3340,7 @@
   RunTransactionTestWithResponse(cache.http_cache(), transaction2, &headers);
 
   // We are expecting a 206.
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2902,7 +3352,7 @@
   handler.set_not_modified(false);
   transaction2.request_headers = kRangeGET_TransactionOK.request_headers;
   RunTransactionTestWithResponse(cache.http_cache(), transaction2, &headers);
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(3, cache.network_layer()->transaction_count());
   EXPECT_EQ(2, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2926,7 +3376,7 @@
   transaction.data = "rg: 70-79 ";
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 70, 79));
+  Verify206Response(headers, 70, 79);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2965,7 +3415,7 @@
   RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
                                  &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -2999,7 +3449,7 @@
   int rv = cache.http_cache()->CreateTransaction(&c->trans);
   EXPECT_EQ(net::OK, rv);
 
-  rv = c->trans->Start(&request, &c->callback, NULL);
+  rv = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = c->callback.WaitForResult();
 
@@ -3019,8 +3469,7 @@
 
   // Verify that the entry has not been deleted.
   disk_cache::Entry* entry;
-  ASSERT_EQ(net::OK, cache.disk_cache()->OpenEntry(kRangeGET_TransactionOK.url,
-                                                   &entry, NULL));
+  ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
   entry->Close();
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 }
@@ -3034,12 +3483,13 @@
 
   RunTransactionTest(cache.http_cache(), kRangeGET_TransactionOK);
   MockHttpRequest request(kRangeGET_TransactionOK);
+  request.load_flags |= net::LOAD_VALIDATE_CACHE;
 
   Context* c = new Context();
   int rv = cache.http_cache()->CreateTransaction(&c->trans);
   EXPECT_EQ(net::OK, rv);
 
-  rv = c->trans->Start(&request, &c->callback, NULL);
+  rv = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = c->callback.WaitForResult();
 
@@ -3064,7 +3514,7 @@
 
   RunTransactionTest(cache.http_cache(), kRangeGET_TransactionOK);
 
-  EXPECT_EQ(3, cache.network_layer()->transaction_count());
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
   RemoveMockTransaction(&kRangeGET_TransactionOK);
@@ -3079,12 +3529,13 @@
 
   RunTransactionTest(cache.http_cache(), kRangeGET_TransactionOK);
   MockHttpRequest request(kRangeGET_TransactionOK);
+  request.load_flags |= net::LOAD_VALIDATE_CACHE;
 
   Context* c = new Context();
   int rv = cache.http_cache()->CreateTransaction(&c->trans);
   EXPECT_EQ(net::OK, rv);
 
-  rv = c->trans->Start(&request, &c->callback, NULL);
+  rv = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   EXPECT_EQ(net::ERR_IO_PENDING, rv);
   rv = c->callback.WaitForResult();
 
@@ -3111,7 +3562,7 @@
   rv = cache.http_cache()->CreateTransaction(&c->trans);
   EXPECT_EQ(net::OK, rv);
 
-  rv = c->trans->Start(&request, &c->callback, NULL);
+  rv = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   EXPECT_EQ(net::ERR_IO_PENDING, rv);
 
   MockDiskEntry::IgnoreCallbacks(true);
@@ -3154,8 +3605,7 @@
 
   // Verify that we don't have a cached entry.
   disk_cache::Entry* entry;
-  EXPECT_NE(net::OK, cache.disk_cache()->OpenEntry(kRangeGET_TransactionOK.url,
-                                                   &entry, NULL));
+  EXPECT_FALSE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
 
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 }
@@ -3184,8 +3634,7 @@
 
   // Verify that we don't have a cached entry.
   disk_cache::Entry* entry;
-  EXPECT_NE(net::OK, cache.disk_cache()->OpenEntry(kRangeGET_TransactionOK.url,
-                                                   &entry, NULL));
+  EXPECT_FALSE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
 
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 }
@@ -3206,7 +3655,7 @@
   AddMockTransaction(&transaction);
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 50, 59));
+  Verify206Response(headers, 50, 59);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
@@ -3219,17 +3668,19 @@
   RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
                                  &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(2, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   // Verify that we cached the first response but not the second one.
   disk_cache::Entry* en;
-  ASSERT_EQ(net::OK, cache.disk_cache()->OpenEntry(kRangeGET_TransactionOK.url,
-                                                   &en, NULL));
+  ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &en));
+
   int64 cached_start = 0;
-  EXPECT_EQ(10, en->GetAvailableRange(40, 20, &cached_start));
+  TestCompletionCallback cb;
+  int rv = en->GetAvailableRange(40, 20, &cached_start, &cb);
+  EXPECT_EQ(10, cb.GetResult(rv));
   EXPECT_EQ(50, cached_start);
   en->Close();
 
@@ -3239,9 +3690,7 @@
 // Tests that we handle large range values properly.
 TEST(HttpCache, RangeGET_LargeValues) {
   // We need a real sparse cache for this test.
-  disk_cache::Backend* disk_cache =
-      disk_cache::CreateInMemoryCacheBackend(1024 * 1024);
-  MockHttpCache cache(disk_cache);
+  MockHttpCache cache(net::HttpCache::DefaultBackend::InMemory(1024 * 1024));
   cache.http_cache()->set_enable_range_support(true);
   std::string headers;
 
@@ -3264,7 +3713,7 @@
 
   // Verify that we have a cached entry.
   disk_cache::Entry* en;
-  ASSERT_TRUE(cache.disk_cache()->OpenEntry(kRangeGET_TransactionOK.url, &en));
+  ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &en));
   en->Close();
 
   RemoveMockTransaction(&kRangeGET_TransactionOK);
@@ -3273,7 +3722,11 @@
 // Tests that we don't crash with a range request if the disk cache was not
 // initialized properly.
 TEST(HttpCache, RangeGET_NoDiskCache) {
-  MockHttpCache cache(NULL);
+  MockBlockingBackendFactory* factory = new MockBlockingBackendFactory();
+  factory->set_fail(true);
+  factory->FinishCreation();  // We'll complete synchronously.
+  MockHttpCache cache(factory);
+
   cache.http_cache()->set_enable_range_support(true);
   AddMockTransaction(&kRangeGET_TransactionOK);
 
@@ -3297,7 +3750,7 @@
   std::string headers;
   RunTransactionTestWithResponse(cache.http_cache(), transaction, &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 70, 79));
+  Verify206Response(headers, 70, 79);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(0, cache.disk_cache()->open_count());
   EXPECT_EQ(0, cache.disk_cache()->create_count());
@@ -3314,6 +3767,7 @@
   MockTransaction transaction(kRangeGET_TransactionOK);
   transaction.request_headers = "Range: bytes = 40-\r\n" EXTRA_HEADER;
   transaction.test_mode = TEST_MODE_SYNC_NET_START;
+  transaction.load_flags |= net::LOAD_VALIDATE_CACHE;
   AddMockTransaction(&transaction);
 
   // Write to the cache.
@@ -3397,7 +3851,7 @@
   EXPECT_EQ(net::OK, rv);
   ASSERT_TRUE(trans.get());
 
-  rv = trans->Start(&request, &callback, NULL);
+  rv = trans->Start(&request, &callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   ASSERT_EQ(net::ERR_CACHE_MISS, rv);
@@ -3416,8 +3870,7 @@
 TEST(HttpCache, WriteResponseInfo_Truncated) {
   MockHttpCache cache;
   disk_cache::Entry* entry;
-  ASSERT_EQ(net::OK, cache.disk_cache()->CreateEntry("http://www.google.com",
-                                                     &entry, NULL));
+  ASSERT_TRUE(cache.CreateBackendEntry("http://www.google.com", &entry));
 
   std::string headers("HTTP/1.1 200 OK");
   headers = net::HttpUtil::AssembleRawHeaders(headers.data(), headers.size());
@@ -3425,15 +3878,15 @@
   response.headers = new net::HttpResponseHeaders(headers);
 
   // Set the last argument for this to be an incomplete request.
-  EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, true));
+  EXPECT_TRUE(MockHttpCache::WriteResponseInfo(entry, &response, true, true));
   bool truncated = false;
-  EXPECT_TRUE(net::HttpCache::ReadResponseInfo(entry, &response, &truncated));
+  EXPECT_TRUE(MockHttpCache::ReadResponseInfo(entry, &response, &truncated));
   EXPECT_TRUE(truncated);
 
   // And now test the opposite case.
-  EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, false));
+  EXPECT_TRUE(MockHttpCache::WriteResponseInfo(entry, &response, true, false));
   truncated = true;
-  EXPECT_TRUE(net::HttpCache::ReadResponseInfo(entry, &response, &truncated));
+  EXPECT_TRUE(MockHttpCache::ReadResponseInfo(entry, &response, &truncated));
   EXPECT_FALSE(truncated);
   entry->Close();
 }
@@ -3450,7 +3903,7 @@
   int rv = cache.http_cache()->CreateTransaction(&c->trans);
   EXPECT_EQ(net::OK, rv);
 
-  rv = c->trans->Start(&request, &c->callback, NULL);
+  rv = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     c->result = c->callback.WaitForResult();
 
@@ -3481,7 +3934,7 @@
   int rv = cache.http_cache()->CreateTransaction(&c->trans);
   EXPECT_EQ(net::OK, rv);
 
-  rv = c->trans->Start(&request, &c->callback, NULL);
+  rv = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = c->callback.WaitForResult();
 
@@ -3525,7 +3978,7 @@
   int rv = cache.http_cache()->CreateTransaction(&c->trans);
   EXPECT_EQ(net::OK, rv);
 
-  rv = c->trans->Start(&request, &c->callback, NULL);
+  rv = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = c->callback.WaitForResult();
 
@@ -3565,11 +4018,11 @@
   AddMockTransaction(&transaction);
   MockHttpRequest request(transaction);
 
-  Context* c = new Context();
+  scoped_ptr<Context> c(new Context());
   int rv = cache.http_cache()->CreateTransaction(&c->trans);
   EXPECT_EQ(net::OK, rv);
 
-  rv = c->trans->Start(&request, &c->callback, NULL);
+  rv = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = c->callback.WaitForResult();
 
@@ -3584,16 +4037,29 @@
     rv = c->callback.WaitForResult();
   EXPECT_EQ(buf->size(), rv);
 
+  // We want to cancel the request when the transaction is busy.
+  rv = c->trans->Read(buf, buf->size(), &c->callback);
+  EXPECT_EQ(net::ERR_IO_PENDING, rv);
+  EXPECT_FALSE(c->callback.have_result());
+
+  g_test_mode = TEST_MODE_SYNC_ALL;
+
   // Destroy the transaction.
-  delete c;
+  c->trans.reset();
+  g_test_mode = 0;
+
+  // Make sure that we don't invoke the callback. We may have an issue if the
+  // UrlRequestJob is killed directly (without cancelling the UrlRequest) so we
+  // could end up with the transaction being deleted twice if we send any
+  // notification from the transaction destructor (see http://crbug.com/31723).
+  EXPECT_FALSE(c->callback.have_result());
 
   // Verify that the entry is marked as incomplete.
   disk_cache::Entry* entry;
-  ASSERT_EQ(net::OK, cache.disk_cache()->OpenEntry(kSimpleGET_Transaction.url,
-                                                   &entry, NULL));
+  ASSERT_TRUE(cache.OpenBackendEntry(kSimpleGET_Transaction.url, &entry));
   net::HttpResponseInfo response;
   bool truncated = false;
-  EXPECT_TRUE(net::HttpCache::ReadResponseInfo(entry, &response, &truncated));
+  EXPECT_TRUE(MockHttpCache::ReadResponseInfo(entry, &response, &truncated));
   EXPECT_TRUE(truncated);
   entry->Close();
 
@@ -3608,9 +4074,7 @@
 
   // Create a disk cache entry that stores an incomplete resource.
   disk_cache::Entry* entry;
-  ASSERT_EQ(net::OK,
-            cache.disk_cache()->CreateEntry(kRangeGET_TransactionOK.url, &entry,
-                                            NULL));
+  ASSERT_TRUE(cache.CreateBackendEntry(kRangeGET_TransactionOK.url, &entry));
 
   std::string raw_headers("HTTP/1.1 200 OK\n"
                           "Last-Modified: Sat, 18 Apr 2009 01:10:43 GMT\n"
@@ -3623,12 +4087,14 @@
   net::HttpResponseInfo response;
   response.headers = new net::HttpResponseHeaders(raw_headers);
   // Set the last argument for this to be an incomplete request.
-  EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, true));
+  EXPECT_TRUE(MockHttpCache::WriteResponseInfo(entry, &response, true, true));
 
   scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(100));
   int len = static_cast<int>(base::strlcpy(buf->data(),
                                            "rg: 00-09 rg: 10-19 ", 100));
-  EXPECT_EQ(len, entry->WriteData(1, 0, buf, len, NULL, true));
+  TestCompletionCallback cb;
+  int rv = entry->WriteData(1, 0, buf, len, &cb, true);
+  EXPECT_EQ(len, cb.GetResult(rv));
 
   // Now make a regular request.
   std::string headers;
@@ -3656,7 +4122,7 @@
   // Verify that the disk entry was updated.
   EXPECT_EQ(80, entry->GetDataSize(1));
   bool truncated = true;
-  EXPECT_TRUE(net::HttpCache::ReadResponseInfo(entry, &response, &truncated));
+  EXPECT_TRUE(MockHttpCache::ReadResponseInfo(entry, &response, &truncated));
   EXPECT_FALSE(truncated);
   entry->Close();
 }
@@ -3669,9 +4135,7 @@
 
   // Create a disk cache entry that stores an incomplete resource.
   disk_cache::Entry* entry;
-  ASSERT_EQ(net::OK,
-            cache.disk_cache()->CreateEntry(kRangeGET_TransactionOK.url, &entry,
-                                            NULL));
+  ASSERT_TRUE(cache.CreateBackendEntry(kRangeGET_TransactionOK.url, &entry));
 
 
   // Content-length will be intentionally bad.
@@ -3686,12 +4150,14 @@
   net::HttpResponseInfo response;
   response.headers = new net::HttpResponseHeaders(raw_headers);
   // Set the last argument for this to be an incomplete request.
-  EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, true));
+  EXPECT_TRUE(MockHttpCache::WriteResponseInfo(entry, &response, true, true));
 
   scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(100));
   int len = static_cast<int>(base::strlcpy(buf->data(),
                                            "rg: 00-09 rg: 10-19 ", 100));
-  EXPECT_EQ(len, entry->WriteData(1, 0, buf, len, NULL, true));
+  TestCompletionCallback cb;
+  int rv = entry->WriteData(1, 0, buf, len, &cb, true);
+  EXPECT_EQ(len, cb.GetResult(rv));
   entry->Close();
 
   // Now make a regular request.
@@ -3717,8 +4183,7 @@
   RemoveMockTransaction(&kRangeGET_TransactionOK);
 
   // Verify that the disk entry was deleted.
-  ASSERT_NE(net::OK, cache.disk_cache()->OpenEntry(kRangeGET_TransactionOK.url,
-                                                   &entry, NULL));
+  ASSERT_FALSE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry));
 }
 
 // Tests that when we cancel a request that was interrupted, we mark it again
@@ -3730,9 +4195,7 @@
 
   // Create a disk cache entry that stores an incomplete resource.
   disk_cache::Entry* entry;
-  ASSERT_EQ(net::OK,
-            cache.disk_cache()->CreateEntry(kRangeGET_TransactionOK.url, &entry,
-                                            NULL));
+  ASSERT_TRUE(cache.CreateBackendEntry(kRangeGET_TransactionOK.url, &entry));
 
   std::string raw_headers("HTTP/1.1 200 OK\n"
                           "Last-Modified: Sat, 18 Apr 2009 01:10:43 GMT\n"
@@ -3746,12 +4209,14 @@
   response.headers = new net::HttpResponseHeaders(raw_headers);
 
   // Set the last argument for this to be an incomplete request.
-  EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, true));
+  EXPECT_TRUE(MockHttpCache::WriteResponseInfo(entry, &response, true, true));
 
   scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(100));
   int len = static_cast<int>(base::strlcpy(buf->data(), "rg: 00-09 rg: 10-19 ",
                                            buf->size()));
-  EXPECT_EQ(len, entry->WriteData(1, 0, buf, len, NULL, true));
+  TestCompletionCallback cb;
+  int rv = entry->WriteData(1, 0, buf, len, &cb, true);
+  EXPECT_EQ(len, cb.GetResult(rv));
 
   // Now make a regular request.
   MockTransaction transaction(kRangeGET_TransactionOK);
@@ -3761,7 +4226,7 @@
   Context* c = new Context();
   EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction(&c->trans));
 
-  int rv = c->trans->Start(&request, &c->callback, NULL);
+  rv = c->trans->Start(&request, &c->callback, net::BoundNetLog());
   EXPECT_EQ(net::OK, c->callback.GetResult(rv));
 
   // Read 20 bytes from the cache, and 10 from the net.
@@ -3783,7 +4248,7 @@
   // Verify that the disk entry was updated: now we have 30 bytes.
   EXPECT_EQ(30, entry->GetDataSize(1));
   bool truncated = false;
-  EXPECT_TRUE(net::HttpCache::ReadResponseInfo(entry, &response, &truncated));
+  EXPECT_TRUE(MockHttpCache::ReadResponseInfo(entry, &response, &truncated));
   EXPECT_TRUE(truncated);
   entry->Close();
 }
@@ -3796,9 +4261,7 @@
 
   // Create a disk cache entry that stores an incomplete resource.
   disk_cache::Entry* entry;
-  ASSERT_EQ(net::OK,
-            cache.disk_cache()->CreateEntry(kRangeGET_TransactionOK.url, &entry,
-                                            NULL));
+  ASSERT_TRUE(cache.CreateBackendEntry(kRangeGET_TransactionOK.url, &entry));
 
   // Content-length will be intentionally bogus.
   std::string raw_headers("HTTP/1.1 200 OK\n"
@@ -3812,12 +4275,14 @@
   net::HttpResponseInfo response;
   response.headers = new net::HttpResponseHeaders(raw_headers);
   // Set the last argument for this to be an incomplete request.
-  EXPECT_TRUE(net::HttpCache::WriteResponseInfo(entry, &response, true, true));
+  EXPECT_TRUE(MockHttpCache::WriteResponseInfo(entry, &response, true, true));
 
   scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(100));
   int len = static_cast<int>(base::strlcpy(buf->data(),
                                            "rg: 00-09 rg: 10-19 ", 100));
-  EXPECT_EQ(len, entry->WriteData(1, 0, buf, len, NULL, true));
+  TestCompletionCallback cb;
+  int rv = entry->WriteData(1, 0, buf, len, &cb, true);
+  EXPECT_EQ(len, cb.GetResult(rv));
   entry->Close();
 
   // Now make a range request.
@@ -3825,7 +4290,7 @@
   RunTransactionTestWithResponse(cache.http_cache(), kRangeGET_TransactionOK,
                                  &headers);
 
-  EXPECT_TRUE(Verify206Response(headers, 40, 49));
+  Verify206Response(headers, 40, 49);
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
   EXPECT_EQ(1, cache.disk_cache()->open_count());
   EXPECT_EQ(2, cache.disk_cache()->create_count());
@@ -3852,13 +4317,13 @@
                           c2(cache.http_cache()),
                           c3(cache.http_cache());
 
-  c1.Start(&r1, NULL);
+  c1.Start(&r1, net::BoundNetLog());
 
   r2.load_flags |= net::LOAD_ONLY_FROM_CACHE;
-  c2.Start(&r2, NULL);
+  c2.Start(&r2, net::BoundNetLog());
 
   r3.load_flags |= net::LOAD_ONLY_FROM_CACHE;
-  c3.Start(&r3, NULL);
+  c3.Start(&r3, net::BoundNetLog());
 
   MessageLoop::current()->Run();
 
@@ -3906,7 +4371,7 @@
     EXPECT_EQ(net::OK, rv);
     ASSERT_TRUE(trans.get());
 
-    rv = trans->Start(&request, &callback, NULL);
+    rv = trans->Start(&request, &callback, net::BoundNetLog());
     if (rv == net::ERR_IO_PENDING)
       rv = callback.WaitForResult();
     ASSERT_EQ(net::OK, rv);
@@ -3934,7 +4399,7 @@
     EXPECT_EQ(net::OK, rv);
     ASSERT_TRUE(trans.get());
 
-    rv = trans->Start(&request, &callback, NULL);
+    rv = trans->Start(&request, &callback, net::BoundNetLog());
     if (rv == net::ERR_IO_PENDING)
       rv = callback.WaitForResult();
     ASSERT_EQ(net::OK, rv);
@@ -3977,8 +4442,7 @@
   EXPECT_EQ(2, cache.disk_cache()->create_count());
 
   disk_cache::Entry* entry;
-  EXPECT_NE(net::OK,
-            cache.disk_cache()->OpenEntry(transaction.url, &entry, NULL));
+  EXPECT_FALSE(cache.OpenBackendEntry(transaction.url, &entry));
 }
 
 TEST(HttpCache, CacheControlNoStore2) {
@@ -4006,8 +4470,7 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   disk_cache::Entry* entry;
-  EXPECT_NE(net::OK,
-            cache.disk_cache()->OpenEntry(transaction.url, &entry, NULL));
+  EXPECT_FALSE(cache.OpenBackendEntry(transaction.url, &entry));
 }
 
 TEST(HttpCache, CacheControlNoStore3) {
@@ -4036,8 +4499,7 @@
   EXPECT_EQ(1, cache.disk_cache()->create_count());
 
   disk_cache::Entry* entry;
-  EXPECT_NE(net::OK,
-            cache.disk_cache()->OpenEntry(transaction.url, &entry, NULL));
+  EXPECT_FALSE(cache.OpenBackendEntry(transaction.url, &entry));
 }
 
 // Ensure that we don't cache requests served over bad HTTPS.
@@ -4062,7 +4524,7 @@
   EXPECT_EQ(net::OK, rv);
   ASSERT_TRUE(trans.get());
 
-  rv = trans->Start(&request, &callback, NULL);
+  rv = trans->Start(&request, &callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   ASSERT_EQ(net::ERR_CACHE_MISS, rv);
@@ -4173,3 +4635,132 @@
 
   RemoveMockTransaction(&mock_network_response);
 }
+
+// Tests that we can write metadata to an entry.
+TEST(HttpCache, WriteMetadata_OK) {
+  MockHttpCache cache;
+
+  // Write to the cache
+  net::HttpResponseInfo response;
+  RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
+                                     &response);
+  EXPECT_TRUE(response.metadata.get() == NULL);
+
+  // Trivial call.
+  cache.http_cache()->WriteMetadata(GURL("foo"), Time::Now(), NULL, 0);
+
+  // Write meta data to the same entry.
+  scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(50));
+  memset(buf->data(), 0, buf->size());
+  base::strlcpy(buf->data(), "Hi there", buf->size());
+  cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
+                                    response.response_time, buf, buf->size());
+
+  // Release the buffer before the operation takes place.
+  buf = NULL;
+
+  // Makes sure we finish pending operations.
+  MessageLoop::current()->RunAllPending();
+
+  RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
+                                     &response);
+  ASSERT_TRUE(response.metadata.get() != NULL);
+  EXPECT_EQ(50, response.metadata->size());
+  EXPECT_EQ(0, strcmp(response.metadata->data(), "Hi there"));
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(2, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests that we only write metadata to an entry if the time stamp matches.
+TEST(HttpCache, WriteMetadata_Fail) {
+  MockHttpCache cache;
+
+  // Write to the cache
+  net::HttpResponseInfo response;
+  RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
+                                     &response);
+  EXPECT_TRUE(response.metadata.get() == NULL);
+
+  // Attempt to write meta data to the same entry.
+  scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(50));
+  memset(buf->data(), 0, buf->size());
+  base::strlcpy(buf->data(), "Hi there", buf->size());
+  base::Time expected_time = response.response_time -
+                             base::TimeDelta::FromMilliseconds(20);
+  cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
+                                    expected_time, buf, buf->size());
+
+  // Makes sure we finish pending operations.
+  MessageLoop::current()->RunAllPending();
+
+  RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
+                                     &response);
+  EXPECT_TRUE(response.metadata.get() == NULL);
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(2, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
+
+// Tests that we can read metadata after validating the entry and with READ mode
+// transactions.
+TEST(HttpCache, ReadMetadata) {
+  MockHttpCache cache;
+
+  // Write to the cache
+  net::HttpResponseInfo response;
+  RunTransactionTestWithResponseInfo(cache.http_cache(),
+                                     kTypicalGET_Transaction, &response);
+  EXPECT_TRUE(response.metadata.get() == NULL);
+
+  // Write meta data to the same entry.
+  scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(50));
+  memset(buf->data(), 0, buf->size());
+  base::strlcpy(buf->data(), "Hi there", buf->size());
+  cache.http_cache()->WriteMetadata(GURL(kTypicalGET_Transaction.url),
+                                    response.response_time, buf, buf->size());
+
+  // Makes sure we finish pending operations.
+  MessageLoop::current()->RunAllPending();
+
+  // Start with a READ mode transaction.
+  MockTransaction trans1(kTypicalGET_Transaction);
+  trans1.load_flags = net::LOAD_ONLY_FROM_CACHE;
+
+  RunTransactionTestWithResponseInfo(cache.http_cache(), trans1, &response);
+  ASSERT_TRUE(response.metadata.get() != NULL);
+  EXPECT_EQ(50, response.metadata->size());
+  EXPECT_EQ(0, strcmp(response.metadata->data(), "Hi there"));
+
+  EXPECT_EQ(1, cache.network_layer()->transaction_count());
+  EXPECT_EQ(2, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+  MessageLoop::current()->RunAllPending();
+
+  // Now make sure that the entry is re-validated with the server.
+  trans1.load_flags = net::LOAD_VALIDATE_CACHE;
+  trans1.status = "HTTP/1.1 304 Not Modified";
+  AddMockTransaction(&trans1);
+
+  response.metadata = NULL;
+  RunTransactionTestWithResponseInfo(cache.http_cache(), trans1, &response);
+  EXPECT_TRUE(response.metadata.get() != NULL);
+
+  EXPECT_EQ(2, cache.network_layer()->transaction_count());
+  EXPECT_EQ(3, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+  MessageLoop::current()->RunAllPending();
+  RemoveMockTransaction(&trans1);
+
+  // Now return 200 when validating the entry so the metadata will be lost.
+  MockTransaction trans2(kTypicalGET_Transaction);
+  trans2.load_flags = net::LOAD_VALIDATE_CACHE;
+  RunTransactionTestWithResponseInfo(cache.http_cache(), trans2, &response);
+  EXPECT_TRUE(response.metadata.get() == NULL);
+
+  EXPECT_EQ(3, cache.network_layer()->transaction_count());
+  EXPECT_EQ(4, cache.disk_cache()->open_count());
+  EXPECT_EQ(1, cache.disk_cache()->create_count());
+}
diff --git a/net/http/http_chunked_decoder.cc b/net/http/http_chunked_decoder.cc
index 619c9a1..1f6ef0d 100644
--- a/net/http/http_chunked_decoder.cc
+++ b/net/http/http_chunked_decoder.cc
@@ -116,11 +116,11 @@
         reached_eof_ = true;
       }
     } else if (chunk_terminator_remaining_) {
-       if (buf_len) {
-         DLOG(ERROR) << "chunk data not terminated properly";
-         return ERR_INVALID_CHUNKED_ENCODING;
-       }
-       chunk_terminator_remaining_ = false;
+      if (buf_len) {
+        DLOG(ERROR) << "chunk data not terminated properly";
+        return ERR_INVALID_CHUNKED_ENCODING;
+      }
+      chunk_terminator_remaining_ = false;
     } else if (buf_len) {
       // Ignore any chunk-extensions.
       size_t index_of_semicolon = base::StringPiece(buf, buf_len).find(';');
diff --git a/net/http/http_net_log_params.h b/net/http/http_net_log_params.h
new file mode 100644
index 0000000..563c799
--- /dev/null
+++ b/net/http/http_net_log_params.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2010 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_HTTP_HTTP_NET_LOG_PARAMS_H_
+#define NET_HTTP_HTTP_NET_LOG_PARAMS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/values.h"
+#include "net/base/net_log.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+
+namespace net {
+
+class NetLogHttpRequestParameter : public NetLog::EventParameters {
+ public:
+  NetLogHttpRequestParameter(const std::string& line,
+                             const HttpRequestHeaders& headers)
+      : line_(line) {
+    headers_.CopyFrom(headers);
+  }
+
+  Value* ToValue() const {
+    DictionaryValue* dict = new DictionaryValue();
+    dict->SetString(L"line", line_);
+    ListValue* headers = new ListValue();
+    HttpRequestHeaders::Iterator iterator(headers_);
+    while (iterator.GetNext()) {
+      headers->Append(
+          new StringValue(StringPrintf("%s: %s",
+                                       iterator.name().c_str(),
+                                       iterator.value().c_str())));
+    }
+    dict->Set(L"headers", headers);
+    return dict;
+  }
+
+ private:
+  ~NetLogHttpRequestParameter() {}
+
+  const std::string line_;
+  HttpRequestHeaders headers_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetLogHttpRequestParameter);
+};
+
+class NetLogHttpResponseParameter : public NetLog::EventParameters {
+ public:
+  explicit NetLogHttpResponseParameter(
+      const scoped_refptr<HttpResponseHeaders>& headers)
+      : headers_(headers) {}
+
+  Value* ToValue() const {
+    DictionaryValue* dict = new DictionaryValue();
+    ListValue* headers = new ListValue();
+    headers->Append(new StringValue(headers_->GetStatusLine()));
+    void* iterator = NULL;
+    std::string name;
+    std::string value;
+    while (headers_->EnumerateHeaderLines(&iterator, &name, &value)) {
+      headers->Append(
+          new StringValue(StringPrintf("%s: %s", name.c_str(), value.c_str())));
+    }
+    dict->Set(L"headers", headers);
+    return dict;
+  }
+
+ private:
+  ~NetLogHttpResponseParameter() {}
+
+  const scoped_refptr<HttpResponseHeaders> headers_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetLogHttpResponseParameter);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_NET_LOG_PARAMS_H_
+
diff --git a/net/http/http_network_delegate.h b/net/http/http_network_delegate.h
new file mode 100644
index 0000000..51c6766
--- /dev/null
+++ b/net/http/http_network_delegate.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2010 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_HTTP_HTTP_NETWORK_DELEGATE_H_
+#define NET_HTTP_HTTP_NETWORK_DELEGATE_H_
+
+namespace net {
+
+class HttpRequestHeaders;
+
+class HttpNetworkDelegate {
+ public:
+  // Called right before the HTTP headers are sent.  Allows the delegate to
+  // read/write |headers| before they get sent out.
+  virtual void OnSendHttpRequest(HttpRequestHeaders* headers) = 0;
+
+ protected:
+  virtual ~HttpNetworkDelegate() {}
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_NETWORK_DELEGATE_H_
diff --git a/net/http/http_network_layer.cc b/net/http/http_network_layer.cc
index c8022b7..7df286f 100644
--- a/net/http/http_network_layer.cc
+++ b/net/http/http_network_layer.cc
@@ -1,18 +1,19 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/http/http_network_layer.h"
 
+#include "base/field_trial.h"
 #include "base/logging.h"
 #include "base/string_util.h"
-#include "net/flip/flip_framer.h"
-#include "net/flip/flip_network_transaction.h"
-#include "net/flip/flip_session.h"
-#include "net/flip/flip_session_pool.h"
 #include "net/http/http_network_session.h"
 #include "net/http/http_network_transaction.h"
 #include "net/socket/client_socket_factory.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_network_transaction.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_session_pool.h"
 
 namespace net {
 
@@ -20,15 +21,19 @@
 
 // static
 HttpTransactionFactory* HttpNetworkLayer::CreateFactory(
-    NetworkChangeNotifier* network_change_notifier,
     HostResolver* host_resolver,
     ProxyService* proxy_service,
-    SSLConfigService* ssl_config_service) {
+    SSLConfigService* ssl_config_service,
+    HttpAuthHandlerFactory* http_auth_handler_factory,
+    HttpNetworkDelegate* network_delegate,
+    NetLog* net_log) {
   DCHECK(proxy_service);
 
   return new HttpNetworkLayer(ClientSocketFactory::GetDefaultFactory(),
-                              network_change_notifier,
-                              host_resolver, proxy_service, ssl_config_service);
+                              host_resolver, proxy_service, ssl_config_service,
+                              http_auth_handler_factory,
+                              network_delegate,
+                              net_log);
 }
 
 // static
@@ -40,21 +45,25 @@
 }
 
 //-----------------------------------------------------------------------------
-bool HttpNetworkLayer::force_flip_ = false;
+bool HttpNetworkLayer::force_spdy_ = false;
 
 HttpNetworkLayer::HttpNetworkLayer(
     ClientSocketFactory* socket_factory,
-    NetworkChangeNotifier* network_change_notifier,
     HostResolver* host_resolver,
     ProxyService* proxy_service,
-    SSLConfigService* ssl_config_service)
+    SSLConfigService* ssl_config_service,
+    HttpAuthHandlerFactory* http_auth_handler_factory,
+    HttpNetworkDelegate* network_delegate,
+    NetLog* net_log)
     : socket_factory_(socket_factory),
-      network_change_notifier_(network_change_notifier),
       host_resolver_(host_resolver),
       proxy_service_(proxy_service),
       ssl_config_service_(ssl_config_service),
       session_(NULL),
-      flip_session_pool_(NULL),
+      spdy_session_pool_(NULL),
+      http_auth_handler_factory_(http_auth_handler_factory),
+      network_delegate_(network_delegate),
+      net_log_(net_log),
       suspended_(false) {
   DCHECK(proxy_service_);
   DCHECK(ssl_config_service_.get());
@@ -62,10 +71,12 @@
 
 HttpNetworkLayer::HttpNetworkLayer(HttpNetworkSession* session)
     : socket_factory_(ClientSocketFactory::GetDefaultFactory()),
-      network_change_notifier_(NULL),
       ssl_config_service_(NULL),
       session_(session),
-      flip_session_pool_(session->flip_session_pool()),
+      spdy_session_pool_(session->spdy_session_pool()),
+      http_auth_handler_factory_(NULL),
+      network_delegate_(NULL),
+      net_log_(NULL),
       suspended_(false) {
   DCHECK(session_.get());
 }
@@ -77,8 +88,8 @@
   if (suspended_)
     return ERR_NETWORK_IO_SUSPENDED;
 
-  if (force_flip_)
-    trans->reset(new FlipNetworkTransaction(GetSession()));
+  if (force_spdy_)
+    trans->reset(new SpdyNetworkTransaction(GetSession()));
   else
     trans->reset(new HttpNetworkTransaction(GetSession()));
   return OK;
@@ -98,47 +109,80 @@
 HttpNetworkSession* HttpNetworkLayer::GetSession() {
   if (!session_) {
     DCHECK(proxy_service_);
-    FlipSessionPool* flip_pool = new FlipSessionPool;
-    session_ = new HttpNetworkSession(
-        network_change_notifier_, host_resolver_, proxy_service_,
-        socket_factory_, ssl_config_service_, flip_pool);
+    SpdySessionPool* spdy_pool = new SpdySessionPool();
+    session_ = new HttpNetworkSession(host_resolver_, proxy_service_,
+        socket_factory_, ssl_config_service_, spdy_pool,
+        http_auth_handler_factory_, network_delegate_, net_log_);
     // These were just temps for lazy-initializing HttpNetworkSession.
-    network_change_notifier_ = NULL;
     host_resolver_ = NULL;
     proxy_service_ = NULL;
     socket_factory_ = NULL;
+    http_auth_handler_factory_ = NULL;
+    net_log_ = NULL;
+    network_delegate_ = NULL;
   }
   return session_;
 }
 
 // static
-void HttpNetworkLayer::EnableFlip(const std::string& mode) {
+void HttpNetworkLayer::EnableSpdy(const std::string& mode) {
   static const char kDisableSSL[] = "no-ssl";
   static const char kDisableCompression[] = "no-compress";
+  static const char kDisableAltProtocols[] = "no-alt-protocols";
+
+  // We want an A/B experiment between SPDY enabled and SPDY disabled,
+  // but only for pages where SPDY *could have been* negotiated.  To do
+  // this, we use NPN, but prevent it from negotiating SPDY.  If the
+  // server negotiates HTTP, rather than SPDY, today that will only happen
+  // on servers that installed NPN (and could have done SPDY).  But this is
+  // a bit of a hack, as this correlation between NPN and SPDY is not
+  // really guaranteed.
   static const char kEnableNPN[] = "npn";
+  static const char kEnableNpnHttpOnly[] = "npn-http";
 
-  std::vector<std::string> flip_options;
-  SplitString(mode, ',', &flip_options);
+  // Except for the first element, the order is irrelevant.  First element
+  // specifies the fallback in case nothing matches
+  // (SSLClientSocket::kNextProtoNoOverlap).  Otherwise, the SSL library
+  // will choose the first overlapping protocol in the server's list, since
+  // it presumedly has a better understanding of which protocol we should
+  // use, therefore the rest of the ordering here is not important.
+  static const char kNpnProtosFull[] =
+      "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy";
+  // No spdy specified.
+  static const char kNpnProtosHttpOnly[] = "\x08http/1.1\x07http1.1";
 
-  // Force flip mode (use FlipNetworkTransaction for all http requests).
-  force_flip_ = true;
+  std::vector<std::string> spdy_options;
+  SplitString(mode, ',', &spdy_options);
 
-  for (std::vector<std::string>::iterator it = flip_options.begin();
-       it != flip_options.end(); ++it) {
+  // Force spdy mode (use SpdyNetworkTransaction for all http requests).
+  force_spdy_ = true;
+
+  bool use_alt_protocols = true;
+
+  for (std::vector<std::string>::iterator it = spdy_options.begin();
+       it != spdy_options.end(); ++it) {
     const std::string& option = *it;
-
-    // Disable SSL
     if (option == kDisableSSL) {
-      FlipSession::SetSSLMode(false);
+      SpdySession::SetSSLMode(false);  // Disable SSL
     } else if (option == kDisableCompression) {
-      flip::FlipFramer::set_enable_compression_default(false);
+      spdy::SpdyFramer::set_enable_compression_default(false);
     } else if (option == kEnableNPN) {
-      HttpNetworkTransaction::SetNextProtos("\007http1.1\004spdy");
-      force_flip_ = false;
-    } else if (option.empty() && it == flip_options.begin()) {
+      HttpNetworkTransaction::SetUseAlternateProtocols(use_alt_protocols);
+      HttpNetworkTransaction::SetNextProtos(kNpnProtosFull);
+      force_spdy_ = false;
+    } else if (option == kEnableNpnHttpOnly) {
+      // Avoid alternate protocol in this case. Otherwise, browser will try SSL
+      // and then fallback to http. This introduces extra load.
+      HttpNetworkTransaction::SetUseAlternateProtocols(false);
+      HttpNetworkTransaction::SetNextProtos(kNpnProtosHttpOnly);
+      force_spdy_ = false;
+    } else if (option == kDisableAltProtocols) {
+      use_alt_protocols = false;
+      HttpNetworkTransaction::SetUseAlternateProtocols(false);
+    } else if (option.empty() && it == spdy_options.begin()) {
       continue;
     } else {
-      LOG(DFATAL) << "Unrecognized flip option: " << option;
+      LOG(DFATAL) << "Unrecognized spdy option: " << option;
     }
   }
 }
diff --git a/net/http/http_network_layer.h b/net/http/http_network_layer.h
index 38c10c7..8cc62ab 100644
--- a/net/http/http_network_layer.h
+++ b/net/http/http_network_layer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/non_thread_safe.h"
 #include "base/ref_counted.h"
 #include "base/scoped_ptr.h"
 #include "net/http/http_transaction_factory.h"
@@ -14,22 +15,27 @@
 namespace net {
 
 class ClientSocketFactory;
-class FlipSessionPool;
 class HostResolver;
+class HttpAuthHandlerFactory;
+class HttpNetworkDelegate;
 class HttpNetworkSession;
-class NetworkChangeNotifier;
+class NetLog;
 class ProxyInfo;
 class ProxyService;
+class SpdySessionPool;
 class SSLConfigService;
 
-class HttpNetworkLayer : public HttpTransactionFactory {
+class HttpNetworkLayer : public HttpTransactionFactory, public NonThreadSafe {
  public:
-  // |socket_factory|, |network_change_notifier|, |proxy_service| and
-  // |host_resolver| must remain valid for the lifetime of HttpNetworkLayer.
+  // |socket_factory|, |proxy_service| and |host_resolver| must remain valid for
+  // the lifetime of HttpNetworkLayer.
   HttpNetworkLayer(ClientSocketFactory* socket_factory,
-                   NetworkChangeNotifier* network_change_notifier,
-                   HostResolver* host_resolver, ProxyService* proxy_service,
-                   SSLConfigService* ssl_config_service);
+                   HostResolver* host_resolver,
+                   ProxyService* proxy_service,
+                   SSLConfigService* ssl_config_service,
+                   HttpAuthHandlerFactory* http_auth_handler_factory,
+                   HttpNetworkDelegate* network_delegate,
+                   NetLog* net_log);
   // Construct a HttpNetworkLayer with an existing HttpNetworkSession which
   // contains a valid ProxyService.
   explicit HttpNetworkLayer(HttpNetworkSession* session);
@@ -38,10 +44,12 @@
   // This function hides the details of how a network layer gets instantiated
   // and allows other implementations to be substituted.
   static HttpTransactionFactory* CreateFactory(
-      NetworkChangeNotifier* network_change_notifier,
       HostResolver* host_resolver,
       ProxyService* proxy_service,
-      SSLConfigService* ssl_config_service);
+      SSLConfigService* ssl_config_service,
+      HttpAuthHandlerFactory* http_auth_handler_factory,
+      HttpNetworkDelegate* network_delegate,
+      NetLog* net_log);
   // Create a transaction factory that instantiate a network layer over an
   // existing network session. Network session contains some valuable
   // information (e.g. authentication data) that we want to share across
@@ -56,20 +64,18 @@
   virtual HttpNetworkSession* GetSession();
   virtual void Suspend(bool suspend);
 
-  // Enable the flip protocol.
-  // Without calling this function, FLIP is disabled.  The mode can be:
+  // Enable the spdy protocol.
+  // Without calling this function, SPDY is disabled.  The mode can be:
   //   ""            : (default) SSL and compression are enabled.
   //   "no-ssl"      : disables SSL.
   //   "no-compress" : disables compression.
   //   "none"        : disables both SSL and compression.
-  static void EnableFlip(const std::string& mode);
+  static void EnableSpdy(const std::string& mode);
 
  private:
   // The factory we will use to create network sockets.
   ClientSocketFactory* socket_factory_;
 
-  NetworkChangeNotifier* network_change_notifier_;
-
   // The host resolver and proxy service that will be used when lazily
   // creating |session_|.
   scoped_refptr<HostResolver> host_resolver_;
@@ -79,10 +85,14 @@
   scoped_refptr<SSLConfigService> ssl_config_service_;
 
   scoped_refptr<HttpNetworkSession> session_;
-  scoped_refptr<FlipSessionPool> flip_session_pool_;
+  scoped_refptr<SpdySessionPool> spdy_session_pool_;
+
+  HttpAuthHandlerFactory* http_auth_handler_factory_;
+  HttpNetworkDelegate* network_delegate_;
+  NetLog* net_log_;
 
   bool suspended_;
-  static bool force_flip_;
+  static bool force_spdy_;
 };
 
 }  // namespace net
diff --git a/net/http/http_network_layer_unittest.cc b/net/http/http_network_layer_unittest.cc
index 1e75ece..274d2dc 100644
--- a/net/http/http_network_layer_unittest.cc
+++ b/net/http/http_network_layer_unittest.cc
@@ -1,8 +1,9 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/base/mock_host_resolver.h"
+#include "net/base/net_log.h"
 #include "net/base/ssl_config_service_defaults.h"
 #include "net/http/http_network_layer.h"
 #include "net/http/http_transaction_unittest.h"
@@ -15,9 +16,9 @@
 };
 
 TEST_F(HttpNetworkLayerTest, CreateAndDestroy) {
-  net::HttpNetworkLayer factory(
-      NULL, NULL, new net::MockHostResolver, net::ProxyService::CreateNull(),
-      new net::SSLConfigServiceDefaults);
+  net::HttpNetworkLayer factory(NULL, new net::MockHostResolver,
+      net::ProxyService::CreateNull(), new net::SSLConfigServiceDefaults, NULL,
+      NULL, NULL);
 
   scoped_ptr<net::HttpTransaction> trans;
   int rv = factory.CreateTransaction(&trans);
@@ -26,9 +27,9 @@
 }
 
 TEST_F(HttpNetworkLayerTest, Suspend) {
-  net::HttpNetworkLayer factory(
-      NULL, NULL, new net::MockHostResolver, net::ProxyService::CreateNull(),
-      new net::SSLConfigServiceDefaults);
+  net::HttpNetworkLayer factory(NULL, new net::MockHostResolver,
+      net::ProxyService::CreateNull(), new net::SSLConfigServiceDefaults, NULL,
+      NULL, NULL);
 
   scoped_ptr<net::HttpTransaction> trans;
   int rv = factory.CreateTransaction(&trans);
@@ -62,13 +63,13 @@
                    "Connection: keep-alive\r\n"
                    "User-Agent: Foo/1.0\r\n\r\n"),
   };
-  net::StaticSocketDataProvider data(data_reads, data_writes);
+  net::StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                     data_writes, arraysize(data_reads));
   mock_socket_factory.AddSocketDataProvider(&data);
 
-  net::HttpNetworkLayer factory(&mock_socket_factory, NULL,
-                                new net::MockHostResolver,
-                                net::ProxyService::CreateNull(),
-                                new net::SSLConfigServiceDefaults);
+  net::HttpNetworkLayer factory(&mock_socket_factory, new net::MockHostResolver,
+      net::ProxyService::CreateNull(), new net::SSLConfigServiceDefaults, NULL,
+      NULL, NULL);
 
   TestCompletionCallback callback;
 
@@ -79,10 +80,11 @@
   net::HttpRequestInfo request_info;
   request_info.url = GURL("http://www.google.com/");
   request_info.method = "GET";
-  request_info.user_agent = "Foo/1.0";
+  request_info.extra_headers.SetHeader(net::HttpRequestHeaders::kUserAgent,
+                                       "Foo/1.0");
   request_info.load_flags = net::LOAD_NORMAL;
 
-  rv = trans->Start(&request_info, &callback, NULL);
+  rv = trans->Start(&request_info, &callback, net::BoundNetLog());
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   ASSERT_EQ(net::OK, rv);
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 9f4cba7..971786a 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -1,40 +1,72 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/http/http_network_session.h"
 
+#include <utility>
+
 #include "base/logging.h"
-#include "net/flip/flip_session_pool.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/url_security_manager.h"
+#include "net/spdy/spdy_session_pool.h"
 
 namespace net {
 
-// static
-int HttpNetworkSession::max_sockets_ = 100;
+namespace {
 
-// static
-int HttpNetworkSession::max_sockets_per_group_ = 6;
+// Total limit of sockets.
+int g_max_sockets = 256;
 
-// static
-uint16 HttpNetworkSession::g_fixed_http_port = 0;
-uint16 HttpNetworkSession::g_fixed_https_port = 0;
+// Default to allow up to 6 connections per host. Experiment and tuning may
+// try other values (greater than 0).  Too large may cause many problems, such
+// as home routers blocking the connections!?!?  See http://crbug.com/12066.
+int g_max_sockets_per_group = 6;
+
+// The max number of sockets to allow per proxy server.  This applies both to
+// http and SOCKS proxies.  See http://crbug.com/12066 and
+// http://crbug.com/44501 for details about proxy server connection limits.
+int g_max_sockets_per_proxy_server = 32;
+
+uint16 g_fixed_http_port = 0;
+uint16 g_fixed_https_port = 0;
+
+}  // namespace
 
 HttpNetworkSession::HttpNetworkSession(
-    NetworkChangeNotifier* network_change_notifier,
     HostResolver* host_resolver,
     ProxyService* proxy_service,
     ClientSocketFactory* client_socket_factory,
     SSLConfigService* ssl_config_service,
-    FlipSessionPool* flip_session_pool)
-    : network_change_notifier_(network_change_notifier),
+    SpdySessionPool* spdy_session_pool,
+    HttpAuthHandlerFactory* http_auth_handler_factory,
+    HttpNetworkDelegate* network_delegate,
+    NetLog* net_log)
+    : tcp_pool_histograms_(new ClientSocketPoolHistograms("TCP")),
+      tcp_for_http_proxy_pool_histograms_(
+          new ClientSocketPoolHistograms("TCPforHTTPProxy")),
+      http_proxy_pool_histograms_(new ClientSocketPoolHistograms("HTTPProxy")),
+      tcp_for_socks_pool_histograms_(
+          new ClientSocketPoolHistograms("TCPforSOCKS")),
+      socks_pool_histograms_(new ClientSocketPoolHistograms("SOCK")),
+      ssl_pool_histograms_(new ClientSocketPoolHistograms("SSL")),
       tcp_socket_pool_(new TCPClientSocketPool(
-          max_sockets_, max_sockets_per_group_,
-          host_resolver, client_socket_factory, network_change_notifier_)),
+          g_max_sockets, g_max_sockets_per_group, tcp_pool_histograms_,
+          host_resolver, client_socket_factory, net_log)),
+      ssl_socket_pool_(new SSLClientSocketPool(
+          g_max_sockets, g_max_sockets_per_group, ssl_pool_histograms_,
+          host_resolver, client_socket_factory, tcp_socket_pool_, NULL,
+          NULL, net_log)),
       socket_factory_(client_socket_factory),
       host_resolver_(host_resolver),
       proxy_service_(proxy_service),
       ssl_config_service_(ssl_config_service),
-      flip_session_pool_(flip_session_pool) {
+      spdy_session_pool_(spdy_session_pool),
+      http_auth_handler_factory_(http_auth_handler_factory),
+      network_delegate_(network_delegate),
+      net_log_(net_log) {
   DCHECK(proxy_service);
   DCHECK(ssl_config_service);
 }
@@ -42,20 +74,98 @@
 HttpNetworkSession::~HttpNetworkSession() {
 }
 
+const scoped_refptr<HttpProxyClientSocketPool>&
+HttpNetworkSession::GetSocketPoolForHTTPProxy(const HostPortPair& http_proxy) {
+  HTTPProxySocketPoolMap::const_iterator it =
+      http_proxy_socket_pools_.find(http_proxy);
+  if (it != http_proxy_socket_pools_.end())
+    return it->second;
+
+  std::pair<HTTPProxySocketPoolMap::iterator, bool> ret =
+      http_proxy_socket_pools_.insert(
+          std::make_pair(
+              http_proxy,
+              new HttpProxyClientSocketPool(
+                  g_max_sockets_per_proxy_server, g_max_sockets_per_group,
+                  http_proxy_pool_histograms_, host_resolver_,
+                  new TCPClientSocketPool(
+                      g_max_sockets_per_proxy_server, g_max_sockets_per_group,
+                      tcp_for_http_proxy_pool_histograms_, host_resolver_,
+                      socket_factory_, net_log_),
+                  net_log_)));
+
+  return ret.first->second;
+}
+
+const scoped_refptr<SOCKSClientSocketPool>&
+HttpNetworkSession::GetSocketPoolForSOCKSProxy(
+    const HostPortPair& socks_proxy) {
+  SOCKSSocketPoolMap::const_iterator it = socks_socket_pools_.find(socks_proxy);
+  if (it != socks_socket_pools_.end())
+    return it->second;
+
+  std::pair<SOCKSSocketPoolMap::iterator, bool> ret =
+      socks_socket_pools_.insert(
+          std::make_pair(socks_proxy, new SOCKSClientSocketPool(
+              g_max_sockets_per_proxy_server, g_max_sockets_per_group,
+              socks_pool_histograms_, host_resolver_,
+              new TCPClientSocketPool(g_max_sockets_per_proxy_server,
+                  g_max_sockets_per_group, tcp_for_socks_pool_histograms_,
+                  host_resolver_, socket_factory_, net_log_),
+              net_log_)));
+
+  return ret.first->second;
+}
+
+const scoped_refptr<SSLClientSocketPool>&
+HttpNetworkSession::GetSocketPoolForSSLWithProxy(
+    const HostPortPair& proxy_server) {
+  SSLSocketPoolMap::const_iterator it =
+      ssl_socket_pools_for_proxies_.find(proxy_server);
+  if (it != ssl_socket_pools_for_proxies_.end())
+    return it->second;
+
+  SSLClientSocketPool* new_pool = new SSLClientSocketPool(
+      g_max_sockets_per_proxy_server, g_max_sockets_per_group,
+      ssl_pool_histograms_, host_resolver_, socket_factory_,
+      NULL,
+      GetSocketPoolForHTTPProxy(proxy_server),
+      GetSocketPoolForSOCKSProxy(proxy_server),
+      net_log_);
+
+  std::pair<SSLSocketPoolMap::iterator, bool> ret =
+      ssl_socket_pools_for_proxies_.insert(std::make_pair(proxy_server,
+                                                          new_pool));
+
+  return ret.first->second;
+}
+
 // static
 void HttpNetworkSession::set_max_sockets_per_group(int socket_count) {
-  DCHECK(0 < socket_count);
+  DCHECK_LT(0, socket_count);
   // The following is a sanity check... but we should NEVER be near this value.
-  DCHECK(100 > socket_count);
-  max_sockets_per_group_ = socket_count;
+  DCHECK_GT(100, socket_count);
+  g_max_sockets_per_group = socket_count;
 }
 
-void HttpNetworkSession::ReplaceTCPSocketPool() {
-  tcp_socket_pool_ = new TCPClientSocketPool(max_sockets_,
-                                             max_sockets_per_group_,
-                                             host_resolver_,
-                                             socket_factory_,
-                                             network_change_notifier_);
+// static
+uint16 HttpNetworkSession::fixed_http_port() {
+  return g_fixed_http_port;
 }
 
-} //  namespace net
+// static
+void HttpNetworkSession::set_fixed_http_port(uint16 port) {
+  g_fixed_http_port = port;
+}
+
+// static
+uint16 HttpNetworkSession::fixed_https_port() {
+  return g_fixed_https_port;
+}
+
+// static
+void HttpNetworkSession::set_fixed_https_port(uint16 port) {
+  g_fixed_https_port = port;
+}
+
+}  //  namespace net
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 32bcdee..de57eba 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -1,89 +1,176 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_HTTP_HTTP_NETWORK_SESSION_H_
 #define NET_HTTP_HTTP_NETWORK_SESSION_H_
 
+#include <map>
+#include "base/non_thread_safe.h"
 #include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/host_resolver.h"
 #include "net/base/ssl_client_auth_cache.h"
 #include "net/base/ssl_config_service.h"
+#include "net/http/http_alternate_protocols.h"
 #include "net/http/http_auth_cache.h"
+#include "net/http/http_network_delegate.h"
+#include "net/http/http_network_transaction.h"
+#include "net/http/http_proxy_client_socket_pool.h"
 #include "net/proxy/proxy_service.h"
+#include "net/socket/client_socket_pool_histograms.h"
+#include "net/socket/socks_client_socket_pool.h"
+#include "net/socket/ssl_client_socket_pool.h"
 #include "net/socket/tcp_client_socket_pool.h"
+#include "net/spdy/spdy_settings_storage.h"
 
 namespace net {
 
 class ClientSocketFactory;
-class FlipSessionPool;
-class NetworkChangeNotifier;
+class HttpAuthHandlerFactory;
+class HttpNetworkDelegate;
+class HttpNetworkSessionPeer;
+class SpdySessionPool;
 
 // This class holds session objects used by HttpNetworkTransaction objects.
-class HttpNetworkSession : public base::RefCounted<HttpNetworkSession> {
+class HttpNetworkSession : public base::RefCounted<HttpNetworkSession>,
+                           public NonThreadSafe {
  public:
   HttpNetworkSession(
-      NetworkChangeNotifier* network_change_notifier,
       HostResolver* host_resolver,
       ProxyService* proxy_service,
       ClientSocketFactory* client_socket_factory,
       SSLConfigService* ssl_config_service,
-      FlipSessionPool* flip_session_pool);
+      SpdySessionPool* spdy_session_pool,
+      HttpAuthHandlerFactory* http_auth_handler_factory,
+      HttpNetworkDelegate* network_delegate,
+      NetLog* net_log);
 
   HttpAuthCache* auth_cache() { return &auth_cache_; }
   SSLClientAuthCache* ssl_client_auth_cache() {
     return &ssl_client_auth_cache_;
   }
 
+  const HttpAlternateProtocols& alternate_protocols() const {
+    return alternate_protocols_;
+  }
+  HttpAlternateProtocols* mutable_alternate_protocols() {
+    return &alternate_protocols_;
+  }
+
+  // Access to the SpdySettingsStorage
+  const SpdySettingsStorage& spdy_settings() const {
+    return spdy_settings_;
+  }
+  SpdySettingsStorage* mutable_spdy_settings() {
+    return &spdy_settings_;
+  }
+
   // TCP sockets come from the tcp_socket_pool().
-  TCPClientSocketPool* tcp_socket_pool() { return tcp_socket_pool_; }
-  // SSL sockets come frmo the socket_factory().
+  const scoped_refptr<TCPClientSocketPool>& tcp_socket_pool() {
+    return tcp_socket_pool_;
+  }
+
+  const scoped_refptr<SSLClientSocketPool>& ssl_socket_pool() {
+    return ssl_socket_pool_;
+  }
+
+  const scoped_refptr<SOCKSClientSocketPool>& GetSocketPoolForSOCKSProxy(
+      const HostPortPair& socks_proxy);
+
+  const scoped_refptr<HttpProxyClientSocketPool>& GetSocketPoolForHTTPProxy(
+      const HostPortPair& http_proxy);
+
+  const scoped_refptr<SSLClientSocketPool>& GetSocketPoolForSSLWithProxy(
+      const HostPortPair& proxy_server);
+
+  // SSL sockets come from the socket_factory().
   ClientSocketFactory* socket_factory() { return socket_factory_; }
   HostResolver* host_resolver() { return host_resolver_; }
   ProxyService* proxy_service() { return proxy_service_; }
   SSLConfigService* ssl_config_service() { return ssl_config_service_; }
-  const scoped_refptr<FlipSessionPool>& flip_session_pool() {
-    return flip_session_pool_;
+  const scoped_refptr<SpdySessionPool>& spdy_session_pool() {
+    return spdy_session_pool_;
   }
-
-  // Replace the current socket pool with a new one.  This effectively
-  // abandons the current pool.  This is only used for debugging.
-  void ReplaceTCPSocketPool();
+  HttpAuthHandlerFactory* http_auth_handler_factory() {
+    return http_auth_handler_factory_;
+  }
+  HttpNetworkDelegate* network_delegate() {
+    return network_delegate_;
+  }
 
   static void set_max_sockets_per_group(int socket_count);
 
-  static uint16 fixed_http_port() { return g_fixed_http_port; }
-  static void set_fixed_http_port(uint16 port) { g_fixed_http_port = port; }
+  static uint16 fixed_http_port();
+  static void set_fixed_http_port(uint16 port);
 
-  static uint16 fixed_https_port() { return g_fixed_https_port; }
-  static void set_fixed_https_port(uint16 port) { g_fixed_https_port = port; }
+  static uint16 fixed_https_port();
+  static void set_fixed_https_port(uint16 port);
+
+#ifdef UNIT_TEST
+  void FlushSocketPools() {
+    if (ssl_socket_pool_.get())
+      ssl_socket_pool_->Flush();
+    if (tcp_socket_pool_.get())
+      tcp_socket_pool_->Flush();
+
+    for (SSLSocketPoolMap::const_iterator it =
+        ssl_socket_pools_for_proxies_.begin();
+        it != ssl_socket_pools_for_proxies_.end();
+        it++)
+      it->second->Flush();
+
+    for (SOCKSSocketPoolMap::const_iterator it =
+        socks_socket_pools_.begin();
+        it != socks_socket_pools_.end();
+        it++)
+      it->second->Flush();
+
+    for (HTTPProxySocketPoolMap::const_iterator it =
+        http_proxy_socket_pools_.begin();
+        it != http_proxy_socket_pools_.end();
+        it++)
+      it->second->Flush();
+  }
+#endif
 
  private:
+  typedef std::map<HostPortPair, scoped_refptr<HttpProxyClientSocketPool> >
+      HTTPProxySocketPoolMap;
+  typedef std::map<HostPortPair, scoped_refptr<SOCKSClientSocketPool> >
+      SOCKSSocketPoolMap;
+  typedef std::map<HostPortPair, scoped_refptr<SSLClientSocketPool> >
+      SSLSocketPoolMap;
+
   friend class base::RefCounted<HttpNetworkSession>;
-  FRIEND_TEST(HttpNetworkTransactionTest, GroupNameForProxyConnections);
+  friend class HttpNetworkSessionPeer;
 
   ~HttpNetworkSession();
 
-  // Total limit of sockets. Not a constant to allow experiments.
-  static int max_sockets_;
-
-  // Default to allow up to 6 connections per host. Experiment and tuning may
-  // try other values (greater than 0).  Too large may cause many problems, such
-  // as home routers blocking the connections!?!?
-  static int max_sockets_per_group_;
-
-  static uint16 g_fixed_http_port;
-  static uint16 g_fixed_https_port;
-
   HttpAuthCache auth_cache_;
   SSLClientAuthCache ssl_client_auth_cache_;
-  NetworkChangeNotifier* const network_change_notifier_;
+  HttpAlternateProtocols alternate_protocols_;
+  scoped_refptr<ClientSocketPoolHistograms> tcp_pool_histograms_;
+  scoped_refptr<ClientSocketPoolHistograms> tcp_for_http_proxy_pool_histograms_;
+  scoped_refptr<ClientSocketPoolHistograms> http_proxy_pool_histograms_;
+  scoped_refptr<ClientSocketPoolHistograms> tcp_for_socks_pool_histograms_;
+  scoped_refptr<ClientSocketPoolHistograms> socks_pool_histograms_;
+  scoped_refptr<ClientSocketPoolHistograms> ssl_pool_histograms_;
   scoped_refptr<TCPClientSocketPool> tcp_socket_pool_;
+  scoped_refptr<SSLClientSocketPool> ssl_socket_pool_;
+  HTTPProxySocketPoolMap http_proxy_socket_pools_;
+  SOCKSSocketPoolMap socks_socket_pools_;
+  SSLSocketPoolMap ssl_socket_pools_for_proxies_;
   ClientSocketFactory* socket_factory_;
   scoped_refptr<HostResolver> host_resolver_;
   scoped_refptr<ProxyService> proxy_service_;
   scoped_refptr<SSLConfigService> ssl_config_service_;
-  scoped_refptr<FlipSessionPool> flip_session_pool_;
+  scoped_refptr<SpdySessionPool> spdy_session_pool_;
+  HttpAuthHandlerFactory* http_auth_handler_factory_;
+  HttpNetworkDelegate* const network_delegate_;
+  NetLog* net_log_;
+  SpdySettingsStorage spdy_settings_;
 };
 
 }  // namespace net
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 4be555f..a85a1ca 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -1,41 +1,51 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/http/http_network_transaction.h"
 
-#include "base/format_macros.h"
-#include "base/scoped_ptr.h"
 #include "base/compiler_specific.h"
 #include "base/field_trial.h"
+#include "base/format_macros.h"
 #include "base/histogram.h"
+#include "base/scoped_ptr.h"
 #include "base/stats_counters.h"
+#include "base/stl_util-inl.h"
 #include "base/string_util.h"
-#include "base/trace_event.h"
 #include "build/build_config.h"
+#include "googleurl/src/gurl.h"
 #include "net/base/connection_type_histograms.h"
+#include "net/base/host_mapping_rules.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
 #include "net/base/ssl_cert_request_info.h"
+#include "net/base/ssl_connection_status_flags.h"
 #include "net/base/upload_data_stream.h"
-#include "net/flip/flip_session.h"
-#include "net/flip/flip_session_pool.h"
-#include "net/flip/flip_stream.h"
 #include "net/http/http_auth.h"
 #include "net/http/http_auth_handler.h"
+#include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_basic_stream.h"
 #include "net/http/http_chunked_decoder.h"
+#include "net/http/http_net_log_params.h"
 #include "net/http/http_network_session.h"
+#include "net/http/http_proxy_client_socket.h"
+#include "net/http/http_proxy_client_socket_pool.h"
+#include "net/http/http_request_headers.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
 #include "net/http/http_util.h"
+#include "net/http/url_security_manager.h"
 #include "net/socket/client_socket_factory.h"
-#include "net/socket/socks5_client_socket.h"
-#include "net/socket/socks_client_socket.h"
+#include "net/socket/socks_client_socket_pool.h"
 #include "net/socket/ssl_client_socket.h"
+#include "net/socket/ssl_client_socket_pool.h"
+#include "net/socket/tcp_client_socket_pool.h"
+#include "net/spdy/spdy_http_stream.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_session_pool.h"
 
 using base::Time;
 
@@ -43,95 +53,143 @@
 
 namespace {
 
+const HostMappingRules* g_host_mapping_rules = NULL;
+const std::string* g_next_protos = NULL;
+bool g_use_alternate_protocols = false;
+
+// A set of host:port strings. These are servers which we have needed to back
+// off to SSLv3 for.
+std::set<std::string>* g_tls_intolerant_servers = NULL;
+
 void BuildRequestHeaders(const HttpRequestInfo* request_info,
-                         const std::string& authorization_headers,
+                         const HttpRequestHeaders& authorization_headers,
                          const UploadDataStream* upload_data_stream,
                          bool using_proxy,
-                         std::string* request_headers) {
+                         std::string* request_line,
+                         HttpRequestHeaders* request_headers) {
   const std::string path = using_proxy ?
-      HttpUtil::SpecForRequest(request_info->url) :
-      HttpUtil::PathForRequest(request_info->url);
-  *request_headers =
-      StringPrintf("%s %s HTTP/1.1\r\nHost: %s\r\n",
-                   request_info->method.c_str(), path.c_str(),
-                   GetHostAndOptionalPort(request_info->url).c_str());
+                           HttpUtil::SpecForRequest(request_info->url) :
+                           HttpUtil::PathForRequest(request_info->url);
+  *request_line = StringPrintf(
+      "%s %s HTTP/1.1\r\n", request_info->method.c_str(), path.c_str());
+  request_headers->SetHeader(HttpRequestHeaders::kHost,
+                             GetHostAndOptionalPort(request_info->url));
 
   // For compat with HTTP/1.0 servers and proxies:
-  if (using_proxy)
-    *request_headers += "Proxy-";
-  *request_headers += "Connection: keep-alive\r\n";
-
-  if (!request_info->user_agent.empty()) {
-    StringAppendF(request_headers, "User-Agent: %s\r\n",
-                  request_info->user_agent.c_str());
+  if (using_proxy) {
+    request_headers->SetHeader(HttpRequestHeaders::kProxyConnection,
+                               "keep-alive");
+  } else {
+    request_headers->SetHeader(HttpRequestHeaders::kConnection, "keep-alive");
   }
 
   // Our consumer should have made sure that this is a safe referrer.  See for
   // instance WebCore::FrameLoader::HideReferrer.
-  if (request_info->referrer.is_valid())
-    StringAppendF(request_headers, "Referer: %s\r\n",
-                  request_info->referrer.spec().c_str());
+  if (request_info->referrer.is_valid()) {
+    request_headers->SetHeader(HttpRequestHeaders::kReferer,
+                               request_info->referrer.spec());
+  }
 
   // Add a content length header?
   if (upload_data_stream) {
-    StringAppendF(request_headers, "Content-Length: %" PRIu64 "\r\n",
-                  upload_data_stream->size());
+    request_headers->SetHeader(
+        HttpRequestHeaders::kContentLength,
+        Uint64ToString(upload_data_stream->size()));
   } else if (request_info->method == "POST" || request_info->method == "PUT" ||
              request_info->method == "HEAD") {
     // An empty POST/PUT request still needs a content length.  As for HEAD,
     // IE and Safari also add a content length header.  Presumably it is to
     // support sending a HEAD request to an URL that only expects to be sent a
     // POST or some other method that normally would have a message body.
-    *request_headers += "Content-Length: 0\r\n";
+    request_headers->SetHeader(HttpRequestHeaders::kContentLength, "0");
   }
 
   // Honor load flags that impact proxy caches.
   if (request_info->load_flags & LOAD_BYPASS_CACHE) {
-    *request_headers += "Pragma: no-cache\r\nCache-Control: no-cache\r\n";
+    request_headers->SetHeader(HttpRequestHeaders::kPragma, "no-cache");
+    request_headers->SetHeader(HttpRequestHeaders::kCacheControl, "no-cache");
   } else if (request_info->load_flags & LOAD_VALIDATE_CACHE) {
-    *request_headers += "Cache-Control: max-age=0\r\n";
+    request_headers->SetHeader(HttpRequestHeaders::kCacheControl, "max-age=0");
   }
 
-  if (!authorization_headers.empty()) {
-    *request_headers += authorization_headers;
-  }
+  request_headers->MergeFrom(authorization_headers);
 
-  // TODO(darin): Need to prune out duplicate headers.
+  // Headers that will be stripped from request_info->extra_headers to prevent,
+  // e.g., plugins from overriding headers that are controlled using other
+  // means. Otherwise a plugin could set a referrer although sending the
+  // referrer is inhibited.
+  // TODO(jochen): check whether also other headers should be stripped.
+  static const char* const kExtraHeadersToBeStripped[] = {
+    "Referer"
+  };
 
-  *request_headers += request_info->extra_headers;
-  *request_headers += "\r\n";
+  HttpRequestHeaders stripped_extra_headers;
+  stripped_extra_headers.CopyFrom(request_info->extra_headers);
+  for (size_t i = 0; i < arraysize(kExtraHeadersToBeStripped); ++i)
+    stripped_extra_headers.RemoveHeader(kExtraHeadersToBeStripped[i]);
+  request_headers->MergeFrom(stripped_extra_headers);
 }
 
-// The HTTP CONNECT method for establishing a tunnel connection is documented
-// in draft-luotonen-web-proxy-tunneling-01.txt and RFC 2817, Sections 5.2 and
-// 5.3.
-void BuildTunnelRequest(const HttpRequestInfo* request_info,
-                        const std::string& authorization_headers,
-                        std::string* request_headers) {
-  // RFC 2616 Section 9 says the Host request-header field MUST accompany all
-  // HTTP/1.1 requests.  Add "Proxy-Connection: keep-alive" for compat with
-  // HTTP/1.0 proxies such as Squid (required for NTLM authentication).
-  *request_headers = StringPrintf(
-      "CONNECT %s HTTP/1.1\r\nHost: %s\r\nProxy-Connection: keep-alive\r\n",
-      GetHostAndPort(request_info->url).c_str(),
-      GetHostAndOptionalPort(request_info->url).c_str());
+void ProcessAlternateProtocol(const HttpResponseHeaders& headers,
+                              const HostPortPair& http_host_port_pair,
+                              HttpAlternateProtocols* alternate_protocols) {
 
-  if (!request_info->user_agent.empty())
-    StringAppendF(request_headers, "User-Agent: %s\r\n",
-                  request_info->user_agent.c_str());
-
-  if (!authorization_headers.empty()) {
-    *request_headers += authorization_headers;
+  std::string alternate_protocol_str;
+  if (!headers.EnumerateHeader(NULL, HttpAlternateProtocols::kHeader,
+                               &alternate_protocol_str)) {
+    // Header is not present.
+    return;
   }
 
-  *request_headers += "\r\n";
+  std::vector<std::string> port_protocol_vector;
+  SplitString(alternate_protocol_str, ':', &port_protocol_vector);
+  if (port_protocol_vector.size() != 2) {
+    DLOG(WARNING) << HttpAlternateProtocols::kHeader
+                  << " header has too many tokens: "
+                  << alternate_protocol_str;
+    return;
+  }
+
+  int port;
+  if (!StringToInt(port_protocol_vector[0], &port) ||
+      port <= 0 || port >= 1 << 16) {
+    DLOG(WARNING) << HttpAlternateProtocols::kHeader
+                  << " header has unrecognizable port: "
+                  << port_protocol_vector[0];
+    return;
+  }
+
+  if (port_protocol_vector[1] !=
+      HttpAlternateProtocols::kProtocolStrings[
+          HttpAlternateProtocols::NPN_SPDY_1]) {
+    // Currently, we only recognize the npn-spdy protocol.
+    DLOG(WARNING) << HttpAlternateProtocols::kHeader
+                  << " header has unrecognized protocol: "
+                  << port_protocol_vector[1];
+    return;
+  }
+
+  HostPortPair host_port(http_host_port_pair);
+  if (g_host_mapping_rules)
+    g_host_mapping_rules->RewriteHost(&host_port);
+
+  if (alternate_protocols->HasAlternateProtocolFor(host_port)) {
+    const HttpAlternateProtocols::PortProtocolPair existing_alternate =
+        alternate_protocols->GetAlternateProtocolFor(host_port);
+    // If we think the alternate protocol is broken, don't change it.
+    if (existing_alternate.protocol == HttpAlternateProtocols::BROKEN)
+      return;
+  }
+
+  alternate_protocols->SetAlternateProtocolFor(
+      host_port, port, HttpAlternateProtocols::NPN_SPDY_1);
 }
 
 }  // namespace
 
 //-----------------------------------------------------------------------------
 
-std::string* HttpNetworkTransaction::g_next_protos = NULL;
+bool HttpNetworkTransaction::g_ignore_certificate_errors = false;
 
 HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session)
     : pending_auth_target_(HttpAuth::AUTH_NONE),
@@ -144,16 +202,34 @@
       connection_(new ClientSocketHandle),
       reused_socket_(false),
       headers_valid_(false),
-      logged_response_time(false),
+      logged_response_time_(false),
       using_ssl_(false),
-      proxy_mode_(kDirectConnection),
-      establishing_tunnel_(false),
-      embedded_identity_used_(false),
+      using_spdy_(false),
+      spdy_certificate_error_(OK),
+      alternate_protocol_mode_(
+          g_use_alternate_protocols ? kUnspecified :
+          kDoNotUseAlternateProtocol),
       read_buf_len_(0),
-      next_state_(STATE_NONE) {
+      next_state_(STATE_NONE),
+      establishing_tunnel_(false) {
   session->ssl_config_service()->GetSSLConfig(&ssl_config_);
   if (g_next_protos)
     ssl_config_.next_protos = *g_next_protos;
+  if (!g_tls_intolerant_servers)
+    g_tls_intolerant_servers = new std::set<std::string>;
+}
+
+// static
+void HttpNetworkTransaction::SetHostMappingRules(const std::string& rules) {
+  HostMappingRules* host_mapping_rules = new HostMappingRules();
+  host_mapping_rules->SetRulesFromString(rules);
+  delete g_host_mapping_rules;
+  g_host_mapping_rules = host_mapping_rules;
+}
+
+// static
+void HttpNetworkTransaction::SetUseAlternateProtocols(bool value) {
+  g_use_alternate_protocols = value;
 }
 
 // static
@@ -162,12 +238,17 @@
   g_next_protos = new std::string(next_protos);
 }
 
+// static
+void HttpNetworkTransaction::IgnoreCertificateErrors(bool enabled) {
+  g_ignore_certificate_errors = enabled;
+}
+
 int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
                                   CompletionCallback* callback,
-                                  LoadLog* load_log) {
+                                  const BoundNetLog& net_log) {
   SIMPLE_STATS_COUNTER("HttpNetworkTransaction.Count");
 
-  load_log_ = load_log;
+  net_log_ = net_log;
   request_ = request_info;
   start_time_ = base::Time::Now();
 
@@ -180,10 +261,18 @@
 
 int HttpNetworkTransaction::RestartIgnoringLastError(
     CompletionCallback* callback) {
-  if (connection_->socket()->IsConnectedAndIdle()) {
-    next_state_ = STATE_SEND_REQUEST;
+  if (connection_->socket() && connection_->socket()->IsConnectedAndIdle()) {
+    // TODO(wtc): Should we update any of the connection histograms that we
+    // update in DoSSLConnectComplete if |result| is OK?
+    if (using_spdy_) {
+      // TODO(cbentzel): Add auth support to spdy. See http://crbug.com/46620
+      next_state_ = STATE_SPDY_GET_STREAM;
+    } else {
+      next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
+    }
   } else {
-    connection_->socket()->Disconnect();
+    if (connection_->socket())
+      connection_->socket()->Disconnect();
     connection_->Reset();
     next_state_ = STATE_INIT_CONNECTION;
   }
@@ -221,22 +310,18 @@
     NOTREACHED();
     return ERR_UNEXPECTED;
   }
-
   pending_auth_target_ = HttpAuth::AUTH_NONE;
 
-  DCHECK(auth_identity_[target].invalid ||
-         (username.empty() && password.empty()));
+  auth_controllers_[target]->ResetAuth(username, password);
 
-  if (auth_identity_[target].invalid) {
-    // Update the username/password.
-    auth_identity_[target].source = HttpAuth::IDENT_SRC_EXTERNAL;
-    auth_identity_[target].invalid = false;
-    auth_identity_[target].username = username;
-    auth_identity_[target].password = password;
+  if (target == HttpAuth::AUTH_PROXY && using_ssl_ && proxy_info_.is_http()) {
+    DCHECK(establishing_tunnel_);
+    next_state_ = STATE_INIT_CONNECTION;
+    ResetStateForRestart();
+  } else {
+    PrepareForAuthRestart(target);
   }
 
-  PrepareForAuthRestart(target);
-
   DCHECK(user_callback_ == NULL);
   int rv = DoLoop(OK);
   if (rv == ERR_IO_PENDING)
@@ -247,29 +332,7 @@
 
 void HttpNetworkTransaction::PrepareForAuthRestart(HttpAuth::Target target) {
   DCHECK(HaveAuth(target));
-  DCHECK(auth_identity_[target].source != HttpAuth::IDENT_SRC_PATH_LOOKUP);
-
-  // Add the auth entry to the cache before restarting. We don't know whether
-  // the identity is valid yet, but if it is valid we want other transactions
-  // to know about it. If an entry for (origin, handler->realm()) already
-  // exists, we update it.
-  //
-  // If auth_identity_[target].source is HttpAuth::IDENT_SRC_NONE,
-  // auth_identity_[target] contains no identity because identity is not
-  // required yet.
-  //
-  // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in
-  // round 1 and round 2, which is redundant but correct.  It would be nice
-  // to add an auth entry to the cache only once, preferrably in round 1.
-  // See http://crbug.com/21015.
-  bool has_auth_identity =
-      auth_identity_[target].source != HttpAuth::IDENT_SRC_NONE;
-  if (has_auth_identity) {
-    session_->auth_cache()->Add(AuthOrigin(target), auth_handler_[target],
-        auth_identity_[target].username, auth_identity_[target].password,
-        AuthPath(target));
-  }
-
+  DCHECK(!establishing_tunnel_);
   bool keep_alive = false;
   // Even if the server says the connection is keep-alive, we have to be
   // able to find the end of each response in order to reuse the connection.
@@ -292,10 +355,11 @@
 }
 
 void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) {
+  DCHECK(!establishing_tunnel_);
   if (keep_alive && connection_->socket()->IsConnectedAndIdle()) {
     // We should call connection_->set_idle_time(), but this doesn't occur
     // often enough to be worth the trouble.
-    next_state_ = STATE_SEND_REQUEST;
+    next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
     connection_->set_is_reused(true);
     reused_socket_ = true;
   } else {
@@ -315,31 +379,34 @@
 
   State next_state = STATE_NONE;
 
+  scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
+  if (headers_valid_ && headers.get() && establishing_tunnel_) {
+    // We're trying to read the body of the response but we're still trying
+    // to establish an SSL tunnel through the proxy.  We can't read these
+    // bytes when establishing a tunnel because they might be controlled by
+    // an active network attacker.  We don't worry about this for HTTP
+    // because an active network attacker can already control HTTP sessions.
+    // We reach this case when the user cancels a 407 proxy auth prompt.
+    // See http://crbug.com/8473.
+    DCHECK(proxy_info_.is_http());
+    DCHECK_EQ(headers->response_code(), 407);
+    LOG(WARNING) << "Blocked proxy response with status "
+                 << headers->response_code() << " to CONNECT request for "
+                 << GetHostAndPort(request_->url) << ".";
+    return ERR_TUNNEL_CONNECTION_FAILED;
+  }
+
   // Are we using SPDY or HTTP?
-  if (spdy_stream_.get()) {
+  if (using_spdy_) {
     DCHECK(!http_stream_.get());
-    DCHECK(spdy_stream_->GetResponseInfo()->headers);
+    DCHECK(spdy_http_stream_->GetResponseInfo()->headers);
     next_state = STATE_SPDY_READ_BODY;
   } else {
-    scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
-    DCHECK(headers.get());
+    DCHECK(!spdy_http_stream_.get());
     next_state = STATE_READ_BODY;
 
     if (!connection_->is_initialized())
-      return 0;  // connection_->has been reset.  Treat like EOF.
-
-    if (establishing_tunnel_) {
-      // We're trying to read the body of the response but we're still trying
-      // to establish an SSL tunnel through the proxy.  We can't read these
-      // bytes when establishing a tunnel because they might be controlled by
-      // an active network attacker.  We don't worry about this for HTTP
-      // because an active network attacker can already control HTTP sessions.
-      // We reach this case when the user cancels a 407 proxy auth prompt.
-      // See http://crbug.com/8473.
-      DCHECK_EQ(407, headers->response_code());
-      LogBlockedTunnelResponse(headers->response_code());
-      return ERR_TUNNEL_CONNECTION_FAILED;
-    }
+      return 0;  // |*connection_| has been reset.  Treat like EOF.
   }
 
   read_buf_ = buf;
@@ -365,11 +432,17 @@
       return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
     case STATE_INIT_CONNECTION_COMPLETE:
       return connection_->GetLoadState();
+    case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE:
+    case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE:
     case STATE_SEND_REQUEST_COMPLETE:
+    case STATE_SPDY_GET_STREAM:
+    case STATE_SPDY_SEND_REQUEST_COMPLETE:
       return LOAD_STATE_SENDING_REQUEST;
     case STATE_READ_HEADERS_COMPLETE:
+    case STATE_SPDY_READ_HEADERS_COMPLETE:
       return LOAD_STATE_WAITING_FOR_RESPONSE;
     case STATE_READ_BODY_COMPLETE:
+    case STATE_SPDY_READ_BODY_COMPLETE:
       return LOAD_STATE_READING_RESPONSE;
     default:
       return LOAD_STATE_IDLE;
@@ -385,15 +458,25 @@
 
 HttpNetworkTransaction::~HttpNetworkTransaction() {
   // If we still have an open socket, then make sure to disconnect it so it
-  // won't call us back and we don't try to reuse it later on.
-  if (connection_.get() && connection_->is_initialized())
-    connection_->socket()->Disconnect();
+  // won't call us back and we don't try to reuse it later on. However,
+  // don't close the socket if we should keep the connection alive.
+  if (connection_.get() && connection_->is_initialized()) {
+    // The STATE_NONE check guarantees there are no pending socket IOs that
+    // could try to call this object back after it is deleted.
+    bool keep_alive = next_state_ == STATE_NONE &&
+                      http_stream_.get() &&
+                      http_stream_->IsResponseBodyComplete() &&
+                      http_stream_->CanFindEndOfResponse() &&
+                      GetResponseHeaders()->IsKeepAlive();
+    if (!keep_alive)
+      connection_->socket()->Disconnect();
+  }
 
   if (pac_request_)
     session_->proxy_service()->CancelPacRequest(pac_request_);
 
-  if (spdy_stream_.get())
-    spdy_stream_->Cancel();
+  if (spdy_http_stream_.get())
+    spdy_http_stream_->Cancel();
 }
 
 void HttpNetworkTransaction::DoCallback(int rv) {
@@ -422,134 +505,103 @@
     switch (state) {
       case STATE_RESOLVE_PROXY:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.resolve_proxy", request_, request_->url.spec());
         rv = DoResolveProxy();
         break;
       case STATE_RESOLVE_PROXY_COMPLETE:
         rv = DoResolveProxyComplete(rv);
-        TRACE_EVENT_END("http.resolve_proxy", request_, request_->url.spec());
         break;
       case STATE_INIT_CONNECTION:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.init_conn", request_, request_->url.spec());
         rv = DoInitConnection();
         break;
       case STATE_INIT_CONNECTION_COMPLETE:
         rv = DoInitConnectionComplete(rv);
-        TRACE_EVENT_END("http.init_conn", request_, request_->url.spec());
         break;
-      case STATE_SOCKS_CONNECT:
+      case STATE_GENERATE_PROXY_AUTH_TOKEN:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.socks_connect", request_, request_->url.spec());
-        rv = DoSOCKSConnect();
+        rv = DoGenerateProxyAuthToken();
         break;
-      case STATE_SOCKS_CONNECT_COMPLETE:
-        rv = DoSOCKSConnectComplete(rv);
-        TRACE_EVENT_END("http.socks_connect", request_, request_->url.spec());
+      case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE:
+        rv = DoGenerateProxyAuthTokenComplete(rv);
         break;
-      case STATE_SSL_CONNECT:
+      case STATE_GENERATE_SERVER_AUTH_TOKEN:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.ssl_connect", request_, request_->url.spec());
-        rv = DoSSLConnect();
+        rv = DoGenerateServerAuthToken();
         break;
-      case STATE_SSL_CONNECT_COMPLETE:
-        rv = DoSSLConnectComplete(rv);
-        TRACE_EVENT_END("http.ssl_connect", request_, request_->url.spec());
+      case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE:
+        rv = DoGenerateServerAuthTokenComplete(rv);
         break;
       case STATE_SEND_REQUEST:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.send_request", request_, request_->url.spec());
-        LoadLog::BeginEvent(load_log_,
-                            LoadLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST);
+        net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST, NULL);
         rv = DoSendRequest();
         break;
       case STATE_SEND_REQUEST_COMPLETE:
         rv = DoSendRequestComplete(rv);
-        TRACE_EVENT_END("http.send_request", request_, request_->url.spec());
-        LoadLog::EndEvent(load_log_,
-                          LoadLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST);
+        net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST, NULL);
         break;
       case STATE_READ_HEADERS:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.read_headers", request_, request_->url.spec());
-        LoadLog::BeginEvent(load_log_,
-                            LoadLog::TYPE_HTTP_TRANSACTION_READ_HEADERS);
+        net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_READ_HEADERS, NULL);
         rv = DoReadHeaders();
         break;
       case STATE_READ_HEADERS_COMPLETE:
         rv = DoReadHeadersComplete(rv);
-        TRACE_EVENT_END("http.read_headers", request_, request_->url.spec());
-        LoadLog::EndEvent(load_log_,
-                          LoadLog::TYPE_HTTP_TRANSACTION_READ_HEADERS);
+        net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_READ_HEADERS, NULL);
         break;
       case STATE_READ_BODY:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.read_body", request_, request_->url.spec());
-        LoadLog::BeginEvent(load_log_,
-                            LoadLog::TYPE_HTTP_TRANSACTION_READ_BODY);
+        net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_READ_BODY, NULL);
         rv = DoReadBody();
         break;
       case STATE_READ_BODY_COMPLETE:
         rv = DoReadBodyComplete(rv);
-        TRACE_EVENT_END("http.read_body", request_, request_->url.spec());
-        LoadLog::EndEvent(load_log_,
-                          LoadLog::TYPE_HTTP_TRANSACTION_READ_BODY);
+        net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_READ_BODY, NULL);
         break;
       case STATE_DRAIN_BODY_FOR_AUTH_RESTART:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.drain_body_for_auth_restart",
-                          request_, request_->url.spec());
-        LoadLog::BeginEvent(
-            load_log_,
-            LoadLog::TYPE_HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART);
+        net_log_.BeginEvent(
+            NetLog::TYPE_HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART, NULL);
         rv = DoDrainBodyForAuthRestart();
         break;
       case STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE:
         rv = DoDrainBodyForAuthRestartComplete(rv);
-        TRACE_EVENT_END("http.drain_body_for_auth_restart",
-                        request_, request_->url.spec());
-        LoadLog::EndEvent(
-            load_log_,
-            LoadLog::TYPE_HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART);
+        net_log_.EndEvent(
+            NetLog::TYPE_HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART, NULL);
+        break;
+      case STATE_SPDY_GET_STREAM:
+        DCHECK_EQ(OK, rv);
+        rv = DoSpdyGetStream();
+        break;
+      case STATE_SPDY_GET_STREAM_COMPLETE:
+        rv = DoSpdyGetStreamComplete(rv);
         break;
       case STATE_SPDY_SEND_REQUEST:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.send_request", request_, request_->url.spec());
-        LoadLog::BeginEvent(load_log_,
-                            LoadLog::TYPE_FLIP_TRANSACTION_SEND_REQUEST);
+        net_log_.BeginEvent(NetLog::TYPE_SPDY_TRANSACTION_SEND_REQUEST, NULL);
         rv = DoSpdySendRequest();
         break;
       case STATE_SPDY_SEND_REQUEST_COMPLETE:
         rv = DoSpdySendRequestComplete(rv);
-        TRACE_EVENT_END("http.send_request", request_, request_->url.spec());
-        LoadLog::EndEvent(load_log_,
-                          LoadLog::TYPE_FLIP_TRANSACTION_SEND_REQUEST);
+        net_log_.EndEvent(NetLog::TYPE_SPDY_TRANSACTION_SEND_REQUEST, NULL);
         break;
       case STATE_SPDY_READ_HEADERS:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.read_headers", request_, request_->url.spec());
-        LoadLog::BeginEvent(load_log_,
-                            LoadLog::TYPE_FLIP_TRANSACTION_READ_HEADERS);
+        net_log_.BeginEvent(NetLog::TYPE_SPDY_TRANSACTION_READ_HEADERS, NULL);
         rv = DoSpdyReadHeaders();
         break;
       case STATE_SPDY_READ_HEADERS_COMPLETE:
         rv = DoSpdyReadHeadersComplete(rv);
-        TRACE_EVENT_END("http.read_headers", request_, request_->url.spec());
-        LoadLog::EndEvent(load_log_,
-                          LoadLog::TYPE_FLIP_TRANSACTION_READ_HEADERS);
+        net_log_.EndEvent(NetLog::TYPE_SPDY_TRANSACTION_READ_HEADERS, NULL);
         break;
       case STATE_SPDY_READ_BODY:
         DCHECK_EQ(OK, rv);
-        TRACE_EVENT_BEGIN("http.read_body", request_, request_->url.spec());
-        LoadLog::BeginEvent(load_log_,
-                            LoadLog::TYPE_FLIP_TRANSACTION_READ_BODY);
+        net_log_.BeginEvent(NetLog::TYPE_SPDY_TRANSACTION_READ_BODY, NULL);
         rv = DoSpdyReadBody();
         break;
       case STATE_SPDY_READ_BODY_COMPLETE:
         rv = DoSpdyReadBodyComplete(rv);
-        TRACE_EVENT_END("http.read_body", request_, request_->url.spec());
-        LoadLog::EndEvent(load_log_,
-                          LoadLog::TYPE_FLIP_TRANSACTION_READ_BODY);
+        net_log_.EndEvent(NetLog::TYPE_SPDY_TRANSACTION_READ_BODY, NULL);
         break;
       default:
         NOTREACHED() << "bad state";
@@ -566,57 +618,73 @@
 
   next_state_ = STATE_RESOLVE_PROXY_COMPLETE;
 
+  // |endpoint_| indicates the final destination endpoint.
+  endpoint_ = HostPortPair(request_->url.HostNoBrackets(),
+                           request_->url.EffectiveIntPort());
+
+  // Extra URL we might be attempting to resolve to.
+  GURL alternate_endpoint_url;
+
+  // Tracks whether we are using |request_->url| or |alternate_endpoint_url|.
+  const GURL *curr_endpoint_url = &request_->url;
+
+  if (g_host_mapping_rules && g_host_mapping_rules->RewriteHost(&endpoint_)) {
+    url_canon::Replacements<char> replacements;
+    const std::string port_str = IntToString(endpoint_.port);
+    replacements.SetPort(port_str.c_str(),
+                         url_parse::Component(0, port_str.size()));
+    replacements.SetHost(endpoint_.host.c_str(),
+                         url_parse::Component(0, endpoint_.host.size()));
+    alternate_endpoint_url = curr_endpoint_url->ReplaceComponents(replacements);
+    curr_endpoint_url = &alternate_endpoint_url;
+  }
+
+  const HttpAlternateProtocols& alternate_protocols =
+      session_->alternate_protocols();
+  if (alternate_protocols.HasAlternateProtocolFor(endpoint_)) {
+    response_.was_alternate_protocol_available = true;
+    if (alternate_protocol_mode_ == kUnspecified) {
+      HttpAlternateProtocols::PortProtocolPair alternate =
+          alternate_protocols.GetAlternateProtocolFor(endpoint_);
+      if (alternate.protocol != HttpAlternateProtocols::BROKEN) {
+        DCHECK_EQ(HttpAlternateProtocols::NPN_SPDY_1, alternate.protocol);
+        endpoint_.port = alternate.port;
+        alternate_protocol_ = HttpAlternateProtocols::NPN_SPDY_1;
+        alternate_protocol_mode_ = kUsingAlternateProtocol;
+
+        url_canon::Replacements<char> replacements;
+        replacements.SetScheme("https",
+                               url_parse::Component(0, strlen("https")));
+        const std::string port_str = IntToString(endpoint_.port);
+        replacements.SetPort(port_str.c_str(),
+                             url_parse::Component(0, port_str.size()));
+        alternate_endpoint_url =
+            curr_endpoint_url->ReplaceComponents(replacements);
+        curr_endpoint_url = &alternate_endpoint_url;
+      }
+    }
+  }
+
   if (request_->load_flags & LOAD_BYPASS_PROXY) {
     proxy_info_.UseDirect();
     return OK;
   }
 
   return session_->proxy_service()->ResolveProxy(
-      request_->url, &proxy_info_, &io_callback_, &pac_request_, load_log_);
+      *curr_endpoint_url, &proxy_info_, &io_callback_, &pac_request_, net_log_);
 }
 
 int HttpNetworkTransaction::DoResolveProxyComplete(int result) {
-
   pac_request_ = NULL;
 
-  if (result != OK) {
-    DLOG(ERROR) << "Failed to resolve proxy: " << result;
-    // Fall-back to direct when there were runtime errors in the PAC script,
-    // or some other failure with the settings.
-    proxy_info_.UseDirect();
-  }
+  if (result != OK)
+    return result;
 
   // Remove unsupported proxies from the list.
   proxy_info_.RemoveProxiesWithoutScheme(
       ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_HTTP |
       ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5);
 
-  // There are four possible outcomes of having run the ProxyService:
-  //   (1) The ProxyService decided we should connect through a proxy.
-  //   (2) The ProxyService decided we should direct-connect.
-  //   (3) The ProxyService decided we should give up, as there are no more
-  //       proxies to try (this is more likely to happen during
-  //       ReconsiderProxyAfterError()).
-  //   (4) The ProxyService failed (which can happen if the PAC script
-  //       we were configured with threw a runtime exception).
-  //
-  // It is important that we fail the connection in case (3) rather than
-  // falling-back to a direct connection, since sending traffic through
-  // a proxy may be integral to the user's privacy/security model.
-  //
-  // For example if a user had configured traffic to go through the TOR
-  // anonymizing proxy to protect their privacy, it would be bad if we
-  // silently fell-back to direct connect if the proxy server were to
-  // become unreachable.
-  //
-  // In case (4) it is less obvious what the right thing to do is. On the
-  // one hand, for consistency it would be natural to hard-fail as well.
-  // However, both Firefox 3.5 and Internet Explorer 8 will silently fall-back
-  // to DIRECT in this case, so we will do the same for compatibility.
-  //
-  // For more information, see:
-  // http://www.chromium.org/developers/design-documents/proxy-settings-fallback
-
   if (proxy_info_.is_empty()) {
     // No proxies/direct to choose from. This happens when we don't support any
     // of the proxies in the returned list.
@@ -630,269 +698,333 @@
 int HttpNetworkTransaction::DoInitConnection() {
   DCHECK(!connection_->is_initialized());
   DCHECK(proxy_info_.proxy_server().is_valid());
-
   next_state_ = STATE_INIT_CONNECTION_COMPLETE;
 
-  using_ssl_ = request_->url.SchemeIs("https");
-
-  if (proxy_info_.is_direct())
-    proxy_mode_ = kDirectConnection;
-  else if (proxy_info_.proxy_server().is_socks())
-    proxy_mode_ = kSOCKSProxy;
-  else if (using_ssl_)
-    proxy_mode_ = kHTTPProxyUsingTunnel;
-  else
-    proxy_mode_ = kHTTPProxy;
-
-  // Build the string used to uniquely identify connections of this type.
-  // Determine the host and port to connect to.
-  std::string connection_group;
-  std::string host;
-  int port;
-  if (proxy_mode_ != kDirectConnection) {
-    ProxyServer proxy_server = proxy_info_.proxy_server();
-    connection_group = "proxy/" + proxy_server.ToURI() + "/";
-    host = proxy_server.HostNoBrackets();
-    port = proxy_server.port();
-  } else {
-    host = request_->url.HostNoBrackets();
-    port = request_->url.EffectiveIntPort();
+  // Now that the proxy server has been resolved, create the auth_controllers_.
+  for (int i = 0; i < HttpAuth::AUTH_NUM_TARGETS; i++) {
+    HttpAuth::Target target = static_cast<HttpAuth::Target>(i);
+    if (!auth_controllers_[target].get())
+      auth_controllers_[target] = new HttpAuthController(target,
+                                                         AuthURL(target),
+                                                         session_);
   }
 
+  bool want_spdy = alternate_protocol_mode_ == kUsingAlternateProtocol
+      && alternate_protocol_ == HttpAlternateProtocols::NPN_SPDY_1;
+  using_ssl_ = request_->url.SchemeIs("https") || want_spdy;
+  using_spdy_ = false;
+  response_.was_fetched_via_proxy = !proxy_info_.is_direct();
+
   // Use the fixed testing ports if they've been provided.
   if (using_ssl_) {
     if (session_->fixed_https_port() != 0)
-      port = session_->fixed_https_port();
+      endpoint_.port = session_->fixed_https_port();
   } else if (session_->fixed_http_port() != 0) {
-    port = session_->fixed_http_port();
+    endpoint_.port = session_->fixed_http_port();
   }
 
-  // For a connection via HTTP proxy not using CONNECT, the connection
-  // is to the proxy server only. For all other cases
-  // (direct, HTTP proxy CONNECT, SOCKS), the connection is upto the
-  // url endpoint. Hence we append the url data into the connection_group.
-  if (proxy_mode_ != kHTTPProxy)
-    connection_group.append(request_->url.GetOrigin().spec());
+  // Check first if we have a spdy session for this group.  If so, then go
+  // straight to using that.
+  if (session_->spdy_session_pool()->HasSession(endpoint_)) {
+    using_spdy_ = true;
+    reused_socket_ = true;
+    next_state_ = STATE_SPDY_GET_STREAM;
+    return OK;
+  }
 
+  // Build the string used to uniquely identify connections of this type.
+  // Determine the host and port to connect to.
+  std::string connection_group = endpoint_.ToString();
   DCHECK(!connection_group.empty());
 
-  HostResolver::RequestInfo resolve_info(host, port);
-  resolve_info.set_priority(request_->priority);
-
-  // The referrer is used by the DNS prefetch system to correlate resolutions
-  // with the page that triggered them. It doesn't impact the actual addresses
-  // that we resolve to.
-  resolve_info.set_referrer(request_->referrer);
+  if (using_ssl_)
+    connection_group = StringPrintf("ssl/%s", connection_group.c_str());
 
   // If the user is refreshing the page, bypass the host cache.
-  if (request_->load_flags & LOAD_BYPASS_CACHE ||
-      request_->load_flags & LOAD_DISABLE_CACHE) {
-    resolve_info.set_allow_cached_response(false);
+  bool disable_resolver_cache = request_->load_flags & LOAD_BYPASS_CACHE ||
+                                request_->load_flags & LOAD_VALIDATE_CACHE ||
+                                request_->load_flags & LOAD_DISABLE_CACHE;
+
+  // Build up the connection parameters.
+  scoped_refptr<TCPSocketParams> tcp_params;
+  scoped_refptr<HttpProxySocketParams> http_proxy_params;
+  scoped_refptr<SOCKSSocketParams> socks_params;
+  scoped_ptr<HostPortPair> proxy_host_port;
+
+  if (proxy_info_.is_direct()) {
+    tcp_params = new TCPSocketParams(endpoint_, request_->priority,
+                                     request_->referrer,
+                                     disable_resolver_cache);
+  } else {
+    ProxyServer proxy_server = proxy_info_.proxy_server();
+    proxy_host_port.reset(new HostPortPair(proxy_server.HostNoBrackets(),
+                                           proxy_server.port()));
+    scoped_refptr<TCPSocketParams> proxy_tcp_params =
+        new TCPSocketParams(*proxy_host_port, request_->priority,
+                            request_->referrer, disable_resolver_cache);
+
+    if (proxy_info_.is_http()) {
+      scoped_refptr<HttpAuthController> http_proxy_auth;
+      if (using_ssl_) {
+        http_proxy_auth = auth_controllers_[HttpAuth::AUTH_PROXY];
+        establishing_tunnel_ = true;
+      }
+      http_proxy_params = new HttpProxySocketParams(proxy_tcp_params,
+                                                    request_->url, endpoint_,
+                                                    http_proxy_auth,
+                                                    using_ssl_);
+    } else {
+      DCHECK(proxy_info_.is_socks());
+      char socks_version;
+      if (proxy_server.scheme() == ProxyServer::SCHEME_SOCKS5)
+        socks_version = '5';
+      else
+        socks_version = '4';
+      connection_group =
+          StringPrintf("socks%c/%s", socks_version, connection_group.c_str());
+
+      socks_params = new SOCKSSocketParams(proxy_tcp_params,
+                                           socks_version == '5',
+                                           endpoint_,
+                                           request_->priority,
+                                           request_->referrer);
+    }
   }
 
-  // Check first if we have a flip session for this group.  If so, then go
-  // straight to using that.
-  if (session_->flip_session_pool()->HasSession(resolve_info))
-    return OK;
+  // Deal with SSL - which layers on top of any given proxy.
+  if (using_ssl_) {
+    if (ContainsKey(*g_tls_intolerant_servers, GetHostAndPort(request_->url))) {
+      LOG(WARNING) << "Falling back to SSLv3 because host is TLS intolerant: "
+                   << GetHostAndPort(request_->url);
+      ssl_config_.ssl3_fallback = true;
+      ssl_config_.tls1_enabled = false;
+    }
 
-  int rv = connection_->Init(connection_group, resolve_info, request_->priority,
-                             &io_callback_, session_->tcp_socket_pool(),
-                             load_log_);
-  return rv;
+    UMA_HISTOGRAM_ENUMERATION("Net.ConnectionUsedSSLv3Fallback",
+                              (int) ssl_config_.ssl3_fallback, 2);
+
+    int load_flags = request_->load_flags;
+    if (g_ignore_certificate_errors)
+      load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS;
+    if (request_->load_flags & LOAD_VERIFY_EV_CERT)
+      ssl_config_.verify_ev_cert = true;
+
+    scoped_refptr<SSLSocketParams> ssl_params =
+        new SSLSocketParams(tcp_params, http_proxy_params, socks_params,
+                            proxy_info_.proxy_server().scheme(),
+                            request_->url.HostNoBrackets(), ssl_config_,
+                            load_flags, want_spdy);
+
+    scoped_refptr<SSLClientSocketPool> ssl_pool;
+    if (proxy_info_.is_direct())
+      ssl_pool = session_->ssl_socket_pool();
+    else
+      ssl_pool = session_->GetSocketPoolForSSLWithProxy(*proxy_host_port);
+
+    return connection_->Init(connection_group, ssl_params, request_->priority,
+                             &io_callback_, ssl_pool, net_log_);
+  }
+
+  // Finally, get the connection started.
+  if (proxy_info_.is_http()) {
+    return connection_->Init(
+        connection_group, http_proxy_params, request_->priority, &io_callback_,
+        session_->GetSocketPoolForHTTPProxy(*proxy_host_port), net_log_);
+  }
+
+  if (proxy_info_.is_socks()) {
+    return connection_->Init(
+        connection_group, socks_params, request_->priority, &io_callback_,
+        session_->GetSocketPoolForSOCKSProxy(*proxy_host_port), net_log_);
+  }
+
+  DCHECK(proxy_info_.is_direct());
+  return connection_->Init(connection_group, tcp_params, request_->priority,
+                           &io_callback_, session_->tcp_socket_pool(),
+                           net_log_);
 }
 
 int HttpNetworkTransaction::DoInitConnectionComplete(int result) {
-  if (result < 0) {
-    UpdateConnectionTypeHistograms(CONNECTION_HTTP, false);
-    return ReconsiderProxyAfterError(result);
+  // |result| may be the result of any of the stacked pools. The following
+  // logic is used when determining how to interpret an error.
+  // If |result| < 0:
+  //   and connection_->socket() != NULL, then the SSL handshake ran and it
+  //     is a potentially recoverable error.
+  //   and connection_->socket == NULL and connection_->is_ssl_error() is true,
+  //     then the SSL handshake ran with an unrecoverable error.
+  //   otherwise, the error came from one of the other pools.
+  bool ssl_started = using_ssl_ && (result == OK || connection_->socket() ||
+                                    connection_->is_ssl_error());
+
+  if (ssl_started && (result == OK || IsCertificateError(result))) {
+    SSLClientSocket* ssl_socket =
+      static_cast<SSLClientSocket*>(connection_->socket());
+    if (ssl_socket->wasNpnNegotiated()) {
+      response_.was_npn_negotiated = true;
+      std::string proto;
+      ssl_socket->GetNextProto(&proto);
+      if (SSLClientSocket::NextProtoFromString(proto) ==
+          SSLClientSocket::kProtoSPDY1)
+        using_spdy_ = true;
+    }
   }
 
-  DCHECK_EQ(OK, result);
+  if (result == ERR_PROXY_AUTH_REQUESTED) {
+    DCHECK(!ssl_started);
+    const HttpResponseInfo& tunnel_auth_response =
+        connection_->ssl_error_response_info();
 
-  // If we don't have an initialized connection, that means we have a flip
-  // connection waiting for us.
-  if (!connection_->is_initialized()) {
-    next_state_ = STATE_SPDY_SEND_REQUEST;
+    response_.headers = tunnel_auth_response.headers;
+    response_.auth_challenge = tunnel_auth_response.auth_challenge;
+    headers_valid_ = true;
+    pending_auth_target_ = HttpAuth::AUTH_PROXY;
     return OK;
   }
 
-  LogTCPConnectedMetrics(*connection_);
+  if ((!ssl_started && result < 0 &&
+       alternate_protocol_mode_ == kUsingAlternateProtocol) ||
+      result == ERR_NPN_NEGOTIATION_FAILED) {
+    // Mark the alternate protocol as broken and fallback.
+    MarkBrokenAlternateProtocolAndFallback();
+    return OK;
+  }
 
-  // Set the reused_socket_ flag to indicate that we are using a keep-alive
-  // connection.  This flag is used to handle errors that occur while we are
-  // trying to reuse a keep-alive connection.
-  reused_socket_ = connection_->is_reused();
-  if (reused_socket_) {
-    next_state_ = STATE_SEND_REQUEST;
-  } else {
-    // Now we have a TCP connected socket.  Perform other connection setup as
-    // needed.
-    UpdateConnectionTypeHistograms(CONNECTION_HTTP, true);
-    if (proxy_mode_ == kSOCKSProxy)
-      next_state_ = STATE_SOCKS_CONNECT;
-    else if (using_ssl_ && proxy_mode_ == kDirectConnection) {
-      next_state_ = STATE_SSL_CONNECT;
-    } else {
-      next_state_ = STATE_SEND_REQUEST;
-      if (proxy_mode_ == kHTTPProxyUsingTunnel)
-        establishing_tunnel_ = true;
+  if (result < 0 && !ssl_started)
+    return ReconsiderProxyAfterError(result);
+  establishing_tunnel_ = false;
+
+  if (connection_->socket()) {
+    LogHttpConnectedMetrics(*connection_);
+
+    // Set the reused_socket_ flag to indicate that we are using a keep-alive
+    // connection.  This flag is used to handle errors that occur while we are
+    // trying to reuse a keep-alive connection.
+    reused_socket_ = connection_->is_reused();
+    // TODO(vandebo) should we exclude SPDY in the following if?
+    if (!reused_socket_)
+      UpdateConnectionTypeHistograms(CONNECTION_HTTP);
+
+    if (!using_ssl_) {
+      DCHECK_EQ(OK, result);
+      next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
+      return result;
     }
   }
 
-  return OK;
-}
-
-int HttpNetworkTransaction::DoSOCKSConnect() {
-  DCHECK_EQ(kSOCKSProxy, proxy_mode_);
-
-  next_state_ = STATE_SOCKS_CONNECT_COMPLETE;
-
-  // Add a SOCKS connection on top of our existing transport socket.
-  ClientSocket* s = connection_->release_socket();
-  HostResolver::RequestInfo req_info(request_->url.HostNoBrackets(),
-                                     request_->url.EffectiveIntPort());
-  req_info.set_referrer(request_->referrer);
-  req_info.set_priority(request_->priority);
-
-  if (proxy_info_.proxy_server().scheme() == ProxyServer::SCHEME_SOCKS5)
-    s = new SOCKS5ClientSocket(s, req_info);
-  else
-    s = new SOCKSClientSocket(s, req_info, session_->host_resolver());
-  connection_->set_socket(s);
-  return connection_->socket()->Connect(&io_callback_, load_log_);
-}
-
-int HttpNetworkTransaction::DoSOCKSConnectComplete(int result) {
-  DCHECK_EQ(kSOCKSProxy, proxy_mode_);
-
-  if (result == OK) {
-    if (using_ssl_) {
-      next_state_ = STATE_SSL_CONNECT;
-    } else {
-      next_state_ = STATE_SEND_REQUEST;
-    }
-  } else {
-    result = ReconsiderProxyAfterError(result);
-  }
-  return result;
-}
-
-int HttpNetworkTransaction::DoSSLConnect() {
-  next_state_ = STATE_SSL_CONNECT_COMPLETE;
-
-  if (request_->load_flags & LOAD_VERIFY_EV_CERT)
-    ssl_config_.verify_ev_cert = true;
-
-  ssl_connect_start_time_ = base::TimeTicks::Now();
-
-  // Add a SSL socket on top of our existing transport socket.
-  ClientSocket* s = connection_->release_socket();
-  s = session_->socket_factory()->CreateSSLClientSocket(
-      s, request_->url.HostNoBrackets(), ssl_config_);
-  connection_->set_socket(s);
-  return connection_->socket()->Connect(&io_callback_, load_log_);
-}
-
-int HttpNetworkTransaction::DoSSLConnectComplete(int result) {
-  SSLClientSocket* ssl_socket =
-      reinterpret_cast<SSLClientSocket*>(connection_->socket());
-
-  SSLClientSocket::NextProtoStatus status =
-      SSLClientSocket::kNextProtoUnsupported;
-  std::string proto;
-  // GetNextProto will fail and and trigger a NOTREACHED if we pass in a socket
-  // that hasn't had SSL_ImportFD called on it. If we get a certificate error
-  // here, then we know that we called SSL_ImportFD.
-  if (result == OK || IsCertificateError(result))
-    status = ssl_socket->GetNextProto(&proto);
-  static const char kSpdyProto[] = "spdy";
-  const bool use_spdy = (status == SSLClientSocket::kNextProtoNegotiated &&
-                         proto == kSpdyProto);
-
+  // Handle SSL errors below.
+  DCHECK(using_ssl_);
+  DCHECK(ssl_started);
   if (IsCertificateError(result)) {
-    if (use_spdy) {
-      // TODO(agl/willchan/wtc): We currently ignore certificate errors for
-      // spdy but we shouldn't. http://crbug.com/32020
+    if (using_spdy_ && request_->url.SchemeIs("http")) {
+      // We ignore certificate errors for http over spdy.
+      spdy_certificate_error_ = result;
       result = OK;
     } else {
       result = HandleCertificateError(result);
+      if (result == OK && !connection_->socket()->IsConnectedAndIdle()) {
+        connection_->socket()->Disconnect();
+        connection_->Reset();
+        next_state_ = STATE_INIT_CONNECTION;
+        return result;
+      }
     }
   }
 
-  if (result == OK) {
-    DCHECK(ssl_connect_start_time_ != base::TimeTicks());
-    base::TimeDelta connect_duration =
-        base::TimeTicks::Now() - ssl_connect_start_time_;
+  if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
+    response_.cert_request_info =
+        connection_->ssl_error_response_info().cert_request_info;
+    return HandleCertificateRequest(result);
+  }
+  if (result < 0)
+    return HandleSSLHandshakeError(result);
 
-    if (use_spdy) {
-      UMA_HISTOGRAM_CUSTOM_TIMES("Net.SpdyConnectionLatency",
-          connect_duration,
-          base::TimeDelta::FromMilliseconds(1),
-          base::TimeDelta::FromMinutes(10),
-          100);
-
-      UpdateConnectionTypeHistograms(CONNECTION_SPDY, true);
-      next_state_ = STATE_SPDY_SEND_REQUEST;
-    } else {
-      UMA_HISTOGRAM_CUSTOM_TIMES("Net.SSL_Connection_Latency",
-          connect_duration,
-          base::TimeDelta::FromMilliseconds(1),
-          base::TimeDelta::FromMinutes(10),
-          100);
-
-      next_state_ = STATE_SEND_REQUEST;
-    }
-  } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
-    result = HandleCertificateRequest(result);
+  if (using_spdy_) {
+    UpdateConnectionTypeHistograms(CONNECTION_SPDY);
+    // TODO(cbentzel): Add auth support to spdy. See http://crbug.com/46620
+    next_state_ = STATE_SPDY_GET_STREAM;
   } else {
-    result = HandleSSLHandshakeError(result);
+    next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
   }
-  return result;
+  return OK;
+}
+
+int HttpNetworkTransaction::DoGenerateProxyAuthToken() {
+  next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE;
+  if (!ShouldApplyProxyAuth())
+    return OK;
+  return auth_controllers_[HttpAuth::AUTH_PROXY]->MaybeGenerateAuthToken(
+      request_, &io_callback_, net_log_);
+}
+
+int HttpNetworkTransaction::DoGenerateProxyAuthTokenComplete(int rv) {
+  DCHECK_NE(ERR_IO_PENDING, rv);
+  if (rv == OK)
+    next_state_ = STATE_GENERATE_SERVER_AUTH_TOKEN;
+  return rv;
+}
+
+int HttpNetworkTransaction::DoGenerateServerAuthToken() {
+  next_state_ = STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE;
+  if (!ShouldApplyServerAuth())
+    return OK;
+  return auth_controllers_[HttpAuth::AUTH_SERVER]->MaybeGenerateAuthToken(
+      request_, &io_callback_, net_log_);
+}
+
+int HttpNetworkTransaction::DoGenerateServerAuthTokenComplete(int rv) {
+  DCHECK_NE(ERR_IO_PENDING, rv);
+  if (rv == OK)
+    next_state_ = STATE_SEND_REQUEST;
+  return rv;
 }
 
 int HttpNetworkTransaction::DoSendRequest() {
   next_state_ = STATE_SEND_REQUEST_COMPLETE;
 
   UploadDataStream* request_body = NULL;
-  if (!establishing_tunnel_ && request_->upload_data)
-    request_body = new UploadDataStream(request_->upload_data);
+  if (request_->upload_data) {
+    int error_code;
+    request_body = UploadDataStream::Create(request_->upload_data, &error_code);
+    if (!request_body)
+      return error_code;
+  }
 
   // This is constructed lazily (instead of within our Start method), so that
   // we have proxy info available.
   if (request_headers_.empty()) {
     // Figure out if we can/should add Proxy-Authentication & Authentication
     // headers.
-    bool have_proxy_auth =
-        ShouldApplyProxyAuth() &&
-        (HaveAuth(HttpAuth::AUTH_PROXY) ||
-         SelectPreemptiveAuth(HttpAuth::AUTH_PROXY));
-    bool have_server_auth =
-        ShouldApplyServerAuth() &&
-        (HaveAuth(HttpAuth::AUTH_SERVER) ||
-         SelectPreemptiveAuth(HttpAuth::AUTH_SERVER));
-
-    std::string authorization_headers;
-
-    // TODO(wtc): If BuildAuthorizationHeader fails (returns an authorization
-    // header with no credentials), we should return an error to prevent
-    // entering an infinite auth restart loop.  See http://crbug.com/21050.
+    HttpRequestHeaders authorization_headers;
+    bool have_proxy_auth = (ShouldApplyProxyAuth() &&
+                            HaveAuth(HttpAuth::AUTH_PROXY));
+    bool have_server_auth = (ShouldApplyServerAuth() &&
+                             HaveAuth(HttpAuth::AUTH_SERVER));
     if (have_proxy_auth)
-      authorization_headers.append(
-          BuildAuthorizationHeader(HttpAuth::AUTH_PROXY));
+      auth_controllers_[HttpAuth::AUTH_PROXY]->AddAuthorizationHeader(
+          &authorization_headers);
     if (have_server_auth)
-      authorization_headers.append(
-          BuildAuthorizationHeader(HttpAuth::AUTH_SERVER));
+      auth_controllers_[HttpAuth::AUTH_SERVER]->AddAuthorizationHeader(
+          &authorization_headers);
+    std::string request_line;
+    HttpRequestHeaders request_headers;
+    BuildRequestHeaders(request_, authorization_headers, request_body,
+                        !using_ssl_ && proxy_info_.is_http(), &request_line,
+                        &request_headers);
 
-    if (establishing_tunnel_) {
-      BuildTunnelRequest(request_, authorization_headers, &request_headers_);
-    } else {
-      BuildRequestHeaders(request_, authorization_headers, request_body,
-                          proxy_mode_ == kHTTPProxy, &request_headers_);
+    if (session_->network_delegate())
+      session_->network_delegate()->OnSendHttpRequest(&request_headers);
+
+    if (net_log_.HasListener()) {
+      net_log_.AddEvent(
+          NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS,
+          new NetLogHttpRequestParameter(request_line, request_headers));
     }
+
+    request_headers_ = request_line + request_headers.ToString();
   }
 
   headers_valid_ = false;
-  http_stream_.reset(new HttpBasicStream(connection_.get(), load_log_));
+  http_stream_.reset(new HttpBasicStream(connection_.get(), net_log_));
 
   return http_stream_->SendRequest(request_, request_headers_,
                                    request_body, &response_, &io_callback_);
@@ -901,24 +1033,16 @@
 int HttpNetworkTransaction::DoSendRequestComplete(int result) {
   if (result < 0)
     return HandleIOError(result);
-
   next_state_ = STATE_READ_HEADERS;
-
   return OK;
 }
 
 int HttpNetworkTransaction::DoReadHeaders() {
   next_state_ = STATE_READ_HEADERS_COMPLETE;
-
   return http_stream_->ReadResponseHeaders(&io_callback_);
 }
 
 int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() {
-  if (establishing_tunnel_) {
-    // The connection was closed before the tunnel could be established.
-    return ERR_TUNNEL_CONNECTION_FAILED;
-  }
-
   if (!response_.headers) {
     // The connection was closed before any data was sent. Likely an error
     // rather than empty HTTP/0.9 response.
@@ -940,9 +1064,30 @@
                  << " during SSL renegotiation";
       result = ERR_CERT_ERROR_IN_SSL_RENEGOTIATION;
     } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
+      response_.cert_request_info = new SSLCertRequestInfo;
+      SSLClientSocket* ssl_socket =
+          static_cast<SSLClientSocket*>(connection_->socket());
+      ssl_socket->GetSSLCertRequestInfo(response_.cert_request_info);
       result = HandleCertificateRequest(result);
       if (result == OK)
         return result;
+    } else if ((result == ERR_SSL_DECOMPRESSION_FAILURE_ALERT ||
+                result == ERR_SSL_BAD_RECORD_MAC_ALERT) &&
+               ssl_config_.tls1_enabled &&
+               !SSLConfigService::IsKnownStrictTLSServer(request_->url.host())){
+      // Some buggy servers select DEFLATE compression when offered and then
+      // fail to ever decompress anything. They will send a fatal alert telling
+      // us this. Normally we would pick this up during the handshake because
+      // our Finished message is compressed and we'll never get the server's
+      // Finished if it fails to process ours.
+      //
+      // However, with False Start, we'll believe that the handshake is
+      // complete as soon as we've /sent/ our Finished message. In this case,
+      // we only find out that the server is buggy here, when we try to read
+      // the initial reply.
+      g_tls_intolerant_servers->insert(GetHostAndPort(request_->url));
+      ResetConnectionAndRequestForResend();
+      return OK;
     }
   }
 
@@ -957,26 +1102,23 @@
   // After we call RestartWithAuth a new response_time will be recorded, and
   // we need to be cautious about incorrectly logging the duration across the
   // authentication activity.
-  if (!logged_response_time) {
-    LogTransactionConnectedMetrics();
-    logged_response_time = true;
-  }
+  LogTransactionConnectedMetrics();
 
   if (result == ERR_CONNECTION_CLOSED) {
+    // For now, if we get at least some data, we do the best we can to make
+    // sense of it and send it back up the stack.
     int rv = HandleConnectionClosedBeforeEndOfHeaders();
     if (rv != OK)
       return rv;
-    // TODO(wtc): Traditionally this code has returned 0 when reading a closed
-    // socket.  That is partially corrected in classes that we call, but
-    // callers need to be updated.
-    result = 0;
+  }
+
+  if (net_log_.HasListener()) {
+    net_log_.AddEvent(
+        NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS,
+        new NetLogHttpResponseParameter(response_.headers));
   }
 
   if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) {
-    // Require the "HTTP/1.x" status line for SSL CONNECT.
-    if (establishing_tunnel_)
-      return ERR_TUNNEL_CONNECTION_FAILED;
-
     // HTTP/0.9 doesn't support the PUT method, so lack of response headers
     // indicates a buggy server.  See:
     // https://bugzilla.mozilla.org/show_bug.cgi?id=193921
@@ -984,44 +1126,6 @@
       return ERR_METHOD_NOT_SUPPORTED;
   }
 
-  if (establishing_tunnel_) {
-    switch (response_.headers->response_code()) {
-      case 200:  // OK
-        if (http_stream_->IsMoreDataBuffered()) {
-          // The proxy sent extraneous data after the headers.
-          return ERR_TUNNEL_CONNECTION_FAILED;
-        }
-        next_state_ = STATE_SSL_CONNECT;
-        // Reset for the real request and response headers.
-        request_headers_.clear();
-        http_stream_.reset(new HttpBasicStream(connection_.get(), load_log_));
-        headers_valid_ = false;
-        establishing_tunnel_ = false;
-        return OK;
-
-      // We aren't able to CONNECT to the remote host through the proxy.  We
-      // need to be very suspicious about the response because an active network
-      // attacker can force us into this state by masquerading as the proxy.
-      // The only safe thing to do here is to fail the connection because our
-      // client is expecting an SSL protected response.
-      // See http://crbug.com/7338.
-      case 407:  // Proxy Authentication Required
-        // We need this status code to allow proxy authentication.  Our
-        // authentication code is smart enough to avoid being tricked by an
-        // active network attacker.
-        break;
-      default:
-        // For all other status codes, we conservatively fail the CONNECT
-        // request.
-        // We lose something by doing this.  We have seen proxy 403, 404, and
-        // 501 response bodies that contain a useful error message.  For
-        // example, Squid uses a 404 response to report the DNS error: "The
-        // domain name does not exist."
-        LogBlockedTunnelResponse(response_.headers->response_code());
-        return ERR_TUNNEL_CONNECTION_FAILED;
-    }
-  }
-
   // Check for an intermediate 100 Continue response.  An origin server is
   // allowed to send this response even if we didn't ask for it, so we just
   // need to skip over it.
@@ -1033,13 +1137,17 @@
     return OK;
   }
 
+  ProcessAlternateProtocol(*response_.headers,
+                           endpoint_,
+                           session_->mutable_alternate_protocols());
+
   int rv = HandleAuthChallenge();
   if (rv != OK)
     return rv;
 
-  if (using_ssl_ && !establishing_tunnel_) {
+  if (using_ssl_) {
     SSLClientSocket* ssl_socket =
-        reinterpret_cast<SSLClientSocket*>(connection_->socket());
+        static_cast<SSLClientSocket*>(connection_->socket());
     ssl_socket->GetSSLInfo(&response_.ssl_info);
   }
 
@@ -1059,21 +1167,14 @@
 
 int HttpNetworkTransaction::DoReadBodyComplete(int result) {
   // We are done with the Read call.
-  DCHECK(!establishing_tunnel_) <<
-      "We should never read a response body of a tunnel.";
-
   bool done = false, keep_alive = false;
-  if (result < 0) {
-    // Error or closed connection while reading the socket.
+  if (result <= 0)
     done = true;
-    // TODO(wtc): Traditionally this code has returned 0 when reading a closed
-    // socket.  That is partially corrected in classes that we call, but
-    // callers need to be updated.
-    if (result == ERR_CONNECTION_CLOSED)
-      result = 0;
-  } else if (http_stream_->IsResponseBodyComplete()) {
+
+  if (http_stream_->IsResponseBodyComplete()) {
     done = true;
-    keep_alive = GetResponseHeaders()->IsKeepAlive();
+    if (http_stream_->CanFindEndOfResponse())
+      keep_alive = GetResponseHeaders()->IsKeepAlive();
   }
 
   // Clean up connection_->if we are done.
@@ -1126,36 +1227,65 @@
   return OK;
 }
 
-int HttpNetworkTransaction::DoSpdySendRequest() {
-  next_state_ = STATE_SPDY_SEND_REQUEST_COMPLETE;
-  CHECK(!spdy_stream_.get());
+int HttpNetworkTransaction::DoSpdyGetStream() {
+  next_state_ = STATE_SPDY_GET_STREAM_COMPLETE;
+  CHECK(!spdy_http_stream_.get());
 
   // First we get a SPDY session.  Theoretically, we've just negotiated one, but
   // if one already exists, then screw it, use the existing one!  Otherwise,
   // use the existing TCP socket.
 
-  HostResolver::RequestInfo req_info(request_->url.HostNoBrackets(),
-                                     request_->url.EffectiveIntPort());
-  req_info.set_priority(request_->priority);
-  const scoped_refptr<FlipSessionPool> spdy_pool =
-      session_->flip_session_pool();
-  scoped_refptr<FlipSession> spdy_session;
+  const scoped_refptr<SpdySessionPool> spdy_pool =
+      session_->spdy_session_pool();
+  scoped_refptr<SpdySession> spdy_session;
 
-  if (spdy_pool->HasSession(req_info)) {
-    spdy_session = spdy_pool->Get(req_info, session_);
+  if (spdy_pool->HasSession(endpoint_)) {
+    spdy_session = spdy_pool->Get(endpoint_, session_, net_log_);
   } else {
-    spdy_session = spdy_pool->GetFlipSessionFromSocket(
-        req_info, session_, connection_.release());
+    // SPDY is negotiated using the TLS next protocol negotiation (NPN)
+    // extension, so |connection_| must contain an SSLClientSocket.
+    DCHECK(using_ssl_);
+    CHECK(connection_->socket());
+    int error = spdy_pool->GetSpdySessionFromSSLSocket(
+        endpoint_, session_, connection_.release(), net_log_,
+        spdy_certificate_error_, &spdy_session);
+    if (error != OK)
+      return error;
   }
 
   CHECK(spdy_session.get());
+  if(spdy_session->IsClosed())
+    return ERR_CONNECTION_CLOSED;
 
-  UploadDataStream* upload_data = request_->upload_data ?
-      new UploadDataStream(request_->upload_data) : NULL;
   headers_valid_ = false;
-  spdy_stream_ = spdy_session->GetOrCreateStream(
-      *request_, upload_data, load_log_);
-  return spdy_stream_->SendRequest(upload_data, &response_, &io_callback_);
+
+  spdy_http_stream_.reset(new SpdyHttpStream());
+  return spdy_http_stream_->InitializeStream(spdy_session, *request_,
+                                             net_log_, &io_callback_);
+}
+
+int HttpNetworkTransaction::DoSpdyGetStreamComplete(int result) {
+  if (result < 0)
+    return result;
+
+  next_state_ = STATE_SPDY_SEND_REQUEST;
+  return OK;
+}
+
+int HttpNetworkTransaction::DoSpdySendRequest() {
+  next_state_ = STATE_SPDY_SEND_REQUEST_COMPLETE;
+
+  UploadDataStream* upload_data_stream = NULL;
+  if (request_->upload_data) {
+    int error_code = OK;
+    upload_data_stream = UploadDataStream::Create(request_->upload_data,
+                                                  &error_code);
+    if (!upload_data_stream)
+      return error_code;
+  }
+  spdy_http_stream_->InitializeRequest(base::Time::Now(), upload_data_stream);
+
+  return spdy_http_stream_->SendRequest(&response_, &io_callback_);
 }
 
 int HttpNetworkTransaction::DoSpdySendRequestComplete(int result) {
@@ -1168,20 +1298,23 @@
 
 int HttpNetworkTransaction::DoSpdyReadHeaders() {
   next_state_ = STATE_SPDY_READ_HEADERS_COMPLETE;
-  return spdy_stream_->ReadResponseHeaders(&io_callback_);
+  return spdy_http_stream_->ReadResponseHeaders(&io_callback_);
 }
 
 int HttpNetworkTransaction::DoSpdyReadHeadersComplete(int result) {
   // TODO(willchan): Flesh out the support for HTTP authentication here.
   if (result == OK)
     headers_valid_ = true;
+
+  LogTransactionConnectedMetrics();
+
   return result;
 }
 
 int HttpNetworkTransaction::DoSpdyReadBody() {
   next_state_ = STATE_SPDY_READ_BODY_COMPLETE;
 
-  return spdy_stream_->ReadResponseBody(
+  return spdy_http_stream_->ReadResponseBody(
       read_buf_, read_buf_len_, &io_callback_);
 }
 
@@ -1190,79 +1323,37 @@
   read_buf_len_ = 0;
 
   if (result <= 0)
-    spdy_stream_ = NULL;
+    spdy_http_stream_.reset();
 
   return result;
 }
 
-void HttpNetworkTransaction::LogTCPConnectedMetrics(
+void HttpNetworkTransaction::LogHttpConnectedMetrics(
     const ClientSocketHandle& handle) {
-  const base::TimeDelta time_to_obtain_connected_socket =
-      base::TimeTicks::Now() - handle.init_time();
-
-  static const bool use_late_binding_histogram =
-      !FieldTrial::MakeName("", "SocketLateBinding").empty();
-
-  if (handle.reuse_type() == ClientSocketHandle::UNUSED) {
-    UMA_HISTOGRAM_CUSTOM_TIMES(
-        "Net.HttpConnectionLatency",
-        time_to_obtain_connected_socket,
-        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
-        100);
-  }
-
-  UMA_HISTOGRAM_ENUMERATION("Net.TCPSocketType", handle.reuse_type(),
-      ClientSocketHandle::NUM_TYPES);
-
-  if (use_late_binding_histogram) {
-    UMA_HISTOGRAM_ENUMERATION(
-        FieldTrial::MakeName("Net.TCPSocketType", "SocketLateBinding"),
-        handle.reuse_type(), ClientSocketHandle::NUM_TYPES);
-  }
-
-  UMA_HISTOGRAM_CLIPPED_TIMES(
-      "Net.TransportSocketRequestTime",
-      time_to_obtain_connected_socket,
-      base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
-      100);
-
-  if (use_late_binding_histogram) {
-    UMA_HISTOGRAM_CUSTOM_TIMES(
-        FieldTrial::MakeName("Net.TransportSocketRequestTime",
-                             "SocketLateBinding").data(),
-        time_to_obtain_connected_socket,
-        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
-        100);
-  }
+  UMA_HISTOGRAM_ENUMERATION("Net.HttpSocketType", handle.reuse_type(),
+                            ClientSocketHandle::NUM_TYPES);
 
   switch (handle.reuse_type()) {
     case ClientSocketHandle::UNUSED:
+      UMA_HISTOGRAM_CUSTOM_TIMES("Net.HttpConnectionLatency",
+                                 handle.setup_time(),
+                                 base::TimeDelta::FromMilliseconds(1),
+                                 base::TimeDelta::FromMinutes(10),
+                                 100);
       break;
     case ClientSocketHandle::UNUSED_IDLE:
-      UMA_HISTOGRAM_CUSTOM_TIMES(
-          "Net.SocketIdleTimeBeforeNextUse_UnusedSocket",
-          handle.idle_time(), base::TimeDelta::FromMilliseconds(1),
-          base::TimeDelta::FromMinutes(6), 100);
-      if (use_late_binding_histogram) {
-        UMA_HISTOGRAM_CUSTOM_TIMES(
-            FieldTrial::MakeName("Net.SocketIdleTimeBeforeNextUse_UnusedSocket",
-                                 "SocketLateBinding").data(),
-            handle.idle_time(), base::TimeDelta::FromMilliseconds(1),
-            base::TimeDelta::FromMinutes(6), 100);
-      }
+      UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_UnusedSocket",
+                                 handle.idle_time(),
+                                 base::TimeDelta::FromMilliseconds(1),
+                                 base::TimeDelta::FromMinutes(6),
+                                 100);
       break;
     case ClientSocketHandle::REUSED_IDLE:
-      UMA_HISTOGRAM_CUSTOM_TIMES(
-          "Net.SocketIdleTimeBeforeNextUse_ReusedSocket",
-          handle.idle_time(), base::TimeDelta::FromMilliseconds(1),
-          base::TimeDelta::FromMinutes(6), 100);
-      if (use_late_binding_histogram) {
-        UMA_HISTOGRAM_CUSTOM_TIMES(
-            FieldTrial::MakeName("Net.SocketIdleTimeBeforeNextUse_ReusedSocket",
-                                 "SocketLateBinding").data(),
-            handle.idle_time(), base::TimeDelta::FromMilliseconds(1),
-            base::TimeDelta::FromMinutes(6), 100);
-      }
+      UMA_HISTOGRAM_CUSTOM_TIMES("Net.SocketIdleTimeBeforeNextUse_ReusedSocket",
+                                 handle.idle_time(),
+                                 base::TimeDelta::FromMilliseconds(1),
+                                 base::TimeDelta::FromMinutes(6),
+                                 100);
       break;
     default:
       NOTREACHED();
@@ -1272,18 +1363,8 @@
 
 void HttpNetworkTransaction::LogIOErrorMetrics(
     const ClientSocketHandle& handle) {
-  static const bool use_late_binding_histogram =
-      !FieldTrial::MakeName("", "SocketLateBinding").empty();
-
   UMA_HISTOGRAM_ENUMERATION("Net.IOError_SocketReuseType",
-       handle.reuse_type(), ClientSocketHandle::NUM_TYPES);
-
-  if (use_late_binding_histogram) {
-    UMA_HISTOGRAM_ENUMERATION(
-        FieldTrial::MakeName("Net.IOError_SocketReuseType",
-                             "SocketLateBinding"),
-        handle.reuse_type(), ClientSocketHandle::NUM_TYPES);
-  }
+                            handle.reuse_type(), ClientSocketHandle::NUM_TYPES);
 
   switch (handle.reuse_type()) {
     case ClientSocketHandle::UNUSED:
@@ -1293,26 +1374,12 @@
           "Net.SocketIdleTimeOnIOError2_UnusedSocket",
           handle.idle_time(), base::TimeDelta::FromMilliseconds(1),
           base::TimeDelta::FromMinutes(6), 100);
-      if (use_late_binding_histogram) {
-        UMA_HISTOGRAM_CUSTOM_TIMES(
-            FieldTrial::MakeName("Net.SocketIdleTimeOnIOError2_UnusedSocket",
-                                 "SocketLateBinding").data(),
-            handle.idle_time(), base::TimeDelta::FromMilliseconds(1),
-            base::TimeDelta::FromMinutes(6), 100);
-      }
       break;
     case ClientSocketHandle::REUSED_IDLE:
       UMA_HISTOGRAM_CUSTOM_TIMES(
           "Net.SocketIdleTimeOnIOError2_ReusedSocket",
           handle.idle_time(), base::TimeDelta::FromMilliseconds(1),
           base::TimeDelta::FromMinutes(6), 100);
-      if (use_late_binding_histogram) {
-        UMA_HISTOGRAM_CUSTOM_TIMES(
-            FieldTrial::MakeName("Net.SocketIdleTimeOnIOError2_ReusedSocket",
-                                 "SocketLateBinding").data(),
-            handle.idle_time(), base::TimeDelta::FromMilliseconds(1),
-            base::TimeDelta::FromMinutes(6), 100);
-      }
       break;
     default:
       NOTREACHED();
@@ -1320,7 +1387,12 @@
   }
 }
 
-void HttpNetworkTransaction::LogTransactionConnectedMetrics() const {
+void HttpNetworkTransaction::LogTransactionConnectedMetrics() {
+  if (logged_response_time_)
+    return;
+
+  logged_response_time_ = true;
+
   base::TimeDelta total_duration = response_.response_time - start_time_;
 
   UMA_HISTOGRAM_CLIPPED_TIMES(
@@ -1329,24 +1401,40 @@
       base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
       100);
 
-  static const bool use_late_binding_histogram =
-      !FieldTrial::MakeName("", "SocketLateBinding").empty();
-
-  if (use_late_binding_histogram) {
-    UMA_HISTOGRAM_CUSTOM_TIMES(
-        FieldTrial::MakeName("Net.Transaction_Connected_Under_10",
-                             "SocketLateBinding").data(),
-        total_duration,
-        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
-        100);
-  }
-
   if (!reused_socket_) {
     UMA_HISTOGRAM_CLIPPED_TIMES(
         "Net.Transaction_Connected_New",
         total_duration,
         base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
         100);
+
+  static bool use_conn_impact_histogram(
+      FieldTrialList::Find("ConnCountImpact") &&
+      !FieldTrialList::Find("ConnCountImpact")->group_name().empty());
+  if (use_conn_impact_histogram) {
+    UMA_HISTOGRAM_CLIPPED_TIMES(
+        FieldTrial::MakeName("Net.Transaction_Connected_New",
+            "ConnCountImpact"),
+        total_duration,
+        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
+        100);
+    }
+  }
+
+  static bool use_spdy_histogram(FieldTrialList::Find("SpdyImpact") &&
+      !FieldTrialList::Find("SpdyImpact")->group_name().empty());
+  if (use_spdy_histogram && response_.was_npn_negotiated) {
+    UMA_HISTOGRAM_CLIPPED_TIMES(
+      FieldTrial::MakeName("Net.Transaction_Connected_Under_10", "SpdyImpact"),
+        total_duration, base::TimeDelta::FromMilliseconds(1),
+        base::TimeDelta::FromMinutes(10), 100);
+
+    if (!reused_socket_) {
+      UMA_HISTOGRAM_CLIPPED_TIMES(
+          FieldTrial::MakeName("Net.Transaction_Connected_New", "SpdyImpact"),
+          total_duration, base::TimeDelta::FromMilliseconds(1),
+          base::TimeDelta::FromMinutes(10), 100);
+    }
   }
 
   // Currently, non-zero priority requests are frame or sub-frame resource
@@ -1369,7 +1457,7 @@
 
 void HttpNetworkTransaction::LogTransactionMetrics() const {
   base::TimeDelta duration = base::Time::Now() -
-      response_.request_time;
+                             response_.request_time;
   if (60 < duration.InMinutes())
     return;
 
@@ -1377,11 +1465,13 @@
 
   UMA_HISTOGRAM_LONG_TIMES("Net.Transaction_Latency", duration);
   UMA_HISTOGRAM_CLIPPED_TIMES("Net.Transaction_Latency_Under_10", duration,
-      base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
-      100);
+                              base::TimeDelta::FromMilliseconds(1),
+                              base::TimeDelta::FromMinutes(10),
+                              100);
   UMA_HISTOGRAM_CLIPPED_TIMES("Net.Transaction_Latency_Total_Under_10",
-      total_duration, base::TimeDelta::FromMilliseconds(1),
-      base::TimeDelta::FromMinutes(10), 100);
+                              total_duration,
+                              base::TimeDelta::FromMilliseconds(1),
+                              base::TimeDelta::FromMinutes(10), 100);
   if (!reused_socket_) {
     UMA_HISTOGRAM_CLIPPED_TIMES(
         "Net.Transaction_Latency_Total_New_Connection_Under_10",
@@ -1390,79 +1480,42 @@
   }
 }
 
-void HttpNetworkTransaction::LogBlockedTunnelResponse(
-    int response_code) const {
-  LOG(WARNING) << "Blocked proxy response with status " << response_code
-               << " to CONNECT request for "
-               << GetHostAndPort(request_->url) << ".";
-}
-
 int HttpNetworkTransaction::HandleCertificateError(int error) {
   DCHECK(using_ssl_);
+  DCHECK(IsCertificateError(error));
 
-  const int kCertFlags = LOAD_IGNORE_CERT_COMMON_NAME_INVALID |
-                         LOAD_IGNORE_CERT_DATE_INVALID |
-                         LOAD_IGNORE_CERT_AUTHORITY_INVALID |
-                         LOAD_IGNORE_CERT_WRONG_USAGE;
-  if (request_->load_flags & kCertFlags) {
-    switch (error) {
-      case ERR_CERT_COMMON_NAME_INVALID:
-        if (request_->load_flags & LOAD_IGNORE_CERT_COMMON_NAME_INVALID)
-          error = OK;
-        break;
-      case ERR_CERT_DATE_INVALID:
-        if (request_->load_flags & LOAD_IGNORE_CERT_DATE_INVALID)
-          error = OK;
-        break;
-      case ERR_CERT_AUTHORITY_INVALID:
-        if (request_->load_flags & LOAD_IGNORE_CERT_AUTHORITY_INVALID)
-          error = OK;
-        break;
-    }
-  }
+  SSLClientSocket* ssl_socket =
+      static_cast<SSLClientSocket*>(connection_->socket());
+  ssl_socket->GetSSLInfo(&response_.ssl_info);
 
-  if (error != OK) {
-    SSLClientSocket* ssl_socket =
-        reinterpret_cast<SSLClientSocket*>(connection_->socket());
-    ssl_socket->GetSSLInfo(&response_.ssl_info);
+  // Add the bad certificate to the set of allowed certificates in the
+  // SSL info object. This data structure will be consulted after calling
+  // RestartIgnoringLastError(). And the user will be asked interactively
+  // before RestartIgnoringLastError() is ever called.
+  SSLConfig::CertAndStatus bad_cert;
+  bad_cert.cert = response_.ssl_info.cert;
+  bad_cert.cert_status = response_.ssl_info.cert_status;
+  ssl_config_.allowed_bad_certs.push_back(bad_cert);
 
-    // Add the bad certificate to the set of allowed certificates in the
-    // SSL info object. This data structure will be consulted after calling
-    // RestartIgnoringLastError(). And the user will be asked interactively
-    // before RestartIgnoringLastError() is ever called.
-    SSLConfig::CertAndStatus bad_cert;
-    bad_cert.cert = response_.ssl_info.cert;
-    bad_cert.cert_status = response_.ssl_info.cert_status;
-    ssl_config_.allowed_bad_certs.push_back(bad_cert);
-  }
+  int load_flags = request_->load_flags;
+  if (g_ignore_certificate_errors)
+    load_flags |= LOAD_IGNORE_ALL_CERT_ERRORS;
+  if (ssl_socket->IgnoreCertError(error, load_flags))
+    return OK;
   return error;
 }
 
 int HttpNetworkTransaction::HandleCertificateRequest(int error) {
-  // Assert that the socket did not send a client certificate.
-  // Note: If we got a reused socket, it was created with some other
-  // transaction's ssl_config_, so we need to disable this assertion.  We can
-  // get a certificate request on a reused socket when the server requested
-  // renegotiation (rehandshake).
-  // TODO(wtc): add a GetSSLParams method to SSLClientSocket so we can query
-  // the SSL parameters it was created with and get rid of the reused_socket_
-  // test.
-  DCHECK(reused_socket_ || !ssl_config_.send_client_cert);
-
-  response_.cert_request_info = new SSLCertRequestInfo;
-  SSLClientSocket* ssl_socket =
-      reinterpret_cast<SSLClientSocket*>(connection_->socket());
-  ssl_socket->GetSSLCertRequestInfo(response_.cert_request_info);
-
   // Close the connection while the user is selecting a certificate to send
   // to the server.
-  connection_->socket()->Disconnect();
+  if (connection_->socket())
+    connection_->socket()->Disconnect();
   connection_->Reset();
 
   // If the user selected one of the certificate in client_certs for this
   // server before, use it automatically.
   X509Certificate* client_cert = session_->ssl_client_auth_cache()->
-      Lookup(GetHostAndPort(request_->url));
+                                 Lookup(GetHostAndPort(request_->url));
   if (client_cert) {
     const std::vector<scoped_refptr<X509Certificate> >& client_certs =
         response_.cert_request_info->client_certs;
@@ -1483,21 +1536,23 @@
 
 int HttpNetworkTransaction::HandleSSLHandshakeError(int error) {
   if (ssl_config_.send_client_cert &&
-     (error == ERR_SSL_PROTOCOL_ERROR ||
-      error == ERR_BAD_SSL_CLIENT_AUTH_CERT)) {
+      (error == ERR_SSL_PROTOCOL_ERROR ||
+       error == ERR_BAD_SSL_CLIENT_AUTH_CERT)) {
     session_->ssl_client_auth_cache()->Remove(GetHostAndPort(request_->url));
   }
 
   switch (error) {
     case ERR_SSL_PROTOCOL_ERROR:
     case ERR_SSL_VERSION_OR_CIPHER_MISMATCH:
-      if (ssl_config_.tls1_enabled) {
-        // This could be a TLS-intolerant server or an SSL 3.0 server that
-        // chose a TLS-only cipher suite.  Turn off TLS 1.0 and retry.
-        ssl_config_.tls1_enabled = false;
-        connection_->socket()->Disconnect();
-        connection_->Reset();
-        next_state_ = STATE_INIT_CONNECTION;
+    case ERR_SSL_DECOMPRESSION_FAILURE_ALERT:
+    case ERR_SSL_BAD_RECORD_MAC_ALERT:
+      if (ssl_config_.tls1_enabled &&
+          !SSLConfigService::IsKnownStrictTLSServer(request_->url.host())) {
+        // This could be a TLS-intolerant server, an SSL 3.0 server that
+        // chose a TLS-only cipher suite or a server with buggy DEFLATE
+        // support. Turn off TLS 1.0, DEFLATE support and retry.
+        g_tls_intolerant_servers->insert(GetHostAndPort(request_->url));
+        ResetConnectionAndRequestForResend();
         error = OK;
       }
       break;
@@ -1546,8 +1601,7 @@
   // NOTE: we resend a request only if we reused a keep-alive connection.
   // This automatically prevents an infinite resend loop because we'll run
   // out of the cached keep-alive connections eventually.
-  if (establishing_tunnel_ ||
-      !connection_->ShouldResendFailedRequest(error) ||
+  if (!connection_->ShouldResendFailedRequest(error) ||
       GetResponseHeaders()) {  // We have received some response headers.
     return false;
   }
@@ -1555,11 +1609,13 @@
 }
 
 void HttpNetworkTransaction::ResetConnectionAndRequestForResend() {
-  connection_->socket()->Disconnect();
+  if (connection_->socket())
+    connection_->socket()->Disconnect();
   connection_->Reset();
   // We need to clear request_headers_ because it contains the real request
   // headers, but we may need to resend the CONNECT request first to recreate
   // the SSL tunnel.
+
   request_headers_.clear();
   next_state_ = STATE_INIT_CONNECTION;  // Resend the request.
 }
@@ -1585,7 +1641,18 @@
     case ERR_CONNECTION_ABORTED:
     case ERR_TIMED_OUT:
     case ERR_TUNNEL_CONNECTION_FAILED:
+    case ERR_SOCKS_CONNECTION_FAILED:
       break;
+    case ERR_SOCKS_CONNECTION_HOST_UNREACHABLE:
+      // Remap the SOCKS-specific "host unreachable" error to a more
+      // generic error code (this way consumers like the link doctor
+      // know to substitute their error page).
+      //
+      // Note that if the host resolving was done by the SOCSK5 proxy, we can't
+      // differentiate between a proxy-side "host not found" versus a proxy-side
+      // "address unreachable" error, and will report both of these failures as
+      // ERR_ADDRESS_UNREACHABLE.
+      return ERR_ADDRESS_UNREACHABLE;
     default:
       return error;
   }
@@ -1595,7 +1662,7 @@
   }
 
   int rv = session_->proxy_service()->ReconsiderProxyAfterError(
-      request_->url, &proxy_info_, &io_callback_, &pac_request_, load_log_);
+      request_->url, &proxy_info_, &io_callback_, &pac_request_, net_log_);
   if (rv == OK || rv == ERR_IO_PENDING) {
     // If the error was during connection setup, there is no socket to
     // disconnect.
@@ -1615,178 +1682,11 @@
 }
 
 bool HttpNetworkTransaction::ShouldApplyProxyAuth() const {
-  return (proxy_mode_ == kHTTPProxy) || establishing_tunnel_;
+  return !using_ssl_ && proxy_info_.is_http();
 }
 
 bool HttpNetworkTransaction::ShouldApplyServerAuth() const {
-  return !establishing_tunnel_ &&
-      !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA);
-}
-
-std::string HttpNetworkTransaction::BuildAuthorizationHeader(
-    HttpAuth::Target target) const {
-  DCHECK(HaveAuth(target));
-
-  // Add a Authorization/Proxy-Authorization header line.
-  std::string credentials = auth_handler_[target]->GenerateCredentials(
-      auth_identity_[target].username,
-      auth_identity_[target].password,
-      request_,
-      &proxy_info_);
-
-  return HttpAuth::GetAuthorizationHeaderName(target) +
-      ": "  + credentials + "\r\n";
-}
-
-GURL HttpNetworkTransaction::AuthOrigin(HttpAuth::Target target) const {
-  return target == HttpAuth::AUTH_PROXY ?
-      GURL("http://" + proxy_info_.proxy_server().host_and_port()) :
-      request_->url.GetOrigin();
-}
-
-std::string HttpNetworkTransaction::AuthPath(HttpAuth::Target target)
-    const {
-  // Proxy authentication realms apply to all paths. So we will use
-  // empty string in place of an absolute path.
-  return target == HttpAuth::AUTH_PROXY ?
-      std::string() : request_->url.path();
-}
-
-// static
-std::string HttpNetworkTransaction::AuthTargetString(
-    HttpAuth::Target target) {
-  return target == HttpAuth::AUTH_PROXY ? "proxy" : "server";
-}
-
-void HttpNetworkTransaction::InvalidateRejectedAuthFromCache(
-    HttpAuth::Target target,
-    const GURL& auth_origin) {
-  DCHECK(HaveAuth(target));
-
-  // TODO(eroman): this short-circuit can be relaxed. If the realm of
-  // the preemptively used auth entry matches the realm of the subsequent
-  // challenge, then we can invalidate the preemptively used entry.
-  // Otherwise as-is we may send the failed credentials one extra time.
-  if (auth_identity_[target].source == HttpAuth::IDENT_SRC_PATH_LOOKUP)
-    return;
-
-  // Clear the cache entry for the identity we just failed on.
-  // Note: we require the username/password to match before invalidating
-  // since the entry in the cache may be newer than what we used last time.
-  session_->auth_cache()->Remove(auth_origin,
-                                 auth_handler_[target]->realm(),
-                                 auth_identity_[target].username,
-                                 auth_identity_[target].password);
-}
-
-bool HttpNetworkTransaction::SelectPreemptiveAuth(HttpAuth::Target target) {
-  DCHECK(!HaveAuth(target));
-
-  // Don't do preemptive authorization if the URL contains a username/password,
-  // since we must first be challenged in order to use the URL's identity.
-  if (request_->url.has_username())
-    return false;
-
-  // SelectPreemptiveAuth() is on the critical path for each request, so it
-  // is expected to be fast. LookupByPath() is fast in the common case, since
-  // the number of http auth cache entries is expected to be very small.
-  // (For most users in fact, it will be 0.)
-
-  HttpAuthCache::Entry* entry = session_->auth_cache()->LookupByPath(
-      AuthOrigin(target), AuthPath(target));
-
-  // We don't support preemptive authentication for connection-based
-  // authentication schemes because they can't reuse entry->handler().
-  // Hopefully we can remove this limitation in the future.
-  if (entry && !entry->handler()->is_connection_based()) {
-    auth_identity_[target].source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
-    auth_identity_[target].invalid = false;
-    auth_identity_[target].username = entry->username();
-    auth_identity_[target].password = entry->password();
-    auth_handler_[target] = entry->handler();
-    return true;
-  }
-  return false;
-}
-
-bool HttpNetworkTransaction::SelectNextAuthIdentityToTry(
-    HttpAuth::Target target,
-    const GURL& auth_origin) {
-  DCHECK(auth_handler_[target]);
-  DCHECK(auth_identity_[target].invalid);
-
-  // Try to use the username/password encoded into the URL first.
-  if (target == HttpAuth::AUTH_SERVER && request_->url.has_username() &&
-      !embedded_identity_used_) {
-    auth_identity_[target].source = HttpAuth::IDENT_SRC_URL;
-    auth_identity_[target].invalid = false;
-    // Extract the username:password from the URL.
-    GetIdentityFromURL(request_->url,
-                       &auth_identity_[target].username,
-                       &auth_identity_[target].password);
-    embedded_identity_used_ = true;
-    // TODO(eroman): If the password is blank, should we also try combining
-    // with a password from the cache?
-    return true;
-  }
-
-  // Check the auth cache for a realm entry.
-  HttpAuthCache::Entry* entry = session_->auth_cache()->LookupByRealm(
-      auth_origin, auth_handler_[target]->realm());
-
-  if (entry) {
-    // Disallow re-using of identity if the scheme of the originating challenge
-    // does not match. This protects against the following situation:
-    // 1. Browser prompts user to sign into DIGEST realm="Foo".
-    // 2. Since the auth-scheme is not BASIC, the user is reasured that it
-    //    will not be sent over the wire in clear text. So they use their
-    //    most trusted password.
-    // 3. Next, the browser receives a challenge for BASIC realm="Foo". This
-    //    is the same realm that we have a cached identity for. However if
-    //    we use that identity, it would get sent over the wire in
-    //    clear text (which isn't what the user agreed to when entering it).
-    if (entry->handler()->scheme() != auth_handler_[target]->scheme()) {
-      LOG(WARNING) << "The scheme of realm " << auth_handler_[target]->realm()
-                   << " has changed from " << entry->handler()->scheme()
-                   << " to " << auth_handler_[target]->scheme();
-      return false;
-    }
-
-    auth_identity_[target].source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
-    auth_identity_[target].invalid = false;
-    auth_identity_[target].username = entry->username();
-    auth_identity_[target].password = entry->password();
-    return true;
-  }
-  return false;
-}
-
-std::string HttpNetworkTransaction::AuthChallengeLogMessage() const {
-  std::string msg;
-  std::string header_val;
-  void* iter = NULL;
-  scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
-  while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) {
-    msg.append("\n  Has header Proxy-Authenticate: ");
-    msg.append(header_val);
-  }
-
-  iter = NULL;
-  while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) {
-    msg.append("\n  Has header WWW-Authenticate: ");
-    msg.append(header_val);
-  }
-
-  // RFC 4559 requires that a proxy indicate its support of NTLM/Negotiate
-  // authentication with a "Proxy-Support: Session-Based-Authentication"
-  // response header.
-  iter = NULL;
-  while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) {
-    msg.append("\n  Has header Proxy-Support: ");
-    msg.append(header_val);
-  }
-
-  return msg;
+  return !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA);
 }
 
 int HttpNetworkTransaction::HandleAuthChallenge() {
@@ -1797,91 +1697,99 @@
   if (status != 401 && status != 407)
     return OK;
   HttpAuth::Target target = status == 407 ?
-      HttpAuth::AUTH_PROXY : HttpAuth::AUTH_SERVER;
-  GURL auth_origin = AuthOrigin(target);
-
-  LOG(INFO) << "The " << AuthTargetString(target) << " "
-            << auth_origin << " requested auth"
-            << AuthChallengeLogMessage();
-
+                            HttpAuth::AUTH_PROXY : HttpAuth::AUTH_SERVER;
   if (target == HttpAuth::AUTH_PROXY && proxy_info_.is_direct())
     return ERR_UNEXPECTED_PROXY_AUTH;
 
-  // The auth we tried just failed, hence it can't be valid. Remove it from
-  // the cache so it won't be used again.
-  // TODO(wtc): IsFinalRound is not the right condition.  In a multi-round
-  // auth sequence, the server may fail the auth in round 1 if our first
-  // authorization header is broken.  We should inspect response_.headers to
-  // determine if the server already failed the auth or wants us to continue.
-  // See http://crbug.com/21015.
-  if (HaveAuth(target) && auth_handler_[target]->IsFinalRound()) {
-    InvalidateRejectedAuthFromCache(target, auth_origin);
-    auth_handler_[target] = NULL;
-    auth_identity_[target] = HttpAuth::Identity();
-  }
+  int rv = auth_controllers_[target]->HandleAuthChallenge(
+      headers, (request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA) != 0, false,
+      net_log_);
+  if (auth_controllers_[target]->HaveAuthHandler())
+      pending_auth_target_ = target;
 
-  auth_identity_[target].invalid = true;
+  scoped_refptr<AuthChallengeInfo> auth_info =
+      auth_controllers_[target]->auth_info();
+  if (auth_info.get())
+      response_.auth_challenge = auth_info;
 
-  if (target != HttpAuth::AUTH_SERVER ||
-      !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA)) {
-    // Find the best authentication challenge that we support.
-    HttpAuth::ChooseBestChallenge(headers, target, auth_origin,
-                                  &auth_handler_[target]);
-  }
-
-  if (!auth_handler_[target]) {
-    if (establishing_tunnel_) {
-      LOG(ERROR) << "Can't perform auth to the " << AuthTargetString(target)
-                 << " " << auth_origin << " when establishing a tunnel"
-                 << AuthChallengeLogMessage();
-
-      // We are establishing a tunnel, we can't show the error page because an
-      // active network attacker could control its contents.  Instead, we just
-      // fail to establish the tunnel.
-      DCHECK(target == HttpAuth::AUTH_PROXY);
-      return ERR_PROXY_AUTH_REQUESTED;
-    }
-    // We found no supported challenge -- let the transaction continue
-    // so we end up displaying the error page.
-    return OK;
-  }
-
-  if (auth_handler_[target]->NeedsIdentity()) {
-    // Pick a new auth identity to try, by looking to the URL and auth cache.
-    // If an identity to try is found, it is saved to auth_identity_[target].
-    SelectNextAuthIdentityToTry(target, auth_origin);
-  } else {
-    // Proceed with the existing identity or a null identity.
-    //
-    // TODO(wtc): Add a safeguard against infinite transaction restarts, if
-    // the server keeps returning "NTLM".
-    auth_identity_[target].invalid = false;
-  }
-
-  // Make a note that we are waiting for auth. This variable is inspected
-  // when the client calls RestartWithAuth() to pick up where we left off.
-  pending_auth_target_ = target;
-
-  if (auth_identity_[target].invalid) {
-    // We have exhausted all identity possibilities, all we can do now is
-    // pass the challenge information back to the client.
-    PopulateAuthChallenge(target, auth_origin);
-  }
-  return OK;
+  return rv;
 }
 
-void HttpNetworkTransaction::PopulateAuthChallenge(HttpAuth::Target target,
-                                                   const GURL& auth_origin) {
-  // Populates response_.auth_challenge with the authentication challenge info.
-  // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo().
-
-  AuthChallengeInfo* auth_info = new AuthChallengeInfo;
-  auth_info->is_proxy = target == HttpAuth::AUTH_PROXY;
-  auth_info->host_and_port = ASCIIToWide(GetHostAndPort(auth_origin));
-  auth_info->scheme = ASCIIToWide(auth_handler_[target]->scheme());
-  // TODO(eroman): decode realm according to RFC 2047.
-  auth_info->realm = ASCIIToWide(auth_handler_[target]->realm());
-  response_.auth_challenge = auth_info;
+GURL HttpNetworkTransaction::AuthURL(HttpAuth::Target target) const {
+  switch (target) {
+    case HttpAuth::AUTH_PROXY:
+      if (!proxy_info_.proxy_server().is_valid() ||
+          proxy_info_.proxy_server().is_direct()) {
+        return GURL();  // There is no proxy server.
+      }
+      return GURL("http://" + proxy_info_.proxy_server().host_and_port());
+    case HttpAuth::AUTH_SERVER:
+      return request_->url;
+    default:
+     return GURL();
+  }
 }
 
+void HttpNetworkTransaction::MarkBrokenAlternateProtocolAndFallback() {
+  // We have to:
+  // * Reset the endpoint to be the unmodified URL specified destination.
+  // * Mark the endpoint as broken so we don't try again.
+  // * Set the alternate protocol mode to kDoNotUseAlternateProtocol so we
+  // ignore future Alternate-Protocol headers from the HostPortPair.
+  // * Reset the connection and go back to STATE_INIT_CONNECTION.
+
+  endpoint_ = HostPortPair(request_->url.HostNoBrackets(),
+                           request_->url.EffectiveIntPort());
+
+  session_->mutable_alternate_protocols()->MarkBrokenAlternateProtocolFor(
+      endpoint_);
+
+  alternate_protocol_mode_ = kDoNotUseAlternateProtocol;
+  if (connection_->socket())
+    connection_->socket()->Disconnect();
+  connection_->Reset();
+  next_state_ = STATE_INIT_CONNECTION;
+}
+
+#define STATE_CASE(s)  case s: \
+                         description = StringPrintf("%s (0x%08X)", #s, s); \
+                         break
+
+std::string HttpNetworkTransaction::DescribeState(State state) {
+  std::string description;
+  switch (state) {
+    STATE_CASE(STATE_RESOLVE_PROXY);
+    STATE_CASE(STATE_RESOLVE_PROXY_COMPLETE);
+    STATE_CASE(STATE_INIT_CONNECTION);
+    STATE_CASE(STATE_INIT_CONNECTION_COMPLETE);
+    STATE_CASE(STATE_GENERATE_PROXY_AUTH_TOKEN);
+    STATE_CASE(STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE);
+    STATE_CASE(STATE_GENERATE_SERVER_AUTH_TOKEN);
+    STATE_CASE(STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE);
+    STATE_CASE(STATE_SEND_REQUEST);
+    STATE_CASE(STATE_SEND_REQUEST_COMPLETE);
+    STATE_CASE(STATE_READ_HEADERS);
+    STATE_CASE(STATE_READ_HEADERS_COMPLETE);
+    STATE_CASE(STATE_READ_BODY);
+    STATE_CASE(STATE_READ_BODY_COMPLETE);
+    STATE_CASE(STATE_DRAIN_BODY_FOR_AUTH_RESTART);
+    STATE_CASE(STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE);
+    STATE_CASE(STATE_SPDY_GET_STREAM);
+    STATE_CASE(STATE_SPDY_GET_STREAM_COMPLETE);
+    STATE_CASE(STATE_SPDY_SEND_REQUEST);
+    STATE_CASE(STATE_SPDY_SEND_REQUEST_COMPLETE);
+    STATE_CASE(STATE_SPDY_READ_HEADERS);
+    STATE_CASE(STATE_SPDY_READ_HEADERS_COMPLETE);
+    STATE_CASE(STATE_SPDY_READ_BODY);
+    STATE_CASE(STATE_SPDY_READ_BODY_COMPLETE);
+    STATE_CASE(STATE_NONE);
+    default:
+      description = StringPrintf("Unknown state 0x%08X (%u)", state, state);
+      break;
+  }
+  return description;
+}
+
+#undef STATE_CASE
+
 }  // namespace net
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index 8a6d460..7c32bd3 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -12,12 +12,14 @@
 #include "base/scoped_ptr.h"
 #include "base/time.h"
 #include "net/base/address_list.h"
-#include "net/base/host_resolver.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_states.h"
+#include "net/base/net_log.h"
 #include "net/base/ssl_config_service.h"
+#include "net/http/http_alternate_protocols.h"
 #include "net/http/http_auth.h"
+#include "net/http/http_auth_controller.h"
 #include "net/http/http_auth_handler.h"
 #include "net/http/http_response_info.h"
 #include "net/http/http_transaction.h"
@@ -29,9 +31,10 @@
 
 class ClientSocketFactory;
 class ClientSocketHandle;
-class FlipStream;
 class HttpNetworkSession;
+class HttpRequestHeaders;
 class HttpStream;
+class SpdyHttpStream;
 
 class HttpNetworkTransaction : public HttpTransaction {
  public:
@@ -39,13 +42,22 @@
 
   virtual ~HttpNetworkTransaction();
 
+  static void SetHostMappingRules(const std::string& rules);
+
+  // Controls whether or not we use the Alternate-Protocol header.
+  static void SetUseAlternateProtocols(bool value);
+
   // Sets the next protocol negotiation value used during the SSL handshake.
   static void SetNextProtos(const std::string& next_protos);
 
+  // Sets the HttpNetworkTransaction into a mode where it can ignore
+  // certificate errors.  This is for testing.
+  static void IgnoreCertificateErrors(bool enabled);
+
   // HttpTransaction methods:
   virtual int Start(const HttpRequestInfo* request_info,
                     CompletionCallback* callback,
-                    LoadLog* load_log);
+                    const BoundNetLog& net_log);
   virtual int RestartIgnoringLastError(CompletionCallback* callback);
   virtual int RestartWithCertificate(X509Certificate* client_cert,
                                      CompletionCallback* callback);
@@ -58,6 +70,7 @@
   }
 
   virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
+  virtual void StopCaching() {}
   virtual const HttpResponseInfo* GetResponseInfo() const;
   virtual LoadState GetLoadState() const;
   virtual uint64 GetUploadProgress() const;
@@ -70,10 +83,10 @@
     STATE_RESOLVE_PROXY_COMPLETE,
     STATE_INIT_CONNECTION,
     STATE_INIT_CONNECTION_COMPLETE,
-    STATE_SOCKS_CONNECT,
-    STATE_SOCKS_CONNECT_COMPLETE,
-    STATE_SSL_CONNECT,
-    STATE_SSL_CONNECT_COMPLETE,
+    STATE_GENERATE_PROXY_AUTH_TOKEN,
+    STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE,
+    STATE_GENERATE_SERVER_AUTH_TOKEN,
+    STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE,
     STATE_SEND_REQUEST,
     STATE_SEND_REQUEST_COMPLETE,
     STATE_READ_HEADERS,
@@ -82,6 +95,8 @@
     STATE_READ_BODY_COMPLETE,
     STATE_DRAIN_BODY_FOR_AUTH_RESTART,
     STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE,
+    STATE_SPDY_GET_STREAM,
+    STATE_SPDY_GET_STREAM_COMPLETE,
     STATE_SPDY_SEND_REQUEST,
     STATE_SPDY_SEND_REQUEST_COMPLETE,
     STATE_SPDY_READ_HEADERS,
@@ -91,11 +106,10 @@
     STATE_NONE
   };
 
-  enum ProxyMode {
-    kDirectConnection,  // If using a direct connection
-    kHTTPProxy,  // If using a proxy for HTTP (not HTTPS)
-    kHTTPProxyUsingTunnel,  // If using a tunnel for HTTPS
-    kSOCKSProxy,  // If using a SOCKS proxy
+  enum AlternateProtocolMode {
+    kUnspecified,  // Unspecified, check HttpAlternateProtocols
+    kUsingAlternateProtocol,  // Using an alternate protocol
+    kDoNotUseAlternateProtocol,  // Failed to connect once, do not try again.
   };
 
   void DoCallback(int result);
@@ -112,10 +126,10 @@
   int DoResolveProxyComplete(int result);
   int DoInitConnection();
   int DoInitConnectionComplete(int result);
-  int DoSOCKSConnect();
-  int DoSOCKSConnectComplete(int result);
-  int DoSSLConnect();
-  int DoSSLConnectComplete(int result);
+  int DoGenerateProxyAuthToken();
+  int DoGenerateProxyAuthTokenComplete(int result);
+  int DoGenerateServerAuthToken();
+  int DoGenerateServerAuthTokenComplete(int result);
   int DoSendRequest();
   int DoSendRequestComplete(int result);
   int DoReadHeaders();
@@ -124,6 +138,8 @@
   int DoReadBodyComplete(int result);
   int DoDrainBodyForAuthRestart();
   int DoDrainBodyForAuthRestartComplete(int result);
+  int DoSpdyGetStream();
+  int DoSpdyGetStreamComplete(int result);
   int DoSpdySendRequest();
   int DoSpdySendRequestComplete(int result);
   int DoSpdyReadHeaders();
@@ -132,10 +148,10 @@
   int DoSpdyReadBodyComplete(int result);
 
   // Record histograms of latency until Connect() completes.
-  static void LogTCPConnectedMetrics(const ClientSocketHandle& handle);
+  static void LogHttpConnectedMetrics(const ClientSocketHandle& handle);
 
   // Record histogram of time until first byte of header is received.
-  void LogTransactionConnectedMetrics() const;
+  void LogTransactionConnectedMetrics();
 
   // Record histogram of latency (durations until last byte received).
   void LogTransactionMetrics() const;
@@ -205,71 +221,28 @@
   // Returns true if we should try to add an Authorization header.
   bool ShouldApplyServerAuth() const;
 
-  // Builds either the proxy auth header, or the origin server auth header,
-  // as specified by |target|.
-  std::string BuildAuthorizationHeader(HttpAuth::Target target) const;
-
-  // Returns a log message for all the response headers related to the auth
-  // challenge.
-  std::string AuthChallengeLogMessage() const;
-
   // Handles HTTP status code 401 or 407.
   // HandleAuthChallenge() returns a network error code, or OK on success.
   // May update |pending_auth_target_| or |response_.auth_challenge|.
   int HandleAuthChallenge();
 
-  // Populates response_.auth_challenge with the challenge information, so that
-  // URLRequestHttpJob can prompt for a username/password.
-  void PopulateAuthChallenge(HttpAuth::Target target,
-                             const GURL& auth_origin);
-
-  // Invalidates any auth cache entries after authentication has failed.
-  // The identity that was rejected is auth_identity_[target].
-  void InvalidateRejectedAuthFromCache(HttpAuth::Target target,
-                                       const GURL& auth_origin);
-
-  // Sets auth_identity_[target] to the next identity that the transaction
-  // should try. It chooses candidates by searching the auth cache
-  // and the URL for a username:password. Returns true if an identity
-  // was found.
-  bool SelectNextAuthIdentityToTry(HttpAuth::Target target,
-                                   const GURL& auth_origin);
-
-  // Searches the auth cache for an entry that encompasses the request's path.
-  // If such an entry is found, updates auth_identity_[target] and
-  // auth_handler_[target] with the cache entry's data and returns true.
-  bool SelectPreemptiveAuth(HttpAuth::Target target);
-
   bool HaveAuth(HttpAuth::Target target) const {
-    return auth_handler_[target].get() && !auth_identity_[target].invalid;
+    return auth_controllers_[target].get() &&
+         auth_controllers_[target]->HaveAuth();
   }
 
-  // Get the {scheme, host, port} for the authentication target
-  GURL AuthOrigin(HttpAuth::Target target) const;
+  // Get the {scheme, host, path, port} for the authentication target
+  GURL AuthURL(HttpAuth::Target target) const;
 
-  // Get the absolute path of the resource needing authentication.
-  // For proxy authentication the path is always empty string.
-  std::string AuthPath(HttpAuth::Target target) const;
+  void MarkBrokenAlternateProtocolAndFallback();
 
-  // Returns a string representation of a HttpAuth::Target value that can be
-  // used in log messages.
-  static std::string AuthTargetString(HttpAuth::Target target);
+  // Debug helper.
+  static std::string DescribeState(State state);
 
-  static std::string* g_next_protos;
+  static bool g_ignore_certificate_errors;
 
-  // The following three auth members are arrays of size two -- index 0 is
-  // for the proxy server, and index 1 is for the origin server.
-  // Use the enum HttpAuth::Target to index into them.
-
-  // auth_handler encapsulates the logic for the particular auth-scheme.
-  // This includes the challenge's parameters. If NULL, then there is no
-  // associated auth handler.
-  scoped_refptr<HttpAuthHandler> auth_handler_[2];
-
-  // auth_identity_ holds the (username/password) that should be used by
-  // the auth_handler_ to generate credentials. This identity can come from
-  // a number of places (url, cache, prompt).
-  HttpAuth::Identity auth_identity_[2];
+  scoped_refptr<HttpAuthController>
+      auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];
 
   // Whether this transaction is waiting for proxy auth, server auth, or is
   // not waiting for any auth at all. |pending_auth_target_| is read and
@@ -281,7 +254,7 @@
 
   scoped_refptr<HttpNetworkSession> session_;
 
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
   const HttpRequestInfo* request_;
   HttpResponseInfo response_;
 
@@ -290,7 +263,7 @@
 
   scoped_ptr<ClientSocketHandle> connection_;
   scoped_ptr<HttpStream> http_stream_;
-  scoped_refptr<FlipStream> spdy_stream_;
+  scoped_ptr<SpdyHttpStream> spdy_http_stream_;
   bool reused_socket_;
 
   // True if we've validated the headers that the stream parser has returned.
@@ -299,22 +272,20 @@
   // True if we've logged the time of the first response byte.  Used to
   // prevent logging across authentication activity where we see multiple
   // responses.
-  bool logged_response_time;
+  bool logged_response_time_;
 
   bool using_ssl_;     // True if handling a HTTPS request
-  ProxyMode proxy_mode_;
 
-  // True while establishing a tunnel.  This allows the HTTP CONNECT
-  // request/response to reuse the STATE_SEND_REQUEST,
-  // STATE_SEND_REQUEST_COMPLETE, STATE_READ_HEADERS, and
-  // STATE_READ_HEADERS_COMPLETE states and allows us to tell them apart from
-  // the real request/response of the transaction.
-  bool establishing_tunnel_;
+  // True if this network transaction is using SPDY instead of HTTP.
+  bool using_spdy_;
 
-  // True if we've used the username/password embedded in the URL.  This
-  // makes sure we use the embedded identity only once for the transaction,
-  // preventing an infinite auth restart loop.
-  bool embedded_identity_used_;
+  // The certificate error while using SPDY over SSL for insecure URLs.
+  int spdy_certificate_error_;
+
+  AlternateProtocolMode alternate_protocol_mode_;
+
+  // Only valid if |alternate_protocol_mode_| == kUsingAlternateProtocol.
+  HttpAlternateProtocols::Protocol alternate_protocol_;
 
   SSLConfig ssl_config_;
 
@@ -332,11 +303,18 @@
   // The time the Start method was called.
   base::Time start_time_;
 
-  // The time the DoSSLConnect() method was called (if it got called).
-  base::TimeTicks ssl_connect_start_time_;
-
   // The next state in the state machine.
   State next_state_;
+
+  // The hostname and port of the endpoint.  This is not necessarily the one
+  // specified by the URL, due to Alternate-Protocol or fixed testing ports.
+  HostPortPair endpoint_;
+
+  // True when the tunnel is in the process of being established - we can't
+  // read from the socket until the tunnel is done.
+  bool establishing_tunnel_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpNetworkTransaction);
 };
 
 }  // namespace net
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 9639457..03e4b31 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -1,88 +1,144 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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 <math.h>  // ceil
+#include "net/http/http_network_transaction.h"
 
+#include <math.h>  // ceil
+#include <vector>
+
+#include "base/basictypes.h"
 #include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/scoped_ptr.h"
+#include "net/base/capturing_net_log.h"
 #include "net/base/completion_callback.h"
 #include "net/base/mock_host_resolver.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/request_priority.h"
 #include "net/base/ssl_config_service_defaults.h"
 #include "net/base/ssl_info.h"
 #include "net/base/test_completion_callback.h"
 #include "net/base/upload_data.h"
-#include "net/flip/flip_session_pool.h"
+#include "net/http/http_auth_handler_digest.h"
+#include "net/http/http_auth_handler_mock.h"
 #include "net/http/http_auth_handler_ntlm.h"
 #include "net/http/http_basic_stream.h"
 #include "net/http/http_network_session.h"
-#include "net/http/http_network_transaction.h"
 #include "net/http/http_stream.h"
 #include "net/http/http_transaction_unittest.h"
 #include "net/proxy/proxy_config_service_fixed.h"
+#include "net/proxy/proxy_resolver.h"
+#include "net/proxy/proxy_service.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/socket_test_util.h"
 #include "net/socket/ssl_client_socket.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_session_pool.h"
+#include "net/spdy/spdy_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
 //-----------------------------------------------------------------------------
 
-// TODO(eroman): Add a regression test for http://crbug.com/32316 -- when the
-// proxy service returns an error, we should fallback to DIRECT instead of
-// failing with ERR_NO_SUPPORTED_PROXIES.
-
 namespace net {
 
-// Create a proxy service which fails on all requests (falls back to direct).
-ProxyService* CreateNullProxyService() {
-  return ProxyService::CreateNull();
-}
+class HttpNetworkSessionPeer {
+ public:
+  explicit HttpNetworkSessionPeer(
+      const scoped_refptr<HttpNetworkSession>& session)
+      : session_(session) {}
+
+  void SetTCPSocketPool(const scoped_refptr<TCPClientSocketPool>& pool) {
+    session_->tcp_socket_pool_ = pool;
+  }
+
+  void SetSocketPoolForSOCKSProxy(
+      const HostPortPair& socks_proxy,
+      const scoped_refptr<SOCKSClientSocketPool>& pool) {
+    session_->socks_socket_pools_[socks_proxy] = pool;
+  }
+
+  void SetSocketPoolForHTTPProxy(
+      const HostPortPair& http_proxy,
+      const scoped_refptr<HttpProxyClientSocketPool>& pool) {
+    session_->http_proxy_socket_pools_[http_proxy] = pool;
+  }
+
+  void SetSSLSocketPool(const scoped_refptr<SSLClientSocketPool>& pool) {
+    session_->ssl_socket_pool_ = pool;
+  }
+
+  void SetSocketPoolForSSLWithProxy(
+      const HostPortPair& proxy_host,
+      const scoped_refptr<SSLClientSocketPool>& pool) {
+    session_->ssl_socket_pools_for_proxies_[proxy_host] = pool;
+  }
+
+ private:
+  const scoped_refptr<HttpNetworkSession> session_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpNetworkSessionPeer);
+};
 
 // Helper to manage the lifetimes of the dependencies for a
 // HttpNetworkTransaction.
-class SessionDependencies {
- public:
+struct SessionDependencies {
   // Default set of dependencies -- "null" proxy service.
   SessionDependencies()
       : host_resolver(new MockHostResolver),
-        proxy_service(CreateNullProxyService()),
+        proxy_service(ProxyService::CreateNull()),
         ssl_config_service(new SSLConfigServiceDefaults),
-        flip_session_pool(new FlipSessionPool) {}
+        http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault()),
+        spdy_session_pool(new SpdySessionPool()),
+        net_log(NULL) {}
 
   // Custom proxy service dependency.
   explicit SessionDependencies(ProxyService* proxy_service)
       : host_resolver(new MockHostResolver),
         proxy_service(proxy_service),
         ssl_config_service(new SSLConfigServiceDefaults),
-        flip_session_pool(new FlipSessionPool) {}
+        http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault()),
+        spdy_session_pool(new SpdySessionPool()),
+        net_log(NULL) {}
 
   scoped_refptr<MockHostResolverBase> host_resolver;
   scoped_refptr<ProxyService> proxy_service;
   scoped_refptr<SSLConfigService> ssl_config_service;
   MockClientSocketFactory socket_factory;
-  scoped_refptr<FlipSessionPool> flip_session_pool;
+  scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory;
+  scoped_refptr<SpdySessionPool> spdy_session_pool;
+  NetLog* net_log;
 };
 
 ProxyService* CreateFixedProxyService(const std::string& proxy) {
   net::ProxyConfig proxy_config;
-  proxy_config.proxy_rules.ParseFromString(proxy);
+  proxy_config.proxy_rules().ParseFromString(proxy);
   return ProxyService::CreateFixed(proxy_config);
 }
 
-
 HttpNetworkSession* CreateSession(SessionDependencies* session_deps) {
-  return new HttpNetworkSession(NULL,
-                                session_deps->host_resolver,
+  return new HttpNetworkSession(session_deps->host_resolver,
                                 session_deps->proxy_service,
                                 &session_deps->socket_factory,
                                 session_deps->ssl_config_service,
-                                session_deps->flip_session_pool);
+                                session_deps->spdy_session_pool,
+                                session_deps->http_auth_handler_factory.get(),
+                                NULL,
+                                session_deps->net_log);
 }
 
 class HttpNetworkTransactionTest : public PlatformTest {
  public:
+  virtual void SetUp() {
+    spdy::SpdyFramer::set_enable_compression_default(false);
+  }
+
   virtual void TearDown() {
+    spdy::SpdyFramer::set_enable_compression_default(true);
     // Empty the current queue.
     MessageLoop::current()->RunAllPending();
     PlatformTest::TearDown();
@@ -97,7 +153,8 @@
     std::string response_data;
   };
 
-  SimpleGetHelperResult SimpleGetHelper(MockRead data_reads[]) {
+  SimpleGetHelperResult SimpleGetHelper(MockRead data_reads[],
+                                        size_t reads_count) {
     SimpleGetHelperResult out;
 
     SessionDependencies session_deps;
@@ -109,12 +166,13 @@
     request.url = GURL("http://www.google.com/");
     request.load_flags = 0;
 
-    StaticSocketDataProvider data(data_reads, NULL);
+    StaticSocketDataProvider data(data_reads, reads_count, NULL, 0);
     session_deps.socket_factory.AddSocketDataProvider(&data);
 
     TestCompletionCallback callback;
 
-    int rv = trans->Start(&request, &callback, NULL);
+    CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+    int rv = trans->Start(&request, &callback, log.bound());
     EXPECT_EQ(ERR_IO_PENDING, rv);
 
     out.rv = callback.WaitForResult();
@@ -129,6 +187,13 @@
 
     rv = ReadTransaction(trans.get(), &out.response_data);
     EXPECT_EQ(OK, rv);
+    size_t pos = ExpectLogContainsSomewhere(
+        log.entries(), 0, NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS,
+        NetLog::PHASE_NONE);
+    ExpectLogContainsSomewhere(
+        log.entries(), pos,
+        NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS,
+        NetLog::PHASE_NONE);
 
     return out;
   }
@@ -183,9 +248,11 @@
   return "WTC-WIN7";
 }
 
-class CaptureGroupNameSocketPool : public TCPClientSocketPool {
+template<typename ParentPool>
+class CaptureGroupNameSocketPool : public ParentPool {
  public:
-  CaptureGroupNameSocketPool() : TCPClientSocketPool(0, 0, NULL, NULL, NULL) {}
+  explicit CaptureGroupNameSocketPool(HttpNetworkSession* session);
+
   const std::string last_group_name_received() const {
     return last_group_name_;
   }
@@ -195,12 +262,12 @@
                             RequestPriority priority,
                             ClientSocketHandle* handle,
                             CompletionCallback* callback,
-                            LoadLog* load_log) {
+                            const BoundNetLog& net_log) {
     last_group_name_ = group_name;
     return ERR_IO_PENDING;
   }
   virtual void CancelRequest(const std::string& group_name,
-                             const ClientSocketHandle* handle) { }
+                             ClientSocketHandle* handle) {}
   virtual void ReleaseSocket(const std::string& group_name,
                              ClientSocket* socket) {}
   virtual void CloseIdleSockets() {}
@@ -217,11 +284,34 @@
                                  const ClientSocketHandle* handle) const {
     return LOAD_STATE_IDLE;
   }
+  virtual base::TimeDelta ConnectionTimeout() const {
+    return base::TimeDelta();
+  }
 
  private:
   std::string last_group_name_;
 };
 
+typedef CaptureGroupNameSocketPool<TCPClientSocketPool>
+CaptureGroupNameTCPSocketPool;
+typedef CaptureGroupNameSocketPool<HttpProxyClientSocketPool>
+CaptureGroupNameHttpProxySocketPool;
+typedef CaptureGroupNameSocketPool<SOCKSClientSocketPool>
+CaptureGroupNameSOCKSSocketPool;
+typedef CaptureGroupNameSocketPool<SSLClientSocketPool>
+CaptureGroupNameSSLSocketPool;
+
+template<typename ParentPool>
+CaptureGroupNameSocketPool<ParentPool>::CaptureGroupNameSocketPool(
+    HttpNetworkSession* session)
+    : ParentPool(0, 0, NULL, session->host_resolver(), NULL, NULL) {}
+
+template<>
+CaptureGroupNameSSLSocketPool::CaptureGroupNameSocketPool(
+    HttpNetworkSession* session)
+    : SSLClientSocketPool(0, 0, NULL, session->host_resolver(), NULL, NULL,
+                          NULL, NULL, NULL) {}
+
 //-----------------------------------------------------------------------------
 
 TEST_F(HttpNetworkTransactionTest, Basic) {
@@ -236,7 +326,8 @@
     MockRead("hello world"),
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
+  SimpleGetHelperResult out = SimpleGetHelper(data_reads,
+                                              arraysize(data_reads));
   EXPECT_EQ(OK, out.rv);
   EXPECT_EQ("HTTP/1.0 200 OK", out.status_line);
   EXPECT_EQ("hello world", out.response_data);
@@ -248,7 +339,8 @@
     MockRead("hello world"),
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
+  SimpleGetHelperResult out = SimpleGetHelper(data_reads,
+                                              arraysize(data_reads));
   EXPECT_EQ(OK, out.rv);
   EXPECT_EQ("HTTP/0.9 200 OK", out.status_line);
   EXPECT_EQ("hello world", out.response_data);
@@ -260,7 +352,8 @@
     MockRead("xxxHTTP/1.0 404 Not Found\nServer: blah\n\nDATA"),
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
+  SimpleGetHelperResult out = SimpleGetHelper(data_reads,
+                                              arraysize(data_reads));
   EXPECT_EQ(OK, out.rv);
   EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line);
   EXPECT_EQ("DATA", out.response_data);
@@ -272,7 +365,8 @@
     MockRead("\n\nQJHTTP/1.0 404 Not Found\nServer: blah\n\nDATA"),
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
+  SimpleGetHelperResult out = SimpleGetHelper(data_reads,
+                                              arraysize(data_reads));
   EXPECT_EQ(OK, out.rv);
   EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line);
   EXPECT_EQ("DATA", out.response_data);
@@ -284,7 +378,8 @@
     MockRead("xxxxxHTTP/1.1 404 Not Found\nServer: blah"),
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
+  SimpleGetHelperResult out = SimpleGetHelper(data_reads,
+                                              arraysize(data_reads));
   EXPECT_EQ(OK, out.rv);
   EXPECT_EQ("HTTP/0.9 200 OK", out.status_line);
   EXPECT_EQ("xxxxxHTTP/1.1 404 Not Found\nServer: blah", out.response_data);
@@ -300,7 +395,8 @@
     MockRead("HTTP/1.0 404 Not Found\nServer: blah\n\nDATA"),
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
+  SimpleGetHelperResult out = SimpleGetHelper(data_reads,
+                                              arraysize(data_reads));
   EXPECT_EQ(OK, out.rv);
   EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line);
   EXPECT_EQ("DATA", out.response_data);
@@ -312,7 +408,8 @@
     MockRead("HTT"),
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
+  SimpleGetHelperResult out = SimpleGetHelper(data_reads,
+                                              arraysize(data_reads));
   EXPECT_EQ(OK, out.rv);
   EXPECT_EQ("HTTP/0.9 200 OK", out.status_line);
   EXPECT_EQ("HTT", out.response_data);
@@ -327,7 +424,8 @@
     MockRead("junk"),  // Should not be read!!
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
+  SimpleGetHelperResult out = SimpleGetHelper(data_reads,
+                                              arraysize(data_reads));
   EXPECT_EQ(OK, out.rv);
   EXPECT_EQ("HTTP/1.1 204 No Content", out.status_line);
   EXPECT_EQ("", out.response_data);
@@ -345,7 +443,8 @@
     MockRead("0\r\n\r\nHTTP/1.1 200 OK\r\n"),
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
+  SimpleGetHelperResult out = SimpleGetHelper(data_reads,
+                                              arraysize(data_reads));
   EXPECT_EQ(OK, out.rv);
   EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
   EXPECT_EQ("Hello world", out.response_data);
@@ -378,12 +477,13 @@
     MockRead(false, ERR_UNEXPECTED),  // Should not be reached.
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -423,7 +523,7 @@
     MockRead("world"),
     MockRead(false, OK),
   };
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   const char* kExpectedResponseData[] = {
@@ -440,7 +540,7 @@
 
     TestCompletionCallback callback;
 
-    int rv = trans->Start(&request, &callback, NULL);
+    int rv = trans->Start(&request, &callback, BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
 
     rv = callback.WaitForResult();
@@ -477,12 +577,12 @@
     MockRead("hello world"),
     MockRead(false, OK),
   };
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -519,12 +619,12 @@
     MockRead("hello world"),
     MockRead(false, OK),
   };
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -556,12 +656,12 @@
     MockRead(false, "HTTP/1.0 100 Continue\r\n"),
     MockRead(true, 0),
   };
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -586,12 +686,12 @@
   MockRead data_reads[] = {
     MockRead(true, 0),
   };
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -615,7 +715,7 @@
     MockRead("hello"),
     read_failure,  // Now, we reuse the connection and fail the first read.
   };
-  StaticSocketDataProvider data1(data1_reads, NULL);
+  StaticSocketDataProvider data1(data1_reads, arraysize(data1_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data1);
 
   MockRead data2_reads[] = {
@@ -623,7 +723,7 @@
     MockRead("world"),
     MockRead(true, OK),
   };
-  StaticSocketDataProvider data2(data2_reads, NULL);
+  StaticSocketDataProvider data2(data2_reads, arraysize(data2_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data2);
 
   const char* kExpectedResponseData[] = {
@@ -635,7 +735,7 @@
 
     scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
 
-    int rv = trans->Start(&request, &callback, NULL);
+    int rv = trans->Start(&request, &callback, BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
 
     rv = callback.WaitForResult();
@@ -680,12 +780,12 @@
     MockRead("hello world"),
     MockRead(false, OK),
   };
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -711,10 +811,65 @@
     MockRead("hello world"),
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
+  SimpleGetHelperResult out = SimpleGetHelper(data_reads,
+                                              arraysize(data_reads));
   EXPECT_EQ(ERR_EMPTY_RESPONSE, out.rv);
 }
 
+// Test that we correctly reuse a keep-alive connection after receiving a 304.
+TEST_F(HttpNetworkTransactionTest, KeepAliveAfter304) {
+  SessionDependencies session_deps;
+  scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps);
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.foo.com/");
+  request.load_flags = 0;
+
+  MockRead data1_reads[] = {
+    MockRead("HTTP/1.1 304 Not Modified\r\n\r\n"),
+    MockRead("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n"),
+    MockRead("hello"),
+  };
+  StaticSocketDataProvider data1(data1_reads, arraysize(data1_reads), NULL, 0);
+  session_deps.socket_factory.AddSocketDataProvider(&data1);
+
+  MockRead data2_reads[] = {
+    MockRead(false, ERR_UNEXPECTED),  // Should not be reached.
+  };
+  StaticSocketDataProvider data2(data2_reads, arraysize(data2_reads), NULL, 0);
+  session_deps.socket_factory.AddSocketDataProvider(&data2);
+
+  for (int i = 0; i < 2; ++i) {
+    TestCompletionCallback callback;
+
+    scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+    int rv = trans->Start(&request, &callback, BoundNetLog());
+    EXPECT_EQ(ERR_IO_PENDING, rv);
+
+    rv = callback.WaitForResult();
+    EXPECT_EQ(OK, rv);
+
+    const HttpResponseInfo* response = trans->GetResponseInfo();
+    EXPECT_TRUE(response != NULL);
+
+    EXPECT_TRUE(response->headers != NULL);
+    if (i == 0) {
+      EXPECT_EQ("HTTP/1.1 304 Not Modified",
+                response->headers->GetStatusLine());
+      // We intentionally don't read the response in this case, to reflect how
+      // HttpCache::Transaction uses HttpNetworkTransaction.
+    } else {
+      EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+      std::string response_data;
+      rv = ReadTransaction(trans.get(), &response_data);
+      EXPECT_EQ(OK, rv);
+      EXPECT_EQ("hello", response_data);
+    }
+  }
+}
+
 // Test the request-challenge-retry sequence for basic auth.
 // (basic auth is the easiest to mock, because it has no randomness).
 TEST_F(HttpNetworkTransactionTest, BasicAuth) {
@@ -763,14 +918,16 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
-  StaticSocketDataProvider data2(data_reads2, data_writes2);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
   session_deps.socket_factory.AddSocketDataProvider(&data2);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -825,11 +982,12 @@
     MockRead(false, ERR_FAILED),
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -879,12 +1037,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -951,12 +1110,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -1031,12 +1191,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -1118,14 +1279,16 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
-  StaticSocketDataProvider data2(data_reads2, data_writes2);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
   session_deps.socket_factory.AddSocketDataProvider(&data2);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -1160,6 +1323,8 @@
 TEST_F(HttpNetworkTransactionTest, BasicAuthProxyKeepAlive) {
   // Configure against proxy server "myproxy:70".
   SessionDependencies session_deps(CreateFixedProxyService("myproxy:70"));
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  session_deps.net_log = log.bound().net_log();
   scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
 
   scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
@@ -1202,16 +1367,24 @@
     MockRead(false, ERR_UNEXPECTED),  // Should not be reached.
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, log.bound());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
   EXPECT_EQ(OK, rv);
+  size_t pos = ExpectLogContainsSomewhere(
+      log.entries(), 0, NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
+      NetLog::PHASE_NONE);
+  ExpectLogContainsSomewhere(
+      log.entries(), pos,
+      NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
+      NetLog::PHASE_NONE);
 
   const HttpResponseInfo* response = trans->GetResponseInfo();
   EXPECT_FALSE(response == NULL);
@@ -1251,6 +1424,10 @@
   EXPECT_EQ(L"myproxy:70", response->auth_challenge->host_and_port);
   EXPECT_EQ(L"MyRealm1", response->auth_challenge->realm);
   EXPECT_EQ(L"basic", response->auth_challenge->scheme);
+
+  // Flush the idle socket before the NetLog and HttpNetworkTransaction go
+  // out of scope.
+  session->FlushSocketPools();
 }
 
 // Test that we don't read the response body when we fail to establish a tunnel,
@@ -1283,12 +1460,13 @@
     MockRead(false, ERR_UNEXPECTED),  // Should not be reached.
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -1305,6 +1483,49 @@
   std::string response_data;
   rv = ReadTransaction(trans.get(), &response_data);
   EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv);
+
+  // Flush the idle socket before the HttpNetworkTransaction goes out of scope.
+  session->FlushSocketPools();
+}
+
+// Test when a server (non-proxy) returns a 407 (proxy-authenticate).
+// The request should fail with ERR_UNEXPECTED_PROXY_AUTH.
+TEST_F(HttpNetworkTransactionTest, UnexpectedProxyAuth) {
+  // We are using a DIRECT connection (i.e. no proxy) for this session.
+  SessionDependencies session_deps;
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  request.load_flags = 0;
+
+  MockWrite data_writes1[] = {
+    MockWrite("GET / HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Connection: keep-alive\r\n\r\n"),
+  };
+
+  MockRead data_reads1[] = {
+    MockRead("HTTP/1.0 407 Proxy Auth required\r\n"),
+    MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+    // Large content-length -- won't matter, as connection will be reset.
+    MockRead("Content-Length: 10000\r\n\r\n"),
+    MockRead(false, ERR_FAILED),
+  };
+
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  session_deps.socket_factory.AddSocketDataProvider(&data1);
+
+  TestCompletionCallback callback;
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  rv = callback.WaitForResult();
+  EXPECT_EQ(ERR_UNEXPECTED_PROXY_AUTH, rv);
 }
 
 void HttpNetworkTransactionTest::ConnectStatusHelperWithExpectedStatus(
@@ -1335,12 +1556,13 @@
     MockRead(false, ERR_UNEXPECTED),  // Should not be reached.
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -1448,7 +1670,7 @@
 TEST_F(HttpNetworkTransactionTest, ConnectStatus407) {
   ConnectStatusHelperWithExpectedStatus(
       MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
-      ERR_PROXY_AUTH_REQUESTED);
+      ERR_PROXY_AUTH_UNSUPPORTED);
 }
 
 TEST_F(HttpNetworkTransactionTest, ConnectStatus408) {
@@ -1590,16 +1812,19 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
-  StaticSocketDataProvider data2(data_reads2, data_writes2);
-  StaticSocketDataProvider data3(data_reads3, data_writes3);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
+  StaticSocketDataProvider data3(data_reads3, arraysize(data_reads3),
+                                 data_writes3, arraysize(data_writes3));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
   session_deps.socket_factory.AddSocketDataProvider(&data2);
   session_deps.socket_factory.AddSocketDataProvider(&data3);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -1655,7 +1880,7 @@
 // Enter the correct password and authenticate successfully.
 TEST_F(HttpNetworkTransactionTest, NTLMAuth1) {
   HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockGenerateRandom1,
-                                                         MockGetHostName);
+                                                    MockGetHostName);
   SessionDependencies session_deps;
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(CreateSession(&session_deps)));
@@ -1673,8 +1898,9 @@
 
   MockRead data_reads1[] = {
     MockRead("HTTP/1.1 401 Access Denied\r\n"),
-    // Negotiate and NTLM are often requested together.  We only support NTLM.
-    MockRead("WWW-Authenticate: Negotiate\r\n"),
+    // Negotiate and NTLM are often requested together.  However, we only want
+    // to test NTLM. Since Negotiate is preferred over NTLM, we have to skip
+    // the header that requests Negotiate for this test.
     MockRead("WWW-Authenticate: NTLM\r\n"),
     MockRead("Connection: close\r\n"),
     MockRead("Content-Length: 42\r\n"),
@@ -1729,14 +1955,16 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
-  StaticSocketDataProvider data2(data_reads2, data_writes2);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
   session_deps.socket_factory.AddSocketDataProvider(&data2);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -1751,9 +1979,10 @@
   EXPECT_FALSE(trans->IsReadyToRestartForAuth());
 
   const HttpResponseInfo* response = trans->GetResponseInfo();
-  EXPECT_FALSE(response == NULL);
+  ASSERT_FALSE(response == NULL);
 
-  // The password prompt info should have been set in response->auth_challenge.
+  // The password prompt info should have been set in
+  // response->auth_challenge.
   EXPECT_FALSE(response->auth_challenge.get() == NULL);
 
   EXPECT_EQ(L"172.22.68.17:80", response->auth_challenge->host_and_port);
@@ -1769,6 +1998,8 @@
   EXPECT_EQ(OK, rv);
 
   response = trans->GetResponseInfo();
+  ASSERT_FALSE(response == NULL);
+
   EXPECT_TRUE(response->auth_challenge.get() == NULL);
   EXPECT_EQ(13, response->headers->GetContentLength());
 }
@@ -1776,7 +2007,7 @@
 // Enter a wrong password, and then the correct one.
 TEST_F(HttpNetworkTransactionTest, NTLMAuth2) {
   HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockGenerateRandom2,
-                                                         MockGetHostName);
+                                                    MockGetHostName);
   SessionDependencies session_deps;
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(CreateSession(&session_deps)));
@@ -1794,8 +2025,9 @@
 
   MockRead data_reads1[] = {
     MockRead("HTTP/1.1 401 Access Denied\r\n"),
-    // Negotiate and NTLM are often requested together.  We only support NTLM.
-    MockRead("WWW-Authenticate: Negotiate\r\n"),
+    // Negotiate and NTLM are often requested together.  However, we only want
+    // to test NTLM. Since Negotiate is preferred over NTLM, we have to skip
+    // the header that requests Negotiate for this test.
     MockRead("WWW-Authenticate: NTLM\r\n"),
     MockRead("Connection: close\r\n"),
     MockRead("Content-Length: 42\r\n"),
@@ -1844,7 +2076,6 @@
 
     // Wrong password.
     MockRead("HTTP/1.1 401 Access Denied\r\n"),
-    MockRead("WWW-Authenticate: Negotiate\r\n"),
     MockRead("WWW-Authenticate: NTLM\r\n"),
     MockRead("Connection: close\r\n"),
     MockRead("Content-Length: 42\r\n"),
@@ -1899,16 +2130,19 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
-  StaticSocketDataProvider data2(data_reads2, data_writes2);
-  StaticSocketDataProvider data3(data_reads3, data_writes3);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
+  StaticSocketDataProvider data3(data_reads3, arraysize(data_reads3),
+                                 data_writes3, arraysize(data_writes3));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
   session_deps.socket_factory.AddSocketDataProvider(&data2);
   session_deps.socket_factory.AddSocketDataProvider(&data3);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -1997,12 +2231,12 @@
     MockRead("\r\nBODY"),
     MockRead(false, OK),
   };
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -2044,12 +2278,13 @@
     MockRead(false, ERR_UNEXPECTED),  // Should not be reached.
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -2093,12 +2328,12 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -2150,12 +2385,12 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -2219,7 +2454,8 @@
     MockWrite(false, 93),  // POST
     MockWrite(false, ERR_CONNECTION_ABORTED),  // POST data
   };
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
 
   // The second socket is used for the second attempt of transaction 2.
 
@@ -2234,7 +2470,8 @@
     MockWrite(false, 93),  // POST
     MockWrite(false, 3),  // POST data
   };
-  StaticSocketDataProvider data2(data_reads2, data_writes2);
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
 
   session_deps.socket_factory.AddSocketDataProvider(&data1);
   session_deps.socket_factory.AddSocketDataProvider(&data2);
@@ -2249,7 +2486,7 @@
 
     TestCompletionCallback callback;
 
-    int rv = trans->Start(&request[i], &callback, NULL);
+    int rv = trans->Start(&request[i], &callback, BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
 
     rv = callback.WaitForResult();
@@ -2315,14 +2552,16 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
-  StaticSocketDataProvider data2(data_reads2, data_writes2);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
   session_deps.socket_factory.AddSocketDataProvider(&data2);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -2408,16 +2647,19 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
-  StaticSocketDataProvider data2(data_reads2, data_writes2);
-  StaticSocketDataProvider data3(data_reads3, data_writes3);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
+  StaticSocketDataProvider data3(data_reads3, arraysize(data_reads3),
+                                 data_writes3, arraysize(data_writes3));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
   session_deps.socket_factory.AddSocketDataProvider(&data2);
   session_deps.socket_factory.AddSocketDataProvider(&data3);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -2501,14 +2743,16 @@
       MockRead(false, OK),
     };
 
-    StaticSocketDataProvider data1(data_reads1, data_writes1);
-    StaticSocketDataProvider data2(data_reads2, data_writes2);
+    StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                   data_writes1, arraysize(data_writes1));
+    StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                   data_writes2, arraysize(data_writes2));
     session_deps.socket_factory.AddSocketDataProvider(&data1);
     session_deps.socket_factory.AddSocketDataProvider(&data2);
 
     TestCompletionCallback callback1;
 
-    int rv = trans->Start(&request, &callback1, NULL);
+    int rv = trans->Start(&request, &callback1, BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
 
     rv = callback1.WaitForResult();
@@ -2584,14 +2828,16 @@
       MockRead(false, OK),
     };
 
-    StaticSocketDataProvider data1(data_reads1, data_writes1);
-    StaticSocketDataProvider data2(data_reads2, data_writes2);
+    StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                   data_writes1, arraysize(data_writes1));
+    StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                   data_writes2, arraysize(data_writes2));
     session_deps.socket_factory.AddSocketDataProvider(&data1);
     session_deps.socket_factory.AddSocketDataProvider(&data2);
 
     TestCompletionCallback callback1;
 
-    int rv = trans->Start(&request, &callback1, NULL);
+    int rv = trans->Start(&request, &callback1, BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
 
     rv = callback1.WaitForResult();
@@ -2650,12 +2896,13 @@
       MockRead(false, OK),
     };
 
-    StaticSocketDataProvider data1(data_reads1, data_writes1);
+    StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                   data_writes1, arraysize(data_writes1));
     session_deps.socket_factory.AddSocketDataProvider(&data1);
 
     TestCompletionCallback callback1;
 
-    int rv = trans->Start(&request, &callback1, NULL);
+    int rv = trans->Start(&request, &callback1, BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
 
     rv = callback1.WaitForResult();
@@ -2708,14 +2955,16 @@
       MockRead(false, OK),
     };
 
-    StaticSocketDataProvider data1(data_reads1, data_writes1);
-    StaticSocketDataProvider data2(data_reads2, data_writes2);
+    StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                   data_writes1, arraysize(data_writes1));
+    StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                   data_writes2, arraysize(data_writes2));
     session_deps.socket_factory.AddSocketDataProvider(&data1);
     session_deps.socket_factory.AddSocketDataProvider(&data2);
 
     TestCompletionCallback callback1;
 
-    int rv = trans->Start(&request, &callback1, NULL);
+    int rv = trans->Start(&request, &callback1, BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
 
     rv = callback1.WaitForResult();
@@ -2792,16 +3041,19 @@
       MockRead(false, OK),
     };
 
-    StaticSocketDataProvider data1(data_reads1, data_writes1);
-    StaticSocketDataProvider data2(data_reads2, data_writes2);
-    StaticSocketDataProvider data3(data_reads3, data_writes3);
+    StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                   data_writes1, arraysize(data_writes1));
+    StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                   data_writes2, arraysize(data_writes2));
+    StaticSocketDataProvider data3(data_reads3, arraysize(data_reads3),
+                                   data_writes3, arraysize(data_writes3));
     session_deps.socket_factory.AddSocketDataProvider(&data1);
     session_deps.socket_factory.AddSocketDataProvider(&data2);
     session_deps.socket_factory.AddSocketDataProvider(&data3);
 
     TestCompletionCallback callback1;
 
-    int rv = trans->Start(&request, &callback1, NULL);
+    int rv = trans->Start(&request, &callback1, BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
 
     rv = callback1.WaitForResult();
@@ -2841,6 +3093,141 @@
   }
 }
 
+// Tests that nonce count increments when multiple auth attempts
+// are started with the same nonce.
+TEST_F(HttpNetworkTransactionTest, DigestPreAuthNonceCount) {
+  SessionDependencies session_deps;
+  scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps);
+  HttpAuthHandlerDigest::SetFixedCnonce(true);
+
+  // Transaction 1: authenticate (foo, bar) on MyRealm1
+  {
+    scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+    HttpRequestInfo request;
+    request.method = "GET";
+    request.url = GURL("http://www.google.com/x/y/z");
+    request.load_flags = 0;
+
+    MockWrite data_writes1[] = {
+      MockWrite("GET /x/y/z HTTP/1.1\r\n"
+                "Host: www.google.com\r\n"
+                "Connection: keep-alive\r\n\r\n"),
+    };
+
+    MockRead data_reads1[] = {
+      MockRead("HTTP/1.0 401 Unauthorized\r\n"),
+      MockRead("WWW-Authenticate: Digest realm=\"digestive\", nonce=\"OU812\", "
+               "algorithm=MD5, qop=\"auth\"\r\n\r\n"),
+      MockRead(false, OK),
+    };
+
+    // Resend with authorization (username=foo, password=bar)
+    MockWrite data_writes2[] = {
+      MockWrite("GET /x/y/z HTTP/1.1\r\n"
+                "Host: www.google.com\r\n"
+                "Connection: keep-alive\r\n"
+                "Authorization: Digest username=\"foo\", realm=\"digestive\", "
+                "nonce=\"OU812\", uri=\"/x/y/z\", algorithm=MD5, "
+                "response=\"03ffbcd30add722589c1de345d7a927f\", qop=auth, "
+                "nc=00000001, cnonce=\"0123456789abcdef\"\r\n\r\n"),
+    };
+
+    // Sever accepts the authorization.
+    MockRead data_reads2[] = {
+      MockRead("HTTP/1.0 200 OK\r\n"),
+      MockRead(false, OK),
+    };
+
+    StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                   data_writes1, arraysize(data_writes1));
+    StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                   data_writes2, arraysize(data_writes2));
+    session_deps.socket_factory.AddSocketDataProvider(&data1);
+    session_deps.socket_factory.AddSocketDataProvider(&data2);
+
+    TestCompletionCallback callback1;
+
+    int rv = trans->Start(&request, &callback1, BoundNetLog());
+    EXPECT_EQ(ERR_IO_PENDING, rv);
+
+    rv = callback1.WaitForResult();
+    EXPECT_EQ(OK, rv);
+
+    const HttpResponseInfo* response = trans->GetResponseInfo();
+    ASSERT_FALSE(response == NULL);
+
+    // The password prompt info should have been set in
+    // response->auth_challenge.
+    ASSERT_FALSE(response->auth_challenge.get() == NULL);
+
+    EXPECT_EQ(L"www.google.com:80", response->auth_challenge->host_and_port);
+    EXPECT_EQ(L"digestive", response->auth_challenge->realm);
+    EXPECT_EQ(L"digest", response->auth_challenge->scheme);
+
+    TestCompletionCallback callback2;
+
+    rv = trans->RestartWithAuth(L"foo", L"bar", &callback2);
+    EXPECT_EQ(ERR_IO_PENDING, rv);
+
+    rv = callback2.WaitForResult();
+    EXPECT_EQ(OK, rv);
+
+    response = trans->GetResponseInfo();
+    ASSERT_FALSE(response == NULL);
+    EXPECT_TRUE(response->auth_challenge.get() == NULL);
+  }
+
+  // ------------------------------------------------------------------------
+
+  // Transaction 2: Request another resource in digestive's protection space.
+  // This will preemptively add an Authorization header which should have an
+  // "nc" value of 2 (as compared to 1 in the first use.
+  {
+    scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+    HttpRequestInfo request;
+    request.method = "GET";
+    // Note that Transaction 1 was at /x/y/z, so this is in the same
+    // protection space as digest.
+    request.url = GURL("http://www.google.com/x/y/a/b");
+    request.load_flags = 0;
+
+    MockWrite data_writes1[] = {
+      MockWrite("GET /x/y/a/b HTTP/1.1\r\n"
+                "Host: www.google.com\r\n"
+                "Connection: keep-alive\r\n"
+                "Authorization: Digest username=\"foo\", realm=\"digestive\", "
+                "nonce=\"OU812\", uri=\"/x/y/a/b\", algorithm=MD5, "
+                "response=\"d6f9a2c07d1c5df7b89379dca1269b35\", qop=auth, "
+                "nc=00000002, cnonce=\"0123456789abcdef\"\r\n\r\n"),
+    };
+
+    // Sever accepts the authorization.
+    MockRead data_reads1[] = {
+      MockRead("HTTP/1.0 200 OK\r\n"),
+      MockRead("Content-Length: 100\r\n\r\n"),
+      MockRead(false, OK),
+    };
+
+    StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                   data_writes1, arraysize(data_writes1));
+    session_deps.socket_factory.AddSocketDataProvider(&data1);
+
+    TestCompletionCallback callback1;
+
+    int rv = trans->Start(&request, &callback1, BoundNetLog());
+    EXPECT_EQ(ERR_IO_PENDING, rv);
+
+    rv = callback1.WaitForResult();
+    EXPECT_EQ(OK, rv);
+
+    const HttpResponseInfo* response = trans->GetResponseInfo();
+    ASSERT_FALSE(response == NULL);
+    EXPECT_TRUE(response->auth_challenge.get() == NULL);
+  }
+}
+
 // Test the ResetStateForRestart() private method.
 TEST_F(HttpNetworkTransactionTest, ResetStateForRestart) {
   // Create a transaction (the dependencies aren't important).
@@ -2865,7 +3252,8 @@
     std::string temp("HTTP/1.1 200 OK\nVary: foo, bar\n\n");
     std::replace(temp.begin(), temp.end(), '\n', '\0');
     scoped_refptr<HttpResponseHeaders> headers = new HttpResponseHeaders(temp);
-    request.extra_headers = "Foo: 1\nbar: 23";
+    request.extra_headers.SetHeader("Foo", "1");
+    request.extra_headers.SetHeader("bar", "23");
     EXPECT_TRUE(response->vary_data.Init(request, *headers));
   }
 
@@ -2908,7 +3296,8 @@
   };
 
   StaticSocketDataProvider ssl_bad_certificate;
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   SSLSocketDataProvider ssl_bad(true, ERR_CERT_AUTHORITY_INVALID);
   SSLSocketDataProvider ssl(true, OK);
 
@@ -2919,7 +3308,7 @@
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -2975,8 +3364,11 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider ssl_bad_certificate(proxy_reads, proxy_writes);
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider ssl_bad_certificate(
+      proxy_reads, arraysize(proxy_reads),
+      proxy_writes, arraysize(proxy_writes));
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   SSLSocketDataProvider ssl_bad(true, ERR_CERT_AUTHORITY_INVALID);
   SSLSocketDataProvider ssl(true, OK);
 
@@ -2993,7 +3385,7 @@
     scoped_ptr<HttpTransaction> trans(
         new HttpNetworkTransaction(CreateSession(&session_deps)));
 
-    int rv = trans->Start(&request, &callback, NULL);
+    int rv = trans->Start(&request, &callback, BoundNetLog());
     EXPECT_EQ(ERR_IO_PENDING, rv);
 
     rv = callback.WaitForResult();
@@ -3020,7 +3412,8 @@
   HttpRequestInfo request;
   request.method = "GET";
   request.url = GURL("http://www.google.com/");
-  request.user_agent = "Chromium Ultra Awesome X Edition";
+  request.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent,
+                                  "Chromium Ultra Awesome X Edition");
 
   MockWrite data_writes[] = {
     MockWrite("GET / HTTP/1.1\r\n"
@@ -3037,12 +3430,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3075,12 +3469,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3111,12 +3506,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3147,12 +3543,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3183,12 +3580,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3221,12 +3619,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3259,12 +3658,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3279,7 +3679,7 @@
   HttpRequestInfo request;
   request.method = "GET";
   request.url = GURL("http://www.google.com/");
-  request.extra_headers = "FooHeader: Bar\r\n";
+  request.extra_headers.SetHeader("FooHeader", "Bar");
 
   MockWrite data_writes[] = {
     MockWrite("GET / HTTP/1.1\r\n"
@@ -3296,12 +3696,54 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  rv = callback.WaitForResult();
+  EXPECT_EQ(OK, rv);
+}
+
+TEST_F(HttpNetworkTransactionTest, BuildRequest_ExtraHeadersStripped) {
+  SessionDependencies session_deps;
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  request.extra_headers.SetHeader("referer", "www.foo.com");
+  request.extra_headers.SetHeader("hEllo", "Kitty");
+  request.extra_headers.SetHeader("FoO", "bar");
+
+  MockWrite data_writes[] = {
+    MockWrite("GET / HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Connection: keep-alive\r\n"
+              "hEllo: Kitty\r\n"
+              "FoO: bar\r\n\r\n"),
+  };
+
+  // Lastly, the server responds with the actual content.
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.0 200 OK\r\n"),
+    MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
+    MockRead("Content-Length: 100\r\n\r\n"),
+    MockRead(false, OK),
+  };
+
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
+  session_deps.socket_factory.AddSocketDataProvider(&data);
+
+  TestCompletionCallback callback;
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3338,12 +3780,13 @@
     MockRead(false, OK)
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3390,7 +3833,8 @@
     MockRead(false, OK)
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   SSLSocketDataProvider ssl(true, OK);
@@ -3398,7 +3842,7 @@
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3457,12 +3901,13 @@
     MockRead(false, OK)
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3523,7 +3968,8 @@
     MockRead(false, OK)
   };
 
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   SSLSocketDataProvider ssl(true, OK);
@@ -3531,7 +3977,7 @@
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3547,70 +3993,226 @@
 }
 
 // Tests that for connection endpoints the group names are correctly set.
-TEST_F(HttpNetworkTransactionTest, GroupNameForProxyConnections) {
-  const struct {
-    const std::string proxy_server;
-    const std::string url;
-    const std::string expected_group_name;
-  } tests[] = {
+
+struct GroupNameTest {
+  std::string proxy_server;
+  std::string url;
+  std::string expected_group_name;
+  bool ssl;
+};
+
+scoped_refptr<HttpNetworkSession> SetupSessionForGroupNameTests(
+    const std::string& proxy_server) {
+  SessionDependencies session_deps(CreateFixedProxyService(proxy_server));
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+
+  HttpAlternateProtocols* alternate_protocols =
+      session->mutable_alternate_protocols();
+  alternate_protocols->SetAlternateProtocolFor(
+      HostPortPair("host.with.alternate", 80), 443,
+      HttpAlternateProtocols::NPN_SPDY_1);
+
+  return session;
+}
+
+int GroupNameTransactionHelper(
+    const std::string& url,
+    const scoped_refptr<HttpNetworkSession>& session) {
+  scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL(url);
+  request.load_flags = 0;
+
+  TestCompletionCallback callback;
+
+  // We do not complete this request, the dtor will clean the transaction up.
+  return trans->Start(&request, &callback, BoundNetLog());
+}
+
+TEST_F(HttpNetworkTransactionTest, GroupNameForDirectConnections) {
+  const GroupNameTest tests[] = {
     {
-      "",  // no proxy (direct)
+      "",  // unused
       "http://www.google.com/direct",
-      "http://www.google.com/",
+      "www.google.com:80",
+      false,
     },
     {
-      "http_proxy",
-      "http://www.google.com/http_proxy_normal",
-      "proxy/http_proxy:80/",
-    },
-    {
-      "socks4://socks_proxy:1080",
-      "http://www.google.com/socks4_direct",
-      "proxy/socks4://socks_proxy:1080/http://www.google.com/",
+      "",  // unused
+      "http://[2001:1418:13:1::25]/direct",
+      "[2001:1418:13:1::25]:80",
+      false,
     },
 
     // SSL Tests
     {
-      "",
+      "",  // unused
       "https://www.google.com/direct_ssl",
-      "https://www.google.com/",
+      "ssl/www.google.com:443",
+      true,
     },
     {
-      "http_proxy",
-      "https://www.google.com/http_connect_ssl",
-      "proxy/http_proxy:80/https://www.google.com/",
+      "",  // unused
+      "https://[2001:1418:13:1::25]/direct",
+      "ssl/[2001:1418:13:1::25]:443",
+      true,
     },
     {
-      "socks4://socks_proxy:1080",
-      "https://www.google.com/socks4_ssl",
-      "proxy/socks4://socks_proxy:1080/https://www.google.com/",
+      "",  // unused
+      "http://host.with.alternate/direct",
+      "ssl/host.with.alternate:443",
+      true,
     },
   };
 
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
-    SessionDependencies session_deps(
-        CreateFixedProxyService(tests[i].proxy_server));
+    scoped_refptr<HttpNetworkSession> session(
+        SetupSessionForGroupNameTests(tests[i].proxy_server));
 
-    scoped_refptr<CaptureGroupNameSocketPool> conn_pool(
-        new CaptureGroupNameSocketPool());
+    HttpNetworkSessionPeer peer(session);
+    scoped_refptr<CaptureGroupNameTCPSocketPool> tcp_conn_pool(
+        new CaptureGroupNameTCPSocketPool(session.get()));
+    peer.SetTCPSocketPool(tcp_conn_pool);
+    scoped_refptr<CaptureGroupNameSSLSocketPool> ssl_conn_pool(
+        new CaptureGroupNameSSLSocketPool(session.get()));
+    peer.SetSSLSocketPool(ssl_conn_pool);
 
-    scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
-    session->tcp_socket_pool_ = conn_pool.get();
+    EXPECT_EQ(ERR_IO_PENDING,
+              GroupNameTransactionHelper(tests[i].url, session));
+    if (tests[i].ssl)
+      EXPECT_EQ(tests[i].expected_group_name,
+                ssl_conn_pool->last_group_name_received());
+    else
+      EXPECT_EQ(tests[i].expected_group_name,
+                tcp_conn_pool->last_group_name_received());
+  }
+
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
+}
+
+TEST_F(HttpNetworkTransactionTest, GroupNameForHTTPProxyConnections) {
+  const GroupNameTest tests[] = {
+    {
+      "http_proxy",
+      "http://www.google.com/http_proxy_normal",
+      "www.google.com:80",
+      false,
+    },
+
+    // SSL Tests
+    {
+      "http_proxy",
+      "https://www.google.com/http_connect_ssl",
+      "ssl/www.google.com:443",
+      true,
+    },
+
+    {
+      "http_proxy",
+      "http://host.with.alternate/direct",
+      "ssl/host.with.alternate:443",
+      true,
+    },
+  };
+
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+    scoped_refptr<HttpNetworkSession> session(
+        SetupSessionForGroupNameTests(tests[i].proxy_server));
+
+    HttpNetworkSessionPeer peer(session);
+
+    HostPortPair proxy_host("http_proxy", 80);
+    scoped_refptr<CaptureGroupNameHttpProxySocketPool> http_proxy_pool(
+        new CaptureGroupNameHttpProxySocketPool(session.get()));
+    peer.SetSocketPoolForHTTPProxy(proxy_host, http_proxy_pool);
+    scoped_refptr<CaptureGroupNameSSLSocketPool> ssl_conn_pool(
+        new CaptureGroupNameSSLSocketPool(session.get()));
+    peer.SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool);
+
+    EXPECT_EQ(ERR_IO_PENDING,
+              GroupNameTransactionHelper(tests[i].url, session));
+    if (tests[i].ssl)
+      EXPECT_EQ(tests[i].expected_group_name,
+                ssl_conn_pool->last_group_name_received());
+    else
+      EXPECT_EQ(tests[i].expected_group_name,
+                http_proxy_pool->last_group_name_received());
+  }
+
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
+}
+
+TEST_F(HttpNetworkTransactionTest, GroupNameForSOCKSConnections) {
+  const GroupNameTest tests[] = {
+    {
+      "socks4://socks_proxy:1080",
+      "http://www.google.com/socks4_direct",
+      "socks4/www.google.com:80",
+      false,
+    },
+    {
+      "socks5://socks_proxy:1080",
+      "http://www.google.com/socks5_direct",
+      "socks5/www.google.com:80",
+      false,
+    },
+
+    // SSL Tests
+    {
+      "socks4://socks_proxy:1080",
+      "https://www.google.com/socks4_ssl",
+      "socks4/ssl/www.google.com:443",
+      true,
+    },
+    {
+      "socks5://socks_proxy:1080",
+      "https://www.google.com/socks5_ssl",
+      "socks5/ssl/www.google.com:443",
+      true,
+    },
+
+    {
+      "socks4://socks_proxy:1080",
+      "http://host.with.alternate/direct",
+      "socks4/ssl/host.with.alternate:443",
+      true,
+    },
+  };
+
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+    scoped_refptr<HttpNetworkSession> session(
+        SetupSessionForGroupNameTests(tests[i].proxy_server));
+    HttpNetworkSessionPeer peer(session);
+
+    HostPortPair proxy_host("socks_proxy", 1080);
+    scoped_refptr<CaptureGroupNameSOCKSSocketPool> socks_conn_pool(
+        new CaptureGroupNameSOCKSSocketPool(session.get()));
+    peer.SetSocketPoolForSOCKSProxy(proxy_host, socks_conn_pool);
+    scoped_refptr<CaptureGroupNameSSLSocketPool> ssl_conn_pool(
+        new CaptureGroupNameSSLSocketPool(session.get()));
+    peer.SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool);
 
     scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
 
-    HttpRequestInfo request;
-    request.method = "GET";
-    request.url = GURL(tests[i].url);
-    request.load_flags = 0;
-
-    TestCompletionCallback callback;
-
-    // We do not complete this request, the dtor will clean the transaction up.
-    EXPECT_EQ(ERR_IO_PENDING, trans->Start(&request, &callback, NULL));
-    EXPECT_EQ(tests[i].expected_group_name,
-              conn_pool->last_group_name_received());
+    EXPECT_EQ(ERR_IO_PENDING,
+              GroupNameTransactionHelper(tests[i].url, session));
+    if (tests[i].ssl)
+      EXPECT_EQ(tests[i].expected_group_name,
+                ssl_conn_pool->last_group_name_received());
+    else
+      EXPECT_EQ(tests[i].expected_group_name,
+                socks_conn_pool->last_group_name_received());
   }
+
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
 }
 
 TEST_F(HttpNetworkTransactionTest, ReconsiderProxyAfterFailedConnection) {
@@ -3630,7 +4232,7 @@
 
   TestCompletionCallback callback;
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3695,7 +4297,7 @@
   MockRead data_reads[] = {
     MockRead(false, ERR_FAILED),
   };
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   // Issue a request, containing an HTTP referrer.
@@ -3706,7 +4308,7 @@
 
   // Run the request until it fails reading from the socket.
   TestCompletionCallback callback;
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   rv = callback.WaitForResult();
   EXPECT_EQ(ERR_FAILED, rv);
@@ -3715,9 +4317,9 @@
   EXPECT_TRUE(resolution_observer.did_complete_with_expected_referrer());
 }
 
-// Make sure that when the load flags contain LOAD_BYPASS_CACHE, the resolver's
-// host cache is bypassed.
-TEST_F(HttpNetworkTransactionTest, BypassHostCacheOnRefresh) {
+// Base test to make sure that when the load flags for a request specify to
+// bypass the cache, the DNS cache is not used.
+void BypassHostCacheOnRefreshHelper(int load_flags) {
   SessionDependencies session_deps;
 
   // Select a host resolver that does caching.
@@ -3731,7 +4333,7 @@
   AddressList addrlist;
   int rv = session_deps.host_resolver->Resolve(
       HostResolver::RequestInfo("www.google.com", 80), &addrlist,
-      NULL, NULL, NULL);
+      NULL, NULL, BoundNetLog());
   EXPECT_EQ(OK, rv);
 
   // Verify that it was added to host cache, by doing a subsequent async lookup
@@ -3739,7 +4341,7 @@
   TestCompletionCallback resolve_callback;
   rv = session_deps.host_resolver->Resolve(
       HostResolver::RequestInfo("www.google.com", 80), &addrlist,
-      &resolve_callback, NULL, NULL);
+      &resolve_callback, NULL, BoundNetLog());
   ASSERT_EQ(OK, rv);
 
   // Inject a failure the next time that "www.google.com" is resolved. This way
@@ -3750,18 +4352,18 @@
   // Connect up a mock socket which will fail with ERR_UNEXPECTED during the
   // first read -- this won't be reached as the host resolution will fail first.
   MockRead data_reads[] = { MockRead(false, ERR_UNEXPECTED) };
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   // Issue a request, asking to bypass the cache(s).
   HttpRequestInfo request;
   request.method = "GET";
-  request.load_flags = LOAD_BYPASS_CACHE;
+  request.load_flags = load_flags;
   request.url = GURL("http://www.google.com/");
 
   // Run the request.
   TestCompletionCallback callback;
-  rv = trans->Start(&request, &callback, NULL);
+  rv = trans->Start(&request, &callback, BoundNetLog());
   ASSERT_EQ(ERR_IO_PENDING, rv);
   rv = callback.WaitForResult();
 
@@ -3770,6 +4372,20 @@
   EXPECT_EQ(ERR_NAME_NOT_RESOLVED, rv);
 }
 
+// There are multiple load flags that should trigger the host cache bypass.
+// Test each in isolation:
+TEST_F(HttpNetworkTransactionTest, BypassHostCacheOnRefresh1) {
+  BypassHostCacheOnRefreshHelper(LOAD_BYPASS_CACHE);
+}
+
+TEST_F(HttpNetworkTransactionTest, BypassHostCacheOnRefresh2) {
+  BypassHostCacheOnRefreshHelper(LOAD_VALIDATE_CACHE);
+}
+
+TEST_F(HttpNetworkTransactionTest, BypassHostCacheOnRefresh3) {
+  BypassHostCacheOnRefreshHelper(LOAD_DISABLE_CACHE);
+}
+
 // Make sure we can handle an error when writing the request.
 TEST_F(HttpNetworkTransactionTest, RequestWriteError) {
   SessionDependencies session_deps;
@@ -3783,7 +4399,8 @@
   MockWrite write_failure[] = {
     MockWrite(true, ERR_CONNECTION_RESET),
   };
-  StaticSocketDataProvider data(NULL, write_failure);
+  StaticSocketDataProvider data(NULL, 0,
+                                write_failure, arraysize(write_failure));
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
@@ -3791,7 +4408,7 @@
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(CreateSession(&session_deps)));
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3813,7 +4430,7 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data(data_reads, NULL);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
   session_deps.socket_factory.AddSocketDataProvider(&data);
 
   TestCompletionCallback callback;
@@ -3821,7 +4438,7 @@
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(CreateSession(&session_deps)));
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3866,7 +4483,8 @@
     MockRead(true, ERR_CONNECTION_RESET),
   };
 
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
   session_deps.socket_factory.AddSocketDataProvider(&data1);
 
   // After calling trans->RestartWithAuth(), this is the request we should
@@ -3886,12 +4504,13 @@
     MockRead(false, OK),
   };
 
-  StaticSocketDataProvider data2(data_reads2, data_writes2);
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
   session_deps.socket_factory.AddSocketDataProvider(&data2);
 
   TestCompletionCallback callback1;
 
-  int rv = trans->Start(&request, &callback1, NULL);
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback1.WaitForResult();
@@ -3935,7 +4554,7 @@
     MockRead(false, OK)
   };
 
-  StaticSocketDataProvider data(proxy_reads, NULL);
+  StaticSocketDataProvider data(proxy_reads, arraysize(proxy_reads), NULL, 0);
   SSLSocketDataProvider ssl(true, OK);
 
   session_deps.socket_factory.AddSocketDataProvider(&data);
@@ -3948,7 +4567,7 @@
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(CreateSession(&session_deps)));
 
-  int rv = trans->Start(&request, &callback, NULL);
+  int rv = trans->Start(&request, &callback, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
@@ -3956,14 +4575,1683 @@
 }
 
 TEST_F(HttpNetworkTransactionTest, LargeContentLengthThenClose) {
+  SessionDependencies session_deps;
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  request.load_flags = 0;
+
   MockRead data_reads[] = {
     MockRead("HTTP/1.0 200 OK\r\nContent-Length:6719476739\r\n\r\n"),
     MockRead(false, OK),
   };
-  SimpleGetHelperResult out = SimpleGetHelper(data_reads);
-  EXPECT_EQ(OK, out.rv);
-  EXPECT_EQ("HTTP/1.0 200 OK", out.status_line);
-  EXPECT_EQ("", out.response_data);
+
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
+  session_deps.socket_factory.AddSocketDataProvider(&data);
+
+  TestCompletionCallback callback;
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response != NULL);
+
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.0 200 OK", response->headers->GetStatusLine());
+
+  std::string response_data;
+  rv = ReadTransaction(trans.get(), &response_data);
+  EXPECT_EQ(ERR_CONNECTION_CLOSED, rv);
+}
+
+TEST_F(HttpNetworkTransactionTest, UploadFileSmallerThanLength) {
+  SessionDependencies session_deps;
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("http://www.google.com/upload");
+  request.upload_data = new UploadData;
+  request.load_flags = 0;
+
+  FilePath temp_file_path;
+  ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path));
+  const uint64 kFakeSize = 100000;  // file is actually blank
+
+  std::vector<UploadData::Element> elements;
+  UploadData::Element element;
+  element.SetToFilePath(temp_file_path);
+  element.SetContentLength(kFakeSize);
+  elements.push_back(element);
+  request.upload_data->set_elements(elements);
+  EXPECT_EQ(kFakeSize, request.upload_data->GetContentLength());
+
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.0 200 OK\r\n\r\n"),
+    MockRead("hello world"),
+    MockRead(false, OK),
+  };
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
+  session_deps.socket_factory.AddSocketDataProvider(&data);
+
+  TestCompletionCallback callback;
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  rv = callback.WaitForResult();
+  EXPECT_EQ(OK, rv);
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response != NULL);
+
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.0 200 OK", response->headers->GetStatusLine());
+
+  std::string response_data;
+  rv = ReadTransaction(trans.get(), &response_data);
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ("hello world", response_data);
+
+  file_util::Delete(temp_file_path, false);
+}
+
+TEST_F(HttpNetworkTransactionTest, UploadUnreadableFile) {
+  // If we try to upload an unreadable file, the network stack should report
+  // the file size as zero and upload zero bytes for that file.
+  SessionDependencies session_deps;
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+  FilePath temp_file;
+  ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file));
+  std::string temp_file_content("Unreadable file.");
+  ASSERT_TRUE(file_util::WriteFile(temp_file, temp_file_content.c_str(),
+                                   temp_file_content.length()));
+  ASSERT_TRUE(file_util::MakeFileUnreadable(temp_file));
+
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("http://www.google.com/upload");
+  request.upload_data = new UploadData;
+  request.load_flags = 0;
+
+  std::vector<UploadData::Element> elements;
+  UploadData::Element element;
+  element.SetToFilePath(temp_file);
+  elements.push_back(element);
+  request.upload_data->set_elements(elements);
+
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.0 200 OK\r\n\r\n"),
+    MockRead(false, OK),
+  };
+  MockWrite data_writes[] = {
+    MockWrite("POST /upload HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Connection: keep-alive\r\n"
+              "Content-Length: 0\r\n\r\n"),
+    MockWrite(false, OK),
+  };
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+                                arraysize(data_writes));
+  session_deps.socket_factory.AddSocketDataProvider(&data);
+
+  TestCompletionCallback callback;
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  rv = callback.WaitForResult();
+  EXPECT_EQ(OK, rv);
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response != NULL);
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.0 200 OK", response->headers->GetStatusLine());
+
+  file_util::Delete(temp_file, false);
+}
+
+TEST_F(HttpNetworkTransactionTest, UnreadableUploadFileAfterAuthRestart) {
+  SessionDependencies session_deps;
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+  FilePath temp_file;
+  ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file));
+  std::string temp_file_contents("Unreadable file.");
+  std::string unreadable_contents(temp_file_contents.length(), '\0');
+  ASSERT_TRUE(file_util::WriteFile(temp_file, temp_file_contents.c_str(),
+                                   temp_file_contents.length()));
+
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("http://www.google.com/upload");
+  request.upload_data = new UploadData;
+  request.load_flags = 0;
+
+  std::vector<UploadData::Element> elements;
+  UploadData::Element element;
+  element.SetToFilePath(temp_file);
+  elements.push_back(element);
+  request.upload_data->set_elements(elements);
+
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.1 401 Unauthorized\r\n"),
+    MockRead("WWW-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+    MockRead("Content-Length: 0\r\n\r\n"),  // No response body.
+
+    MockRead("HTTP/1.1 200 OK\r\n"),
+    MockRead("Content-Length: 0\r\n\r\n"),
+    MockRead(false, OK),
+  };
+  MockWrite data_writes[] = {
+    MockWrite("POST /upload HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Connection: keep-alive\r\n"
+              "Content-Length: 16\r\n\r\n"),
+    MockWrite(false, temp_file_contents.c_str()),
+
+    MockWrite("POST /upload HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Connection: keep-alive\r\n"
+              "Content-Length: 16\r\n"
+              "Authorization: Basic Zm9vOmJhcg==\r\n\r\n"),
+    MockWrite(false, unreadable_contents.c_str(), temp_file_contents.length()),
+    MockWrite(false, OK),
+  };
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes,
+                                arraysize(data_writes));
+  session_deps.socket_factory.AddSocketDataProvider(&data);
+
+  TestCompletionCallback callback1;
+
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  rv = callback1.WaitForResult();
+  EXPECT_EQ(OK, rv);
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response != NULL);
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 401 Unauthorized", response->headers->GetStatusLine());
+
+  // The password prompt info should have been set in response->auth_challenge.
+  EXPECT_TRUE(response->auth_challenge.get() != NULL);
+  EXPECT_EQ(L"www.google.com:80", response->auth_challenge->host_and_port);
+  EXPECT_EQ(L"MyRealm1", response->auth_challenge->realm);
+  EXPECT_EQ(L"basic", response->auth_challenge->scheme);
+
+  // Now make the file unreadable and try again.
+  ASSERT_TRUE(file_util::MakeFileUnreadable(temp_file));
+
+  TestCompletionCallback callback2;
+
+  rv = trans->RestartWithAuth(L"foo", L"bar", &callback2);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  rv = callback2.WaitForResult();
+  EXPECT_EQ(OK, rv);
+
+  response = trans->GetResponseInfo();
+  EXPECT_TRUE(response != NULL);
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_TRUE(response->auth_challenge.get() == NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+  file_util::Delete(temp_file, false);
+}
+
+// Tests that changes to Auth realms are treated like auth rejections.
+TEST_F(HttpNetworkTransactionTest, ChangeAuthRealms) {
+  SessionDependencies session_deps;
+  scoped_ptr<HttpTransaction> trans(
+      new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  request.load_flags = 0;
+
+  // First transaction will request a resource and receive a Basic challenge
+  // with realm="first_realm".
+  MockWrite data_writes1[] = {
+    MockWrite("GET / HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Connection: keep-alive\r\n"
+              "\r\n"),
+  };
+  MockRead data_reads1[] = {
+    MockRead("HTTP/1.1 401 Unauthorized\r\n"
+             "WWW-Authenticate: Basic realm=\"first_realm\"\r\n"
+             "\r\n"),
+  };
+
+  // After calling trans->RestartWithAuth(), provide an Authentication header
+  // for first_realm. The server will reject and provide a challenge with
+  // second_realm.
+  MockWrite data_writes2[] = {
+    MockWrite("GET / HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Connection: keep-alive\r\n"
+              "Authorization: Basic Zmlyc3Q6YmF6\r\n"
+              "\r\n"),
+  };
+  MockRead data_reads2[] = {
+    MockRead("HTTP/1.1 401 Unauthorized\r\n"
+             "WWW-Authenticate: Basic realm=\"second_realm\"\r\n"
+             "\r\n"),
+  };
+
+  // This again fails, and goes back to first_realm. Make sure that the
+  // entry is removed from cache.
+  MockWrite data_writes3[] = {
+    MockWrite("GET / HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Connection: keep-alive\r\n"
+              "Authorization: Basic c2Vjb25kOmZvdQ==\r\n"
+              "\r\n"),
+  };
+  MockRead data_reads3[] = {
+    MockRead("HTTP/1.1 401 Unauthorized\r\n"
+             "WWW-Authenticate: Basic realm=\"first_realm\"\r\n"
+             "\r\n"),
+  };
+
+  // Try one last time (with the correct password) and get the resource.
+  MockWrite data_writes4[] = {
+    MockWrite("GET / HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Connection: keep-alive\r\n"
+              "Authorization: Basic Zmlyc3Q6YmFy\r\n"
+              "\r\n"),
+  };
+  MockRead data_reads4[] = {
+    MockRead("HTTP/1.1 200 OK\r\n"
+             "Content-Type: text/html; charset=iso-8859-1\r\n"
+             "Content-Length: 100\r\n"
+             "\r\n"),
+  };
+
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
+  StaticSocketDataProvider data3(data_reads3, arraysize(data_reads3),
+                                 data_writes3, arraysize(data_writes3));
+  StaticSocketDataProvider data4(data_reads4, arraysize(data_reads4),
+                                 data_writes4, arraysize(data_writes4));
+  session_deps.socket_factory.AddSocketDataProvider(&data1);
+  session_deps.socket_factory.AddSocketDataProvider(&data2);
+  session_deps.socket_factory.AddSocketDataProvider(&data3);
+  session_deps.socket_factory.AddSocketDataProvider(&data4);
+
+  TestCompletionCallback callback1;
+
+  // Issue the first request with Authorize headers. There should be a
+  // password prompt for first_realm waiting to be filled in after the
+  // transaction completes.
+  int rv = trans->Start(&request, &callback1, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  rv = callback1.WaitForResult();
+  EXPECT_EQ(OK, rv);
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_FALSE(response == NULL);
+  ASSERT_FALSE(response->auth_challenge.get() == NULL);
+  EXPECT_EQ(L"www.google.com:80", response->auth_challenge->host_and_port);
+  EXPECT_EQ(L"first_realm", response->auth_challenge->realm);
+  EXPECT_EQ(L"basic", response->auth_challenge->scheme);
+
+  // Issue the second request with an incorrect password. There should be a
+  // password prompt for second_realm waiting to be filled in after the
+  // transaction completes.
+  TestCompletionCallback callback2;
+  rv = trans->RestartWithAuth(L"first", L"baz", &callback2);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  rv = callback2.WaitForResult();
+  EXPECT_EQ(OK, rv);
+  response = trans->GetResponseInfo();
+  ASSERT_FALSE(response == NULL);
+  ASSERT_FALSE(response->auth_challenge.get() == NULL);
+  EXPECT_EQ(L"www.google.com:80", response->auth_challenge->host_and_port);
+  EXPECT_EQ(L"second_realm", response->auth_challenge->realm);
+  EXPECT_EQ(L"basic", response->auth_challenge->scheme);
+
+  // Issue the third request with another incorrect password. There should be
+  // a password prompt for first_realm waiting to be filled in. If the password
+  // prompt is not present, it indicates that the HttpAuthCacheEntry for
+  // first_realm was not correctly removed.
+  TestCompletionCallback callback3;
+  rv = trans->RestartWithAuth(L"second", L"fou", &callback3);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  rv = callback3.WaitForResult();
+  EXPECT_EQ(OK, rv);
+  response = trans->GetResponseInfo();
+  ASSERT_FALSE(response == NULL);
+  ASSERT_FALSE(response->auth_challenge.get() == NULL);
+  EXPECT_EQ(L"www.google.com:80", response->auth_challenge->host_and_port);
+  EXPECT_EQ(L"first_realm", response->auth_challenge->realm);
+  EXPECT_EQ(L"basic", response->auth_challenge->scheme);
+
+  // Issue the fourth request with the correct password and username.
+  TestCompletionCallback callback4;
+  rv = trans->RestartWithAuth(L"first", L"bar", &callback4);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  rv = callback4.WaitForResult();
+  EXPECT_EQ(OK, rv);
+  response = trans->GetResponseInfo();
+  ASSERT_FALSE(response == NULL);
+  EXPECT_TRUE(response->auth_challenge.get() == NULL);
+}
+
+TEST_F(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) {
+  HttpNetworkTransaction::SetNextProtos("needs_to_be_set_for_this_test");
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+
+  SessionDependencies session_deps;
+
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.1 200 OK\r\n"),
+    MockRead("Alternate-Protocol: 443:npn-spdy/1\r\n\r\n"),
+    MockRead("hello world"),
+    MockRead(false, OK),
+  };
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  request.load_flags = 0;
+
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
+
+  session_deps.socket_factory.AddSocketDataProvider(&data);
+
+  TestCompletionCallback callback;
+
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+  scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  HostPortPair http_host_port_pair;
+  http_host_port_pair.host = "www.google.com";
+  http_host_port_pair.port = 80;
+  const HttpAlternateProtocols& alternate_protocols =
+      session->alternate_protocols();
+  EXPECT_FALSE(
+      alternate_protocols.HasAlternateProtocolFor(http_host_port_pair));
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+  EXPECT_FALSE(response->was_fetched_via_spdy);
+  EXPECT_FALSE(response->was_npn_negotiated);
+  EXPECT_FALSE(response->was_alternate_protocol_available);
+
+  std::string response_data;
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("hello world", response_data);
+
+  ASSERT_TRUE(alternate_protocols.HasAlternateProtocolFor(http_host_port_pair));
+  const HttpAlternateProtocols::PortProtocolPair alternate =
+      alternate_protocols.GetAlternateProtocolFor(http_host_port_pair);
+  HttpAlternateProtocols::PortProtocolPair expected_alternate;
+  expected_alternate.port = 443;
+  expected_alternate.protocol = HttpAlternateProtocols::NPN_SPDY_1;
+  EXPECT_TRUE(expected_alternate.Equals(alternate));
+
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
+  HttpNetworkTransaction::SetNextProtos("");
+}
+
+TEST_F(HttpNetworkTransactionTest, MarkBrokenAlternateProtocol) {
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+  SessionDependencies session_deps;
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  request.load_flags = 0;
+
+  MockConnect mock_connect(true, ERR_CONNECTION_REFUSED);
+  StaticSocketDataProvider first_data;
+  first_data.set_connect_data(mock_connect);
+  session_deps.socket_factory.AddSocketDataProvider(&first_data);
+
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+    MockRead("hello world"),
+    MockRead(true, OK),
+  };
+  StaticSocketDataProvider second_data(
+      data_reads, arraysize(data_reads), NULL, 0);
+  session_deps.socket_factory.AddSocketDataProvider(&second_data);
+
+  // TODO(willchan): Delete this extra data provider.  It's necessary due to a
+  // ClientSocketPoolBaseHelper bug that starts up too many ConnectJobs:
+  // http://crbug.com/37454.
+  session_deps.socket_factory.AddSocketDataProvider(&second_data);
+
+  TestCompletionCallback callback;
+
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+
+  HostPortPair http_host_port_pair;
+  http_host_port_pair.host = "www.google.com";
+  http_host_port_pair.port = 80;
+  HttpAlternateProtocols* alternate_protocols =
+      session->mutable_alternate_protocols();
+  alternate_protocols->SetAlternateProtocolFor(
+      http_host_port_pair, 1234 /* port is ignored by MockConnect anyway */,
+      HttpAlternateProtocols::NPN_SPDY_1);
+
+  scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+  std::string response_data;
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("hello world", response_data);
+
+  ASSERT_TRUE(
+      alternate_protocols->HasAlternateProtocolFor(http_host_port_pair));
+  const HttpAlternateProtocols::PortProtocolPair alternate =
+      alternate_protocols->GetAlternateProtocolFor(http_host_port_pair);
+  EXPECT_EQ(HttpAlternateProtocols::BROKEN, alternate.protocol);
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
+}
+
+// TODO(willchan): Redo this test to use TLS/NPN=>SPDY.  Currently, the code
+// says that it does SPDY, but it just does the TLS handshake, but the NPN
+// response does not indicate SPDY, so we just do standard HTTPS over the port.
+// We should add code such that we don't fallback to HTTPS, but fallback to HTTP
+// on the original port.
+//  TEST_F(HttpNetworkTransactionTest, UseAlternateProtocol) {
+//    SessionDependencies session_deps;
+//
+//    HttpRequestInfo request;
+//    request.method = "GET";
+//    request.url = GURL("http://www.google.com/");
+//    request.load_flags = 0;
+//
+//    MockRead data_reads[] = {
+//      MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+//      MockRead("hello world"),
+//      MockRead(true, OK),
+//    };
+//    StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0);
+//    session_deps.socket_factory.AddSocketDataProvider(&data);
+//
+//    SSLSocketDataProvider ssl(true, OK);
+//    session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+//
+//    TestCompletionCallback callback;
+//
+//    scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+//
+//    HostPortPair http_host_port_pair;
+//    http_host_port_pair.host = "www.google.com";
+//    http_host_port_pair.port = 80;
+//    HttpAlternateProtocols* alternate_protocols =
+//        session->mutable_alternate_protocols();
+//    alternate_protocols->SetAlternateProtocolFor(
+//        http_host_port_pair, 1234 /* port is ignored */,
+//        HttpAlternateProtocols::NPN_SPDY_1);
+//
+//    scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+//
+//    int rv = trans->Start(&request, &callback, BoundNetLog());
+//    EXPECT_EQ(ERR_IO_PENDING, rv);
+//    EXPECT_EQ(OK, callback.WaitForResult());
+//
+//    const HttpResponseInfo* response = trans->GetResponseInfo();
+//    ASSERT_TRUE(response != NULL);
+//    ASSERT_TRUE(response->headers != NULL);
+//    EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+//
+//    std::string response_data;
+//    ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+//    EXPECT_EQ("hello world", response_data);
+//  }
+
+TEST_F(HttpNetworkTransactionTest, FailNpnSpdyAndFallback) {
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+  HttpNetworkTransaction::SetNextProtos(
+      "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy");
+  SessionDependencies session_deps;
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  request.load_flags = 0;
+
+  StaticSocketDataProvider first_tcp_connect;
+  session_deps.socket_factory.AddSocketDataProvider(&first_tcp_connect);
+
+  SSLSocketDataProvider ssl(true, OK);
+  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+    MockRead("hello world"),
+    MockRead(true, OK),
+  };
+  StaticSocketDataProvider fallback_data(
+      data_reads, arraysize(data_reads), NULL, 0);
+  session_deps.socket_factory.AddSocketDataProvider(&fallback_data);
+
+  TestCompletionCallback callback;
+
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+
+  HostPortPair http_host_port_pair;
+  http_host_port_pair.host = "www.google.com";
+  http_host_port_pair.port = 80;
+  HttpAlternateProtocols* alternate_protocols =
+      session->mutable_alternate_protocols();
+  alternate_protocols->SetAlternateProtocolFor(
+      http_host_port_pair, 1234 /* port is ignored */,
+      HttpAlternateProtocols::NPN_SPDY_1);
+
+  scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+  std::string response_data;
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("hello world", response_data);
+  HttpNetworkTransaction::SetNextProtos("");
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
+}
+
+TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) {
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+  HttpNetworkTransaction::SetNextProtos(
+      "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy");
+  SessionDependencies session_deps;
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  request.load_flags = 0;
+
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.1 200 OK\r\n"),
+    MockRead("Alternate-Protocol: 443:npn-spdy/1\r\n\r\n"),
+    MockRead("hello world"),
+    MockRead(true, OK),
+  };
+
+  StaticSocketDataProvider first_transaction(
+      data_reads, arraysize(data_reads), NULL, 0);
+  session_deps.socket_factory.AddSocketDataProvider(&first_transaction);
+
+  SSLSocketDataProvider ssl(true, OK);
+  ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+  ssl.next_proto = "spdy/1";
+  ssl.was_npn_negotiated = true;
+  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite spdy_writes[] = { CreateMockWrite(*req) };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> data(ConstructSpdyBodyFrame(1, true));
+  MockRead spdy_reads[] = {
+    CreateMockRead(*resp),
+    CreateMockRead(*data),
+    MockRead(true, 0, 0),
+  };
+
+  scoped_refptr<DelayedSocketData> spdy_data(
+      new DelayedSocketData(
+          1,  // wait for one write to finish before reading.
+          spdy_reads, arraysize(spdy_reads),
+          spdy_writes, arraysize(spdy_writes)));
+  session_deps.socket_factory.AddSocketDataProvider(spdy_data);
+
+  TestCompletionCallback callback;
+
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+  scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session));
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+  std::string response_data;
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("hello world", response_data);
+
+  trans.reset(new HttpNetworkTransaction(session));
+
+  rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  EXPECT_TRUE(response->was_npn_negotiated);
+  EXPECT_TRUE(response->was_alternate_protocol_available);
+
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("hello!", response_data);
+
+  HttpNetworkTransaction::SetNextProtos("");
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
+}
+
+class CapturingProxyResolver : public ProxyResolver {
+ public:
+  CapturingProxyResolver() : ProxyResolver(false /* expects_pac_bytes */) {}
+  virtual ~CapturingProxyResolver() {}
+
+  virtual int GetProxyForURL(const GURL& url,
+                             ProxyInfo* results,
+                             CompletionCallback* callback,
+                             RequestHandle* request,
+                             const BoundNetLog& net_log) {
+    ProxyServer proxy_server(
+        ProxyServer::SCHEME_HTTP, "myproxy", 80);
+    results->UseProxyServer(proxy_server);
+    resolved_.push_back(url);
+    return OK;
+  }
+
+  virtual void CancelRequest(RequestHandle request) {
+    NOTREACHED();
+  }
+
+  virtual int SetPacScript(const scoped_refptr<ProxyResolverScriptData>&,
+                           CompletionCallback* /*callback*/) {
+    return OK;
+  }
+
+  const std::vector<GURL>& resolved() const { return resolved_; }
+
+ private:
+  std::vector<GURL> resolved_;
+
+  DISALLOW_COPY_AND_ASSIGN(CapturingProxyResolver);
+};
+
+TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) {
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+  HttpNetworkTransaction::SetNextProtos(
+          "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy");
+
+  ProxyConfig proxy_config;
+  proxy_config.set_auto_detect(true);
+  proxy_config.set_pac_url(GURL("http://fooproxyurl"));
+
+  CapturingProxyResolver* capturing_proxy_resolver =
+      new CapturingProxyResolver();
+  SessionDependencies session_deps(new ProxyService(
+      new ProxyConfigServiceFixed(proxy_config), capturing_proxy_resolver,
+      NULL));
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  request.load_flags = 0;
+
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.1 200 OK\r\n"),
+    MockRead("Alternate-Protocol: 443:npn-spdy/1\r\n\r\n"),
+    MockRead("hello world"),
+    MockRead(true, OK),
+  };
+
+  StaticSocketDataProvider first_transaction(
+      data_reads, arraysize(data_reads), NULL, 0);
+  session_deps.socket_factory.AddSocketDataProvider(&first_transaction);
+
+  SSLSocketDataProvider ssl(true, OK);
+  ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+  ssl.next_proto = "spdy/1";
+  ssl.was_npn_negotiated = true;
+  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite spdy_writes[] = {
+    MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Proxy-Connection: keep-alive\r\n\r\n"),  // 0
+    CreateMockWrite(*req)  // 3
+  };
+
+  const char kCONNECTResponse[] = "HTTP/1.1 200 Connected\r\n\r\n";
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> data(ConstructSpdyBodyFrame(1, true));
+  MockRead spdy_reads[] = {
+    MockRead(true, kCONNECTResponse, arraysize(kCONNECTResponse) - 1, 1),  // 1
+    CreateMockRead(*resp.get(), 4),  // 2, 4
+    CreateMockRead(*data.get(), 4),  // 5
+    MockRead(true, 0, 0, 4),  // 6
+  };
+
+  scoped_refptr<OrderedSocketData> spdy_data(
+      new OrderedSocketData(
+          spdy_reads, arraysize(spdy_reads),
+          spdy_writes, arraysize(spdy_writes)));
+  session_deps.socket_factory.AddSocketDataProvider(spdy_data);
+
+  TestCompletionCallback callback;
+
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+  scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session));
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+  EXPECT_FALSE(response->was_fetched_via_spdy);
+  EXPECT_FALSE(response->was_npn_negotiated);
+
+  std::string response_data;
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("hello world", response_data);
+
+  trans.reset(new HttpNetworkTransaction(session));
+
+  rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  EXPECT_TRUE(response->was_npn_negotiated);
+
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("hello!", response_data);
+  ASSERT_EQ(2u, capturing_proxy_resolver->resolved().size());
+  EXPECT_EQ("http://www.google.com/",
+            capturing_proxy_resolver->resolved()[0].spec());
+  EXPECT_EQ("https://www.google.com/",
+            capturing_proxy_resolver->resolved()[1].spec());
+
+  HttpNetworkTransaction::SetNextProtos("");
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
+}
+
+TEST_F(HttpNetworkTransactionTest,
+       UseAlternateProtocolForNpnSpdyWithExistingSpdySession) {
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+  HttpNetworkTransaction::SetNextProtos(
+      "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy");
+  SessionDependencies session_deps;
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  request.load_flags = 0;
+
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.1 200 OK\r\n"),
+    MockRead("Alternate-Protocol: 443:npn-spdy/1\r\n\r\n"),
+    MockRead("hello world"),
+    MockRead(true, OK),
+  };
+
+  StaticSocketDataProvider first_transaction(
+      data_reads, arraysize(data_reads), NULL, 0);
+  session_deps.socket_factory.AddSocketDataProvider(&first_transaction);
+
+  SSLSocketDataProvider ssl(true, OK);
+  ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+  ssl.next_proto = "spdy/1";
+  ssl.was_npn_negotiated = true;
+  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+  // Make sure we use ssl for spdy here.
+  SpdySession::SetSSLMode(true);
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite spdy_writes[] = { CreateMockWrite(*req) };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> data(ConstructSpdyBodyFrame(1, true));
+  MockRead spdy_reads[] = {
+    CreateMockRead(*resp),
+    CreateMockRead(*data),
+    MockRead(true, 0, 0),
+  };
+
+  scoped_refptr<DelayedSocketData> spdy_data(
+      new DelayedSocketData(
+          1,  // wait for one write to finish before reading.
+          spdy_reads, arraysize(spdy_reads),
+          spdy_writes, arraysize(spdy_writes)));
+  session_deps.socket_factory.AddSocketDataProvider(spdy_data);
+
+  TestCompletionCallback callback;
+
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+
+  scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session));
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+  std::string response_data;
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("hello world", response_data);
+
+  // Set up an initial SpdySession in the pool to reuse.
+  scoped_refptr<SpdySession> spdy_session =
+      session->spdy_session_pool()->Get(HostPortPair("www.google.com", 443),
+                                        session, BoundNetLog());
+  scoped_refptr<TCPSocketParams> tcp_params =
+      new TCPSocketParams("www.google.com", 443, MEDIUM, GURL(), false);
+  spdy_session->Connect("www.google.com:443", tcp_params, MEDIUM);
+  trans.reset(new HttpNetworkTransaction(session));
+
+  rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  EXPECT_TRUE(response->was_npn_negotiated);
+  EXPECT_TRUE(response->was_alternate_protocol_available);
+
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("hello!", response_data);
+
+  HttpNetworkTransaction::SetNextProtos("");
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
+}
+
+// GenerateAuthToken is a mighty big test.
+// It tests all permutation of GenerateAuthToken behavior:
+//   - Synchronous and Asynchronous completion.
+//   - OK or error on completion.
+//   - Direct connection, non-authenticating proxy, and authenticating proxy.
+//   - HTTP or HTTPS backend (to include proxy tunneling).
+//   - Non-authenticating and authenticating backend.
+//
+// In all, there are 44 reasonable permuations (for example, if there are
+// problems generating an auth token for an authenticating proxy, we don't
+// need to test all permutations of the backend server).
+//
+// The test proceeds by going over each of the configuration cases, and
+// potentially running up to three rounds in each of the tests. The TestConfig
+// specifies both the configuration for the test as well as the expectations
+// for the results.
+TEST_F(HttpNetworkTransactionTest, GenerateAuthToken) {
+  const char* kServer = "http://www.example.com";
+  const char* kSecureServer = "https://www.example.com";
+  const char* kProxy = "myproxy:70";
+  const int kAuthErr = ERR_INVALID_AUTH_CREDENTIALS;
+
+  enum AuthTiming {
+    AUTH_NONE,
+    AUTH_SYNC,
+    AUTH_ASYNC,
+  };
+
+  const MockWrite kGet(
+      "GET / HTTP/1.1\r\n"
+      "Host: www.example.com\r\n"
+      "Connection: keep-alive\r\n\r\n");
+  const MockWrite kGetProxy(
+      "GET http://www.example.com/ HTTP/1.1\r\n"
+      "Host: www.example.com\r\n"
+      "Proxy-Connection: keep-alive\r\n\r\n");
+  const MockWrite kGetAuth(
+      "GET / HTTP/1.1\r\n"
+      "Host: www.example.com\r\n"
+      "Connection: keep-alive\r\n"
+      "Authorization: auth_token\r\n\r\n");
+  const MockWrite kGetProxyAuth(
+      "GET http://www.example.com/ HTTP/1.1\r\n"
+      "Host: www.example.com\r\n"
+      "Proxy-Connection: keep-alive\r\n"
+      "Proxy-Authorization: auth_token\r\n\r\n");
+  const MockWrite kGetAuthThroughProxy(
+      "GET http://www.example.com/ HTTP/1.1\r\n"
+      "Host: www.example.com\r\n"
+      "Proxy-Connection: keep-alive\r\n"
+      "Authorization: auth_token\r\n\r\n");
+  const MockWrite kGetAuthWithProxyAuth(
+      "GET http://www.example.com/ HTTP/1.1\r\n"
+      "Host: www.example.com\r\n"
+      "Proxy-Connection: keep-alive\r\n"
+      "Proxy-Authorization: auth_token\r\n"
+      "Authorization: auth_token\r\n\r\n");
+  const MockWrite kConnect(
+      "CONNECT www.example.com:443 HTTP/1.1\r\n"
+      "Host: www.example.com\r\n"
+      "Proxy-Connection: keep-alive\r\n\r\n");
+  const MockWrite kConnectProxyAuth(
+      "CONNECT www.example.com:443 HTTP/1.1\r\n"
+      "Host: www.example.com\r\n"
+      "Proxy-Connection: keep-alive\r\n"
+      "Proxy-Authorization: auth_token\r\n\r\n");
+
+  const MockRead kSuccess(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Type: text/html; charset=iso-8859-1\r\n"
+      "Content-Length: 3\r\n\r\n"
+      "Yes");
+  const MockRead kFailure(
+      "Should not be called.");
+  const MockRead kServerChallenge(
+      "HTTP/1.1 401 Unauthorized\r\n"
+      "WWW-Authenticate: Mock realm=server\r\n"
+      "Content-Type: text/html; charset=iso-8859-1\r\n"
+      "Content-Length: 14\r\n\r\n"
+      "Unauthorized\r\n");
+  const MockRead kProxyChallenge(
+      "HTTP/1.1 407 Unauthorized\r\n"
+      "Proxy-Authenticate: Mock realm=proxy\r\n"
+      "Proxy-Connection: close\r\n"
+      "Content-Type: text/html; charset=iso-8859-1\r\n"
+      "Content-Length: 14\r\n\r\n"
+      "Unauthorized\r\n");
+  const MockRead kProxyConnected(
+      "HTTP/1.1 200 Connection Established\r\n\r\n");
+
+  // NOTE(cbentzel): I wanted TestReadWriteRound to be a simple struct with
+  // no constructors, but the C++ compiler on Windows warns about
+  // unspecified data in compound literals. So, moved to using constructors,
+  // and TestRound's created with the default constructor should not be used.
+  struct TestRound {
+    TestRound()
+        : expected_rv(ERR_UNEXPECTED),
+          extra_write(NULL),
+          extra_read(NULL) {
+    }
+    TestRound(const MockWrite& write_arg, const MockRead& read_arg,
+              int expected_rv_arg)
+        : write(write_arg),
+          read(read_arg),
+          expected_rv(expected_rv_arg),
+          extra_write(NULL),
+          extra_read(NULL) {
+    }
+    TestRound(const MockWrite& write_arg, const MockRead& read_arg,
+              int expected_rv_arg, const MockWrite* extra_write_arg,
+              const MockWrite* extra_read_arg)
+        : write(write_arg),
+          read(read_arg),
+          expected_rv(expected_rv_arg),
+          extra_write(extra_write_arg),
+          extra_read(extra_read_arg) {
+    }
+    MockWrite write;
+    MockRead read;
+    int expected_rv;
+    const MockWrite* extra_write;
+    const MockRead* extra_read;
+  };
+
+  static const int kNoSSL = 500;
+
+  struct TestConfig {
+    const char* proxy_url;
+    AuthTiming proxy_auth_timing;
+    int proxy_auth_rv;
+    const char* server_url;
+    AuthTiming server_auth_timing;
+    int server_auth_rv;
+    int num_auth_rounds;
+    int first_ssl_round;
+    TestRound rounds[3];
+  } test_configs[] = {
+    // Non-authenticating HTTP server with a direct connection.
+    { NULL, AUTH_NONE, OK, kServer, AUTH_NONE, OK, 1, kNoSSL,
+      { TestRound(kGet, kSuccess, OK)}},
+    // Authenticating HTTP server with a direct connection.
+    { NULL, AUTH_NONE, OK, kServer, AUTH_SYNC, OK, 2, kNoSSL,
+      { TestRound(kGet, kServerChallenge, OK),
+        TestRound(kGetAuth, kSuccess, OK)}},
+    { NULL, AUTH_NONE, OK, kServer, AUTH_SYNC, kAuthErr, 2, kNoSSL,
+      { TestRound(kGet, kServerChallenge, OK),
+        TestRound(kGetAuth, kFailure, kAuthErr)}},
+    { NULL, AUTH_NONE, OK, kServer, AUTH_ASYNC, OK, 2, kNoSSL,
+      { TestRound(kGet, kServerChallenge, OK),
+        TestRound(kGetAuth, kSuccess, OK)}},
+    { NULL, AUTH_NONE, OK, kServer, AUTH_ASYNC, kAuthErr, 2, kNoSSL,
+      { TestRound(kGet, kServerChallenge, OK),
+        TestRound(kGetAuth, kFailure, kAuthErr)}},
+    // Non-authenticating HTTP server through a non-authenticating proxy.
+    { kProxy, AUTH_NONE, OK, kServer, AUTH_NONE, OK, 1, kNoSSL,
+      { TestRound(kGetProxy, kSuccess, OK)}},
+    // Authenticating HTTP server through a non-authenticating proxy.
+    { kProxy, AUTH_NONE, OK, kServer, AUTH_SYNC, OK, 2, kNoSSL,
+      { TestRound(kGetProxy, kServerChallenge, OK),
+        TestRound(kGetAuthThroughProxy, kSuccess, OK)}},
+    { kProxy, AUTH_NONE, OK, kServer, AUTH_SYNC, kAuthErr, 2, kNoSSL,
+      { TestRound(kGetProxy, kServerChallenge, OK),
+        TestRound(kGetAuthThroughProxy, kFailure, kAuthErr)}},
+    { kProxy, AUTH_NONE, OK, kServer, AUTH_ASYNC, OK, 2, kNoSSL,
+      { TestRound(kGetProxy, kServerChallenge, OK),
+        TestRound(kGetAuthThroughProxy, kSuccess, OK)}},
+    { kProxy, AUTH_NONE, OK, kServer, AUTH_ASYNC, kAuthErr, 2, kNoSSL,
+      { TestRound(kGetProxy, kServerChallenge, OK),
+        TestRound(kGetAuthThroughProxy, kFailure, kAuthErr)}},
+    // Non-authenticating HTTP server through an authenticating proxy.
+    { kProxy, AUTH_SYNC, OK, kServer, AUTH_NONE, OK, 2, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kSuccess, OK)}},
+    { kProxy, AUTH_SYNC, kAuthErr, kServer, AUTH_NONE, OK, 2, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kFailure, kAuthErr)}},
+    { kProxy, AUTH_ASYNC, OK, kServer, AUTH_NONE, OK, 2, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kSuccess, OK)}},
+    { kProxy, AUTH_ASYNC, kAuthErr, kServer, AUTH_NONE, OK, 2, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kFailure, kAuthErr)}},
+    // Authenticating HTTP server through an authenticating proxy.
+    { kProxy, AUTH_SYNC, OK, kServer, AUTH_SYNC, OK, 3, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kServerChallenge, OK),
+        TestRound(kGetAuthWithProxyAuth, kSuccess, OK)}},
+    { kProxy, AUTH_SYNC, OK, kServer, AUTH_SYNC, kAuthErr, 3, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kServerChallenge, OK),
+        TestRound(kGetAuthWithProxyAuth, kFailure, kAuthErr)}},
+    { kProxy, AUTH_ASYNC, OK, kServer, AUTH_SYNC, OK, 3, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kServerChallenge, OK),
+        TestRound(kGetAuthWithProxyAuth, kSuccess, OK)}},
+    { kProxy, AUTH_ASYNC, OK, kServer, AUTH_SYNC, kAuthErr, 3, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kServerChallenge, OK),
+        TestRound(kGetAuthWithProxyAuth, kFailure, kAuthErr)}},
+    { kProxy, AUTH_SYNC, OK, kServer, AUTH_ASYNC, OK, 3, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kServerChallenge, OK),
+        TestRound(kGetAuthWithProxyAuth, kSuccess, OK)}},
+    { kProxy, AUTH_SYNC, OK, kServer, AUTH_ASYNC, kAuthErr, 3, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kServerChallenge, OK),
+        TestRound(kGetAuthWithProxyAuth, kFailure, kAuthErr)}},
+    { kProxy, AUTH_ASYNC, OK, kServer, AUTH_ASYNC, OK, 3, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kServerChallenge, OK),
+        TestRound(kGetAuthWithProxyAuth, kSuccess, OK)}},
+    { kProxy, AUTH_ASYNC, OK, kServer, AUTH_ASYNC, kAuthErr, 3, kNoSSL,
+      { TestRound(kGetProxy, kProxyChallenge, OK),
+        TestRound(kGetProxyAuth, kServerChallenge, OK),
+        TestRound(kGetAuthWithProxyAuth, kFailure, kAuthErr)}},
+    // Non-authenticating HTTPS server with a direct connection.
+    { NULL, AUTH_NONE, OK, kSecureServer, AUTH_NONE, OK, 1, 0,
+      { TestRound(kGet, kSuccess, OK)}},
+    // Authenticating HTTPS server with a direct connection.
+    { NULL, AUTH_NONE, OK, kSecureServer, AUTH_SYNC, OK, 2, 0,
+      { TestRound(kGet, kServerChallenge, OK),
+        TestRound(kGetAuth, kSuccess, OK)}},
+    { NULL, AUTH_NONE, OK, kSecureServer, AUTH_SYNC, kAuthErr, 2, 0,
+      { TestRound(kGet, kServerChallenge, OK),
+        TestRound(kGetAuth, kFailure, kAuthErr)}},
+    { NULL, AUTH_NONE, OK, kSecureServer, AUTH_ASYNC, OK, 2, 0,
+      { TestRound(kGet, kServerChallenge, OK),
+        TestRound(kGetAuth, kSuccess, OK)}},
+    { NULL, AUTH_NONE, OK, kSecureServer, AUTH_ASYNC, kAuthErr, 2, 0,
+      { TestRound(kGet, kServerChallenge, OK),
+        TestRound(kGetAuth, kFailure, kAuthErr)}},
+    // Non-authenticating HTTPS server with a non-authenticating proxy.
+    { kProxy, AUTH_NONE, OK, kSecureServer, AUTH_NONE, OK, 1, 0,
+      { TestRound(kConnect, kProxyConnected, OK, &kGet, &kSuccess)}},
+    // Authenticating HTTPS server through a non-authenticating proxy.
+    { kProxy, AUTH_NONE, OK, kSecureServer, AUTH_SYNC, OK, 2, 0,
+      { TestRound(kConnect, kProxyConnected, OK, &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kSuccess, OK)}},
+    { kProxy, AUTH_NONE, OK, kSecureServer, AUTH_SYNC, kAuthErr, 2, 0,
+      { TestRound(kConnect, kProxyConnected, OK, &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kFailure, kAuthErr)}},
+    { kProxy, AUTH_NONE, OK, kSecureServer, AUTH_ASYNC, OK, 2, 0,
+      { TestRound(kConnect, kProxyConnected, OK, &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kSuccess, OK)}},
+    { kProxy, AUTH_NONE, OK, kSecureServer, AUTH_ASYNC, kAuthErr, 2, 0,
+      { TestRound(kConnect, kProxyConnected, OK, &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kFailure, kAuthErr)}},
+    // Non-Authenticating HTTPS server through an authenticating proxy.
+    { kProxy, AUTH_SYNC, OK, kSecureServer, AUTH_NONE, OK, 2, 1,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kProxyConnected, OK, &kGet, &kSuccess)}},
+    { kProxy, AUTH_SYNC, kAuthErr, kSecureServer, AUTH_NONE, OK, 2, kNoSSL,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kFailure, kAuthErr)}},
+    { kProxy, AUTH_ASYNC, OK, kSecureServer, AUTH_NONE, OK, 2, 1,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kProxyConnected, OK, &kGet, &kSuccess)}},
+    { kProxy, AUTH_ASYNC, kAuthErr, kSecureServer, AUTH_NONE, OK, 2, kNoSSL,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kFailure, kAuthErr)}},
+    // Authenticating HTTPS server through an authenticating proxy.
+    { kProxy, AUTH_SYNC, OK, kSecureServer, AUTH_SYNC, OK, 3, 1,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kProxyConnected, OK,
+                  &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kSuccess, OK)}},
+    { kProxy, AUTH_SYNC, OK, kSecureServer, AUTH_SYNC, kAuthErr, 3, 1,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kProxyConnected, OK,
+                  &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kFailure, kAuthErr)}},
+    { kProxy, AUTH_ASYNC, OK, kSecureServer, AUTH_SYNC, OK, 3, 1,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kProxyConnected, OK,
+                  &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kSuccess, OK)}},
+    { kProxy, AUTH_ASYNC, OK, kSecureServer, AUTH_SYNC, kAuthErr, 3, 1,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kProxyConnected, OK,
+                  &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kFailure, kAuthErr)}},
+    { kProxy, AUTH_SYNC, OK, kSecureServer, AUTH_ASYNC, OK, 3, 1,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kProxyConnected, OK,
+                  &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kSuccess, OK)}},
+    { kProxy, AUTH_SYNC, OK, kSecureServer, AUTH_ASYNC, kAuthErr, 3, 1,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kProxyConnected, OK,
+                  &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kFailure, kAuthErr)}},
+    { kProxy, AUTH_ASYNC, OK, kSecureServer, AUTH_ASYNC, OK, 3, 1,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kProxyConnected, OK,
+                  &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kSuccess, OK)}},
+    { kProxy, AUTH_ASYNC, OK, kSecureServer, AUTH_ASYNC, kAuthErr, 3, 1,
+      { TestRound(kConnect, kProxyChallenge, OK),
+        TestRound(kConnectProxyAuth, kProxyConnected, OK,
+                  &kGet, &kServerChallenge),
+        TestRound(kGetAuth, kFailure, kAuthErr)}},
+  };
+
+  SessionDependencies session_deps;
+  scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps);
+  HttpAuthHandlerMock::Factory* auth_factory(
+      new HttpAuthHandlerMock::Factory());
+  session_deps.http_auth_handler_factory.reset(auth_factory);
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_configs); ++i) {
+    const TestConfig& test_config = test_configs[i];
+
+    // Set up authentication handlers as necessary.
+    if (test_config.proxy_auth_timing != AUTH_NONE) {
+      HttpAuthHandlerMock* auth_handler(new HttpAuthHandlerMock());
+      std::string auth_challenge = "Mock realm=proxy";
+      GURL origin(test_config.proxy_url);
+      HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(),
+                                             auth_challenge.end());
+      auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_PROXY,
+                                      origin, BoundNetLog());
+      auth_handler->SetGenerateExpectation(
+          test_config.proxy_auth_timing == AUTH_ASYNC,
+          test_config.proxy_auth_rv);
+      auth_factory->set_mock_handler(auth_handler, HttpAuth::AUTH_PROXY);
+    }
+    if (test_config.server_auth_timing != AUTH_NONE) {
+      HttpAuthHandlerMock* auth_handler(new HttpAuthHandlerMock());
+      std::string auth_challenge = "Mock realm=server";
+      GURL origin(test_config.server_url);
+      HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(),
+                                             auth_challenge.end());
+      auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_SERVER,
+                                      origin, BoundNetLog());
+      auth_handler->SetGenerateExpectation(
+          test_config.server_auth_timing == AUTH_ASYNC,
+          test_config.server_auth_rv);
+      auth_factory->set_mock_handler(auth_handler, HttpAuth::AUTH_SERVER);
+    }
+    if (test_config.proxy_url) {
+      session_deps.proxy_service =
+          CreateFixedProxyService(test_config.proxy_url);
+    } else {
+      session_deps.proxy_service = ProxyService::CreateNull();
+    }
+
+    HttpRequestInfo request;
+    request.method = "GET";
+    request.url = GURL(test_config.server_url);
+    request.load_flags = 0;
+
+    scoped_ptr<HttpTransaction> trans(
+        new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+    for (int round = 0; round < test_config.num_auth_rounds; ++round) {
+      const TestRound& read_write_round = test_config.rounds[round];
+
+      // Set up expected reads and writes.
+      MockRead reads[2];
+      reads[0] = read_write_round.read;
+      size_t length_reads = 1;
+      if (read_write_round.extra_read) {
+        reads[1] = *read_write_round.extra_read;
+        length_reads = 2;
+      }
+
+      MockWrite writes[2];
+      writes[0] = read_write_round.write;
+      size_t length_writes = 1;
+      if (read_write_round.extra_write) {
+        writes[1] = *read_write_round.extra_write;
+        length_writes = 2;
+      }
+      StaticSocketDataProvider data_provider(
+          reads, length_reads, writes, length_writes);
+      session_deps.socket_factory.AddSocketDataProvider(&data_provider);
+
+      // Add an SSL sequence if necessary.
+      SSLSocketDataProvider ssl_socket_data_provider(false, OK);
+      if (round >= test_config.first_ssl_round)
+        session_deps.socket_factory.AddSSLSocketDataProvider(
+            &ssl_socket_data_provider);
+
+      // Start or restart the transaction.
+      TestCompletionCallback callback;
+      int rv;
+      if (round == 0) {
+        rv = trans->Start(&request, &callback, BoundNetLog());
+      } else {
+        rv = trans->RestartWithAuth(L"foo", L"bar", &callback);
+      }
+      if (rv == ERR_IO_PENDING)
+        rv = callback.WaitForResult();
+
+      // Compare results with expected data.
+      EXPECT_EQ(read_write_round.expected_rv, rv);
+      const HttpResponseInfo* response = trans->GetResponseInfo();
+      if (read_write_round.expected_rv == OK) {
+        EXPECT_FALSE(response == NULL);
+      } else {
+        EXPECT_TRUE(response == NULL);
+        EXPECT_EQ(round + 1, test_config.num_auth_rounds);
+        continue;
+      }
+      if (round + 1 < test_config.num_auth_rounds) {
+        EXPECT_FALSE(response->auth_challenge.get() == NULL);
+      } else {
+        EXPECT_TRUE(response->auth_challenge.get() == NULL);
+      }
+    }
+  }
+
+  // Flush the idle socket before the HttpNetworkTransaction goes out of scope.
+  session->FlushSocketPools();
+}
+
+TEST_F(HttpNetworkTransactionTest, MultiRoundAuth) {
+  // Do multi-round authentication and make sure it works correctly.
+  SessionDependencies session_deps;
+  HttpAuthHandlerMock::Factory* auth_factory(
+      new HttpAuthHandlerMock::Factory());
+  session_deps.http_auth_handler_factory.reset(auth_factory);
+  session_deps.proxy_service = ProxyService::CreateNull();
+  session_deps.host_resolver->rules()->AddRule("www.example.com", "10.0.0.1");
+  session_deps.host_resolver->set_synchronous_mode(true);
+
+  HttpAuthHandlerMock* auth_handler(new HttpAuthHandlerMock());
+  auth_handler->set_connection_based(true);
+  std::string auth_challenge = "Mock realm=server";
+  GURL origin("http://www.example.com");
+  HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(),
+                                         auth_challenge.end());
+  auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_SERVER,
+                                  origin, BoundNetLog());
+  auth_factory->set_mock_handler(auth_handler, HttpAuth::AUTH_SERVER);
+
+  scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps);
+  scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+  int rv = OK;
+  const HttpResponseInfo* response = NULL;
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = origin;
+  request.load_flags = 0;
+  TestCompletionCallback callback;
+
+  const MockWrite kGet(
+      "GET / HTTP/1.1\r\n"
+      "Host: www.example.com\r\n"
+      "Connection: keep-alive\r\n\r\n");
+  const MockWrite kGetAuth(
+      "GET / HTTP/1.1\r\n"
+      "Host: www.example.com\r\n"
+      "Connection: keep-alive\r\n"
+      "Authorization: auth_token\r\n\r\n");
+
+  const MockRead kServerChallenge(
+      "HTTP/1.1 401 Unauthorized\r\n"
+      "WWW-Authenticate: Mock realm=server\r\n"
+      "Content-Type: text/html; charset=iso-8859-1\r\n"
+      "Content-Length: 14\r\n\r\n"
+      "Unauthorized\r\n");
+  const MockRead kSuccess(
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Type: text/html; charset=iso-8859-1\r\n"
+      "Content-Length: 3\r\n\r\n"
+      "Yes");
+
+  MockWrite writes[] = {
+    // First round
+    kGet,
+    // Second round
+    kGetAuth,
+    // Third round
+    kGetAuth,
+  };
+  MockRead reads[] = {
+    // First round
+    kServerChallenge,
+    // Second round
+    kServerChallenge,
+    // Third round
+    kSuccess,
+  };
+  StaticSocketDataProvider data_provider(reads, arraysize(reads),
+                                         writes, arraysize(writes));
+  session_deps.socket_factory.AddSocketDataProvider(&data_provider);
+
+  // First round
+  auth_handler->SetGenerateExpectation(false, OK);
+  rv = trans->Start(&request, &callback, BoundNetLog());
+  if (rv == ERR_IO_PENDING)
+    rv = callback.WaitForResult();
+  EXPECT_EQ(OK, rv);
+  response = trans->GetResponseInfo();
+  ASSERT_FALSE(response == NULL);
+  EXPECT_FALSE(response->auth_challenge.get() == NULL);
+
+  // Second round
+  auth_handler->SetGenerateExpectation(false, OK);
+  rv = trans->RestartWithAuth(L"foo", L"bar", &callback);
+  if (rv == ERR_IO_PENDING)
+    rv = callback.WaitForResult();
+  EXPECT_EQ(OK, rv);
+  response = trans->GetResponseInfo();
+  ASSERT_FALSE(response == NULL);
+  EXPECT_TRUE(response->auth_challenge.get() == NULL);
+
+  // Third round
+  auth_handler->SetGenerateExpectation(false, OK);
+  rv = trans->RestartWithAuth(L"", L"", &callback);
+  if (rv == ERR_IO_PENDING)
+    rv = callback.WaitForResult();
+  EXPECT_EQ(OK, rv);
+  response = trans->GetResponseInfo();
+  ASSERT_FALSE(response == NULL);
+  EXPECT_TRUE(response->auth_challenge.get() == NULL);
+}
+
+class TLSDecompressionFailureSocketDataProvider : public SocketDataProvider {
+ public:
+  explicit TLSDecompressionFailureSocketDataProvider(bool fail_all)
+      : fail_all_(fail_all) {
+  }
+
+  virtual MockRead GetNextRead() {
+    if (fail_all_)
+      return MockRead(false /* async */, ERR_SSL_DECOMPRESSION_FAILURE_ALERT);
+
+    return MockRead(false /* async */,
+                    "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nok.\r\n");
+  }
+
+  virtual MockWriteResult OnWrite(const std::string& data) {
+    return MockWriteResult(false /* async */, data.size());
+  }
+
+  void Reset() {
+  }
+
+ private:
+  const bool fail_all_;
+};
+
+// Test that we restart a connection when we see a decompression failure from
+// the peer during the handshake. (In the real world we'll restart with SSLv3
+// and we won't offer DEFLATE in that case.)
+TEST_F(HttpNetworkTransactionTest, RestartAfterTLSDecompressionFailure) {
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://tlsdecompressionfailure.example.com/");
+  request.load_flags = 0;
+
+  SessionDependencies session_deps;
+  TLSDecompressionFailureSocketDataProvider socket_data_provider1(
+      false /* fail all reads */);
+  TLSDecompressionFailureSocketDataProvider socket_data_provider2(false);
+  SSLSocketDataProvider ssl_socket_data_provider1(
+      false, ERR_SSL_DECOMPRESSION_FAILURE_ALERT);
+  SSLSocketDataProvider ssl_socket_data_provider2(false, OK);
+  session_deps.socket_factory.AddSocketDataProvider(&socket_data_provider1);
+  session_deps.socket_factory.AddSocketDataProvider(&socket_data_provider2);
+  session_deps.socket_factory.AddSSLSocketDataProvider(
+      &ssl_socket_data_provider1);
+  session_deps.socket_factory.AddSSLSocketDataProvider(
+      &ssl_socket_data_provider2);
+
+  // Work around http://crbug.com/37454
+  StaticSocketDataProvider bug37454_connection;
+  bug37454_connection.set_connect_data(MockConnect(true, ERR_UNEXPECTED));
+  session_deps.socket_factory.AddSocketDataProvider(&bug37454_connection);
+
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+  scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+  TestCompletionCallback callback;
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+  std::string response_data;
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("ok.", response_data);
+}
+
+// Test that we restart a connection if we get a decompression failure from the
+// peer while reading the first bytes from the connection. This occurs when the
+// peer cannot handle DEFLATE but we're using False Start, so we don't notice
+// in the handshake.
+TEST_F(HttpNetworkTransactionTest,
+       RestartAfterTLSDecompressionFailureWithFalseStart) {
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://tlsdecompressionfailure2.example.com/");
+  request.load_flags = 0;
+
+  SessionDependencies session_deps;
+  TLSDecompressionFailureSocketDataProvider socket_data_provider1(
+      true /* fail all reads */);
+  TLSDecompressionFailureSocketDataProvider socket_data_provider2(false);
+  SSLSocketDataProvider ssl_socket_data_provider1(false, OK);
+  SSLSocketDataProvider ssl_socket_data_provider2(false, OK);
+  session_deps.socket_factory.AddSocketDataProvider(&socket_data_provider1);
+  session_deps.socket_factory.AddSocketDataProvider(&socket_data_provider2);
+  session_deps.socket_factory.AddSSLSocketDataProvider(
+      &ssl_socket_data_provider1);
+  session_deps.socket_factory.AddSSLSocketDataProvider(
+      &ssl_socket_data_provider2);
+
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+  scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+  TestCompletionCallback callback;
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+  std::string response_data;
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("ok.", response_data);
+}
+
+// This tests the case that a request is issued via http instead of spdy after
+// npn is negotiated.
+TEST_F(HttpNetworkTransactionTest, NpnWithHttpOverSSL) {
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+  HttpNetworkTransaction::SetNextProtos("\x08http/1.1\x07http1.1");
+  SessionDependencies session_deps;
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.google.com/");
+  request.load_flags = 0;
+
+  MockWrite data_writes[] = {
+    MockWrite("GET / HTTP/1.1\r\n"
+              "Host: www.google.com\r\n"
+              "Connection: keep-alive\r\n\r\n"),
+  };
+
+  MockRead data_reads[] = {
+    MockRead("HTTP/1.1 200 OK\r\n"),
+    MockRead("Alternate-Protocol: 443:npn-spdy/1\r\n\r\n"),
+    MockRead("hello world"),
+    MockRead(false, OK),
+  };
+
+  SSLSocketDataProvider ssl(true, OK);
+  ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+  ssl.next_proto = "http/1.1";
+
+  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
+  session_deps.socket_factory.AddSocketDataProvider(&data);
+
+  TestCompletionCallback callback;
+
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+  scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  ASSERT_TRUE(response != NULL);
+  ASSERT_TRUE(response->headers != NULL);
+  EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+
+  std::string response_data;
+  ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+  EXPECT_EQ("hello world", response_data);
+
+  EXPECT_FALSE(response->was_fetched_via_spdy);
+  EXPECT_TRUE(response->was_npn_negotiated);
+  EXPECT_FALSE(response->was_alternate_protocol_available);
+
+  HttpNetworkTransaction::SetNextProtos("");
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
+}
+
+TEST_F(HttpNetworkTransactionTest, SpdyPostNPNServerHangup) {
+  // Simulate the SSL handshake completing with an NPN negotiation
+  // followed by an immediate server closing of the socket.
+  // Fix crash:  http://crbug.com/46369
+  HttpNetworkTransaction::SetUseAlternateProtocols(true);
+  HttpNetworkTransaction::SetNextProtos(
+      "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy");
+  SessionDependencies session_deps;
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.google.com/");
+  request.load_flags = 0;
+
+  SSLSocketDataProvider ssl(true, OK);
+  ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+  ssl.next_proto = "spdy/1";
+  ssl.was_npn_negotiated = true;
+  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite spdy_writes[] = { CreateMockWrite(*req) };
+
+  MockRead spdy_reads[] = {
+    MockRead(false, 0, 0)   // Not async - return 0 immediately.
+  };
+
+  scoped_refptr<DelayedSocketData> spdy_data(
+      new DelayedSocketData(
+          0,  // don't wait in this case, immediate hangup.
+          spdy_reads, arraysize(spdy_reads),
+          spdy_writes, arraysize(spdy_writes)));
+  session_deps.socket_factory.AddSocketDataProvider(spdy_data);
+
+  TestCompletionCallback callback;
+
+  scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+  scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session));
+
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
+
+  HttpNetworkTransaction::SetNextProtos("");
+  HttpNetworkTransaction::SetUseAlternateProtocols(false);
 }
 
 }  // namespace net
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
new file mode 100644
index 0000000..ec4b753
--- /dev/null
+++ b/net/http/http_proxy_client_socket.cc
@@ -0,0 +1,420 @@
+// Copyright (c) 2010 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/http/http_proxy_client_socket.h"
+
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/http/http_basic_stream.h"
+#include "net/http/http_net_log_params.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_request_info.h"
+#include "net/socket/client_socket_handle.h"
+
+namespace net {
+
+namespace {
+
+// The HTTP CONNECT method for establishing a tunnel connection is documented
+// in draft-luotonen-web-proxy-tunneling-01.txt and RFC 2817, Sections 5.2 and
+// 5.3.
+void BuildTunnelRequest(const HttpRequestInfo* request_info,
+                        const HttpRequestHeaders& authorization_headers,
+                        const HostPortPair& endpoint,
+                        std::string* request_line,
+                        HttpRequestHeaders* request_headers) {
+  // RFC 2616 Section 9 says the Host request-header field MUST accompany all
+  // HTTP/1.1 requests.  Add "Proxy-Connection: keep-alive" for compat with
+  // HTTP/1.0 proxies such as Squid (required for NTLM authentication).
+  *request_line = StringPrintf(
+      "CONNECT %s HTTP/1.1\r\n", endpoint.ToString().c_str());
+  request_headers->SetHeader(HttpRequestHeaders::kHost,
+                             GetHostAndOptionalPort(request_info->url));
+  request_headers->SetHeader(HttpRequestHeaders::kProxyConnection,
+                             "keep-alive");
+
+  std::string user_agent;
+  if (request_info->extra_headers.GetHeader(HttpRequestHeaders::kUserAgent,
+                                            &user_agent))
+    request_headers->SetHeader(HttpRequestHeaders::kUserAgent, user_agent);
+
+  request_headers->MergeFrom(authorization_headers);
+}
+
+}  // namespace
+
+HttpProxyClientSocket::HttpProxyClientSocket(
+    ClientSocketHandle* transport_socket, const GURL& request_url,
+    const HostPortPair& endpoint, const scoped_refptr<HttpAuthController>& auth,
+    bool tunnel)
+    : ALLOW_THIS_IN_INITIALIZER_LIST(
+          io_callback_(this, &HttpProxyClientSocket::OnIOComplete)),
+      next_state_(STATE_NONE),
+      user_callback_(NULL),
+      transport_(transport_socket),
+      endpoint_(endpoint),
+      auth_(auth),
+      tunnel_(tunnel),
+      net_log_(transport_socket->socket()->NetLog()) {
+  DCHECK_EQ(tunnel, auth != NULL);
+  // Synthesize the bits of a request that we actually use.
+  request_.url = request_url;
+  request_.method = "GET";
+}
+
+HttpProxyClientSocket::~HttpProxyClientSocket() {
+  Disconnect();
+}
+
+int HttpProxyClientSocket::Connect(CompletionCallback* callback) {
+  DCHECK(transport_.get());
+  DCHECK(transport_->socket());
+  DCHECK(transport_->socket()->IsConnected());
+  DCHECK(!user_callback_);
+
+  if (!tunnel_)
+    next_state_ = STATE_DONE;
+  if (next_state_ == STATE_DONE)
+    return OK;
+
+  DCHECK_EQ(STATE_NONE, next_state_);
+  next_state_ = STATE_GENERATE_AUTH_TOKEN;
+
+  int rv = DoLoop(OK);
+  if (rv == ERR_IO_PENDING)
+    user_callback_ = callback;
+  return rv;
+}
+
+int HttpProxyClientSocket::RestartWithAuth(CompletionCallback* callback) {
+  DCHECK_EQ(STATE_NONE, next_state_);
+  DCHECK(!user_callback_);
+
+  int rv = PrepareForAuthRestart();
+  if (rv != OK)
+    return rv;
+
+  rv = DoLoop(OK);
+  if (rv == ERR_IO_PENDING)
+    user_callback_ = callback;
+  return rv;
+}
+
+int HttpProxyClientSocket::PrepareForAuthRestart() {
+  if (!response_.headers.get())
+    return ERR_CONNECTION_RESET;
+
+  bool keep_alive = false;
+  if (response_.headers->IsKeepAlive() &&
+      http_stream_->CanFindEndOfResponse()) {
+    if (!http_stream_->IsResponseBodyComplete()) {
+      next_state_ = STATE_DRAIN_BODY;
+      drain_buf_ = new IOBuffer(kDrainBodyBufferSize);
+      return OK;
+    }
+    keep_alive = true;
+  }
+
+  // We don't need to drain the response body, so we act as if we had drained
+  // the response body.
+  return DidDrainBodyForAuthRestart(keep_alive);
+}
+
+int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) {
+  int rc = OK;
+  if (keep_alive && transport_->socket()->IsConnectedAndIdle()) {
+    next_state_ = STATE_GENERATE_AUTH_TOKEN;
+    transport_->set_is_reused(true);
+  } else {
+    transport_->socket()->Disconnect();
+    rc = ERR_RETRY_CONNECTION;
+  }
+
+  // Reset the other member variables.
+  drain_buf_ = NULL;
+  http_stream_.reset();
+  request_headers_.clear();
+  response_ = HttpResponseInfo();
+  return rc;
+}
+
+void HttpProxyClientSocket::LogBlockedTunnelResponse(int response_code) const {
+  LOG(WARNING) << "Blocked proxy response with status " << response_code
+               << " to CONNECT request for "
+               << GetHostAndPort(request_.url) << ".";
+}
+
+void HttpProxyClientSocket::Disconnect() {
+  transport_->socket()->Disconnect();
+
+  // Reset other states to make sure they aren't mistakenly used later.
+  // These are the states initialized by Connect().
+  next_state_ = STATE_NONE;
+  user_callback_ = NULL;
+}
+
+bool HttpProxyClientSocket::IsConnected() const {
+  return transport_->socket()->IsConnected();
+}
+
+bool HttpProxyClientSocket::IsConnectedAndIdle() const {
+  return transport_->socket()->IsConnectedAndIdle();
+}
+
+bool HttpProxyClientSocket::NeedsRestartWithAuth() const {
+  return next_state_ != STATE_DONE;
+}
+
+int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len,
+                            CompletionCallback* callback) {
+  DCHECK(!user_callback_);
+  if (next_state_ != STATE_DONE) {
+    // We're trying to read the body of the response but we're still trying
+    // to establish an SSL tunnel through the proxy.  We can't read these
+    // bytes when establishing a tunnel because they might be controlled by
+    // an active network attacker.  We don't worry about this for HTTP
+    // because an active network attacker can already control HTTP sessions.
+    // We reach this case when the user cancels a 407 proxy auth prompt.
+    // See http://crbug.com/8473.
+    DCHECK_EQ(407, response_.headers->response_code());
+    LogBlockedTunnelResponse(response_.headers->response_code());
+
+    return ERR_TUNNEL_CONNECTION_FAILED;
+  }
+
+  return transport_->socket()->Read(buf, buf_len, callback);
+}
+
+int HttpProxyClientSocket::Write(IOBuffer* buf, int buf_len,
+                                 CompletionCallback* callback) {
+  DCHECK_EQ(STATE_DONE, next_state_);
+  DCHECK(!user_callback_);
+
+  return transport_->socket()->Write(buf, buf_len, callback);
+}
+
+bool HttpProxyClientSocket::SetReceiveBufferSize(int32 size) {
+  return transport_->socket()->SetReceiveBufferSize(size);
+}
+
+bool HttpProxyClientSocket::SetSendBufferSize(int32 size) {
+  return transport_->socket()->SetSendBufferSize(size);
+}
+
+int HttpProxyClientSocket::GetPeerAddress(AddressList* address) const {
+  return transport_->socket()->GetPeerAddress(address);
+}
+
+void HttpProxyClientSocket::DoCallback(int result) {
+  DCHECK_NE(ERR_IO_PENDING, result);
+  DCHECK(user_callback_);
+
+  // Since Run() may result in Read being called,
+  // clear user_callback_ up front.
+  CompletionCallback* c = user_callback_;
+  user_callback_ = NULL;
+  c->Run(result);
+}
+
+void HttpProxyClientSocket::OnIOComplete(int result) {
+  DCHECK_NE(STATE_NONE, next_state_);
+  DCHECK_NE(STATE_DONE, next_state_);
+  int rv = DoLoop(result);
+  if (rv != ERR_IO_PENDING)
+    DoCallback(rv);
+}
+
+int HttpProxyClientSocket::DoLoop(int last_io_result) {
+  DCHECK_NE(next_state_, STATE_NONE);
+  DCHECK_NE(next_state_, STATE_DONE);
+  int rv = last_io_result;
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_GENERATE_AUTH_TOKEN:
+        DCHECK_EQ(OK, rv);
+        rv = DoGenerateAuthToken();
+        break;
+      case STATE_GENERATE_AUTH_TOKEN_COMPLETE:
+        rv = DoGenerateAuthTokenComplete(rv);
+        break;
+      case STATE_SEND_REQUEST:
+        DCHECK_EQ(OK, rv);
+        net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
+                            NULL);
+        rv = DoSendRequest();
+        break;
+      case STATE_SEND_REQUEST_COMPLETE:
+        rv = DoSendRequestComplete(rv);
+        net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
+                          NULL);
+        break;
+      case STATE_READ_HEADERS:
+        DCHECK_EQ(OK, rv);
+        net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS,
+                            NULL);
+        rv = DoReadHeaders();
+        break;
+      case STATE_READ_HEADERS_COMPLETE:
+        rv = DoReadHeadersComplete(rv);
+        net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS,
+                          NULL);
+        break;
+      case STATE_DRAIN_BODY:
+        DCHECK_EQ(OK, rv);
+        rv = DoDrainBody();
+        break;
+      case STATE_DRAIN_BODY_COMPLETE:
+        rv = DoDrainBodyComplete(rv);
+        break;
+      case STATE_DONE:
+        break;
+      default:
+        NOTREACHED() << "bad state";
+        rv = ERR_UNEXPECTED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE
+           && next_state_ != STATE_DONE);
+  return rv;
+}
+
+int HttpProxyClientSocket::DoGenerateAuthToken() {
+  next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE;
+  return auth_->MaybeGenerateAuthToken(&request_, &io_callback_, net_log_);
+}
+
+int HttpProxyClientSocket::DoGenerateAuthTokenComplete(int result) {
+  DCHECK_NE(ERR_IO_PENDING, result);
+  if (result == OK)
+    next_state_ = STATE_SEND_REQUEST;
+  return result;
+}
+
+int HttpProxyClientSocket::DoSendRequest() {
+  next_state_ = STATE_SEND_REQUEST_COMPLETE;
+
+  // This is constructed lazily (instead of within our Start method), so that
+  // we have proxy info available.
+  if (request_headers_.empty()) {
+    HttpRequestHeaders authorization_headers;
+    if (auth_->HaveAuth())
+      auth_->AddAuthorizationHeader(&authorization_headers);
+    std::string request_line;
+    HttpRequestHeaders request_headers;
+    BuildTunnelRequest(&request_, authorization_headers, endpoint_,
+                       &request_line, &request_headers);
+    if (net_log_.HasListener()) {
+      net_log_.AddEvent(
+          NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
+          new NetLogHttpRequestParameter(
+              request_line, request_headers));
+    }
+    request_headers_ = request_line + request_headers.ToString();
+  }
+
+  http_stream_.reset(new HttpBasicStream(transport_.get(), net_log_));
+
+  return http_stream_->SendRequest(&request_, request_headers_, NULL,
+                                   &response_, &io_callback_);
+}
+
+int HttpProxyClientSocket::DoSendRequestComplete(int result) {
+  if (result < 0)
+    return result;
+
+  next_state_ = STATE_READ_HEADERS;
+  return OK;
+}
+
+int HttpProxyClientSocket::DoReadHeaders() {
+  next_state_ = STATE_READ_HEADERS_COMPLETE;
+  return http_stream_->ReadResponseHeaders(&io_callback_);
+}
+
+int HttpProxyClientSocket::DoReadHeadersComplete(int result) {
+  if (result < 0)
+    return result;
+
+  // Require the "HTTP/1.x" status line for SSL CONNECT.
+  if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
+    return ERR_TUNNEL_CONNECTION_FAILED;
+
+  if (net_log_.HasListener()) {
+    net_log_.AddEvent(
+        NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
+        new NetLogHttpResponseParameter(response_.headers));
+  }
+
+  switch (response_.headers->response_code()) {
+    case 200:  // OK
+      if (http_stream_->IsMoreDataBuffered())
+        // The proxy sent extraneous data after the headers.
+        return ERR_TUNNEL_CONNECTION_FAILED;
+
+      next_state_ = STATE_DONE;
+      return OK;
+
+      // We aren't able to CONNECT to the remote host through the proxy.  We
+      // need to be very suspicious about the response because an active network
+      // attacker can force us into this state by masquerading as the proxy.
+      // The only safe thing to do here is to fail the connection because our
+      // client is expecting an SSL protected response.
+      // See http://crbug.com/7338.
+    case 407:  // Proxy Authentication Required
+      // We need this status code to allow proxy authentication.  Our
+      // authentication code is smart enough to avoid being tricked by an
+      // active network attacker.
+      // The next state is intentionally not set as it should be STATE_NONE;
+      return HandleAuthChallenge();
+
+    default:
+      // For all other status codes, we conservatively fail the CONNECT
+      // request.
+      // We lose something by doing this.  We have seen proxy 403, 404, and
+      // 501 response bodies that contain a useful error message.  For
+      // example, Squid uses a 404 response to report the DNS error: "The
+      // domain name does not exist."
+      LogBlockedTunnelResponse(response_.headers->response_code());
+      return ERR_TUNNEL_CONNECTION_FAILED;
+  }
+}
+
+int HttpProxyClientSocket::DoDrainBody() {
+  DCHECK(drain_buf_);
+  DCHECK(transport_->is_initialized());
+  next_state_ = STATE_DRAIN_BODY_COMPLETE;
+  return http_stream_->ReadResponseBody(drain_buf_, kDrainBodyBufferSize,
+                                        &io_callback_);
+}
+
+int HttpProxyClientSocket::DoDrainBodyComplete(int result) {
+  if (result < 0)
+    return result;
+
+  if (http_stream_->IsResponseBodyComplete())
+    return DidDrainBodyForAuthRestart(true);
+
+  // Keep draining.
+  next_state_ = STATE_DRAIN_BODY;
+  return OK;
+}
+
+int HttpProxyClientSocket::HandleAuthChallenge() {
+  DCHECK(response_.headers);
+
+  int rv = auth_->HandleAuthChallenge(response_.headers, false, true, net_log_);
+  response_.auth_challenge = auth_->auth_info();
+  if (rv == OK)
+    return ERR_PROXY_AUTH_REQUESTED;
+
+  return rv;
+}
+
+}  // namespace net
diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h
new file mode 100644
index 0000000..61e8158
--- /dev/null
+++ b/net/http/http_proxy_client_socket.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2010 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_HTTP_HTTP_PROXY_CLIENT_SOCKET_H_
+#define NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_log.h"
+#include "net/http/http_auth_controller.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
+#include "net/socket/client_socket.h"
+
+class GURL;
+
+namespace net {
+
+class AddressList;
+class ClientSocketHandle;
+class HttpStream;
+class IOBuffer;;
+
+class HttpProxyClientSocket : public ClientSocket {
+ public:
+  // Takes ownership of |transport_socket|, which should already be connected
+  // by the time Connect() is called.  If tunnel is true then on Connect()
+  // this socket will establish an Http tunnel.
+  HttpProxyClientSocket(ClientSocketHandle* transport_socket,
+                        const GURL& request_url, const HostPortPair& endpoint,
+                        const scoped_refptr<HttpAuthController>& auth,
+                        bool tunnel);
+
+  // On destruction Disconnect() is called.
+  virtual ~HttpProxyClientSocket();
+
+  // If Connect (or its callback) returns PROXY_AUTH_REQUESTED, then
+  // credentials should be added to the HttpAuthController before calling
+  // RestartWithAuth.
+  int RestartWithAuth(CompletionCallback* callback);
+
+  // Indicates if RestartWithAuth needs to be called. i.e. if Connect
+  // returned PROXY_AUTH_REQUESTED.  Only valid after Connect has been called.
+  bool NeedsRestartWithAuth() const;
+
+  const HttpResponseInfo* GetResponseInfo() const {
+      return response_.headers ? &response_ : NULL;
+  }
+
+  // ClientSocket methods:
+
+  // Authenticates to the Http Proxy and then passes data freely.
+  virtual int Connect(CompletionCallback* callback);
+  virtual void Disconnect();
+  virtual bool IsConnected() const;
+  virtual bool IsConnectedAndIdle() const;
+  virtual const BoundNetLog& NetLog() const { return net_log_; }
+
+  // Socket methods:
+  virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
+  virtual int Write(IOBuffer* buf, int buf_len, CompletionCallback* callback);
+
+  virtual bool SetReceiveBufferSize(int32 size);
+  virtual bool SetSendBufferSize(int32 size);
+
+  virtual int GetPeerAddress(AddressList* address) const;
+
+ private:
+  enum State {
+    STATE_NONE,
+    STATE_GENERATE_AUTH_TOKEN,
+    STATE_GENERATE_AUTH_TOKEN_COMPLETE,
+    STATE_SEND_REQUEST,
+    STATE_SEND_REQUEST_COMPLETE,
+    STATE_READ_HEADERS,
+    STATE_READ_HEADERS_COMPLETE,
+    STATE_RESOLVE_CANONICAL_NAME,
+    STATE_RESOLVE_CANONICAL_NAME_COMPLETE,
+    STATE_DRAIN_BODY,
+    STATE_DRAIN_BODY_COMPLETE,
+    STATE_DONE,
+  };
+
+  // The size in bytes of the buffer we use to drain the response body that
+  // we want to throw away.  The response body is typically a small error
+  // page just a few hundred bytes long.
+  enum { kDrainBodyBufferSize = 1024 };
+
+  int PrepareForAuthRestart();
+  int DidDrainBodyForAuthRestart(bool keep_alive);
+
+  int HandleAuthChallenge();
+
+  void LogBlockedTunnelResponse(int response_code) const;
+
+  void DoCallback(int result);
+  void OnIOComplete(int result);
+
+  int DoLoop(int last_io_result);
+  int DoGenerateAuthToken();
+  int DoGenerateAuthTokenComplete(int result);
+  int DoSendRequest();
+  int DoSendRequestComplete(int result);
+  int DoReadHeaders();
+  int DoReadHeadersComplete(int result);
+  int DoDrainBody();
+  int DoDrainBodyComplete(int result);
+
+  CompletionCallbackImpl<HttpProxyClientSocket> io_callback_;
+  State next_state_;
+
+  // Stores the callback to the layer above, called on completing Connect().
+  CompletionCallback* user_callback_;
+
+  HttpRequestInfo request_;
+  HttpResponseInfo response_;
+
+  scoped_ptr<HttpStream> http_stream_;
+  scoped_refptr<IOBuffer> drain_buf_;
+
+  // Stores the underlying socket.
+  const scoped_ptr<ClientSocketHandle> transport_;
+
+  // The hostname and port of the endpoint.  This is not necessarily the one
+  // specified by the URL, due to Alternate-Protocol or fixed testing ports.
+  const HostPortPair endpoint_;
+  scoped_refptr<HttpAuthController> auth_;
+  const bool tunnel_;
+
+  std::string request_headers_;
+
+  const BoundNetLog net_log_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpProxyClientSocket);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_H_
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
new file mode 100644
index 0000000..a142576
--- /dev/null
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -0,0 +1,229 @@
+// Copyright (c) 2010 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/http/http_proxy_client_socket_pool.h"
+
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_proxy_client_socket.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_base.h"
+
+namespace net {
+
+HttpProxySocketParams::HttpProxySocketParams(
+    const scoped_refptr<TCPSocketParams>& proxy_server,
+    const GURL& request_url,
+    HostPortPair endpoint,
+    scoped_refptr<HttpAuthController> auth_controller,
+    bool tunnel)
+    : tcp_params_(proxy_server),
+      request_url_(request_url),
+      endpoint_(endpoint),
+      auth_controller_(auth_controller),
+      tunnel_(tunnel) {
+}
+
+HttpProxySocketParams::~HttpProxySocketParams() {}
+
+// HttpProxyConnectJobs will time out after this many seconds.  Note this is on
+// top of the timeout for the transport socket.
+static const int kHttpProxyConnectJobTimeoutInSeconds = 30;
+
+HttpProxyConnectJob::HttpProxyConnectJob(
+    const std::string& group_name,
+    const scoped_refptr<HttpProxySocketParams>& params,
+    const base::TimeDelta& timeout_duration,
+    const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+    const scoped_refptr<HostResolver>& host_resolver,
+    Delegate* delegate,
+    NetLog* net_log)
+    : ConnectJob(group_name, timeout_duration, delegate,
+                 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
+      params_(params),
+      tcp_pool_(tcp_pool),
+      resolver_(host_resolver),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          callback_(this, &HttpProxyConnectJob::OnIOComplete)) {
+}
+
+HttpProxyConnectJob::~HttpProxyConnectJob() {}
+
+LoadState HttpProxyConnectJob::GetLoadState() const {
+  switch (next_state_) {
+    case kStateTCPConnect:
+    case kStateTCPConnectComplete:
+      return tcp_socket_handle_->GetLoadState();
+    case kStateHttpProxyConnect:
+    case kStateHttpProxyConnectComplete:
+      return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
+    default:
+      NOTREACHED();
+      return LOAD_STATE_IDLE;
+  }
+}
+
+int HttpProxyConnectJob::ConnectInternal() {
+  next_state_ = kStateTCPConnect;
+  return DoLoop(OK);
+}
+
+void HttpProxyConnectJob::OnIOComplete(int result) {
+  int rv = DoLoop(result);
+  if (rv != ERR_IO_PENDING)
+    NotifyDelegateOfCompletion(rv);  // Deletes |this|
+}
+
+int HttpProxyConnectJob::DoLoop(int result) {
+  DCHECK_NE(next_state_, kStateNone);
+
+  int rv = result;
+  do {
+    State state = next_state_;
+    next_state_ = kStateNone;
+    switch (state) {
+      case kStateTCPConnect:
+        DCHECK_EQ(OK, rv);
+        rv = DoTCPConnect();
+        break;
+      case kStateTCPConnectComplete:
+        rv = DoTCPConnectComplete(rv);
+        break;
+      case kStateHttpProxyConnect:
+        DCHECK_EQ(OK, rv);
+        rv = DoHttpProxyConnect();
+        break;
+      case kStateHttpProxyConnectComplete:
+        rv = DoHttpProxyConnectComplete(rv);
+        break;
+      default:
+        NOTREACHED() << "bad state";
+        rv = ERR_FAILED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != kStateNone);
+
+  return rv;
+}
+
+int HttpProxyConnectJob::DoTCPConnect() {
+  next_state_ = kStateTCPConnectComplete;
+  tcp_socket_handle_.reset(new ClientSocketHandle());
+  return tcp_socket_handle_->Init(
+      group_name(), params_->tcp_params(),
+      params_->tcp_params()->destination().priority(), &callback_, tcp_pool_,
+      net_log());
+}
+
+int HttpProxyConnectJob::DoTCPConnectComplete(int result) {
+  if (result != OK)
+    return result;
+
+  // Reset the timer to just the length of time allowed for HttpProxy handshake
+  // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
+  // longer to timeout than it should.
+  ResetTimer(base::TimeDelta::FromSeconds(
+      kHttpProxyConnectJobTimeoutInSeconds));
+  next_state_ = kStateHttpProxyConnect;
+  return result;
+}
+
+int HttpProxyConnectJob::DoHttpProxyConnect() {
+  next_state_ = kStateHttpProxyConnectComplete;
+
+  // Add a HttpProxy connection on top of the tcp socket.
+  socket_.reset(new HttpProxyClientSocket(tcp_socket_handle_.release(),
+                                          params_->request_url(),
+                                          params_->endpoint(),
+                                          params_->auth_controller(),
+                                          params_->tunnel()));
+  return socket_->Connect(&callback_);
+}
+
+int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) {
+  DCHECK_NE(result, ERR_RETRY_CONNECTION);
+
+  if (result == OK || result == ERR_PROXY_AUTH_REQUESTED)
+      set_socket(socket_.release());
+
+  return result;
+}
+
+ConnectJob*
+HttpProxyClientSocketPool::HttpProxyConnectJobFactory::NewConnectJob(
+    const std::string& group_name,
+    const PoolBase::Request& request,
+    ConnectJob::Delegate* delegate) const {
+  return new HttpProxyConnectJob(group_name, request.params(),
+                                 ConnectionTimeout(), tcp_pool_, host_resolver_,
+                                 delegate, net_log_);
+}
+
+base::TimeDelta
+HttpProxyClientSocketPool::HttpProxyConnectJobFactory::ConnectionTimeout()
+const {
+  return tcp_pool_->ConnectionTimeout() +
+      base::TimeDelta::FromSeconds(kHttpProxyConnectJobTimeoutInSeconds);
+}
+
+HttpProxyClientSocketPool::HttpProxyClientSocketPool(
+    int max_sockets,
+    int max_sockets_per_group,
+    const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+    const scoped_refptr<HostResolver>& host_resolver,
+    const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+    NetLog* net_log)
+    : base_(max_sockets, max_sockets_per_group, histograms,
+            base::TimeDelta::FromSeconds(
+                ClientSocketPool::unused_idle_socket_timeout()),
+            base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
+            new HttpProxyConnectJobFactory(tcp_pool, host_resolver, net_log)) {}
+
+HttpProxyClientSocketPool::~HttpProxyClientSocketPool() {}
+
+int HttpProxyClientSocketPool::RequestSocket(const std::string& group_name,
+                                             const void* socket_params,
+                                             RequestPriority priority,
+                                             ClientSocketHandle* handle,
+                                             CompletionCallback* callback,
+                                             const BoundNetLog& net_log) {
+  const scoped_refptr<HttpProxySocketParams>* casted_socket_params =
+      static_cast<const scoped_refptr<HttpProxySocketParams>*>(socket_params);
+
+  return base_.RequestSocket(group_name, *casted_socket_params, priority,
+                             handle, callback, net_log);
+}
+
+void HttpProxyClientSocketPool::CancelRequest(
+    const std::string& group_name,
+    ClientSocketHandle* handle) {
+  base_.CancelRequest(group_name, handle);
+}
+
+void HttpProxyClientSocketPool::ReleaseSocket(const std::string& group_name,
+                                              ClientSocket* socket, int id) {
+  base_.ReleaseSocket(group_name, socket, id);
+}
+
+void HttpProxyClientSocketPool::Flush() {
+  base_.Flush();
+}
+
+void HttpProxyClientSocketPool::CloseIdleSockets() {
+  base_.CloseIdleSockets();
+}
+
+int HttpProxyClientSocketPool::IdleSocketCountInGroup(
+    const std::string& group_name) const {
+  return base_.IdleSocketCountInGroup(group_name);
+}
+
+LoadState HttpProxyClientSocketPool::GetLoadState(
+    const std::string& group_name, const ClientSocketHandle* handle) const {
+  return base_.GetLoadState(group_name, handle);
+}
+
+}  // namespace net
diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h
new file mode 100644
index 0000000..afe7d19
--- /dev/null
+++ b/net/http/http_proxy_client_socket_pool.h
@@ -0,0 +1,204 @@
+// Copyright (c) 2010 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_HTTP_HTTP_PROXY_CLIENT_SOCKET_POOL_H_
+#define NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_POOL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/host_resolver.h"
+#include "net/http/http_auth.h"
+#include "net/proxy/proxy_server.h"
+#include "net/socket/client_socket_pool_base.h"
+#include "net/socket/client_socket_pool_histograms.h"
+#include "net/socket/client_socket_pool.h"
+#include "net/socket/tcp_client_socket_pool.h"
+
+namespace net {
+
+class ClientSocketFactory;
+class ConnectJobFactory;
+class HttpAuthController;
+
+class HttpProxySocketParams : public base::RefCounted<HttpProxySocketParams> {
+ public:
+  HttpProxySocketParams(const scoped_refptr<TCPSocketParams>& proxy_server,
+                        const GURL& request_url, HostPortPair endpoint,
+                        scoped_refptr<HttpAuthController> auth_controller,
+                        bool tunnel);
+
+  const scoped_refptr<TCPSocketParams>& tcp_params() const {
+    return tcp_params_;
+  }
+  const GURL& request_url() const { return request_url_; }
+  const HostPortPair& endpoint() const { return endpoint_; }
+  const scoped_refptr<HttpAuthController>& auth_controller() {
+    return auth_controller_;
+  }
+  bool tunnel() const { return tunnel_; }
+
+ private:
+  friend class base::RefCounted<HttpProxySocketParams>;
+  ~HttpProxySocketParams();
+
+  const scoped_refptr<TCPSocketParams> tcp_params_;
+  const GURL request_url_;
+  const HostPortPair endpoint_;
+  const scoped_refptr<HttpAuthController> auth_controller_;
+  const bool tunnel_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpProxySocketParams);
+};
+
+// HttpProxyConnectJob optionally establishes a tunnel through the proxy
+// server after connecting the underlying transport socket.
+class HttpProxyConnectJob : public ConnectJob {
+ public:
+  HttpProxyConnectJob(const std::string& group_name,
+                      const scoped_refptr<HttpProxySocketParams>& params,
+                      const base::TimeDelta& timeout_duration,
+                      const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+                      const scoped_refptr<HostResolver> &host_resolver,
+                      Delegate* delegate,
+                      NetLog* net_log);
+  virtual ~HttpProxyConnectJob();
+
+  // ConnectJob methods.
+  virtual LoadState GetLoadState() const;
+
+ private:
+  enum State {
+    kStateTCPConnect,
+    kStateTCPConnectComplete,
+    kStateHttpProxyConnect,
+    kStateHttpProxyConnectComplete,
+    kStateNone,
+  };
+
+  // Begins the tcp connection and the optional Http proxy tunnel.  If the
+  // request is not immediately servicable (likely), the request will return
+  // ERR_IO_PENDING. An OK return from this function or the callback means
+  // that the connection is established; ERR_PROXY_AUTH_REQUESTED means
+  // that the tunnel needs authentication credentials, the socket will be
+  // returned in this case, and must be release back to the pool; or
+  // a standard net error code will be returned.
+  virtual int ConnectInternal();
+
+  void OnIOComplete(int result);
+
+  // Runs the state transition loop.
+  int DoLoop(int result);
+
+  int DoTCPConnect();
+  int DoTCPConnectComplete(int result);
+  int DoHttpProxyConnect();
+  int DoHttpProxyConnectComplete(int result);
+
+  scoped_refptr<HttpProxySocketParams> params_;
+  const scoped_refptr<TCPClientSocketPool> tcp_pool_;
+  const scoped_refptr<HostResolver> resolver_;
+
+  State next_state_;
+  CompletionCallbackImpl<HttpProxyConnectJob> callback_;
+  scoped_ptr<ClientSocketHandle> tcp_socket_handle_;
+  scoped_ptr<ClientSocket> socket_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpProxyConnectJob);
+};
+
+class HttpProxyClientSocketPool : public ClientSocketPool {
+ public:
+  HttpProxyClientSocketPool(
+      int max_sockets,
+      int max_sockets_per_group,
+      const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+      const scoped_refptr<HostResolver>& host_resolver,
+      const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+      NetLog* net_log);
+
+  // ClientSocketPool methods:
+  virtual int RequestSocket(const std::string& group_name,
+                            const void* connect_params,
+                            RequestPriority priority,
+                            ClientSocketHandle* handle,
+                            CompletionCallback* callback,
+                            const BoundNetLog& net_log);
+
+  virtual void CancelRequest(const std::string& group_name,
+                             ClientSocketHandle* handle);
+
+  virtual void ReleaseSocket(const std::string& group_name,
+                             ClientSocket* socket,
+                             int id);
+
+  virtual void Flush();
+
+  virtual void CloseIdleSockets();
+
+  virtual int IdleSocketCount() const {
+    return base_.idle_socket_count();
+  }
+
+  virtual int IdleSocketCountInGroup(const std::string& group_name) const;
+
+  virtual LoadState GetLoadState(const std::string& group_name,
+                                 const ClientSocketHandle* handle) const;
+
+  virtual base::TimeDelta ConnectionTimeout() const {
+    return base_.ConnectionTimeout();
+  }
+
+  virtual scoped_refptr<ClientSocketPoolHistograms> histograms() const {
+    return base_.histograms();
+  };
+
+ protected:
+  virtual ~HttpProxyClientSocketPool();
+
+ private:
+  typedef ClientSocketPoolBase<HttpProxySocketParams> PoolBase;
+
+  class HttpProxyConnectJobFactory : public PoolBase::ConnectJobFactory {
+   public:
+    HttpProxyConnectJobFactory(
+        const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+        HostResolver* host_resolver,
+        NetLog* net_log)
+        : tcp_pool_(tcp_pool),
+          host_resolver_(host_resolver),
+          net_log_(net_log) {}
+
+    virtual ~HttpProxyConnectJobFactory() {}
+
+    // ClientSocketPoolBase::ConnectJobFactory methods.
+    virtual ConnectJob* NewConnectJob(const std::string& group_name,
+                                      const PoolBase::Request& request,
+                                      ConnectJob::Delegate* delegate) const;
+
+    virtual base::TimeDelta ConnectionTimeout() const;
+
+   private:
+    const scoped_refptr<TCPClientSocketPool> tcp_pool_;
+    const scoped_refptr<HostResolver> host_resolver_;
+    NetLog* net_log_;
+
+    DISALLOW_COPY_AND_ASSIGN(HttpProxyConnectJobFactory);
+  };
+
+  PoolBase base_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpProxyClientSocketPool);
+};
+
+REGISTER_SOCKET_PARAMS_FOR_POOL(HttpProxyClientSocketPool,
+                                HttpProxySocketParams);
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_POOL_H_
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
new file mode 100644
index 0000000..9400dd8
--- /dev/null
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -0,0 +1,272 @@
+// Copyright (c) 2010 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/http/http_proxy_client_socket_pool.h"
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/http/http_proxy_client_socket.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_histograms.h"
+#include "net/socket/socket_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+const int kMaxSockets = 32;
+const int kMaxSocketsPerGroup = 6;
+
+class HttpProxyClientSocketPoolTest : public ClientSocketPoolTest {
+ protected:
+  HttpProxyClientSocketPoolTest()
+      : ignored_tcp_socket_params_(new TCPSocketParams(
+            HostPortPair("proxy", 80), MEDIUM, GURL(), false)),
+        tcp_histograms_(new ClientSocketPoolHistograms("MockTCP")),
+        tcp_socket_pool_(new MockTCPClientSocketPool(kMaxSockets,
+            kMaxSocketsPerGroup, tcp_histograms_, &tcp_client_socket_factory_)),
+        notunnel_socket_params_(new HttpProxySocketParams(
+              ignored_tcp_socket_params_, GURL("http://host"),
+              HostPortPair("host", 80), NULL, false)),
+        auth_controller_(new MockHttpAuthController),
+        tunnel_socket_params_(new HttpProxySocketParams(
+              ignored_tcp_socket_params_, GURL("http://host"),
+              HostPortPair("host", 80), auth_controller_, true)),
+        http_proxy_histograms_(
+            new ClientSocketPoolHistograms("HttpProxyUnitTest")),
+        pool_(new HttpProxyClientSocketPool(kMaxSockets, kMaxSocketsPerGroup,
+            http_proxy_histograms_, NULL, tcp_socket_pool_, NULL)) {
+  }
+
+  int StartRequest(const std::string& group_name, RequestPriority priority) {
+    return StartRequestUsingPool(
+        pool_, group_name, priority, tunnel_socket_params_);
+  }
+
+  scoped_refptr<TCPSocketParams> ignored_tcp_socket_params_;
+  scoped_refptr<ClientSocketPoolHistograms> tcp_histograms_;
+  MockClientSocketFactory tcp_client_socket_factory_;
+  scoped_refptr<MockTCPClientSocketPool> tcp_socket_pool_;
+
+  scoped_refptr<HttpProxySocketParams> notunnel_socket_params_;
+  scoped_refptr<MockHttpAuthController> auth_controller_;
+  scoped_refptr<HttpProxySocketParams> tunnel_socket_params_;
+  scoped_refptr<ClientSocketPoolHistograms> http_proxy_histograms_;
+  scoped_refptr<HttpProxyClientSocketPool> pool_;
+};
+
+TEST_F(HttpProxyClientSocketPoolTest, NoTunnel) {
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(false, 0));
+  tcp_client_socket_factory_.AddSocketDataProvider(&data);
+
+  ClientSocketHandle handle;
+  int rv = handle.Init("a", notunnel_socket_params_, LOW, NULL, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+  HttpProxyClientSocket* tunnel_socket =
+          static_cast<HttpProxyClientSocket*>(handle.socket());
+  EXPECT_FALSE(tunnel_socket->NeedsRestartWithAuth());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, NeedAuth) {
+  MockWrite writes[] = {
+      MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+  };
+  MockRead reads[] = {
+      // No credentials.
+      MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
+      MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+      MockRead("Content-Length: 10\r\n\r\n"),
+      MockRead("0123456789"),
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), writes,
+                                arraysize(writes));
+
+  tcp_client_socket_factory_.AddSocketDataProvider(&data);
+  MockHttpAuthControllerData auth_data[] = {
+    MockHttpAuthControllerData(""),
+  };
+  auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data));
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+  HttpProxyClientSocket* tunnel_socket =
+          static_cast<HttpProxyClientSocket*>(handle.socket());
+  EXPECT_TRUE(tunnel_socket->NeedsRestartWithAuth());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, HaveAuth) {
+  MockWrite writes[] = {
+      MockWrite(false,
+                "CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+  };
+  MockRead reads[] = {
+      MockRead(false, "HTTP/1.1 200 Connection Established\r\n\r\n"),
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), writes,
+                                arraysize(writes));
+  data.set_connect_data(MockConnect(false, 0));
+
+  tcp_client_socket_factory_.AddSocketDataProvider(&data);
+  MockHttpAuthControllerData auth_data[] = {
+    MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+  };
+  auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data));
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+  HttpProxyClientSocket* tunnel_socket =
+          static_cast<HttpProxyClientSocket*>(handle.socket());
+  EXPECT_FALSE(tunnel_socket->NeedsRestartWithAuth());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, AsyncHaveAuth) {
+  MockWrite writes[] = {
+      MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+  };
+  MockRead reads[] = {
+      MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), writes,
+                                arraysize(writes));
+
+  tcp_client_socket_factory_.AddSocketDataProvider(&data);
+  MockHttpAuthControllerData auth_data[] = {
+    MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+  };
+  auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data));
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+  HttpProxyClientSocket* tunnel_socket =
+          static_cast<HttpProxyClientSocket*>(handle.socket());
+  EXPECT_FALSE(tunnel_socket->NeedsRestartWithAuth());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, TCPError) {
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(true, ERR_CONNECTION_CLOSED));
+
+  tcp_client_socket_factory_.AddSocketDataProvider(&data);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) {
+  MockWrite writes[] = {
+      MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+  };
+  MockRead reads[] = {
+      MockRead("HTTP/1.1 200 Conn"),
+      MockRead(true, ERR_CONNECTION_CLOSED),
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), writes,
+                                arraysize(writes));
+
+  tcp_client_socket_factory_.AddSocketDataProvider(&data);
+  MockHttpAuthControllerData auth_data[] = {
+    MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+  };
+  auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data));
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, TunnelSetupError) {
+  MockWrite writes[] = {
+      MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+  };
+  MockRead reads[] = {
+      MockRead("HTTP/1.1 304 Not Modified\r\n\r\n"),
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), writes,
+                                arraysize(writes));
+
+  tcp_client_socket_factory_.AddSocketDataProvider(&data);
+  MockHttpAuthControllerData auth_data[] = {
+    MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+  };
+  auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data));
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+}
+
+// It would be nice to also test the timeouts in HttpProxyClientSocketPool.
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/http/http_request_headers.cc b/net/http/http_request_headers.cc
new file mode 100644
index 0000000..29379c1
--- /dev/null
+++ b/net/http/http_request_headers.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2010 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/http/http_request_headers.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "net/http/http_util.h"
+
+namespace net {
+
+const char HttpRequestHeaders::kGetMethod[] = "GET";
+const char HttpRequestHeaders::kAcceptCharset[] = "Accept-Charset";
+const char HttpRequestHeaders::kAcceptEncoding[] = "Accept-Encoding";
+const char HttpRequestHeaders::kAcceptLanguage[] = "Accept-Language";
+const char HttpRequestHeaders::kCacheControl[] = "Cache-Control";
+const char HttpRequestHeaders::kConnection[] = "Connection";
+const char HttpRequestHeaders::kContentLength[] = "Content-Length";
+const char HttpRequestHeaders::kContentType[] = "Content-Type";
+const char HttpRequestHeaders::kCookie[] = "Cookie";
+const char HttpRequestHeaders::kHost[] = "Host";
+const char HttpRequestHeaders::kIfModifiedSince[] = "If-Modified-Since";
+const char HttpRequestHeaders::kIfNoneMatch[] = "If-None-Match";
+const char HttpRequestHeaders::kIfRange[] = "If-Range";
+const char HttpRequestHeaders::kOrigin[] = "Origin";
+const char HttpRequestHeaders::kPragma[] = "Pragma";
+const char HttpRequestHeaders::kProxyConnection[] = "Proxy-Connection";
+const char HttpRequestHeaders::kRange[] = "Range";
+const char HttpRequestHeaders::kReferer[] = "Referer";
+const char HttpRequestHeaders::kUserAgent[] = "User-Agent";
+
+HttpRequestHeaders::Iterator::Iterator(const HttpRequestHeaders& headers)
+    : started_(false),
+      curr_(headers.headers_.begin()),
+      end_(headers.headers_.end()) {}
+
+HttpRequestHeaders::Iterator::~Iterator() {}
+
+bool HttpRequestHeaders::Iterator::GetNext() {
+  if (!started_) {
+    started_ = true;
+    return curr_ != end_;
+  }
+
+  if (curr_ == end_)
+    return false;
+
+  ++curr_;
+  return curr_ != end_;
+}
+
+HttpRequestHeaders::HttpRequestHeaders() {}
+HttpRequestHeaders::~HttpRequestHeaders() {}
+
+bool HttpRequestHeaders::GetHeader(const base::StringPiece& key,
+                                   std::string* out) const {
+  HeaderVector::const_iterator it = FindHeader(key);
+  if (it == headers_.end())
+    return false;
+  out->assign(it->value);
+  return true;
+}
+
+void HttpRequestHeaders::Clear() {
+  headers_.clear();
+}
+
+void HttpRequestHeaders::SetHeader(const base::StringPiece& key,
+                                   const base::StringPiece& value) {
+  HeaderVector::iterator it = FindHeader(key);
+  if (it != headers_.end())
+    it->value = value.as_string();
+  else
+    headers_.push_back(HeaderKeyValuePair(key.as_string(), value.as_string()));
+}
+
+void HttpRequestHeaders::RemoveHeader(const base::StringPiece& key) {
+  HeaderVector::iterator it = FindHeader(key);
+  if (it != headers_.end())
+    headers_.erase(it);
+}
+
+void HttpRequestHeaders::AddHeaderFromString(
+    const base::StringPiece& header_line) {
+  DCHECK_EQ(std::string::npos, header_line.find("\r\n"))
+      << "\"" << header_line << "\" contains CRLF.";
+
+  const std::string::size_type key_end_index = header_line.find(":");
+  if (key_end_index == std::string::npos) {
+    LOG(DFATAL) << "\"" << header_line << "\" is missing colon delimiter.";
+    return;
+  }
+
+  if (key_end_index == 0) {
+    LOG(DFATAL) << "\"" << header_line << "\" is missing header key.";
+    return;
+  }
+
+  const base::StringPiece header_key(header_line.data(), key_end_index);
+
+  const std::string::size_type value_index = key_end_index + 1;
+
+  if (value_index < header_line.size()) {
+    std::string header_value(header_line.data() + value_index,
+                             header_line.size() - value_index);
+    std::string::const_iterator header_value_begin =
+        header_value.begin();
+    std::string::const_iterator header_value_end =
+        header_value.end();
+    HttpUtil::TrimLWS(&header_value_begin, &header_value_end);
+
+    if (header_value_begin == header_value_end) {
+      // Value was all LWS.
+      SetHeader(header_key, "");
+    } else {
+      SetHeader(header_key,
+                base::StringPiece(&*header_value_begin,
+                                  header_value_end - header_value_begin));
+    }
+  } else if (value_index == header_line.size()) {
+    SetHeader(header_key, "");
+  } else {
+    NOTREACHED();
+  }
+}
+
+void HttpRequestHeaders::AddHeadersFromString(
+    const base::StringPiece& headers) {
+  // TODO(willchan): Consider adding more StringPiece support in string_util.h
+  // to eliminate copies.
+  std::vector<std::string> header_line_vector;
+  SplitStringUsingSubstr(headers.as_string(), "\r\n", &header_line_vector);
+  for (std::vector<std::string>::const_iterator it = header_line_vector.begin();
+       it != header_line_vector.end(); ++it) {
+    if (!it->empty())
+      AddHeaderFromString(*it);
+  }
+}
+
+void HttpRequestHeaders::MergeFrom(const HttpRequestHeaders& other) {
+  for (HeaderVector::const_iterator it = other.headers_.begin();
+       it != other.headers_.end(); ++it ) {
+    SetHeader(it->key, it->value);
+  }
+}
+
+std::string HttpRequestHeaders::ToString() const {
+  std::string output;
+  for (HeaderVector::const_iterator it = headers_.begin();
+       it != headers_.end(); ++it) {
+    if (!it->value.empty())
+      StringAppendF(&output, "%s: %s\r\n", it->key.c_str(), it->value.c_str());
+    else
+      StringAppendF(&output, "%s:\r\n", it->key.c_str());
+  }
+  output.append("\r\n");
+  return output;
+}
+
+HttpRequestHeaders::HeaderVector::iterator
+HttpRequestHeaders::FindHeader(const base::StringPiece& key) {
+  for (HeaderVector::iterator it = headers_.begin();
+       it != headers_.end(); ++it) {
+    if (key.length() == it->key.length() &&
+        !base::strncasecmp(key.data(), it->key.data(), key.length()))
+      return it;
+  }
+
+  return headers_.end();
+}
+
+HttpRequestHeaders::HeaderVector::const_iterator
+HttpRequestHeaders::FindHeader(const base::StringPiece& key) const {
+  for (HeaderVector::const_iterator it = headers_.begin();
+       it != headers_.end(); ++it) {
+    if (key.length() == it->key.length() &&
+        !base::strncasecmp(key.data(), it->key.data(), key.length()))
+      return it;
+  }
+
+  return headers_.end();
+}
+
+}  // namespace net
diff --git a/net/http/http_request_headers.h b/net/http/http_request_headers.h
new file mode 100644
index 0000000..c1f98b6
--- /dev/null
+++ b/net/http/http_request_headers.h
@@ -0,0 +1,148 @@
+// Copyright (c) 2010 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.
+//
+// HttpRequestHeaders manages the request headers.
+// It maintains these in a vector of header key/value pairs, thereby maintaining
+// the order of the headers.  This means that any lookups are linear time
+// operations.
+
+#ifndef NET_HTTP_HTTP_REQUEST_HEADERS_H_
+#define NET_HTTP_HTTP_REQUEST_HEADERS_H_
+
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/string_piece.h"
+
+namespace net {
+
+class HttpRequestHeaders {
+ public:
+  struct HeaderKeyValuePair {
+    HeaderKeyValuePair() {}
+    HeaderKeyValuePair(const base::StringPiece& key,
+                       const base::StringPiece& value)
+        : key(key.data(), key.size()), value(value.data(), value.size()) {}
+
+    std::string key;
+    std::string value;
+  };
+
+  typedef std::vector<HeaderKeyValuePair> HeaderVector;
+
+  class Iterator {
+   public:
+    explicit Iterator(const HttpRequestHeaders& headers);
+    ~Iterator();
+
+    // Advances the iterator to the next header, if any.  Returns true if there
+    // is a next header.  Use name() and value() methods to access the resultant
+    // header name and value.
+    bool GetNext();
+
+    // These two accessors are only valid if GetNext() returned true.
+    const std::string& name() const { return curr_->key; }
+    const std::string& value() const { return curr_->value; }
+
+   private:
+    bool started_;
+    HttpRequestHeaders::HeaderVector::const_iterator curr_;
+    const HttpRequestHeaders::HeaderVector::const_iterator end_;
+
+    DISALLOW_COPY_AND_ASSIGN(Iterator);
+  };
+
+  static const char kGetMethod[];
+
+  static const char kAcceptCharset[];
+  static const char kAcceptEncoding[];
+  static const char kAcceptLanguage[];
+  static const char kCacheControl[];
+  static const char kConnection[];
+  static const char kContentType[];
+  static const char kCookie[];
+  static const char kContentLength[];
+  static const char kHost[];
+  static const char kIfModifiedSince[];
+  static const char kIfNoneMatch[];
+  static const char kIfRange[];
+  static const char kOrigin[];
+  static const char kPragma[];
+  static const char kProxyConnection[];
+  static const char kRange[];
+  static const char kReferer[];
+  static const char kUserAgent[];
+
+  HttpRequestHeaders();
+  ~HttpRequestHeaders();
+
+  bool IsEmpty() const { return headers_.empty(); }
+
+  bool HasHeader(const base::StringPiece& key) const {
+    return FindHeader(key) != headers_.end();
+  }
+
+  // Gets the first header that matches |key|.  If found, returns true and
+  // writes the value to |out|.
+  bool GetHeader(const base::StringPiece& key, std::string* out) const;
+
+  // Clears all the headers.
+  void Clear();
+
+  // Sets the header value pair for |key| and |value|.  If |key| already exists,
+  // then the header value is modified, but the key is untouched, and the order
+  // in the vector remains the same.  When comparing |key|, case is ignored.
+  void SetHeader(const base::StringPiece& key, const base::StringPiece& value);
+
+  // Removes the first header that matches (case insensitive) |key|.
+  void RemoveHeader(const base::StringPiece& key);
+
+  // Parses the header from a string and calls SetHeader() with it.  This string
+  // should not contain any CRLF.  As per RFC2616, the format is:
+  //
+  // message-header = field-name ":" [ field-value ]
+  // field-name     = token
+  // field-value    = *( field-content | LWS )
+  // field-content  = <the OCTETs making up the field-value
+  //                  and consisting of either *TEXT or combinations
+  //                  of token, separators, and quoted-string>
+  //
+  // AddHeaderFromString() will trim any LWS surrounding the
+  // field-content.
+  void AddHeaderFromString(const base::StringPiece& header_line);
+
+  // Same thing as AddHeaderFromString() except that |headers| is a "\r\n"
+  // delimited string of header lines.  It will split up the string by "\r\n"
+  // and call AddHeaderFromString() on each.
+  void AddHeadersFromString(const base::StringPiece& headers);
+
+  // Calls SetHeader() on each header from |other|, maintaining order.
+  void MergeFrom(const HttpRequestHeaders& other);
+
+  // Copies from |other| to |this|.
+  void CopyFrom(const HttpRequestHeaders& other) {
+    *this = other;
+  }
+
+  // Serializes HttpRequestHeaders to a string representation.  Joins all the
+  // header keys and values with ": ", and inserts "\r\n" between each header
+  // line, and adds the trailing "\r\n".
+  std::string ToString() const;
+
+ private:
+  HeaderVector::iterator FindHeader(const base::StringPiece& key);
+  HeaderVector::const_iterator FindHeader(const base::StringPiece& key) const;
+
+  HeaderVector headers_;
+
+  // Allow the copy construction and operator= to facilitate copying in
+  // HttpRequestInfo.
+  // TODO(willchan): Investigate to see if we can remove the need to copy
+  // HttpRequestInfo.
+  // DISALLOW_COPY_AND_ASSIGN(HttpRequestHeaders);
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_HTTP_REQUEST_HEADERS_H_
diff --git a/net/http/http_request_headers_unittest.cc b/net/http/http_request_headers_unittest.cc
new file mode 100644
index 0000000..f3abfbe
--- /dev/null
+++ b/net/http/http_request_headers_unittest.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2010 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/http/http_request_headers.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+TEST(HttpRequestHeaders, HasHeader) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("Foo", "bar");
+  EXPECT_TRUE(headers.HasHeader("foo"));
+  EXPECT_TRUE(headers.HasHeader("Foo"));
+  EXPECT_FALSE(headers.HasHeader("Fo"));
+
+  const HttpRequestHeaders& headers_ref = headers;
+  EXPECT_TRUE(headers_ref.HasHeader("foo"));
+  EXPECT_TRUE(headers_ref.HasHeader("Foo"));
+  EXPECT_FALSE(headers_ref.HasHeader("Fo"));
+}
+
+TEST(HttpRequestHeaders, SetHeader) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("Foo", "bar");
+  EXPECT_EQ("Foo: bar\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, SetMultipleHeaders) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("Cookie-Monster", "Nom nom nom");
+  headers.SetHeader("Domo-Kun", "Loves Chrome");
+  EXPECT_EQ("Cookie-Monster: Nom nom nom\r\nDomo-Kun: Loves Chrome\r\n\r\n",
+            headers.ToString());
+}
+
+TEST(HttpRequestHeaders, SetHeaderTwice) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("Foo", "bar");
+  headers.SetHeader("Foo", "bar");
+  EXPECT_EQ("Foo: bar\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, SetHeaderTwiceCaseInsensitive) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("Foo", "bar");
+  headers.SetHeader("FoO", "Bar");
+  EXPECT_EQ("Foo: Bar\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, SetHeaderTwiceSamePrefix) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("FooBar", "smokes");
+  headers.SetHeader("Foo", "crack");
+  EXPECT_EQ("FooBar: smokes\r\nFoo: crack\r\n\r\n", headers.ToString());
+  const HttpRequestHeaders& headers_ref = headers;
+  EXPECT_EQ("FooBar: smokes\r\nFoo: crack\r\n\r\n", headers_ref.ToString());
+}
+
+TEST(HttpRequestHeaders, SetEmptyHeader) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("Foo", "Bar");
+  headers.SetHeader("Bar", "");
+  EXPECT_EQ("Foo: Bar\r\nBar:\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, RemoveHeader) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("Foo", "bar");
+  headers.RemoveHeader("Foo");
+  EXPECT_EQ("\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, RemoveHeaderMissingHeader) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("Foo", "bar");
+  headers.RemoveHeader("Bar");
+  EXPECT_EQ("Foo: bar\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, RemoveHeaderCaseInsensitive) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("Foo", "bar");
+  headers.SetHeader("All-Your-Base", "Belongs To Chrome");
+  headers.RemoveHeader("foo");
+  EXPECT_EQ("All-Your-Base: Belongs To Chrome\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, AddHeaderFromString) {
+  HttpRequestHeaders headers;
+  headers.AddHeaderFromString("Foo: bar");
+  EXPECT_EQ("Foo: bar\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, AddHeaderFromStringNoLeadingWhitespace) {
+  HttpRequestHeaders headers;
+  headers.AddHeaderFromString("Foo:bar");
+  EXPECT_EQ("Foo: bar\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, AddHeaderFromStringMoreLeadingWhitespace) {
+  HttpRequestHeaders headers;
+  headers.AddHeaderFromString("Foo: \t  \t  bar");
+  EXPECT_EQ("Foo: bar\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, AddHeaderFromStringTrailingWhitespace) {
+  HttpRequestHeaders headers;
+  headers.AddHeaderFromString("Foo: bar  \t  \t   ");
+  EXPECT_EQ("Foo: bar\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, AddHeaderFromStringLeadingTrailingWhitespace) {
+  HttpRequestHeaders headers;
+  headers.AddHeaderFromString("Foo: \t    bar\t       ");
+  EXPECT_EQ("Foo: bar\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, AddHeaderFromStringWithEmptyValue) {
+  HttpRequestHeaders headers;
+  headers.AddHeaderFromString("Foo:");
+  EXPECT_EQ("Foo:\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, AddHeaderFromStringWithWhitespaceValue) {
+  HttpRequestHeaders headers;
+  headers.AddHeaderFromString("Foo: ");
+  EXPECT_EQ("Foo:\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, MergeFrom) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("A", "A");
+  headers.SetHeader("B", "B");
+
+  HttpRequestHeaders headers2;
+  headers2.SetHeader("B", "b");
+  headers2.SetHeader("C", "c");
+  headers.MergeFrom(headers2);
+  EXPECT_EQ("A: A\r\nB: b\r\nC: c\r\n\r\n", headers.ToString());
+}
+
+TEST(HttpRequestHeaders, CopyFrom) {
+  HttpRequestHeaders headers;
+  headers.SetHeader("A", "A");
+  headers.SetHeader("B", "B");
+
+  HttpRequestHeaders headers2;
+  headers2.SetHeader("B", "b");
+  headers2.SetHeader("C", "c");
+  headers.CopyFrom(headers2);
+  EXPECT_EQ("B: b\r\nC: c\r\n\r\n", headers.ToString());
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/http/http_request_info.h b/net/http/http_request_info.h
index 7ada539..c36e21a 100644
--- a/net/http/http_request_info.h
+++ b/net/http/http_request_info.h
@@ -10,10 +10,11 @@
 #include "googleurl/src/gurl.h"
 #include "net/base/request_priority.h"
 #include "net/base/upload_data.h"
+#include "net/http/http_request_headers.h"
 
 namespace net {
 
-class HttpRequestInfo {
+struct HttpRequestInfo {
  public:
   HttpRequestInfo() : load_flags(0), priority(LOWEST) {
   }
@@ -27,12 +28,8 @@
   // The method to use (GET, POST, etc.).
   std::string method;
 
-  // The user agent string to use.  TODO(darin): we should just add this to
-  // extra_headers
-  std::string user_agent;
-
-  // Any extra request headers (\r\n-delimited).
-  std::string extra_headers;
+  // Any extra request headers (including User-Agent).
+  HttpRequestHeaders extra_headers;
 
   // Any upload data.
   scoped_refptr<UploadData> upload_data;
diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc
index 82272bb..08f397d 100644
--- a/net/http/http_response_headers.cc
+++ b/net/http/http_response_headers.cc
@@ -137,7 +137,7 @@
 
     // Locate the start of the next header.
     size_t k = i;
-    while (++k < parsed_.size() && parsed_[k].is_continuation());
+    while (++k < parsed_.size() && parsed_[k].is_continuation()) {}
     --k;
 
     std::string header_name(parsed_[i].name_begin, parsed_[i].name_end);
@@ -177,7 +177,7 @@
 
     // Locate the start of the next header.
     size_t k = i;
-    while (++k < new_parsed.size() && new_parsed[k].is_continuation());
+    while (++k < new_parsed.size() && new_parsed[k].is_continuation()) {}
     --k;
 
     const std::string::const_iterator& name_begin = new_parsed[i].name_begin;
@@ -208,7 +208,7 @@
 
     // Locate the start of the next header.
     size_t k = i;
-    while (++k < parsed_.size() && parsed_[k].is_continuation());
+    while (++k < parsed_.size() && parsed_[k].is_continuation()) {}
     --k;
 
     std::string name(parsed_[i].name_begin, parsed_[i].name_end);
@@ -274,8 +274,9 @@
       find(line_begin, raw_input.end(), '\0');
   // has_headers = true, if there is any data following the status line.
   // Used by ParseStatusLine() to decide if a HTTP/0.9 is really a HTTP/1.0.
-  bool has_headers = line_end != raw_input.end() &&
-      (line_end + 1) != raw_input.end() && *(line_end + 1) != '\0';
+  bool has_headers = (line_end != raw_input.end() &&
+                      (line_end + 1) != raw_input.end() &&
+                      *(line_end + 1) != '\0');
   ParseStatusLine(line_begin, line_end, has_headers);
 
   if (line_end == raw_input.end()) {
@@ -357,6 +358,30 @@
   output->push_back('\n');
 }
 
+void HttpResponseHeaders::GetRawHeaders(std::string* output) const {
+  if (!output)
+    return;
+  output->erase();
+  const char* headers_string = raw_headers().c_str();
+  size_t headers_length = raw_headers().length();
+  if (!headers_string)
+    return;
+  // The headers_string is a NULL-terminated status line, followed by NULL-
+  // terminated headers.
+  std::string raw_string = headers_string;
+  size_t current_length = strlen(headers_string) + 1;
+  while (headers_length > current_length) {
+    // Move to the next header, and append it.
+    headers_string += current_length;
+    headers_length -= current_length;
+    raw_string += "\n";
+    raw_string += headers_string;
+    // Get the next header location.
+    current_length = strlen(headers_string) + 1;
+  }
+  *output = raw_string;
+}
+
 bool HttpResponseHeaders::GetNormalizedHeader(const std::string& name,
                                               std::string* value) const {
   // If you hit this assertion, please use EnumerateHeader instead!
@@ -464,6 +489,10 @@
   return false;
 }
 
+bool HttpResponseHeaders::HasHeader(const std::string& name) const {
+  return FindHeader(0, name) != std::string::npos;
+}
+
 // Note: this implementation implicitly assumes that line_end points at a valid
 // sentinel character (such as '\0').
 // static
@@ -1115,9 +1144,9 @@
 
       // Obtain last-byte-pos.
       std::string::const_iterator last_byte_pos_begin =
-           byte_range_resp_spec.begin() + minus_position + 1;
+          byte_range_resp_spec.begin() + minus_position + 1;
       std::string::const_iterator last_byte_pos_end =
-           byte_range_resp_spec.end();
+          byte_range_resp_spec.end();
       HttpUtil::TrimLWS(&last_byte_pos_begin, &last_byte_pos_end);
 
       ok &= StringToInt64(
@@ -1146,8 +1175,8 @@
   if (LowerCaseEqualsASCII(instance_length_begin, instance_length_end, "*")) {
     return false;
   } else if (!StringToInt64(
-                 std::string(instance_length_begin, instance_length_end),
-                 instance_length)) {
+      std::string(instance_length_begin, instance_length_end),
+      instance_length)) {
     *instance_length = -1;
     return false;
   }
diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h
index f6656e3..30e43ea 100644
--- a/net/http/http_response_headers.h
+++ b/net/http/http_response_headers.h
@@ -99,6 +99,9 @@
   //
   void GetNormalizedHeaders(std::string* output) const;
 
+  // Gets the raw stored headers, in human-readable form.
+  void GetRawHeaders(std::string* output) const;
+
   // Fetch the "normalized" value of a single header, where all values for the
   // header name are separated by commas.  See the GetNormalizedHeaders for
   // format details.  Returns false if this header wasn't found.
@@ -154,6 +157,10 @@
   // Both name and value are compared case insensitively.
   bool HasHeaderValue(const std::string& name, const std::string& value) const;
 
+  // Returns true if the response contains the specified header.
+  // The name is compared case insensitively.
+  bool HasHeader(const std::string& name) const;
+
   // Get the mime type and charset values in lower case form from the headers.
   // Empty strings are returned if the values are not present.
   void GetMimeTypeAndCharset(std::string* mime_type,
diff --git a/net/http/http_response_headers_unittest.cc b/net/http/http_response_headers_unittest.cc
index bea4f36..18cd1c4 100644
--- a/net/http/http_response_headers_unittest.cc
+++ b/net/http/http_response_headers_unittest.cc
@@ -277,9 +277,9 @@
 
 TEST(HttpResponseHeadersTest, GetNormalizedHeader) {
   std::string headers =
-    "HTTP/1.1 200 OK\n"
-    "Cache-control: private\n"
-    "cache-Control: no-store\n";
+      "HTTP/1.1 200 OK\n"
+      "Cache-control: private\n"
+      "cache-Control: no-store\n";
   HeadersToRaw(&headers);
   scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers);
 
@@ -468,9 +468,9 @@
   // Ensure that commas in quoted strings are not regarded as value separators.
   // Ensure that whitespace following a value is trimmed properly
   std::string headers =
-    "HTTP/1.1 200 OK\n"
-    "Cache-control:private , no-cache=\"set-cookie,server\" \n"
-    "cache-Control: no-store\n";
+      "HTTP/1.1 200 OK\n"
+      "Cache-control:private , no-cache=\"set-cookie,server\" \n"
+      "cache-Control: no-store\n";
   HeadersToRaw(&headers);
   scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers);
 
@@ -489,9 +489,9 @@
   // Even though WWW-Authenticate has commas, it should not be treated as
   // coalesced values.
   std::string headers =
-    "HTTP/1.1 401 OK\n"
-    "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n"
-    "WWW-Authenticate:Basic realm=quatar\n";
+      "HTTP/1.1 401 OK\n"
+      "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n"
+      "WWW-Authenticate:Basic realm=quatar\n";
   HeadersToRaw(&headers);
   scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers);
 
@@ -508,9 +508,9 @@
   // The comma in a date valued header should not be treated as a
   // field-value separator
   std::string headers =
-    "HTTP/1.1 200 OK\n"
-    "Date: Tue, 07 Aug 2007 23:10:55 GMT\n"
-    "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n";
+      "HTTP/1.1 200 OK\n"
+      "Date: Tue, 07 Aug 2007 23:10:55 GMT\n"
+      "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n";
   HeadersToRaw(&headers);
   scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers);
 
@@ -524,130 +524,130 @@
 TEST(HttpResponseHeadersTest, GetMimeType) {
   const ContentTypeTestData tests[] = {
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html\n",
+      "Content-type: text/html\n",
       "text/html", true,
       "", false,
       "text/html" },
     // Multiple content-type headers should give us the last one.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html\n"
-        "Content-type: text/html\n",
+      "Content-type: text/html\n"
+      "Content-type: text/html\n",
       "text/html", true,
       "", false,
       "text/html, text/html" },
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/plain\n"
-        "Content-type: text/html\n"
-        "Content-type: text/plain\n"
-        "Content-type: text/html\n",
+      "Content-type: text/plain\n"
+      "Content-type: text/html\n"
+      "Content-type: text/plain\n"
+      "Content-type: text/html\n",
       "text/html", true,
       "", false,
       "text/plain, text/html, text/plain, text/html" },
     // Test charset parsing.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html\n"
-        "Content-type: text/html; charset=ISO-8859-1\n",
+      "Content-type: text/html\n"
+      "Content-type: text/html; charset=ISO-8859-1\n",
       "text/html", true,
       "iso-8859-1", true,
       "text/html, text/html; charset=ISO-8859-1" },
     // Test charset in double quotes.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html\n"
-        "Content-type: text/html; charset=\"ISO-8859-1\"\n",
+      "Content-type: text/html\n"
+      "Content-type: text/html; charset=\"ISO-8859-1\"\n",
       "text/html", true,
       "iso-8859-1", true,
       "text/html, text/html; charset=\"ISO-8859-1\"" },
     // If there are multiple matching content-type headers, we carry
     // over the charset value.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html;charset=utf-8\n"
-        "Content-type: text/html\n",
+      "Content-type: text/html;charset=utf-8\n"
+      "Content-type: text/html\n",
       "text/html", true,
       "utf-8", true,
       "text/html;charset=utf-8, text/html" },
     // Test single quotes.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html;charset='utf-8'\n"
-        "Content-type: text/html\n",
+      "Content-type: text/html;charset='utf-8'\n"
+      "Content-type: text/html\n",
       "text/html", true,
       "utf-8", true,
       "text/html;charset='utf-8', text/html" },
     // Last charset wins if matching content-type.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html;charset=utf-8\n"
-        "Content-type: text/html;charset=iso-8859-1\n",
+      "Content-type: text/html;charset=utf-8\n"
+      "Content-type: text/html;charset=iso-8859-1\n",
       "text/html", true,
       "iso-8859-1", true,
       "text/html;charset=utf-8, text/html;charset=iso-8859-1" },
     // Charset is ignored if the content types change.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/plain;charset=utf-8\n"
-        "Content-type: text/html\n",
+      "Content-type: text/plain;charset=utf-8\n"
+      "Content-type: text/html\n",
       "text/html", true,
       "", false,
       "text/plain;charset=utf-8, text/html" },
     // Empty content-type
     { "HTTP/1.1 200 OK\n"
-        "Content-type: \n",
+      "Content-type: \n",
       "", false,
       "", false,
       "" },
     // Emtpy charset
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html;charset=\n",
+      "Content-type: text/html;charset=\n",
       "text/html", true,
       "", false,
       "text/html;charset=" },
     // Multiple charsets, last one wins.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n",
+      "Content-type: text/html;charset=utf-8; charset=iso-8859-1\n",
       "text/html", true,
       "iso-8859-1", true,
       "text/html;charset=utf-8; charset=iso-8859-1" },
     // Multiple params.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n",
+      "Content-type: text/html; foo=utf-8; charset=iso-8859-1\n",
       "text/html", true,
       "iso-8859-1", true,
       "text/html; foo=utf-8; charset=iso-8859-1" },
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n",
+      "Content-type: text/html ; charset=utf-8 ; bar=iso-8859-1\n",
       "text/html", true,
       "utf-8", true,
       "text/html ; charset=utf-8 ; bar=iso-8859-1" },
     // Comma embeded in quotes.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html ; charset='utf-8,text/plain' ;\n",
+      "Content-type: text/html ; charset='utf-8,text/plain' ;\n",
       "text/html", true,
       "utf-8,text/plain", true,
       "text/html ; charset='utf-8,text/plain' ;" },
     // Charset with leading spaces.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html ; charset= 'utf-8' ;\n",
+      "Content-type: text/html ; charset= 'utf-8' ;\n",
       "text/html", true,
       "utf-8", true,
       "text/html ; charset= 'utf-8' ;" },
     // Media type comments in mime-type.
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html (html)\n",
+      "Content-type: text/html (html)\n",
       "text/html", true,
       "", false,
       "text/html (html)" },
     // Incomplete charset= param
     { "HTTP/1.1 200 OK\n"
-        "Content-type: text/html; char=\n",
+      "Content-type: text/html; char=\n",
       "text/html", true,
       "", false,
       "text/html; char=" },
     // Invalid media type: no slash
     { "HTTP/1.1 200 OK\n"
-        "Content-type: texthtml\n",
+      "Content-type: texthtml\n",
       "", false,
       "", false,
       "texthtml" },
     // Invalid media type: */*
     { "HTTP/1.1 200 OK\n"
-        "Content-type: */*\n",
+      "Content-type: */*\n",
       "", false,
       "", false,
       "*/*" },
@@ -1477,7 +1477,7 @@
         new HttpResponseHeaders(headers);
 
     EXPECT_EQ(tests[i].expected_result, parsed->HasStrongValidators()) <<
-      "Failed test case " << i;
+        "Failed test case " << i;
   }
 }
 
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc
index 1a548b1..0159caf 100644
--- a/net/http/http_response_info.cc
+++ b/net/http/http_response_info.cc
@@ -41,13 +41,26 @@
   // This bit is set if the response was received via SPDY.
   RESPONSE_INFO_WAS_SPDY = 1 << 13,
 
+  // This bit is set if the request has NPN negotiated.
+  RESPONSE_INFO_WAS_NPN = 1 << 14,
+
+  // This bit is set if the request was fetched via an explicit proxy.
+  RESPONSE_INFO_WAS_PROXY = 1 << 15,
+
+  // This bit is set if response could use alternate protocol. However, browser
+  // will ingore the alternate protocol if spdy is not enabled.
+  RESPONSE_INFO_WAS_ALTERNATE_PROTOCOL_AVAILABLE = 1 << 16,
+
   // TODO(darin): Add other bits to indicate alternate request methods.
   // For now, we don't support storing those.
 };
 
 HttpResponseInfo::HttpResponseInfo()
     : was_cached(false),
-      was_fetched_via_spdy(false) {
+      was_fetched_via_spdy(false),
+      was_npn_negotiated(false),
+      was_alternate_protocol_available(false),
+      was_fetched_via_proxy(false) {
 }
 
 HttpResponseInfo::~HttpResponseInfo() {
@@ -109,6 +122,13 @@
 
   was_fetched_via_spdy = (flags & RESPONSE_INFO_WAS_SPDY) != 0;
 
+  was_npn_negotiated = (flags & RESPONSE_INFO_WAS_NPN) != 0;
+
+  was_alternate_protocol_available =
+      (flags & RESPONSE_INFO_WAS_ALTERNATE_PROTOCOL_AVAILABLE) != 0;
+
+  was_fetched_via_proxy = (flags & RESPONSE_INFO_WAS_PROXY) != 0;
+
   *response_truncated = (flags & RESPONSE_INFO_TRUNCATED) ? true : false;
 
   return true;
@@ -130,6 +150,12 @@
     flags |= RESPONSE_INFO_TRUNCATED;
   if (was_fetched_via_spdy)
     flags |= RESPONSE_INFO_WAS_SPDY;
+  if (was_npn_negotiated)
+    flags |= RESPONSE_INFO_WAS_NPN;
+  if (was_alternate_protocol_available)
+    flags |= RESPONSE_INFO_WAS_ALTERNATE_PROTOCOL_AVAILABLE;
+  if (was_fetched_via_proxy)
+    flags |= RESPONSE_INFO_WAS_PROXY;
 
   pickle->WriteInt(flags);
   pickle->WriteInt64(request_time.ToInternalValue());
diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h
index a3e0123..52c3a0e 100644
--- a/net/http/http_response_info.h
+++ b/net/http/http_response_info.h
@@ -7,6 +7,7 @@
 
 #include "base/time.h"
 #include "net/base/auth.h"
+#include "net/base/io_buffer.h"
 #include "net/base/ssl_cert_request_info.h"
 #include "net/base/ssl_info.h"
 #include "net/http/http_response_headers.h"
@@ -27,13 +28,25 @@
   // request_time may corresponds to a time "far" in the past.  Note that
   // stale content (perhaps un-cacheable) may be fetched from cache subject to
   // the load flags specified on the request info.  For example, this is done
-  // when a user presses the back button to re-render pages, or at startup, when
-  // reloading previously visited pages (without going over the network).
+  // when a user presses the back button to re-render pages, or at startup,
+  // when reloading previously visited pages (without going over the network).
   bool was_cached;
 
   // True if the request was fetched over a SPDY channel.
   bool was_fetched_via_spdy;
 
+  // True if the npn was negotiated for this request.
+  bool was_npn_negotiated;
+
+  // True if response could use alternate protocol. However, browser
+  // will ingore the alternate protocol if spdy is not enabled.
+  bool was_alternate_protocol_available;
+
+  // True if the request was fetched via an explicit proxy.  The proxy could
+  // be any type of proxy, HTTP or SOCKS.  Note, we do not know if a
+  // transparent proxy may have been involved.
+  bool was_fetched_via_proxy;
+
   // The time at which the request was made that resulted in this response.
   // For cached responses, this is the last time the cache entry was validated.
   base::Time request_time;
@@ -61,6 +74,9 @@
   // The "Vary" header data for this response.
   HttpVaryData vary_data;
 
+  // Any metadata asociated with this resource's cached data.
+  scoped_refptr<IOBufferWithSize> metadata;
+
   // Initializes from the representation stored in the given pickle.
   bool InitFromPickle(const Pickle& pickle, bool* response_truncated);
 
diff --git a/net/http/http_stream.h b/net/http/http_stream.h
index 49651dd..de2a8d7 100644
--- a/net/http/http_stream.h
+++ b/net/http/http_stream.h
@@ -13,11 +13,11 @@
 #include <string>
 
 #include "base/basictypes.h"
-#include "net/socket/client_socket_handle.h"
+#include "net/base/completion_callback.h"
 
 namespace net {
 
-class HttpRequestInfo;
+struct HttpRequestInfo;
 class HttpResponseInfo;
 class IOBuffer;
 class UploadDataStream;
@@ -53,12 +53,14 @@
 
   // Reads response body data, up to |buf_len| bytes. |buf_len| should be a
   // reasonable size (<2MB). The number of bytes read is returned, or an
-  // error is returned upon failure.  ERR_CONNECTION_CLOSED is returned to
-  // indicate end-of-connection.  ERR_IO_PENDING is returned if the operation
-  // could not be completed synchronously, in which case the result will be
-  // passed to the callback when available. If the operation is not completed
-  // immediately, the socket acquires a reference to the provided buffer until
-  // the callback is invoked or the socket is destroyed.
+  // error is returned upon failure.  0 indicates that the request has been
+  // fully satisfied and there is no more data to read.
+  // ERR_CONNECTION_CLOSED is returned when the connection has been closed
+  // prematurely.  ERR_IO_PENDING is returned if the operation could not be
+  // completed synchronously, in which case the result will be passed to the
+  // callback when available. If the operation is not completed immediately,
+  // the socket acquires a reference to the provided buffer until the callback
+  // is invoked or the socket is destroyed.
   virtual int ReadResponseBody(IOBuffer* buf, int buf_len,
                                CompletionCallback* callback) = 0;
 
diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc
index abbf112..1426957 100644
--- a/net/http/http_stream_parser.cc
+++ b/net/http/http_stream_parser.cc
@@ -1,11 +1,11 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/http/http_stream_parser.h"
 
 #include "base/compiler_specific.h"
-#include "base/trace_event.h"
+#include "base/histogram.h"
 #include "net/base/io_buffer.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_response_headers.h"
@@ -15,7 +15,7 @@
 
 HttpStreamParser::HttpStreamParser(ClientSocketHandle* connection,
                                    GrowableIOBuffer* read_buffer,
-                                   LoadLog* load_log)
+                                   const BoundNetLog& net_log)
     : io_state_(STATE_NONE),
       request_(NULL),
       request_headers_(NULL),
@@ -30,12 +30,14 @@
       user_read_buf_len_(0),
       user_callback_(NULL),
       connection_(connection),
-      load_log_(load_log),
+      net_log_(net_log),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           io_callback_(this, &HttpStreamParser::OnIOComplete)) {
   DCHECK_EQ(0, read_buffer->offset());
 }
 
+HttpStreamParser::~HttpStreamParser() {}
+
 int HttpStreamParser::SendRequest(const HttpRequestInfo* request,
                                   const std::string& headers,
                                   UploadDataStream* request_body,
@@ -90,7 +92,7 @@
 }
 
 int HttpStreamParser::ReadResponseBody(IOBuffer* buf, int buf_len,
-                                      CompletionCallback* callback) {
+                                       CompletionCallback* callback) {
   DCHECK(io_state_ == STATE_BODY_PENDING || io_state_ == STATE_DONE);
   DCHECK(!user_callback_);
   DCHECK(callback);
@@ -127,49 +129,39 @@
   do {
     switch (io_state_) {
       case STATE_SENDING_HEADERS:
-        TRACE_EVENT_BEGIN("http.write_headers", request_, request_->url.spec());
         if (result < 0)
           can_do_more = false;
         else
           result = DoSendHeaders(result);
-        TRACE_EVENT_END("http.write_headers", request_, request_->url.spec());
         break;
       case STATE_SENDING_BODY:
-        TRACE_EVENT_BEGIN("http.write_body", request_, request_->url.spec());
         if (result < 0)
           can_do_more = false;
         else
           result = DoSendBody(result);
-        TRACE_EVENT_END("http.write_body", request_, request_->url.spec());
         break;
       case STATE_REQUEST_SENT:
         DCHECK(result != ERR_IO_PENDING);
         can_do_more = false;
         break;
       case STATE_READ_HEADERS:
-        TRACE_EVENT_BEGIN("http.read_headers", request_, request_->url.spec());
-        LoadLog::BeginEvent(load_log_,
-                            LoadLog::TYPE_HTTP_STREAM_PARSER_READ_HEADERS);
+        net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_PARSER_READ_HEADERS, NULL);
         result = DoReadHeaders();
         break;
       case STATE_READ_HEADERS_COMPLETE:
         result = DoReadHeadersComplete(result);
-        LoadLog::EndEvent(load_log_,
-                          LoadLog::TYPE_HTTP_STREAM_PARSER_READ_HEADERS);
-        TRACE_EVENT_END("http.read_headers", request_, request_->url.spec());
+        net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_PARSER_READ_HEADERS, NULL);
         break;
       case STATE_BODY_PENDING:
         DCHECK(result != ERR_IO_PENDING);
         can_do_more = false;
         break;
       case STATE_READ_BODY:
-        TRACE_EVENT_BEGIN("http.read_body", request_, request_->url.spec());
         result = DoReadBody();
         // DoReadBodyComplete handles error conditions.
         break;
       case STATE_READ_BODY_COMPLETE:
         result = DoReadBodyComplete(result);
-        TRACE_EVENT_END("http.read_body", request_, request_->url.spec());
         break;
       case STATE_DONE:
         DCHECK(result != ERR_IO_PENDING);
@@ -187,15 +179,43 @@
 
 int HttpStreamParser::DoSendHeaders(int result) {
   request_headers_->DidConsume(result);
-
-  if (request_headers_->BytesRemaining() > 0) {
+  int bytes_remaining = request_headers_->BytesRemaining();
+  if (bytes_remaining > 0) {
     // Record our best estimate of the 'request time' as the time when we send
     // out the first bytes of the request headers.
-    if (request_headers_->BytesRemaining() == request_headers_->size()) {
+    if (bytes_remaining == request_headers_->size()) {
       response_->request_time = base::Time::Now();
+
+      // We'll record the count of uncoalesced packets IFF coalescing will help,
+      // and otherwise we'll use an enum to tell why it won't help.
+      enum COALESCE_POTENTIAL {
+        NO_ADVANTAGE = 0,   // Coalescing won't reduce packet count.
+        HEADER_ONLY = 1,    // There is only a header packet (can't coalesce).
+        COALESCE_POTENTIAL_MAX = 30 // Various cases of coalasced savings.
+      };
+      size_t coalesce = HEADER_ONLY;
+      if (request_body_ != NULL) {
+        const size_t kBytesPerPacket = 1430;
+        uint64 body_packets = (request_body_->size() + kBytesPerPacket - 1) /
+                              kBytesPerPacket;
+        uint64 header_packets = (bytes_remaining + kBytesPerPacket - 1) /
+                                kBytesPerPacket;
+        uint64 coalesced_packets = (request_body_->size() + bytes_remaining +
+                                    kBytesPerPacket - 1) / kBytesPerPacket;
+        if (coalesced_packets < header_packets + body_packets) {
+          if (coalesced_packets > COALESCE_POTENTIAL_MAX)
+            coalesce = COALESCE_POTENTIAL_MAX;
+          else
+            coalesce = static_cast<size_t>(header_packets + body_packets);
+        } else {
+          coalesce = NO_ADVANTAGE;
+        }
+      }
+      UMA_HISTOGRAM_ENUMERATION("Net.CoalescePotential", coalesce,
+                                COALESCE_POTENTIAL_MAX);
     }
     result = connection_->socket()->Write(request_headers_,
-                                          request_headers_->BytesRemaining(),
+                                          bytes_remaining,
                                           &io_callback_);
   } else if (request_body_ != NULL && request_body_->size()) {
     io_state_ = STATE_SENDING_BODY;
@@ -210,7 +230,7 @@
   if (result > 0)
     request_body_->DidConsume(result);
 
-  if (request_body_->position() < request_body_->size()) {
+  if (!request_body_->eof()) {
     int buf_len = static_cast<int>(request_body_->buf_len());
     result = connection_->socket()->Write(request_body_->buf(), buf_len,
                                           &io_callback_);
@@ -310,7 +330,7 @@
         io_state_ = STATE_DONE;
         int extra_bytes = read_buf_->offset() - read_buf_unused_offset_;
         if (extra_bytes) {
-          CHECK(extra_bytes > 0);
+          CHECK_GT(extra_bytes, 0);
           memmove(read_buf_->StartOfBuffer(),
                   read_buf_->StartOfBuffer() + read_buf_unused_offset_,
                   extra_bytes);
@@ -331,7 +351,7 @@
   if (read_buf_->offset()) {
     int available = read_buf_->offset() - read_buf_unused_offset_;
     if (available) {
-      CHECK(available > 0);
+      CHECK_GT(available, 0);
       int bytes_from_buffer = std::min(available, user_read_buf_len_);
       memcpy(user_read_buf_->data(),
              read_buf_->StartOfBuffer() + read_buf_unused_offset_,
@@ -358,7 +378,11 @@
 }
 
 int HttpStreamParser::DoReadBodyComplete(int result) {
-  if (result == 0)
+  // If we didn't get a content-length and aren't using a chunked encoding,
+  // the only way to signal the end of a stream is to close the connection,
+  // so we don't treat that as an error, though in some cases we may not
+  // have completely received the resource.
+  if (result == 0 && !IsResponseBodyComplete() && CanFindEndOfResponse())
     result = ERR_CONNECTION_CLOSED;
 
   // Filter incoming data if appropriate.  FilterBuf may return an error.
@@ -375,7 +399,7 @@
   if (result > 0)
     response_body_read_ += result;
 
-  if (result < 0 || IsResponseBodyComplete()) {
+  if (result <= 0 || IsResponseBodyComplete()) {
     io_state_ = STATE_DONE;
 
     // Save the overflow data, which can be in two places.  There may be
@@ -396,7 +420,7 @@
       }
     }
 
-    CHECK(save_amount + additional_save_amount <= kMaxBufSize);
+    CHECK_LE(save_amount + additional_save_amount, kMaxBufSize);
     if (read_buf_->capacity() < save_amount + additional_save_amount) {
       read_buf_->SetCapacity(save_amount + additional_save_amount);
     }
diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h
index c47d09e..44cc9e8 100644
--- a/net/http/http_stream_parser.h
+++ b/net/http/http_stream_parser.h
@@ -9,7 +9,7 @@
 
 #include "base/basictypes.h"
 #include "net/base/io_buffer.h"
-#include "net/base/load_log.h"
+#include "net/base/net_log.h"
 #include "net/base/upload_data_stream.h"
 #include "net/http/http_chunked_decoder.h"
 #include "net/http/http_response_info.h"
@@ -18,7 +18,7 @@
 namespace net {
 
 class ClientSocketHandle;
-class HttpRequestInfo;
+struct HttpRequestInfo;
 
 class HttpStreamParser {
  public:
@@ -29,8 +29,8 @@
   // have its capacity changed.
   HttpStreamParser(ClientSocketHandle* connection,
                    GrowableIOBuffer* read_buffer,
-                   LoadLog* load_log);
-  ~HttpStreamParser() {}
+                   const BoundNetLog& net_log);
+  ~HttpStreamParser();
 
   // These functions implement the interface described in HttpStream with
   // some additional functionality
@@ -162,7 +162,7 @@
   // The underlying socket.
   ClientSocketHandle* const connection_;
 
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
 
   // Callback to be used when doing IO.
   CompletionCallbackImpl<HttpStreamParser> io_callback_;
diff --git a/net/http/http_transaction.h b/net/http/http_transaction.h
index 9dfd658..103f8f6 100644
--- a/net/http/http_transaction.h
+++ b/net/http/http_transaction.h
@@ -5,15 +5,17 @@
 #ifndef NET_HTTP_HTTP_TRANSACTION_H_
 #define NET_HTTP_HTTP_TRANSACTION_H_
 
+#include <string>
+
 #include "net/base/completion_callback.h"
 #include "net/base/load_states.h"
 
 namespace net {
 
-class HttpRequestInfo;
+class BoundNetLog;
+struct HttpRequestInfo;
 class HttpResponseInfo;
 class IOBuffer;
-class LoadLog;
 class X509Certificate;
 
 // Represents a single HTTP transaction (i.e., a single request/response pair).
@@ -37,10 +39,10 @@
   //
   // NOTE: The transaction is not responsible for deleting the callback object.
   //
-  // Profiling information for the request is saved to |load_log| if non-NULL.
+  // Profiling information for the request is saved to |net_log| if non-NULL.
   virtual int Start(const HttpRequestInfo* request_info,
                     CompletionCallback* callback,
-                    LoadLog* load_log) = 0;
+                    const BoundNetLog& net_log) = 0;
 
   // Restarts the HTTP transaction, ignoring the last error.  This call can
   // only be made after a call to Start (or RestartIgnoringLastError) failed.
@@ -89,6 +91,9 @@
   virtual int Read(IOBuffer* buf, int buf_len,
                    CompletionCallback* callback) = 0;
 
+  // Stops further caching of this request by the HTTP cache, if there is any.
+  virtual void StopCaching() = 0;
+
   // Returns the response info for this transaction or NULL if the response
   // info is not available.
   virtual const HttpResponseInfo* GetResponseInfo() const = 0;
diff --git a/net/http/http_transaction_unittest.cc b/net/http/http_transaction_unittest.cc
index 01e6ea5..4d9daa5 100644
--- a/net/http/http_transaction_unittest.cc
+++ b/net/http/http_transaction_unittest.cc
@@ -105,7 +105,7 @@
 };
 
 typedef base::hash_map<std::string, const MockTransaction*>
-    MockTransactionMap;
+MockTransactionMap;
 static MockTransactionMap mock_transactions;
 
 void AddMockTransaction(const MockTransaction* trans) {
diff --git a/net/http/http_transaction_unittest.h b/net/http/http_transaction_unittest.h
index cffde7a..e550903 100644
--- a/net/http/http_transaction_unittest.h
+++ b/net/http/http_transaction_unittest.h
@@ -10,6 +10,7 @@
 #include <algorithm>
 #include <string>
 
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/message_loop.h"
 #include "base/string_util.h"
@@ -34,9 +35,9 @@
   TEST_MODE_SYNC_CACHE_START = 1 << 2,
   TEST_MODE_SYNC_CACHE_READ  = 1 << 3,
   TEST_MODE_SYNC_CACHE_WRITE  = 1 << 4,
-  TEST_MODE_SYNC_ALL = TEST_MODE_SYNC_NET_START | TEST_MODE_SYNC_NET_READ |
-                       TEST_MODE_SYNC_CACHE_START | TEST_MODE_SYNC_CACHE_READ |
-                       TEST_MODE_SYNC_CACHE_WRITE
+  TEST_MODE_SYNC_ALL = (TEST_MODE_SYNC_NET_START | TEST_MODE_SYNC_NET_READ |
+                        TEST_MODE_SYNC_CACHE_START | TEST_MODE_SYNC_CACHE_READ |
+                        TEST_MODE_SYNC_CACHE_WRITE)
 };
 
 typedef void (*MockTransactionHandler)(const net::HttpRequestInfo* request,
@@ -96,7 +97,7 @@
   explicit MockHttpRequest(const MockTransaction& t) {
     url = GURL(t.url);
     method = t.method;
-    extra_headers = t.request_headers;
+    extra_headers.AddHeadersFromString(t.request_headers);
     load_flags = t.load_flags;
   }
 };
@@ -118,9 +119,10 @@
   ~TestTransactionConsumer() {
   }
 
-  void Start(const net::HttpRequestInfo* request, net::LoadLog* load_log) {
+  void Start(const net::HttpRequestInfo* request,
+             const net::BoundNetLog& net_log) {
     state_ = STARTING;
-    int result = trans_->Start(request, this, load_log);
+    int result = trans_->Start(request, this, net_log);
     if (result != net::ERR_IO_PENDING)
       DidStart(result);
   }
@@ -211,7 +213,7 @@
 
   virtual int Start(const net::HttpRequestInfo* request,
                     net::CompletionCallback* callback,
-                    net::LoadLog* load_log) {
+                    const net::BoundNetLog& net_log) {
     const MockTransaction* t = FindMockTransaction(request->url);
     if (!t)
       return net::ERR_FAILED;
@@ -282,12 +284,15 @@
     return net::ERR_IO_PENDING;
   }
 
+  virtual void StopCaching() {}
+
   virtual const net::HttpResponseInfo* GetResponseInfo() const {
     return &response_;
   }
 
   virtual net::LoadState GetLoadState() const {
-    NOTREACHED() << "define some mock state transitions";
+    if (data_cursor_)
+      return net::LOAD_STATE_READING_RESPONSE;
     return net::LOAD_STATE_IDLE;
   }
 
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index 5ea46d0..8168102 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -112,15 +112,16 @@
       size_t cur_param_end =
           FindDelimiter(content_type_str, cur_param_start, ';');
 
-      size_t param_name_start = content_type_str.find_first_not_of(HTTP_LWS,
-          cur_param_start);
+      size_t param_name_start = content_type_str.find_first_not_of(
+          HTTP_LWS, cur_param_start);
       param_name_start = std::min(param_name_start, cur_param_end);
 
       static const char charset_str[] = "charset=";
-      size_t charset_end_offset = std::min(param_name_start +
-          sizeof(charset_str) - 1, cur_param_end);
-      if (LowerCaseEqualsASCII(content_type_str.begin() + param_name_start,
-          content_type_str.begin() + charset_end_offset, charset_str)) {
+      size_t charset_end_offset = std::min(
+          param_name_start + sizeof(charset_str) - 1, cur_param_end);
+      if (LowerCaseEqualsASCII(
+              content_type_str.begin() + param_name_start,
+              content_type_str.begin() + charset_end_offset, charset_str)) {
         charset_val = param_name_start + sizeof(charset_str) - 1;
         charset_end = cur_param_end;
         type_has_charset = true;
@@ -160,9 +161,9 @@
       content_type_str.find_first_of('/') != string::npos) {
     // Common case here is that mime_type is empty
     bool eq = !mime_type->empty() &&
-        LowerCaseEqualsASCII(content_type_str.begin() + type_val,
-                             content_type_str.begin() + type_end,
-                             mime_type->data());
+              LowerCaseEqualsASCII(content_type_str.begin() + type_val,
+                                   content_type_str.begin() + type_end,
+                                   mime_type->data());
     if (!eq) {
       mime_type->assign(content_type_str.begin() + type_val,
                         content_type_str.begin() + type_end);
@@ -202,6 +203,12 @@
   if (ranges_specifier.empty())
     return false;
 
+  return ParseRangeHeader(ranges_specifier, ranges);
+}
+
+// static
+bool HttpUtil::ParseRangeHeader(const std::string& ranges_specifier,
+                                std::vector<HttpByteRange>* ranges) {
   size_t equal_char_offset = ranges_specifier.find('=');
   if (equal_char_offset == std::string::npos)
     return false;
@@ -236,11 +243,11 @@
     HttpByteRange range;
     // Try to obtain first-byte-pos.
     if (!first_byte_pos.empty()) {
-        int64 first_byte_position = -1;
-        if (!StringToInt64(first_byte_pos, &first_byte_position))
-          return false;
-        range.set_first_byte_position(first_byte_position);
-      }
+      int64 first_byte_position = -1;
+      if (!StringToInt64(first_byte_pos, &first_byte_position))
+        return false;
+      range.set_first_byte_position(first_byte_position);
+    }
 
     std::string::const_iterator last_byte_pos_begin =
         byte_range_set_iterator.value_begin() + minus_char_offset + 1;
diff --git a/net/http/http_util.h b/net/http/http_util.h
index c630cfe..447e490 100644
--- a/net/http/http_util.h
+++ b/net/http/http_util.h
@@ -19,14 +19,14 @@
 
 class HttpUtil {
  public:
-   // Returns the absolute path of the URL, to be used for the http request.
-   // The absolute path starts with a '/' and may contain a query.
-   static std::string PathForRequest(const GURL& url);
+  // Returns the absolute path of the URL, to be used for the http request.
+  // The absolute path starts with a '/' and may contain a query.
+  static std::string PathForRequest(const GURL& url);
 
-   // Returns the absolute URL, to be used for the http request. This url is
-   // made up of the protocol, host, [port], path, [query]. Everything else
-   // is stripped (username, password, reference).
-   static std::string SpecForRequest(const GURL& url);
+  // Returns the absolute URL, to be used for the http request. This url is
+  // made up of the protocol, host, [port], path, [query]. Everything else
+  // is stripped (username, password, reference).
+  static std::string SpecForRequest(const GURL& url);
 
   // Locates the next occurance of delimiter in line, skipping over quoted
   // strings (e.g., commas will not be treated as delimiters if they appear
@@ -54,6 +54,11 @@
   static bool ParseRanges(const std::string& headers,
                           std::vector<HttpByteRange>* ranges);
 
+  // Same thing as ParseRanges except the Range header is known and its value
+  // is directly passed in, rather than requiring searching through a string.
+  static bool ParseRangeHeader(const std::string& range_specifier,
+                               std::vector<HttpByteRange>* ranges);
+
   // Scans the '\r\n'-delimited headers for the given header name.  Returns
   // true if a match is found.  Input is assumed to be well-formed.
   // TODO(darin): kill this
diff --git a/net/http/http_util_unittest.cc b/net/http/http_util_unittest.cc
index bdae329..0c018a2 100644
--- a/net/http/http_util_unittest.cc
+++ b/net/http/http_util_unittest.cc
@@ -36,21 +36,21 @@
 
 TEST(HttpUtilTest, StripHeaders) {
   static const char* headers =
-    "Origin: origin\r\n"
-    "Content-Type: text/plain\r\n"
-    "Cookies: foo1\r\n"
-    "Custom: baz\r\n"
-    "COOKIES: foo2\r\n"
-    "Server: Apache\r\n"
-    "OrIGin: origin2\r\n";
+      "Origin: origin\r\n"
+      "Content-Type: text/plain\r\n"
+      "Cookies: foo1\r\n"
+      "Custom: baz\r\n"
+      "COOKIES: foo2\r\n"
+      "Server: Apache\r\n"
+      "OrIGin: origin2\r\n";
 
   static const char* header_names[] = {
     "origin", "content-type", "cookies"
   };
 
   static const char* expected_stripped_headers =
-    "Custom: baz\r\n"
-    "Server: Apache\r\n";
+      "Custom: baz\r\n"
+      "Server: Apache\r\n";
 
   EXPECT_EQ(expected_stripped_headers,
             HttpUtil::StripHeaders(headers, header_names,
diff --git a/net/http/http_vary_data.cc b/net/http/http_vary_data.cc
index fa7a325..f5c7514 100644
--- a/net/http/http_vary_data.cc
+++ b/net/http/http_vary_data.cc
@@ -8,6 +8,7 @@
 
 #include "base/pickle.h"
 #include "base/string_util.h"
+#include "net/http/http_request_headers.h"
 #include "net/http/http_request_info.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
@@ -97,33 +98,18 @@
     const HttpRequestInfo& request_info,
     const std::string& request_header) {
   // Some special cases:
-  if (LowerCaseEqualsASCII(request_header, "referer"))
+  if (!base::strcasecmp(request_header.c_str(), HttpRequestHeaders::kReferer))
     return request_info.referrer.spec();
-  if (LowerCaseEqualsASCII(request_header, "user-agent"))
-    return request_info.user_agent;
-
-  std::string result;
-
-  // Check extra headers:
-  HttpUtil::HeadersIterator it(request_info.extra_headers.begin(),
-                               request_info.extra_headers.end(),
-                               "\r\n");
-  while (it.GetNext()) {
-    size_t name_len = it.name_end() - it.name_begin();
-    if (request_header.size() == name_len &&
-        std::equal(it.name_begin(), it.name_end(), request_header.begin(),
-                   CaseInsensitiveCompare<char>())) {
-      if (!result.empty())
-        result.append(1, ',');
-      result.append(it.values());
-    }
-  }
 
   // Unfortunately, we do not have access to all of the request headers at this
   // point.  Most notably, we do not have access to an Authorization header if
   // one will be added to the request.
 
-  return result;
+  std::string result;
+  if (request_info.extra_headers.GetHeader(request_header, &result))
+    return result;
+
+  return "";
 }
 
 // static
diff --git a/net/http/http_vary_data.h b/net/http/http_vary_data.h
index 360799d..98b94fa 100644
--- a/net/http/http_vary_data.h
+++ b/net/http/http_vary_data.h
@@ -11,7 +11,7 @@
 
 namespace net {
 
-class HttpRequestInfo;
+struct HttpRequestInfo;
 class HttpResponseHeaders;
 
 // Used to implement the HTTP/1.1 Vary header.  This class contains a MD5 hash
diff --git a/net/http/http_vary_data_unittest.cc b/net/http/http_vary_data_unittest.cc
index 7b2bd16..38e4e32 100644
--- a/net/http/http_vary_data_unittest.cc
+++ b/net/http/http_vary_data_unittest.cc
@@ -23,7 +23,8 @@
     std::replace(temp.begin(), temp.end(), '\n', '\0');
     response = new net::HttpResponseHeaders(temp);
 
-    request.extra_headers = request_headers;
+    request.extra_headers.Clear();
+    request.extra_headers.AddHeadersFromString(request_headers);
   }
 };
 
@@ -54,13 +55,13 @@
 
   // Init to something valid.
   TestTransaction t1;
-  t1.Init("Foo: 1\nbar: 23", "HTTP/1.1 200 OK\nVary: foo, bar\n\n");
+  t1.Init("Foo: 1\r\nbar: 23", "HTTP/1.1 200 OK\nVary: foo, bar\n\n");
   EXPECT_TRUE(v.Init(t1.request, *t1.response));
   EXPECT_TRUE(v.is_valid());
 
   // Now overwrite by initializing to something invalid.
   TestTransaction t2;
-  t2.Init("Foo: 1\nbar: 23", "HTTP/1.1 200 OK\nVary: *\n\n");
+  t2.Init("Foo: 1\r\nbar: 23", "HTTP/1.1 200 OK\nVary: *\n\n");
   EXPECT_FALSE(v.Init(t2.request, *t2.response));
   EXPECT_FALSE(v.is_valid());
 }
@@ -80,10 +81,10 @@
 
 TEST(HttpVaryDataTest, DoesVary2) {
   TestTransaction a;
-  a.Init("Foo: 1\nbar: 23", "HTTP/1.1 200 OK\nVary: foo, bar\n\n");
+  a.Init("Foo: 1\r\nbar: 23", "HTTP/1.1 200 OK\nVary: foo, bar\n\n");
 
   TestTransaction b;
-  b.Init("Foo: 12\nbar: 3", "HTTP/1.1 200 OK\nVary: foo, bar\n\n");
+  b.Init("Foo: 12\r\nbar: 3", "HTTP/1.1 200 OK\nVary: foo, bar\n\n");
 
   net::HttpVaryData v;
   EXPECT_TRUE(v.Init(a.request, *a.response));
@@ -106,10 +107,10 @@
 
 TEST(HttpVaryDataTest, DoesntVary2) {
   TestTransaction a;
-  a.Init("Foo: 1\nbAr: 2", "HTTP/1.1 200 OK\nVary: foo, bar\n\n");
+  a.Init("Foo: 1\r\nbAr: 2", "HTTP/1.1 200 OK\nVary: foo, bar\n\n");
 
   TestTransaction b;
-  b.Init("Foo: 1\nbaR: 2", "HTTP/1.1 200 OK\nVary: foo\nVary: bar\n\n");
+  b.Init("Foo: 1\r\nbaR: 2", "HTTP/1.1 200 OK\nVary: foo\nVary: bar\n\n");
 
   net::HttpVaryData v;
   EXPECT_TRUE(v.Init(a.request, *a.response));
diff --git a/net/http/mock_gssapi_library_posix.cc b/net/http/mock_gssapi_library_posix.cc
new file mode 100644
index 0000000..fed2ccd
--- /dev/null
+++ b/net/http/mock_gssapi_library_posix.cc
@@ -0,0 +1,468 @@
+// Copyright (c) 2010 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/http/mock_gssapi_library_posix.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "net/third_party/gssapi/gssapi.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace test {
+
+struct GssNameMockImpl {
+  std::string name;
+  gss_OID_desc name_type;
+};
+
+}  // namespace test
+
+namespace {
+
+// gss_OID helpers.
+// NOTE: gss_OID's do not own the data they point to, which should be static.
+void ClearOid(gss_OID dest) {
+  if (!dest)
+    return;
+  dest->length = 0;
+  dest->elements = NULL;
+}
+
+void SetOid(gss_OID dest, const void* src, size_t length) {
+  if (!dest)
+    return;
+  ClearOid(dest);
+  if (!src)
+    return;
+  dest->length = length;
+  if (length)
+    dest->elements = const_cast<void*>(src);
+}
+
+void CopyOid(gss_OID dest, const gss_OID_desc* src) {
+  if (!dest)
+    return;
+  ClearOid(dest);
+  if (!src)
+    return;
+  SetOid(dest, src->elements, src->length);
+}
+
+std::string OidToString(const gss_OID src) {
+  std::string dest;
+  if (!src)
+    return dest;
+  const char* string = reinterpret_cast<char*>(src->elements);
+  dest.assign(string, src->length);
+  return dest;
+}
+
+void OidFromString(const std::string& src, gss_OID dest) {
+  if (!dest)
+    return;
+  SetOid(dest, src.c_str(), src.length());
+}
+
+// gss_buffer_t helpers.
+void ClearBuffer(gss_buffer_t dest) {
+  if (!dest)
+    return;
+  dest->length = 0;
+  delete [] reinterpret_cast<char*>(dest->value);
+  dest->value = NULL;
+}
+
+void SetBuffer(gss_buffer_t dest, const void* src, size_t length) {
+  if (!dest)
+    return;
+  ClearBuffer(dest);
+  if (!src)
+    return;
+  dest->length = length;
+  if (length) {
+    dest->value = new char[length];
+    memcpy(dest->value, src, length);
+  }
+}
+
+void CopyBuffer(gss_buffer_t dest, const gss_buffer_t src) {
+  if (!dest)
+    return;
+  ClearBuffer(dest);
+  if (!src)
+    return;
+  SetBuffer(dest, src->value, src->length);
+}
+
+std::string BufferToString(const gss_buffer_t src) {
+  std::string dest;
+  if (!src)
+    return dest;
+  const char* string = reinterpret_cast<char*>(src->value);
+  dest.assign(string, src->length);
+  return dest;
+}
+
+void BufferFromString(const std::string& src, gss_buffer_t dest) {
+  if (!dest)
+    return;
+  SetBuffer(dest, src.c_str(), src.length());
+}
+
+// gss_name_t helpers.
+void ClearName(gss_name_t dest) {
+  if (!dest)
+    return;
+  test::GssNameMockImpl* name = reinterpret_cast<test::GssNameMockImpl*>(dest);
+  name->name.clear();
+  ClearOid(&name->name_type);
+}
+
+void SetName(gss_name_t dest, const void* src, size_t length) {
+  if (!dest)
+    return;
+  ClearName(dest);
+  if (!src)
+    return;
+  test::GssNameMockImpl* name = reinterpret_cast<test::GssNameMockImpl*>(dest);
+  name->name.assign(reinterpret_cast<const char*>(src), length);
+}
+
+void CopyName(gss_name_t dest, const gss_name_t src) {
+  if (!dest)
+    return;
+  ClearName(dest);
+  if (!src)
+    return;
+  test::GssNameMockImpl* name_dst =
+      reinterpret_cast<test::GssNameMockImpl*>(dest);
+  test::GssNameMockImpl* name_src =
+      reinterpret_cast<test::GssNameMockImpl*>(dest);
+  name_dst->name = name_src->name;
+  CopyOid(&name_dst->name_type, &name_src->name_type);
+}
+
+std::string NameToString(const gss_name_t& src) {
+  std::string dest;
+  if (!src)
+    return dest;
+  test::GssNameMockImpl* string =
+      reinterpret_cast<test::GssNameMockImpl*>(src);
+  dest = string->name;
+  return dest;
+}
+
+void NameFromString(const std::string& src, gss_name_t dest) {
+  if (!dest)
+    return;
+  SetName(dest, src.c_str(), src.length());
+}
+
+}  // namespace
+
+namespace test {
+
+GssContextMockImpl::GssContextMockImpl()
+  : lifetime_rec(0),
+    ctx_flags(0),
+    locally_initiated(0),
+    open(0) {
+  ClearOid(&mech_type);
+}
+
+GssContextMockImpl::GssContextMockImpl(const GssContextMockImpl& other)
+  : src_name(other.src_name),
+    targ_name(other.targ_name),
+    lifetime_rec(other.lifetime_rec),
+    ctx_flags(other.ctx_flags),
+    locally_initiated(other.locally_initiated),
+    open(other.open) {
+  CopyOid(&mech_type, &other.mech_type);
+}
+
+GssContextMockImpl::GssContextMockImpl(const char* src_name_in,
+                                       const char* targ_name_in,
+                                       OM_uint32 lifetime_rec_in,
+                                       const gss_OID_desc& mech_type_in,
+                                       OM_uint32 ctx_flags_in,
+                                       int locally_initiated_in,
+                                       int open_in)
+    : src_name(src_name_in ? src_name_in : ""),
+      targ_name(targ_name_in ? targ_name_in : ""),
+      lifetime_rec(lifetime_rec_in),
+      ctx_flags(ctx_flags_in),
+      locally_initiated(locally_initiated_in),
+      open(open_in) {
+  CopyOid(&mech_type, &mech_type_in);
+}
+
+GssContextMockImpl::~GssContextMockImpl() {
+  ClearOid(&mech_type);
+}
+
+void GssContextMockImpl::Assign(
+    const GssContextMockImpl& other) {
+  if (&other == this)
+    return;
+  src_name = other.src_name;
+  targ_name = other.targ_name;
+  lifetime_rec = other.lifetime_rec;
+  CopyOid(&mech_type, &other.mech_type);
+  ctx_flags = other.ctx_flags;
+  locally_initiated = other.locally_initiated;
+  open = other.open;
+}
+
+MockGSSAPILibrary::MockGSSAPILibrary() {
+}
+
+MockGSSAPILibrary::~MockGSSAPILibrary() {
+}
+
+bool MockGSSAPILibrary::Init() {
+  return true;
+}
+
+// These methods match the ones in the GSSAPI library.
+OM_uint32 MockGSSAPILibrary::import_name(
+      OM_uint32* minor_status,
+      const gss_buffer_t input_name_buffer,
+      const gss_OID input_name_type,
+      gss_name_t* output_name) {
+  if (minor_status)
+    *minor_status = 0;
+  if (!output_name)
+    return GSS_S_BAD_NAME;
+  if (!input_name_buffer)
+    return GSS_S_CALL_BAD_STRUCTURE;
+  if (!input_name_type)
+    return GSS_S_BAD_NAMETYPE;
+  GssNameMockImpl* output = new GssNameMockImpl;
+  if (output == NULL)
+    return GSS_S_FAILURE;
+  output->name_type.length = 0;
+  output->name_type.elements = NULL;
+
+  // Save the data.
+  output->name = BufferToString(input_name_buffer);
+  CopyOid(&output->name_type, input_name_type);
+  *output_name = output;
+
+  return GSS_S_COMPLETE;
+}
+
+OM_uint32 MockGSSAPILibrary::release_name(
+      OM_uint32* minor_status,
+      gss_name_t* input_name) {
+  if (minor_status)
+    *minor_status = 0;
+  if (!input_name)
+    return GSS_S_BAD_NAME;
+  if (!*input_name)
+    return GSS_S_COMPLETE;
+  GssNameMockImpl* name = *reinterpret_cast<GssNameMockImpl**>(input_name);
+  ClearName(*input_name);
+  delete name;
+  *input_name = NULL;
+  return GSS_S_COMPLETE;
+}
+
+OM_uint32 MockGSSAPILibrary::release_buffer(
+      OM_uint32* minor_status,
+      gss_buffer_t buffer) {
+  if (minor_status)
+    *minor_status = 0;
+  if (!buffer)
+    return GSS_S_BAD_NAME;
+  ClearBuffer(buffer);
+  return GSS_S_COMPLETE;
+}
+
+OM_uint32 MockGSSAPILibrary::display_name(
+    OM_uint32* minor_status,
+    const gss_name_t input_name,
+    gss_buffer_t output_name_buffer,
+    gss_OID* output_name_type) {
+  if (minor_status)
+    *minor_status = 0;
+  if (!input_name)
+    return GSS_S_BAD_NAME;
+  if (!output_name_buffer)
+    return GSS_S_CALL_BAD_STRUCTURE;
+  if (!output_name_type)
+    return GSS_S_CALL_BAD_STRUCTURE;
+  std::string name(NameToString(input_name));
+  BufferFromString(name, output_name_buffer);
+  GssNameMockImpl* internal_name =
+      *reinterpret_cast<GssNameMockImpl**>(input_name);
+  if (output_name_type)
+    *output_name_type = internal_name ? &internal_name->name_type : NULL;
+  return GSS_S_COMPLETE;
+}
+
+OM_uint32 MockGSSAPILibrary::display_status(
+      OM_uint32* minor_status,
+      OM_uint32 status_value,
+      int status_type,
+      const gss_OID mech_type,
+      OM_uint32* message_context,
+      gss_buffer_t status_string) {
+  if (minor_status)
+    *minor_status = 0;
+  std::string msg = StringPrintf("Value: %u, Type %u",
+                                 status_value,
+                                 status_type);
+  if (message_context)
+    *message_context = 0;
+  BufferFromString(msg, status_string);
+  return GSS_S_COMPLETE;
+}
+
+OM_uint32 MockGSSAPILibrary::init_sec_context(
+      OM_uint32* minor_status,
+      const gss_cred_id_t initiator_cred_handle,
+      gss_ctx_id_t* context_handle,
+      const gss_name_t target_name,
+      const gss_OID mech_type,
+      OM_uint32 req_flags,
+      OM_uint32 time_req,
+      const gss_channel_bindings_t input_chan_bindings,
+      const gss_buffer_t input_token,
+      gss_OID* actual_mech_type,
+      gss_buffer_t output_token,
+      OM_uint32* ret_flags,
+      OM_uint32* time_rec) {
+  if (minor_status)
+    *minor_status = 0;
+  if (!context_handle)
+    return GSS_S_CALL_BAD_STRUCTURE;
+  GssContextMockImpl** internal_context_handle =
+      reinterpret_cast<test::GssContextMockImpl**>(context_handle);
+  // Create it if necessary.
+  if (!*internal_context_handle) {
+    *internal_context_handle = new GssContextMockImpl;
+  }
+  EXPECT_TRUE(*internal_context_handle);
+  GssContextMockImpl& context = **internal_context_handle;
+  if (expected_security_queries_.empty()) {
+    return GSS_S_UNAVAILABLE;
+  }
+  SecurityContextQuery security_query = expected_security_queries_.front();
+  expected_security_queries_.pop_front();
+  EXPECT_EQ(std::string("Negotiate"), security_query.expected_package);
+  OM_uint32 major_status = security_query.response_code;
+  if (minor_status)
+    *minor_status = security_query.minor_response_code;
+  context.src_name = security_query.context_info.src_name;
+  context.targ_name = security_query.context_info.targ_name;
+  context.lifetime_rec = security_query.context_info.lifetime_rec;
+  CopyOid(&context.mech_type, &security_query.context_info.mech_type);
+  context.ctx_flags = security_query.context_info.ctx_flags;
+  context.locally_initiated = security_query.context_info.locally_initiated;
+  context.open = security_query.context_info.open;
+  if (!input_token) {
+    EXPECT_FALSE(security_query.expected_input_token.length);
+  } else {
+    EXPECT_EQ(input_token->length, security_query.expected_input_token.length);
+    if (input_token->length) {
+      EXPECT_EQ(0, memcmp(input_token->value,
+                          security_query.expected_input_token.value,
+                          input_token->length));
+    }
+  }
+  CopyBuffer(output_token, &security_query.output_token);
+  if (actual_mech_type)
+    CopyOid(*actual_mech_type, mech_type);
+  if (ret_flags)
+    *ret_flags = req_flags;
+  return major_status;
+}
+
+OM_uint32 MockGSSAPILibrary::wrap_size_limit(
+      OM_uint32* minor_status,
+      const gss_ctx_id_t context_handle,
+      int conf_req_flag,
+      gss_qop_t qop_req,
+      OM_uint32 req_output_size,
+      OM_uint32* max_input_size) {
+  if (minor_status)
+    *minor_status = 0;
+  ADD_FAILURE();
+  return GSS_S_UNAVAILABLE;
+}
+
+OM_uint32 MockGSSAPILibrary::delete_sec_context(
+      OM_uint32* minor_status,
+      gss_ctx_id_t* context_handle,
+      gss_buffer_t output_token) {
+  if (minor_status)
+    *minor_status = 0;
+  if (!context_handle)
+    return GSS_S_CALL_BAD_STRUCTURE;
+  GssContextMockImpl** internal_context_handle =
+      reinterpret_cast<GssContextMockImpl**>(context_handle);
+  if (*internal_context_handle) {
+    delete *internal_context_handle;
+    *internal_context_handle = NULL;
+  }
+  return GSS_S_COMPLETE;
+}
+
+OM_uint32 MockGSSAPILibrary::inquire_context(
+    OM_uint32* minor_status,
+    const gss_ctx_id_t context_handle,
+    gss_name_t* src_name,
+    gss_name_t* targ_name,
+    OM_uint32* lifetime_rec,
+    gss_OID* mech_type,
+    OM_uint32* ctx_flags,
+    int* locally_initiated,
+    int* open) {
+  if (minor_status)
+    *minor_status = 0;
+  if (!context_handle)
+    return GSS_S_CALL_BAD_STRUCTURE;
+  GssContextMockImpl* internal_context_ptr =
+      reinterpret_cast<GssContextMockImpl*>(context_handle);
+  GssContextMockImpl& context = *internal_context_ptr;
+  if (src_name)
+    NameFromString(context.src_name, *src_name);
+  if (targ_name)
+    NameFromString(context.targ_name, *targ_name);
+  if (lifetime_rec)
+    *lifetime_rec = context.lifetime_rec;
+  if (mech_type)
+    CopyOid(*mech_type, &context.mech_type);
+  if (ctx_flags)
+    *ctx_flags = context.ctx_flags;
+  if (locally_initiated)
+    *locally_initiated = context.locally_initiated;
+  if (open)
+    *open = context.open;
+  return GSS_S_COMPLETE;
+}
+
+void MockGSSAPILibrary::ExpectSecurityContext(
+    const std::string& expected_package,
+    OM_uint32 response_code,
+    OM_uint32 minor_response_code,
+    const GssContextMockImpl& context_info,
+    const gss_buffer_desc& expected_input_token,
+    const gss_buffer_desc& output_token) {
+  SecurityContextQuery security_query;
+  security_query.expected_package = expected_package;
+  security_query.response_code = response_code;
+  security_query.minor_response_code = minor_response_code;
+  security_query.context_info.Assign(context_info);
+  security_query.expected_input_token = expected_input_token;
+  security_query.output_token = output_token;
+  expected_security_queries_.push_back(security_query);
+}
+
+}  // namespace test
+
+}  // namespace net
+
diff --git a/net/http/mock_gssapi_library_posix.h b/net/http/mock_gssapi_library_posix.h
new file mode 100644
index 0000000..a78fb00
--- /dev/null
+++ b/net/http/mock_gssapi_library_posix.h
@@ -0,0 +1,185 @@
+// Copyright (c) 2010 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_HTTP_MOCK_GSSAPI_LIBRARY_POSIX_H_
+#define NET_HTTP_MOCK_GSSAPI_LIBRARY_POSIX_H_
+
+#include <list>
+#include <set>
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "net/http/http_auth_gssapi_posix.h"
+#include "net/third_party/gssapi/gssapi.h"
+
+namespace net {
+
+namespace test {
+
+class GssContextMockImpl {
+ public:
+  GssContextMockImpl();
+  GssContextMockImpl(const GssContextMockImpl& other);
+  GssContextMockImpl(const char* src_name,
+                     const char* targ_name,
+                     OM_uint32 lifetime_rec,
+                     const gss_OID_desc& mech_type,
+                     OM_uint32 ctx_flags,
+                     int locally_initiated,
+                     int open);
+  ~GssContextMockImpl();
+
+  void Assign(const GssContextMockImpl& other);
+
+  std::string src_name;
+  std::string targ_name;
+  OM_uint32 lifetime_rec;
+  gss_OID_desc mech_type;
+  OM_uint32 ctx_flags;
+  int locally_initiated;
+  int open;
+};
+
+// The MockGSSAPILibrary class is intended for unit tests which want to bypass
+// the system GSSAPI library calls.
+class MockGSSAPILibrary : public GSSAPILibrary {
+ public:
+
+  MockGSSAPILibrary();
+  virtual ~MockGSSAPILibrary();
+
+  // GSSAPILibrary methods:
+
+  // Initializes the library, including any necessary dynamic libraries.
+  // This is done separately from construction (which happens at startup time)
+  // in order to delay work until the class is actually needed.
+  virtual bool Init();
+
+  // These methods match the ones in the GSSAPI library.
+  virtual OM_uint32 import_name(
+      OM_uint32* minor_status,
+      const gss_buffer_t input_name_buffer,
+      const gss_OID input_name_type,
+      gss_name_t* output_name);
+  virtual OM_uint32 release_name(
+      OM_uint32* minor_status,
+      gss_name_t* input_name);
+  virtual OM_uint32 release_buffer(
+      OM_uint32* minor_status,
+      gss_buffer_t buffer);
+  virtual OM_uint32 display_name(
+      OM_uint32* minor_status,
+      const gss_name_t input_name,
+      gss_buffer_t output_name_buffer,
+      gss_OID* output_name_type);
+  virtual OM_uint32 display_status(
+      OM_uint32* minor_status,
+      OM_uint32 status_value,
+      int status_type,
+      const gss_OID mech_type,
+      OM_uint32* message_contex,
+      gss_buffer_t status_string);
+  virtual OM_uint32 init_sec_context(
+      OM_uint32* minor_status,
+      const gss_cred_id_t initiator_cred_handle,
+      gss_ctx_id_t* context_handle,
+      const gss_name_t target_name,
+      const gss_OID mech_type,
+      OM_uint32 req_flags,
+      OM_uint32 time_req,
+      const gss_channel_bindings_t input_chan_bindings,
+      const gss_buffer_t input_token,
+      gss_OID* actual_mech_type,
+      gss_buffer_t output_token,
+      OM_uint32* ret_flags,
+      OM_uint32* time_rec);
+  virtual OM_uint32 wrap_size_limit(
+      OM_uint32* minor_status,
+      const gss_ctx_id_t context_handle,
+      int conf_req_flag,
+      gss_qop_t qop_req,
+      OM_uint32 req_output_size,
+      OM_uint32* max_input_size);
+  virtual OM_uint32 delete_sec_context(
+      OM_uint32* minor_status,
+      gss_ctx_id_t* context_handle,
+      gss_buffer_t output_token);
+  virtual OM_uint32 inquire_context(
+      OM_uint32* minor_status,
+      const gss_ctx_id_t context_handle,
+      gss_name_t* src_name,
+      gss_name_t* targ_name,
+      OM_uint32* lifetime_rec,
+      gss_OID* mech_type,
+      OM_uint32* ctx_flags,
+      int* locally_initiated,
+      int* open);
+
+  // Establishes an expectation for a |init_sec_context()| call.
+  //
+  // Each expectation established by |ExpectSecurityContext()| must be
+  // matched by a call to |init_sec_context()| during the lifetime of
+  // the MockGSSAPILibrary. The |expected_package| argument must equal the
+  // value associated with the |target_name| argument to |init_sec_context()|
+  // for there to be a match. The expectations also establish an explicit
+  // ordering.
+  //
+  // For example, this sequence will be successful.
+  //   MockGSSAPILibrary lib;
+  //   lib.ExpectSecurityContext("NTLM", ...)
+  //   lib.ExpectSecurityContext("Negotiate", ...)
+  //   lib.init_sec_context("NTLM", ...)
+  //   lib.init_sec_context("Negotiate", ...)
+  //
+  // This sequence will fail since the queries do not occur in the order
+  // established by the expectations.
+  //   MockGSSAPILibrary lib;
+  //   lib.ExpectSecurityContext("NTLM", ...)
+  //   lib.ExpectSecurityContext("Negotiate", ...)
+  //   lib.init_sec_context("Negotiate", ...)
+  //   lib.init_sec_context("NTLM", ...)
+  //
+  // This sequence will fail because there were not enough queries.
+  //   MockGSSAPILibrary lib;
+  //   lib.ExpectSecurityContext("NTLM", ...)
+  //   lib.ExpectSecurityContext("Negotiate", ...)
+  //   lib.init_sec_context("NTLM", ...)
+  //
+  // |response_code| is used as the return value for |init_sec_context()|.
+  // If |response_code| is GSS_S_COMPLETE,
+  //
+  // |context_info| is the expected value of the |**context_handle| in after
+  // |init_sec_context()| returns.
+  void ExpectSecurityContext(const std::string& expected_package,
+                             OM_uint32 response_code,
+                             OM_uint32 minor_response_code,
+                             const test::GssContextMockImpl& context_info,
+                             const gss_buffer_desc& expected_input_token,
+                             const gss_buffer_desc& output_token);
+
+  // Unit tests need access to this. "Friend"ing didn't help.
+  struct SecurityContextQuery {
+    std::string expected_package;
+    OM_uint32 response_code;
+    OM_uint32 minor_response_code;
+    test::GssContextMockImpl context_info;
+    gss_buffer_desc expected_input_token;
+    gss_buffer_desc output_token;
+  };
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(HttpAuthGSSAPIPOSIXTest, GSSAPICycle);
+
+  // |expected_security_queries| contains an ordered list of expected
+  // |init_sec_context()| calls and the return values for those
+  // calls.
+  std::list<SecurityContextQuery> expected_security_queries_;
+};
+
+}  // namespace test
+
+}  // namespace net
+
+#endif  // NET_HTTP_MOCK_GSSAPI_LIBRARY_POSIX_H_
+
diff --git a/net/http/mock_sspi_library_win.cc b/net/http/mock_sspi_library_win.cc
new file mode 100644
index 0000000..d0dae51
--- /dev/null
+++ b/net/http/mock_sspi_library_win.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2010 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/http/mock_sspi_library_win.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+MockSSPILibrary::MockSSPILibrary() {
+}
+
+MockSSPILibrary::~MockSSPILibrary() {
+  EXPECT_TRUE(expected_package_queries_.empty());
+  EXPECT_TRUE(expected_freed_packages_.empty());
+}
+
+SECURITY_STATUS MockSSPILibrary::AcquireCredentialsHandle(
+    LPWSTR pszPrincipal,
+    LPWSTR pszPackage,
+    unsigned long fCredentialUse,
+    void* pvLogonId,
+    void* pvAuthData,
+    SEC_GET_KEY_FN pGetKeyFn,
+    void* pvGetKeyArgument,
+    PCredHandle phCredential,
+    PTimeStamp ptsExpiry) {
+  // Fill in phCredential with arbitrary value.
+  phCredential->dwLower = phCredential->dwUpper = ((ULONG_PTR) ((INT_PTR)0));
+  return SEC_E_OK;
+}
+
+SECURITY_STATUS MockSSPILibrary::InitializeSecurityContext(
+    PCredHandle phCredential,
+    PCtxtHandle phContext,
+    SEC_WCHAR* pszTargetName,
+    unsigned long fContextReq,
+    unsigned long Reserved1,
+    unsigned long TargetDataRep,
+    PSecBufferDesc pInput,
+    unsigned long Reserved2,
+    PCtxtHandle phNewContext,
+    PSecBufferDesc pOutput,
+    unsigned long* contextAttr,
+    PTimeStamp ptsExpiry) {
+  // Fill in the outbound buffer with garbage data.
+  PSecBuffer out_buffer = pOutput->pBuffers;
+  out_buffer->cbBuffer = 2;
+  uint8* buf = reinterpret_cast<uint8 *>(out_buffer->pvBuffer);
+  buf[0] = 0xAB;
+  buf[1] = 0xBA;
+  return SEC_E_OK;
+}
+
+SECURITY_STATUS MockSSPILibrary::QuerySecurityPackageInfo(
+    LPWSTR pszPackageName, PSecPkgInfoW *pkgInfo) {
+  EXPECT_TRUE(!expected_package_queries_.empty());
+  PackageQuery package_query = expected_package_queries_.front();
+  expected_package_queries_.pop_front();
+  std::wstring actual_package(pszPackageName);
+  EXPECT_EQ(package_query.expected_package, actual_package);
+  *pkgInfo = package_query.package_info;
+  if (package_query.response_code == SEC_E_OK)
+    expected_freed_packages_.insert(package_query.package_info);
+  return package_query.response_code;
+}
+
+SECURITY_STATUS MockSSPILibrary::FreeCredentialsHandle(
+    PCredHandle phCredential) {
+  EXPECT_TRUE(phCredential->dwLower == ((ULONG_PTR) ((INT_PTR) 0)));
+  EXPECT_TRUE(phCredential->dwLower == ((ULONG_PTR) ((INT_PTR) 0)));
+  SecInvalidateHandle(phCredential);
+  return SEC_E_OK;
+}
+
+SECURITY_STATUS MockSSPILibrary::DeleteSecurityContext(PCtxtHandle phContext) {
+  ADD_FAILURE();
+  return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+SECURITY_STATUS MockSSPILibrary::FreeContextBuffer(PVOID pvContextBuffer) {
+  PSecPkgInfoW package_info = static_cast<PSecPkgInfoW>(pvContextBuffer);
+  std::set<PSecPkgInfoW>::iterator it = expected_freed_packages_.find(
+      package_info);
+  EXPECT_TRUE(it != expected_freed_packages_.end());
+  expected_freed_packages_.erase(it);
+  return SEC_E_OK;
+}
+
+void MockSSPILibrary::ExpectQuerySecurityPackageInfo(
+    const std::wstring& expected_package,
+    SECURITY_STATUS response_code,
+    PSecPkgInfoW package_info) {
+  PackageQuery package_query = {expected_package, response_code,
+                                package_info};
+  expected_package_queries_.push_back(package_query);
+}
+
+}  // namespace net
diff --git a/net/http/mock_sspi_library_win.h b/net/http/mock_sspi_library_win.h
new file mode 100644
index 0000000..eca3eb5
--- /dev/null
+++ b/net/http/mock_sspi_library_win.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2010 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_HTTP_MOCK_SSPI_LIBRARY_WIN_H_
+#define NET_HTTP_MOCK_SSPI_LIBRARY_WIN_H_
+
+#include <list>
+#include <set>
+
+#include "net/http/http_auth_sspi_win.h"
+
+namespace net {
+
+// The MockSSPILibrary class is intended for unit tests which want to bypass
+// the system SSPI library calls.
+class MockSSPILibrary : public SSPILibrary {
+ public:
+  MockSSPILibrary();
+  virtual ~MockSSPILibrary();
+
+  // TODO(cbentzel): Only QuerySecurityPackageInfo and FreeContextBuffer
+  //                 are properly handled currently.
+  // SSPILibrary methods:
+  virtual SECURITY_STATUS AcquireCredentialsHandle(LPWSTR pszPrincipal,
+                                                   LPWSTR pszPackage,
+                                                   unsigned long fCredentialUse,
+                                                   void* pvLogonId,
+                                                   void* pvAuthData,
+                                                   SEC_GET_KEY_FN pGetKeyFn,
+                                                   void* pvGetKeyArgument,
+                                                   PCredHandle phCredential,
+                                                   PTimeStamp ptsExpiry);
+  virtual SECURITY_STATUS InitializeSecurityContext(PCredHandle phCredential,
+                                                    PCtxtHandle phContext,
+                                                    SEC_WCHAR* pszTargetName,
+                                                    unsigned long fContextReq,
+                                                    unsigned long Reserved1,
+                                                    unsigned long TargetDataRep,
+                                                    PSecBufferDesc pInput,
+                                                    unsigned long Reserved2,
+                                                    PCtxtHandle phNewContext,
+                                                    PSecBufferDesc pOutput,
+                                                    unsigned long* contextAttr,
+                                                    PTimeStamp ptsExpiry);
+  virtual SECURITY_STATUS QuerySecurityPackageInfo(LPWSTR pszPackageName,
+                                                   PSecPkgInfoW *pkgInfo);
+  virtual SECURITY_STATUS FreeCredentialsHandle(PCredHandle phCredential);
+  virtual SECURITY_STATUS DeleteSecurityContext(PCtxtHandle phContext);
+  virtual SECURITY_STATUS FreeContextBuffer(PVOID pvContextBuffer);
+
+  // Establishes an expectation for a |QuerySecurityPackageInfo()| call.
+  //
+  // Each expectation established by |ExpectSecurityQueryPackageInfo()| must be
+  // matched by a call to |QuerySecurityPackageInfo()| during the lifetime of
+  // the MockSSPILibrary. The |expected_package| argument must equal the
+  // |*pszPackageName| argument to |QuerySecurityPackageInfo()| for there to be
+  // a match. The expectations also establish an explicit ordering.
+  //
+  // For example, this sequence will be successful.
+  //   MockSSPILibrary lib;
+  //   lib.ExpectQuerySecurityPackageInfo(L"NTLM", ...)
+  //   lib.ExpectQuerySecurityPackageInfo(L"Negotiate", ...)
+  //   lib.QuerySecurityPackageInfo(L"NTLM", ...)
+  //   lib.QuerySecurityPackageInfo(L"Negotiate", ...)
+  //
+  // This sequence will fail since the queries do not occur in the order
+  // established by the expectations.
+  //   MockSSPILibrary lib;
+  //   lib.ExpectQuerySecurityPackageInfo(L"NTLM", ...)
+  //   lib.ExpectQuerySecurityPackageInfo(L"Negotiate", ...)
+  //   lib.QuerySecurityPackageInfo(L"Negotiate", ...)
+  //   lib.QuerySecurityPackageInfo(L"NTLM", ...)
+  //
+  // This sequence will fail because there were not enough queries.
+  //   MockSSPILibrary lib;
+  //   lib.ExpectQuerySecurityPackageInfo(L"NTLM", ...)
+  //   lib.ExpectQuerySecurityPackageInfo(L"Negotiate", ...)
+  //   lib.QuerySecurityPackageInfo(L"NTLM", ...)
+  //
+  // |response_code| is used as the return value for
+  // |QuerySecurityPackageInfo()|. If |response_code| is SEC_E_OK,
+  // an expectation is also set for a call to |FreeContextBuffer()| after
+  // the matching |QuerySecurityPackageInfo()| is called.
+  //
+  // |package_info| is assigned to |*pkgInfo| in |QuerySecurityPackageInfo|.
+  // The lifetime of |*package_info| should last at least until the matching
+  // |QuerySecurityPackageInfo()| is called.
+  void ExpectQuerySecurityPackageInfo(const std::wstring& expected_package,
+                                      SECURITY_STATUS response_code,
+                                      PSecPkgInfoW package_info);
+
+ private:
+  struct PackageQuery {
+    std::wstring expected_package;
+    SECURITY_STATUS response_code;
+    PSecPkgInfoW package_info;
+  };
+
+  // expected_package_queries contains an ordered list of expected
+  // |QuerySecurityPackageInfo()| calls and the return values for those
+  // calls.
+  std::list<PackageQuery> expected_package_queries_;
+
+  // Set of packages which should be freed.
+  std::set<PSecPkgInfoW> expected_freed_packages_;
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_MOCK_SSPI_LIBRARY_WIN_H_
diff --git a/net/http/partial_data.cc b/net/http/partial_data.cc
index f727699..49d68d2 100644
--- a/net/http/partial_data.cc
+++ b/net/http/partial_data.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2010 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.
 
@@ -12,6 +12,8 @@
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
 
+namespace net {
+
 namespace {
 
 // The headers that we have to process.
@@ -19,13 +21,101 @@
 const char kRangeHeader[] = "Content-Range";
 const int kDataStream = 1;
 
+void AddRangeHeader(int64 start, int64 end, HttpRequestHeaders* headers) {
+  DCHECK(start >= 0 || end >= 0);
+  std::string my_start, my_end;
+  if (start >= 0)
+    my_start = Int64ToString(start);
+  if (end >= 0)
+    my_end = Int64ToString(end);
+
+  headers->SetHeader(
+      HttpRequestHeaders::kRange,
+      StringPrintf("bytes=%s-%s", my_start.c_str(), my_end.c_str()));
 }
 
-namespace net {
+}  // namespace
 
-bool PartialData::Init(const std::string& headers) {
+// A core object that can be detached from the Partialdata object at destruction
+// so that asynchronous operations cleanup can be performed.
+class PartialData::Core {
+ public:
+  // Build a new core object. Lifetime management is automatic.
+  static Core* CreateCore(PartialData* owner) {
+    return new Core(owner);
+  }
+
+  // Wrapper for Entry::GetAvailableRange. If this method returns ERR_IO_PENDING
+  // PartialData::GetAvailableRangeCompleted() will be invoked on the owner
+  // object when finished (unless Cancel() is called first).
+  int GetAvailableRange(disk_cache::Entry* entry, int64 offset, int len,
+                        int64* start);
+
+  // Cancels a pending operation. It is a mistake to call this method if there
+  // is no operation in progress; in fact, there will be no object to do so.
+  void Cancel();
+
+ private:
+  explicit Core(PartialData* owner);
+  ~Core();
+
+  // Pending io completion routine.
+  void OnIOComplete(int result);
+
+  PartialData* owner_;
+  int64 start_;
+  net::CompletionCallbackImpl<Core> callback_;
+  DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+PartialData::Core::Core(PartialData* owner)
+    : owner_(owner),
+      ALLOW_THIS_IN_INITIALIZER_LIST(callback_(this, &Core::OnIOComplete)) {
+  DCHECK(!owner_->core_);
+  owner_->core_ = this;
+}
+
+PartialData::Core::~Core() {
+  if (owner_)
+    owner_->core_ = NULL;
+}
+
+void PartialData::Core::Cancel() {
+  DCHECK(owner_);
+  owner_ = NULL;
+}
+
+int PartialData::Core::GetAvailableRange(disk_cache::Entry* entry, int64 offset,
+                                         int len, int64* start) {
+  int rv = entry->GetAvailableRange(offset, len, &start_, &callback_);
+  if (rv != net::ERR_IO_PENDING) {
+    // The callback will not be invoked. Lets cleanup.
+    *start = start_;
+    delete this;
+  }
+  return rv;
+}
+
+void PartialData::Core::OnIOComplete(int result) {
+  if (owner_)
+    owner_->GetAvailableRangeCompleted(result, start_);
+  delete this;
+}
+
+// -----------------------------------------------------------------------------
+
+PartialData::~PartialData() {
+  if (core_)
+    core_->Cancel();
+}
+
+bool PartialData::Init(const HttpRequestHeaders& headers) {
+  std::string range_header;
+  if (!headers.GetHeader(HttpRequestHeaders::kRange, &range_header))
+    return false;
+
   std::vector<HttpByteRange> ranges;
-  if (!HttpUtil::ParseRanges(headers, &ranges) || ranges.size() != 1)
+  if (!HttpUtil::ParseRangeHeader(range_header, &ranges) || ranges.size() != 1)
     return false;
 
   // We can handle this range request.
@@ -38,38 +128,40 @@
   return true;
 }
 
-void PartialData::SetHeaders(const std::string& headers) {
-  DCHECK(extra_headers_.empty());
-  extra_headers_ = headers;
+void PartialData::SetHeaders(const HttpRequestHeaders& headers) {
+  DCHECK(extra_headers_.IsEmpty());
+  extra_headers_.CopyFrom(headers);
 }
 
-void PartialData::RestoreHeaders(std::string* headers) const {
+void PartialData::RestoreHeaders(HttpRequestHeaders* headers) const {
   DCHECK(current_range_start_ >= 0 || byte_range_.IsSuffixByteRange());
   int64 end = byte_range_.IsSuffixByteRange() ?
               byte_range_.suffix_length() : byte_range_.last_byte_position();
 
-  headers->assign(extra_headers_);
+  headers->CopyFrom(extra_headers_);
   if (byte_range_.IsValid())
     AddRangeHeader(current_range_start_, end, headers);
 }
 
-int PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
-                                        std::string* headers) {
-  DCHECK(current_range_start_ >= 0);
+int PartialData::ShouldValidateCache(disk_cache::Entry* entry,
+                                     CompletionCallback* callback) {
+  DCHECK_GE(current_range_start_, 0);
 
   // Scan the disk cache for the first cached portion within this range.
-  int64 range_len = byte_range_.HasLastBytePosition() ?
-      byte_range_.last_byte_position() - current_range_start_ + 1 : kint32max;
-  if (range_len > kint32max)
-    range_len = kint32max;
-  int len = static_cast<int32>(range_len);
+  int len = GetNextRangeLen();
   if (!len)
     return 0;
-  range_present_ = false;
 
   if (sparse_entry_) {
-    cached_min_len_ = entry->GetAvailableRange(current_range_start_, len,
-                                               &cached_start_);
+    DCHECK(!callback_);
+    Core* core = Core::CreateCore(this);
+    cached_min_len_ = core->GetAvailableRange(entry, current_range_start_, len,
+                                              &cached_start_);
+
+    if (cached_min_len_ == ERR_IO_PENDING) {
+      callback_ = callback;
+      return ERR_IO_PENDING;
+    }
   } else if (truncated_) {
     if (!current_range_start_) {
       // Update the cached range only the first time.
@@ -81,12 +173,23 @@
     cached_start_ = current_range_start_;
   }
 
-  if (cached_min_len_ < 0) {
-    DCHECK(cached_min_len_ != ERR_IO_PENDING);
+  if (cached_min_len_ < 0)
     return cached_min_len_;
-  }
 
-  headers->assign(extra_headers_);
+  // Return a positive number to indicate success (versus error or finished).
+  return 1;
+}
+
+void PartialData::PrepareCacheValidation(disk_cache::Entry* entry,
+                                         HttpRequestHeaders* headers) {
+  DCHECK_GE(current_range_start_, 0);
+  DCHECK_GE(cached_min_len_, 0);
+
+  int len = GetNextRangeLen();
+  DCHECK_NE(0, len);
+  range_present_ = false;
+
+  headers->CopyFrom(extra_headers_);
 
   if (!cached_min_len_) {
     // We don't have anything else stored.
@@ -106,9 +209,6 @@
     // This range is not in the cache.
     AddRangeHeader(current_range_start_, cached_start_ - 1, headers);
   }
-
-  // Return a positive number to indicate success (versus error or finished).
-  return 1;
 }
 
 bool PartialData::IsCurrentRangeCached() const {
@@ -158,11 +258,7 @@
   resource_size_ = length_value;
 
   // Make sure that this is really a sparse entry.
-  int64 n;
-  if (ERR_CACHE_OPERATION_NOT_SUPPORTED == entry->GetAvailableRange(0, 5, &n))
-    return false;
-
-  return true;
+  return entry->CouldBeSparse();
 }
 
 bool PartialData::IsRequestedRangeOK() {
@@ -194,7 +290,7 @@
 
     // We must have a complete range here.
     return byte_range_.HasFirstBytePosition() &&
-           byte_range_.HasLastBytePosition();
+        byte_range_.HasLastBytePosition();
   }
 
   int64 start, end, total_length;
@@ -296,7 +392,7 @@
 }
 
 int PartialData::CacheWrite(disk_cache::Entry* entry, IOBuffer* data,
-                           int data_len, CompletionCallback* callback) {
+                            int data_len, CompletionCallback* callback) {
   if (sparse_entry_) {
     return entry->WriteSparseData(current_range_start_, data, data_len,
                                   callback);
@@ -313,7 +409,7 @@
   if (result > 0) {
     current_range_start_ += result;
     cached_min_len_ -= result;
-    DCHECK(cached_min_len_ >= 0);
+    DCHECK_GE(cached_min_len_, 0);
   }
 }
 
@@ -322,17 +418,28 @@
     current_range_start_ += result;
 }
 
-// Static.
-void PartialData::AddRangeHeader(int64 start, int64 end, std::string* headers) {
-  DCHECK(start >= 0 || end >= 0);
-  std::string my_start, my_end;
-  if (start >= 0)
-    my_start = Int64ToString(start);
-  if (end >= 0)
-    my_end = Int64ToString(end);
+int PartialData::GetNextRangeLen() {
+  int64 range_len =
+      byte_range_.HasLastBytePosition() ?
+      byte_range_.last_byte_position() - current_range_start_ + 1 :
+      kint32max;
+  if (range_len > kint32max)
+    range_len = kint32max;
+  return static_cast<int32>(range_len);
+}
 
-  headers->append(StringPrintf("Range: bytes=%s-%s\r\n", my_start.c_str(),
-                               my_end.c_str()));
+void PartialData::GetAvailableRangeCompleted(int result, int64 start) {
+  DCHECK(callback_);
+  DCHECK_NE(ERR_IO_PENDING, result);
+
+  cached_start_ = start;
+  cached_min_len_ = result;
+  if (result >= 0)
+    result = 1;  // Return success, go ahead and validate the entry.
+
+  CompletionCallback* cb = callback_;
+  callback_ = NULL;
+  cb->Run(result);
 }
 
 }  // namespace net
diff --git a/net/http/partial_data.h b/net/http/partial_data.h
index 3a30e0a..b720e6e 100644
--- a/net/http/partial_data.h
+++ b/net/http/partial_data.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2009-2010 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.
 
@@ -10,6 +10,7 @@
 #include "base/basictypes.h"
 #include "net/base/completion_callback.h"
 #include "net/http/http_byte_range.h"
+#include "net/http/http_request_headers.h"
 
 namespace disk_cache {
 class Entry;
@@ -33,30 +34,36 @@
  public:
   PartialData()
       : range_present_(false), final_range_(false), sparse_entry_(true),
-        truncated_(false) {}
-  ~PartialData() {}
+        truncated_(false), core_(NULL), callback_(NULL) {}
+  ~PartialData();
 
-  // Performs initialization of the object by parsing the request |headers|
+  // Performs initialization of the object by examining the request |headers|
   // and verifying that we can process the requested range. Returns true if
   // we can process the requested range, and false otherwise.
-  bool Init(const std::string& headers);
+  bool Init(const HttpRequestHeaders& headers);
 
   // Sets the headers that we should use to make byte range requests. This is a
   // subset of the request extra headers, with byte-range related headers
   // removed.
-  void SetHeaders(const std::string& headers);
+  void SetHeaders(const HttpRequestHeaders& headers);
 
   // Restores the byte-range headers, by appending the byte range to the headers
   // provided to SetHeaders().
-  void RestoreHeaders(std::string* headers) const;
+  void RestoreHeaders(HttpRequestHeaders* headers) const;
+
+  // Starts the checks to perform a cache validation. Returns 0 when there is no
+  // need to perform more operations because we reached the end of the request
+  // (so 0 bytes should be actually returned to the user), a positive number to
+  // indicate that PrepareCacheValidation should be called, or an appropriate
+  // error code. If this method returns ERR_IO_PENDING, the |callback| will be
+  // notified when the result is ready.
+  int ShouldValidateCache(disk_cache::Entry* entry,
+                          CompletionCallback* callback);
 
   // Builds the required |headers| to perform the proper cache validation for
-  // the next range to be fetched. Returns 0 when there is no need to perform
-  // more operations because we reached the end of the request (so 0 bytes
-  // should be actually returned to the user), a positive number to indicate
-  // that |headers| should be used to validate the cache, or an appropriate
-  // error code.
-  int PrepareCacheValidation(disk_cache::Entry* entry, std::string* headers);
+  // the next range to be fetched.
+  void PrepareCacheValidation(disk_cache::Entry* entry,
+                              HttpRequestHeaders* headers);
 
   // Returns true if the current range is stored in the cache.
   bool IsCurrentRangeCached() const;
@@ -105,18 +112,26 @@
   void OnNetworkReadCompleted(int result);
 
  private:
-  static void AddRangeHeader(int64 start, int64 end, std::string* headers);
+  class Core;
+  // Returns the length to use when scanning the cache.
+  int GetNextRangeLen();
+
+  // Completion routine for our callback.
+  void GetAvailableRangeCompleted(int result, int64 start);
 
   int64 current_range_start_;
   int64 cached_start_;
   int64 resource_size_;
   int cached_min_len_;
   HttpByteRange byte_range_;  // The range requested by the user.
-  std::string extra_headers_;  // The clean set of extra headers (no ranges).
+  // The clean set of extra headers (no ranges).
+  HttpRequestHeaders extra_headers_;
   bool range_present_;  // True if next range entry is already stored.
   bool final_range_;
   bool sparse_entry_;
   bool truncated_;  // We have an incomplete 200 stored.
+  Core* core_;
+  CompletionCallback* callback_;
 
   DISALLOW_COPY_AND_ASSIGN(PartialData);
 };
diff --git a/net/http/url_security_manager.cc b/net/http/url_security_manager.cc
new file mode 100644
index 0000000..d3bc42e
--- /dev/null
+++ b/net/http/url_security_manager.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2010 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/http/url_security_manager.h"
+
+#include "net/http/http_auth_filter.h"
+
+namespace net {
+
+URLSecurityManagerWhitelist::URLSecurityManagerWhitelist(
+    HttpAuthFilter* whitelist) : whitelist_(whitelist) {
+}
+
+bool URLSecurityManagerWhitelist::CanUseDefaultCredentials(
+    const GURL& auth_origin) {
+  if (whitelist_.get())
+    return whitelist_->IsValid(auth_origin, HttpAuth::AUTH_SERVER);
+  return false;
+}
+
+}  //  namespace net
diff --git a/net/http/url_security_manager.h b/net/http/url_security_manager.h
new file mode 100644
index 0000000..cd80a7d
--- /dev/null
+++ b/net/http/url_security_manager.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2010 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_HTTP_URL_SECURITY_MANAGER_H_
+#define NET_HTTP_URL_SECURITY_MANAGER_H_
+
+#include "base/scoped_ptr.h"
+#include "base/basictypes.h"
+
+class GURL;
+
+namespace net {
+
+class HttpAuthFilter;
+
+// The URL security manager controls the policies (allow, deny, prompt user)
+// regarding URL actions (e.g., sending the default credentials to a server).
+class URLSecurityManager {
+ public:
+  URLSecurityManager() {}
+  virtual ~URLSecurityManager() {}
+
+  // Creates a platform-dependent instance of URLSecurityManager.
+  // The URLSecurityManager takes ownership of the HttpAuthFilter.
+  static URLSecurityManager* Create(HttpAuthFilter* whitelist);
+
+  // Returns true if we can send the default credentials to the server at
+  // |auth_origin| for HTTP NTLM or Negotiate authentication.
+  virtual bool CanUseDefaultCredentials(const GURL& auth_origin) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(URLSecurityManager);
+};
+
+class URLSecurityManagerWhitelist : public URLSecurityManager {
+ public:
+  // The URLSecurityManagerWhitelist takes ownership of the HttpAuthFilter.
+  explicit URLSecurityManagerWhitelist(HttpAuthFilter* whitelist);
+
+  // URLSecurityManager methods.
+  virtual bool CanUseDefaultCredentials(const GURL& auth_origin);
+
+ private:
+  scoped_ptr<HttpAuthFilter> whitelist_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLSecurityManagerWhitelist);
+};
+
+#if defined(UNIT_TEST)
+// An URLSecurityManager which always allows default credentials.
+class URLSecurityManagerAllow : public URLSecurityManager {
+ public:
+  URLSecurityManagerAllow() {}
+  virtual ~URLSecurityManagerAllow() {}
+
+  virtual bool CanUseDefaultCredentials(const GURL& auth_origin) {
+    return true;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(URLSecurityManagerAllow);
+};
+#endif  // defined(UNIT_TEST)
+
+}  // namespace net
+
+#endif  // NET_HTTP_URL_SECURITY_MANAGER_H_
diff --git a/net/http/url_security_manager_posix.cc b/net/http/url_security_manager_posix.cc
new file mode 100644
index 0000000..931d9cc
--- /dev/null
+++ b/net/http/url_security_manager_posix.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2010 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/http/url_security_manager.h"
+
+#include "net/http/http_auth_filter.h"
+
+namespace net {
+
+// static
+URLSecurityManager* URLSecurityManager::Create(
+    HttpAuthFilter* whitelist) {
+  return new URLSecurityManagerWhitelist(whitelist);
+}
+
+}  //  namespace net
diff --git a/net/http/url_security_manager_unittest.cc b/net/http/url_security_manager_unittest.cc
new file mode 100644
index 0000000..b08a7b2
--- /dev/null
+++ b/net/http/url_security_manager_unittest.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2010 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/http/url_security_manager.h"
+
+#include "base/basictypes.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_auth_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+struct TestData {
+  const char* url;
+  bool succeds_in_windows_default;
+  bool succeeds_in_whitelist;
+};
+
+const char* kTestAuthWhitelist = "*example.com,*foobar.com,baz";
+
+// Under Windows the following will be allowed by default:
+//    localhost
+//    host names without a period.
+// In Posix systems (or on Windows if a whitelist is specified explicitly),
+// everything depends on the whitelist.
+const TestData kTestDataList[] = {
+  { "http://localhost", true, false },
+  { "http://bat", true, false },
+  { "http://www.example.com", false, true },
+  { "http://example.com", false, true },
+  { "http://foobar.com", false, true },
+  { "http://boo.foobar.com", false, true },
+  { "http://baz", true, true },
+  { "http://www.exampl.com", false, false },
+  { "http://example.org", false, false },
+  { "http://foobar.net", false, false },
+  { "http://boo.fubar.com", false, false },
+};
+
+}  // namespace
+
+// For Windows, This relies on the contents of the registry, so in theory it
+// might fail.
+TEST(URLSecurityManager, FLAKY_CreateNoWhitelist) {
+  scoped_ptr<URLSecurityManager> url_security_manager(
+      URLSecurityManager::Create(NULL));
+  ASSERT_TRUE(url_security_manager.get());
+
+  for (size_t i = 0; i < arraysize(kTestDataList); ++i) {
+    GURL gurl(kTestDataList[i].url);
+    bool can_use_default =
+        url_security_manager->CanUseDefaultCredentials(gurl);
+
+#if defined(OS_WIN)
+    EXPECT_EQ(kTestDataList[i].succeds_in_windows_default, can_use_default)
+        << " Run: " << i << " URL: '" << gurl << "'";
+#else
+    // No whitelist means nothing can use the default.
+    EXPECT_FALSE(can_use_default)
+        << " Run: " << i << " URL: '" << gurl << "'";
+#endif // OS_WIN
+  }
+}
+
+TEST(URLSecurityManager, CreateWhitelist) {
+  HttpAuthFilterWhitelist* auth_filter = new HttpAuthFilterWhitelist();
+  ASSERT_TRUE(auth_filter);
+  auth_filter->SetWhitelist(kTestAuthWhitelist);
+  // The URL security manager takes ownership of |auth_filter|.
+  scoped_ptr<URLSecurityManager> url_security_manager(
+      URLSecurityManager::Create(auth_filter));
+  ASSERT_TRUE(url_security_manager.get());
+
+  for (size_t i = 0; i < arraysize(kTestDataList); ++i) {
+    GURL gurl(kTestDataList[i].url);
+    bool can_use_default =
+        url_security_manager->CanUseDefaultCredentials(gurl);
+
+    EXPECT_EQ(kTestDataList[i].succeeds_in_whitelist, can_use_default)
+        << " Run: " << i << " URL: '" << gurl << "'";
+  }
+}
+
+}  // namespace net
diff --git a/net/http/url_security_manager_win.cc b/net/http/url_security_manager_win.cc
new file mode 100644
index 0000000..4eaef78
--- /dev/null
+++ b/net/http/url_security_manager_win.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2010 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/http/url_security_manager.h"
+
+#include <urlmon.h>
+#pragma comment(lib, "urlmon.lib")
+
+#include "base/scoped_comptr_win.h"
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+
+// The Windows implementation of URLSecurityManager uses WinINet/IE's
+// URL security zone manager.  See the MSDN page "URL Security Zones" at
+// http://msdn.microsoft.com/en-us/library/ms537021(VS.85).aspx for more
+// info on the Internet Security Manager and Internet Zone Manager objects.
+//
+// On Windows, we honor the WinINet/IE settings and group policy related to
+// URL Security Zones.  See the Microsoft Knowledge Base article 182569
+// "Internet Explorer security zones registry entries for advanced users"
+// (http://support.microsoft.com/kb/182569) for more info on these registry
+// keys.
+
+namespace net {
+
+class URLSecurityManagerWin : public URLSecurityManager {
+ public:
+  URLSecurityManagerWin();
+
+  // URLSecurityManager methods:
+  virtual bool CanUseDefaultCredentials(const GURL& auth_origin);
+
+ private:
+  ScopedComPtr<IInternetSecurityManager> security_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLSecurityManagerWin);
+};
+
+URLSecurityManagerWin::URLSecurityManagerWin() {
+}
+
+
+bool URLSecurityManagerWin::CanUseDefaultCredentials(
+    const GURL& auth_origin) {
+  if (!security_manager_) {
+    HRESULT hr = CoInternetCreateSecurityManager(NULL,
+                                                 security_manager_.Receive(),
+                                                 NULL);
+    if (FAILED(hr) || !security_manager_) {
+      LOG(ERROR) << "Unable to create the Windows Security Manager instance";
+      return false;
+    }
+  }
+
+  std::wstring url_w = ASCIIToWide(auth_origin.spec());
+  DWORD policy = 0;
+  HRESULT hr;
+  hr = security_manager_->ProcessUrlAction(url_w.c_str(),
+                                           URLACTION_CREDENTIALS_USE,
+                                           reinterpret_cast<BYTE*>(&policy),
+                                           sizeof(policy), NULL, 0,
+                                           PUAF_NOUI, 0);
+  if (FAILED(hr)) {
+    LOG(ERROR) << "IInternetSecurityManager::ProcessUrlAction failed: " << hr;
+    return false;
+  }
+
+  // Four possible policies for URLACTION_CREDENTIALS_USE.  See the MSDN page
+  // "About URL Security Zones" at
+  // http://msdn.microsoft.com/en-us/library/ms537183(VS.85).aspx
+  switch (policy) {
+    case URLPOLICY_CREDENTIALS_SILENT_LOGON_OK:
+      return true;
+    case URLPOLICY_CREDENTIALS_CONDITIONAL_PROMPT: {
+      // This policy means "prompt the user for permission if the resource is
+      // not located in the Intranet zone".  TODO(wtc): Note that it's
+      // prompting for permission (to use the default credentials), as opposed
+      // to prompting the user to enter a user name and password.
+
+      // URLZONE_LOCAL_MACHINE 0
+      // URLZONE_INTRANET      1
+      // URLZONE_TRUSTED       2
+      // URLZONE_INTERNET      3
+      // URLZONE_UNTRUSTED     4
+      DWORD zone = 0;
+      hr = security_manager_->MapUrlToZone(url_w.c_str(), &zone, 0);
+      if (FAILED(hr)) {
+        LOG(ERROR) << "IInternetSecurityManager::MapUrlToZone failed: " << hr;
+        return false;
+      }
+      return zone <= URLZONE_INTRANET;
+    }
+    case URLPOLICY_CREDENTIALS_MUST_PROMPT_USER:
+      return false;
+    case URLPOLICY_CREDENTIALS_ANONYMOUS_ONLY:
+      // TODO(wtc): we should fail the authentication.
+      return false;
+    default:
+      NOTREACHED();
+      return false;
+  }
+}
+
+// static
+URLSecurityManager* URLSecurityManager::Create(
+    HttpAuthFilter* whitelist) {
+  // If we have a whitelist, just use that.
+  if (whitelist)
+    return new URLSecurityManagerWhitelist(whitelist);
+  return new URLSecurityManagerWin();
+}
+
+}  //  namespace net
diff --git a/net/http_listen_socket.target.mk b/net/http_listen_socket.target.mk
new file mode 100644
index 0000000..9a98489
--- /dev/null
+++ b/net/http_listen_socket.target.mk
@@ -0,0 +1,154 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := http_listen_socket
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I. \
+	-Itesting/gtest/include
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I. \
+	-Itesting/gtest/include
+
+OBJS := $(obj).target/$(TARGET)/net/server/http_listen_socket.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,--gc-sections
+
+LIBS := 
+
+$(obj).target/net/libhttp_listen_socket.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(obj).target/net/libhttp_listen_socket.a: LIBS := $(LIBS)
+$(obj).target/net/libhttp_listen_socket.a: TOOLSET := $(TOOLSET)
+$(obj).target/net/libhttp_listen_socket.a: $(OBJS) FORCE_DO_CMD
+	$(call do_cmd,alink)
+
+all_deps += $(obj).target/net/libhttp_listen_socket.a
+# Add target alias
+.PHONY: http_listen_socket
+http_listen_socket: $(obj).target/net/libhttp_listen_socket.a
+
+# Add target alias to "all" target.
+.PHONY: all
+all: http_listen_socket
+
diff --git a/net/net.Makefile b/net/net.Makefile
new file mode 100644
index 0000000..3ac1873
--- /dev/null
+++ b/net/net.Makefile
@@ -0,0 +1,6 @@
+# This file is generated by gyp; do not edit.
+
+export builddir_name ?= /usr/local/google/src/chromium-merge/src/net/out
+.PHONY: all
+all:
+	$(MAKE) -C .. net_resources net_base tld_cleanup net hresolv net_test_support fetch_client http_listen_socket fetch_server run_testserver net_perftests crash_cache net_unittests stress_cache
diff --git a/net/net.gyp b/net/net.gyp
old mode 100755
new mode 100644
index 37e89a8..1dcaf25
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -24,8 +24,12 @@
         'base/address_family.h',
         'base/address_list.cc',
         'base/address_list.h',
+        'base/address_list_net_log_param.cc',
+        'base/address_list_net_log_param.h',
         'base/auth.h',
         'base/cache_type.h',
+        'base/capturing_net_log.cc',
+        'base/capturing_net_log.h',
         'base/cert_database.h',
         'base/cert_database_mac.cc',
         'base/cert_database_nss.cc',
@@ -58,22 +62,24 @@
         'base/file_stream_win.cc',
         'base/filter.cc',
         'base/filter.h',
-        'base/fixed_host_resolver.cc',
-        'base/fixed_host_resolver.h',
+        'base/forwarding_net_log.cc',
+        'base/forwarding_net_log.h',
         'base/gzip_filter.cc',
         'base/gzip_filter.h',
         'base/gzip_header.cc',
         'base/gzip_header.h',
         'base/host_cache.cc',
         'base/host_cache.h',
+        'base/host_mapping_rules.cc',
+        'base/host_mapping_rules.h',
+        'base/host_port_pair.cc',
+        'base/host_port_pair.h',
         'base/host_resolver.cc',
         'base/host_resolver.h',
         'base/host_resolver_impl.cc',
         'base/host_resolver_impl.h',
         'base/host_resolver_proc.cc',
         'base/host_resolver_proc.h',
-        'base/https_prober.h',
-        'base/https_prober.cc',
         'base/io_buffer.cc',
         'base/io_buffer.h',
         'base/keygen_handler.h',
@@ -83,12 +89,10 @@
         'base/listen_socket.cc',
         'base/listen_socket.h',
         'base/load_flags.h',
-        'base/load_log.h',
-        'base/load_log.cc',
-        'base/load_log_event_type_list.h',
-        'base/load_log_util.cc',
-        'base/load_log_util.h',
+        'base/load_flags_list.h',
         'base/load_states.h',
+        'base/mapped_host_resolver.cc',
+        'base/mapped_host_resolver.h',
         'base/mime_sniffer.cc',
         'base/mime_sniffer.h',
         'base/mime_util.cc',
@@ -99,6 +103,10 @@
         'base/net_error_list.h',
         'base/net_errors.cc',
         'base/net_errors.h',
+        'base/net_log.cc',
+        'base/net_log.h',
+        'base/net_log_event_type_list.h',
+        'base/net_log_source_type_list.h',
         'base/net_module.cc',
         'base/net_module.h',
         'base/net_util.cc',
@@ -107,12 +115,12 @@
         'base/net_util_win.cc',
         'base/network_change_notifier.cc',
         'base/network_change_notifier.h',
-        'base/network_change_notifier_helper.cc',
-        'base/network_change_notifier_helper.h',
         'base/network_change_notifier_linux.cc',
         'base/network_change_notifier_linux.h',
         'base/network_change_notifier_mac.cc',
         'base/network_change_notifier_mac.h',
+        'base/network_change_notifier_netlink_linux.cc',
+        'base/network_change_notifier_netlink_linux.h',
         'base/network_change_notifier_win.cc',
         'base/network_change_notifier_win.h',
         'base/nss_memio.c',
@@ -130,6 +138,8 @@
         'base/sdch_manager.cc',
         'base/sdch_manager.h',
         'base/ssl_cert_request_info.h',
+        'base/ssl_cipher_suite_names.cc',
+        'base/ssl_cipher_suite_names.h',
         'base/ssl_client_auth_cache.cc',
         'base/ssl_client_auth_cache.h',
         'base/ssl_config_service.cc',
@@ -159,18 +169,33 @@
         'base/x509_certificate_mac.cc',
         'base/x509_certificate_nss.cc',
         'base/x509_certificate_win.cc',
+        'base/x509_cert_types.cc',
+        'base/x509_cert_types.h',
+        'base/x509_cert_types_mac.cc',
+        'third_party/mozilla_security_manager/nsKeygenHandler.cpp',
+        'third_party/mozilla_security_manager/nsKeygenHandler.h',
       ],
       'export_dependent_settings': [
         '../base/base.gyp:base',
       ],
       'conditions': [
         [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd"', {
-          'dependencies': [
-            '../build/linux/system.gyp:gconf',
-            '../build/linux/system.gyp:gdk',
-            '../build/linux/system.gyp:nss',
-          ],
-        }],
+            'dependencies': [
+              '../build/linux/system.gyp:gconf',
+              '../build/linux/system.gyp:gdk',
+              '../build/linux/system.gyp:nss',
+            ],
+          },
+          {  # else: OS is not in the above list
+            'sources!': [
+              'base/cert_database_nss.cc',
+              'base/keygen_handler_nss.cc',
+              'base/x509_certificate_nss.cc',
+              'third_party/mozilla_security_manager/nsKeygenHandler.cpp',
+              'third_party/mozilla_security_manager/nsKeygenHandler.h',
+            ],
+          },
+        ],
         [ 'OS == "win"', {
             'dependencies': [
               # For nss_memio.{c,h}, which require only NSPR.
@@ -179,27 +204,18 @@
             ],
           },
           {  # else: OS != "win"
+            'dependencies': [
+              '../third_party/libevent/libevent.gyp:libevent',
+            ],
             'sources!': [
               'base/winsock_init.cc',
             ],
           },
         ],
-        [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd"', {
-          },
-          {  # else: OS is not in the above list
-            'sources!': [
-              'base/cert_database_nss.cc',
-              'base/keygen_handler_nss.cc',
-              'base/x509_certificate_nss.cc',
-            ],
-          },
-        ],
         [ 'OS == "mac"', {
-            'sources!': [
-              # TODO(wtc): Remove nss_memio.{c,h} when http://crbug.com/30689
-              # is fixed.
-              'base/nss_memio.c',
-              'base/nss_memio.h',
+            'dependencies': [
+              # For nss_memio.{c,h}, which require only NSPR.
+              '../third_party/nss/nss.gyp:nspr',
             ],
             'link_settings': {
               'libraries': [
@@ -253,6 +269,10 @@
         'disk_cache/hash.cc',
         'disk_cache/hash.h',
         'disk_cache/histogram_macros.h',
+        'disk_cache/in_flight_backend_io.cc',
+        'disk_cache/in_flight_backend_io.h',
+        'disk_cache/in_flight_io.cc',
+        'disk_cache/in_flight_io.h',
         'disk_cache/mapped_file.h',
         'disk_cache/mapped_file_posix.cc',
         'disk_cache/mapped_file_win.cc',
@@ -274,23 +294,6 @@
         'disk_cache/storage_block.h',
         'disk_cache/trace.cc',
         'disk_cache/trace.h',
-        'flip/flip_bitmasks.h',
-        'flip/flip_frame_builder.cc',
-        'flip/flip_frame_builder.h',
-        'flip/flip_framer.cc',
-        'flip/flip_framer.h',
-        'flip/flip_io_buffer.cc',
-        'flip/flip_io_buffer.h',
-        'flip/flip_network_transaction.cc',
-        'flip/flip_network_transaction.h',
-        'flip/flip_protocol.h',
-        'flip/flip_session.cc',
-        'flip/flip_session.h',
-        'flip/flip_session_pool.cc',
-        'flip/flip_session_pool.h',
-        'flip/flip_stream.cc',
-        'flip/flip_stream.h',
-        'flip/flip_transaction_factory.h',
         'ftp/ftp_auth_cache.cc',
         'ftp/ftp_auth_cache.h',
         'ftp/ftp_ctrl_response_buffer.cc',
@@ -324,20 +327,30 @@
         'ftp/ftp_util.h',
         'http/des.cc',
         'http/des.h',
+        'http/http_alternate_protocols.cc',
+        'http/http_alternate_protocols.h',
         'http/http_atom_list.h',
         'http/http_auth.cc',
         'http/http_auth.h',
         'http/http_auth_cache.cc',
         'http/http_auth_cache.h',
-        'http/http_auth_handler.h',
+        'http/http_auth_controller.cc',
+        'http/http_auth_controller.h',
+        'http/http_auth_filter.cc',
+        'http/http_auth_filter.h',
+        'http/http_auth_filter_win.h',
+        'http/http_auth_gssapi_posix.cc',
+        'http/http_auth_gssapi_posix.h',
         'http/http_auth_handler.cc',
+        'http/http_auth_handler.h',
         'http/http_auth_handler_basic.cc',
         'http/http_auth_handler_basic.h',
         'http/http_auth_handler_digest.cc',
         'http/http_auth_handler_digest.h',
+        'http/http_auth_handler_factory.cc',
+        'http/http_auth_handler_factory.h',
         'http/http_auth_handler_negotiate.h',
-        'http/http_auth_handler_negotiate_posix.cc',
-        'http/http_auth_handler_negotiate_win.cc',
+        'http/http_auth_handler_negotiate.cc',
         'http/http_auth_handler_ntlm.cc',
         'http/http_auth_handler_ntlm.h',
         'http/http_auth_handler_ntlm_portable.cc',
@@ -354,12 +367,16 @@
         'http/http_cache_transaction.h',
         'http/http_chunked_decoder.cc',
         'http/http_chunked_decoder.h',
+        'http/http_net_log_params.h',
+        'http/http_network_delegate.h',
         'http/http_network_layer.cc',
         'http/http_network_layer.h',
         'http/http_network_session.cc',
         'http/http_network_session.h',
         'http/http_network_transaction.cc',
         'http/http_network_transaction.h',
+        'http/http_request_headers.cc',
+        'http/http_request_headers.h',
         'http/http_request_info.h',
         'http/http_response_headers.cc',
         'http/http_response_headers.h',
@@ -370,6 +387,14 @@
         'http/http_stream_parser.h',
         'http/http_transaction.h',
         'http/http_transaction_factory.h',
+        'http/url_security_manager.h',
+        'http/url_security_manager.cc',
+        'http/url_security_manager_posix.cc',
+        'http/url_security_manager_win.cc',
+        'http/http_proxy_client_socket.cc',
+        'http/http_proxy_client_socket.h',
+        'http/http_proxy_client_socket_pool.cc',
+        'http/http_proxy_client_socket_pool.h',
         'http/http_util.cc',
         'http/http_util_icu.cc',
         'http/http_util.h',
@@ -384,6 +409,10 @@
         'ocsp/nss_ocsp.h',
         'proxy/init_proxy_resolver.cc',
         'proxy/init_proxy_resolver.h',
+        'proxy/multi_threaded_proxy_resolver.cc',
+        'proxy/multi_threaded_proxy_resolver.h',
+        'proxy/proxy_bypass_rules.cc',
+        'proxy/proxy_bypass_rules.h',
         'proxy/proxy_config.cc',
         'proxy/proxy_config.h',
         'proxy/proxy_config_service.h',
@@ -403,7 +432,10 @@
         'proxy/proxy_resolver_js_bindings.h',
         'proxy/proxy_resolver_mac.cc',
         'proxy/proxy_resolver_mac.h',
+        'proxy/proxy_resolver_request_context.h',
         'proxy/proxy_resolver_script.h',
+        'proxy/proxy_resolver_script_data.cc',
+        'proxy/proxy_resolver_script_data.h',
         'proxy/proxy_resolver_v8.cc',
         'proxy/proxy_resolver_v8.h',
         'proxy/proxy_resolver_winhttp.cc',
@@ -416,31 +448,39 @@
         'proxy/proxy_server.h',
         'proxy/proxy_service.cc',
         'proxy/proxy_service.h',
-        'proxy/single_threaded_proxy_resolver.cc',
-        'proxy/single_threaded_proxy_resolver.h',
+        'proxy/sync_host_resolver_bridge.cc',
+        'proxy/sync_host_resolver_bridge.h',
         'socket/client_socket.h',
         'socket/client_socket_factory.cc',
         'socket/client_socket_factory.h',
         'socket/client_socket_handle.cc',
         'socket/client_socket_handle.h',
         'socket/client_socket_pool.h',
+        'socket/client_socket_pool.cc',
         'socket/client_socket_pool_base.cc',
         'socket/client_socket_pool_base.h',
+        'socket/client_socket_pool_histograms.cc',
+        'socket/client_socket_pool_histograms.h',
         'socket/socket.h',
         'socket/socks5_client_socket.cc',
         'socket/socks5_client_socket.h',
         'socket/socks_client_socket.cc',
         'socket/socks_client_socket.h',
+        'socket/socks_client_socket_pool.cc',
+        'socket/socks_client_socket_pool.h',
         'socket/ssl_client_socket.h',
         'socket/ssl_client_socket_mac.cc',
         'socket/ssl_client_socket_mac.h',
-        'socket/ssl_client_socket_nss_factory.cc',
+        'socket/ssl_client_socket_mac_factory.cc',
+        'socket/ssl_client_socket_mac_factory.h',
         'socket/ssl_client_socket_nss.cc',
         'socket/ssl_client_socket_nss.h',
+        'socket/ssl_client_socket_nss_factory.cc',
+        'socket/ssl_client_socket_nss_factory.h',
+        'socket/ssl_client_socket_pool.cc',
+        'socket/ssl_client_socket_pool.h',
         'socket/ssl_client_socket_win.cc',
         'socket/ssl_client_socket_win.h',
-        'socket/ssl_test_util.cc',
-        'socket/ssl_test_util.h',
         'socket/tcp_client_socket.h',
         'socket/tcp_client_socket_libevent.cc',
         'socket/tcp_client_socket_libevent.h',
@@ -451,11 +491,35 @@
         'socket/tcp_pinger.h',
         'socket_stream/socket_stream.cc',
         'socket_stream/socket_stream.h',
+        'socket_stream/socket_stream_job.cc',
+        'socket_stream/socket_stream_job.h',
+        'socket_stream/socket_stream_job_manager.cc',
+        'socket_stream/socket_stream_job_manager.h',
         'socket_stream/socket_stream_metrics.cc',
         'socket_stream/socket_stream_metrics.h',
-        'socket_stream/socket_stream_throttle.cc',
-        'socket_stream/socket_stream_throttle.h',
-        'url_request/request_tracker.h',
+        'spdy/spdy_bitmasks.h',
+        'spdy/spdy_frame_builder.cc',
+        'spdy/spdy_frame_builder.h',
+        'spdy/spdy_framer.cc',
+        'spdy/spdy_framer.h',
+        'spdy/spdy_http_stream.cc',
+        'spdy/spdy_http_stream.h',
+        'spdy/spdy_io_buffer.cc',
+        'spdy/spdy_io_buffer.h',
+        'spdy/spdy_network_transaction.cc',
+        'spdy/spdy_network_transaction.h',
+        'spdy/spdy_protocol.h',
+        'spdy/spdy_session.cc',
+        'spdy/spdy_session.h',
+        'spdy/spdy_session_pool.cc',
+        'spdy/spdy_session_pool.h',
+        'spdy/spdy_settings_storage.cc',
+        'spdy/spdy_settings_storage.h',
+        'spdy/spdy_stream.cc',
+        'spdy/spdy_stream.h',
+        'spdy/spdy_transaction_factory.h',
+        'url_request/https_prober.h',
+        'url_request/https_prober.cc',
         'url_request/url_request.cc',
         'url_request/url_request.h',
         'url_request/url_request_about_job.cc',
@@ -471,6 +535,8 @@
         'url_request/url_request_file_job.h',
         'url_request/url_request_filter.cc',
         'url_request/url_request_filter.h',
+        'url_request/url_request_ftp_job.cc',
+        'url_request/url_request_ftp_job.h',
         'url_request/url_request_http_job.cc',
         'url_request/url_request_http_job.h',
         'url_request/url_request_job.cc',
@@ -481,8 +547,8 @@
         'url_request/url_request_job_metrics.h',
         'url_request/url_request_job_tracker.cc',
         'url_request/url_request_job_tracker.h',
-        'url_request/url_request_new_ftp_job.cc',
-        'url_request/url_request_new_ftp_job.h',
+        'url_request/url_request_netlog_params.cc',
+        'url_request/url_request_netlog_params.h',
         'url_request/url_request_redirect_job.cc',
         'url_request/url_request_redirect_job.h',
         'url_request/url_request_simple_job.cc',
@@ -490,12 +556,20 @@
         'url_request/url_request_status.h',
         'url_request/url_request_test_job.cc',
         'url_request/url_request_test_job.h',
-        'url_request/url_request_view_net_internals_job.cc',
-        'url_request/url_request_view_net_internals_job.h',
         'url_request/view_cache_helper.cc',
         'url_request/view_cache_helper.h',
         'websockets/websocket.cc',
         'websockets/websocket.h',
+        'websockets/websocket_frame_handler.cc',
+        'websockets/websocket_frame_handler.h',
+        'websockets/websocket_handshake.cc',
+        'websockets/websocket_handshake.h',
+        'websockets/websocket_handshake_draft75.cc',
+        'websockets/websocket_handshake_draft75.h',
+        'websockets/websocket_handshake_handler.cc',
+        'websockets/websocket_handshake_handler.h',
+        'websockets/websocket_job.cc',
+        'websockets/websocket_job.h',
         'websockets/websocket_throttle.cc',
         'websockets/websocket_throttle.h',
       ],
@@ -509,12 +583,19 @@
           ],
         }],
         [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd"', {
-          'dependencies': [
-            '../build/linux/system.gyp:gconf',
-            '../build/linux/system.gyp:gdk',
-            '../build/linux/system.gyp:nss',
-          ],
-        }],
+            'dependencies': [
+              '../build/linux/system.gyp:gconf',
+              '../build/linux/system.gyp:gdk',
+              '../build/linux/system.gyp:nss',
+            ],
+          },
+          {  # else: OS is not in the above list
+            'sources!': [
+              'ocsp/nss_ocsp.cc',
+              'ocsp/nss_ocsp.h',
+            ],
+          },
+        ],
         [ 'OS == "win"', {
             'sources!': [
               'http/http_auth_handler_ntlm_portable.cc',
@@ -522,32 +603,25 @@
             ],
             'dependencies': [
               '../third_party/nss/nss.gyp:nss',
-              'third_party/nss/nss.gyp:ssl',
+              'third_party/nss/ssl.gyp:ssl',
               'tld_cleanup',
             ],
           },
           {  # else: OS != "win"
+            'dependencies': [
+              '../third_party/libevent/libevent.gyp:libevent',
+            ],
             'sources!': [
               'proxy/proxy_resolver_winhttp.cc',
               'socket/ssl_client_socket_nss_factory.cc',
-            ],
-          },
-        ],
-        [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd"', {
-          },
-          {  # else: OS != "linux"
-            'sources!': [
-              'ocsp/nss_ocsp.cc',
-              'ocsp/nss_ocsp.h',
+              'socket/ssl_client_socket_nss_factory.h',
             ],
           },
         ],
         [ 'OS == "mac"', {
-            'sources!': [
-              # TODO(wtc): Remove ssl_client_socket_nss.{cc,h} when
-              # http://crbug.com/30689 is fixed.
-              'socket/ssl_client_socket_nss.cc',
-              'socket/ssl_client_socket_nss.h',
+            'dependencies': [
+              '../third_party/nss/nss.gyp:nss',
+              'third_party/nss/ssl.gyp:ssl',
             ],
             'link_settings': {
               'libraries': [
@@ -556,6 +630,12 @@
               ]
             },
           },
+          {  # else: OS != "mac"
+            'sources!': [
+              'socket/ssl_client_socket_mac_factory.cc',
+              'socket/ssl_client_socket_mac_factory.h',
+            ],
+          },
         ],
       ],
     },
@@ -582,22 +662,26 @@
         'base/file_stream_unittest.cc',
         'base/filter_unittest.cc',
         'base/filter_unittest.h',
+        'base/forwarding_net_log_unittest.cc',
         'base/gzip_filter_unittest.cc',
         'base/host_cache_unittest.cc',
+        'base/host_mapping_rules_unittest.cc',
         'base/host_resolver_impl_unittest.cc',
-        'base/load_log_unittest.cc',
-        'base/load_log_unittest.h',
-        'base/load_log_util_unittest.cc',
+        'base/keygen_handler_unittest.cc',
+        'base/leak_annotations.h',
         'base/listen_socket_unittest.cc',
         'base/listen_socket_unittest.h',
+        'base/mapped_host_resolver_unittest.cc',
         'base/mime_sniffer_unittest.cc',
         'base/mime_util_unittest.cc',
-        'base/mock_network_change_notifier.h',
+        'base/net_log_unittest.h',
         'base/net_test_constants.h',
+        'base/net_test_suite.h',
         'base/net_util_unittest.cc',
         'base/registry_controlled_domain_unittest.cc',
         'base/run_all_unittests.cc',
         'base/sdch_filter_unittest.cc',
+        'base/ssl_cipher_suite_names_unittest.cc',
         'base/ssl_client_auth_cache_unittest.cc',
         'base/ssl_config_service_mac_unittest.cc',
         'base/ssl_config_service_win_unittest.cc',
@@ -606,7 +690,9 @@
         'base/telnet_server_unittest.cc',
         'base/test_certificate_data.h',
         'base/test_completion_callback_unittest.cc',
+        'base/upload_data_stream_unittest.cc',
         'base/x509_certificate_unittest.cc',
+        'base/x509_cert_types_unittest.cc',
         'disk_cache/addr_unittest.cc',
         'disk_cache/backend_unittest.cc',
         'disk_cache/bitmap_unittest.cc',
@@ -627,14 +713,17 @@
         'ftp/ftp_network_transaction_unittest.cc',
         'ftp/ftp_util_unittest.cc',
         'http/des_unittest.cc',
-        'flip/flip_framer_test.cc',
-        'flip/flip_network_transaction_unittest.cc',
-        'flip/flip_protocol_test.cc',
-        'flip/flip_session_unittest.cc',
-        'flip/flip_stream_unittest.cc',
+        'http/http_alternate_protocols_unittest.cc',
         'http/http_auth_cache_unittest.cc',
+        'http/http_auth_filter_unittest.cc',
+        'http/http_auth_gssapi_posix_unittest.cc',
         'http/http_auth_handler_basic_unittest.cc',
         'http/http_auth_handler_digest_unittest.cc',
+        'http/http_auth_handler_factory_unittest.cc',
+        'http/http_auth_handler_mock.cc',
+        'http/http_auth_handler_mock.h',
+        'http/http_auth_handler_negotiate_unittest.cc',
+        'http/http_auth_handler_unittest.cc',
         'http/http_auth_sspi_win_unittest.cc',
         'http/http_auth_unittest.cc',
         'http/http_byte_range_unittest.cc',
@@ -642,13 +731,22 @@
         'http/http_chunked_decoder_unittest.cc',
         'http/http_network_layer_unittest.cc',
         'http/http_network_transaction_unittest.cc',
+        'http/http_proxy_client_socket_pool_unittest.cc',
+        'http/http_request_headers_unittest.cc',
         'http/http_response_headers_unittest.cc',
         'http/http_transaction_unittest.cc',
         'http/http_transaction_unittest.h',
         'http/http_util_unittest.cc',
         'http/http_vary_data_unittest.cc',
+        'http/mock_gssapi_library_posix.cc',
+        'http/mock_gssapi_library_posix.h',
+        'http/mock_sspi_library_win.h',
+        'http/mock_sspi_library_win.cc',
+        'http/url_security_manager_unittest.cc',
         'proxy/init_proxy_resolver_unittest.cc',
         'proxy/mock_proxy_resolver.h',
+        'proxy/multi_threaded_proxy_resolver_unittest.cc',
+        'proxy/proxy_bypass_rules_unittest.cc',
         'proxy/proxy_config_service_linux_unittest.cc',
         'proxy/proxy_config_service_win_unittest.cc',
         'proxy/proxy_config_unittest.cc',
@@ -658,19 +756,38 @@
         'proxy/proxy_script_fetcher_unittest.cc',
         'proxy/proxy_server_unittest.cc',
         'proxy/proxy_service_unittest.cc',
-        'proxy/single_threaded_proxy_resolver_unittest.cc',
+        'proxy/sync_host_resolver_bridge_unittest.cc',
         'socket/client_socket_pool_base_unittest.cc',
         'socket/socks5_client_socket_unittest.cc',
+        'socket/socks_client_socket_pool_unittest.cc',
         'socket/socks_client_socket_unittest.cc',
         'socket/ssl_client_socket_unittest.cc',
+        'socket/ssl_client_socket_pool_unittest.cc',
         'socket/tcp_client_socket_pool_unittest.cc',
         'socket/tcp_client_socket_unittest.cc',
         'socket/tcp_pinger_unittest.cc',
         'socket_stream/socket_stream_metrics_unittest.cc',
         'socket_stream/socket_stream_unittest.cc',
-        'url_request/request_tracker_unittest.cc',
+        'spdy/spdy_framer_test.cc',
+        'spdy/spdy_http_stream_unittest.cc',
+        'spdy/spdy_network_transaction_unittest.cc',
+        'spdy/spdy_protocol_test.cc',
+        'spdy/spdy_session_unittest.cc',
+        'spdy/spdy_stream_unittest.cc',
+        'spdy/spdy_test_util.cc',
+        'spdy/spdy_test_util.h',
+        'tools/dump_cache/url_to_filename_encoder.cc',
+        'tools/dump_cache/url_to_filename_encoder.h',
+        'tools/dump_cache/url_to_filename_encoder_unittest.cc',
+        'url_request/url_request_job_tracker_unittest.cc',
         'url_request/url_request_unittest.cc',
         'url_request/url_request_unittest.h',
+        'url_request/view_cache_helper_unittest.cc',
+        'websockets/websocket_frame_handler_unittest.cc',
+        'websockets/websocket_handshake_draft75_unittest.cc',
+        'websockets/websocket_handshake_handler_unittest.cc',
+        'websockets/websocket_handshake_unittest.cc',
+        'websockets/websocket_job_unittest.cc',
         'websockets/websocket_throttle_unittest.cc',
         'websockets/websocket_unittest.cc',
       ],
@@ -678,6 +795,7 @@
         [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd"', {
             'dependencies': [
               '../build/linux/system.gyp:gtk',
+              '../build/linux/system.gyp:nss',
             ],
             'sources!': [
               'base/sdch_filter_unittest.cc',
@@ -693,9 +811,12 @@
             }],
           ],
         }],
-        # This is needed to trigger the dll copy step on windows.
-        # TODO(mark): Specifying this here shouldn't be necessary.
         [ 'OS == "win"', {
+            'sources!': [
+              'http/http_auth_gssapi_posix_unittest.cc',
+            ],
+            # This is needed to trigger the dll copy step on windows.
+            # TODO(mark): Specifying this here shouldn't be necessary.
             'dependencies': [
               '../third_party/icu/icu.gyp:icudata',
             ],
@@ -771,6 +892,20 @@
       ],
     },
     {
+      'target_name': 'run_testserver',
+      'type': 'executable',
+      'dependencies': [
+        'net',
+        'net_test_support',
+        '../base/base.gyp:base',
+        '../testing/gtest.gyp:gtest',
+      ],
+      'msvs_guid': '506F2468-6B1D-48E2-A67C-9D9C6BAC0EC5',
+      'sources': [
+        'tools/testserver/run_testserver.cc',
+      ],
+    },
+    {
       'target_name': 'net_test_support',
       'type': '<(library)',
       'dependencies': [
@@ -779,37 +914,71 @@
         '../testing/gtest.gyp:gtest',
       ],
       'sources': [
+        'base/cert_test_util.cc',
+        'base/cert_test_util.h',
         'disk_cache/disk_cache_test_util.cc',
         'disk_cache/disk_cache_test_util.h',
         'proxy/proxy_config_service_common_unittest.cc',
         'proxy/proxy_config_service_common_unittest.h',
         'socket/socket_test_util.cc',
         'socket/socket_test_util.h',
+        'test/test_server.cc',
+        'test/test_server.h',
+      ],
+      'conditions': [
+        ['inside_chromium_build==1', {
+          'dependencies': [
+            '../chrome/browser/sync/protocol/sync_proto.gyp:sync_proto',
+            '../third_party/protobuf2/protobuf.gyp:py_proto',
+          ],
+        }],
+        ['OS == "linux" or OS == "freebsd" or OS == "openbsd"', {
+          'dependencies': [
+            '../build/linux/system.gyp:nss',
+          ],
+        }],
+        ['OS == "linux"', {
+          'conditions': [
+            ['linux_use_tcmalloc==1', {
+              'dependencies': [
+                '../base/allocator/allocator.gyp:allocator',
+              ],
+            }],
+          ],
+        }],
       ],
     },
     {
       'target_name': 'net_resources',
       'type': 'none',
       'msvs_guid': '8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942',
+      'variables': {
+        'grit_cmd': ['python', '../tools/grit/grit.py'],
+        'grit_info_cmd': ['python', '../tools/grit/grit_info.py'],
+        'input_paths': [
+          'base/net_resources.grd',
+        ],
+      },
       'rules': [
         {
           'rule_name': 'grit',
           'extension': 'grd',
           'inputs': [
-            '../tools/grit/grit.py',
+            '<!@(<(grit_info_cmd) --inputs <(input_paths))',
           ],
           'outputs': [
-            '<(SHARED_INTERMEDIATE_DIR)/net/grit/<(RULE_INPUT_ROOT).h',
-            '<(SHARED_INTERMEDIATE_DIR)/net/<(RULE_INPUT_ROOT).rc',
-            '<(SHARED_INTERMEDIATE_DIR)/net/<(RULE_INPUT_ROOT).pak',
+            '<!@(<(grit_info_cmd) '
+            '--outputs \'<(SHARED_INTERMEDIATE_DIR)/net\' <(input_paths))',
           ],
           'action':
-            ['python', '<@(_inputs)', '-i', '<(RULE_INPUT_PATH)', 'build', '-o', '<(SHARED_INTERMEDIATE_DIR)/net'],
+            ['<@(grit_cmd)',
+             '-i', '<(RULE_INPUT_PATH)', 'build',
+             '-o', '<(SHARED_INTERMEDIATE_DIR)/net'],
           'message': 'Generating resources from <(RULE_INPUT_PATH)',
         },
       ],
       'sources': [
-        'base/net_resources.grd',
+        '<@(input_paths)',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
@@ -857,6 +1026,21 @@
       ],
     },
     {
+      'target_name': 'http_listen_socket',
+      'type': '<(library)',
+      'dependencies': [
+        'net',
+        '../base/base.gyp:base',
+        '../testing/gtest.gyp:gtest',
+      ],
+      'msvs_guid': 'FCB894A4-CC6C-48C2-B495-52C80527E9BE',
+      'sources': [
+        'server/http_listen_socket.cc',
+        'server/http_listen_socket.h',
+        'server/http_server_request_info.h',
+      ],
+    },
+    {
       'target_name': 'hresolv',
       'type': 'executable',
       'dependencies': [
@@ -869,6 +1053,56 @@
     },
   ],
   'conditions': [
+    # ['OS=="linux"', {
+    #   'targets': [
+    #     {
+    #       'target_name': 'flip_in_mem_edsm_server',
+    #       'type': 'executable',
+    #       'dependencies': [
+    #         '../base/base.gyp:base',
+    #         'net.gyp:net',
+    #       ],
+    #       'link_settings': {
+    #         'ldflags': [
+    #           '-lssl'
+    #         ],
+    #         'libraries': [
+    #           '-lssl'
+    #         ],
+    #       },
+    #       'sources': [
+    #         'tools/flip_server/balsa_enums.h',
+    #         'tools/flip_server/balsa_frame.cc',
+    #         'tools/flip_server/balsa_frame.h',
+    #         'tools/flip_server/balsa_headers.cc',
+    #         'tools/flip_server/balsa_headers.h',
+    #         'tools/flip_server/balsa_headers_token_utils.cc',
+    #         'tools/flip_server/balsa_headers_token_utils.h',
+    #         'tools/flip_server/balsa_visitor_interface.h',
+    #         'tools/flip_server/buffer_interface.h',
+    #         'tools/flip_server/create_listener.cc',
+    #         'tools/flip_server/create_listener.h',
+    #         'tools/flip_server/epoll_server.cc',
+    #         'tools/flip_server/epoll_server.h',
+    #         'tools/flip_server/flip_in_mem_edsm_server.cc',
+    #         'tools/flip_server/http_message_constants.cc',
+    #         'tools/flip_server/http_message_constants.h',
+    #         'tools/flip_server/loadtime_measurement.h',
+    #         'tools/flip_server/porting.txt',
+    #         'tools/flip_server/ring_buffer.cc',
+    #         'tools/flip_server/ring_buffer.h',
+    #         'tools/flip_server/simple_buffer.cc',
+    #         'tools/flip_server/simple_buffer.h',
+    #         'tools/flip_server/split.h',
+    #         'tools/flip_server/split.cc',
+    #         'tools/flip_server/string_piece_utils.h',
+    #         'tools/flip_server/thread.h',
+    #         'tools/flip_server/url_to_filename_encoder.h',
+    #         'tools/flip_server/url_utilities.h',
+    #       ],
+    #     },
+    #   ]
+    # }],
     ['OS=="win"', {
       'targets': [
         {
@@ -885,7 +1119,9 @@
             'tools/dump_cache/dump_cache.cc',
             'tools/dump_cache/dump_files.cc',
             'tools/dump_cache/upgrade.cc',
+            'tools/dump_cache/url_to_filename_encoder.cc',
             'tools/dump_cache/url_to_filename_encoder.h',
+            'tools/dump_cache/url_utilties.h',
           ],
         },
       ],
diff --git a/net/net.target.mk b/net/net.target.mk
new file mode 100644
index 0000000..0d01fba
--- /dev/null
+++ b/net/net.target.mk
@@ -0,0 +1,351 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := net
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DU_STATIC_IMPLEMENTATION' \
+	'-DUSE_SYSTEM_ZLIB' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-DORBIT2=1 \
+	-pthread \
+	-I/usr/include/gconf/2 \
+	-I/usr/include/orbit-2.0 \
+	-I/usr/include/dbus-1.0 \
+	-I/usr/lib/dbus-1.0/include \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-I../net/third_party/nss/ssl \
+	-Inet/third_party/nss/ssl \
+	-IWebKit/chromium/net/third_party/nss/ssl \
+	-I/usr/include/nss \
+	-I/usr/include/nspr \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -Ithird_party/icu/public/common \
+	-Ithird_party/icu/public/i18n \
+	-I. \
+	-Isdch/open-vcdiff/src \
+	-I$(obj)/gen/net \
+	-Iv8/include
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DU_STATIC_IMPLEMENTATION' \
+	'-DUSE_SYSTEM_ZLIB' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-DORBIT2=1 \
+	-pthread \
+	-I/usr/include/gconf/2 \
+	-I/usr/include/orbit-2.0 \
+	-I/usr/include/dbus-1.0 \
+	-I/usr/lib/dbus-1.0/include \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-I../net/third_party/nss/ssl \
+	-Inet/third_party/nss/ssl \
+	-IWebKit/chromium/net/third_party/nss/ssl \
+	-I/usr/include/nss \
+	-I/usr/include/nspr \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -Ithird_party/icu/public/common \
+	-Ithird_party/icu/public/i18n \
+	-I. \
+	-Isdch/open-vcdiff/src \
+	-I$(obj)/gen/net \
+	-Iv8/include
+
+OBJS := $(obj).target/$(TARGET)/net/disk_cache/addr.o \
+	$(obj).target/$(TARGET)/net/disk_cache/backend_impl.o \
+	$(obj).target/$(TARGET)/net/disk_cache/bitmap.o \
+	$(obj).target/$(TARGET)/net/disk_cache/block_files.o \
+	$(obj).target/$(TARGET)/net/disk_cache/cache_util_posix.o \
+	$(obj).target/$(TARGET)/net/disk_cache/entry_impl.o \
+	$(obj).target/$(TARGET)/net/disk_cache/eviction.o \
+	$(obj).target/$(TARGET)/net/disk_cache/file_lock.o \
+	$(obj).target/$(TARGET)/net/disk_cache/file_posix.o \
+	$(obj).target/$(TARGET)/net/disk_cache/hash.o \
+	$(obj).target/$(TARGET)/net/disk_cache/in_flight_backend_io.o \
+	$(obj).target/$(TARGET)/net/disk_cache/in_flight_io.o \
+	$(obj).target/$(TARGET)/net/disk_cache/mapped_file_posix.o \
+	$(obj).target/$(TARGET)/net/disk_cache/mem_backend_impl.o \
+	$(obj).target/$(TARGET)/net/disk_cache/mem_entry_impl.o \
+	$(obj).target/$(TARGET)/net/disk_cache/mem_rankings.o \
+	$(obj).target/$(TARGET)/net/disk_cache/rankings.o \
+	$(obj).target/$(TARGET)/net/disk_cache/sparse_control.o \
+	$(obj).target/$(TARGET)/net/disk_cache/stats.o \
+	$(obj).target/$(TARGET)/net/disk_cache/stats_histogram.o \
+	$(obj).target/$(TARGET)/net/disk_cache/trace.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_auth_cache.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_ctrl_response_buffer.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_buffer.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser_ls.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser_mlsd.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser_netware.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser_vms.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser_windows.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_network_layer.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_network_transaction.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_server_type_histograms.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_util.o \
+	$(obj).target/$(TARGET)/net/http/des.o \
+	$(obj).target/$(TARGET)/net/http/http_alternate_protocols.o \
+	$(obj).target/$(TARGET)/net/http/http_auth.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_cache.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_controller.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_filter.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_gssapi_posix.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_basic.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_digest.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_factory.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_negotiate.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_ntlm.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_ntlm_portable.o \
+	$(obj).target/$(TARGET)/net/http/http_basic_stream.o \
+	$(obj).target/$(TARGET)/net/http/http_byte_range.o \
+	$(obj).target/$(TARGET)/net/http/http_cache.o \
+	$(obj).target/$(TARGET)/net/http/http_cache_transaction.o \
+	$(obj).target/$(TARGET)/net/http/http_chunked_decoder.o \
+	$(obj).target/$(TARGET)/net/http/http_network_layer.o \
+	$(obj).target/$(TARGET)/net/http/http_network_session.o \
+	$(obj).target/$(TARGET)/net/http/http_network_transaction.o \
+	$(obj).target/$(TARGET)/net/http/http_request_headers.o \
+	$(obj).target/$(TARGET)/net/http/http_response_headers.o \
+	$(obj).target/$(TARGET)/net/http/http_response_info.o \
+	$(obj).target/$(TARGET)/net/http/http_stream_parser.o \
+	$(obj).target/$(TARGET)/net/http/url_security_manager.o \
+	$(obj).target/$(TARGET)/net/http/url_security_manager_posix.o \
+	$(obj).target/$(TARGET)/net/http/http_proxy_client_socket.o \
+	$(obj).target/$(TARGET)/net/http/http_proxy_client_socket_pool.o \
+	$(obj).target/$(TARGET)/net/http/http_util.o \
+	$(obj).target/$(TARGET)/net/http/http_util_icu.o \
+	$(obj).target/$(TARGET)/net/http/http_vary_data.o \
+	$(obj).target/$(TARGET)/net/http/md4.o \
+	$(obj).target/$(TARGET)/net/http/partial_data.o \
+	$(obj).target/$(TARGET)/net/ocsp/nss_ocsp.o \
+	$(obj).target/$(TARGET)/net/proxy/init_proxy_resolver.o \
+	$(obj).target/$(TARGET)/net/proxy/multi_threaded_proxy_resolver.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_bypass_rules.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_config.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_config_service_linux.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_info.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_list.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_resolver_js_bindings.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_resolver_script_data.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_resolver_v8.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_script_fetcher.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_server.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_service.o \
+	$(obj).target/$(TARGET)/net/proxy/sync_host_resolver_bridge.o \
+	$(obj).target/$(TARGET)/net/socket/client_socket_factory.o \
+	$(obj).target/$(TARGET)/net/socket/client_socket_handle.o \
+	$(obj).target/$(TARGET)/net/socket/client_socket_pool.o \
+	$(obj).target/$(TARGET)/net/socket/client_socket_pool_base.o \
+	$(obj).target/$(TARGET)/net/socket/client_socket_pool_histograms.o \
+	$(obj).target/$(TARGET)/net/socket/socks5_client_socket.o \
+	$(obj).target/$(TARGET)/net/socket/socks_client_socket.o \
+	$(obj).target/$(TARGET)/net/socket/socks_client_socket_pool.o \
+	$(obj).target/$(TARGET)/net/socket/ssl_client_socket_nss.o \
+	$(obj).target/$(TARGET)/net/socket/ssl_client_socket_pool.o \
+	$(obj).target/$(TARGET)/net/socket/tcp_client_socket_libevent.o \
+	$(obj).target/$(TARGET)/net/socket/tcp_client_socket_pool.o \
+	$(obj).target/$(TARGET)/net/socket_stream/socket_stream.o \
+	$(obj).target/$(TARGET)/net/socket_stream/socket_stream_job.o \
+	$(obj).target/$(TARGET)/net/socket_stream/socket_stream_job_manager.o \
+	$(obj).target/$(TARGET)/net/socket_stream/socket_stream_metrics.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_frame_builder.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_framer.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_http_stream.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_io_buffer.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_network_transaction.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_session.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_session_pool.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_settings_storage.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_stream.o \
+	$(obj).target/$(TARGET)/net/url_request/https_prober.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_about_job.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_data_job.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_error_job.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_file_dir_job.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_file_job.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_filter.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_ftp_job.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_http_job.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_job.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_job_manager.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_job_metrics.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_job_tracker.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_netlog_params.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_redirect_job.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_simple_job.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_test_job.o \
+	$(obj).target/$(TARGET)/net/url_request/view_cache_helper.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_frame_handler.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_handshake.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_handshake_draft75.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_handshake_handler.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_job.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_throttle.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/net/net_resources.stamp $(obj).target/v8/tools/gyp/v8.stamp
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,--gc-sections
+
+LIBS := 
+
+$(obj).target/net/libnet.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(obj).target/net/libnet.a: LIBS := $(LIBS)
+$(obj).target/net/libnet.a: TOOLSET := $(TOOLSET)
+$(obj).target/net/libnet.a: $(OBJS) FORCE_DO_CMD
+	$(call do_cmd,alink)
+
+all_deps += $(obj).target/net/libnet.a
+# Add target alias
+.PHONY: net
+net: $(obj).target/net/libnet.a
+
+# Add target alias to "all" target.
+.PHONY: all
+all: net
+
diff --git a/net/net_base.target.mk b/net/net_base.target.mk
new file mode 100644
index 0000000..a3c4d6d
--- /dev/null
+++ b/net/net_base.target.mk
@@ -0,0 +1,288 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := net_base
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DU_STATIC_IMPLEMENTATION' \
+	'-DUSE_SYSTEM_ZLIB' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-DORBIT2=1 \
+	-pthread \
+	-I/usr/include/gconf/2 \
+	-I/usr/include/orbit-2.0 \
+	-I/usr/include/dbus-1.0 \
+	-I/usr/lib/dbus-1.0/include \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-I../net/third_party/nss/ssl \
+	-Inet/third_party/nss/ssl \
+	-IWebKit/chromium/net/third_party/nss/ssl \
+	-I/usr/include/nss \
+	-I/usr/include/nspr \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -Ithird_party/icu/public/common \
+	-Ithird_party/icu/public/i18n \
+	-I. \
+	-Isdch/open-vcdiff/src \
+	-I$(obj)/gen/net
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DU_STATIC_IMPLEMENTATION' \
+	'-DUSE_SYSTEM_ZLIB' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-DORBIT2=1 \
+	-pthread \
+	-I/usr/include/gconf/2 \
+	-I/usr/include/orbit-2.0 \
+	-I/usr/include/dbus-1.0 \
+	-I/usr/lib/dbus-1.0/include \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-I../net/third_party/nss/ssl \
+	-Inet/third_party/nss/ssl \
+	-IWebKit/chromium/net/third_party/nss/ssl \
+	-I/usr/include/nss \
+	-I/usr/include/nspr \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -Ithird_party/icu/public/common \
+	-Ithird_party/icu/public/i18n \
+	-I. \
+	-Isdch/open-vcdiff/src \
+	-I$(obj)/gen/net
+
+OBJS := $(obj).target/$(TARGET)/net/base/address_list.o \
+	$(obj).target/$(TARGET)/net/base/address_list_net_log_param.o \
+	$(obj).target/$(TARGET)/net/base/capturing_net_log.o \
+	$(obj).target/$(TARGET)/net/base/cert_database_nss.o \
+	$(obj).target/$(TARGET)/net/base/cert_status_flags.o \
+	$(obj).target/$(TARGET)/net/base/cert_verifier.o \
+	$(obj).target/$(TARGET)/net/base/connection_type_histograms.o \
+	$(obj).target/$(TARGET)/net/base/cookie_monster.o \
+	$(obj).target/$(TARGET)/net/base/data_url.o \
+	$(obj).target/$(TARGET)/net/base/directory_lister.o \
+	$(obj).target/$(TARGET)/net/base/dns_util.o \
+	$(obj).target/$(TARGET)/net/base/escape.o \
+	$(obj).target/$(TARGET)/net/base/ev_root_ca_metadata.o \
+	$(obj).target/$(TARGET)/net/base/file_stream_posix.o \
+	$(obj).target/$(TARGET)/net/base/filter.o \
+	$(obj).target/$(TARGET)/net/base/forwarding_net_log.o \
+	$(obj).target/$(TARGET)/net/base/gzip_filter.o \
+	$(obj).target/$(TARGET)/net/base/gzip_header.o \
+	$(obj).target/$(TARGET)/net/base/host_cache.o \
+	$(obj).target/$(TARGET)/net/base/host_mapping_rules.o \
+	$(obj).target/$(TARGET)/net/base/host_port_pair.o \
+	$(obj).target/$(TARGET)/net/base/host_resolver.o \
+	$(obj).target/$(TARGET)/net/base/host_resolver_impl.o \
+	$(obj).target/$(TARGET)/net/base/host_resolver_proc.o \
+	$(obj).target/$(TARGET)/net/base/io_buffer.o \
+	$(obj).target/$(TARGET)/net/base/keygen_handler_nss.o \
+	$(obj).target/$(TARGET)/net/base/listen_socket.o \
+	$(obj).target/$(TARGET)/net/base/mapped_host_resolver.o \
+	$(obj).target/$(TARGET)/net/base/mime_sniffer.o \
+	$(obj).target/$(TARGET)/net/base/mime_util.o \
+	$(obj).target/$(TARGET)/net/base/mock_host_resolver.o \
+	$(obj).target/$(TARGET)/net/base/net_errors.o \
+	$(obj).target/$(TARGET)/net/base/net_log.o \
+	$(obj).target/$(TARGET)/net/base/net_module.o \
+	$(obj).target/$(TARGET)/net/base/net_util.o \
+	$(obj).target/$(TARGET)/net/base/net_util_posix.o \
+	$(obj).target/$(TARGET)/net/base/network_change_notifier.o \
+	$(obj).target/$(TARGET)/net/base/network_change_notifier_linux.o \
+	$(obj).target/$(TARGET)/net/base/network_change_notifier_netlink_linux.o \
+	$(obj).target/$(TARGET)/net/base/nss_memio.o \
+	$(obj).target/$(TARGET)/net/base/platform_mime_util_linux.o \
+	$(obj).target/$(TARGET)/net/base/registry_controlled_domain.o \
+	$(obj).target/$(TARGET)/net/base/sdch_filter.o \
+	$(obj).target/$(TARGET)/net/base/sdch_manager.o \
+	$(obj).target/$(TARGET)/net/base/ssl_cipher_suite_names.o \
+	$(obj).target/$(TARGET)/net/base/ssl_client_auth_cache.o \
+	$(obj).target/$(TARGET)/net/base/ssl_config_service.o \
+	$(obj).target/$(TARGET)/net/base/static_cookie_policy.o \
+	$(obj).target/$(TARGET)/net/base/transport_security_state.o \
+	$(obj).target/$(TARGET)/net/base/telnet_server.o \
+	$(obj).target/$(TARGET)/net/base/upload_data.o \
+	$(obj).target/$(TARGET)/net/base/upload_data_stream.o \
+	$(obj).target/$(TARGET)/net/base/x509_certificate.o \
+	$(obj).target/$(TARGET)/net/base/x509_certificate_nss.o \
+	$(obj).target/$(TARGET)/net/base/x509_cert_types.o \
+	$(obj).target/$(TARGET)/net/third_party/mozilla_security_manager/nsKeygenHandler.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/net/net_resources.stamp
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD
+	@$(call do_cmd,cc,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD
+	@$(call do_cmd,cc,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD
+	@$(call do_cmd,cc,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cpp FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,--gc-sections
+
+LIBS := 
+
+$(obj).target/net/libnet_base.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(obj).target/net/libnet_base.a: LIBS := $(LIBS)
+$(obj).target/net/libnet_base.a: TOOLSET := $(TOOLSET)
+$(obj).target/net/libnet_base.a: $(OBJS) FORCE_DO_CMD
+	$(call do_cmd,alink)
+
+all_deps += $(obj).target/net/libnet_base.a
+# Add target alias
+.PHONY: net_base
+net_base: $(obj).target/net/libnet_base.a
+
+# Add target alias to "all" target.
+.PHONY: all
+all: net_base
+
diff --git a/net/net_perftests.target.mk b/net/net_perftests.target.mk
new file mode 100644
index 0000000..65afebf
--- /dev/null
+++ b/net/net_perftests.target.mk
@@ -0,0 +1,193 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := net_perftests
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DPERF_TEST' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I. \
+	-Itesting/gtest/include
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DPERF_TEST' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I. \
+	-Itesting/gtest/include
+
+OBJS := $(obj).target/$(TARGET)/net/base/cookie_monster_perftest.o \
+	$(obj).target/$(TARGET)/net/disk_cache/disk_cache_perftest.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_resolver_perftest.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/net/libnet.a $(obj).target/net/libnet_test_support.a $(obj).target/base/libbase.a $(obj).target/base/libbase_i18n.a $(obj).target/base/libtest_support_base.a $(obj).target/base/libtest_support_perf.a $(obj).target/testing/libgtest.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-Wl,--gc-sections
+
+LIBS := -lrt \
+	-ldl \
+	-lgtk-x11-2.0 \
+	-lgdk-x11-2.0 \
+	-latk-1.0 \
+	-lgio-2.0 \
+	-lpangoft2-1.0 \
+	-lgdk_pixbuf-2.0 \
+	-lm \
+	-lpangocairo-1.0 \
+	-lcairo \
+	-lpango-1.0 \
+	-lfreetype \
+	-lfontconfig \
+	-lgobject-2.0 \
+	-lgmodule-2.0 \
+	-lgthread-2.0 \
+	-lglib-2.0 \
+	-lnss3 \
+	-lnssutil3 \
+	-lsmime3 \
+	-lplds4 \
+	-lplc4 \
+	-lnspr4 \
+	-lpthread \
+	-lz \
+	-lgconf-2
+
+$(builddir)/net_perftests: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(builddir)/net_perftests: LIBS := $(LIBS)
+$(builddir)/net_perftests: TOOLSET := $(TOOLSET)
+$(builddir)/net_perftests: $(OBJS) $(obj).target/net/libnet.a $(obj).target/net/libnet_test_support.a $(obj).target/base/libbase.a $(obj).target/base/libbase_i18n.a $(obj).target/base/libtest_support_base.a $(obj).target/base/libtest_support_perf.a $(obj).target/testing/libgtest.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a FORCE_DO_CMD
+	$(call do_cmd,link)
+
+all_deps += $(builddir)/net_perftests
+# Add target alias
+.PHONY: net_perftests
+net_perftests: $(builddir)/net_perftests
+
+# Add executable to "all" target.
+.PHONY: all
+all: $(builddir)/net_perftests
+
diff --git a/net/net_resources.target.mk b/net/net_resources.target.mk
new file mode 100644
index 0000000..6b8fe7a
--- /dev/null
+++ b/net/net_resources.target.mk
@@ -0,0 +1,121 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := net_resources
+### Generated for rule net_resources_grit:
+$(obj)/gen/net/grit/net_resources.h: obj := $(abs_obj)
+
+$(obj)/gen/net/grit/net_resources.h: builddir := $(abs_builddir)
+
+$(obj)/gen/net/grit/net_resources.h: TOOLSET := $(TOOLSET)
+$(obj)/gen/net/grit/net_resources.h: net/base/net_resources.grd net/base/dir_header.html net/base/net_resources.grd tools/grit/grit.py tools/grit/grit_info.py tools/grit/resource_ids tools/grit/grit/exception.py tools/grit/grit/tclib.py tools/grit/grit/pseudo_unittest.py tools/grit/grit/grd_reader_unittest.py tools/grit/grit/grd_reader.py tools/grit/grit/scons.py tools/grit/grit/clique_unittest.py tools/grit/grit/constants.py tools/grit/grit/grit_runner.py tools/grit/grit/clique.py tools/grit/grit/grit_runner_unittest.py tools/grit/grit/shortcuts.py tools/grit/grit/util_unittest.py tools/grit/grit/__init__.py tools/grit/grit/tclib_unittest.py tools/grit/grit/xtb_reader_unittest.py tools/grit/grit/xtb_reader.py tools/grit/grit/pseudo.py tools/grit/grit/test_suite_all.py tools/grit/grit/util.py tools/grit/grit/shortcuts_unittests.py tools/grit/grit/extern/tclib.py tools/grit/grit/extern/FP.py tools/grit/grit/extern/__init__.py tools/grit/grit/node/structure.py tools/grit/grit/node/io_unittest.py tools/grit/grit/node/message.py tools/grit/grit/node/structure_unittest.py tools/grit/grit/node/base.py tools/grit/grit/node/empty.py tools/grit/grit/node/misc.py tools/grit/grit/node/__init__.py tools/grit/grit/node/include.py tools/grit/grit/node/mapping.py tools/grit/grit/node/message_unittest.py tools/grit/grit/node/io.py tools/grit/grit/node/variant.py tools/grit/grit/node/base_unittest.py tools/grit/grit/node/misc_unittest.py tools/grit/grit/node/custom/filename_unittest.py tools/grit/grit/node/custom/filename.py tools/grit/grit/node/custom/__init__.py tools/grit/grit/tool/newgrd.py tools/grit/grit/tool/transl2tc.py tools/grit/grit/tool/postprocess_interface.py tools/grit/grit/tool/preprocess_unittest.py tools/grit/grit/tool/test.py tools/grit/grit/tool/count.py tools/grit/grit/tool/toolbar_preprocess.py tools/grit/grit/tool/toolbar_postprocess.py tools/grit/grit/tool/resize.py tools/grit/grit/tool/unit.py tools/grit/grit/tool/preprocess_interface.py tools/grit/grit/tool/menu_from_parts.py tools/grit/grit/tool/__init__.py tools/grit/grit/tool/rc2grd_unittest.py tools/grit/grit/tool/interface.py tools/grit/grit/tool/postprocess_unittest.py tools/grit/grit/tool/diff_structures.py tools/grit/grit/tool/transl2tc_unittest.py tools/grit/grit/tool/rc2grd.py tools/grit/grit/tool/build.py tools/grit/grit/gather/admin_template.py tools/grit/grit/gather/muppet_strings_unittest.py tools/grit/grit/gather/rc_unittest.py tools/grit/grit/gather/tr_html.py tools/grit/grit/gather/regexp.py tools/grit/grit/gather/__init__.py tools/grit/grit/gather/admin_template_unittest.py tools/grit/grit/gather/interface.py tools/grit/grit/gather/muppet_strings.py tools/grit/grit/gather/rc.py tools/grit/grit/gather/txt_unittest.py tools/grit/grit/gather/tr_html_unittest.py tools/grit/grit/gather/txt.py tools/grit/grit/format/rc_header_unittest.py tools/grit/grit/format/data_pack.py tools/grit/grit/format/data_pack_unittest.py tools/grit/grit/format/rc_unittest.py tools/grit/grit/format/js_map_format.py tools/grit/grit/format/resource_map.py tools/grit/grit/format/rc_header.py tools/grit/grit/format/__init__.py tools/grit/grit/format/html_inline.py tools/grit/grit/format/interface.py tools/grit/grit/format/rc.py tools/grit/grit/format/js_map_format_unittest.py FORCE_DO_CMD
+	$(call do_cmd,net_resources_grit_0)
+$(obj)/gen/net/net_resources.pak $(obj)/gen/net/net_resources.rc: $(obj)/gen/net/grit/net_resources.h
+$(obj)/gen/net/net_resources.pak $(obj)/gen/net/net_resources.rc: ;
+
+all_deps += $(obj)/gen/net/grit/net_resources.h $(obj)/gen/net/net_resources.pak $(obj)/gen/net/net_resources.rc
+cmd_net_resources_grit_0 = export LD_LIBRARY_PATH=$(builddir)/lib.host:$(builddir)/lib.target:$$LD_LIBRARY_PATH; cd net; mkdir -p $(obj)/gen/net/grit $(obj)/gen/net; python ../tools/grit/grit.py -i "$(abspath $<)" build -o "$(obj)/gen/net"
+quiet_cmd_net_resources_grit_0 = RULE net_resources_grit_0 $@
+
+rule_net_resources_grit_outputs := $(obj)/gen/net/grit/net_resources.h \
+	$(obj)/gen/net/net_resources.pak \
+	$(obj)/gen/net/net_resources.rc
+
+### Finished generating for rule: net_resources_grit
+
+### Finished generating for all rules
+
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := 
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := 
+
+OBJS := 
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our actions/rules run before any of us.
+$(OBJS): | $(rule_net_resources_grit_outputs)
+
+
+### Rules for final target.
+# Build our special outputs first.
+$(obj).target/net/net_resources.stamp: | $(rule_net_resources_grit_outputs)
+
+# Preserve order dependency of special output on deps.
+$(rule_net_resources_grit_outputs): | 
+
+$(obj).target/net/net_resources.stamp: TOOLSET := $(TOOLSET)
+$(obj).target/net/net_resources.stamp:  FORCE_DO_CMD
+	$(call do_cmd,touch)
+
+all_deps += $(obj).target/net/net_resources.stamp
+# Add target alias
+.PHONY: net_resources
+net_resources: $(obj).target/net/net_resources.stamp
+
+# Add target alias to "all" target.
+.PHONY: all
+all: net_resources
+
diff --git a/net/net_test_support.target.mk b/net/net_test_support.target.mk
new file mode 100644
index 0000000..8e2077e
--- /dev/null
+++ b/net/net_test_support.target.mk
@@ -0,0 +1,171 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := net_test_support
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-I../net/third_party/nss/ssl \
+	-Inet/third_party/nss/ssl \
+	-IWebKit/chromium/net/third_party/nss/ssl \
+	-I/usr/include/nss \
+	-I/usr/include/nspr \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I. \
+	-Itesting/gtest/include
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-I../net/third_party/nss/ssl \
+	-Inet/third_party/nss/ssl \
+	-IWebKit/chromium/net/third_party/nss/ssl \
+	-I/usr/include/nss \
+	-I/usr/include/nspr \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I. \
+	-Itesting/gtest/include
+
+OBJS := $(obj).target/$(TARGET)/net/base/cert_test_util.o \
+	$(obj).target/$(TARGET)/net/disk_cache/disk_cache_test_util.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_config_service_common_unittest.o \
+	$(obj).target/$(TARGET)/net/socket/socket_test_util.o \
+	$(obj).target/$(TARGET)/net/test/test_server.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/chrome/browser/sync/protocol/sync_proto.stamp $(obj).target/third_party/protobuf2/py_proto.stamp
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,--gc-sections
+
+LIBS := 
+
+$(obj).target/net/libnet_test_support.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(obj).target/net/libnet_test_support.a: LIBS := $(LIBS)
+$(obj).target/net/libnet_test_support.a: TOOLSET := $(TOOLSET)
+$(obj).target/net/libnet_test_support.a: $(OBJS) FORCE_DO_CMD
+	$(call do_cmd,alink)
+
+all_deps += $(obj).target/net/libnet_test_support.a
+# Add target alias
+.PHONY: net_test_support
+net_test_support: $(obj).target/net/libnet_test_support.a
+
+# Add target alias to "all" target.
+.PHONY: all
+all: net_test_support
+
diff --git a/net/net_unittests.target.mk b/net/net_unittests.target.mk
new file mode 100644
index 0000000..4b45d03
--- /dev/null
+++ b/net/net_unittests.target.mk
@@ -0,0 +1,317 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := net_unittests
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-DUSE_SYSTEM_ZLIB' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-I../net/third_party/nss/ssl \
+	-Inet/third_party/nss/ssl \
+	-IWebKit/chromium/net/third_party/nss/ssl \
+	-I/usr/include/nss \
+	-I/usr/include/nspr \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I. \
+	-Itesting/gmock/include \
+	-Itesting/gtest/include
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-DUSE_SYSTEM_ZLIB' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-I../net/third_party/nss/ssl \
+	-Inet/third_party/nss/ssl \
+	-IWebKit/chromium/net/third_party/nss/ssl \
+	-I/usr/include/nss \
+	-I/usr/include/nspr \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I. \
+	-Itesting/gmock/include \
+	-Itesting/gtest/include
+
+OBJS := $(obj).target/$(TARGET)/net/base/address_list_unittest.o \
+	$(obj).target/$(TARGET)/net/base/cookie_monster_unittest.o \
+	$(obj).target/$(TARGET)/net/base/data_url_unittest.o \
+	$(obj).target/$(TARGET)/net/base/directory_lister_unittest.o \
+	$(obj).target/$(TARGET)/net/base/dns_util_unittest.o \
+	$(obj).target/$(TARGET)/net/base/escape_unittest.o \
+	$(obj).target/$(TARGET)/net/base/file_stream_unittest.o \
+	$(obj).target/$(TARGET)/net/base/filter_unittest.o \
+	$(obj).target/$(TARGET)/net/base/forwarding_net_log_unittest.o \
+	$(obj).target/$(TARGET)/net/base/gzip_filter_unittest.o \
+	$(obj).target/$(TARGET)/net/base/host_cache_unittest.o \
+	$(obj).target/$(TARGET)/net/base/host_mapping_rules_unittest.o \
+	$(obj).target/$(TARGET)/net/base/host_resolver_impl_unittest.o \
+	$(obj).target/$(TARGET)/net/base/keygen_handler_unittest.o \
+	$(obj).target/$(TARGET)/net/base/listen_socket_unittest.o \
+	$(obj).target/$(TARGET)/net/base/mapped_host_resolver_unittest.o \
+	$(obj).target/$(TARGET)/net/base/mime_sniffer_unittest.o \
+	$(obj).target/$(TARGET)/net/base/mime_util_unittest.o \
+	$(obj).target/$(TARGET)/net/base/net_util_unittest.o \
+	$(obj).target/$(TARGET)/net/base/registry_controlled_domain_unittest.o \
+	$(obj).target/$(TARGET)/net/base/run_all_unittests.o \
+	$(obj).target/$(TARGET)/net/base/ssl_cipher_suite_names_unittest.o \
+	$(obj).target/$(TARGET)/net/base/ssl_client_auth_cache_unittest.o \
+	$(obj).target/$(TARGET)/net/base/static_cookie_policy_unittest.o \
+	$(obj).target/$(TARGET)/net/base/transport_security_state_unittest.o \
+	$(obj).target/$(TARGET)/net/base/telnet_server_unittest.o \
+	$(obj).target/$(TARGET)/net/base/test_completion_callback_unittest.o \
+	$(obj).target/$(TARGET)/net/base/upload_data_stream_unittest.o \
+	$(obj).target/$(TARGET)/net/base/x509_certificate_unittest.o \
+	$(obj).target/$(TARGET)/net/base/x509_cert_types_unittest.o \
+	$(obj).target/$(TARGET)/net/disk_cache/addr_unittest.o \
+	$(obj).target/$(TARGET)/net/disk_cache/backend_unittest.o \
+	$(obj).target/$(TARGET)/net/disk_cache/bitmap_unittest.o \
+	$(obj).target/$(TARGET)/net/disk_cache/block_files_unittest.o \
+	$(obj).target/$(TARGET)/net/disk_cache/disk_cache_test_base.o \
+	$(obj).target/$(TARGET)/net/disk_cache/entry_unittest.o \
+	$(obj).target/$(TARGET)/net/disk_cache/mapped_file_unittest.o \
+	$(obj).target/$(TARGET)/net/disk_cache/storage_block_unittest.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_auth_cache_unittest.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_ctrl_response_buffer_unittest.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_buffer_unittest.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser_ls_unittest.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser_mlsd_unittest.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser_netware_unittest.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser_vms_unittest.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_directory_listing_parser_windows_unittest.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_network_transaction_unittest.o \
+	$(obj).target/$(TARGET)/net/ftp/ftp_util_unittest.o \
+	$(obj).target/$(TARGET)/net/http/des_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_alternate_protocols_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_cache_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_filter_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_gssapi_posix_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_basic_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_digest_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_factory_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_mock.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_negotiate_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_handler_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_auth_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_byte_range_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_cache_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_chunked_decoder_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_network_layer_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_network_transaction_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_proxy_client_socket_pool_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_request_headers_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_response_headers_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_transaction_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_util_unittest.o \
+	$(obj).target/$(TARGET)/net/http/http_vary_data_unittest.o \
+	$(obj).target/$(TARGET)/net/http/mock_gssapi_library_posix.o \
+	$(obj).target/$(TARGET)/net/http/url_security_manager_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/init_proxy_resolver_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/multi_threaded_proxy_resolver_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_bypass_rules_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_config_service_linux_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_config_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_list_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_resolver_js_bindings_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_resolver_v8_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_script_fetcher_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_server_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/proxy_service_unittest.o \
+	$(obj).target/$(TARGET)/net/proxy/sync_host_resolver_bridge_unittest.o \
+	$(obj).target/$(TARGET)/net/socket/client_socket_pool_base_unittest.o \
+	$(obj).target/$(TARGET)/net/socket/socks5_client_socket_unittest.o \
+	$(obj).target/$(TARGET)/net/socket/socks_client_socket_pool_unittest.o \
+	$(obj).target/$(TARGET)/net/socket/socks_client_socket_unittest.o \
+	$(obj).target/$(TARGET)/net/socket/ssl_client_socket_unittest.o \
+	$(obj).target/$(TARGET)/net/socket/ssl_client_socket_pool_unittest.o \
+	$(obj).target/$(TARGET)/net/socket/tcp_client_socket_pool_unittest.o \
+	$(obj).target/$(TARGET)/net/socket/tcp_client_socket_unittest.o \
+	$(obj).target/$(TARGET)/net/socket/tcp_pinger_unittest.o \
+	$(obj).target/$(TARGET)/net/socket_stream/socket_stream_metrics_unittest.o \
+	$(obj).target/$(TARGET)/net/socket_stream/socket_stream_unittest.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_framer_test.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_http_stream_unittest.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_network_transaction_unittest.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_protocol_test.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_session_unittest.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_stream_unittest.o \
+	$(obj).target/$(TARGET)/net/spdy/spdy_test_util.o \
+	$(obj).target/$(TARGET)/net/tools/dump_cache/url_to_filename_encoder.o \
+	$(obj).target/$(TARGET)/net/tools/dump_cache/url_to_filename_encoder_unittest.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_job_tracker_unittest.o \
+	$(obj).target/$(TARGET)/net/url_request/url_request_unittest.o \
+	$(obj).target/$(TARGET)/net/url_request/view_cache_helper_unittest.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_frame_handler_unittest.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_handshake_draft75_unittest.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_handshake_handler_unittest.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_handshake_unittest.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_job_unittest.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_throttle_unittest.o \
+	$(obj).target/$(TARGET)/net/websockets/websocket_unittest.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/net/libnet.a $(obj).target/net/libnet_test_support.a $(obj).target/base/libbase.a $(obj).target/base/libbase_i18n.a $(obj).target/testing/libgmock.a $(obj).target/testing/libgtest.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/base/libxdg_mime.a $(obj).target/third_party/libevent/libevent.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-Wl,--gc-sections
+
+LIBS := -lrt \
+	-ldl \
+	-lgtk-x11-2.0 \
+	-lgdk-x11-2.0 \
+	-latk-1.0 \
+	-lgio-2.0 \
+	-lpangoft2-1.0 \
+	-lgdk_pixbuf-2.0 \
+	-lm \
+	-lpangocairo-1.0 \
+	-lcairo \
+	-lpango-1.0 \
+	-lfreetype \
+	-lfontconfig \
+	-lgobject-2.0 \
+	-lgmodule-2.0 \
+	-lgthread-2.0 \
+	-lglib-2.0 \
+	-lnss3 \
+	-lnssutil3 \
+	-lsmime3 \
+	-lplds4 \
+	-lplc4 \
+	-lnspr4 \
+	-lpthread \
+	-lz \
+	-lgconf-2
+
+$(builddir)/net_unittests: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(builddir)/net_unittests: LIBS := $(LIBS)
+$(builddir)/net_unittests: TOOLSET := $(TOOLSET)
+$(builddir)/net_unittests: $(OBJS) $(obj).target/net/libnet.a $(obj).target/net/libnet_test_support.a $(obj).target/base/libbase.a $(obj).target/base/libbase_i18n.a $(obj).target/testing/libgmock.a $(obj).target/testing/libgtest.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/base/libxdg_mime.a $(obj).target/third_party/libevent/libevent.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a FORCE_DO_CMD
+	$(call do_cmd,link)
+
+all_deps += $(builddir)/net_unittests
+# Add target alias
+.PHONY: net_unittests
+net_unittests: $(builddir)/net_unittests
+
+# Add executable to "all" target.
+.PHONY: all
+all: $(builddir)/net_unittests
+
diff --git a/net/ocsp/nss_ocsp.cc b/net/ocsp/nss_ocsp.cc
index c135dfd..702baa6 100644
--- a/net/ocsp/nss_ocsp.cc
+++ b/net/ocsp/nss_ocsp.cc
@@ -15,6 +15,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/condition_variable.h"
+#include "base/histogram.h"
 #include "base/logging.h"
 #include "base/message_loop.h"
 #include "base/singleton.h"
@@ -23,6 +24,7 @@
 #include "googleurl/src/gurl.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
+#include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
@@ -63,6 +65,8 @@
                                 PRUint32* http_response_data_len);
 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
 
+char* GetAlternateOCSPAIAInfo(CERTCertificate *cert);
+
 class OCSPInitSingleton : public MessageLoop::DestructionObserver {
  public:
   // Called on IO thread.
@@ -97,6 +101,11 @@
       : io_loop_(MessageLoopForIO::current()) {
     DCHECK(io_loop_);
     io_loop_->AddDestructionObserver(this);
+
+    // NSS calls the functions in the function table to download certificates
+    // or CRLs or talk to OCSP responders over HTTP.  These functions must
+    // set an NSS/NSPR error code when they fail.  Otherwise NSS will get the
+    // residual error code from an earlier failed function call.
     client_fcn_.version = 1;
     SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
     ft->createSessionFcn = OCSPCreateSession;
@@ -112,6 +121,20 @@
     if (status != SECSuccess) {
       NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
     }
+
+    // Work around NSS bugs 524013 and 564334.  NSS incorrectly thinks the
+    // CRLs for Network Solutions Certificate Authority have bad signatures,
+    // which causes certificates issued by that CA to be reported as revoked.
+    // By using OCSP for those certificates, which don't have AIA extensions,
+    // we can work around these bugs.  See http://crbug.com/41730.
+    CERT_StringFromCertFcn old_callback = NULL;
+    status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
+        GetAlternateOCSPAIAInfo, &old_callback);
+    if (status == SECSuccess) {
+      DCHECK(!old_callback);
+    } else {
+      NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
+    }
   }
 
   virtual ~OCSPInitSingleton() {
@@ -166,10 +189,8 @@
   }
 
   void AddHeader(const char* http_header_name, const char* http_header_value) {
-    if (!extra_request_headers_.empty())
-      extra_request_headers_ += "\r\n";
-    StringAppendF(&extra_request_headers_,
-                  "%s: %s", http_header_name, http_header_value);
+    extra_request_headers_.SetHeader(http_header_name,
+                                     http_header_value);
   }
 
   void Start() {
@@ -343,14 +364,12 @@
       DCHECK(!upload_content_type_.empty());
 
       request_->set_method("POST");
-      if (!extra_request_headers_.empty())
-        extra_request_headers_ += "\r\n";
-      StringAppendF(&extra_request_headers_,
-                    "Content-Type: %s", upload_content_type_.c_str());
+      extra_request_headers_.SetHeader(
+          net::HttpRequestHeaders::kContentType, upload_content_type_);
       request_->AppendBytesToUpload(upload_content_.data(),
                                     static_cast<int>(upload_content_.size()));
     }
-    if (!extra_request_headers_.empty())
+    if (!extra_request_headers_.IsEmpty())
       request_->SetExtraRequestHeaders(extra_request_headers_);
 
     request_->Start();
@@ -388,7 +407,7 @@
   base::TimeDelta timeout_;       // The timeout for OCSP
   URLRequest* request_;           // The actual request this wraps
   scoped_refptr<net::IOBuffer> buffer_;  // Read buffer
-  std::string extra_request_headers_;  // Extra headers for the request, if any
+  net::HttpRequestHeaders extra_request_headers_;
   std::string upload_content_;    // HTTP POST payload
   std::string upload_content_type_;  // MIME type of POST payload
 
@@ -421,8 +440,10 @@
     // We dont' support "https" because we haven't thought about
     // whether it's safe to re-enter this code from talking to an OCSP
     // responder over SSL.
-    if (strcmp(http_protocol_variant, "http") != 0)
+    if (strcmp(http_protocol_variant, "http") != 0) {
+      PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
       return NULL;
+    }
 
     // TODO(ukai): If |host| is an IPv6 literal, we need to quote it with
     //  square brackets [].
@@ -455,6 +476,10 @@
   DCHECK(!MessageLoop::current());
   if (OCSPInitSingleton::url_request_context() == NULL) {
     LOG(ERROR) << "No URLRequestContext for OCSP handler.";
+    // The application failed to call SetURLRequestContextForOCSP, so we
+    // can't create and use URLRequest.  PR_NOT_IMPLEMENTED_ERROR is not an
+    // accurate error code for this error condition, but is close enough.
+    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
     return SECFailure;
   }
   *pSession = new OCSPServerSession(host, portnum);
@@ -532,20 +557,21 @@
 // It is helper routine for OCSP trySendAndReceiveFcn.
 // |http_response_data_len| could be used as input parameter.  If it has
 // non-zero value, it is considered as maximum size of |http_response_data|.
-bool OCSPSetResponse(OCSPRequestSession* req,
-                     PRUint16* http_response_code,
-                     const char** http_response_content_type,
-                     const char** http_response_headers,
-                     const char** http_response_data,
-                     PRUint32* http_response_data_len) {
+SECStatus OCSPSetResponse(OCSPRequestSession* req,
+                          PRUint16* http_response_code,
+                          const char** http_response_content_type,
+                          const char** http_response_headers,
+                          const char** http_response_data,
+                          PRUint32* http_response_data_len) {
   DCHECK(req->Finished());
   const std::string& data = req->http_response_data();
   if (http_response_data_len && *http_response_data_len) {
     if (*http_response_data_len < data.size()) {
-      LOG(ERROR) << "data size too large: " << *http_response_data_len
+      LOG(ERROR) << "response body too large: " << *http_response_data_len
                  << " < " << data.size();
       *http_response_data_len = data.size();
-      return false;
+      PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);
+      return SECFailure;
     }
   }
   LOG(INFO) << "OCSP response "
@@ -563,7 +589,7 @@
     *http_response_data = data.data();
   if (http_response_data_len)
     *http_response_data_len = data.size();
-  return true;
+  return SECSuccess;
 }
 
 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
@@ -573,6 +599,12 @@
                                 const char** http_response_headers,
                                 const char** http_response_data,
                                 PRUint32* http_response_data_len) {
+  if (http_response_data_len) {
+    // We must always set an output value, even on failure.  The output value 0
+    // means the failure was unrelated to the acceptable response data length.
+    *http_response_data_len = 0;
+  }
+
   LOG(INFO) << "OCSP try send and receive";
   DCHECK(!MessageLoop::current());
   OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
@@ -584,30 +616,70 @@
     // We support blocking mode only, so this function shouldn't be called
     // again when req has stareted or finished.
     NOTREACHED();
-    goto failed;
+    PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);  // Simple approximation.
+    return SECFailure;
   }
-  req->Start();
-  if (!req->Wait())
-    goto failed;
 
-  // If the response code is -1, the request failed and there is no response.
-  if (req->http_response_code() == static_cast<PRUint16>(-1))
-    goto failed;
+  const base::Time start_time = base::Time::Now();
+  req->Start();
+  if (!req->Wait() || req->http_response_code() == static_cast<PRUint16>(-1)) {
+    // If the response code is -1, the request failed and there is no response.
+    PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);  // Simple approximation.
+    return SECFailure;
+  }
+  const base::TimeDelta duration = base::Time::Now() - start_time;
+
+  // We want to know if this was:
+  //   1) An OCSP request
+  //   2) A CRL request
+  //   3) A request for a missing intermediate certificate
+  // There's no sure way to do this, so we use heuristics like MIME type and
+  // URL.
+  const char* mime_type = req->http_response_content_type().c_str();
+  bool is_ocsp_resp =
+      strcasecmp(mime_type, "application/ocsp-response") == 0;
+  bool is_crl_resp = strcasecmp(mime_type, "application/x-pkcs7-crl") == 0 ||
+                     strcasecmp(mime_type, "application/x-x509-crl") == 0 ||
+                     strcasecmp(mime_type, "application/pkix-crl") == 0;
+  bool is_crt_resp =
+      strcasecmp(mime_type, "application/x-x509-ca-cert") == 0 ||
+      strcasecmp(mime_type, "application/x-x509-server-cert") == 0 ||
+      strcasecmp(mime_type, "application/pkix-cert") == 0 ||
+      strcasecmp(mime_type, "application/pkcs7-mime") == 0;
+  bool known_resp_type = is_crt_resp || is_crt_resp || is_ocsp_resp;
+
+  bool crl_in_url = false, crt_in_url = false, ocsp_in_url = false,
+       have_url_hint = false;
+  if (!known_resp_type) {
+    const std::string path = req->url().path();
+    const std::string host = req->url().host();
+    crl_in_url = strcasestr(path.c_str(), ".crl") != NULL;
+    crt_in_url = strcasestr(path.c_str(), ".crt") != NULL ||
+                 strcasestr(path.c_str(), ".p7c") != NULL ||
+                 strcasestr(path.c_str(), ".cer") != NULL;
+    ocsp_in_url = strcasestr(host.c_str(), "ocsp") != NULL;
+    have_url_hint = crl_in_url || crt_in_url || ocsp_in_url;
+  }
+
+  if (is_ocsp_resp ||
+      (!known_resp_type && (ocsp_in_url ||
+                            (!have_url_hint &&
+                             req->http_request_method() == "POST")))) {
+    UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration);
+  } else if (is_crl_resp || (!known_resp_type && crl_in_url)) {
+    UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration);
+  } else if (is_crt_resp || (!known_resp_type && crt_in_url)) {
+    UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration);
+  } else {
+    UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration);
+  }
 
   return OCSPSetResponse(
       req, http_response_code,
       http_response_content_type,
       http_response_headers,
       http_response_data,
-      http_response_data_len) ? SECSuccess : SECFailure;
-
- failed:
-  if (http_response_data_len) {
-    // We must always set an output value, even on failure.  The output value 0
-    // means the failure was unrelated to the acceptable response data length.
-    *http_response_data_len = 0;
-  }
-  return SECFailure;
+      http_response_data_len);
 }
 
 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) {
@@ -619,6 +691,92 @@
   return SECSuccess;
 }
 
+// Data for GetAlternateOCSPAIAInfo.
+
+// CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
+//
+// There are two CAs with this name.  Their key IDs are listed next.
+const unsigned char network_solutions_ca_name[] = {
+  0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+  0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
+  0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
+  0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
+  0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
+  0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+  0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
+  0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
+  0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+  0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
+};
+const unsigned int network_solutions_ca_name_len = 100;
+
+// This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
+const unsigned char network_solutions_ca_key_id[] = {
+  0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
+  0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
+};
+const unsigned int network_solutions_ca_key_id_len = 20;
+
+// This CA is a root CA.  It is also cross-certified by
+// UTN-USERFirst-Hardware.
+const unsigned char network_solutions_ca_key_id2[] = {
+  0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
+  0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
+};
+const unsigned int network_solutions_ca_key_id2_len = 20;
+
+// An entry in our OCSP responder table.  |issuer| and |issuer_key_id| are
+// the key.  |ocsp_url| is the value.
+struct OCSPResponderTableEntry {
+  SECItem issuer;
+  SECItem issuer_key_id;
+  const char *ocsp_url;
+};
+
+const OCSPResponderTableEntry g_ocsp_responder_table[] = {
+  {
+    {
+      siBuffer,
+      const_cast<unsigned char*>(network_solutions_ca_name),
+      network_solutions_ca_name_len
+    },
+    {
+      siBuffer,
+      const_cast<unsigned char*>(network_solutions_ca_key_id),
+      network_solutions_ca_key_id_len
+    },
+    "http://ocsp.netsolssl.com"
+  },
+  {
+    {
+      siBuffer,
+      const_cast<unsigned char*>(network_solutions_ca_name),
+      network_solutions_ca_name_len
+    },
+    {
+      siBuffer,
+      const_cast<unsigned char*>(network_solutions_ca_key_id2),
+      network_solutions_ca_key_id2_len
+    },
+    "http://ocsp.netsolssl.com"
+  }
+};
+
+char* GetAlternateOCSPAIAInfo(CERTCertificate *cert) {
+  if (cert && !cert->isRoot && cert->authKeyID) {
+    for (unsigned int i=0; i < arraysize(g_ocsp_responder_table); i++) {
+      if (SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer,
+                              &cert->derIssuer) == SECEqual &&
+          SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer_key_id,
+                              &cert->authKeyID->keyID) == SECEqual) {
+        return PORT_Strdup(g_ocsp_responder_table[i].ocsp_url);
+      }
+    }
+  }
+
+  return NULL;
+}
+
 }  // anonymous namespace
 
 namespace net {
diff --git a/net/proxy/init_proxy_resolver.cc b/net/proxy/init_proxy_resolver.cc
index 236e094..04f828b 100644
--- a/net/proxy/init_proxy_resolver.cc
+++ b/net/proxy/init_proxy_resolver.cc
@@ -8,7 +8,7 @@
 #include "base/format_macros.h"
 #include "base/logging.h"
 #include "base/string_util.h"
-#include "net/base/load_log.h"
+#include "net/base/net_log.h"
 #include "net/base/net_errors.h"
 #include "net/proxy/proxy_config.h"
 #include "net/proxy/proxy_resolver.h"
@@ -17,14 +17,17 @@
 namespace net {
 
 InitProxyResolver::InitProxyResolver(ProxyResolver* resolver,
-                                     ProxyScriptFetcher* proxy_script_fetcher)
+                                     ProxyScriptFetcher* proxy_script_fetcher,
+                                     NetLog* net_log)
     : resolver_(resolver),
       proxy_script_fetcher_(proxy_script_fetcher),
       ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(
           this, &InitProxyResolver::OnIOCompletion)),
       user_callback_(NULL),
       current_pac_url_index_(0u),
-      next_state_(STATE_NONE) {
+      next_state_(STATE_NONE),
+      net_log_(BoundNetLog::Make(
+          net_log, NetLog::SOURCE_INIT_PROXY_RESOLVER)) {
 }
 
 InitProxyResolver::~InitProxyResolver() {
@@ -33,16 +36,12 @@
 }
 
 int InitProxyResolver::Init(const ProxyConfig& config,
-                            CompletionCallback* callback,
-                            LoadLog* load_log) {
+                            CompletionCallback* callback) {
   DCHECK_EQ(STATE_NONE, next_state_);
   DCHECK(callback);
   DCHECK(config.MayRequirePACResolver());
-  DCHECK(!load_log_);
 
-  load_log_ = load_log;
-
-  LoadLog::BeginEvent(load_log_, LoadLog::TYPE_INIT_PROXY_RESOLVER);
+  net_log_.BeginEvent(NetLog::TYPE_INIT_PROXY_RESOLVER, NULL);
 
   pac_urls_ = BuildPacUrlsFallbackList(config);
   DCHECK(!pac_urls_.empty());
@@ -64,13 +63,10 @@
 InitProxyResolver::UrlList InitProxyResolver::BuildPacUrlsFallbackList(
     const ProxyConfig& config) const {
   UrlList pac_urls;
-  if (config.auto_detect) {
-     GURL pac_url = resolver_->expects_pac_bytes() ?
-        GURL("http://wpad/wpad.dat") : GURL();
-     pac_urls.push_back(pac_url);
-  }
-  if (config.pac_url.is_valid())
-    pac_urls.push_back(config.pac_url);
+  if (config.auto_detect())
+    pac_urls.push_back(PacURL(true, GURL()));
+  if (config.has_pac_url())
+    pac_urls.push_back(PacURL(false, config.pac_url()));
   return pac_urls;
 }
 
@@ -122,70 +118,73 @@
 int InitProxyResolver::DoFetchPacScript() {
   DCHECK(resolver_->expects_pac_bytes());
 
-  LoadLog::BeginEvent(load_log_,
-      LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT);
-
   next_state_ = STATE_FETCH_PAC_SCRIPT_COMPLETE;
 
-  const GURL& pac_url = current_pac_url();
+  const PacURL& pac_url = current_pac_url();
 
-  LoadLog::AddString(load_log_, pac_url.spec());
+  const GURL effective_pac_url =
+      pac_url.auto_detect ? GURL("http://wpad/wpad.dat") : pac_url.url;
+
+  net_log_.BeginEvent(
+      NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT,
+      new NetLogStringParameter("url",
+                                effective_pac_url.possibly_invalid_spec()));
 
   if (!proxy_script_fetcher_) {
-    LoadLog::AddStringLiteral(load_log_,
-        "Can't download PAC script, because no fetcher was specified");
+    net_log_.AddEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_HAS_NO_FETCHER, NULL);
     return ERR_UNEXPECTED;
   }
 
-  return proxy_script_fetcher_->Fetch(pac_url, &pac_bytes_, &io_callback_);
+  return proxy_script_fetcher_->Fetch(effective_pac_url,
+                                      &pac_script_,
+                                      &io_callback_);
 }
 
 int InitProxyResolver::DoFetchPacScriptComplete(int result) {
   DCHECK(resolver_->expects_pac_bytes());
 
-  LoadLog::AddString(load_log_,
-      StringPrintf(
-          "Completed fetch with result %s. Received %" PRIuS " bytes",
-          ErrorToString(result),
-          pac_bytes_.size()));
-
-  LoadLog::EndEvent(load_log_,
-      LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT);
-
-  if (result != OK)
+  if (result == OK) {
+    net_log_.EndEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT, NULL);
+  } else {
+    net_log_.EndEvent(
+        NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT,
+        new NetLogIntegerParameter("net_error", result));
     return TryToFallbackPacUrl(result);
+  }
 
   next_state_ = STATE_SET_PAC_SCRIPT;
   return result;
 }
 
 int InitProxyResolver::DoSetPacScript() {
-  LoadLog::BeginEvent(load_log_,
-      LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT);
+  net_log_.BeginEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT, NULL);
 
-  const GURL& pac_url = current_pac_url();
+  const PacURL& pac_url = current_pac_url();
 
   next_state_ = STATE_SET_PAC_SCRIPT_COMPLETE;
 
-  return resolver_->expects_pac_bytes() ?
-      resolver_->SetPacScriptByData(pac_bytes_, &io_callback_) :
-      resolver_->SetPacScriptByUrl(pac_url, &io_callback_);
+  scoped_refptr<ProxyResolverScriptData> script_data;
+
+  if (resolver_->expects_pac_bytes()) {
+    script_data = ProxyResolverScriptData::FromUTF16(pac_script_);
+  } else {
+    script_data = pac_url.auto_detect ?
+        ProxyResolverScriptData::ForAutoDetect() :
+        ProxyResolverScriptData::FromURL(pac_url.url);
+  }
+
+  return resolver_->SetPacScript(script_data, &io_callback_);
 }
 
 int InitProxyResolver::DoSetPacScriptComplete(int result) {
   if (result != OK) {
-    LoadLog::AddString(load_log_,
-        StringPrintf("Failed initializing the PAC script with error: %s",
-                     ErrorToString(result)));
-    LoadLog::EndEvent(load_log_,
-        LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT);
+    net_log_.EndEvent(
+        NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT,
+        new NetLogIntegerParameter("net_error", result));
     return TryToFallbackPacUrl(result);
   }
 
-  LoadLog::AddStringLiteral(load_log_, "Successfully initialized PAC script.");
-
-  LoadLog::EndEvent(load_log_,
-      LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT);
+  net_log_.EndEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT, NULL);
   return result;
 }
 
@@ -200,7 +199,8 @@
   // Advance to next URL in our list.
   ++current_pac_url_index_;
 
-  LoadLog::AddStringLiteral(load_log_, "Falling back to next PAC URL...");
+  net_log_.AddEvent(
+      NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL, NULL);
 
   next_state_ = GetStartState();
 
@@ -212,19 +212,19 @@
       STATE_FETCH_PAC_SCRIPT : STATE_SET_PAC_SCRIPT;
 }
 
-const GURL& InitProxyResolver::current_pac_url() const {
+const InitProxyResolver::PacURL& InitProxyResolver::current_pac_url() const {
   DCHECK_LT(current_pac_url_index_, pac_urls_.size());
   return pac_urls_[current_pac_url_index_];
 }
 
 void InitProxyResolver::DidCompleteInit() {
-  LoadLog::EndEvent(load_log_, LoadLog::TYPE_INIT_PROXY_RESOLVER);
+  net_log_.EndEvent(NetLog::TYPE_INIT_PROXY_RESOLVER, NULL);
 }
 
 void InitProxyResolver::Cancel() {
   DCHECK_NE(STATE_NONE, next_state_);
 
-  LoadLog::AddEvent(load_log_, LoadLog::TYPE_CANCELLED);
+  net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
 
   switch (next_state_) {
     case STATE_FETCH_PAC_SCRIPT_COMPLETE:
diff --git a/net/proxy/init_proxy_resolver.h b/net/proxy/init_proxy_resolver.h
index 0bec8e9..814e932 100644
--- a/net/proxy/init_proxy_resolver.h
+++ b/net/proxy/init_proxy_resolver.h
@@ -10,10 +10,10 @@
 
 #include "googleurl/src/gurl.h"
 #include "net/base/completion_callback.h"
+#include "net/base/net_log.h"
 
 namespace net {
 
-class LoadLog;
 class ProxyConfig;
 class ProxyResolver;
 class ProxyScriptFetcher;
@@ -36,18 +36,18 @@
 //
 class InitProxyResolver {
  public:
-  // |resolver| and |proxy_script_fetcher| must remain valid for
+  // |resolver|, |proxy_script_fetcher| and |net_log| must remain valid for
   // the lifespan of InitProxyResolver.
   InitProxyResolver(ProxyResolver* resolver,
-                    ProxyScriptFetcher* proxy_script_fetcher);
+                    ProxyScriptFetcher* proxy_script_fetcher,
+                    NetLog* net_log);
 
   // Aborts any in-progress request.
   ~InitProxyResolver();
 
   // Apply the PAC settings of |config| to |resolver_|.
   int Init(const ProxyConfig& config,
-           CompletionCallback* callback,
-           LoadLog* load_log);
+           CompletionCallback* callback);
 
  private:
   enum State {
@@ -57,7 +57,15 @@
     STATE_SET_PAC_SCRIPT,
     STATE_SET_PAC_SCRIPT_COMPLETE,
   };
-  typedef std::vector<GURL> UrlList;
+
+  struct PacURL {
+    PacURL(bool auto_detect, const GURL& url)
+        : auto_detect(auto_detect), url(url) {}
+    bool auto_detect;
+    GURL url;
+  };
+
+  typedef std::vector<PacURL> UrlList;
 
   // Returns ordered list of PAC urls to try for |config|.
   UrlList BuildPacUrlsFallbackList(const ProxyConfig& config) const;
@@ -83,7 +91,7 @@
   State GetStartState() const;
 
   // Returns the current PAC URL we are fetching/testing.
-  const GURL& current_pac_url() const;
+  const PacURL& current_pac_url() const;
 
   void DidCompleteInit();
   void Cancel();
@@ -97,12 +105,12 @@
   size_t current_pac_url_index_;
 
   // Filled when the PAC script fetch completes.
-  std::string pac_bytes_;
+  string16 pac_script_;
 
   UrlList pac_urls_;
   State next_state_;
 
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
 
   DISALLOW_COPY_AND_ASSIGN(InitProxyResolver);
 };
diff --git a/net/proxy/init_proxy_resolver_unittest.cc b/net/proxy/init_proxy_resolver_unittest.cc
index 46b8b79..458cc53 100644
--- a/net/proxy/init_proxy_resolver_unittest.cc
+++ b/net/proxy/init_proxy_resolver_unittest.cc
@@ -4,10 +4,11 @@
 
 #include <vector>
 
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "net/base/net_errors.h"
-#include "net/base/load_log.h"
-#include "net/base/load_log_util.h"
-#include "net/base/load_log_unittest.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/test_completion_callback.h"
 #include "net/proxy/init_proxy_resolver.h"
 #include "net/proxy/proxy_config.h"
@@ -34,12 +35,12 @@
           set_pac_error(set_pac_error) {
     }
 
-    std::string bytes() const {
+    string16 text() const {
       if (set_pac_error == OK)
-        return url.spec() + "!valid-script";
+        return UTF8ToUTF16(url.spec() + "!valid-script");
       if (fetch_error == OK)
-        return url.spec() + "!invalid-script";
-      return std::string();
+        return UTF8ToUTF16(url.spec() + "!invalid-script");
+      return string16();
     }
 
     GURL url;
@@ -69,17 +70,17 @@
       if (it->url == url)
         return *it;
     }
-    CHECK(false) << "Rule not found for " << url;
+    LOG(FATAL) << "Rule not found for " << url;
     return rules_[0];
   }
 
-  const Rule& GetRuleByBytes(const std::string& bytes) const {
+  const Rule& GetRuleByText(const string16& text) const {
     for (RuleList::const_iterator it = rules_.begin(); it != rules_.end();
          ++it) {
-      if (it->bytes() == bytes)
+      if (it->text() == text)
         return *it;
     }
-    CHECK(false) << "Rule not found for " << bytes;
+    LOG(FATAL) << "Rule not found for " << text;
     return rules_[0];
   }
 
@@ -94,13 +95,13 @@
 
   // ProxyScriptFetcher implementation.
   virtual int Fetch(const GURL& url,
-                    std::string* bytes,
+                    string16* text,
                     CompletionCallback* callback) {
     const Rules::Rule& rule = rules_->GetRuleByUrl(url);
     int rv = rule.fetch_error;
     EXPECT_NE(ERR_UNEXPECTED, rv);
     if (rv == OK)
-      *bytes = rule.bytes();
+      *text = rule.text();
     return rv;
   }
 
@@ -120,7 +121,7 @@
                              ProxyInfo* /*results*/,
                              CompletionCallback* /*callback*/,
                              RequestHandle* /*request_handle*/,
-                             LoadLog* /*load_log*/) {
+                             const BoundNetLog& /*net_log*/) {
     NOTREACHED();
     return ERR_UNEXPECTED;
   }
@@ -129,35 +130,37 @@
     NOTREACHED();
   }
 
-  virtual int SetPacScript(const GURL& pac_url,
-                           const std::string& pac_bytes,
-                           CompletionCallback* callback) {
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& script_data,
+      CompletionCallback* callback) {
+
+   const GURL url =
+      script_data->type() == ProxyResolverScriptData::TYPE_SCRIPT_URL ?
+          script_data->url() : GURL();
+
     const Rules::Rule& rule = expects_pac_bytes() ?
-        rules_->GetRuleByBytes(pac_bytes) :
-        rules_->GetRuleByUrl(pac_url);
+        rules_->GetRuleByText(script_data->utf16()) :
+        rules_->GetRuleByUrl(url);
 
     int rv = rule.set_pac_error;
     EXPECT_NE(ERR_UNEXPECTED, rv);
 
-    if (expects_pac_bytes())
-      EXPECT_EQ(rule.bytes(), pac_bytes);
-    else
-      EXPECT_EQ(rule.url, pac_url);
-
-    if (rv == OK) {
-      pac_bytes_ = pac_bytes;
-      pac_url_ = pac_url;
+    if (expects_pac_bytes()) {
+      EXPECT_EQ(rule.text(), script_data->utf16());
+    } else {
+      EXPECT_EQ(rule.url, url);
     }
+
+    if (rv == OK)
+      script_data_ = script_data;
     return rv;
   }
 
-  const std::string& pac_bytes() const { return pac_bytes_; }
-  const GURL& pac_url() const { return pac_url_; }
+  const ProxyResolverScriptData* script_data() const { return script_data_; }
 
  private:
   const Rules* rules_;
-  std::string pac_bytes_;
-  GURL pac_url_;
+  scoped_refptr<ProxyResolverScriptData> script_data_;
 };
 
 // Succeed using custom PAC script.
@@ -167,29 +170,30 @@
   RuleBasedProxyScriptFetcher fetcher(&rules);
 
   ProxyConfig config;
-  config.pac_url = GURL("http://custom/proxy.pac");
+  config.set_pac_url(GURL("http://custom/proxy.pac"));
 
   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
 
   TestCompletionCallback callback;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  InitProxyResolver init(&resolver, &fetcher);
-  EXPECT_EQ(OK, init.Init(config, &callback, log));
-  EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
+  InitProxyResolver init(&resolver, &fetcher, &log);
+  EXPECT_EQ(OK, init.Init(config, &callback));
+  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
 
-  // Check the LoadLog was filled correctly.
-  EXPECT_EQ(9u, log->entries().size());
+  // Check the NetLog was filled correctly.
+  EXPECT_EQ(6u, log.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 0, LoadLog::TYPE_INIT_PROXY_RESOLVER));
+      log.entries(), 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 1, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+      log.entries(), 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 4, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+      log.entries(), 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 5, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+      log.entries(), 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 7, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
-  EXPECT_TRUE(LogContainsEndEvent(*log, 8, LoadLog::TYPE_INIT_PROXY_RESOLVER));
+      log.entries(), 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 5, NetLog::TYPE_INIT_PROXY_RESOLVER));
 }
 
 // Fail downloading the custom PAC script.
@@ -199,25 +203,26 @@
   RuleBasedProxyScriptFetcher fetcher(&rules);
 
   ProxyConfig config;
-  config.pac_url = GURL("http://custom/proxy.pac");
+  config.set_pac_url(GURL("http://custom/proxy.pac"));
 
   rules.AddFailDownloadRule("http://custom/proxy.pac");
 
   TestCompletionCallback callback;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  InitProxyResolver init(&resolver, &fetcher);
-  EXPECT_EQ(kFailedDownloading, init.Init(config, &callback, log));
-  EXPECT_EQ("", resolver.pac_bytes());
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
+  InitProxyResolver init(&resolver, &fetcher, &log);
+  EXPECT_EQ(kFailedDownloading, init.Init(config, &callback));
+  EXPECT_EQ(NULL, resolver.script_data());
 
-  // Check the LoadLog was filled correctly.
-  EXPECT_EQ(6u, log->entries().size());
+  // Check the NetLog was filled correctly.
+  EXPECT_EQ(4u, log.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 0, LoadLog::TYPE_INIT_PROXY_RESOLVER));
+      log.entries(), 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 1, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+      log.entries(), 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 4, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
-  EXPECT_TRUE(LogContainsEndEvent(*log, 5, LoadLog::TYPE_INIT_PROXY_RESOLVER));
+      log.entries(), 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 3, NetLog::TYPE_INIT_PROXY_RESOLVER));
 }
 
 // Fail parsing the custom PAC script.
@@ -227,14 +232,14 @@
   RuleBasedProxyScriptFetcher fetcher(&rules);
 
   ProxyConfig config;
-  config.pac_url = GURL("http://custom/proxy.pac");
+  config.set_pac_url(GURL("http://custom/proxy.pac"));
 
   rules.AddFailParsingRule("http://custom/proxy.pac");
 
   TestCompletionCallback callback;
-  InitProxyResolver init(&resolver, &fetcher);
-  EXPECT_EQ(kFailedParsing, init.Init(config, &callback, NULL));
-  EXPECT_EQ("", resolver.pac_bytes());
+  InitProxyResolver init(&resolver, &fetcher, NULL);
+  EXPECT_EQ(kFailedParsing, init.Init(config, &callback));
+  EXPECT_EQ(NULL, resolver.script_data());
 }
 
 // Fail downloading the custom PAC script, because the fetcher was NULL.
@@ -243,12 +248,12 @@
   RuleBasedProxyResolver resolver(&rules, true /*expects_pac_bytes*/);
 
   ProxyConfig config;
-  config.pac_url = GURL("http://custom/proxy.pac");
+  config.set_pac_url(GURL("http://custom/proxy.pac"));
 
   TestCompletionCallback callback;
-  InitProxyResolver init(&resolver, NULL);
-  EXPECT_EQ(ERR_UNEXPECTED, init.Init(config, &callback, NULL));
-  EXPECT_EQ("", resolver.pac_bytes());
+  InitProxyResolver init(&resolver, NULL, NULL);
+  EXPECT_EQ(ERR_UNEXPECTED, init.Init(config, &callback));
+  EXPECT_EQ(NULL, resolver.script_data());
 }
 
 // Succeeds in choosing autodetect (wpad).
@@ -258,14 +263,14 @@
   RuleBasedProxyScriptFetcher fetcher(&rules);
 
   ProxyConfig config;
-  config.auto_detect = true;
+  config.set_auto_detect(true);
 
   Rules::Rule rule = rules.AddSuccessRule("http://wpad/wpad.dat");
 
   TestCompletionCallback callback;
-  InitProxyResolver init(&resolver, &fetcher);
-  EXPECT_EQ(OK, init.Init(config, &callback, NULL));
-  EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
+  InitProxyResolver init(&resolver, &fetcher, NULL);
+  EXPECT_EQ(OK, init.Init(config, &callback));
+  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
 }
 
 // Fails at WPAD (downloading), but succeeds in choosing the custom PAC.
@@ -275,16 +280,16 @@
   RuleBasedProxyScriptFetcher fetcher(&rules);
 
   ProxyConfig config;
-  config.auto_detect = true;
-  config.pac_url = GURL("http://custom/proxy.pac");
+  config.set_auto_detect(true);
+  config.set_pac_url(GURL("http://custom/proxy.pac"));
 
   rules.AddFailDownloadRule("http://wpad/wpad.dat");
   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
 
   TestCompletionCallback callback;
-  InitProxyResolver init(&resolver, &fetcher);
-  EXPECT_EQ(OK, init.Init(config, &callback, NULL));
-  EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
+  InitProxyResolver init(&resolver, &fetcher, NULL);
+  EXPECT_EQ(OK, init.Init(config, &callback));
+  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
 }
 
 // Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
@@ -294,41 +299,46 @@
   RuleBasedProxyScriptFetcher fetcher(&rules);
 
   ProxyConfig config;
-  config.auto_detect = true;
-  config.pac_url = GURL("http://custom/proxy.pac");
+  config.set_auto_detect(true);
+  config.set_pac_url(GURL("http://custom/proxy.pac"));
 
   rules.AddFailParsingRule("http://wpad/wpad.dat");
   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
 
   TestCompletionCallback callback;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  InitProxyResolver init(&resolver, &fetcher);
-  EXPECT_EQ(OK, init.Init(config, &callback, log));
-  EXPECT_EQ(rule.bytes(), resolver.pac_bytes());
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
+  InitProxyResolver init(&resolver, &fetcher, &log);
+  EXPECT_EQ(OK, init.Init(config, &callback));
+  EXPECT_EQ(rule.text(), resolver.script_data()->utf16());
 
-  // Check the LoadLog was filled correctly.
+  // Check the NetLog was filled correctly.
   // (Note that the Fetch and Set states are repeated since both WPAD and custom
   // PAC scripts are tried).
-  EXPECT_EQ(17u, log->entries().size());
+  EXPECT_EQ(11u, log.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 0, LoadLog::TYPE_INIT_PROXY_RESOLVER));
+      log.entries(), 0, NetLog::TYPE_INIT_PROXY_RESOLVER));
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 1, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+      log.entries(), 1, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 4, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+      log.entries(), 2, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 5, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+      log.entries(), 3, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 7, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+      log.entries(), 4, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+  EXPECT_TRUE(LogContainsEvent(
+      log.entries(), 5,
+      NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL,
+      NetLog::PHASE_NONE));
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 9, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+      log.entries(), 6, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 12, LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
+      log.entries(), 7, NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT));
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 13, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+      log.entries(), 8, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 15, LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
-  EXPECT_TRUE(LogContainsEndEvent(*log, 16, LoadLog::TYPE_INIT_PROXY_RESOLVER));
+      log.entries(), 9, NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 10, NetLog::TYPE_INIT_PROXY_RESOLVER));
 }
 
 // Fails at WPAD (downloading), and fails at custom PAC (downloading).
@@ -338,16 +348,16 @@
   RuleBasedProxyScriptFetcher fetcher(&rules);
 
   ProxyConfig config;
-  config.auto_detect = true;
-  config.pac_url = GURL("http://custom/proxy.pac");
+  config.set_auto_detect(true);
+  config.set_pac_url(GURL("http://custom/proxy.pac"));
 
   rules.AddFailDownloadRule("http://wpad/wpad.dat");
   rules.AddFailDownloadRule("http://custom/proxy.pac");
 
   TestCompletionCallback callback;
-  InitProxyResolver init(&resolver, &fetcher);
-  EXPECT_EQ(kFailedDownloading, init.Init(config, &callback, NULL));
-  EXPECT_EQ("", resolver.pac_bytes());
+  InitProxyResolver init(&resolver, &fetcher, NULL);
+  EXPECT_EQ(kFailedDownloading, init.Init(config, &callback));
+  EXPECT_EQ(NULL, resolver.script_data());
 }
 
 // Fails at WPAD (downloading), and fails at custom PAC (parsing).
@@ -357,16 +367,16 @@
   RuleBasedProxyScriptFetcher fetcher(&rules);
 
   ProxyConfig config;
-  config.auto_detect = true;
-  config.pac_url = GURL("http://custom/proxy.pac");
+  config.set_auto_detect(true);
+  config.set_pac_url(GURL("http://custom/proxy.pac"));
 
   rules.AddFailDownloadRule("http://wpad/wpad.dat");
   rules.AddFailParsingRule("http://custom/proxy.pac");
 
   TestCompletionCallback callback;
-  InitProxyResolver init(&resolver, &fetcher);
-  EXPECT_EQ(kFailedParsing, init.Init(config, &callback, NULL));
-  EXPECT_EQ("", resolver.pac_bytes());
+  InitProxyResolver init(&resolver, &fetcher, NULL);
+  EXPECT_EQ(kFailedParsing, init.Init(config, &callback));
+  EXPECT_EQ(NULL, resolver.script_data());
 }
 
 // Fails at WPAD (parsing), but succeeds in choosing the custom PAC.
@@ -378,16 +388,16 @@
   RuleBasedProxyScriptFetcher fetcher(&rules);
 
   ProxyConfig config;
-  config.auto_detect = true;
-  config.pac_url = GURL("http://custom/proxy.pac");
+  config.set_auto_detect(true);
+  config.set_pac_url(GURL("http://custom/proxy.pac"));
 
   rules.AddFailParsingRule("");  // Autodetect.
   Rules::Rule rule = rules.AddSuccessRule("http://custom/proxy.pac");
 
   TestCompletionCallback callback;
-  InitProxyResolver init(&resolver, &fetcher);
-  EXPECT_EQ(OK, init.Init(config, &callback, NULL));
-  EXPECT_EQ(rule.url, resolver.pac_url());
+  InitProxyResolver init(&resolver, &fetcher, NULL);
+  EXPECT_EQ(OK, init.Init(config, &callback));
+  EXPECT_EQ(rule.url, resolver.script_data()->url());
 }
 
 }  // namespace
diff --git a/net/proxy/mock_proxy_resolver.h b/net/proxy/mock_proxy_resolver.h
index c284eb1..9babb66 100644
--- a/net/proxy/mock_proxy_resolver.h
+++ b/net/proxy/mock_proxy_resolver.h
@@ -59,19 +59,17 @@
 
   class SetPacScriptRequest {
    public:
-    SetPacScriptRequest(MockAsyncProxyResolverBase* resolver,
-                        const GURL& pac_url,
-                        const std::string& pac_bytes,
-                        CompletionCallback* callback)
+    SetPacScriptRequest(
+        MockAsyncProxyResolverBase* resolver,
+        const scoped_refptr<ProxyResolverScriptData>& script_data,
+        CompletionCallback* callback)
         : resolver_(resolver),
-          pac_url_(pac_url),
-          pac_bytes_(pac_bytes),
+          script_data_(script_data),
           callback_(callback),
           origin_loop_(MessageLoop::current()) {
     }
 
-    const GURL& pac_url() const { return pac_url_; }
-    const std::string& pac_bytes() const { return pac_bytes_; }
+    const ProxyResolverScriptData* script_data() const { return script_data_; }
 
     void CompleteNow(int rv) {
        CompletionCallback* callback = callback_;
@@ -84,8 +82,7 @@
 
    private:
     MockAsyncProxyResolverBase* resolver_;
-    const GURL pac_url_;
-    const std::string pac_bytes_;
+    const scoped_refptr<ProxyResolverScriptData> script_data_;
     CompletionCallback* callback_;
     MessageLoop* origin_loop_;
   };
@@ -97,7 +94,7 @@
                              ProxyInfo* results,
                              CompletionCallback* callback,
                              RequestHandle* request_handle,
-                             LoadLog* /*load_log*/) {
+                             const BoundNetLog& /*net_log*/) {
     scoped_refptr<Request> request = new Request(this, url, results, callback);
     pending_requests_.push_back(request);
 
@@ -114,12 +111,12 @@
     RemovePendingRequest(request);
   }
 
-  virtual int SetPacScript(const GURL& pac_url,
-                           const std::string& pac_bytes,
-                           CompletionCallback* callback) {
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& script_data,
+      CompletionCallback* callback) {
     DCHECK(!pending_set_pac_script_request_.get());
     pending_set_pac_script_request_.reset(
-        new SetPacScriptRequest(this, pac_url, pac_bytes, callback));
+        new SetPacScriptRequest(this, script_data, callback));
     // Finished when user calls SetPacScriptRequest::CompleteNow().
     return ERR_IO_PENDING;
   }
diff --git a/net/proxy/multi_threaded_proxy_resolver.cc b/net/proxy/multi_threaded_proxy_resolver.cc
new file mode 100644
index 0000000..69e9ef7
--- /dev/null
+++ b/net/proxy/multi_threaded_proxy_resolver.cc
@@ -0,0 +1,575 @@
+// Copyright (c) 2010 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/proxy/multi_threaded_proxy_resolver.h"
+
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "net/base/capturing_net_log.h"
+#include "net/base/net_errors.h"
+#include "net/proxy/proxy_info.h"
+
+// TODO(eroman): Have the MultiThreadedProxyResolver clear its PAC script
+//               data when SetPacScript fails. That will reclaim memory when
+//               testing bogus scripts.
+
+namespace net {
+
+namespace {
+
+class PurgeMemoryTask : public base::RefCountedThreadSafe<PurgeMemoryTask> {
+ public:
+  explicit PurgeMemoryTask(ProxyResolver* resolver) : resolver_(resolver) {}
+  void PurgeMemory() { resolver_->PurgeMemory(); }
+ private:
+  friend class base::RefCountedThreadSafe<PurgeMemoryTask>;
+  ~PurgeMemoryTask() {}
+  ProxyResolver* resolver_;
+};
+
+}  // namespace
+
+// An "executor" is a job-runner for PAC requests. It encapsulates a worker
+// thread and a synchronous ProxyResolver (which will be operated on said
+// thread.)
+class MultiThreadedProxyResolver::Executor
+    : public base::RefCountedThreadSafe<MultiThreadedProxyResolver::Executor > {
+ public:
+  // |coordinator| must remain valid throughout our lifetime. It is used to
+  // signal when the executor is ready to receive work by calling
+  // |coordinator->OnExecutorReady()|.
+  // The constructor takes ownership of |resolver|.
+  // |thread_number| is an identifier used when naming the worker thread.
+  Executor(MultiThreadedProxyResolver* coordinator,
+           ProxyResolver* resolver,
+           int thread_number);
+
+  // Submit a job to this executor.
+  void StartJob(Job* job);
+
+  // Callback for when a job has completed running on the executor's thread.
+  void OnJobCompleted(Job* job);
+
+  // Cleanup the executor. Cancels all outstanding work, and frees the thread
+  // and resolver.
+  void Destroy();
+
+  void PurgeMemory();
+
+  // Returns the outstanding job, or NULL.
+  Job* outstanding_job() const { return outstanding_job_.get(); }
+
+  ProxyResolver* resolver() { return resolver_.get(); }
+
+  int thread_number() const { return thread_number_; }
+
+ private:
+  friend class base::RefCountedThreadSafe<Executor>;
+  ~Executor();
+
+  MultiThreadedProxyResolver* coordinator_;
+  const int thread_number_;
+
+  // The currently active job for this executor (either a SetPacScript or
+  // GetProxyForURL task).
+  scoped_refptr<Job> outstanding_job_;
+
+  // The synchronous resolver implementation.
+  scoped_ptr<ProxyResolver> resolver_;
+
+  // The thread where |resolver_| is run on.
+  // Note that declaration ordering is important here. |thread_| needs to be
+  // destroyed *before* |resolver_|, in case |resolver_| is currently
+  // executing on |thread_|.
+  scoped_ptr<base::Thread> thread_;
+};
+
+// MultiThreadedProxyResolver::Job ---------------------------------------------
+
+class MultiThreadedProxyResolver::Job
+    : public base::RefCountedThreadSafe<MultiThreadedProxyResolver::Job> {
+ public:
+  // Identifies the subclass of Job (only being used for debugging purposes).
+  enum Type {
+    TYPE_GET_PROXY_FOR_URL,
+    TYPE_SET_PAC_SCRIPT,
+    TYPE_SET_PAC_SCRIPT_INTERNAL,
+  };
+
+  Job(Type type, CompletionCallback* user_callback)
+      : type_(type),
+        user_callback_(user_callback),
+        executor_(NULL),
+        was_cancelled_(false) {
+  }
+
+  void set_executor(Executor* executor) {
+    executor_ = executor;
+  }
+
+  // The "executor" is the job runner that is scheduling this job. If
+  // this job has not been submitted to an executor yet, this will be
+  // NULL (and we know it hasn't started yet).
+  Executor* executor() {
+    return executor_;
+  }
+
+  // Mark the job as having been cancelled.
+  void Cancel() {
+    was_cancelled_ = true;
+  }
+
+  // Returns true if Cancel() has been called.
+  bool was_cancelled() const { return was_cancelled_; }
+
+  Type type() const { return type_; }
+
+  // Returns true if this job still has a user callback. Some jobs
+  // do not have a user callback, because they were helper jobs
+  // scheduled internally (for example TYPE_SET_PAC_SCRIPT_INTERNAL).
+  //
+  // Otherwise jobs that correspond with user-initiated work will
+  // have a non-NULL callback up until the callback is run.
+  bool has_user_callback() const { return user_callback_ != NULL; }
+
+  // This method is called when the job is inserted into a wait queue
+  // because no executors were ready to accept it.
+  virtual void WaitingForThread() {}
+
+  // This method is called just before the job is posted to the work thread.
+  virtual void FinishedWaitingForThread() {}
+
+  // This method is called on the worker thread to do the job's work. On
+  // completion, implementors are expected to call OnJobCompleted() on
+  // |origin_loop|.
+  virtual void Run(MessageLoop* origin_loop) = 0;
+
+ protected:
+  void OnJobCompleted() {
+    // |executor_| will be NULL if the executor has already been deleted.
+    if (executor_)
+      executor_->OnJobCompleted(this);
+  }
+
+  void RunUserCallback(int result) {
+    DCHECK(has_user_callback());
+    CompletionCallback* callback = user_callback_;
+    // Null the callback so has_user_callback() will now return false.
+    user_callback_ = NULL;
+    callback->Run(result);
+  }
+
+  friend class base::RefCountedThreadSafe<MultiThreadedProxyResolver::Job>;
+
+  virtual ~Job() {}
+
+ private:
+  const Type type_;
+  CompletionCallback* user_callback_;
+  Executor* executor_;
+  bool was_cancelled_;
+};
+
+// MultiThreadedProxyResolver::SetPacScriptJob ---------------------------------
+
+// Runs on the worker thread to call ProxyResolver::SetPacScript.
+class MultiThreadedProxyResolver::SetPacScriptJob
+    : public MultiThreadedProxyResolver::Job {
+ public:
+  SetPacScriptJob(const scoped_refptr<ProxyResolverScriptData>& script_data,
+                  CompletionCallback* callback)
+    : Job(callback ? TYPE_SET_PAC_SCRIPT : TYPE_SET_PAC_SCRIPT_INTERNAL,
+          callback),
+      script_data_(script_data) {
+  }
+
+  // Runs on the worker thread.
+  virtual void Run(MessageLoop* origin_loop) {
+    ProxyResolver* resolver = executor()->resolver();
+    int rv = resolver->SetPacScript(script_data_, NULL);
+
+    DCHECK_NE(rv, ERR_IO_PENDING);
+    origin_loop->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this, &SetPacScriptJob::RequestComplete, rv));
+  }
+
+ private:
+  // Runs the completion callback on the origin thread.
+  void RequestComplete(int result_code) {
+    // The task may have been cancelled after it was started.
+    if (!was_cancelled() && has_user_callback()) {
+      RunUserCallback(result_code);
+    }
+    OnJobCompleted();
+  }
+
+  const scoped_refptr<ProxyResolverScriptData> script_data_;
+};
+
+// MultiThreadedProxyResolver::GetProxyForURLJob ------------------------------
+
+class MultiThreadedProxyResolver::GetProxyForURLJob
+    : public MultiThreadedProxyResolver::Job {
+ public:
+  // |url|         -- the URL of the query.
+  // |results|     -- the structure to fill with proxy resolve results.
+  GetProxyForURLJob(const GURL& url,
+                    ProxyInfo* results,
+                    CompletionCallback* callback,
+                    const BoundNetLog& net_log)
+      : Job(TYPE_GET_PROXY_FOR_URL, callback),
+        results_(results),
+        net_log_(net_log),
+        url_(url),
+        was_waiting_for_thread_(false) {
+    DCHECK(callback);
+  }
+
+  BoundNetLog* net_log() { return &net_log_; }
+
+  virtual void WaitingForThread() {
+    was_waiting_for_thread_ = true;
+    net_log_.BeginEvent(
+        NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD, NULL);
+  }
+
+  virtual void FinishedWaitingForThread() {
+    DCHECK(executor());
+
+    if (was_waiting_for_thread_) {
+      net_log_.EndEvent(
+          NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD, NULL);
+    }
+
+    net_log_.AddEvent(
+        NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
+        new NetLogIntegerParameter(
+            "thread_number", executor()->thread_number()));
+  }
+
+  // Runs on the worker thread.
+  virtual void Run(MessageLoop* origin_loop) {
+    const size_t kNetLogBound = 50u;
+    worker_log_.reset(new CapturingNetLog(kNetLogBound));
+    BoundNetLog bound_worker_log(NetLog::Source(), worker_log_.get());
+
+    ProxyResolver* resolver = executor()->resolver();
+    int rv = resolver->GetProxyForURL(
+        url_, &results_buf_, NULL, NULL, bound_worker_log);
+    DCHECK_NE(rv, ERR_IO_PENDING);
+
+    origin_loop->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this, &GetProxyForURLJob::QueryComplete, rv));
+  }
+
+ private:
+  // Runs the completion callback on the origin thread.
+  void QueryComplete(int result_code) {
+    // The Job may have been cancelled after it was started.
+    if (!was_cancelled()) {
+      // Merge the load log that was generated on the worker thread, into the
+      // main log.
+      CapturingBoundNetLog bound_worker_log(NetLog::Source(),
+                                            worker_log_.release());
+      bound_worker_log.AppendTo(net_log_);
+
+      if (result_code >= OK) {  // Note: unit-tests use values > 0.
+        results_->Use(results_buf_);
+      }
+      RunUserCallback(result_code);
+    }
+    OnJobCompleted();
+  }
+
+  // Must only be used on the "origin" thread.
+  ProxyInfo* results_;
+  BoundNetLog net_log_;
+  const GURL url_;
+
+  // Usable from within DoQuery on the worker thread.
+  ProxyInfo results_buf_;
+
+  // Used to pass the captured events between DoQuery [worker thread] and
+  // QueryComplete [origin thread].
+  scoped_ptr<CapturingNetLog> worker_log_;
+
+  bool was_waiting_for_thread_;
+};
+
+// MultiThreadedProxyResolver::Executor ----------------------------------------
+
+MultiThreadedProxyResolver::Executor::Executor(
+    MultiThreadedProxyResolver* coordinator,
+    ProxyResolver* resolver,
+    int thread_number)
+    : coordinator_(coordinator),
+      thread_number_(thread_number),
+      resolver_(resolver) {
+  DCHECK(coordinator);
+  DCHECK(resolver);
+  // Start up the thread.
+  // Note that it is safe to pass a temporary C-String to Thread(), as it will
+  // make a copy.
+  std::string thread_name =
+      StringPrintf("PAC thread #%d", thread_number);
+  thread_.reset(new base::Thread(thread_name.c_str()));
+  thread_->Start();
+}
+
+void MultiThreadedProxyResolver::Executor::StartJob(Job* job) {
+  DCHECK(!outstanding_job_);
+  outstanding_job_ = job;
+
+  // Run the job. Once it has completed (regardless of whether it was
+  // cancelled), it will invoke OnJobCompleted() on this thread.
+  job->set_executor(this);
+  job->FinishedWaitingForThread();
+  thread_->message_loop()->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(job, &Job::Run, MessageLoop::current()));
+}
+
+void MultiThreadedProxyResolver::Executor::OnJobCompleted(Job* job) {
+  DCHECK_EQ(job, outstanding_job_.get());
+  outstanding_job_ = NULL;
+  coordinator_->OnExecutorReady(this);
+}
+
+void MultiThreadedProxyResolver::Executor::Destroy() {
+  DCHECK(coordinator_);
+
+  // Give the resolver an opportunity to shutdown from THIS THREAD before
+  // joining on the resolver thread. This allows certain implementations
+  // to avoid deadlocks.
+  resolver_->Shutdown();
+
+  // Join the worker thread.
+  thread_.reset();
+
+  // Cancel any outstanding job.
+  if (outstanding_job_) {
+    outstanding_job_->Cancel();
+    // Orphan the job (since this executor may be deleted soon).
+    outstanding_job_->set_executor(NULL);
+  }
+
+  // It is now safe to free the ProxyResolver, since all the tasks that
+  // were using it on the resolver thread have completed.
+  resolver_.reset();
+
+  // Null some stuff as a precaution.
+  coordinator_ = NULL;
+  outstanding_job_ = NULL;
+}
+
+void MultiThreadedProxyResolver::Executor::PurgeMemory() {
+  scoped_refptr<PurgeMemoryTask> helper(new PurgeMemoryTask(resolver_.get()));
+  thread_->message_loop()->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(helper.get(), &PurgeMemoryTask::PurgeMemory));
+}
+
+MultiThreadedProxyResolver::Executor::~Executor() {
+  // The important cleanup happens as part of Destroy(), which should always be
+  // called first.
+  DCHECK(!coordinator_) << "Destroy() was not called";
+  DCHECK(!thread_.get());
+  DCHECK(!resolver_.get());
+  DCHECK(!outstanding_job_);
+}
+
+// MultiThreadedProxyResolver --------------------------------------------------
+
+MultiThreadedProxyResolver::MultiThreadedProxyResolver(
+    ProxyResolverFactory* resolver_factory,
+    size_t max_num_threads)
+    : ProxyResolver(resolver_factory->resolvers_expect_pac_bytes()),
+      resolver_factory_(resolver_factory),
+      max_num_threads_(max_num_threads) {
+  DCHECK_GE(max_num_threads, 1u);
+}
+
+MultiThreadedProxyResolver::~MultiThreadedProxyResolver() {
+  // We will cancel all outstanding requests.
+  pending_jobs_.clear();
+  ReleaseAllExecutors();
+}
+
+int MultiThreadedProxyResolver::GetProxyForURL(const GURL& url,
+                                               ProxyInfo* results,
+                                               CompletionCallback* callback,
+                                               RequestHandle* request,
+                                               const BoundNetLog& net_log) {
+  DCHECK(CalledOnValidThread());
+  DCHECK(callback);
+  DCHECK(current_script_data_.get())
+      << "Resolver is un-initialized. Must call SetPacScript() first!";
+
+  scoped_refptr<GetProxyForURLJob> job =
+      new GetProxyForURLJob(url, results, callback, net_log);
+
+  // Completion will be notified through |callback|, unless the caller cancels
+  // the request using |request|.
+  if (request)
+    *request = reinterpret_cast<RequestHandle>(job.get());
+
+  // If there is an executor that is ready to run this request, submit it!
+  Executor* executor = FindIdleExecutor();
+  if (executor) {
+    DCHECK_EQ(0u, pending_jobs_.size());
+    executor->StartJob(job);
+    return ERR_IO_PENDING;
+  }
+
+  // Otherwise queue this request. (We will schedule it to a thread once one
+  // becomes available).
+  job->WaitingForThread();
+  pending_jobs_.push_back(job);
+
+  // If we haven't already reached the thread limit, provision a new thread to
+  // drain the requests more quickly.
+  if (executors_.size() < max_num_threads_) {
+    executor = AddNewExecutor();
+    executor->StartJob(
+        new SetPacScriptJob(current_script_data_, NULL));
+  }
+
+  return ERR_IO_PENDING;
+}
+
+void MultiThreadedProxyResolver::CancelRequest(RequestHandle req) {
+  DCHECK(CalledOnValidThread());
+  DCHECK(req);
+
+  Job* job = reinterpret_cast<Job*>(req);
+  DCHECK_EQ(Job::TYPE_GET_PROXY_FOR_URL, job->type());
+
+  if (job->executor()) {
+    // If the job was already submitted to the executor, just mark it
+    // as cancelled so the user callback isn't run on completion.
+    job->Cancel();
+  } else {
+    // Otherwise the job is just sitting in a queue.
+    PendingJobsQueue::iterator it =
+        std::find(pending_jobs_.begin(), pending_jobs_.end(), job);
+    DCHECK(it != pending_jobs_.end());
+    pending_jobs_.erase(it);
+  }
+}
+
+void MultiThreadedProxyResolver::CancelSetPacScript() {
+  DCHECK(CalledOnValidThread());
+  DCHECK_EQ(0u, pending_jobs_.size());
+  DCHECK_EQ(1u, executors_.size());
+  DCHECK_EQ(Job::TYPE_SET_PAC_SCRIPT,
+            executors_[0]->outstanding_job()->type());
+
+  // Defensively clear some data which shouldn't be getting used
+  // anymore.
+  current_script_data_ = NULL;
+
+  ReleaseAllExecutors();
+}
+
+void MultiThreadedProxyResolver::PurgeMemory() {
+  DCHECK(CalledOnValidThread());
+  for (ExecutorList::iterator it = executors_.begin();
+       it != executors_.end(); ++it) {
+    Executor* executor = *it;
+    executor->PurgeMemory();
+  }
+}
+
+int MultiThreadedProxyResolver::SetPacScript(
+    const scoped_refptr<ProxyResolverScriptData>& script_data,
+    CompletionCallback* callback) {
+  DCHECK(CalledOnValidThread());
+  DCHECK(callback);
+
+  // Save the script details, so we can provision new executors later.
+  current_script_data_ = script_data;
+
+  // The user should not have any outstanding requests when they call
+  // SetPacScript().
+  CheckNoOutstandingUserRequests();
+
+  // Destroy all of the current threads and their proxy resolvers.
+  ReleaseAllExecutors();
+
+  // Provision a new executor, and run the SetPacScript request. On completion
+  // notification will be sent through |callback|.
+  Executor* executor = AddNewExecutor();
+  executor->StartJob(new SetPacScriptJob(script_data, callback));
+  return ERR_IO_PENDING;
+}
+
+void MultiThreadedProxyResolver::CheckNoOutstandingUserRequests() const {
+  DCHECK(CalledOnValidThread());
+  CHECK_EQ(0u, pending_jobs_.size());
+
+  for (ExecutorList::const_iterator it = executors_.begin();
+       it != executors_.end(); ++it) {
+    const Executor* executor = *it;
+    Job* job = executor->outstanding_job();
+    // The "has_user_callback()" is to exclude jobs for which the callback
+    // has already been invoked, or was not user-initiated (as in the case of
+    // lazy thread provisions). User-initiated jobs may !has_user_callback()
+    // when the callback has already been run. (Since we only clear the
+    // outstanding job AFTER the callback has been invoked, it is possible
+    // for a new request to be started from within the callback).
+    CHECK(!job || job->was_cancelled() || !job->has_user_callback());
+  }
+}
+
+void MultiThreadedProxyResolver::ReleaseAllExecutors() {
+  DCHECK(CalledOnValidThread());
+  for (ExecutorList::iterator it = executors_.begin();
+       it != executors_.end(); ++it) {
+    Executor* executor = *it;
+    executor->Destroy();
+  }
+  executors_.clear();
+}
+
+MultiThreadedProxyResolver::Executor*
+MultiThreadedProxyResolver::FindIdleExecutor() {
+  DCHECK(CalledOnValidThread());
+  for (ExecutorList::iterator it = executors_.begin();
+       it != executors_.end(); ++it) {
+    Executor* executor = *it;
+    if (!executor->outstanding_job())
+      return executor;
+  }
+  return NULL;
+}
+
+MultiThreadedProxyResolver::Executor*
+MultiThreadedProxyResolver::AddNewExecutor() {
+  DCHECK(CalledOnValidThread());
+  DCHECK_LT(executors_.size(), max_num_threads_);
+  // The "thread number" is used to give the thread a unique name.
+  int thread_number = executors_.size();
+  ProxyResolver* resolver = resolver_factory_->CreateProxyResolver();
+  Executor* executor = new Executor(
+      this, resolver, thread_number);
+  executors_.push_back(executor);
+  return executor;
+}
+
+void MultiThreadedProxyResolver::OnExecutorReady(Executor* executor) {
+  DCHECK(CalledOnValidThread());
+  if (pending_jobs_.empty())
+    return;
+
+  // Get the next job to process (FIFO). Transfer it from the pending queue
+  // to the executor.
+  scoped_refptr<Job> job = pending_jobs_.front();
+  pending_jobs_.pop_front();
+  executor->StartJob(job);
+}
+
+}  // namespace net
diff --git a/net/proxy/multi_threaded_proxy_resolver.h b/net/proxy/multi_threaded_proxy_resolver.h
new file mode 100644
index 0000000..a69699a
--- /dev/null
+++ b/net/proxy/multi_threaded_proxy_resolver.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2010 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_PROXY_MULTI_THREADED_PROXY_RESOLVER_H_
+#define NET_PROXY_MULTI_THREADED_PROXY_RESOLVER_H_
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/non_thread_safe.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "net/proxy/proxy_resolver.h"
+
+namespace base {
+class Thread;
+}  // namespace base
+
+namespace net {
+
+// ProxyResolverFactory is an interface for creating ProxyResolver instances.
+class ProxyResolverFactory {
+ public:
+  explicit ProxyResolverFactory(bool resolvers_expect_pac_bytes)
+      : resolvers_expect_pac_bytes_(resolvers_expect_pac_bytes) {}
+
+  virtual ~ProxyResolverFactory() {}
+
+  // Creates a new ProxyResolver. The caller is responsible for freeing this
+  // object.
+  virtual ProxyResolver* CreateProxyResolver() = 0;
+
+  bool resolvers_expect_pac_bytes() const {
+    return resolvers_expect_pac_bytes_;
+  }
+
+ private:
+  bool resolvers_expect_pac_bytes_;
+  DISALLOW_COPY_AND_ASSIGN(ProxyResolverFactory);
+};
+
+// MultiThreadedProxyResolver is a ProxyResolver implementation that runs
+// synchronous ProxyResolver implementations on worker threads.
+//
+// Threads are created lazily on demand, up to a maximum total. The advantage
+// of having a pool of threads, is faster performance. In particular, being
+// able to keep servicing PAC requests even if one blocks its execution.
+//
+// During initialization (SetPacScript), a single thread is spun up to test
+// the script. If this succeeds, we cache the input script, and will re-use
+// this to lazily provision any new threads as needed.
+//
+// For each new thread that we spawn, a corresponding new ProxyResolver is
+// created using ProxyResolverFactory.
+//
+// Because we are creating multiple ProxyResolver instances, this means we
+// are duplicating script contexts for what is ordinarily seen as being a
+// single script. This can affect compatibility on some classes of PAC
+// script:
+//
+// (a) Scripts whose initialization has external dependencies on network or
+//     time may end up successfully initializing on some threads, but not
+//     others. So depending on what thread services the request, the result
+//     may jump between several possibilities.
+//
+// (b) Scripts whose FindProxyForURL() depends on side-effects may now
+//     work differently. For example, a PAC script which was incrementing
+//     a global counter and using that to make a decision. In the
+//     multi-threaded model, each thread may have a different value for this
+//     counter, so it won't globally be seen as monotonically increasing!
+class MultiThreadedProxyResolver : public ProxyResolver, public NonThreadSafe {
+ public:
+  // Creates an asynchronous ProxyResolver that runs requests on up to
+  // |max_num_threads|.
+  //
+  // For each thread that is created, an accompanying synchronous ProxyResolver
+  // will be provisioned using |resolver_factory|. All methods on these
+  // ProxyResolvers will be called on the one thread, with the exception of
+  // ProxyResolver::Shutdown() which will be called from the origin thread
+  // prior to destruction.
+  //
+  // The constructor takes ownership of |resolver_factory|.
+  MultiThreadedProxyResolver(ProxyResolverFactory* resolver_factory,
+                             size_t max_num_threads);
+
+  virtual ~MultiThreadedProxyResolver();
+
+  // ProxyResolver implementation:
+  virtual int GetProxyForURL(const GURL& url,
+                             ProxyInfo* results,
+                             CompletionCallback* callback,
+                             RequestHandle* request,
+                             const BoundNetLog& net_log);
+  virtual void CancelRequest(RequestHandle request);
+  virtual void CancelSetPacScript();
+  virtual void PurgeMemory();
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& script_data,
+      CompletionCallback* callback);
+
+ private:
+  class Executor;
+  class Job;
+  class SetPacScriptJob;
+  class GetProxyForURLJob;
+  // FIFO queue of pending jobs waiting to be started.
+  // TODO(eroman): Make this priority queue.
+  typedef std::deque<scoped_refptr<Job> > PendingJobsQueue;
+  typedef std::vector<scoped_refptr<Executor> > ExecutorList;
+
+  // Asserts that there are no outstanding user-initiated jobs on any of the
+  // worker threads.
+  void CheckNoOutstandingUserRequests() const;
+
+  // Stops and deletes all of the worker threads.
+  void ReleaseAllExecutors();
+
+  // Returns an idle worker thread which is ready to receive GetProxyForURL()
+  // requests. If all threads are occupied, returns NULL.
+  Executor* FindIdleExecutor();
+
+  // Creates a new worker thread, and appends it to |executors_|.
+  Executor* AddNewExecutor();
+
+  // Starts the next job from |pending_jobs_| if possible.
+  void OnExecutorReady(Executor* executor);
+
+  const scoped_ptr<ProxyResolverFactory> resolver_factory_;
+  const size_t max_num_threads_;
+  PendingJobsQueue pending_jobs_;
+  ExecutorList executors_;
+  scoped_refptr<ProxyResolverScriptData> current_script_data_;
+};
+
+}  // namespace net
+
+#endif  // NET_PROXY_MULTI_THREADED_PROXY_RESOLVER_H_
diff --git a/net/proxy/multi_threaded_proxy_resolver_unittest.cc b/net/proxy/multi_threaded_proxy_resolver_unittest.cc
new file mode 100644
index 0000000..1a76d21
--- /dev/null
+++ b/net/proxy/multi_threaded_proxy_resolver_unittest.cc
@@ -0,0 +1,751 @@
+// Copyright (c) 2009 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/proxy/multi_threaded_proxy_resolver.h"
+
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "base/waitable_event.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/proxy/proxy_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+// A synchronous mock ProxyResolver implementation, which can be used in
+// conjunction with MultiThreadedProxyResolver.
+//       - returns a single-item proxy list with the query's host.
+class MockProxyResolver : public ProxyResolver {
+ public:
+  MockProxyResolver()
+      : ProxyResolver(true /*expects_pac_bytes*/),
+        wrong_loop_(MessageLoop::current()),
+        request_count_(0),
+        purge_count_(0),
+        resolve_latency_ms_(0) {}
+
+  // ProxyResolver implementation:
+  virtual int GetProxyForURL(const GURL& query_url,
+                             ProxyInfo* results,
+                             CompletionCallback* callback,
+                             RequestHandle* request,
+                             const BoundNetLog& net_log) {
+    if (resolve_latency_ms_)
+      PlatformThread::Sleep(resolve_latency_ms_);
+
+    CheckIsOnWorkerThread();
+
+    EXPECT_TRUE(callback == NULL);
+    EXPECT_TRUE(request == NULL);
+
+    // Write something into |net_log| (doesn't really have any meaning.)
+    net_log.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE, NULL);
+
+    results->UseNamedProxy(query_url.host());
+
+    // Return a success code which represents the request's order.
+    return request_count_++;
+  }
+
+  virtual void CancelRequest(RequestHandle request) {
+    NOTREACHED();
+  }
+
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& script_data,
+      CompletionCallback* callback) {
+    CheckIsOnWorkerThread();
+    last_script_data_ = script_data;
+    return OK;
+  }
+
+  virtual void PurgeMemory() {
+    CheckIsOnWorkerThread();
+    ++purge_count_;
+  }
+
+  int purge_count() const { return purge_count_; }
+  int request_count() const { return request_count_; }
+
+  const ProxyResolverScriptData* last_script_data() const {
+    return last_script_data_;
+  }
+
+  void SetResolveLatency(int latency_ms) {
+    resolve_latency_ms_ = latency_ms;
+  }
+
+ private:
+  void CheckIsOnWorkerThread() {
+    // We should be running on the worker thread -- while we don't know the
+    // message loop of MultiThreadedProxyResolver's worker thread, we do
+    // know that it is going to be distinct from the loop running the
+    // test, so at least make sure it isn't the main loop.
+    EXPECT_NE(MessageLoop::current(), wrong_loop_);
+  }
+
+  MessageLoop* wrong_loop_;
+  int request_count_;
+  int purge_count_;
+  scoped_refptr<ProxyResolverScriptData> last_script_data_;
+  int resolve_latency_ms_;
+};
+
+
+// A mock synchronous ProxyResolver which can be set to block upon reaching
+// GetProxyForURL().
+// TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(),
+//               otherwise there will be a race on |should_block_| since it is
+//               read without any synchronization.
+class BlockableProxyResolver : public MockProxyResolver {
+ public:
+  BlockableProxyResolver()
+      : should_block_(false),
+        unblocked_(true, true),
+        blocked_(true, false) {
+  }
+
+  void Block() {
+    should_block_ = true;
+    unblocked_.Reset();
+  }
+
+  void Unblock() {
+    should_block_ = false;
+    blocked_.Reset();
+    unblocked_.Signal();
+  }
+
+  void WaitUntilBlocked() {
+    blocked_.Wait();
+  }
+
+  virtual int GetProxyForURL(const GURL& query_url,
+                             ProxyInfo* results,
+                             CompletionCallback* callback,
+                             RequestHandle* request,
+                             const BoundNetLog& net_log) {
+    if (should_block_) {
+      blocked_.Signal();
+      unblocked_.Wait();
+    }
+
+    return MockProxyResolver::GetProxyForURL(
+        query_url, results, callback, request, net_log);
+  }
+
+ private:
+  bool should_block_;
+  base::WaitableEvent unblocked_;
+  base::WaitableEvent blocked_;
+};
+
+// ForwardingProxyResolver forwards all requests to |impl|.
+class ForwardingProxyResolver : public ProxyResolver {
+ public:
+  explicit ForwardingProxyResolver(ProxyResolver* impl)
+      : ProxyResolver(impl->expects_pac_bytes()),
+        impl_(impl) {}
+
+  virtual int GetProxyForURL(const GURL& query_url,
+                             ProxyInfo* results,
+                             CompletionCallback* callback,
+                             RequestHandle* request,
+                             const BoundNetLog& net_log) {
+    return impl_->GetProxyForURL(
+        query_url, results, callback, request, net_log);
+  }
+
+  virtual void CancelRequest(RequestHandle request) {
+    impl_->CancelRequest(request);
+  }
+
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& script_data,
+      CompletionCallback* callback) {
+    return impl_->SetPacScript(script_data, callback);
+  }
+
+  virtual void PurgeMemory() {
+    impl_->PurgeMemory();
+  }
+
+ private:
+  ProxyResolver* impl_;
+};
+
+// This factory returns ProxyResolvers that forward all requests to
+// |resolver|.
+class ForwardingProxyResolverFactory : public ProxyResolverFactory {
+ public:
+  explicit ForwardingProxyResolverFactory(ProxyResolver* resolver)
+      : ProxyResolverFactory(resolver->expects_pac_bytes()),
+        resolver_(resolver) {}
+
+  virtual ProxyResolver* CreateProxyResolver() {
+    return new ForwardingProxyResolver(resolver_);
+  }
+
+ private:
+  ProxyResolver* resolver_;
+};
+
+// This factory returns new instances of BlockableProxyResolver.
+class BlockableProxyResolverFactory : public ProxyResolverFactory {
+ public:
+  BlockableProxyResolverFactory() : ProxyResolverFactory(true) {}
+
+  ~BlockableProxyResolverFactory() {
+    STLDeleteElements(&resolvers_);
+  }
+
+  virtual ProxyResolver* CreateProxyResolver() {
+    BlockableProxyResolver* resolver = new BlockableProxyResolver;
+    resolvers_.push_back(resolver);
+    return new ForwardingProxyResolver(resolver);
+  }
+
+  std::vector<BlockableProxyResolver*> resolvers() {
+    return resolvers_;
+  }
+
+ private:
+  std::vector<BlockableProxyResolver*> resolvers_;
+};
+
+TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) {
+  const size_t kNumThreads = 1u;
+  scoped_ptr<MockProxyResolver> mock(new MockProxyResolver);
+  MultiThreadedProxyResolver resolver(
+      new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
+
+  int rv;
+
+  EXPECT_TRUE(resolver.expects_pac_bytes());
+
+  // Call SetPacScriptByData() -- verify that it reaches the synchronous
+  // resolver.
+  TestCompletionCallback set_script_callback;
+  rv = resolver.SetPacScript(
+      ProxyResolverScriptData::FromUTF8("pac script bytes"),
+      &set_script_callback);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, set_script_callback.WaitForResult());
+  EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
+            mock->last_script_data()->utf16());
+
+  // Start request 0.
+  TestCompletionCallback callback0;
+  CapturingBoundNetLog log0(CapturingNetLog::kUnbounded);
+  ProxyInfo results0;
+  rv = resolver.GetProxyForURL(
+      GURL("http://request0"), &results0, &callback0, NULL, log0.bound());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Wait for request 0 to finish.
+  rv = callback0.WaitForResult();
+  EXPECT_EQ(0, rv);
+  EXPECT_EQ("PROXY request0:80", results0.ToPacString());
+
+  // The mock proxy resolver should have written 1 log entry. And
+  // on completion, this should have been copied into |log0|.
+  // We also have 1 log entry that was emitted by the
+  // MultiThreadedProxyResolver.
+  ASSERT_EQ(2u, log0.entries().size());
+  EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
+            log0.entries()[0].type);
+
+  // Start 3 more requests (request1 to request3).
+
+  TestCompletionCallback callback1;
+  ProxyInfo results1;
+  rv = resolver.GetProxyForURL(
+      GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  TestCompletionCallback callback2;
+  ProxyInfo results2;
+  rv = resolver.GetProxyForURL(
+      GURL("http://request2"), &results2, &callback2, NULL, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  TestCompletionCallback callback3;
+  ProxyInfo results3;
+  rv = resolver.GetProxyForURL(
+      GURL("http://request3"), &results3, &callback3, NULL, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Wait for the requests to finish (they must finish in the order they were
+  // started, which is what we check for from their magic return value)
+
+  rv = callback1.WaitForResult();
+  EXPECT_EQ(1, rv);
+  EXPECT_EQ("PROXY request1:80", results1.ToPacString());
+
+  rv = callback2.WaitForResult();
+  EXPECT_EQ(2, rv);
+  EXPECT_EQ("PROXY request2:80", results2.ToPacString());
+
+  rv = callback3.WaitForResult();
+  EXPECT_EQ(3, rv);
+  EXPECT_EQ("PROXY request3:80", results3.ToPacString());
+
+  // Ensure that PurgeMemory() reaches the wrapped resolver and happens on the
+  // right thread.
+  EXPECT_EQ(0, mock->purge_count());
+  resolver.PurgeMemory();
+  // There is no way to get a callback directly when PurgeMemory() completes, so
+  // we queue up a dummy request after the PurgeMemory() call and wait until it
+  // finishes to ensure PurgeMemory() has had a chance to run.
+  TestCompletionCallback dummy_callback;
+  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("dummy"),
+                             &dummy_callback);
+  EXPECT_EQ(OK, dummy_callback.WaitForResult());
+  EXPECT_EQ(1, mock->purge_count());
+}
+
+// Tests that the NetLog is updated to include the time the request was waiting
+// to be scheduled to a thread.
+TEST(MultiThreadedProxyResolverTest,
+     SingleThread_UpdatesNetLogWithThreadWait) {
+  const size_t kNumThreads = 1u;
+  scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
+  MultiThreadedProxyResolver resolver(
+      new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
+
+  int rv;
+
+  // Initialize the resolver.
+  TestCompletionCallback init_callback;
+  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
+                             &init_callback);
+  EXPECT_EQ(OK, init_callback.WaitForResult());
+
+  // Block the proxy resolver, so no request can complete.
+  mock->Block();
+
+  // Start request 0.
+  ProxyResolver::RequestHandle request0;
+  TestCompletionCallback callback0;
+  ProxyInfo results0;
+  CapturingBoundNetLog log0(CapturingNetLog::kUnbounded);
+  rv = resolver.GetProxyForURL(
+      GURL("http://request0"), &results0, &callback0, &request0, log0.bound());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Start 2 more requests (request1 and request2).
+
+  TestCompletionCallback callback1;
+  ProxyInfo results1;
+  CapturingBoundNetLog log1(CapturingNetLog::kUnbounded);
+  rv = resolver.GetProxyForURL(
+      GURL("http://request1"), &results1, &callback1, NULL, log1.bound());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  ProxyResolver::RequestHandle request2;
+  TestCompletionCallback callback2;
+  ProxyInfo results2;
+  CapturingBoundNetLog log2(CapturingNetLog::kUnbounded);
+  rv = resolver.GetProxyForURL(
+      GURL("http://request2"), &results2, &callback2, &request2, log2.bound());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Unblock the worker thread so the requests can continue running.
+  mock->WaitUntilBlocked();
+  mock->Unblock();
+
+  // Check that request 0 completed as expected.
+  // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and
+  // 1 entry from the mock proxy resolver.
+  EXPECT_EQ(0, callback0.WaitForResult());
+  EXPECT_EQ("PROXY request0:80", results0.ToPacString());
+  ASSERT_EQ(2u, log0.entries().size());
+  EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD,
+            log0.entries()[0].type);
+
+  // Check that request 1 completed as expected.
+  EXPECT_EQ(1, callback1.WaitForResult());
+  EXPECT_EQ("PROXY request1:80", results1.ToPacString());
+  ASSERT_EQ(4u, log1.entries().size());
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log1.entries(), 0,
+      NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log1.entries(), 1,
+      NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
+
+  // Check that request 2 completed as expected.
+  EXPECT_EQ(2, callback2.WaitForResult());
+  EXPECT_EQ("PROXY request2:80", results2.ToPacString());
+  ASSERT_EQ(4u, log2.entries().size());
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log2.entries(), 0,
+      NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log2.entries(), 1,
+      NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD));
+}
+
+// Cancel a request which is in progress, and then cancel a request which
+// is pending.
+TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) {
+  const size_t kNumThreads = 1u;
+  scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
+  MultiThreadedProxyResolver resolver(
+      new ForwardingProxyResolverFactory(mock.get()),
+                                      kNumThreads);
+
+  int rv;
+
+  // Initialize the resolver.
+  TestCompletionCallback init_callback;
+  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
+                             &init_callback);
+  EXPECT_EQ(OK, init_callback.WaitForResult());
+
+  // Block the proxy resolver, so no request can complete.
+  mock->Block();
+
+  // Start request 0.
+  ProxyResolver::RequestHandle request0;
+  TestCompletionCallback callback0;
+  ProxyInfo results0;
+  rv = resolver.GetProxyForURL(
+      GURL("http://request0"), &results0, &callback0, &request0, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Wait until requests 0 reaches the worker thread.
+  mock->WaitUntilBlocked();
+
+  // Start 3 more requests (request1 : request3).
+
+  TestCompletionCallback callback1;
+  ProxyInfo results1;
+  rv = resolver.GetProxyForURL(
+      GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  ProxyResolver::RequestHandle request2;
+  TestCompletionCallback callback2;
+  ProxyInfo results2;
+  rv = resolver.GetProxyForURL(
+      GURL("http://request2"), &results2, &callback2, &request2, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  TestCompletionCallback callback3;
+  ProxyInfo results3;
+  rv = resolver.GetProxyForURL(
+      GURL("http://request3"), &results3, &callback3, NULL, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Cancel request0 (inprogress) and request2 (pending).
+  resolver.CancelRequest(request0);
+  resolver.CancelRequest(request2);
+
+  // Unblock the worker thread so the requests can continue running.
+  mock->Unblock();
+
+  // Wait for requests 1 and 3 to finish.
+
+  rv = callback1.WaitForResult();
+  EXPECT_EQ(1, rv);
+  EXPECT_EQ("PROXY request1:80", results1.ToPacString());
+
+  rv = callback3.WaitForResult();
+  // Note that since request2 was cancelled before reaching the resolver,
+  // the request count is 2 and not 3 here.
+  EXPECT_EQ(2, rv);
+  EXPECT_EQ("PROXY request3:80", results3.ToPacString());
+
+  // Requests 0 and 2 which were cancelled, hence their completion callbacks
+  // were never summoned.
+  EXPECT_FALSE(callback0.have_result());
+  EXPECT_FALSE(callback2.have_result());
+}
+
+// Test that deleting MultiThreadedProxyResolver while requests are
+// outstanding cancels them (and doesn't leak anything).
+TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) {
+  const size_t kNumThreads = 1u;
+  scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
+  scoped_ptr<MultiThreadedProxyResolver> resolver(
+      new MultiThreadedProxyResolver(
+          new ForwardingProxyResolverFactory(mock.get()), kNumThreads));
+
+  int rv;
+
+  // Initialize the resolver.
+  TestCompletionCallback init_callback;
+  rv = resolver->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"),
+                              &init_callback);
+  EXPECT_EQ(OK, init_callback.WaitForResult());
+
+  // Block the proxy resolver, so no request can complete.
+  mock->Block();
+
+  // Start 3 requests.
+
+  TestCompletionCallback callback0;
+  ProxyInfo results0;
+  rv = resolver->GetProxyForURL(
+      GURL("http://request0"), &results0, &callback0, NULL, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  TestCompletionCallback callback1;
+  ProxyInfo results1;
+  rv = resolver->GetProxyForURL(
+      GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  TestCompletionCallback callback2;
+  ProxyInfo results2;
+  rv = resolver->GetProxyForURL(
+      GURL("http://request2"), &results2, &callback2, NULL, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Wait until request 0 reaches the worker thread.
+  mock->WaitUntilBlocked();
+
+  // Add some latency, to improve the chance that when
+  // MultiThreadedProxyResolver is deleted below we are still running inside
+  // of the worker thread. The test will pass regardless, so this race doesn't
+  // cause flakiness. However the destruction during execution is a more
+  // interesting case to test.
+  mock->SetResolveLatency(100);
+
+  // Unblock the worker thread and delete the underlying
+  // MultiThreadedProxyResolver immediately.
+  mock->Unblock();
+  resolver.reset();
+
+  // Give any posted tasks a chance to run (in case there is badness).
+  MessageLoop::current()->RunAllPending();
+
+  // Check that none of the outstanding requests were completed.
+  EXPECT_FALSE(callback0.have_result());
+  EXPECT_FALSE(callback1.have_result());
+  EXPECT_FALSE(callback2.have_result());
+}
+
+// Cancel an outstanding call to SetPacScriptByData().
+TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) {
+  const size_t kNumThreads = 1u;
+  scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver);
+  MultiThreadedProxyResolver resolver(
+      new ForwardingProxyResolverFactory(mock.get()), kNumThreads);
+
+  int rv;
+
+  TestCompletionCallback set_pac_script_callback;
+  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data"),
+                             &set_pac_script_callback);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Cancel the SetPacScriptByData request.
+  resolver.CancelSetPacScript();
+
+  // Start another SetPacScript request
+  TestCompletionCallback set_pac_script_callback2;
+  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"),
+                             &set_pac_script_callback2);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Wait for the initialization to complete.
+
+  rv = set_pac_script_callback2.WaitForResult();
+  EXPECT_EQ(0, rv);
+  EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_script_data()->utf16());
+
+  // The first SetPacScript callback should never have been completed.
+  EXPECT_FALSE(set_pac_script_callback.have_result());
+}
+
+// Tests setting the PAC script once, lazily creating new threads, and
+// cancelling requests.
+TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) {
+  const size_t kNumThreads = 3u;
+  BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
+  MultiThreadedProxyResolver resolver(factory, kNumThreads);
+
+  int rv;
+
+  EXPECT_TRUE(resolver.expects_pac_bytes());
+
+  // Call SetPacScriptByData() -- verify that it reaches the synchronous
+  // resolver.
+  TestCompletionCallback set_script_callback;
+  rv = resolver.SetPacScript(
+      ProxyResolverScriptData::FromUTF8("pac script bytes"),
+      &set_script_callback);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, set_script_callback.WaitForResult());
+  // One thread has been provisioned (i.e. one ProxyResolver was created).
+  ASSERT_EQ(1u, factory->resolvers().size());
+  EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
+            factory->resolvers()[0]->last_script_data()->utf16());
+
+  const int kNumRequests = 9;
+  TestCompletionCallback callback[kNumRequests];
+  ProxyInfo results[kNumRequests];
+  ProxyResolver::RequestHandle request[kNumRequests];
+
+  // Start request 0 -- this should run on thread 0 as there is nothing else
+  // going on right now.
+  rv = resolver.GetProxyForURL(
+      GURL("http://request0"), &results[0], &callback[0], &request[0],
+      BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  // Wait for request 0 to finish.
+  rv = callback[0].WaitForResult();
+  EXPECT_EQ(0, rv);
+  EXPECT_EQ("PROXY request0:80", results[0].ToPacString());
+  ASSERT_EQ(1u, factory->resolvers().size());
+  EXPECT_EQ(1, factory->resolvers()[0]->request_count());
+
+  MessageLoop::current()->RunAllPending();
+
+  // We now start 8 requests in parallel -- this will cause the maximum of
+  // three threads to be provisioned (an additional two from what we already
+  // have).
+
+  for (int i = 1; i < kNumRequests; ++i) {
+    rv = resolver.GetProxyForURL(
+        GURL(StringPrintf("http://request%d", i)), &results[i], &callback[i],
+        &request[i], BoundNetLog());
+    EXPECT_EQ(ERR_IO_PENDING, rv);
+  }
+
+  // We should now have a total of 3 threads, each with its own ProxyResolver
+  // that will get initialized with the same data. (We check this later since
+  // the assignment happens on the worker threads and may not have occurred
+  // yet.)
+  ASSERT_EQ(3u, factory->resolvers().size());
+
+  // Cancel 3 of the 8 oustanding requests.
+  resolver.CancelRequest(request[1]);
+  resolver.CancelRequest(request[3]);
+  resolver.CancelRequest(request[6]);
+
+  // Wait for the remaining requests to complete.
+  int kNonCancelledRequests[] = {2, 4, 5, 7, 8};
+  for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) {
+    int request_index = kNonCancelledRequests[i];
+    EXPECT_GE(callback[request_index].WaitForResult(), 0);
+  }
+
+  // Check that the cancelled requests never invoked their callback.
+  EXPECT_FALSE(callback[1].have_result());
+  EXPECT_FALSE(callback[3].have_result());
+  EXPECT_FALSE(callback[6].have_result());
+
+  // We call SetPacScript again, solely to stop the current worker threads.
+  // (That way we can test to see the values observed by the synchronous
+  // resolvers in a non-racy manner).
+  TestCompletionCallback set_script_callback2;
+  rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"),
+                             &set_script_callback2);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, set_script_callback2.WaitForResult());
+  ASSERT_EQ(4u, factory->resolvers().size());
+
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_EQ(
+        ASCIIToUTF16("pac script bytes"),
+        factory->resolvers()[i]->last_script_data()->utf16()) << "i=" << i;
+  }
+
+  EXPECT_EQ(ASCIIToUTF16("xyz"),
+            factory->resolvers()[3]->last_script_data()->utf16());
+
+  // We don't know the exact ordering that requests ran on threads with,
+  // but we do know the total count that should have reached the threads.
+  // 8 total were submitted, and three were cancelled. Of the three that
+  // were cancelled, one of them (request 1) was cancelled after it had
+  // already been posted to the worker thread. So the resolvers will
+  // have seen 6 total (and 1 from the run prior).
+  ASSERT_EQ(4u, factory->resolvers().size());
+  int total_count = 0;
+  for (int i = 0; i < 3; ++i) {
+    total_count += factory->resolvers()[i]->request_count();
+  }
+  EXPECT_EQ(7, total_count);
+}
+
+// Tests using two threads. The first request hangs the first thread. Checks
+// that other requests are able to complete while this first request remains
+// stalled.
+TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) {
+  const size_t kNumThreads = 2u;
+  BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory;
+  MultiThreadedProxyResolver resolver(factory, kNumThreads);
+
+  int rv;
+
+  EXPECT_TRUE(resolver.expects_pac_bytes());
+
+  // Initialize the resolver.
+  TestCompletionCallback set_script_callback;
+  rv = resolver.SetPacScript(
+      ProxyResolverScriptData::FromUTF8("pac script bytes"),
+      &set_script_callback);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_EQ(OK, set_script_callback.WaitForResult());
+  // One thread has been provisioned (i.e. one ProxyResolver was created).
+  ASSERT_EQ(1u, factory->resolvers().size());
+  EXPECT_EQ(ASCIIToUTF16("pac script bytes"),
+            factory->resolvers()[0]->last_script_data()->utf16());
+
+  const int kNumRequests = 4;
+  TestCompletionCallback callback[kNumRequests];
+  ProxyInfo results[kNumRequests];
+  ProxyResolver::RequestHandle request[kNumRequests];
+
+  // Start a request that will block the first thread.
+
+  factory->resolvers()[0]->Block();
+
+  rv = resolver.GetProxyForURL(
+      GURL("http://request0"), &results[0], &callback[0], &request[0],
+      BoundNetLog());
+
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  factory->resolvers()[0]->WaitUntilBlocked();
+
+  // Start 3 more requests -- they should all be serviced by thread #2
+  // since thread #1 is blocked.
+
+  for (int i = 1; i < kNumRequests; ++i) {
+    rv = resolver.GetProxyForURL(
+        GURL(StringPrintf("http://request%d", i)),
+        &results[i], &callback[i], &request[i], BoundNetLog());
+    EXPECT_EQ(ERR_IO_PENDING, rv);
+  }
+
+  // Wait for the three requests to complete (they should complete in FIFO
+  // order).
+  for (int i = 1; i < kNumRequests; ++i) {
+    EXPECT_EQ(i - 1, callback[i].WaitForResult());
+  }
+
+  // Unblock the first thread.
+  factory->resolvers()[0]->Unblock();
+  EXPECT_EQ(0, callback[0].WaitForResult());
+
+  // All in all, the first thread should have seen just 1 request. And the
+  // second thread 3 requests.
+  ASSERT_EQ(2u, factory->resolvers().size());
+  EXPECT_EQ(1, factory->resolvers()[0]->request_count());
+  EXPECT_EQ(3, factory->resolvers()[1]->request_count());
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/proxy/proxy_bypass_rules.cc b/net/proxy/proxy_bypass_rules.cc
new file mode 100644
index 0000000..e273d67
--- /dev/null
+++ b/net/proxy/proxy_bypass_rules.cc
@@ -0,0 +1,282 @@
+// Copyright (c) 2010 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/proxy/proxy_bypass_rules.h"
+
+#include "base/logging.h"
+#include "base/string_tokenizer.h"
+#include "base/string_util.h"
+#include "net/base/net_util.h"
+
+namespace net {
+
+namespace {
+
+class HostnamePatternRule : public ProxyBypassRules::Rule {
+ public:
+  HostnamePatternRule(const std::string& optional_scheme,
+                      const std::string& hostname_pattern,
+                      int optional_port)
+      : optional_scheme_(StringToLowerASCII(optional_scheme)),
+        hostname_pattern_(StringToLowerASCII(hostname_pattern)),
+        optional_port_(optional_port) {
+  }
+
+  virtual bool Matches(const GURL& url) const {
+    if (optional_port_ != -1 && url.EffectiveIntPort() != optional_port_)
+      return false;  // Didn't match port expectation.
+
+    if (!optional_scheme_.empty() && url.scheme() != optional_scheme_)
+      return false;  // Didn't match scheme expectation.
+
+    // Note it is necessary to lower-case the host, since GURL uses capital
+    // letters for percent-escaped characters.
+    return MatchPatternASCII(StringToLowerASCII(url.host()),
+                             hostname_pattern_);
+  }
+
+  virtual std::string ToString() const {
+    std::string str;
+    if (!optional_scheme_.empty())
+      StringAppendF(&str, "%s://", optional_scheme_.c_str());
+    str += hostname_pattern_;
+    if (optional_port_ != -1)
+      StringAppendF(&str, ":%d", optional_port_);
+    return str;
+  }
+
+ private:
+  const std::string optional_scheme_;
+  const std::string hostname_pattern_;
+  const int optional_port_;
+};
+
+class BypassLocalRule : public ProxyBypassRules::Rule {
+ public:
+  virtual bool Matches(const GURL& url) const {
+    const std::string& host = url.host();
+    if (host == "127.0.0.1" || host == "[::1]")
+      return true;
+    return host.find('.') == std::string::npos;
+  }
+
+  virtual std::string ToString() const {
+    return "<local>";
+  }
+};
+
+// Rule for matching a URL that is an IP address, if that IP address falls
+// within a certain numeric range. For example, you could use this rule to
+// match all the IPs in the CIDR block 10.10.3.4/24.
+class BypassIPBlockRule : public ProxyBypassRules::Rule {
+ public:
+  // |ip_prefix| + |prefix_length| define the IP block to match.
+  BypassIPBlockRule(const std::string& description,
+                    const std::string& optional_scheme,
+                    const IPAddressNumber& ip_prefix,
+                    size_t prefix_length_in_bits)
+      : description_(description),
+        optional_scheme_(optional_scheme),
+        ip_prefix_(ip_prefix),
+        prefix_length_in_bits_(prefix_length_in_bits) {
+  }
+
+  virtual bool Matches(const GURL& url) const {
+    if (!url.HostIsIPAddress())
+      return false;
+
+    if (!optional_scheme_.empty() && url.scheme() != optional_scheme_)
+      return false;  // Didn't match scheme expectation.
+
+    // Parse the input IP literal to a number.
+    IPAddressNumber ip_number;
+    if (!ParseIPLiteralToNumber(url.HostNoBrackets(), &ip_number))
+      return false;
+
+    // Test if it has the expected prefix.
+    return IPNumberMatchesPrefix(ip_number, ip_prefix_,
+                                 prefix_length_in_bits_);
+  }
+
+  virtual std::string ToString() const {
+    return description_;
+  }
+
+ private:
+  const std::string description_;
+  const std::string optional_scheme_;
+  const IPAddressNumber ip_prefix_;
+  const size_t prefix_length_in_bits_;
+};
+
+// Returns true if the given string represents an IP address.
+bool IsIPAddress(const std::string& domain) {
+  // From GURL::HostIsIPAddress()
+  url_canon::RawCanonOutputT<char, 128> ignored_output;
+  url_canon::CanonHostInfo host_info;
+  url_parse::Component domain_comp(0, domain.size());
+  url_canon::CanonicalizeIPAddress(domain.c_str(), domain_comp,
+                                   &ignored_output, &host_info);
+  return host_info.IsIPAddress();
+}
+
+}  // namespace
+
+ProxyBypassRules::~ProxyBypassRules() {
+}
+
+bool ProxyBypassRules::Matches(const GURL& url) const {
+  for (RuleList::const_iterator it = rules_.begin(); it != rules_.end(); ++it) {
+    if ((*it)->Matches(url))
+      return true;
+  }
+  return false;
+}
+
+bool ProxyBypassRules::Equals(const ProxyBypassRules& other) const {
+  if (rules_.size() != other.rules().size())
+    return false;
+
+  for (size_t i = 0; i < rules_.size(); ++i) {
+    if (!rules_[i]->Equals(*other.rules()[i]))
+      return false;
+  }
+  return true;
+}
+
+void ProxyBypassRules::ParseFromString(const std::string& raw) {
+  ParseFromStringInternal(raw, false);
+}
+
+void ProxyBypassRules::ParseFromStringUsingSuffixMatching(
+    const std::string& raw) {
+  ParseFromStringInternal(raw, true);
+}
+
+bool ProxyBypassRules::AddRuleForHostname(const std::string& optional_scheme,
+                                          const std::string& hostname_pattern,
+                                          int optional_port) {
+  if (hostname_pattern.empty())
+    return false;
+
+  rules_.push_back(new HostnamePatternRule(optional_scheme,
+                                           hostname_pattern,
+                                           optional_port));
+  return true;
+}
+
+void ProxyBypassRules::AddRuleToBypassLocal() {
+  rules_.push_back(new BypassLocalRule);
+}
+
+bool ProxyBypassRules::AddRuleFromString(const std::string& raw) {
+  return AddRuleFromStringInternalWithLogging(raw, false);
+}
+
+bool ProxyBypassRules::AddRuleFromStringUsingSuffixMatching(
+    const std::string& raw) {
+  return AddRuleFromStringInternalWithLogging(raw, true);
+}
+
+void ProxyBypassRules::Clear() {
+  rules_.clear();
+}
+
+void ProxyBypassRules::ParseFromStringInternal(
+    const std::string& raw,
+    bool use_hostname_suffix_matching) {
+  Clear();
+
+  StringTokenizer entries(raw, ",;");
+  while (entries.GetNext()) {
+    AddRuleFromStringInternalWithLogging(entries.token(),
+                                         use_hostname_suffix_matching);
+  }
+}
+
+bool ProxyBypassRules::AddRuleFromStringInternal(
+    const std::string& raw_untrimmed,
+    bool use_hostname_suffix_matching) {
+  std::string raw;
+  TrimWhitespaceASCII(raw_untrimmed, TRIM_ALL, &raw);
+
+  // This is the special syntax used by WinInet's bypass list -- we allow it
+  // on all platforms and interpret it the same way.
+  if (LowerCaseEqualsASCII(raw, "<local>")) {
+    AddRuleToBypassLocal();
+    return true;
+  }
+
+  // Extract any scheme-restriction.
+  std::string::size_type scheme_pos = raw.find("://");
+  std::string scheme;
+  if (scheme_pos != std::string::npos) {
+    scheme = raw.substr(0, scheme_pos);
+    raw = raw.substr(scheme_pos + 3);
+    if (scheme.empty())
+      return false;
+  }
+
+  if (raw.empty())
+    return false;
+
+  // If there is a forward slash in the input, it is probably a CIDR style
+  // mask.
+  if (raw.find('/') != std::string::npos) {
+    IPAddressNumber ip_prefix;
+    size_t prefix_length_in_bits;
+
+    if (!ParseCIDRBlock(raw, &ip_prefix, &prefix_length_in_bits))
+      return false;
+
+    rules_.push_back(
+        new BypassIPBlockRule(raw, scheme, ip_prefix, prefix_length_in_bits));
+
+    return true;
+  }
+
+  // Check if we have an <ip-address>[:port] input. We need to treat this
+  // separately since the IP literal may not be in a canonical form.
+  std::string host;
+  int port;
+  if (ParseHostAndPort(raw, &host, &port)) {
+    if (IsIPAddress(host)) {
+      // Canonicalize the IP literal before adding it as a string pattern.
+      GURL tmp_url("http://" + host);
+      return AddRuleForHostname(scheme, tmp_url.host(), port);
+    }
+  }
+
+  // Otherwise assume we have <hostname-pattern>[:port].
+  std::string::size_type pos_colon = raw.rfind(':');
+  host = raw;
+  port = -1;
+  if (pos_colon != std::string::npos) {
+    if (!StringToInt(raw.substr(pos_colon + 1), &port) ||
+        (port < 0 || port > 0xFFFF)) {
+      return false;  // Port was invalid.
+    }
+    raw = raw.substr(0, pos_colon);
+  }
+
+  // Special-case hostnames that begin with a period.
+  // For example, we remap ".google.com" --> "*.google.com".
+  if (StartsWithASCII(raw, ".", false))
+    raw = "*" + raw;
+
+  // If suffix matching was asked for, make sure the pattern starts with a
+  // wildcard.
+  if (use_hostname_suffix_matching && !StartsWithASCII(raw, "*", false))
+    raw = "*" + raw;
+
+  return AddRuleForHostname(scheme, raw, port);
+}
+
+bool ProxyBypassRules::AddRuleFromStringInternalWithLogging(
+    const std::string& raw,
+    bool use_hostname_suffix_matching) {
+  return AddRuleFromStringInternal(raw, use_hostname_suffix_matching);
+}
+
+}  // namespace net
diff --git a/net/proxy/proxy_bypass_rules.h b/net/proxy/proxy_bypass_rules.h
new file mode 100644
index 0000000..267fdc9
--- /dev/null
+++ b/net/proxy/proxy_bypass_rules.h
@@ -0,0 +1,170 @@
+// Copyright (c) 2010 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_PROXY_PROXY_BYPASS_RULES_H_
+#define NET_PROXY_PROXY_BYPASS_RULES_H_
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "googleurl/src/gurl.h"
+
+namespace net {
+
+// ProxyBypassRules describes the set of URLs that should bypass the proxy
+// settings, as a list of rules. A URL is said to match the bypass rules
+// if it matches any one of these rules.
+class ProxyBypassRules {
+ public:
+  // Interface for an individual proxy bypass rule.
+  class Rule : public base::RefCounted<Rule> {
+   public:
+    Rule() {}
+    virtual ~Rule() {}
+
+    // Returns true if |url| matches the rule.
+    virtual bool Matches(const GURL& url) const = 0;
+
+    // Returns a string representation of this rule. This is used both for
+    // visualizing the rules, and also to test equality of a rules list.
+    virtual std::string ToString() const = 0;
+
+    bool Equals(const Rule& rule) const {
+      return ToString() == rule.ToString();
+    }
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Rule);
+  };
+
+  typedef std::vector<scoped_refptr<Rule> > RuleList;
+
+  // Note: This class supports copy constructor and assignment.
+
+  ~ProxyBypassRules();
+
+  // Returns the current list of rules.
+  const RuleList& rules() const { return rules_; }
+
+  // Returns true if |url| matches any of the proxy bypass rules.
+  bool Matches(const GURL& url) const;
+
+  // Returns true if |*this| is equal to |other|; in other words, whether they
+  // describe the same set of rules.
+  bool Equals(const ProxyBypassRules& other) const;
+
+  // Initializes the list of rules by parsing the string |raw|. |raw| is a
+  // comma separated list of rules. See AddRuleFromString() to see the list
+  // of supported formats.
+  void ParseFromString(const std::string& raw);
+
+  // This is a variant of ParseFromString, which interprets hostname patterns
+  // as suffix tests rather than hostname tests (so "google.com" would actually
+  // match "*google.com"). This is only currently used for the linux no_proxy
+  // evironment variable. It is less flexible, since with the suffix matching
+  // format you can't match an individual host.
+  // NOTE: Use ParseFromString() unless you truly need this behavior.
+  void ParseFromStringUsingSuffixMatching(const std::string& raw);
+
+  // Adds a rule that matches a URL when all of the following are true:
+  //  (a) The URL's scheme matches |optional_scheme|, if
+  //      |!optional_scheme.empty()|
+  //  (b) The URL's hostname matches |hostname_pattern|.
+  //  (c) The URL's (effective) port number matches |optional_port| if
+  //      |optional_port != -1|
+  // Returns true if the rule was successfully added.
+  bool AddRuleForHostname(const std::string& optional_scheme,
+                          const std::string& hostname_pattern,
+                          int optional_port);
+
+  // Adds a rule that bypasses all "local" hostnames.
+  // This matches IE's interpretation of the
+  // "Bypass proxy server for local addresses" settings checkbox. Fully
+  // qualified domain names or IP addresses are considered non-local,
+  // regardless of what they map to (except for the loopback addresses).
+  void AddRuleToBypassLocal();
+
+  // Adds a rule given by the string |raw|. The format of |raw| can be any of
+  // the following:
+  //
+  // (1) [ URL_SCHEME "://" ] HOSTNAME_PATTERN [ ":" <port> ]
+  //
+  //   Match all hostnames that match the pattern HOSTNAME_PATTERN.
+  //
+  //   Examples:
+  //     "foobar.com", "*foobar.com", "*.foobar.com", "*foobar.com:99",
+  //     "https://x.*.y.com:99"
+  //
+  // (2) "." HOSTNAME_SUFFIX_PATTERN [ ":" PORT ]
+  //
+  //   Match a particular domain suffix.
+  //
+  //   Examples:
+  //     ".google.com", ".com", "http://.google.com"
+  //
+  // (3) [ SCHEME "://" ] IP_LITERAL [ ":" PORT ]
+  //
+  //   Match URLs which are IP address literals.
+  //
+  //   Conceptually this is the similar to (1), but with special cases
+  //   to handle IP literal canonicalization. For example matching
+  //   on "[0:0:0::1]" would be the same as matching on "[::1]" since
+  //   the IPv6 canonicalization is done internally.
+  //
+  //   Examples:
+  //     "127.0.1", "[0:0::1]", "[::1]", "http://[::1]:99"
+  //
+  // (4)  IP_LITERAL "/" PREFIX_LENGHT_IN_BITS
+  //
+  //   Match any URL that is to an IP literal that falls between the
+  //   given range. IP range is specified using CIDR notation.
+  //
+  //   Examples:
+  //     "192.168.1.1/16", "fefe:13::abc/33".
+  //
+  // (5)  "<local>"
+  //
+  //   Match local addresses. The meaning of "<local>" is whether the
+  //   host matches one of: "127.0.0.1", "::1", "localhost".
+  //
+  // See the unit-tests for more examples.
+  //
+  // Returns true if the rule was successfully added.
+  //
+  // TODO(eroman): support IPv6 literals without brackets.
+  //
+  bool AddRuleFromString(const std::string& raw);
+
+  // This is a variant of AddFromString, which interprets hostname patterns as
+  // suffix tests rather than hostname tests (so "google.com" would actually
+  // match "*google.com"). This is used for KDE which interprets every rule as
+  // a suffix test. It is less flexible, since with the suffix matching format
+  // you can't match an individual host.
+  //
+  // Returns true if the rule was successfully added.
+  //
+  // NOTE: Use AddRuleFromString() unless you truly need this behavior.
+  bool AddRuleFromStringUsingSuffixMatching(const std::string& raw);
+
+  // Removes all the rules.
+  void Clear();
+
+ private:
+  // The following are variants of ParseFromString() and AddRuleFromString(),
+  // which additionally prefix hostname patterns with a wildcard if
+  // |use_hostname_suffix_matching| was true.
+  void ParseFromStringInternal(const std::string& raw,
+                               bool use_hostname_suffix_matching);
+  bool AddRuleFromStringInternal(const std::string& raw,
+                                 bool use_hostname_suffix_matching);
+  bool AddRuleFromStringInternalWithLogging(const std::string& raw,
+                                            bool use_hostname_suffix_matching);
+
+  RuleList rules_;
+};
+
+}  // namespace net
+
+#endif  // NET_PROXY_PROXY_BYPASS_RULES_H_
diff --git a/net/proxy/proxy_bypass_rules_unittest.cc b/net/proxy/proxy_bypass_rules_unittest.cc
new file mode 100644
index 0000000..208f9cd
--- /dev/null
+++ b/net/proxy/proxy_bypass_rules_unittest.cc
@@ -0,0 +1,314 @@
+// Copyright (c) 2010 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/proxy/proxy_bypass_rules.h"
+
+#include "base/string_util.h"
+#include "net/proxy/proxy_config_service_common_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+TEST(ProxyBypassRulesTest, ParseAndMatchBasicHost) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("wWw.gOogle.com");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("www.google.com", rules.rules()[0]->ToString());
+
+  // All of these match; port, scheme, and non-hostname components don't
+  // matter.
+  EXPECT_TRUE(rules.Matches(GURL("http://www.google.com")));
+  EXPECT_TRUE(rules.Matches(GURL("ftp://www.google.com:99")));
+  EXPECT_TRUE(rules.Matches(GURL("https://www.google.com:81")));
+
+  // Must be a strict host match to work.
+  EXPECT_FALSE(rules.Matches(GURL("http://foo.www.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://xxx.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://www.google.com.baz.org")));
+}
+
+TEST(ProxyBypassRulesTest, ParseAndMatchBasicDomain) {
+  ProxyBypassRules rules;
+  rules.ParseFromString(".gOOgle.com");
+  ASSERT_EQ(1u, rules.rules().size());
+  // Note that we inferred this was an "ends with" test.
+  EXPECT_EQ("*.google.com", rules.rules()[0]->ToString());
+
+  // All of these match; port, scheme, and non-hostname components don't
+  // matter.
+  EXPECT_TRUE(rules.Matches(GURL("http://www.google.com")));
+  EXPECT_TRUE(rules.Matches(GURL("ftp://www.google.com:99")));
+  EXPECT_TRUE(rules.Matches(GURL("https://a.google.com:81")));
+  EXPECT_TRUE(rules.Matches(GURL("http://foo.google.com/x/y?q")));
+  EXPECT_TRUE(rules.Matches(GURL("http://foo:bar@baz.google.com#x")));
+
+  // Must be a strict "ends with" to work.
+  EXPECT_FALSE(rules.Matches(GURL("http://google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://foo.google.com.baz.org")));
+}
+
+TEST(ProxyBypassRulesTest, ParseAndMatchBasicDomainWithPort) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("*.GOOGLE.com:80");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("*.google.com:80", rules.rules()[0]->ToString());
+
+  // All of these match; scheme, and non-hostname components don't matter.
+  EXPECT_TRUE(rules.Matches(GURL("http://www.google.com")));
+  EXPECT_TRUE(rules.Matches(GURL("ftp://www.google.com:80")));
+  EXPECT_TRUE(rules.Matches(GURL("https://a.google.com:80?x")));
+
+  // Must be a strict "ends with" to work.
+  EXPECT_FALSE(rules.Matches(GURL("http://google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://foo.google.com.baz.org")));
+
+  // The ports must match.
+  EXPECT_FALSE(rules.Matches(GURL("http://www.google.com:90")));
+  EXPECT_FALSE(rules.Matches(GURL("https://www.google.com")));
+}
+
+TEST(ProxyBypassRulesTest, MatchAll) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("*");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("*", rules.rules()[0]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://www.google.com")));
+  EXPECT_TRUE(rules.Matches(GURL("ftp://www.foobar.com:99")));
+  EXPECT_TRUE(rules.Matches(GURL("https://a.google.com:80?x")));
+}
+
+TEST(ProxyBypassRulesTest, WildcardAtStart) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("*.org:443");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("*.org:443", rules.rules()[0]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://www.google.org:443")));
+  EXPECT_TRUE(rules.Matches(GURL("https://www.google.org")));
+
+  EXPECT_FALSE(rules.Matches(GURL("http://www.google.org")));
+  EXPECT_FALSE(rules.Matches(GURL("https://www.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("https://www.google.org.com")));
+}
+
+TEST(ProxyBypassRulesTest, IPV4Address) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("192.168.1.1");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("192.168.1.1", rules.rules()[0]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://192.168.1.1")));
+  EXPECT_TRUE(rules.Matches(GURL("https://192.168.1.1:90")));
+
+  EXPECT_FALSE(rules.Matches(GURL("http://www.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://sup.192.168.1.1")));
+}
+
+TEST(ProxyBypassRulesTest, IPV4AddressWithPort) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("192.168.1.1:33");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("192.168.1.1:33", rules.rules()[0]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://192.168.1.1:33")));
+
+  EXPECT_FALSE(rules.Matches(GURL("http://www.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://192.168.1.1")));
+  EXPECT_FALSE(rules.Matches(GURL("http://sup.192.168.1.1:33")));
+}
+
+TEST(ProxyBypassRulesTest, IPV6Address) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("[3ffe:2a00:100:7031:0:0::1]");
+  ASSERT_EQ(1u, rules.rules().size());
+  // Note that we canonicalized the IP address.
+  EXPECT_EQ("[3ffe:2a00:100:7031::1]", rules.rules()[0]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://[3ffe:2a00:100:7031::1]")));
+  EXPECT_TRUE(rules.Matches(GURL("http://[3ffe:2a00:100:7031::1]:33")));
+
+  EXPECT_FALSE(rules.Matches(GURL("http://www.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://sup.192.168.1.1:33")));
+}
+
+TEST(ProxyBypassRulesTest, IPV6AddressWithPort) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("[3ffe:2a00:100:7031::1]:33");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("[3ffe:2a00:100:7031::1]:33", rules.rules()[0]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://[3ffe:2a00:100:7031::1]:33")));
+
+  EXPECT_FALSE(rules.Matches(GURL("http://[3ffe:2a00:100:7031::1]")));
+  EXPECT_FALSE(rules.Matches(GURL("http://www.google.com")));
+}
+
+TEST(ProxyBypassRulesTest, HTTPOnly) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("http://www.google.com");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("http://www.google.com", rules.rules()[0]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://www.google.com/foo")));
+  EXPECT_TRUE(rules.Matches(GURL("http://www.google.com:99")));
+
+  EXPECT_FALSE(rules.Matches(GURL("https://www.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("ftp://www.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://foo.www.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://www.google.com.org")));
+  EXPECT_FALSE(rules.Matches(GURL("https://www.google.com")));
+}
+
+TEST(ProxyBypassRulesTest, HTTPOnlyWithWildcard) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("http://*www.google.com");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("http://*www.google.com", rules.rules()[0]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://www.google.com/foo")));
+  EXPECT_TRUE(rules.Matches(GURL("http://www.google.com:99")));
+  EXPECT_TRUE(rules.Matches(GURL("http://foo.www.google.com")));
+
+  EXPECT_FALSE(rules.Matches(GURL("https://www.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("ftp://www.google.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://www.google.com.org")));
+  EXPECT_FALSE(rules.Matches(GURL("https://www.google.com")));
+}
+
+TEST(ProxyBypassRulesTest, UseSuffixMatching) {
+  ProxyBypassRules rules;
+  rules.ParseFromStringUsingSuffixMatching(
+      "foo1.com, .foo2.com, 192.168.1.1, "
+      "*foobar.com:80, *.foo, http://baz, <local>");
+  ASSERT_EQ(7u, rules.rules().size());
+  EXPECT_EQ("*foo1.com", rules.rules()[0]->ToString());
+  EXPECT_EQ("*.foo2.com", rules.rules()[1]->ToString());
+  EXPECT_EQ("192.168.1.1", rules.rules()[2]->ToString());
+  EXPECT_EQ("*foobar.com:80", rules.rules()[3]->ToString());
+  EXPECT_EQ("*.foo", rules.rules()[4]->ToString());
+  EXPECT_EQ("http://*baz", rules.rules()[5]->ToString());
+  EXPECT_EQ("<local>", rules.rules()[6]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://foo1.com")));
+  EXPECT_TRUE(rules.Matches(GURL("http://aaafoo1.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://aaafoo1.com.net")));
+}
+
+TEST(ProxyBypassRulesTest, MultipleRules) {
+  ProxyBypassRules rules;
+  rules.ParseFromString(".google.com , .foobar.com:30");
+  ASSERT_EQ(2u, rules.rules().size());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://baz.google.com:40")));
+  EXPECT_FALSE(rules.Matches(GURL("http://google.com:40")));
+  EXPECT_TRUE(rules.Matches(GURL("http://bar.foobar.com:30")));
+  EXPECT_FALSE(rules.Matches(GURL("http://bar.foobar.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://bar.foobar.com:33")));
+}
+
+TEST(ProxyBypassRulesTest, BadInputs) {
+  ProxyBypassRules rules;
+  EXPECT_FALSE(rules.AddRuleFromString("://"));
+  EXPECT_FALSE(rules.AddRuleFromString("  "));
+  EXPECT_FALSE(rules.AddRuleFromString("http://"));
+  EXPECT_FALSE(rules.AddRuleFromString("*.foo.com:-34"));
+  EXPECT_EQ(0u, rules.rules().size());
+}
+
+TEST(ProxyBypassRulesTest, Equals) {
+  ProxyBypassRules rules1;
+  ProxyBypassRules rules2;
+
+  rules1.ParseFromString("foo1.com, .foo2.com");
+  rules2.ParseFromString("foo1.com,.FOo2.com");
+
+  EXPECT_TRUE(rules1.Equals(rules2));
+  EXPECT_TRUE(rules2.Equals(rules1));
+
+  rules1.ParseFromString(".foo2.com");
+  rules2.ParseFromString("foo1.com,.FOo2.com");
+
+  EXPECT_FALSE(rules1.Equals(rules2));
+  EXPECT_FALSE(rules2.Equals(rules1));
+}
+
+TEST(ProxyBypassRulesTest, BypassLocalNames) {
+  const struct {
+    const char* url;
+    bool expected_is_local;
+  } tests[] = {
+    // Single-component hostnames are considered local.
+    {"http://localhost/x", true},
+    {"http://www", true},
+
+    // IPv4 loopback interface.
+    {"http://127.0.0.1/x", true},
+    {"http://127.0.0.1:80/x", true},
+
+    // IPv6 loopback interface.
+    {"http://[::1]:80/x", true},
+    {"http://[0:0::1]:6233/x", true},
+    {"http://[0:0:0:0:0:0:0:1]/x", true},
+
+    // Non-local URLs.
+    {"http://foo.com/", false},
+    {"http://localhost.i/", false},
+    {"http://www.google.com/", false},
+    {"http://192.168.0.1/", false},
+
+    // Try with different protocols.
+    {"ftp://127.0.0.1/x", true},
+    {"ftp://foobar.com/x", false},
+
+    // This is a bit of a gray-area, but GURL does not strip trailing dots
+    // in host-names, so the following are considered non-local.
+    {"http://www./x", false},
+    {"http://localhost./x", false},
+  };
+
+  ProxyBypassRules rules;
+  rules.ParseFromString("<local>");
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+    SCOPED_TRACE(StringPrintf(
+        "Test[%d]: %s", static_cast<int>(i), tests[i].url));
+    EXPECT_EQ(tests[i].expected_is_local, rules.Matches(GURL(tests[i].url)));
+  }
+}
+
+TEST(ProxyBypassRulesTest, ParseAndMatchCIDR_IPv4) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("192.168.1.1/16");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("192.168.1.1/16", rules.rules()[0]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://192.168.1.1")));
+  EXPECT_TRUE(rules.Matches(GURL("ftp://192.168.4.4")));
+  EXPECT_TRUE(rules.Matches(GURL("https://192.168.0.0:81")));
+  EXPECT_TRUE(rules.Matches(GURL("http://[::ffff:192.168.11.11]")));
+
+  EXPECT_FALSE(rules.Matches(GURL("http://foobar.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://192.169.1.1")));
+  EXPECT_FALSE(rules.Matches(GURL("http://xxx.192.168.1.1")));
+  EXPECT_FALSE(rules.Matches(GURL("http://192.168.1.1.xx")));
+}
+
+TEST(ProxyBypassRulesTest, ParseAndMatchCIDR_IPv6) {
+  ProxyBypassRules rules;
+  rules.ParseFromString("a:b:c:d::/48");
+  ASSERT_EQ(1u, rules.rules().size());
+  EXPECT_EQ("a:b:c:d::/48", rules.rules()[0]->ToString());
+
+  EXPECT_TRUE(rules.Matches(GURL("http://[A:b:C:9::]")));
+  EXPECT_FALSE(rules.Matches(GURL("http://foobar.com")));
+  EXPECT_FALSE(rules.Matches(GURL("http://192.169.1.1")));
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/proxy/proxy_config.cc b/net/proxy/proxy_config.cc
index 836559e..6e73101 100644
--- a/net/proxy/proxy_config.cc
+++ b/net/proxy/proxy_config.cc
@@ -6,27 +6,57 @@
 
 #include "base/string_tokenizer.h"
 #include "base/string_util.h"
+#include "net/proxy/proxy_info.h"
 
 namespace net {
 
-ProxyConfig::ProxyConfig()
-    : auto_detect(false),
-      proxy_bypass_local_names(false),
-      id_(INVALID_ID) {
+bool ProxyConfig::ProxyRules::Equals(const ProxyRules& other) const {
+  return type == other.type &&
+         single_proxy == other.single_proxy &&
+         proxy_for_http == other.proxy_for_http &&
+         proxy_for_https == other.proxy_for_https &&
+         proxy_for_ftp == other.proxy_for_ftp &&
+         socks_proxy == other.socks_proxy &&
+         bypass_rules.Equals(other.bypass_rules) &&
+         reverse_bypass == other.reverse_bypass;
 }
 
-bool ProxyConfig::Equals(const ProxyConfig& other) const {
-  // The two configs can have different IDs.  We are just interested in if they
-  // have the same settings.
-  return auto_detect == other.auto_detect &&
-         pac_url == other.pac_url &&
-         proxy_rules == other.proxy_rules &&
-         proxy_bypass == other.proxy_bypass &&
-         proxy_bypass_local_names == other.proxy_bypass_local_names;
-}
+void ProxyConfig::ProxyRules::Apply(const GURL& url, ProxyInfo* result) {
+  if (empty()) {
+    result->UseDirect();
+    return;
+  }
 
-bool ProxyConfig::MayRequirePACResolver() const {
-  return auto_detect || pac_url.is_valid();
+  bool bypass_proxy = bypass_rules.Matches(url);
+  if (reverse_bypass)
+    bypass_proxy = !bypass_proxy;
+  if (bypass_proxy) {
+    result->UseDirect();
+    return;
+  }
+
+  switch (type) {
+    case ProxyRules::TYPE_SINGLE_PROXY: {
+      result->UseProxyServer(single_proxy);
+      return;
+    }
+    case ProxyRules::TYPE_PROXY_PER_SCHEME: {
+      const ProxyServer* entry = MapUrlSchemeToProxy(url.scheme());
+      if (entry) {
+        result->UseProxyServer(*entry);
+      } else {
+        // We failed to find a matching proxy server for the current URL
+        // scheme. Default to direct.
+        result->UseDirect();
+      }
+      return;
+    }
+    default: {
+      result->UseDirect();
+      NOTREACHED();
+      return;
+    }
+  }
 }
 
 void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) {
@@ -98,70 +128,19 @@
   return NULL;  // No mapping for this scheme.
 }
 
-namespace {
-
-// Returns true if the given string represents an IP address.
-bool IsIPAddress(const std::string& domain) {
-  // From GURL::HostIsIPAddress()
-  url_canon::RawCanonOutputT<char, 128> ignored_output;
-  url_canon::CanonHostInfo host_info;
-  url_parse::Component domain_comp(0, domain.size());
-  url_canon::CanonicalizeIPAddress(domain.c_str(), domain_comp,
-                                   &ignored_output, &host_info);
-  return host_info.IsIPAddress();
+ProxyConfig::ProxyConfig() : auto_detect_(false), id_(INVALID_ID) {
 }
 
-}  // namespace
+bool ProxyConfig::Equals(const ProxyConfig& other) const {
+  // The two configs can have different IDs.  We are just interested in if they
+  // have the same settings.
+  return auto_detect_ == other.auto_detect_ &&
+         pac_url_ == other.pac_url_ &&
+         proxy_rules_.Equals(other.proxy_rules());
+}
 
-void ProxyConfig::ParseNoProxyList(const std::string& no_proxy) {
-  proxy_bypass.clear();
-  if (no_proxy.empty())
-    return;
-  // Traditional semantics:
-  // A single "*" is specifically allowed and unproxies anything.
-  // "*" wildcards other than a single "*" entry are not universally
-  // supported. We will support them, as we get * wildcards for free
-  // (see MatchPatternASCII() called from
-  // ProxyService::ShouldBypassProxyForURL()).
-  // no_proxy is a comma-separated list of <trailing_domain>[:<port>].
-  // If no port is specified then any port matches.
-  // The historical definition has trailing_domain match using a simple
-  // string "endswith" test, so that the match need not correspond to a
-  // "." boundary. For example: "google.com" matches "igoogle.com" too.
-  // Seems like that could be confusing, but we'll obey tradition.
-  // IP CIDR patterns are supposed to be supported too. We intend
-  // to do this in proxy_service.cc, but it's currently a TODO.
-  // See: http://crbug.com/9835.
-  StringTokenizer no_proxy_list(no_proxy, ",");
-  while (no_proxy_list.GetNext()) {
-    std::string bypass_entry = no_proxy_list.token();
-    TrimWhitespaceASCII(bypass_entry, TRIM_ALL, &bypass_entry);
-    if (bypass_entry.empty())
-      continue;
-    if (bypass_entry.at(0) != '*') {
-      // Insert a wildcard * to obtain an endsWith match, unless the
-      // entry looks like it might be an IP or CIDR.
-      // First look for either a :<port> or CIDR mask length suffix.
-      std::string::const_iterator begin = bypass_entry.begin();
-      std::string::const_iterator scan = bypass_entry.end() - 1;
-      while (scan > begin && IsAsciiDigit(*scan))
-        --scan;
-      std::string potential_ip;
-      if (*scan == '/' || *scan == ':')
-        potential_ip = std::string(begin, scan - 1);
-      else
-        potential_ip = bypass_entry;
-      if (!IsIPAddress(potential_ip)) {
-        // Do insert a wildcard.
-        bypass_entry.insert(0, "*");
-      }
-      // TODO(sdoyon): When CIDR matching is implemented in
-      // proxy_service.cc, consider making proxy_bypass more
-      // sophisticated to avoid parsing out the string on every
-      // request.
-    }
-    proxy_bypass.push_back(bypass_entry);
-  }
+bool ProxyConfig::MayRequirePACResolver() const {
+  return auto_detect_ || has_pac_url();
 }
 
 }  // namespace net
@@ -213,10 +192,10 @@
 std::ostream& operator<<(std::ostream& out, const net::ProxyConfig& config) {
   // "Automatic" settings.
   out << "Automatic settings:\n";
-  out << "  Auto-detect: " << BoolToYesNoString(config.auto_detect) << "\n";
+  out << "  Auto-detect: " << BoolToYesNoString(config.auto_detect()) << "\n";
   out << "  Custom PAC script: ";
-  if (config.pac_url.is_valid())
-    out << config.pac_url;
+  if (config.has_pac_url())
+    out << config.pac_url();
   else
     out << "[None]";
   out << "\n";
@@ -225,40 +204,42 @@
   out << "Manual settings:\n";
   out << "  Proxy server: ";
 
-  switch (config.proxy_rules.type) {
+  switch (config.proxy_rules().type) {
     case net::ProxyConfig::ProxyRules::TYPE_NO_RULES:
       out << "[None]\n";
       break;
     case net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY:
-      out << config.proxy_rules.single_proxy;
+      out << config.proxy_rules().single_proxy;
       out << "\n";
       break;
     case net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME:
       out << "\n";
-      if (config.proxy_rules.proxy_for_http.is_valid())
-        out << "    HTTP: " << config.proxy_rules.proxy_for_http << "\n";
-      if (config.proxy_rules.proxy_for_https.is_valid())
-        out << "    HTTPS: " << config.proxy_rules.proxy_for_https << "\n";
-      if (config.proxy_rules.proxy_for_ftp.is_valid())
-        out << "    FTP: " << config.proxy_rules.proxy_for_ftp << "\n";
-      if (config.proxy_rules.socks_proxy.is_valid())
-        out << "    SOCKS: " << config.proxy_rules.socks_proxy << "\n";
+      if (config.proxy_rules().proxy_for_http.is_valid())
+        out << "    HTTP: " << config.proxy_rules().proxy_for_http << "\n";
+      if (config.proxy_rules().proxy_for_https.is_valid())
+        out << "    HTTPS: " << config.proxy_rules().proxy_for_https << "\n";
+      if (config.proxy_rules().proxy_for_ftp.is_valid())
+        out << "    FTP: " << config.proxy_rules().proxy_for_ftp << "\n";
+      if (config.proxy_rules().socks_proxy.is_valid())
+        out << "    SOCKS: " << config.proxy_rules().socks_proxy << "\n";
       break;
   }
 
-  out << "  Bypass list: ";
-  if (config.proxy_bypass.empty()) {
-    out << "[None]\n";
+  if (config.proxy_rules().reverse_bypass)
+    out << "  Only use proxy for: ";
+  else
+    out << "  Bypass list: ";
+  if (config.proxy_rules().bypass_rules.rules().empty()) {
+    out << "[None]";
   } else {
-    out << "\n";
-    std::vector<std::string>::const_iterator it;
-    for (it = config.proxy_bypass.begin();
-         it != config.proxy_bypass.end(); ++it) {
-      out << "    " << *it << "\n";
+    const net::ProxyBypassRules& bypass_rules =
+        config.proxy_rules().bypass_rules;
+    net::ProxyBypassRules::RuleList::const_iterator it;
+    for (it = bypass_rules.rules().begin();
+         it != bypass_rules.rules().end(); ++it) {
+      out << "\n    " << (*it)->ToString();
     }
   }
 
-  out << "  Bypass local names: "
-      << BoolToYesNoString(config.proxy_bypass_local_names);
   return out;
 }
diff --git a/net/proxy/proxy_config.h b/net/proxy/proxy_config.h
index c48f74b..a247e38 100644
--- a/net/proxy/proxy_config.h
+++ b/net/proxy/proxy_config.h
@@ -10,32 +10,28 @@
 #include <vector>
 
 #include "googleurl/src/gurl.h"
+#include "net/proxy/proxy_bypass_rules.h"
 #include "net/proxy/proxy_server.h"
 
 namespace net {
 
-// Proxy configuration used to by the ProxyService.
+class ProxyInfo;
+
+// ProxyConfig describes a user's proxy settings.
+//
+// There are two categories of proxy settings:
+//   (1) Automatic (indicates the methods to obtain a PAC script)
+//   (2) Manual (simple set of proxy servers per scheme, and bypass patterns)
+//
+// When both automatic and manual settings are specified, the Automatic ones
+// take precedence over the manual ones.
+//
+// For more details see:
+// http://www.chromium.org/developers/design-documents/proxy-settings-fallback
 class ProxyConfig {
  public:
-  typedef int ID;
-
-  // Indicates an invalid proxy config.
-  enum { INVALID_ID = 0 };
-
-  ProxyConfig();
-  // Default copy-constructor and assignment operator are OK!
-
-  // Used to numerically identify this configuration.
-  ID id() const { return id_; }
-  void set_id(int id) { id_ = id; }
-  bool is_valid() { return id_ != INVALID_ID; }
-
-  // True if the proxy configuration should be auto-detected.
-  bool auto_detect;
-
-  // If non-empty, indicates the URL of the proxy auto-config file to use.
-  GURL pac_url;
-
+  // ProxyRules describes the "manual" proxy settings.
+  // TODO(eroman): Turn this into a class.
   struct ProxyRules {
     enum Type {
       TYPE_NO_RULES,
@@ -45,15 +41,18 @@
 
     // Note that the default of TYPE_NO_RULES results in direct connections
     // being made when using this ProxyConfig.
-    ProxyRules() : type(TYPE_NO_RULES) {}
+    ProxyRules() : reverse_bypass(false), type(TYPE_NO_RULES) {}
 
     bool empty() const {
       return type == TYPE_NO_RULES;
     }
 
+    // Sets |result| with the proxy to use for |url| based on the current rules.
+    void Apply(const GURL& url, ProxyInfo* result);
+
     // Parses the rules from a string, indicating which proxies to use.
     //
-    //   proxy-uri = [<proxy-scheme>://]<proxy-host>[:"<proxy-port>]
+    //   proxy-uri = [<proxy-scheme>"://"]<proxy-host>[":"<proxy-port>]
     //
     // If the proxy to use depends on the scheme of the URL, can instead specify
     // a semicolon separated list of:
@@ -61,8 +60,9 @@
     //   <url-scheme>"="<proxy-uri>
     //
     // For example:
-    //   "http=foopy:80;ftp=foopy2"  -- use HTTP proxy "foopy:80" for http URLs,
-    //                                  and HTTP proxy "foopy2:80" for ftp URLs.
+    //   "http=foopy:80;ftp=foopy2"  -- use HTTP proxy "foopy:80" for http://
+    //                                  URLs, and HTTP proxy "foopy2:80" for
+    //                                  ftp:// URLs.
     //   "foopy:80"                  -- use HTTP proxy "foopy:80" for all URLs.
     //   "socks4://foopy"            -- use SOCKS v4 proxy "foopy:1080" for all
     //                                  URLs.
@@ -75,14 +75,14 @@
     // Should only call this if the type is TYPE_PROXY_PER_SCHEME.
     const ProxyServer* MapUrlSchemeToProxy(const std::string& url_scheme) const;
 
-    bool operator==(const ProxyRules& other) const {
-      return type == other.type &&
-             single_proxy == other.single_proxy &&
-             proxy_for_http == other.proxy_for_http &&
-             proxy_for_https == other.proxy_for_https &&
-             proxy_for_ftp == other.proxy_for_ftp &&
-             socks_proxy == other.socks_proxy;
-    }
+    // Returns true if |*this| describes the same configuration as |other|.
+    bool Equals(const ProxyRules& other) const;
+
+    // Exceptions for when not to use a proxy.
+    ProxyBypassRules bypass_rules;
+
+    // Reverse the meaning of |bypass_rules|.
+    bool reverse_bypass;
 
     Type type;
 
@@ -94,8 +94,9 @@
     ProxyServer proxy_for_https;
     ProxyServer proxy_for_ftp;
 
-    // Set if configuration has SOCKS proxy.
+    // Set if the configuration has a SOCKS proxy fallback.
     ProxyServer socks_proxy;
+
    private:
     // Returns one of {&proxy_for_http, &proxy_for_https, &proxy_for_ftp,
     // &socks_proxy}, or NULL if it is a scheme that we don't have a mapping
@@ -103,21 +104,17 @@
     ProxyServer* MapSchemeToProxy(const std::string& scheme);
   };
 
-  ProxyRules proxy_rules;
+  typedef int ID;
 
-  // Parses entries from a comma-separated list of hosts for which proxy
-  // configurations should be bypassed. Clears proxy_bypass and sets it to the
-  // resulting list.
-  void ParseNoProxyList(const std::string& no_proxy);
+  // Indicates an invalid proxy config.
+  enum { INVALID_ID = 0 };
 
-  // Indicates a list of hosts that should bypass any proxy configuration.  For
-  // these hosts, a direct connection should always be used.
-  // The form <host>:<port> is also supported, meaning that only
-  // connections on the specified port should be direct.
-  std::vector<std::string> proxy_bypass;
+  ProxyConfig();
 
-  // Indicates whether local names (no dots) bypass proxies.
-  bool proxy_bypass_local_names;
+  // Used to numerically identify this configuration.
+  ID id() const { return id_; }
+  void set_id(int id) { id_ = id; }
+  bool is_valid() { return id_ != INVALID_ID; }
 
   // Returns true if the given config is equivalent to this config.
   bool Equals(const ProxyConfig& other) const;
@@ -126,7 +123,62 @@
   // use a PAC resolver.
   bool MayRequirePACResolver() const;
 
+  ProxyRules& proxy_rules() {
+    return proxy_rules_;
+  }
+
+  const ProxyRules& proxy_rules() const {
+    return proxy_rules_;
+  }
+
+  void set_pac_url(const GURL& url) {
+    pac_url_ = url;
+  }
+
+  const GURL& pac_url() const {
+    return pac_url_;
+  }
+
+  bool has_pac_url() const {
+    return pac_url_.is_valid();
+  }
+
+  void set_auto_detect(bool enable_auto_detect) {
+    auto_detect_ = enable_auto_detect;
+  }
+
+  bool auto_detect() const {
+    return auto_detect_;
+  }
+
+  // Helpers to construct some common proxy configurations.
+
+  static ProxyConfig CreateDirect() {
+    return ProxyConfig();
+  }
+
+  static ProxyConfig CreateAutoDetect() {
+    ProxyConfig config;
+    config.set_auto_detect(true);
+    return config;
+  }
+
+  static ProxyConfig CreateFromCustomPacURL(const GURL& pac_url) {
+    ProxyConfig config;
+    config.set_pac_url(pac_url);
+    return config;
+  }
+
  private:
+  // True if the proxy configuration should be auto-detected.
+  bool auto_detect_;
+
+  // If non-empty, indicates the URL of the proxy auto-config file to use.
+  GURL pac_url_;
+
+  // Manual proxy settings.
+  ProxyRules proxy_rules_;
+
   int id_;
 };
 
diff --git a/net/proxy/proxy_config_service_common_unittest.cc b/net/proxy/proxy_config_service_common_unittest.cc
index 1a61468..9e5fd76 100644
--- a/net/proxy/proxy_config_service_common_unittest.cc
+++ b/net/proxy/proxy_config_service_common_unittest.cc
@@ -9,58 +9,138 @@
 
 #include "net/proxy/proxy_config.h"
 
+#include "testing/gtest/include/gtest/gtest.h"
+
 namespace net {
 
-ProxyConfig::ProxyRules MakeProxyRules(
-    ProxyConfig::ProxyRules::Type type,
+namespace {
+
+// Helper to verify that |expected_proxy| matches |actual_proxy|. If it does
+// not, then |*did_fail| is set to true, and |*failure_details| is filled with
+// a description of the failure.
+void MatchesProxyServerHelper(const char* failure_message,
+                              const char* expected_proxy,
+                              const ProxyServer& actual_proxy,
+                              ::testing::AssertionResult* failure_details,
+                              bool* did_fail) {
+  std::string actual_proxy_string;
+  if (actual_proxy.is_valid())
+    actual_proxy_string = actual_proxy.ToURI();
+
+  if (std::string(expected_proxy) != actual_proxy_string) {
+    *failure_details
+        << failure_message << ". Was expecting: \"" << expected_proxy
+        << "\" but got: \"" << actual_proxy_string << "\"";
+    *did_fail = true;
+  }
+}
+
+std::string FlattenProxyBypass(const ProxyBypassRules& bypass_rules) {
+  std::string flattened_proxy_bypass;
+  for (ProxyBypassRules::RuleList::const_iterator it =
+       bypass_rules.rules().begin();
+       it != bypass_rules.rules().end(); ++it) {
+    if (!flattened_proxy_bypass.empty())
+      flattened_proxy_bypass += ",";
+    flattened_proxy_bypass += (*it)->ToString();
+  }
+  return flattened_proxy_bypass;
+}
+
+}  // namespace
+
+::testing::AssertionResult ProxyRulesExpectation::Matches(
+    const ProxyConfig::ProxyRules& rules) const {
+  ::testing::AssertionResult failure_details = ::testing::AssertionFailure();
+  bool failed = false;
+
+  if (rules.type != type) {
+    failure_details << "Type mismatch. Expected: "
+                    << type << " but was: " << rules.type;
+    failed = true;
+  }
+
+  MatchesProxyServerHelper("Bad single_proxy", single_proxy,
+                           rules.single_proxy, &failure_details, &failed);
+  MatchesProxyServerHelper("Bad proxy_for_http", proxy_for_http,
+                           rules.proxy_for_http, &failure_details, &failed);
+  MatchesProxyServerHelper("Bad proxy_for_https", proxy_for_https,
+                           rules.proxy_for_https, &failure_details, &failed);
+  MatchesProxyServerHelper("Bad proxy_for_socks", socks_proxy,
+                           rules.socks_proxy, &failure_details, &failed);
+
+  std::string actual_flattened_bypass = FlattenProxyBypass(rules.bypass_rules);
+  if (std::string(flattened_bypass_rules) != actual_flattened_bypass) {
+    failure_details
+        << "Bad bypass rules. Expected: \"" << flattened_bypass_rules
+        << "\" but got: \"" << actual_flattened_bypass << "\"";
+    failed = true;
+  }
+
+  if (rules.reverse_bypass != reverse_bypass) {
+    failure_details << "Bad reverse_bypass. Expected: " << reverse_bypass
+                    << " but got: " << rules.reverse_bypass;
+    failed = true;
+  }
+
+  return failed ? failure_details : ::testing::AssertionSuccess();
+}
+
+// static
+ProxyRulesExpectation ProxyRulesExpectation::Empty() {
+  return ProxyRulesExpectation(ProxyConfig::ProxyRules::TYPE_NO_RULES,
+                               "", "", "", "", "", "", false);
+}
+
+// static
+ProxyRulesExpectation ProxyRulesExpectation::EmptyWithBypass(
+    const char* flattened_bypass_rules) {
+  return ProxyRulesExpectation(ProxyConfig::ProxyRules::TYPE_NO_RULES,
+                               "", "", "", "", "", flattened_bypass_rules,
+                               false);
+}
+
+// static
+ProxyRulesExpectation ProxyRulesExpectation::Single(
     const char* single_proxy,
-    const char* proxy_for_http,
-    const char* proxy_for_https,
-    const char* proxy_for_ftp,
-    const char* socks_proxy) {
-  ProxyConfig::ProxyRules rules;
-  rules.type = type;
-  rules.single_proxy = ProxyServer::FromURI(single_proxy,
-                                            ProxyServer::SCHEME_HTTP);
-  rules.proxy_for_http = ProxyServer::FromURI(proxy_for_http,
-                                              ProxyServer::SCHEME_HTTP);
-  rules.proxy_for_https = ProxyServer::FromURI(proxy_for_https,
-                                               ProxyServer::SCHEME_HTTP);
-  rules.proxy_for_ftp = ProxyServer::FromURI(proxy_for_ftp,
-                                             ProxyServer::SCHEME_HTTP);
-  rules.socks_proxy = ProxyServer::FromURI(socks_proxy,
-                                           ProxyServer::SCHEME_SOCKS4);
-  return rules;
+    const char* flattened_bypass_rules) {
+  return ProxyRulesExpectation(ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY,
+                               single_proxy, "", "", "", "",
+                               flattened_bypass_rules, false);
 }
 
-ProxyConfig::ProxyRules MakeSingleProxyRules(const char* single_proxy) {
-  return MakeProxyRules(ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY,
-                        single_proxy, "", "", "", "");
-}
-
-ProxyConfig::ProxyRules MakeProxyPerSchemeRules(
-    const char* proxy_http,
-    const char* proxy_https,
-    const char* proxy_ftp) {
-  return MakeProxyRules(ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
-                        "", proxy_http, proxy_https, proxy_ftp, "");
-}
-ProxyConfig::ProxyRules MakeProxyPerSchemeRules(
+// static
+ProxyRulesExpectation ProxyRulesExpectation::PerScheme(
     const char* proxy_http,
     const char* proxy_https,
     const char* proxy_ftp,
-    const char* socks_proxy) {
-  return MakeProxyRules(ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
-                        "", proxy_http, proxy_https, proxy_ftp, socks_proxy);
+    const char* flattened_bypass_rules) {
+  return ProxyRulesExpectation(ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
+                               "", proxy_http, proxy_https, proxy_ftp, "",
+                               flattened_bypass_rules, false);
 }
 
-std::string FlattenProxyBypass(const BypassList& proxy_bypass) {
-  std::string flattened_proxy_bypass;
-  for (BypassList::const_iterator it = proxy_bypass.begin();
-       it != proxy_bypass.end(); ++it) {
-    flattened_proxy_bypass += *it + "\n";
-  }
-  return flattened_proxy_bypass;
+// static
+ProxyRulesExpectation ProxyRulesExpectation::PerSchemeWithSocks(
+    const char* proxy_http,
+    const char* proxy_https,
+    const char* proxy_ftp,
+    const char* socks_proxy,
+    const char* flattened_bypass_rules) {
+  return ProxyRulesExpectation(ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
+                               "", proxy_http, proxy_https, proxy_ftp,
+                               socks_proxy, flattened_bypass_rules, false);
+}
+
+// static
+ProxyRulesExpectation ProxyRulesExpectation::PerSchemeWithBypassReversed(
+    const char* proxy_http,
+    const char* proxy_https,
+    const char* proxy_ftp,
+    const char* flattened_bypass_rules) {
+  return ProxyRulesExpectation(ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
+                               "", proxy_http, proxy_https, proxy_ftp, "",
+                               flattened_bypass_rules, true);
 }
 
 }  // namespace net
diff --git a/net/proxy/proxy_config_service_common_unittest.h b/net/proxy/proxy_config_service_common_unittest.h
index 783fc6f..3b70ef0 100644
--- a/net/proxy/proxy_config_service_common_unittest.h
+++ b/net/proxy/proxy_config_service_common_unittest.h
@@ -9,36 +9,86 @@
 #include <vector>
 
 #include "net/proxy/proxy_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
-// A few small helper functions common to the win and linux unittests.
+// Helper functions to describe the expected value of a
+// ProxyConfig::ProxyRules, and to check for a match.
 
 namespace net {
 
-ProxyConfig::ProxyRules MakeProxyRules(
-    ProxyConfig::ProxyRules::Type type,
-    const char* single_proxy,
-    const char* proxy_for_http,
-    const char* proxy_for_https,
-    const char* proxy_for_ftp,
-    const char* socks_proxy);
+class ProxyBypassRules;
 
-ProxyConfig::ProxyRules MakeSingleProxyRules(const char* single_proxy);
+// This structure contains our expectations on what values the ProxyRules
+// should have.
+struct ProxyRulesExpectation {
+  ProxyRulesExpectation(ProxyConfig::ProxyRules::Type type,
+                        const char* single_proxy,
+                        const char* proxy_for_http,
+                        const char* proxy_for_https,
+                        const char* proxy_for_ftp,
+                        const char* socks_proxy,
+                        const char* flattened_bypass_rules,
+                        bool reverse_bypass)
+    : type(type),
+      single_proxy(single_proxy),
+      proxy_for_http(proxy_for_http),
+      proxy_for_https(proxy_for_https),
+      proxy_for_ftp(proxy_for_ftp),
+      socks_proxy(socks_proxy),
+      flattened_bypass_rules(flattened_bypass_rules),
+      reverse_bypass(reverse_bypass) {
+  }
 
-ProxyConfig::ProxyRules MakeProxyPerSchemeRules(
-    const char* proxy_http,
-    const char* proxy_https,
-    const char* proxy_ftp);
 
-ProxyConfig::ProxyRules MakeProxyPerSchemeRules(
-    const char* proxy_http,
-    const char* proxy_https,
-    const char* proxy_ftp,
-    const char* socks_proxy);
+  // Call this within an EXPECT_TRUE(), to assert that |rules| matches
+  // our expected values |*this|.
+  ::testing::AssertionResult Matches(
+      const ProxyConfig::ProxyRules& rules) const;
 
-typedef std::vector<std::string> BypassList;
+  // Creates an expectation that the ProxyRules has no rules.
+  static ProxyRulesExpectation Empty();
 
-// Joins the proxy bypass list using "\n" to make it into a single string.
-std::string FlattenProxyBypass(const BypassList& proxy_bypass);
+  // Creates an expectation that the ProxyRules has nothing other than
+  // the specified bypass rules.
+  static ProxyRulesExpectation EmptyWithBypass(
+      const char* flattened_bypass_rules);
+
+  // Creates an expectation that the ProxyRules is for a single proxy
+  // server for all schemes.
+  static ProxyRulesExpectation Single(const char* single_proxy,
+                                      const char* flattened_bypass_rules);
+
+  // Creates an expectation that the ProxyRules specifies a different
+  // proxy server for each URL scheme.
+  static ProxyRulesExpectation PerScheme(const char* proxy_http,
+                                         const char* proxy_https,
+                                         const char* proxy_ftp,
+                                         const char* flattened_bypass_rules);
+
+  // Same as above, but additionally with a SOCKS fallback.
+  static ProxyRulesExpectation PerSchemeWithSocks(
+      const char* proxy_http,
+      const char* proxy_https,
+      const char* proxy_ftp,
+      const char* socks_proxy,
+      const char* flattened_bypass_rules);
+
+  // Same as PerScheme, but with the bypass rules reversed
+  static ProxyRulesExpectation PerSchemeWithBypassReversed(
+      const char* proxy_http,
+      const char* proxy_https,
+      const char* proxy_ftp,
+      const char* flattened_bypass_rules);
+
+  ProxyConfig::ProxyRules::Type type;
+  const char* single_proxy;
+  const char* proxy_for_http;
+  const char* proxy_for_https;
+  const char* proxy_for_ftp;
+  const char* socks_proxy;
+  const char* flattened_bypass_rules;
+  bool reverse_bypass;
+};
 
 }  // namespace net
 
diff --git a/net/proxy/proxy_config_service_fixed.h b/net/proxy/proxy_config_service_fixed.h
index 54fd9ac..b677eb4 100644
--- a/net/proxy/proxy_config_service_fixed.h
+++ b/net/proxy/proxy_config_service_fixed.h
@@ -6,6 +6,7 @@
 #define NET_PROXY_PROXY_CONFIG_SERVICE_FIXED_H_
 
 #include "net/base/net_errors.h"
+#include "net/proxy/proxy_config.h"
 #include "net/proxy/proxy_config_service.h"
 
 namespace net {
diff --git a/net/proxy/proxy_config_service_linux.cc b/net/proxy/proxy_config_service_linux.cc
index 3ca35c5..bd863bd 100644
--- a/net/proxy/proxy_config_service_linux.cc
+++ b/net/proxy/proxy_config_service_linux.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -13,6 +13,9 @@
 #include <sys/inotify.h>
 #include <unistd.h>
 
+#include <map>
+
+#include "base/env_var.h"
 #include "base/file_path.h"
 #include "base/file_util.h"
 #include "base/logging.h"
@@ -21,6 +24,7 @@
 #include "base/string_util.h"
 #include "base/task.h"
 #include "base/timer.h"
+#include "base/xdg_util.h"
 #include "googleurl/src/url_canon.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_util.h"
@@ -78,7 +82,7 @@
     const char* variable, ProxyServer::Scheme scheme,
     ProxyServer* result_server) {
   std::string env_value;
-  if (env_var_getter_->Getenv(variable, &env_value)) {
+  if (env_var_getter_->GetEnv(variable, &env_value)) {
     if (!env_value.empty()) {
       env_value = FixupProxyHostScheme(scheme, env_value);
       ProxyServer proxy_server =
@@ -106,25 +110,25 @@
   // extension has ever used this, but it still sounds like a good
   // idea.
   std::string auto_proxy;
-  if (env_var_getter_->Getenv("auto_proxy", &auto_proxy)) {
+  if (env_var_getter_->GetEnv("auto_proxy", &auto_proxy)) {
     if (auto_proxy.empty()) {
       // Defined and empty => autodetect
-      config->auto_detect = true;
+      config->set_auto_detect(true);
     } else {
       // specified autoconfig URL
-      config->pac_url = GURL(auto_proxy);
+      config->set_pac_url(GURL(auto_proxy));
     }
     return true;
   }
   // "all_proxy" is a shortcut to avoid defining {http,https,ftp}_proxy.
   ProxyServer proxy_server;
   if (GetProxyFromEnvVar("all_proxy", &proxy_server)) {
-    config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
-    config->proxy_rules.single_proxy = proxy_server;
+    config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
+    config->proxy_rules().single_proxy = proxy_server;
   } else {
     bool have_http = GetProxyFromEnvVar("http_proxy", &proxy_server);
     if (have_http)
-      config->proxy_rules.proxy_for_http = proxy_server;
+      config->proxy_rules().proxy_for_http = proxy_server;
     // It would be tempting to let http_proxy apply for all protocols
     // if https_proxy and ftp_proxy are not defined. Googling turns up
     // several documents that mention only http_proxy. But then the
@@ -132,38 +136,42 @@
     // like other apps do this. So we will refrain.
     bool have_https = GetProxyFromEnvVar("https_proxy", &proxy_server);
     if (have_https)
-      config->proxy_rules.proxy_for_https = proxy_server;
+      config->proxy_rules().proxy_for_https = proxy_server;
     bool have_ftp = GetProxyFromEnvVar("ftp_proxy", &proxy_server);
     if (have_ftp)
-      config->proxy_rules.proxy_for_ftp = proxy_server;
+      config->proxy_rules().proxy_for_ftp = proxy_server;
     if (have_http || have_https || have_ftp) {
       // mustn't change type unless some rules are actually set.
-      config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
+      config->proxy_rules().type =
+          ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
     }
   }
-  if (config->proxy_rules.empty()) {
+  if (config->proxy_rules().empty()) {
     // If the above were not defined, try for socks.
     ProxyServer::Scheme scheme = ProxyServer::SCHEME_SOCKS4;
     std::string env_version;
-    if (env_var_getter_->Getenv("SOCKS_VERSION", &env_version)
+    if (env_var_getter_->GetEnv("SOCKS_VERSION", &env_version)
         && env_version == "5")
       scheme = ProxyServer::SCHEME_SOCKS5;
     if (GetProxyFromEnvVarForScheme("SOCKS_SERVER", scheme, &proxy_server)) {
-      config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
-      config->proxy_rules.single_proxy = proxy_server;
+      config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
+      config->proxy_rules().single_proxy = proxy_server;
     }
   }
   // Look for the proxy bypass list.
   std::string no_proxy;
-  env_var_getter_->Getenv("no_proxy", &no_proxy);
-  if (config->proxy_rules.empty()) {
+  env_var_getter_->GetEnv("no_proxy", &no_proxy);
+  if (config->proxy_rules().empty()) {
     // Having only "no_proxy" set, presumably to "*", makes it
     // explicit that env vars do specify a configuration: having no
     // rules specified only means the user explicitly asks for direct
     // connections.
     return !no_proxy.empty();
   }
-  config->ParseNoProxyList(no_proxy);
+  // Note that this uses "suffix" matching. So a bypass of "google.com"
+  // is understood to mean a bypass of "*google.com".
+  config->proxy_rules().bypass_rules.ParseFromStringUsingSuffixMatching(
+      no_proxy);
   return true;
 }
 
@@ -343,6 +351,15 @@
     return true;
   }
 
+  virtual bool BypassListIsReversed() {
+    // This is a KDE-specific setting.
+    return false;
+  }
+
+  virtual bool MatchHostsUsingSuffixMatching() {
+    return false;
+  }
+
  private:
   // Logs and frees a glib error. Returns false if there was no error
   // (error is NULL).
@@ -404,21 +421,60 @@
     : public ProxyConfigServiceLinux::GConfSettingGetter,
       public base::MessagePumpLibevent::Watcher {
  public:
-  explicit GConfSettingGetterImplKDE(
-      base::EnvironmentVariableGetter* env_var_getter)
+  explicit GConfSettingGetterImplKDE(base::EnvVarGetter* env_var_getter)
       : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false),
-        auto_no_pac_(false), reversed_exception_(false), file_loop_(NULL) {
-    // We don't save the env var getter for later use since we don't own it.
-    // Instead we use it here and save the result we actually care about.
-    std::string kde_home;
-    if (!env_var_getter->Getenv("KDE_HOME", &kde_home)) {
-      if (!env_var_getter->Getenv("HOME", &kde_home))
+        auto_no_pac_(false), reversed_bypass_list_(false),
+        env_var_getter_(env_var_getter), file_loop_(NULL) {
+    // Derive the location of the kde config dir from the environment.
+    std::string home;
+    if (env_var_getter->GetEnv("KDEHOME", &home) && !home.empty()) {
+      // $KDEHOME is set. Use it unconditionally.
+      kde_config_dir_ = KDEHomeToConfigPath(FilePath(home));
+    } else {
+      // $KDEHOME is unset. Try to figure out what to use. This seems to be
+      // the common case on most distributions.
+      if (!env_var_getter->GetEnv(base::env_vars::kHome, &home))
         // User has no $HOME? Give up. Later we'll report the failure.
         return;
-      kde_home = FilePath(kde_home).Append(FILE_PATH_LITERAL(".kde")).value();
+      if (base::GetDesktopEnvironment(env_var_getter) ==
+          base::DESKTOP_ENVIRONMENT_KDE3) {
+        // KDE3 always uses .kde for its configuration.
+        FilePath kde_path = FilePath(home).Append(".kde");
+        kde_config_dir_ = KDEHomeToConfigPath(kde_path);
+      } else {
+        // Some distributions patch KDE4 to use .kde4 instead of .kde, so that
+        // both can be installed side-by-side. Sadly they don't all do this, and
+        // they don't always do this: some distributions have started switching
+        // back as well. So if there is a .kde4 directory, check the timestamps
+        // of the config directories within and use the newest one.
+        // Note that we should currently be running in the UI thread, because in
+        // the gconf version, that is the only thread that can access the proxy
+        // settings (a gconf restriction). As noted below, the initial read of
+        // the proxy settings will be done in this thread anyway, so we check
+        // for .kde4 here in this thread as well.
+        FilePath kde3_path = FilePath(home).Append(".kde");
+        FilePath kde3_config = KDEHomeToConfigPath(kde3_path);
+        FilePath kde4_path = FilePath(home).Append(".kde4");
+        FilePath kde4_config = KDEHomeToConfigPath(kde4_path);
+        bool use_kde4 = false;
+        if (file_util::DirectoryExists(kde4_path)) {
+          file_util::FileInfo kde3_info;
+          file_util::FileInfo kde4_info;
+          if (file_util::GetFileInfo(kde4_config, &kde4_info)) {
+            if (file_util::GetFileInfo(kde3_config, &kde3_info)) {
+              use_kde4 = kde4_info.last_modified >= kde3_info.last_modified;
+            } else {
+              use_kde4 = true;
+            }
+          }
+        }
+        if (use_kde4) {
+          kde_config_dir_ = KDEHomeToConfigPath(kde4_path);
+        } else {
+          kde_config_dir_ = KDEHomeToConfigPath(kde3_path);
+        }
+      }
     }
-    kde_config_dir_ = FilePath(kde_home).Append(
-        FILE_PATH_LITERAL("share")).Append(FILE_PATH_LITERAL("config"));
   }
 
   virtual ~GConfSettingGetterImplKDE() {
@@ -523,16 +579,28 @@
     return true;
   }
 
+  virtual bool BypassListIsReversed() {
+    return reversed_bypass_list_;
+  }
+
+  virtual bool MatchHostsUsingSuffixMatching() {
+    return true;
+  }
+
  private:
   void ResetCachedSettings() {
     string_table_.clear();
     strings_table_.clear();
     indirect_manual_ = false;
     auto_no_pac_ = false;
-    reversed_exception_ = false;
+    reversed_bypass_list_ = false;
   }
 
-  void AddProxy(std::string prefix, std::string value) {
+  FilePath KDEHomeToConfigPath(const FilePath& kde_home) {
+    return kde_home.Append("share").Append("config");
+  }
+
+  void AddProxy(const std::string& prefix, const std::string& value) {
     if (value.empty() || value.substr(0, 3) == "//:")
       // No proxy.
       return;
@@ -542,6 +610,17 @@
     string_table_[prefix + "host"] = value;
   }
 
+  void AddHostList(const std::string& key, const std::string& value) {
+    std::vector<std::string> tokens;
+    StringTokenizer tk(value, ", ");
+    while (tk.GetNext()) {
+      std::string token = tk.token();
+      if (!token.empty())
+        tokens.push_back(token);
+    }
+    strings_table_[key] = tokens;
+  }
+
   void AddKDESetting(const std::string& key, const std::string& value) {
     // The astute reader may notice that there is no mention of SOCKS
     // here. That's because KDE handles socks is a strange way, and we
@@ -584,16 +663,9 @@
       // We count "true" or any nonzero number as true, otherwise false.
       // Note that if the value is not actually numeric StringToInt()
       // will return 0, which we count as false.
-      reversed_exception_ = value == "true" || StringToInt(value);
+      reversed_bypass_list_ = (value == "true" || StringToInt(value));
     } else if (key == "NoProxyFor") {
-      std::vector<std::string> exceptions;
-      StringTokenizer tk(value, ",");
-      while (tk.GetNext()) {
-        std::string token = tk.token();
-        if (!token.empty())
-          exceptions.push_back(token);
-      }
-      strings_table_["/system/http_proxy/ignore_hosts"] = exceptions;
+      AddHostList("/system/http_proxy/ignore_hosts", value);
     } else if (key == "AuthMode") {
       // Check for authentication, just so we can warn.
       int mode = StringToInt(value);
@@ -606,16 +678,26 @@
     }
   }
 
-  void ResolveIndirect(std::string key) {
-    // We can't save the environment variable getter that was passed
-    // when this object was constructed, but this setting is likely
-    // to be pretty unusual and the actual values it would return can
-    // be tested without using it. So we just use getenv() here.
+  void ResolveIndirect(const std::string& key) {
     string_map_type::iterator it = string_table_.find(key);
     if (it != string_table_.end()) {
-      char* value = getenv(it->second.c_str());
-      if (value)
+      std::string value;
+      if (env_var_getter_->GetEnv(it->second.c_str(), &value))
         it->second = value;
+      else
+        string_table_.erase(it);
+    }
+  }
+
+  void ResolveIndirectList(const std::string& key) {
+    strings_map_type::iterator it = strings_table_.find(key);
+    if (it != strings_table_.end()) {
+      std::string value;
+      if (!it->second.empty() &&
+          env_var_getter_->GetEnv(it->second[0].c_str(), &value))
+        AddHostList(key, value);
+      else
+        strings_table_.erase(it);
     }
   }
 
@@ -628,26 +710,18 @@
       ResolveIndirect("/system/http_proxy/host");
       ResolveIndirect("/system/proxy/secure_host");
       ResolveIndirect("/system/proxy/ftp_host");
+      ResolveIndirectList("/system/http_proxy/ignore_hosts");
     }
     if (auto_no_pac_) {
       // Remove the PAC URL; we're not supposed to use it.
       string_table_.erase("/system/proxy/autoconfig_url");
     }
-    if (reversed_exception_) {
-      // We don't actually support this setting. (It means to use the proxy
-      // *only* for the exception list, rather than everything but them.)
-      // Nevertheless we can do better than *exactly the opposite* of the
-      // desired behavior by clearing the exception list and warning.
-      strings_table_.erase("/system/http_proxy/ignore_hosts");
-      LOG(WARNING) << "KDE reversed proxy exception list not supported";
-    }
   }
 
   // Reads kioslaverc one line at a time and calls AddKDESetting() to add
   // each relevant name-value pair to the appropriate value table.
   void UpdateCachedSettings() {
-    FilePath kioslaverc = kde_config_dir_.Append(
-        FILE_PATH_LITERAL("kioslaverc"));
+    FilePath kioslaverc = kde_config_dir_.Append("kioslaverc");
     file_util::ScopedFILE input(file_util::OpenFile(kioslaverc, "r"));
     if (!input.get())
       return;
@@ -747,8 +821,8 @@
       while (event_ptr < event_buf + r) {
         inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr);
         // The kernel always feeds us whole events.
-        CHECK(event_ptr + sizeof(inotify_event) <= event_buf + r);
-        CHECK(event->name + event->len <= event_buf + r);
+        CHECK_LE(event_ptr + sizeof(inotify_event), event_buf + r);
+        CHECK_LE(event->name + event->len, event_buf + r);
         if (!strcmp(event->name, "kioslaverc"))
           kioslaverc_touched = true;
         // Advance the pointer just past the end of the filename.
@@ -795,7 +869,11 @@
   FilePath kde_config_dir_;
   bool indirect_manual_;
   bool auto_no_pac_;
-  bool reversed_exception_;
+  bool reversed_bypass_list_;
+  // We don't own |env_var_getter_|.  It's safe to hold a pointer to it, since
+  // both it and us are owned by ProxyConfigServiceLinux::Delegate, and have the
+  // same lifetime.
+  base::EnvVarGetter* env_var_getter_;
 
   // We cache these settings whenever we re-read the kioslaverc file.
   string_map_type string_table_;
@@ -862,11 +940,11 @@
         GURL pac_url(pac_url_str);
         if (!pac_url.is_valid())
           return false;
-        config->pac_url = pac_url;
+        config->set_pac_url(pac_url);
         return true;
       }
     }
-    config->auto_detect = true;
+    config->set_auto_detect(true);
     return true;
   }
 
@@ -896,37 +974,37 @@
     if (GetProxyFromGConf("/system/proxy/socks_", true, &proxy_server)) {
       // gconf settings do not appear to distinguish between socks
       // version. We default to version 4.
-      config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
-      config->proxy_rules.single_proxy = proxy_server;
+      config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
+      config->proxy_rules().single_proxy = proxy_server;
     }
   }
-  if (config->proxy_rules.empty()) {
+  if (config->proxy_rules().empty()) {
     bool have_http = GetProxyFromGConf("/system/http_proxy/", false,
                                        &proxy_server);
     if (same_proxy) {
       if (have_http) {
-        config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
-        config->proxy_rules.single_proxy = proxy_server;
+        config->proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
+        config->proxy_rules().single_proxy = proxy_server;
       }
     } else {
       // Protocol specific settings.
       if (have_http)
-        config->proxy_rules.proxy_for_http = proxy_server;
+        config->proxy_rules().proxy_for_http = proxy_server;
       bool have_secure = GetProxyFromGConf("/system/proxy/secure_", false,
                                            &proxy_server);
       if (have_secure)
-        config->proxy_rules.proxy_for_https = proxy_server;
+        config->proxy_rules().proxy_for_https = proxy_server;
       bool have_ftp = GetProxyFromGConf("/system/proxy/ftp_", false,
                                         &proxy_server);
       if (have_ftp)
-        config->proxy_rules.proxy_for_ftp = proxy_server;
+        config->proxy_rules().proxy_for_ftp = proxy_server;
       if (have_http || have_secure || have_ftp)
-        config->proxy_rules.type =
+        config->proxy_rules().type =
             ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
     }
   }
 
-  if (config->proxy_rules.empty()) {
+  if (config->proxy_rules().empty()) {
     // Manual mode but we couldn't parse any rules.
     return false;
   }
@@ -943,16 +1021,31 @@
   }
 
   // Now the bypass list.
-  gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
-                               &config->proxy_bypass);
+  std::vector<std::string> ignore_hosts_list;
+  config->proxy_rules().bypass_rules.Clear();
+  if (gconf_getter_->GetStringList("/system/http_proxy/ignore_hosts",
+                                   &ignore_hosts_list)) {
+    std::vector<std::string>::const_iterator it(ignore_hosts_list.begin());
+    for (; it != ignore_hosts_list.end(); ++it) {
+      if (gconf_getter_->MatchHostsUsingSuffixMatching()) {
+        config->proxy_rules().bypass_rules.
+            AddRuleFromStringUsingSuffixMatching(*it);
+      } else {
+        config->proxy_rules().bypass_rules.AddRuleFromString(*it);
+      }
+    }
+  }
   // Note that there are no settings with semantics corresponding to
-  // config->proxy_bypass_local_names.
+  // bypass of local names in GNOME. In KDE, "<local>" is supported
+  // as a hostname rule.
+
+  // KDE allows one to reverse the bypass rules.
+  config->proxy_rules().reverse_bypass = gconf_getter_->BypassListIsReversed();
 
   return true;
 }
 
-ProxyConfigServiceLinux::Delegate::Delegate(
-    base::EnvironmentVariableGetter* env_var_getter)
+ProxyConfigServiceLinux::Delegate::Delegate(base::EnvVarGetter* env_var_getter)
     : env_var_getter_(env_var_getter),
       glib_default_loop_(NULL), io_loop_(NULL) {
   // Figure out which GConfSettingGetterImpl to use, if any.
@@ -964,13 +1057,13 @@
     case base::DESKTOP_ENVIRONMENT_KDE4:
       gconf_getter_.reset(new GConfSettingGetterImplKDE(env_var_getter));
       break;
+    case base::DESKTOP_ENVIRONMENT_XFCE:
     case base::DESKTOP_ENVIRONMENT_OTHER:
       break;
   }
 }
 
-ProxyConfigServiceLinux::Delegate::Delegate(
-    base::EnvironmentVariableGetter* env_var_getter,
+ProxyConfigServiceLinux::Delegate::Delegate(base::EnvVarGetter* env_var_getter,
     GConfSettingGetter* gconf_getter)
     : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter),
       glib_default_loop_(NULL), io_loop_(NULL) {
@@ -998,7 +1091,7 @@
   // the ProxyService.
 
   // Note: It would be nice to prioritize environment variables
-  // and only fallback to gconf if env vars were unset. But
+  // and only fall back to gconf if env vars were unset. But
   // gnome-terminal "helpfully" sets http_proxy and no_proxy, and it
   // does so even if the proxy mode is set to auto, which would
   // mislead us.
@@ -1108,16 +1201,16 @@
 }
 
 ProxyConfigServiceLinux::ProxyConfigServiceLinux()
-    : delegate_(new Delegate(base::EnvironmentVariableGetter::Create())) {
+    : delegate_(new Delegate(base::EnvVarGetter::Create())) {
 }
 
 ProxyConfigServiceLinux::ProxyConfigServiceLinux(
-    base::EnvironmentVariableGetter* env_var_getter)
+    base::EnvVarGetter* env_var_getter)
     : delegate_(new Delegate(env_var_getter)) {
 }
 
 ProxyConfigServiceLinux::ProxyConfigServiceLinux(
-    base::EnvironmentVariableGetter* env_var_getter,
+    base::EnvVarGetter* env_var_getter,
     GConfSettingGetter* gconf_getter)
     : delegate_(new Delegate(env_var_getter, gconf_getter)) {
 }
diff --git a/net/proxy/proxy_config_service_linux.h b/net/proxy/proxy_config_service_linux.h
index a55ba35..c6e9c90 100644
--- a/net/proxy/proxy_config_service_linux.h
+++ b/net/proxy/proxy_config_service_linux.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -9,7 +9,7 @@
 #include <vector>
 
 #include "base/basictypes.h"
-#include "base/linux_util.h"
+#include "base/env_var.h"
 #include "base/message_loop.h"
 #include "base/ref_counted.h"
 #include "base/scoped_ptr.h"
@@ -76,6 +76,14 @@
     virtual bool GetStringList(const char* key,
                                std::vector<std::string>* result) = 0;
 
+    // Returns true if the bypass list should be interpreted as a proxy
+    // whitelist rather than blacklist. (This is KDE-specific.)
+    virtual bool BypassListIsReversed() = 0;
+
+    // Returns true if the bypass rules should be interpreted as
+    // suffix-matching rules.
+    virtual bool MatchHostsUsingSuffixMatching() = 0;
+
    private:
     DISALLOW_COPY_AND_ASSIGN(GConfSettingGetter);
   };
@@ -105,10 +113,10 @@
    public:
     // Constructor receives env var getter implementation to use, and
     // takes ownership of it. This is the normal constructor.
-    explicit Delegate(base::EnvironmentVariableGetter* env_var_getter);
+    explicit Delegate(base::EnvVarGetter* env_var_getter);
     // Constructor receives gconf and env var getter implementations
     // to use, and takes ownership of them. Used for testing.
-    Delegate(base::EnvironmentVariableGetter* env_var_getter,
+    Delegate(base::EnvVarGetter* env_var_getter,
              GConfSettingGetter* gconf_getter);
     // Synchronously obtains the proxy configuration. If gconf is
     // used, also enables gconf notification for setting
@@ -168,7 +176,7 @@
     // carry the new config information.
     void SetNewProxyConfig(const ProxyConfig& new_config);
 
-    scoped_ptr<base::EnvironmentVariableGetter> env_var_getter_;
+    scoped_ptr<base::EnvVarGetter> env_var_getter_;
     scoped_ptr<GConfSettingGetter> gconf_getter_;
 
     // Cached proxy configuration, to be returned by
@@ -203,9 +211,8 @@
   // Usual constructor
   ProxyConfigServiceLinux();
   // For testing: take alternate gconf and env var getter implementations.
-  explicit ProxyConfigServiceLinux(
-      base::EnvironmentVariableGetter* env_var_getter);
-  ProxyConfigServiceLinux(base::EnvironmentVariableGetter* env_var_getter,
+  explicit ProxyConfigServiceLinux(base::EnvVarGetter* env_var_getter);
+  ProxyConfigServiceLinux(base::EnvVarGetter* env_var_getter,
                           GConfSettingGetter* gconf_getter);
 
   virtual ~ProxyConfigServiceLinux() {
diff --git a/net/proxy/proxy_config_service_linux_unittest.cc b/net/proxy/proxy_config_service_linux_unittest.cc
index 4ebc12e..0a84548 100644
--- a/net/proxy/proxy_config_service_linux_unittest.cc
+++ b/net/proxy/proxy_config_service_linux_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -29,7 +29,8 @@
 struct EnvVarValues {
   // The strange capitalization is so that the field matches the
   // environment variable name exactly.
-  const char *DESKTOP_SESSION, *KDE_HOME,
+  const char *DESKTOP_SESSION, *HOME,
+      *KDEHOME, *KDE_SESSION_VERSION,
       *auto_proxy, *all_proxy,
       *http_proxy, *https_proxy, *ftp_proxy,
       *SOCKS_SERVER, *SOCKS_VERSION,
@@ -77,12 +78,14 @@
   map_type settings;
 };
 
-class MockEnvironmentVariableGetter : public base::EnvironmentVariableGetter {
+class MockEnvVarGetter : public base::EnvVarGetter {
  public:
-  MockEnvironmentVariableGetter() {
+  MockEnvVarGetter() {
 #define ENTRY(x) table.settings[#x] = &values.x
     ENTRY(DESKTOP_SESSION);
-    ENTRY(KDE_HOME);
+    ENTRY(HOME);
+    ENTRY(KDEHOME);
+    ENTRY(KDE_SESSION_VERSION);
     ENTRY(auto_proxy);
     ENTRY(all_proxy);
     ENTRY(http_proxy);
@@ -101,7 +104,7 @@
     values = zero_values;
   }
 
-  virtual bool Getenv(const char* variable_name, std::string* result) {
+  virtual bool GetEnv(const char* variable_name, std::string* result) {
     const char* env_value = table.Get(variable_name);
     if (env_value) {
       // Note that the variable may be defined but empty.
@@ -111,6 +114,11 @@
     return false;
   }
 
+  virtual bool SetEnv(const char* variable_name, const std::string& new_value) {
+    NOTIMPLEMENTED();
+    return false;
+  }
+
   // Intentionally public, for convenience when setting up a test.
   EnvVarValues values;
 
@@ -210,6 +218,14 @@
     return !result->empty();
   }
 
+  virtual bool BypassListIsReversed() {
+    return false;
+  }
+
+  virtual bool MatchHostsUsingSuffixMatching() {
+    return false;
+  }
+
   // Intentionally public, for convenience when setting up a test.
   GConfValues values;
 
@@ -309,11 +325,7 @@
   int get_config_result_;  // Return value from GetProxyConfig().
 };
 
-template <>
-struct RunnableMethodTraits<SynchConfigGetter> {
-  void RetainCallee(SynchConfigGetter*) {}
-  void ReleaseCallee(SynchConfigGetter*) {}
-};
+DISABLE_RUNNABLE_METHOD_REFCOUNT(SynchConfigGetter);
 
 namespace net {
 
@@ -325,23 +337,34 @@
   virtual void SetUp() {
     PlatformTest::SetUp();
     // Set up a temporary KDE home directory.
-    std::string prefix("ProxyConfigServiceLinuxTest_kde_home");
-    file_util::CreateNewTempDirectory(prefix, &kde_home_);
+    std::string prefix("ProxyConfigServiceLinuxTest_user_home");
+    file_util::CreateNewTempDirectory(prefix, &user_home_);
+    kde_home_ = user_home_.Append(FILE_PATH_LITERAL(".kde"));
     FilePath path = kde_home_.Append(FILE_PATH_LITERAL("share"));
-    file_util::CreateDirectory(path);
     path = path.Append(FILE_PATH_LITERAL("config"));
     file_util::CreateDirectory(path);
     kioslaverc_ = path.Append(FILE_PATH_LITERAL("kioslaverc"));
+    // Set up paths but do not create the directory for .kde4.
+    kde4_home_ = user_home_.Append(FILE_PATH_LITERAL(".kde4"));
+    path = kde4_home_.Append(FILE_PATH_LITERAL("share"));
+    kde4_config_ = path.Append(FILE_PATH_LITERAL("config"));
+    kioslaverc4_ = kde4_config_.Append(FILE_PATH_LITERAL("kioslaverc"));
   }
 
   virtual void TearDown() {
     // Delete the temporary KDE home directory.
-    file_util::Delete(kde_home_, true);
+    file_util::Delete(user_home_, true);
     PlatformTest::TearDown();
   }
 
+  FilePath user_home_;
+  // KDE3 paths.
   FilePath kde_home_;
   FilePath kioslaverc_;
+  // KDE4 paths.
+  FilePath kde4_home_;
+  FilePath kde4_config_;
+  FilePath kioslaverc4_;
 };
 
 // Builds an identifier for each test in an array.
@@ -365,9 +388,7 @@
     // Expected outputs (fields of the ProxyConfig).
     bool auto_detect;
     GURL pac_url;
-    ProxyConfig::ProxyRules proxy_rules;
-    const char* proxy_bypass_list;  // newline separated
-    bool bypass_local_names;
+    ProxyRulesExpectation proxy_rules;
   } tests[] = {
     {
       TEST_DESC("No proxying"),
@@ -383,9 +404,7 @@
       // Expected result.
       false,                      // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "",                         // proxy_bypass_list
-      false,                      // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
@@ -402,13 +421,11 @@
       // Expected result.
       true,                       // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "",                         // proxy_bypass_list
-      false,                      // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
-      TEST_DESC("Valid PAC url"),
+      TEST_DESC("Valid PAC URL"),
       { // Input.
         "auto",                      // mode
         "http://wpad/wpad.dat",      // autoconfig_url
@@ -421,13 +438,11 @@
       // Expected result.
       false,                         // auto_detect
       GURL("http://wpad/wpad.dat"),  // pac_url
-      ProxyConfig::ProxyRules(),     // proxy_rules
-      "",                            // proxy_bypass_list
-      false,                         // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
-      TEST_DESC("Invalid PAC url"),
+      TEST_DESC("Invalid PAC URL"),
       { // Input.
         "auto",                      // mode
         "wpad.dat",                  // autoconfig_url
@@ -440,9 +455,7 @@
       // Expected result.
       false,                         // auto_detect
       GURL(),                        // pac_url
-      ProxyConfig::ProxyRules(),     // proxy_rules
-      "",                            // proxy_bypass_list
-      false,                         // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
@@ -459,9 +472,9 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeSingleProxyRules("www.google.com"),  // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "www.google.com:80",  // single proxy
+          ""),                  // bypass rules
     },
 
     {
@@ -478,9 +491,7 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      ProxyConfig::ProxyRules(),               // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
@@ -497,10 +508,11 @@
       // Expected result.
       false,                                          // auto_detect
       GURL(),                                         // pac_url
-      MakeProxyPerSchemeRules("www.google.com",       // proxy_rules
-                              "", ""),
-      "",                                             // proxy_bypass_list
-      false,                                          // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "",                   // https
+          "",                   // ftp
+          ""),                  // bypass rules
     },
 
     {
@@ -517,9 +529,9 @@
       // Expected result.
       false,                                          // auto_detect
       GURL(),                                         // pac_url
-      MakeSingleProxyRules("www.google.com:88"),      // proxy_rules
-      "",                                             // proxy_bypass_list
-      false,                                          // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "www.google.com:88",  // single proxy
+          ""),                  // bypass rules
     },
 
     {
@@ -539,11 +551,11 @@
       // Expected result.
       false,                                          // auto_detect
       GURL(),                                         // pac_url
-      MakeProxyPerSchemeRules("www.google.com:88",    // proxy_rules
-                              "www.foo.com:110",
-                              "ftp.foo.com:121"),
-      "",                                             // proxy_bypass_list
-      false,                                          // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:88",  // http
+          "www.foo.com:110",    // https
+          "ftp.foo.com:121",    // ftp
+          ""),                  // bypass rules
     },
 
     {
@@ -560,9 +572,9 @@
       // Expected result.
       false,                                          // auto_detect
       GURL(),                                         // pac_url
-      MakeSingleProxyRules("socks4://socks.com:99"),  // proxy_rules
-      "",                                             // proxy_bypass_list
-      false,                                          // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "socks4://socks.com:99",  // single proxy
+          "")                       // bypass rules
     },
 
     {
@@ -578,17 +590,16 @@
 
       false,                                          // auto_detect
       GURL(),                                         // pac_url
-      MakeSingleProxyRules("www.google.com"),         // proxy_rules
-      "*.google.com\n",                               // proxy_bypass_list
-      false,                                          // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "www.google.com:80",   // single proxy
+          "*.google.com"),       // bypass rules
     },
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
     SCOPED_TRACE(StringPrintf("Test[%" PRIuS "] %s", i,
                               tests[i].description.c_str()));
-    MockEnvironmentVariableGetter* env_getter =
-        new MockEnvironmentVariableGetter;
+    MockEnvVarGetter* env_getter = new MockEnvVarGetter;
     MockGConfSettingGetter* gconf_getter = new MockGConfSettingGetter;
     SynchConfigGetter sync_config_getter(
         new ProxyConfigServiceLinux(env_getter, gconf_getter));
@@ -597,12 +608,9 @@
     sync_config_getter.SetupAndInitialFetch();
     sync_config_getter.SyncGetProxyConfig(&config);
 
-    EXPECT_EQ(tests[i].auto_detect, config.auto_detect);
-    EXPECT_EQ(tests[i].pac_url, config.pac_url);
-    EXPECT_EQ(tests[i].proxy_bypass_list,
-              FlattenProxyBypass(config.proxy_bypass));
-    EXPECT_EQ(tests[i].bypass_local_names, config.proxy_bypass_local_names);
-    EXPECT_EQ(tests[i].proxy_rules, config.proxy_rules);
+    EXPECT_EQ(tests[i].auto_detect, config.auto_detect());
+    EXPECT_EQ(tests[i].pac_url, config.pac_url());
+    EXPECT_TRUE(tests[i].proxy_rules.Matches(config.proxy_rules()));
   }
 }
 
@@ -618,15 +626,15 @@
     // Expected outputs (fields of the ProxyConfig).
     bool auto_detect;
     GURL pac_url;
-    ProxyConfig::ProxyRules proxy_rules;
-    const char* proxy_bypass_list;  // newline separated
-    bool bypass_local_names;
+    ProxyRulesExpectation proxy_rules;
   } tests[] = {
     {
       TEST_DESC("No proxying"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         NULL,  // auto_proxy
         NULL,  // all_proxy
         NULL, NULL, NULL,  // per-proto proxies
@@ -637,16 +645,16 @@
       // Expected result.
       false,                      // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "",                         // proxy_bypass_list
-      false,                      // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
       TEST_DESC("Auto detect"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         "",    // auto_proxy
         NULL,  // all_proxy
         NULL, NULL, NULL,  // per-proto proxies
@@ -657,16 +665,16 @@
       // Expected result.
       true,                       // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "",                         // proxy_bypass_list
-      false,                      // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
-      TEST_DESC("Valid PAC url"),
+      TEST_DESC("Valid PAC URL"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         "http://wpad/wpad.dat",  // auto_proxy
         NULL,  // all_proxy
         NULL, NULL, NULL,  // per-proto proxies
@@ -677,16 +685,16 @@
       // Expected result.
       false,                         // auto_detect
       GURL("http://wpad/wpad.dat"),  // pac_url
-      ProxyConfig::ProxyRules(),     // proxy_rules
-      "",                            // proxy_bypass_list
-      false,                         // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
-      TEST_DESC("Invalid PAC url"),
+      TEST_DESC("Invalid PAC URL"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         "wpad.dat",  // auto_proxy
         NULL,  // all_proxy
         NULL, NULL, NULL,  // per-proto proxies
@@ -697,16 +705,16 @@
       // Expected result.
       false,                       // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "",                         // proxy_bypass_list
-      false,                      // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
       TEST_DESC("Single-host in proxy list"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         NULL,  // auto_proxy
         "www.google.com",  // all_proxy
         NULL, NULL, NULL,  // per-proto proxies
@@ -717,16 +725,18 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeSingleProxyRules("www.google.com"),  // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "www.google.com:80",  // single proxy
+          ""),                  // bypass rules
     },
 
     {
       TEST_DESC("Single-host, different port"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         NULL,  // auto_proxy
         "www.google.com:99",  // all_proxy
         NULL, NULL, NULL,  // per-proto proxies
@@ -737,16 +747,18 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeSingleProxyRules("www.google.com:99"),  // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "www.google.com:99",  // single
+          ""),                  // bypass rules
     },
 
     {
       TEST_DESC("Tolerate a scheme"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         NULL,  // auto_proxy
         "http://www.google.com:99",  // all_proxy
         NULL, NULL, NULL,  // per-proto proxies
@@ -757,16 +769,18 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeSingleProxyRules("www.google.com:99"),  // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "www.google.com:99",  // single proxy
+          ""),                  // bypass rules
     },
 
     {
       TEST_DESC("Per-scheme proxy rules"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         NULL,  // auto_proxy
         NULL,  // all_proxy
         "www.google.com:80", "www.foo.com:110", "ftp.foo.com:121",  // per-proto
@@ -777,17 +791,20 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com", "www.foo.com:110",
-                              "ftp.foo.com:121"),
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "www.foo.com:110",    // https
+          "ftp.foo.com:121",    // ftp
+          ""),                  // bypass rules
     },
 
     {
       TEST_DESC("socks"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         NULL,  // auto_proxy
         "",  // all_proxy
         NULL, NULL, NULL,  // per-proto proxies
@@ -798,16 +815,18 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeSingleProxyRules("socks4://socks.com:888"),  // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "socks4://socks.com:888",  // single proxy
+          ""),                       // bypass rules
     },
 
     {
       TEST_DESC("socks5"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         NULL,  // auto_proxy
         "",  // all_proxy
         NULL, NULL, NULL,  // per-proto proxies
@@ -818,16 +837,18 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeSingleProxyRules("socks5://socks.com:888"),  // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "socks5://socks.com:888",  // single proxy
+          ""),                       // bypass rules
     },
 
     {
       TEST_DESC("socks default port"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         NULL,  // auto_proxy
         "",  // all_proxy
         NULL, NULL, NULL,  // per-proto proxies
@@ -838,16 +859,18 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeSingleProxyRules("socks4://socks.com"),  // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "socks4://socks.com:1080",  // single proxy
+          ""),                        // bypass rules
     },
 
     {
       TEST_DESC("bypass"),
       { // Input.
         NULL,  // DESKTOP_SESSION
-        NULL,  // KDE_HOME
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
         NULL,  // auto_proxy
         "www.google.com",  // all_proxy
         NULL, NULL, NULL,  // per-proto
@@ -857,18 +880,16 @@
 
       false,                      // auto_detect
       GURL(),                     // pac_url
-      MakeSingleProxyRules("www.google.com"),  // proxy_rules
-      // proxy_bypass_list
-      "*.google.com\n*foo.com:99\n1.2.3.4:22\n127.0.0.1/8\n",
-      false,                        // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "www.google.com:80",
+          "*.google.com,*foo.com:99,1.2.3.4:22,127.0.0.1/8"),
     },
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
     SCOPED_TRACE(StringPrintf("Test[%" PRIuS "] %s", i,
                               tests[i].description.c_str()));
-    MockEnvironmentVariableGetter* env_getter =
-        new MockEnvironmentVariableGetter;
+    MockEnvVarGetter* env_getter = new MockEnvVarGetter;
     MockGConfSettingGetter* gconf_getter = new MockGConfSettingGetter;
     SynchConfigGetter sync_config_getter(
         new ProxyConfigServiceLinux(env_getter, gconf_getter));
@@ -877,18 +898,14 @@
     sync_config_getter.SetupAndInitialFetch();
     sync_config_getter.SyncGetProxyConfig(&config);
 
-    EXPECT_EQ(tests[i].auto_detect, config.auto_detect);
-    EXPECT_EQ(tests[i].pac_url, config.pac_url);
-    EXPECT_EQ(tests[i].proxy_bypass_list,
-              FlattenProxyBypass(config.proxy_bypass));
-    EXPECT_EQ(tests[i].bypass_local_names, config.proxy_bypass_local_names);
-    EXPECT_EQ(tests[i].proxy_rules, config.proxy_rules);
+    EXPECT_EQ(tests[i].auto_detect, config.auto_detect());
+    EXPECT_EQ(tests[i].pac_url, config.pac_url());
+    EXPECT_TRUE(tests[i].proxy_rules.Matches(config.proxy_rules()));
   }
 }
 
 TEST_F(ProxyConfigServiceLinuxTest, GconfNotification) {
-  MockEnvironmentVariableGetter* env_getter =
-      new MockEnvironmentVariableGetter;
+  MockEnvVarGetter* env_getter = new MockEnvVarGetter;
   MockGConfSettingGetter* gconf_getter = new MockGConfSettingGetter;
   ProxyConfigServiceLinux* service =
       new ProxyConfigServiceLinux(env_getter, gconf_getter);
@@ -899,14 +916,14 @@
   gconf_getter->values.mode = "none";
   sync_config_getter.SetupAndInitialFetch();
   sync_config_getter.SyncGetProxyConfig(&config);
-  EXPECT_FALSE(config.auto_detect);
+  EXPECT_FALSE(config.auto_detect());
 
   // Now set to auto-detect.
   gconf_getter->values.mode = "auto";
   // Simulate gconf notification callback.
   service->OnCheckProxyConfigSettings();
   sync_config_getter.SyncGetProxyConfig(&config);
-  EXPECT_TRUE(config.auto_detect);
+  EXPECT_TRUE(config.auto_detect());
 }
 
 TEST_F(ProxyConfigServiceLinuxTest, KDEConfigParser) {
@@ -924,26 +941,24 @@
 
     // Input.
     std::string kioslaverc;
+    EnvVarValues env_values;
 
     // Expected outputs (fields of the ProxyConfig).
     bool auto_detect;
     GURL pac_url;
-    ProxyConfig::ProxyRules proxy_rules;
-    const char* proxy_bypass_list;  // newline separated
-    bool bypass_local_names;
+    ProxyRulesExpectation proxy_rules;
   } tests[] = {
     {
       TEST_DESC("No proxying"),
 
       // Input.
       "[Proxy Settings]\nProxyType=0\n",
+      {},                                      // env_values
 
       // Expected result.
       false,                      // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "",                         // proxy_bypass_list
-      false,                      // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
@@ -951,28 +966,26 @@
 
       // Input.
       "[Proxy Settings]\nProxyType=3\n",
+      {},                                      // env_values
 
       // Expected result.
       true,                       // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "",                         // proxy_bypass_list
-      false,                      // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
-      TEST_DESC("Valid PAC url"),
+      TEST_DESC("Valid PAC URL"),
 
       // Input.
       "[Proxy Settings]\nProxyType=2\n"
           "Proxy Config Script=http://wpad/wpad.dat\n",
+      {},                                      // env_values
 
       // Expected result.
       false,                         // auto_detect
       GURL("http://wpad/wpad.dat"),  // pac_url
-      ProxyConfig::ProxyRules(),     // proxy_rules
-      "",                            // proxy_bypass_list
-      false,                         // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
@@ -981,15 +994,16 @@
       // Input.
       "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
           "httpsProxy=www.foo.com\nftpProxy=ftp.foo.com\n",
+      {},                                      // env_values
 
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "www.foo.com",
-                              "ftp.foo.com"),  // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "www.foo.com:80",     // https
+          "ftp.foo.com:80",     // http
+          ""),                  // bypass rules
     },
 
     {
@@ -998,14 +1012,16 @@
       // Input.
       "[Proxy Settings]\nProxyType=1\n"
           "httpProxy=www.google.com\n",
+      {},                                      // env_values
 
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", ""),         // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "",                   // https
+          "",                   // ftp
+          ""),                  // bypass rules
     },
 
     {
@@ -1014,14 +1030,16 @@
       // Input.
       "[Proxy Settings]\nProxyType=1\n"
           "httpProxy=www.google.com:88\n",
+      {},                                      // env_values
 
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com:88",
-                              "", ""),         // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:88",  // http
+          "",                   // https
+          "",                   // ftp
+          ""),                  // bypass rules
     },
 
     {
@@ -1029,14 +1047,16 @@
 
       // Input.
       "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
-          "NoProxyFor=*.google.com\n",
+          "NoProxyFor=.google.com\n",
+      {},                                      // env_values
 
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", ""),         // proxy_rules
-      "*.google.com\n",                        // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "",                   // https
+          "",                   // ftp
+          "*.google.com"),      // bypass rules
     },
 
     {
@@ -1044,29 +1064,50 @@
 
       // Input.
       "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
-          "NoProxyFor=*.google.com,*.kde.org\n",
+          "NoProxyFor=.google.com,.kde.org\n",
+      {},                                      // env_values
 
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", ""),         // proxy_rules
-      "*.google.com\n*.kde.org\n",             // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",           // http
+          "",                            // https
+          "",                            // ftp
+          "*.google.com,*.kde.org"),     // bypass rules
     },
 
     {
-      TEST_DESC("Ignore bypass list with ReversedException"),
+      TEST_DESC("Correctly parse bypass list with ReversedException"),
 
       // Input.
       "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
-          "NoProxyFor=*.google.com\nReversedException=true\n",
+          "NoProxyFor=.google.com\nReversedException=true\n",
+      {},                                      // env_values
 
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", ""),         // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerSchemeWithBypassReversed(
+          "www.google.com:80",  // http
+          "",                   // https
+          "",                   // ftp
+          "*.google.com"),      // bypass rules
+    },
+
+    {
+      TEST_DESC("Treat all hostname patterns as wildcard patterns"),
+
+      // Input.
+      "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
+          "NoProxyFor=google.com,kde.org,<local>\n",
+      {},                                      // env_values
+
+      false,                                   // auto_detect
+      GURL(),                                  // pac_url
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",              // http
+          "",                               // https
+          "",                               // ftp
+          "*google.com,*kde.org,<local>"),  // bypass rules
     },
 
     {
@@ -1074,14 +1115,16 @@
 
       // Input.
       "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
-          "NoProxyFor=*.google.com\nReversedException=true  \n",
+          "NoProxyFor=.google.com\nReversedException=true  \n",
+      {},                                      // env_values
 
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", ""),         // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerSchemeWithBypassReversed(
+          "www.google.com:80",  // http
+          "",                   // https
+          "",                   // ftp
+          "*.google.com"),      // bypass rules
     },
 
     {
@@ -1090,13 +1133,15 @@
       // Input.
       "httpsProxy=www.foo.com\n[Proxy Settings]\nProxyType=1\n"
           "httpProxy=www.google.com\n[Other Section]\nftpProxy=ftp.foo.com\n",
+      {},                                      // env_values
 
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", ""),         // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "",                   // https
+          "",                   // ftp
+          ""),                  // bypass rules
     },
 
     {
@@ -1104,13 +1149,15 @@
 
       // Input.
       "[Proxy Settings]\r\nProxyType=1\r\nhttpProxy=www.google.com\r\n",
+      {},                                      // env_values
 
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", ""),         // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "",                   // https
+          "",                   // ftp
+          ""),                  // bypass rules
     },
 
     {
@@ -1118,13 +1165,15 @@
 
       // Input.
       "[Proxy Settings]\r\n\nProxyType=1\n\r\nhttpProxy=www.google.com\n\n",
+      {},                                      // env_values
 
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", ""),         // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "",                   // https
+          "",                   // ftp
+          ""),                  // bypass rules
     },
 
     {
@@ -1132,13 +1181,15 @@
 
       // Input.
       "[Proxy Settings]\nProxyType[$e]=1\nhttpProxy[$e]=www.google.com\n",
+      {},                                      // env_values
 
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", ""),         // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "",                   // https
+          "",                   // ftp
+          ""),                  // bypass rules
     },
 
     {
@@ -1147,13 +1198,15 @@
       // Input.
       "[Proxy Settings]\nProxyType=1\nhttpProxy=www.google.com\n"
           "httpsProxy$e]=www.foo.com\nftpProxy=ftp.foo.com\n",
+      {},                                      // env_values
 
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", "ftp.foo.com"),  // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "",                   // https
+          "ftp.foo.com:80",     // ftp
+          ""),                  // bypass rules
     },
 
     {
@@ -1162,12 +1215,11 @@
       // Input.
       "[Proxy Settings]\nProxyType [$e] =2\n"
           "  Proxy Config Script =  http:// foo\n",
+      {},                                      // env_values
 
       false,                                   // auto_detect
       GURL("http:// foo"),                     // pac_url
-      ProxyConfig::ProxyRules(),               // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     {
@@ -1176,24 +1228,69 @@
       // Input.
       std::string("[Proxy Settings]\nProxyType=1\nftpProxy=ftp.foo.com\n") +
           long_line + "httpsProxy=www.foo.com\nhttpProxy=www.google.com\n",
+      {},                                          // env_values
 
       false,                                       // auto_detect
       GURL(),                                      // pac_url
-      MakeProxyPerSchemeRules("www.google.com",
-                              "", "ftp.foo.com"),  // proxy_rules
-      "",                                          // proxy_bypass_list
-      false,                                       // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "",                   // https
+          "ftp.foo.com:80",     // ftp
+          ""),                  // bypass rules
     },
+
+    {
+      TEST_DESC("Indirect Proxy - no env vars set"),
+
+      // Input.
+      "[Proxy Settings]\nProxyType=4\nhttpProxy=http_proxy\n"
+          "httpsProxy=https_proxy\nftpProxy=ftp_proxy\nNoProxyFor=no_proxy\n",
+      {},                                      // env_values
+
+      false,                                   // auto_detect
+      GURL(),                                  // pac_url
+      ProxyRulesExpectation::Empty(),
+    },
+
+    {
+      TEST_DESC("Indirect Proxy - with env vars set"),
+
+      // Input.
+      "[Proxy Settings]\nProxyType=4\nhttpProxy=http_proxy\n"
+          "httpsProxy=https_proxy\nftpProxy=ftp_proxy\nNoProxyFor=no_proxy\n",
+      {  // env_values
+        NULL,  // DESKTOP_SESSION
+        NULL,  // HOME
+        NULL,  // KDEHOME
+        NULL,  // KDE_SESSION_VERSION
+        NULL,  // auto_proxy
+        NULL,  // all_proxy
+        "www.normal.com",  // http_proxy
+        "www.secure.com",  // https_proxy
+        "ftp.foo.com",  // ftp_proxy
+        NULL, NULL,  // SOCKS
+        ".google.com, .kde.org",  // no_proxy
+      },
+
+      false,                                   // auto_detect
+      GURL(),                                  // pac_url
+      ProxyRulesExpectation::PerScheme(
+          "www.normal.com:80",           // http
+          "www.secure.com:80",           // https
+          "ftp.foo.com:80",              // ftp
+          "*.google.com,*.kde.org"),     // bypass rules
+    },
+
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
     SCOPED_TRACE(StringPrintf("Test[%" PRIuS "] %s", i,
                               tests[i].description.c_str()));
-    MockEnvironmentVariableGetter* env_getter =
-        new MockEnvironmentVariableGetter;
+    MockEnvVarGetter* env_getter = new MockEnvVarGetter;
+    env_getter->values = tests[i].env_values;
     // Force the KDE getter to be used and tell it where the test is.
     env_getter->values.DESKTOP_SESSION = "kde4";
-    env_getter->values.KDE_HOME = kde_home_.value().c_str();
+    env_getter->values.KDEHOME = kde_home_.value().c_str();
     SynchConfigGetter sync_config_getter(
         new ProxyConfigServiceLinux(env_getter));
     ProxyConfig config;
@@ -1203,12 +1300,101 @@
     sync_config_getter.SetupAndInitialFetch();
     sync_config_getter.SyncGetProxyConfig(&config);
 
-    EXPECT_EQ(tests[i].auto_detect, config.auto_detect);
-    EXPECT_EQ(tests[i].pac_url, config.pac_url);
-    EXPECT_EQ(tests[i].proxy_bypass_list,
-              FlattenProxyBypass(config.proxy_bypass));
-    EXPECT_EQ(tests[i].bypass_local_names, config.proxy_bypass_local_names);
-    EXPECT_EQ(tests[i].proxy_rules, config.proxy_rules);
+    EXPECT_EQ(tests[i].auto_detect, config.auto_detect());
+    EXPECT_EQ(tests[i].pac_url, config.pac_url());
+    EXPECT_TRUE(tests[i].proxy_rules.Matches(config.proxy_rules()));
+  }
+}
+
+TEST_F(ProxyConfigServiceLinuxTest, KDEHomePicker) {
+  // Auto detect proxy settings.
+  std::string slaverc3 = "[Proxy Settings]\nProxyType=3\n";
+  // Valid PAC URL.
+  std::string slaverc4 = "[Proxy Settings]\nProxyType=2\n"
+                             "Proxy Config Script=http://wpad/wpad.dat\n";
+  GURL slaverc4_pac_url("http://wpad/wpad.dat");
+
+  // Overwrite the .kde kioslaverc file.
+  file_util::WriteFile(kioslaverc_, slaverc3.c_str(), slaverc3.length());
+
+  // If .kde4 exists it will mess up the first test. It should not, as
+  // we created the directory for $HOME in the test setup.
+  CHECK(!file_util::DirectoryExists(kde4_home_));
+
+  { SCOPED_TRACE("KDE4, no .kde4 directory, verify fallback");
+    MockEnvVarGetter* env_getter = new MockEnvVarGetter;
+    env_getter->values.DESKTOP_SESSION = "kde4";
+    env_getter->values.HOME = user_home_.value().c_str();
+    SynchConfigGetter sync_config_getter(
+        new ProxyConfigServiceLinux(env_getter));
+    ProxyConfig config;
+    sync_config_getter.SetupAndInitialFetch();
+    sync_config_getter.SyncGetProxyConfig(&config);
+    EXPECT_TRUE(config.auto_detect());
+    EXPECT_EQ(GURL(), config.pac_url());
+  }
+
+  // Now create .kde4 and put a kioslaverc in the config directory.
+  // Note that its timestamp will be at least as new as the .kde one.
+  file_util::CreateDirectory(kde4_config_);
+  file_util::WriteFile(kioslaverc4_, slaverc4.c_str(), slaverc4.length());
+  CHECK(file_util::PathExists(kioslaverc4_));
+
+  { SCOPED_TRACE("KDE4, .kde4 directory present, use it");
+    MockEnvVarGetter* env_getter = new MockEnvVarGetter;
+    env_getter->values.DESKTOP_SESSION = "kde4";
+    env_getter->values.HOME = user_home_.value().c_str();
+    SynchConfigGetter sync_config_getter(
+        new ProxyConfigServiceLinux(env_getter));
+    ProxyConfig config;
+    sync_config_getter.SetupAndInitialFetch();
+    sync_config_getter.SyncGetProxyConfig(&config);
+    EXPECT_FALSE(config.auto_detect());
+    EXPECT_EQ(slaverc4_pac_url, config.pac_url());
+  }
+
+  { SCOPED_TRACE("KDE3, .kde4 directory present, ignore it");
+    MockEnvVarGetter* env_getter = new MockEnvVarGetter;
+    env_getter->values.DESKTOP_SESSION = "kde";
+    env_getter->values.HOME = user_home_.value().c_str();
+    SynchConfigGetter sync_config_getter(
+        new ProxyConfigServiceLinux(env_getter));
+    ProxyConfig config;
+    sync_config_getter.SetupAndInitialFetch();
+    sync_config_getter.SyncGetProxyConfig(&config);
+    EXPECT_TRUE(config.auto_detect());
+    EXPECT_EQ(GURL(), config.pac_url());
+  }
+
+  { SCOPED_TRACE("KDE4, .kde4 directory present, KDEHOME set to .kde");
+    MockEnvVarGetter* env_getter = new MockEnvVarGetter;
+    env_getter->values.DESKTOP_SESSION = "kde4";
+    env_getter->values.HOME = user_home_.value().c_str();
+    env_getter->values.KDEHOME = kde_home_.value().c_str();
+    SynchConfigGetter sync_config_getter(
+        new ProxyConfigServiceLinux(env_getter));
+    ProxyConfig config;
+    sync_config_getter.SetupAndInitialFetch();
+    sync_config_getter.SyncGetProxyConfig(&config);
+    EXPECT_TRUE(config.auto_detect());
+    EXPECT_EQ(GURL(), config.pac_url());
+  }
+
+  // Finally, make the .kde4 config directory older than the .kde directory
+  // and make sure we then use .kde instead of .kde4 since it's newer.
+  file_util::SetLastModifiedTime(kde4_config_, base::Time());
+
+  { SCOPED_TRACE("KDE4, very old .kde4 directory present, use .kde");
+    MockEnvVarGetter* env_getter = new MockEnvVarGetter;
+    env_getter->values.DESKTOP_SESSION = "kde4";
+    env_getter->values.HOME = user_home_.value().c_str();
+    SynchConfigGetter sync_config_getter(
+        new ProxyConfigServiceLinux(env_getter));
+    ProxyConfig config;
+    sync_config_getter.SetupAndInitialFetch();
+    sync_config_getter.SyncGetProxyConfig(&config);
+    EXPECT_TRUE(config.auto_detect());
+    EXPECT_EQ(GURL(), config.pac_url());
   }
 }
 
diff --git a/net/proxy/proxy_config_service_mac.cc b/net/proxy/proxy_config_service_mac.cc
index 3359cc3..1e04ff0 100644
--- a/net/proxy/proxy_config_service_mac.cc
+++ b/net/proxy/proxy_config_service_mac.cc
@@ -49,10 +49,10 @@
   // There appears to be no UI for this configuration option, and we're not sure
   // if Apple's proxy code even takes it into account. But the constant is in
   // the header file so we'll use it.
-  config->auto_detect =
+  config->set_auto_detect(
       GetBoolFromDictionary(config_dict.get(),
                             kSCPropNetProxiesProxyAutoDiscoveryEnable,
-                            false);
+                            false));
 
   // PAC file
 
@@ -64,7 +64,7 @@
         kSCPropNetProxiesProxyAutoConfigURLString,
         CFStringGetTypeID());
     if (pac_url_ref)
-      config->pac_url = GURL(base::SysCFStringRefToUTF8(pac_url_ref));
+      config->set_pac_url(GURL(base::SysCFStringRefToUTF8(pac_url_ref)));
   }
 
   // proxies (for now ftp, http, https, and SOCKS)
@@ -78,8 +78,9 @@
                                     kSCPropNetProxiesFTPProxy,
                                     kSCPropNetProxiesFTPPort);
     if (proxy_server.is_valid()) {
-      config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
-      config->proxy_rules.proxy_for_ftp = proxy_server;
+      config->proxy_rules().type =
+          ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
+      config->proxy_rules().proxy_for_ftp = proxy_server;
     }
   }
   if (GetBoolFromDictionary(config_dict.get(),
@@ -91,8 +92,9 @@
                                     kSCPropNetProxiesHTTPProxy,
                                     kSCPropNetProxiesHTTPPort);
     if (proxy_server.is_valid()) {
-      config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
-      config->proxy_rules.proxy_for_http = proxy_server;
+      config->proxy_rules().type =
+          ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
+      config->proxy_rules().proxy_for_http = proxy_server;
     }
   }
   if (GetBoolFromDictionary(config_dict.get(),
@@ -104,8 +106,9 @@
                                     kSCPropNetProxiesHTTPSProxy,
                                     kSCPropNetProxiesHTTPSPort);
     if (proxy_server.is_valid()) {
-      config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
-      config->proxy_rules.proxy_for_https = proxy_server;
+      config->proxy_rules().type =
+          ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
+      config->proxy_rules().proxy_for_https = proxy_server;
     }
   }
   if (GetBoolFromDictionary(config_dict.get(),
@@ -117,8 +120,9 @@
                                     kSCPropNetProxiesSOCKSProxy,
                                     kSCPropNetProxiesSOCKSPort);
     if (proxy_server.is_valid()) {
-      config->proxy_rules.type = ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
-      config->proxy_rules.socks_proxy = proxy_server;
+      config->proxy_rules().type =
+          ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME;
+      config->proxy_rules().socks_proxy = proxy_server;
     }
   }
 
@@ -140,7 +144,7 @@
                         " to be a CFStringRef but it was not";
 
       } else {
-        config->proxy_bypass.push_back(
+        config->proxy_rules().bypass_rules.AddRuleFromString(
             base::SysCFStringRefToUTF8(bypass_item_ref));
       }
     }
@@ -148,10 +152,11 @@
 
   // proxy bypass boolean
 
-  config->proxy_bypass_local_names =
-      GetBoolFromDictionary(config_dict.get(),
+  if (GetBoolFromDictionary(config_dict.get(),
                             kSCPropNetProxiesExcludeSimpleHostnames,
-                            false);
+                            false)) {
+    config->proxy_rules().bypass_rules.AddRuleToBypassLocal();
+  }
 
   return OK;
 }
diff --git a/net/proxy/proxy_config_service_win.cc b/net/proxy/proxy_config_service_win.cc
index 30c4adb..38819de 100644
--- a/net/proxy/proxy_config_service_win.cc
+++ b/net/proxy/proxy_config_service_win.cc
@@ -43,11 +43,11 @@
     ProxyConfig* config,
     const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG& ie_config) {
   if (ie_config.fAutoDetect)
-    config->auto_detect = true;
+    config->set_auto_detect(true);
   if (ie_config.lpszProxy) {
     // lpszProxy may be a single proxy, or a proxy per scheme. The format
     // is compatible with ProxyConfig::ProxyRules's string format.
-    config->proxy_rules.ParseFromString(WideToASCII(ie_config.lpszProxy));
+    config->proxy_rules().ParseFromString(WideToASCII(ie_config.lpszProxy));
   }
   if (ie_config.lpszProxyBypass) {
     std::string proxy_bypass = WideToASCII(ie_config.lpszProxyBypass);
@@ -55,14 +55,11 @@
     StringTokenizer proxy_server_bypass_list(proxy_bypass, "; \t\n\r");
     while (proxy_server_bypass_list.GetNext()) {
       std::string bypass_url_domain = proxy_server_bypass_list.token();
-      if (bypass_url_domain == "<local>")
-        config->proxy_bypass_local_names = true;
-      else
-        config->proxy_bypass.push_back(bypass_url_domain);
+      config->proxy_rules().bypass_rules.AddRuleFromString(bypass_url_domain);
     }
   }
   if (ie_config.lpszAutoConfigUrl)
-    config->pac_url = GURL(ie_config.lpszAutoConfigUrl);
+    config->set_pac_url(GURL(ie_config.lpszAutoConfigUrl));
 }
 
 }  // namespace net
diff --git a/net/proxy/proxy_config_service_win_unittest.cc b/net/proxy/proxy_config_service_win_unittest.cc
index 2b0ff66..1d4f45d 100644
--- a/net/proxy/proxy_config_service_win_unittest.cc
+++ b/net/proxy/proxy_config_service_win_unittest.cc
@@ -19,9 +19,8 @@
     // Expected outputs (fields of the ProxyConfig).
     bool auto_detect;
     GURL pac_url;
-    ProxyConfig::ProxyRules proxy_rules;
+    ProxyRulesExpectation proxy_rules;
     const char* proxy_bypass_list;  // newline separated
-    bool bypass_local_names;
   } tests[] = {
     // Auto detect.
     {
@@ -35,9 +34,7 @@
       // Expected result.
       true,                       // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "",                         // proxy_bypass_list
-      false,                      // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     // Valid PAC url
@@ -52,9 +49,7 @@
       // Expected result.
       false,                         // auto_detect
       GURL("http://wpad/wpad.dat"),  // pac_url
-      ProxyConfig::ProxyRules(),     // proxy_rules
-      "",                            // proxy_bypass_list
-      false,                         // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     // Invalid PAC url string.
@@ -69,9 +64,7 @@
       // Expected result.
       false,                      // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "",                         // proxy_bypass_list
-      false,                      // bypass_local_names
+      ProxyRulesExpectation::Empty(),
     },
 
     // Single-host in proxy list.
@@ -86,9 +79,9 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeSingleProxyRules("www.google.com"),  // proxy_rules
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::Single(
+          "www.google.com:80",  // single proxy
+          ""),                  // bypass rules
     },
 
     // Per-scheme proxy rules.
@@ -103,9 +96,11 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com:80", "www.foo.com:110", ""),
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerScheme(
+          "www.google.com:80",  // http
+          "www.foo.com:110",   // https
+          "",                  // ftp
+          ""),                 // bypass rules
     },
 
     // SOCKS proxy configuration
@@ -121,10 +116,12 @@
       // Expected result.
       false,                                   // auto_detect
       GURL(),                                  // pac_url
-      MakeProxyPerSchemeRules("www.google.com:80", "www.foo.com:110",
-                              "ftpproxy:20", "foopy:130"),
-      "",                                      // proxy_bypass_list
-      false,                                   // bypass_local_names
+      ProxyRulesExpectation::PerSchemeWithSocks(
+          "www.google.com:80",   // http
+          "www.foo.com:110",     // https
+          "ftpproxy:20",         // ftp
+          "socks4://foopy:130",  // socks
+          ""),                   // bypass rules
     },
 
     // Bypass local names.
@@ -138,9 +135,7 @@
 
       true,                       // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "",                         // proxy_bypass_list
-      true,                       // bypass_local_names
+      ProxyRulesExpectation::EmptyWithBypass("<local>"),
     },
 
     // Bypass "google.com" and local names, using semicolon as delimeter
@@ -156,9 +151,7 @@
       // Expected result.
       true,                       // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "google.com\n",             // proxy_bypass_list
-      true,                       // bypass_local_names
+      ProxyRulesExpectation::EmptyWithBypass("<local>,google.com"),
     },
 
     // Bypass "foo.com" and "google.com", using lines as delimeter.
@@ -173,9 +166,7 @@
       // Expected result.
       true,                       // auto_detect
       GURL(),                     // pac_url
-      ProxyConfig::ProxyRules(),  // proxy_rules
-      "foo.com\ngoogle.com\n",    // proxy_bypass_list
-      false,                      // bypass_local_names
+      ProxyRulesExpectation::EmptyWithBypass("foo.com,google.com"),
     },
   };
 
@@ -183,12 +174,9 @@
     ProxyConfig config;
     ProxyConfigServiceWin::SetFromIEConfig(&config, tests[i].ie_config);
 
-    EXPECT_EQ(tests[i].auto_detect, config.auto_detect);
-    EXPECT_EQ(tests[i].pac_url, config.pac_url);
-    EXPECT_EQ(tests[i].proxy_bypass_list,
-              FlattenProxyBypass(config.proxy_bypass));
-    EXPECT_EQ(tests[i].bypass_local_names, config.proxy_bypass_local_names);
-    EXPECT_EQ(tests[i].proxy_rules, config.proxy_rules);
+    EXPECT_EQ(tests[i].auto_detect, config.auto_detect());
+    EXPECT_EQ(tests[i].pac_url, config.pac_url());
+    EXPECT_TRUE(tests[i].proxy_rules.Matches(config.proxy_rules()));
   }
 }
 
diff --git a/net/proxy/proxy_config_unittest.cc b/net/proxy/proxy_config_unittest.cc
index 259f1fa..5806f30 100644
--- a/net/proxy/proxy_config_unittest.cc
+++ b/net/proxy/proxy_config_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "net/proxy/proxy_config.h"
 #include "net/proxy/proxy_config_service_common_unittest.h"
+#include "net/proxy/proxy_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
@@ -24,73 +25,73 @@
   // Test |ProxyConfig::auto_detect|.
 
   ProxyConfig config1;
-  config1.auto_detect = true;
+  config1.set_auto_detect(true);
 
   ProxyConfig config2;
-  config2.auto_detect = false;
+  config2.set_auto_detect(false);
 
   EXPECT_FALSE(config1.Equals(config2));
   EXPECT_FALSE(config2.Equals(config1));
 
-  config2.auto_detect = true;
+  config2.set_auto_detect(true);
 
   EXPECT_TRUE(config1.Equals(config2));
   EXPECT_TRUE(config2.Equals(config1));
 
   // Test |ProxyConfig::pac_url|.
 
-  config2.pac_url = GURL("http://wpad/wpad.dat");
+  config2.set_pac_url(GURL("http://wpad/wpad.dat"));
 
   EXPECT_FALSE(config1.Equals(config2));
   EXPECT_FALSE(config2.Equals(config1));
 
-  config1.pac_url = GURL("http://wpad/wpad.dat");
+  config1.set_pac_url(GURL("http://wpad/wpad.dat"));
 
   EXPECT_TRUE(config1.Equals(config2));
   EXPECT_TRUE(config2.Equals(config1));
 
   // Test |ProxyConfig::proxy_rules|.
 
-  config2.proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
-  config2.proxy_rules.single_proxy =
+  config2.proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
+  config2.proxy_rules().single_proxy =
       ProxyServer::FromURI("myproxy:80", ProxyServer::SCHEME_HTTP);
 
   EXPECT_FALSE(config1.Equals(config2));
   EXPECT_FALSE(config2.Equals(config1));
 
-  config1.proxy_rules.type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
-  config1.proxy_rules.single_proxy =
+  config1.proxy_rules().type = ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY;
+  config1.proxy_rules().single_proxy =
       ProxyServer::FromURI("myproxy:100", ProxyServer::SCHEME_HTTP);
 
   EXPECT_FALSE(config1.Equals(config2));
   EXPECT_FALSE(config2.Equals(config1));
 
-  config1.proxy_rules.single_proxy =
+  config1.proxy_rules().single_proxy =
       ProxyServer::FromURI("myproxy", ProxyServer::SCHEME_HTTP);
 
   EXPECT_TRUE(config1.Equals(config2));
   EXPECT_TRUE(config2.Equals(config1));
 
-  // Test |ProxyConfig::proxy_bypass|.
+  // Test |ProxyConfig::bypass_rules|.
 
-  config2.proxy_bypass.push_back("*.google.com");
+  config2.proxy_rules().bypass_rules.AddRuleFromString("*.google.com");
 
   EXPECT_FALSE(config1.Equals(config2));
   EXPECT_FALSE(config2.Equals(config1));
 
-  config1.proxy_bypass.push_back("*.google.com");
+  config1.proxy_rules().bypass_rules.AddRuleFromString("*.google.com");
 
   EXPECT_TRUE(config1.Equals(config2));
   EXPECT_TRUE(config2.Equals(config1));
 
-  // Test |ProxyConfig::proxy_bypass_local_names|.
+  // Test |ProxyConfig::proxy_rules.reverse_bypass|.
 
-  config1.proxy_bypass_local_names = true;
+  config2.proxy_rules().reverse_bypass = true;
 
   EXPECT_FALSE(config1.Equals(config2));
   EXPECT_FALSE(config2.Equals(config1));
 
-  config2.proxy_bypass_local_names = true;
+  config1.proxy_rules().reverse_bypass = true;
 
   EXPECT_TRUE(config1.Equals(config2));
   EXPECT_TRUE(config2.Equals(config1));
@@ -236,52 +237,19 @@
   ProxyConfig config;
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
-    config.proxy_rules.ParseFromString(tests[i].proxy_rules);
+    config.proxy_rules().ParseFromString(tests[i].proxy_rules);
 
-    EXPECT_EQ(tests[i].type, config.proxy_rules.type);
+    EXPECT_EQ(tests[i].type, config.proxy_rules().type);
     ExpectProxyServerEquals(tests[i].single_proxy,
-                            config.proxy_rules.single_proxy);
+                            config.proxy_rules().single_proxy);
     ExpectProxyServerEquals(tests[i].proxy_for_http,
-                            config.proxy_rules.proxy_for_http);
+                            config.proxy_rules().proxy_for_http);
     ExpectProxyServerEquals(tests[i].proxy_for_https,
-                            config.proxy_rules.proxy_for_https);
+                            config.proxy_rules().proxy_for_https);
     ExpectProxyServerEquals(tests[i].proxy_for_ftp,
-                            config.proxy_rules.proxy_for_ftp);
+                            config.proxy_rules().proxy_for_ftp);
     ExpectProxyServerEquals(tests[i].socks_proxy,
-                            config.proxy_rules.socks_proxy);
-  }
-}
-
-TEST(ProxyConfigTest, ParseProxyBypassList) {
-  struct bypass_test {
-    const char* proxy_bypass_input;
-    const char* flattened_output;
-  };
-
-  const struct {
-    const char* proxy_bypass_input;
-    const char* flattened_output;
-  } tests[] = {
-    {
-      "*",
-      "*\n"
-    },
-    {
-      ".google.com, .foo.com:42",
-      "*.google.com\n*.foo.com:42\n"
-    },
-    {
-      ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8",
-      "*.google.com\n*foo.com:99\n1.2.3.4:22\n127.0.0.1/8\n"
-    }
-  };
-
-  ProxyConfig config;
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
-    config.ParseNoProxyList(tests[i].proxy_bypass_input);
-    EXPECT_EQ(tests[i].flattened_output,
-              FlattenProxyBypass(config.proxy_bypass));
+                            config.proxy_rules().socks_proxy);
   }
 }
 
@@ -295,44 +263,42 @@
   // Manual proxy.
   {
     ProxyConfig config;
-    config.auto_detect = false;
-    config.proxy_rules.ParseFromString("http://single-proxy:81");
+    config.set_auto_detect(false);
+    config.proxy_rules().ParseFromString("http://single-proxy:81");
 
     EXPECT_EQ("Automatic settings:\n"
               "  Auto-detect: No\n"
               "  Custom PAC script: [None]\n"
               "Manual settings:\n"
               "  Proxy server: single-proxy:81\n"
-              "  Bypass list: [None]\n"
-              "  Bypass local names: No",
+              "  Bypass list: [None]",
               ProxyConfigToString(config));
   }
 
   // Autodetect + custom PAC + manual proxy.
   {
     ProxyConfig config;
-    config.auto_detect = true;
-    config.pac_url = GURL("http://custom/pac.js");
-    config.proxy_rules.ParseFromString("http://single-proxy:81");
+    config.set_auto_detect(true);
+    config.set_pac_url(GURL("http://custom/pac.js"));
+    config.proxy_rules().ParseFromString("http://single-proxy:81");
 
     EXPECT_EQ("Automatic settings:\n"
               "  Auto-detect: Yes\n"
               "  Custom PAC script: http://custom/pac.js\n"
               "Manual settings:\n"
               "  Proxy server: single-proxy:81\n"
-              "  Bypass list: [None]\n"
-              "  Bypass local names: No",
+              "  Bypass list: [None]",
               ProxyConfigToString(config));
   }
 
   // Manual proxy with bypass list + bypass local.
   {
     ProxyConfig config;
-    config.auto_detect = false;
-    config.proxy_rules.ParseFromString("http://single-proxy:81");
-    config.proxy_bypass.push_back("google.com");
-    config.proxy_bypass.push_back("bypass2.net:1730");
-    config.proxy_bypass_local_names = true;
+    config.set_auto_detect(false);
+    config.proxy_rules().ParseFromString("http://single-proxy:81");
+    config.proxy_rules().bypass_rules.AddRuleFromString("google.com");
+    config.proxy_rules().bypass_rules.AddRuleFromString("bypass2.net:1730");
+    config.proxy_rules().bypass_rules.AddRuleToBypassLocal();
 
     EXPECT_EQ("Automatic settings:\n"
               "  Auto-detect: No\n"
@@ -342,15 +308,15 @@
               "  Bypass list: \n"
               "    google.com\n"
               "    bypass2.net:1730\n"
-              "  Bypass local names: Yes",
+              "    <local>",
               ProxyConfigToString(config));
   }
 
   // Proxy-per scheme (HTTP and HTTPS)
   {
     ProxyConfig config;
-    config.auto_detect = false;
-    config.proxy_rules.ParseFromString(
+    config.set_auto_detect(false);
+    config.proxy_rules().ParseFromString(
         "http=proxy-for-http:1801; https=proxy-for-https:1802");
 
     EXPECT_EQ("Automatic settings:\n"
@@ -360,16 +326,15 @@
               "  Proxy server: \n"
               "    HTTP: proxy-for-http:1801\n"
               "    HTTPS: proxy-for-https:1802\n"
-              "  Bypass list: [None]\n"
-              "  Bypass local names: No",
+              "  Bypass list: [None]",
               ProxyConfigToString(config));
   }
 
   // Proxy-per scheme (HTTP and SOCKS)
   {
     ProxyConfig config;
-    config.auto_detect = false;
-    config.proxy_rules.ParseFromString(
+    config.set_auto_detect(false);
+    config.proxy_rules().ParseFromString(
         "http=http://proxy-for-http:1801; socks=socks-server:6083");
 
     EXPECT_EQ("Automatic settings:\n"
@@ -379,23 +344,43 @@
               "  Proxy server: \n"
               "    HTTP: proxy-for-http:1801\n"
               "    SOCKS: socks4://socks-server:6083\n"
-              "  Bypass list: [None]\n"
-              "  Bypass local names: No",
+              "  Bypass list: [None]",
               ProxyConfigToString(config));
   }
 
   // No proxy.
   {
     ProxyConfig config;
-    config.auto_detect = false;
+    config.set_auto_detect(false);
 
     EXPECT_EQ("Automatic settings:\n"
               "  Auto-detect: No\n"
               "  Custom PAC script: [None]\n"
               "Manual settings:\n"
               "  Proxy server: [None]\n"
-              "  Bypass list: [None]\n"
-              "  Bypass local names: No",
+              "  Bypass list: [None]",
+              ProxyConfigToString(config));
+  }
+
+  // Manual proxy with bypass list + bypass local, list reversed.
+  {
+    ProxyConfig config;
+    config.set_auto_detect(false);
+    config.proxy_rules().ParseFromString("http://single-proxy:81");
+    config.proxy_rules().bypass_rules.AddRuleFromString("google.com");
+    config.proxy_rules().bypass_rules.AddRuleFromString("bypass2.net:1730");
+    config.proxy_rules().bypass_rules.AddRuleToBypassLocal();
+    config.proxy_rules().reverse_bypass = true;
+
+    EXPECT_EQ("Automatic settings:\n"
+              "  Auto-detect: No\n"
+              "  Custom PAC script: [None]\n"
+              "Manual settings:\n"
+              "  Proxy server: single-proxy:81\n"
+              "  Only use proxy for: \n"
+              "    google.com\n"
+              "    bypass2.net:1730\n"
+              "    <local>",
               ProxyConfigToString(config));
   }
 }
@@ -407,21 +392,47 @@
   }
   {
     ProxyConfig config;
-    config.auto_detect = true;
+    config.set_auto_detect(true);
     EXPECT_TRUE(config.MayRequirePACResolver());
   }
   {
     ProxyConfig config;
-    config.pac_url = GURL("http://custom/pac.js");
+    config.set_pac_url(GURL("http://custom/pac.js"));
     EXPECT_TRUE(config.MayRequirePACResolver());
   }
   {
     ProxyConfig config;
-    config.pac_url = GURL("notvalid");
+    config.set_pac_url(GURL("notvalid"));
     EXPECT_FALSE(config.MayRequirePACResolver());
   }
 }
 
+TEST(ProxyConfigTest, ReversedBypassList) {
+  {
+    ProxyConfig config;
+    config.set_auto_detect(false);
+    config.proxy_rules().ParseFromString("http://single-proxy:81");
+    config.proxy_rules().bypass_rules.AddRuleFromString("google.com");
+    config.proxy_rules().bypass_rules.AddRuleFromString("bypass2.net:1730");
+    config.proxy_rules().bypass_rules.AddRuleToBypassLocal();
+    config.proxy_rules().reverse_bypass = true;
+
+    ProxyInfo info[3];
+    GURL url0("http://google.com");
+    GURL url1("http://www.webkit.com");
+    GURL url2("http://bypass2.net:1730");
+
+    config.proxy_rules().Apply(url0, &info[0]);
+    EXPECT_EQ("single-proxy:81", info[0].proxy_server().ToURI());
+
+    config.proxy_rules().Apply(url1, &info[1]);
+    EXPECT_TRUE(info[1].is_direct());
+
+    config.proxy_rules().Apply(url2, &info[2]);
+    EXPECT_EQ("single-proxy:81", info[2].proxy_server().ToURI());
+  }
+}
+
 }  // namespace
 }  // namespace net
 
diff --git a/net/proxy/proxy_info.h b/net/proxy/proxy_info.h
index b18feb8..2531c6d 100644
--- a/net/proxy/proxy_info.h
+++ b/net/proxy/proxy_info.h
@@ -22,22 +22,22 @@
   ProxyInfo();
   // Default copy-constructor and assignment operator are OK!
 
-  // Use the same proxy server as the given |proxy_info|.
+  // Uses the same proxy server as the given |proxy_info|.
   void Use(const ProxyInfo& proxy_info);
 
-  // Use a direct connection.
+  // Uses a direct connection.
   void UseDirect();
 
-  // Use a specific proxy server, of the form:
+  // Uses a specific proxy server, of the form:
   //   proxy-uri = [<scheme> "://"] <hostname> [":" <port>]
   // This may optionally be a semi-colon delimited list of <proxy-uri>.
   // It is OK to have LWS between entries.
   void UseNamedProxy(const std::string& proxy_uri_list);
 
-  // Set the proxy list to a single entry, |proxy_server|.
+  // Sets the proxy list to a single entry, |proxy_server|.
   void UseProxyServer(const ProxyServer& proxy_server);
 
-  // Parse from the given PAC result.
+  // Parses from the given PAC result.
   void UsePacString(const std::string& pac_string) {
     proxy_list_.SetFromPacString(pac_string);
   }
@@ -50,6 +50,27 @@
     return proxy_list_.Get().is_direct();
   }
 
+  // Returns true if the first valid proxy server is an https proxy.
+  bool is_https() const {
+    if (is_empty())
+      return false;
+    return proxy_server().is_https();
+  }
+
+  // Returns true if the first valid proxy server is an http proxy.
+  bool is_http() const {
+    if (is_empty())
+      return false;
+    return proxy_server().is_http();
+  }
+
+  // Returns true if the first valid proxy server is a socks server.
+  bool is_socks() const {
+    if (is_empty())
+      return false;
+    return proxy_server().is_socks();
+  }
+
   // Returns true if this proxy info has no proxies left to try.
   bool is_empty() const {
     return proxy_list_.IsEmpty();
@@ -74,7 +95,7 @@
     proxy_list_.DeprioritizeBadProxies(proxy_retry_info);
   }
 
-  // Delete any entry which doesn't have one of the specified proxy schemes.
+  // Deletes any entry which doesn't have one of the specified proxy schemes.
   void RemoveProxiesWithoutScheme(int scheme_bit_field) {
     proxy_list_.RemoveProxiesWithoutScheme(scheme_bit_field);
   }
diff --git a/net/proxy/proxy_list.h b/net/proxy/proxy_list.h
index 1afd1d9..5df1e0a 100644
--- a/net/proxy/proxy_list.h
+++ b/net/proxy/proxy_list.h
@@ -40,11 +40,12 @@
   // this if !IsEmpty().
   const ProxyServer& Get() const;
 
-  // Set the list by parsing the pac result |pac_string|.
+  // Sets the list by parsing the pac result |pac_string|.
   // Some examples for |pac_string|:
   //   "DIRECT"
   //   "PROXY foopy1"
   //   "PROXY foopy1; SOCKS4 foopy2:1188"
+  // Does a best-effort parse, and silently discards any errors.
   void SetFromPacString(const std::string& pac_string);
 
   // Returns a PAC-style semicolon-separated list of valid proxy servers.
diff --git a/net/proxy/proxy_resolver.h b/net/proxy/proxy_resolver.h
index 2e83097..003fa84 100644
--- a/net/proxy/proxy_resolver.h
+++ b/net/proxy/proxy_resolver.h
@@ -5,15 +5,16 @@
 #ifndef NET_PROXY_PROXY_RESOLVER_H_
 #define NET_PROXY_PROXY_RESOLVER_H_
 
-#include <string>
-
 #include "base/logging.h"
+#include "base/ref_counted.h"
+#include "base/string16.h"
 #include "googleurl/src/gurl.h"
 #include "net/base/completion_callback.h"
+#include "net/proxy/proxy_resolver_script_data.h"
 
 namespace net {
 
-class LoadLog;
+class BoundNetLog;
 class ProxyInfo;
 
 // Interface for "proxy resolvers". A ProxyResolver fills in a list of proxies
@@ -41,30 +42,17 @@
                              ProxyInfo* results,
                              CompletionCallback* callback,
                              RequestHandle* request,
-                             LoadLog* load_log) = 0;
+                             const BoundNetLog& net_log) = 0;
 
   // Cancels |request|.
   virtual void CancelRequest(RequestHandle request) = 0;
 
   // The PAC script backend can be specified to the ProxyResolver either via
   // URL, or via the javascript text itself.  If |expects_pac_bytes| is true,
-  // then PAC scripts should be specified using SetPacScriptByData(). Otherwise
-  // they should be specified using SetPacScriptByUrl().
+  // then the ProxyResolverScriptData passed to SetPacScript() should
+  // contain the actual script bytes rather than just the URL.
   bool expects_pac_bytes() const { return expects_pac_bytes_; }
 
-  // Sets the PAC script backend to use for this proxy resolver (by URL).
-  int SetPacScriptByUrl(const GURL& url, CompletionCallback* callback) {
-    DCHECK(!expects_pac_bytes());
-    return SetPacScript(url, std::string(), callback);
-  }
-
-  // Sets the PAC script backend to use for this proxy resolver (by contents).
-  int SetPacScriptByData(const std::string& bytes_utf8,
-                         CompletionCallback* callback) {
-    DCHECK(expects_pac_bytes());
-    return SetPacScript(GURL(), bytes_utf8, callback);
-  }
-
   // TODO(eroman): Make this =0.
   virtual void CancelSetPacScript() {
     NOTREACHED();
@@ -75,16 +63,18 @@
   // no-op implementation.
   virtual void PurgeMemory() {}
 
- private:
-  // Called to set the PAC script backend to use. If |pac_url| is invalid,
-  // this is a request to use WPAD (auto detect). |bytes_utf8| may be empty if
-  // the fetch failed, or if the fetch returned no content.
+  // Called to set the PAC script backend to use.
   // Returns ERR_IO_PENDING in the case of asynchronous completion, and notifies
   // the result through |callback|.
-  virtual int SetPacScript(const GURL& pac_url,
-                           const std::string& bytes_utf8,
-                           CompletionCallback* callback) = 0;
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& pac_script,
+      CompletionCallback* callback) = 0;
 
+  // Optional shutdown code to be run before destruction. This is only used
+  // by the multithreaded runner to signal cleanup from origin thread
+  virtual void Shutdown() {}
+
+ private:
   const bool expects_pac_bytes_;
 
   DISALLOW_COPY_AND_ASSIGN(ProxyResolver);
diff --git a/net/proxy/proxy_resolver_js_bindings.cc b/net/proxy/proxy_resolver_js_bindings.cc
index eb7e4f8..56604cd 100644
--- a/net/proxy/proxy_resolver_js_bindings.cc
+++ b/net/proxy/proxy_resolver_js_bindings.cc
@@ -1,153 +1,210 @@
-// Copyright (c) 2009 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.
+// Copyright (c) 2010 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/proxy/proxy_resolver_js_bindings.h"
 
-#include "base/compiler_specific.h"
 #include "base/logging.h"
-#include "base/message_loop.h"
-#include "base/waitable_event.h"
+#include "base/string_util.h"
+#include "base/values.h"
 #include "net/base/address_list.h"
+#include "net/base/host_cache.h"
 #include "net/base/host_resolver.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
 #include "net/base/net_util.h"
 #include "net/base/sys_addrinfo.h"
+#include "net/proxy/proxy_resolver_request_context.h"
 
 namespace net {
+
 namespace {
 
-// Wrapper around HostResolver to give a sync API while running the resolve
-// in async mode on |host_resolver_loop|. If |host_resolver_loop| is NULL,
-// runs sync on the current thread (this mode is just used by testing).
-class SyncHostResolverBridge
-    : public base::RefCountedThreadSafe<SyncHostResolverBridge> {
+// Event parameters for a PAC error message (line number + message).
+class ErrorNetlogParams : public NetLog::EventParameters {
  public:
-  SyncHostResolverBridge(HostResolver* host_resolver,
-                         MessageLoop* host_resolver_loop)
-      : host_resolver_(host_resolver),
-        host_resolver_loop_(host_resolver_loop),
-        event_(false, false),
-        ALLOW_THIS_IN_INITIALIZER_LIST(
-            callback_(this, &SyncHostResolverBridge::OnResolveCompletion)) {
+  ErrorNetlogParams(int line_number,
+                    const string16& message)
+      : line_number_(line_number),
+        message_(message) {
   }
 
-  // Run the resolve on host_resolver_loop, and wait for result.
-  int Resolve(const std::string& hostname,
-              AddressFamily address_family,
-              net::AddressList* addresses) {
-    // Port number doesn't matter.
-    HostResolver::RequestInfo info(hostname, 80);
-    info.set_address_family(address_family);
-
-    // Hack for tests -- run synchronously on current thread.
-    if (!host_resolver_loop_)
-      return host_resolver_->Resolve(info, addresses, NULL, NULL, NULL);
-
-    // Otherwise start an async resolve on the resolver's thread.
-    host_resolver_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
-        &SyncHostResolverBridge::StartResolve, info, addresses));
-
-    // Wait for the resolve to complete in the resolver's thread.
-    event_.Wait();
-    return err_;
+  virtual Value* ToValue() const {
+    DictionaryValue* dict = new DictionaryValue();
+    dict->SetInteger(L"line_number", line_number_);
+    dict->SetStringFromUTF16(L"message", message_);
+    return dict;
   }
 
  private:
-  friend class base::RefCountedThreadSafe<SyncHostResolverBridge>;
+  const int line_number_;
+  const string16 message_;
 
-  ~SyncHostResolverBridge() {}
+  DISALLOW_COPY_AND_ASSIGN(ErrorNetlogParams);
+};
 
-  // Called on host_resolver_loop_.
-  void StartResolve(const HostResolver::RequestInfo& info,
-                    net::AddressList* addresses) {
-    DCHECK_EQ(host_resolver_loop_, MessageLoop::current());
-    int error = host_resolver_->Resolve(
-        info, addresses, &callback_, NULL, NULL);
-    if (error != ERR_IO_PENDING)
-      OnResolveCompletion(error);  // Completed synchronously.
+// Event parameters for a PAC alert().
+class AlertNetlogParams : public NetLog::EventParameters {
+ public:
+  explicit AlertNetlogParams(const string16& message) : message_(message) {
   }
 
-  // Called on host_resolver_loop_.
-  void OnResolveCompletion(int result) {
-    DCHECK_EQ(host_resolver_loop_, MessageLoop::current());
-    err_ = result;
-    event_.Signal();
+  virtual Value* ToValue() const {
+    DictionaryValue* dict = new DictionaryValue();
+    dict->SetStringFromUTF16(L"message", message_);
+    return dict;
   }
 
-  scoped_refptr<HostResolver> host_resolver_;
-  MessageLoop* host_resolver_loop_;
+ private:
+  const string16 message_;
 
-  // Event to notify completion of resolve request.
-  base::WaitableEvent event_;
-
-  // Callback for when the resolve completes on host_resolver_loop_.
-  net::CompletionCallbackImpl<SyncHostResolverBridge> callback_;
-
-  // The result from the result request (set by in host_resolver_loop_).
-  int err_;
+  DISALLOW_COPY_AND_ASSIGN(AlertNetlogParams);
 };
 
 // ProxyResolverJSBindings implementation.
 class DefaultJSBindings : public ProxyResolverJSBindings {
  public:
-  DefaultJSBindings(HostResolver* host_resolver,
-                    MessageLoop* host_resolver_loop)
-      : host_resolver_(new SyncHostResolverBridge(
-          host_resolver, host_resolver_loop)) {}
+  DefaultJSBindings(HostResolver* host_resolver, NetLog* net_log)
+      : host_resolver_(host_resolver),
+        net_log_(net_log) {
+  }
 
   // Handler for "alert(message)".
-  virtual void Alert(const std::string& message) {
+  virtual void Alert(const string16& message) {
     LOG(INFO) << "PAC-alert: " << message;
+
+    // Send to the NetLog.
+    LogEventToCurrentRequestAndGlobally(NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
+                                        new AlertNetlogParams(message));
   }
 
-  // Handler for "myIpAddress()". Returns empty string on failure.
+  // Handler for "myIpAddress()".
   // TODO(eroman): Perhaps enumerate the interfaces directly, using
   // getifaddrs().
-  virtual std::string MyIpAddress() {
-    // DnsResolve("") returns "", so no need to check for failure.
-    return DnsResolve(GetHostName());
+  virtual bool MyIpAddress(std::string* first_ip_address) {
+    LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
+                             NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS,
+                             NULL);
+
+    bool ok = MyIpAddressImpl(first_ip_address);
+
+    LogEventToCurrentRequest(NetLog::PHASE_END,
+                             NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS,
+                             NULL);
+    return ok;
   }
 
-  // Handler for "myIpAddressEx()". Returns empty string on failure.
-  virtual std::string MyIpAddressEx() {
-    return DnsResolveEx(GetHostName());
+  // Handler for "myIpAddressEx()".
+  virtual bool MyIpAddressEx(std::string* ip_address_list) {
+    LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
+                             NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX,
+                             NULL);
+
+    bool ok = MyIpAddressExImpl(ip_address_list);
+
+    LogEventToCurrentRequest(NetLog::PHASE_END,
+                             NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX,
+                             NULL);
+    return ok;
   }
 
-  // Handler for "dnsResolve(host)". Returns empty string on failure.
-  virtual std::string DnsResolve(const std::string& host) {
+  // Handler for "dnsResolve(host)".
+  virtual bool DnsResolve(const std::string& host,
+                          std::string* first_ip_address) {
+    LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
+                             NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE,
+                             NULL);
+
+    bool ok = DnsResolveImpl(host, first_ip_address);
+
+    LogEventToCurrentRequest(NetLog::PHASE_END,
+                             NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE,
+                             NULL);
+    return ok;
+  }
+
+  // Handler for "dnsResolveEx(host)".
+  virtual bool DnsResolveEx(const std::string& host,
+                            std::string* ip_address_list) {
+    LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
+                             NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX,
+                             NULL);
+
+    bool ok = DnsResolveExImpl(host, ip_address_list);
+
+    LogEventToCurrentRequest(NetLog::PHASE_END,
+                             NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX,
+                             NULL);
+    return ok;
+  }
+
+  // Handler for when an error is encountered. |line_number| may be -1.
+  virtual void OnError(int line_number, const string16& message) {
+    // Send to the chrome log.
+    if (line_number == -1)
+      LOG(INFO) << "PAC-error: " << message;
+    else
+      LOG(INFO) << "PAC-error: " << "line: " << line_number << ": " << message;
+
+    // Send the error to the NetLog.
+    LogEventToCurrentRequestAndGlobally(
+        NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
+        new ErrorNetlogParams(line_number, message));
+  }
+
+  virtual void Shutdown() {
+    host_resolver_->Shutdown();
+  }
+
+ private:
+  bool MyIpAddressImpl(std::string* first_ip_address) {
+    std::string my_hostname = GetHostName();
+    if (my_hostname.empty())
+      return false;
+    return DnsResolveImpl(my_hostname, first_ip_address);
+  }
+
+  bool MyIpAddressExImpl(std::string* ip_address_list) {
+    std::string my_hostname = GetHostName();
+    if (my_hostname.empty())
+      return false;
+    return DnsResolveExImpl(my_hostname, ip_address_list);
+  }
+
+  bool DnsResolveImpl(const std::string& host,
+                      std::string* first_ip_address) {
     // Do a sync resolve of the hostname.
     // Disable IPv6 results. We do this because the PAC specification isn't
     // really IPv6 friendly, and Internet Explorer also restricts to IPv4.
     // Consequently a lot of existing PAC scripts assume they will only get
     // IPv4 results, and will misbehave if they get an IPv6 result.
     // See http://crbug.com/24641 for more details.
-    net::AddressList address_list;
-    int result = host_resolver_->Resolve(host,
-                                         ADDRESS_FAMILY_IPV4,
-                                         &address_list);
+    HostResolver::RequestInfo info(host, 80);  // Port doesn't matter.
+    info.set_address_family(ADDRESS_FAMILY_IPV4);
+    AddressList address_list;
 
+    int result = DnsResolveHelper(info, &address_list);
     if (result != OK)
-      return std::string();  // Failed.
-
-    if (!address_list.head())
-      return std::string();
+      return false;
 
     // There may be multiple results; we will just use the first one.
     // This returns empty string on failure.
-    return net::NetAddressToString(address_list.head());
+    *first_ip_address = net::NetAddressToString(address_list.head());
+    if (first_ip_address->empty())
+      return false;
+
+    return true;
   }
 
-  // Handler for "dnsResolveEx(host)". Returns empty string on failure.
-  virtual std::string DnsResolveEx(const std::string& host) {
+  bool DnsResolveExImpl(const std::string& host,
+                        std::string* ip_address_list) {
     // Do a sync resolve of the hostname.
-    net::AddressList address_list;
-    int result = host_resolver_->Resolve(host,
-                                         ADDRESS_FAMILY_UNSPECIFIED,
-                                         &address_list);
+    HostResolver::RequestInfo info(host, 80);  // Port doesn't matter.
+    AddressList address_list;
+    int result = DnsResolveHelper(info, &address_list);
 
     if (result != OK)
-      return std::string();  // Failed.
+      return false;
 
     // Stringify all of the addresses in the address list, separated
     // by semicolons.
@@ -156,31 +213,88 @@
     while (current_address) {
       if (!address_list_str.empty())
         address_list_str += ";";
-      address_list_str += net::NetAddressToString(current_address);
+      const std::string address_string = NetAddressToString(current_address);
+      if (address_string.empty())
+        return false;
+      address_list_str += address_string;
       current_address = current_address->ai_next;
     }
 
-    return address_list_str;
+    *ip_address_list = address_list_str;
+    return true;
   }
 
-  // Handler for when an error is encountered. |line_number| may be -1.
-  virtual void OnError(int line_number, const std::string& message) {
-    if (line_number == -1)
-      LOG(INFO) << "PAC-error: " << message;
-    else
-      LOG(INFO) << "PAC-error: " << "line: " << line_number << ": " << message;
+  // Helper to execute a synchronous DNS resolve, using the per-request
+  // DNS cache if there is one.
+  int DnsResolveHelper(const HostResolver::RequestInfo& info,
+                       AddressList* address_list) {
+    HostCache::Key cache_key(info.hostname(),
+                             info.address_family(),
+                             info.host_resolver_flags());
+
+    HostCache* host_cache = current_request_context() ?
+        current_request_context()->host_cache : NULL;
+
+    // First try to service this request from the per-request DNS cache.
+    // (we cache DNS failures much more aggressively within the context
+    // of a FindProxyForURL() request).
+    if (host_cache) {
+      const HostCache::Entry* entry =
+          host_cache->Lookup(cache_key, base::TimeTicks::Now());
+      if (entry) {
+        if (entry->error == OK)
+          *address_list = entry->addrlist;
+        return entry->error;
+      }
+    }
+
+    // Otherwise ask the resolver.
+    int result = host_resolver_->Resolve(info, address_list, NULL, NULL,
+                                         BoundNetLog());
+
+    // Save the result back to the per-request DNS cache.
+    if (host_cache) {
+      host_cache->Set(cache_key, result, *address_list,
+                      base::TimeTicks::Now());
+    }
+
+    return result;
   }
 
- private:
-  scoped_refptr<SyncHostResolverBridge> host_resolver_;
+  void LogEventToCurrentRequest(
+      NetLog::EventPhase phase,
+      NetLog::EventType type,
+      scoped_refptr<NetLog::EventParameters> params) {
+    if (current_request_context() && current_request_context()->net_log)
+      current_request_context()->net_log->AddEntry(type, phase, params);
+  }
+
+  void LogEventToCurrentRequestAndGlobally(
+      NetLog::EventType type,
+      scoped_refptr<NetLog::EventParameters> params) {
+    LogEventToCurrentRequest(NetLog::PHASE_NONE, type, params);
+
+    // Emit to the global NetLog event stream.
+    if (net_log_) {
+      net_log_->AddEntry(
+          type,
+          base::TimeTicks::Now(),
+          NetLog::Source(),
+          NetLog::PHASE_NONE,
+          params);
+    }
+  }
+
+  scoped_refptr<HostResolver> host_resolver_;
+  NetLog* net_log_;
 };
 
 }  // namespace
 
 // static
 ProxyResolverJSBindings* ProxyResolverJSBindings::CreateDefault(
-    HostResolver* host_resolver, MessageLoop* host_resolver_loop) {
-  return new DefaultJSBindings(host_resolver, host_resolver_loop);
+    HostResolver* host_resolver, NetLog* net_log) {
+  return new DefaultJSBindings(host_resolver, net_log);
 }
 
 }  // namespace net
diff --git a/net/proxy/proxy_resolver_js_bindings.h b/net/proxy/proxy_resolver_js_bindings.h
index 03ad61a..ae13584 100644
--- a/net/proxy/proxy_resolver_js_bindings.h
+++ b/net/proxy/proxy_resolver_js_bindings.h
@@ -1,60 +1,87 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_PROXY_PROXY_JS_BINDINGS_H
-#define NET_PROXY_PROXY_JS_BINDINGS_H
+#ifndef NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_
+#define NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_
 
 #include <string>
 
+#include "base/string16.h"
+
 class MessageLoop;
 
 namespace net {
 
 class HostResolver;
+class NetLog;
+struct ProxyResolverRequestContext;
 
 // Interface for the javascript bindings.
 class ProxyResolverJSBindings {
  public:
+  ProxyResolverJSBindings() : current_request_context_(NULL) {}
+
   virtual ~ProxyResolverJSBindings() {}
 
   // Handler for "alert(message)"
-  virtual void Alert(const std::string& message) = 0;
+  virtual void Alert(const string16& message) = 0;
 
-  // Handler for "myIpAddress()". Returns empty string on failure.
-  virtual std::string MyIpAddress() = 0;
+  // Handler for "myIpAddress()". Returns true on success and fills
+  // |*first_ip_address| with the result.
+  virtual bool MyIpAddress(std::string* first_ip_address) = 0;
 
-  // Handler for "myIpAddressEx()". Returns empty string on failure.
+  // Handler for "myIpAddressEx()". Returns true on success and fills
+  // |*ip_address_list| with the result.
   //
   // This is a Microsoft extension to PAC for IPv6, see:
   // http://blogs.msdn.com/wndp/articles/IPV6_PAC_Extensions_v0_9.aspx
-  virtual std::string MyIpAddressEx() = 0;
+  virtual bool MyIpAddressEx(std::string* ip_address_list) = 0;
 
-  // Handler for "dnsResolve(host)". Returns empty string on failure.
-  virtual std::string DnsResolve(const std::string& host) = 0;
+  // Handler for "dnsResolve(host)". Returns true on success and fills
+  // |*first_ip_address| with the result.
+  virtual bool DnsResolve(const std::string& host,
+                          std::string* first_ip_address) = 0;
 
-  // Handler for "dnsResolveEx(host)". Returns empty string on failure.
+  // Handler for "dnsResolveEx(host)". Returns true on success and fills
+  // |*ip_address_list| with the result.
   //
   // This is a Microsoft extension to PAC for IPv6, see:
   // http://blogs.msdn.com/wndp/articles/IPV6_PAC_Extensions_v0_9.aspx
-  virtual std::string DnsResolveEx(const std::string& host) = 0;
+  virtual bool DnsResolveEx(const std::string& host,
+                            std::string* ip_address_list) = 0;
 
   // Handler for when an error is encountered. |line_number| may be -1
   // if a line number is not applicable to this error.
-  virtual void OnError(int line_number, const std::string& error) = 0;
+  virtual void OnError(int line_number, const string16& error) = 0;
+
+  // Called before the thread running the proxy resolver is stopped.
+  virtual void Shutdown() = 0;
 
   // Creates a default javascript bindings implementation that will:
-  //   - Send script error messages to LOG(INFO)
-  //   - Send script alert()s to LOG(INFO)
+  //   - Send script error messages to both LOG(INFO), and the NetLog.
+  //   - Send script alert()s to both LOG(INFO), and the NetLog.
   //   - Use the provided host resolver to service dnsResolve().
   //
-  // |host_resolver| will be used in async mode on |host_resolver_loop|. If
-  // |host_resolver_loop| is NULL, then |host_resolver| will be used in sync
-  // mode on the PAC thread.
-  static ProxyResolverJSBindings* CreateDefault(
-      HostResolver* host_resolver, MessageLoop* host_resolver_loop);
+  // Note that |host_resolver| will be used in sync mode mode.
+  static ProxyResolverJSBindings* CreateDefault(HostResolver* host_resolver,
+                                                NetLog* net_log);
+
+  // Sets details about the currently executing FindProxyForURL() request.
+  void set_current_request_context(
+      ProxyResolverRequestContext* current_request_context) {
+    current_request_context_ = current_request_context;
+  }
+
+  // Retrieves details about the currently executing FindProxyForURL() request.
+  ProxyResolverRequestContext* current_request_context() {
+    return current_request_context_;
+  }
+
+ private:
+  ProxyResolverRequestContext* current_request_context_;
 };
 
 }  // namespace net
 
-#endif  // NET_PROXY_PROXY_JS_BINDINGS_H
+#endif  // NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_
diff --git a/net/proxy/proxy_resolver_js_bindings_unittest.cc b/net/proxy/proxy_resolver_js_bindings_unittest.cc
index 5035f3e..0124469 100644
--- a/net/proxy/proxy_resolver_js_bindings_unittest.cc
+++ b/net/proxy/proxy_resolver_js_bindings_unittest.cc
@@ -1,17 +1,23 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/proxy/proxy_resolver_js_bindings.h"
+
 #include "base/scoped_ptr.h"
+#include "base/string_util.h"
 #include "net/base/address_list.h"
 #include "net/base/mock_host_resolver.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/net_util.h"
 #include "net/base/sys_addrinfo.h"
-#include "net/proxy/proxy_resolver_js_bindings.h"
+#include "net/proxy/proxy_resolver_request_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
+
 namespace {
 
 // This is a HostResolver that synchronously resolves all hosts to the
@@ -26,7 +32,7 @@
                       AddressList* addresses,
                       CompletionCallback* callback,
                       RequestHandle* out_req,
-                      LoadLog* load_log) {
+                      const BoundNetLog& net_log) {
     // Build up the result list (in reverse).
     AddressList temp_list = ResolveIPLiteral("200.100.1.2");
     temp_list = PrependAddressToList("172.22.34.1", temp_list);
@@ -47,7 +53,8 @@
     AddressList result;
     int rv = SystemHostResolverProc(ip_literal,
                                     ADDRESS_FAMILY_UNSPECIFIED,
-                                    &result);
+                                    0,
+                                    &result, NULL);
     EXPECT_EQ(OK, rv);
     EXPECT_EQ(NULL, result.head()->ai_next);
     return result;
@@ -67,7 +74,7 @@
 
     // Make a copy of the concatenated list.
     AddressList concatenated;
-    concatenated.Copy(result.head());
+    concatenated.Copy(result.head(), true);
 
     // Restore |result| (so it is freed properly).
     result_head->ai_next = NULL;
@@ -76,6 +83,33 @@
   }
 };
 
+class MockFailingHostResolver : public HostResolver {
+ public:
+  MockFailingHostResolver() : count_(0) {}
+
+  // HostResolver methods:
+  virtual int Resolve(const RequestInfo& info,
+                      AddressList* addresses,
+                      CompletionCallback* callback,
+                      RequestHandle* out_req,
+                      const BoundNetLog& net_log) {
+    count_++;
+    return ERR_NAME_NOT_RESOLVED;
+  }
+
+  virtual void CancelRequest(RequestHandle req) {}
+  virtual void AddObserver(Observer* observer) {}
+  virtual void RemoveObserver(Observer* observer) {}
+  virtual void Shutdown() {}
+
+  // Returns the number of times Resolve() has been called.
+  int count() const { return count_; }
+  void ResetCount() { count_ = 0; }
+
+ private:
+  int count_;
+};
+
 TEST(ProxyResolverJSBindingsTest, DnsResolve) {
   scoped_refptr<MockHostResolver> host_resolver(new MockHostResolver);
 
@@ -83,18 +117,21 @@
   scoped_ptr<ProxyResolverJSBindings> bindings(
       ProxyResolverJSBindings::CreateDefault(host_resolver, NULL));
 
+  std::string ip_address;
+
   // Empty string is not considered a valid host (even though on some systems
   // requesting this will resolve to localhost).
   host_resolver->rules()->AddSimulatedFailure("");
-  EXPECT_EQ("", bindings->DnsResolve(""));
+  EXPECT_FALSE(bindings->DnsResolve("", &ip_address));
 
   // Should call through to the HostResolver.
   host_resolver->rules()->AddRule("google.com", "192.168.1.1");
-  EXPECT_EQ("192.168.1.1", bindings->DnsResolve("google.com"));
+  EXPECT_TRUE(bindings->DnsResolve("google.com", &ip_address));
+  EXPECT_EQ("192.168.1.1", ip_address);
 
   // Resolve failures should give empty string.
   host_resolver->rules()->AddSimulatedFailure("fail");
-  EXPECT_EQ("", bindings->DnsResolve("fail"));
+  EXPECT_FALSE(bindings->DnsResolve("fail", &ip_address));
 
   // TODO(eroman): would be nice to have an IPV6 test here too, but that
   // won't work on all systems.
@@ -103,12 +140,12 @@
 TEST(ProxyResolverJSBindingsTest, MyIpAddress) {
   // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
   scoped_ptr<ProxyResolverJSBindings> bindings(
-      ProxyResolverJSBindings::CreateDefault(
-          new MockHostResolver, NULL));
+      ProxyResolverJSBindings::CreateDefault(new MockHostResolver, NULL));
 
   // Our IP address is always going to be 127.0.0.1, since we are using a
   // mock host resolver.
-  std::string my_ip_address = bindings->MyIpAddress();
+  std::string my_ip_address;
+  EXPECT_TRUE(bindings->MyIpAddress(&my_ip_address));
 
   EXPECT_EQ("127.0.0.1", my_ip_address);
 }
@@ -130,8 +167,7 @@
 
   // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
   scoped_ptr<ProxyResolverJSBindings> bindings(
-      ProxyResolverJSBindings::CreateDefault(
-          host_resolver, NULL));
+      ProxyResolverJSBindings::CreateDefault(host_resolver, NULL));
 
   // Make it so requests resolve to particular address patterns based on family:
   //  IPV4_ONLY --> 192.168.1.*
@@ -149,21 +185,34 @@
   // depending if the address family was IPV4_ONLY or not.
   HostResolver::RequestInfo info("foo", 80);
   AddressList address_list;
-  EXPECT_EQ(OK, host_resolver->Resolve(info, &address_list, NULL, NULL, NULL));
+  EXPECT_EQ(OK, host_resolver->Resolve(info, &address_list, NULL, NULL,
+                                       BoundNetLog()));
   EXPECT_EQ("192.168.2.1", NetAddressToString(address_list.head()));
 
   info.set_address_family(ADDRESS_FAMILY_IPV4);
-  EXPECT_EQ(OK, host_resolver->Resolve(info, &address_list, NULL, NULL, NULL));
+  EXPECT_EQ(OK, host_resolver->Resolve(info, &address_list, NULL, NULL,
+                                       BoundNetLog()));
   EXPECT_EQ("192.168.1.1", NetAddressToString(address_list.head()));
 
+  std::string ip_address;
   // Now the actual test.
-  EXPECT_EQ("192.168.1.2", bindings->MyIpAddress());       // IPv4 restricted.
-  EXPECT_EQ("192.168.1.1", bindings->DnsResolve("foo"));   // IPv4 restricted.
-  EXPECT_EQ("192.168.1.2", bindings->DnsResolve("foo2"));  // IPv4 restricted.
+  EXPECT_TRUE(bindings->MyIpAddress(&ip_address));
+  EXPECT_EQ("192.168.1.2", ip_address);  // IPv4 restricted.
 
-  EXPECT_EQ("192.168.2.2", bindings->MyIpAddressEx());       // Unrestricted.
-  EXPECT_EQ("192.168.2.1", bindings->DnsResolveEx("foo"));   // Unrestricted.
-  EXPECT_EQ("192.168.2.2", bindings->DnsResolveEx("foo2"));  // Unrestricted.
+  EXPECT_TRUE(bindings->DnsResolve("foo", &ip_address));
+  EXPECT_EQ("192.168.1.1", ip_address);  // IPv4 restricted.
+
+  EXPECT_TRUE(bindings->DnsResolve("foo2", &ip_address));
+  EXPECT_EQ("192.168.1.2", ip_address);  // IPv4 restricted.
+
+  EXPECT_TRUE(bindings->MyIpAddressEx(&ip_address));
+  EXPECT_EQ("192.168.2.2", ip_address);  // Unrestricted.
+
+  EXPECT_TRUE(bindings->DnsResolveEx("foo", &ip_address));
+  EXPECT_EQ("192.168.2.1", ip_address);  // Unrestricted.
+
+  EXPECT_TRUE(bindings->DnsResolveEx("foo2", &ip_address));
+  EXPECT_EQ("192.168.2.2", ip_address);  // Unrestricted.
 }
 
 // Test that myIpAddressEx() and dnsResolveEx() both return a semi-colon
@@ -177,12 +226,144 @@
   scoped_ptr<ProxyResolverJSBindings> bindings(
       ProxyResolverJSBindings::CreateDefault(host_resolver, NULL));
 
-  EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2",
-            bindings->MyIpAddressEx());
+  std::string ip_addresses;
 
-  EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2",
-            bindings->DnsResolveEx("FOO"));
+  EXPECT_TRUE(bindings->MyIpAddressEx(&ip_addresses));
+  EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2", ip_addresses);
+
+  EXPECT_TRUE(bindings->DnsResolveEx("FOO", &ip_addresses));
+  EXPECT_EQ("192.168.1.1;172.22.34.1;200.100.1.2", ip_addresses);
+}
+
+TEST(ProxyResolverJSBindingsTest, PerRequestDNSCache) {
+  scoped_refptr<MockFailingHostResolver> host_resolver(
+      new MockFailingHostResolver);
+
+  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
+  scoped_ptr<ProxyResolverJSBindings> bindings(
+      ProxyResolverJSBindings::CreateDefault(host_resolver, NULL));
+
+  std::string ip_address;
+
+  // Call DnsResolve() 4 times for the same hostname -- this should issue
+  // 4 separate calls to the underlying host resolver, since there is no
+  // current request context.
+  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
+  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
+  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
+  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
+  EXPECT_EQ(4, host_resolver->count());
+
+  host_resolver->ResetCount();
+
+  // Now setup a per-request context, and try the same experiment -- we
+  // expect the underlying host resolver to receive only 1 request this time,
+  // since it will service the others from the per-request DNS cache.
+  HostCache cache(50,
+                  base::TimeDelta::FromMinutes(10),
+                  base::TimeDelta::FromMinutes(10));
+  ProxyResolverRequestContext context(NULL, &cache);
+  bindings->set_current_request_context(&context);
+
+  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
+  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
+  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
+  EXPECT_FALSE(bindings->DnsResolve("foo", &ip_address));
+  EXPECT_EQ(1, host_resolver->count());
+
+  host_resolver->ResetCount();
+
+  // The "Ex" version shares this same cache, however since the flags
+  // are different it won't reuse this particular entry.
+  EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address));
+  EXPECT_EQ(1, host_resolver->count());
+  EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address));
+  EXPECT_FALSE(bindings->DnsResolveEx("foo", &ip_address));
+  EXPECT_EQ(1, host_resolver->count());
+
+  bindings->set_current_request_context(NULL);
+}
+
+// Test that when a binding is called, it logs to the per-request NetLog.
+TEST(ProxyResolverJSBindingsTest, NetLog) {
+  scoped_refptr<MockFailingHostResolver> host_resolver(
+      new MockFailingHostResolver);
+
+  CapturingNetLog global_log(CapturingNetLog::kUnbounded);
+
+  // Get a hold of a DefaultJSBindings* (it is a hidden impl class).
+  scoped_ptr<ProxyResolverJSBindings> bindings(
+      ProxyResolverJSBindings::CreateDefault(host_resolver, &global_log));
+
+  // Attach a capturing NetLog as the current request's log stream.
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
+  BoundNetLog bound_log(NetLog::Source(NetLog::SOURCE_NONE, 0), &log);
+  ProxyResolverRequestContext context(&bound_log, NULL);
+  bindings->set_current_request_context(&context);
+
+  std::string ip_address;
+
+  ASSERT_EQ(0u, log.entries().size());
+
+  // Call all the bindings. Each call should be logging something to
+  // our NetLog.
+
+  bindings->MyIpAddress(&ip_address);
+  EXPECT_EQ(2u, log.entries().size());
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 0, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 1, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS));
+
+  bindings->MyIpAddressEx(&ip_address);
+  EXPECT_EQ(4u, log.entries().size());
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 2, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 3, NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX));
+
+  bindings->DnsResolve("foo", &ip_address);
+  EXPECT_EQ(6u, log.entries().size());
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 4, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 5, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE));
+
+  bindings->DnsResolveEx("foo", &ip_address);
+  EXPECT_EQ(8u, log.entries().size());
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 6, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 7, NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX));
+
+  // Nothing has been emitted globally yet.
+  EXPECT_EQ(0u, global_log.entries().size());
+
+  bindings->OnError(30, string16());
+  EXPECT_EQ(9u, log.entries().size());
+  EXPECT_TRUE(LogContainsEvent(
+      log.entries(), 8, NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
+      NetLog::PHASE_NONE));
+
+  // We also emit errors to the top-level log stream.
+  EXPECT_EQ(1u, global_log.entries().size());
+  EXPECT_TRUE(LogContainsEvent(
+      global_log.entries(), 0, NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
+      NetLog::PHASE_NONE));
+
+  bindings->Alert(string16());
+  EXPECT_EQ(10u, log.entries().size());
+  EXPECT_TRUE(LogContainsEvent(
+      log.entries(), 9, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
+      NetLog::PHASE_NONE));
+
+  // We also emit javascript alerts to the top-level log stream.
+  EXPECT_EQ(2u, global_log.entries().size());
+  EXPECT_TRUE(LogContainsEvent(
+      global_log.entries(), 1, NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
+      NetLog::PHASE_NONE));
 }
 
 }  // namespace
+
 }  // namespace net
diff --git a/net/proxy/proxy_resolver_mac.cc b/net/proxy/proxy_resolver_mac.cc
index 6baf78f..8e8ef20 100644
--- a/net/proxy/proxy_resolver_mac.cc
+++ b/net/proxy/proxy_resolver_mac.cc
@@ -59,7 +59,7 @@
                                      ProxyInfo* results,
                                      CompletionCallback* /*callback*/,
                                      RequestHandle* /*request*/,
-                                     LoadLog* load_log) {
+                                     const BoundNetLog& net_log) {
   scoped_cftyperef<CFStringRef> query_ref(
       base::SysUTF8ToCFStringRef(query_url.spec()));
   scoped_cftyperef<CFURLRef> query_url_ref(
@@ -69,7 +69,9 @@
   if (!query_url_ref.get())
     return ERR_FAILED;
   scoped_cftyperef<CFStringRef> pac_ref(
-      base::SysUTF8ToCFStringRef(pac_url_.spec()));
+      base::SysUTF8ToCFStringRef(
+          script_data_->type() == ProxyResolverScriptData::TYPE_AUTO_DETECT ?
+              std::string() : script_data_->url().spec()));
   scoped_cftyperef<CFURLRef> pac_url_ref(
       CFURLCreateWithString(kCFAllocatorDefault,
                             pac_ref.get(),
diff --git a/net/proxy/proxy_resolver_mac.h b/net/proxy/proxy_resolver_mac.h
index 6676b2f..cacf6c8 100644
--- a/net/proxy/proxy_resolver_mac.h
+++ b/net/proxy/proxy_resolver_mac.h
@@ -24,21 +24,21 @@
                              ProxyInfo* results,
                              CompletionCallback* callback,
                              RequestHandle* request,
-                             LoadLog* load_log);
+                             const BoundNetLog& net_log);
 
   virtual void CancelRequest(RequestHandle request) {
     NOTREACHED();
   }
 
- private:
-  virtual int SetPacScript(const GURL& pac_url,
-                           const std::string& /*pac_bytes*/,
-                           CompletionCallback* /*callback*/) {
-    pac_url_ = pac_url;
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& script_data,
+      CompletionCallback* /*callback*/) {
+    script_data_ = script_data_;
     return OK;
   }
 
-  GURL pac_url_;
+ private:
+  scoped_refptr<ProxyResolverScriptData> script_data_;
 };
 
 }  // namespace net
diff --git a/net/proxy/proxy_resolver_perftest.cc b/net/proxy/proxy_resolver_perftest.cc
index 6e336e9..0c57955 100644
--- a/net/proxy/proxy_resolver_perftest.cc
+++ b/net/proxy/proxy_resolver_perftest.cc
@@ -1,8 +1,9 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/perftimer.h"
+#include "base/string_util.h"
 #include "net/base/mock_host_resolver.h"
 #include "net/proxy/proxy_resolver_js_bindings.h"
 #include "net/proxy/proxy_resolver_v8.h"
@@ -97,7 +98,8 @@
       InitHttpServer();
       GURL pac_url =
           server_->TestServerPage(std::string("files/") + script_name);
-      int rv = resolver_->SetPacScriptByUrl(pac_url, NULL);
+      int rv = resolver_->SetPacScript(
+          net::ProxyResolverScriptData::FromURL(pac_url), NULL);
       EXPECT_EQ(net::OK, rv);
     } else {
       LoadPacScriptIntoResolver(script_name);
@@ -109,7 +111,8 @@
     {
       net::ProxyInfo proxy_info;
       int result = resolver_->GetProxyForURL(
-          GURL("http://www.warmup.com"), &proxy_info, NULL, NULL, NULL);
+          GURL("http://www.warmup.com"), &proxy_info, NULL, NULL,
+          net::BoundNetLog());
       ASSERT_EQ(net::OK, result);
     }
 
@@ -124,7 +127,8 @@
       // Resolve.
       net::ProxyInfo proxy_info;
       int result = resolver_->GetProxyForURL(GURL(query.query_url),
-                                             &proxy_info, NULL, NULL, NULL);
+                                             &proxy_info, NULL, NULL,
+                                             net::BoundNetLog());
 
       // Check that the result was correct. Note that ToPacString() and
       // ASSERT_EQ() are fast, so they won't skew the results.
@@ -164,7 +168,8 @@
     ASSERT_TRUE(ok);
 
     // Load the PAC script into the ProxyResolver.
-    int rv = resolver_->SetPacScriptByData(file_contents, NULL);
+    int rv = resolver_->SetPacScript(
+        net::ProxyResolverScriptData::FromUTF8(file_contents), NULL);
     EXPECT_EQ(net::OK, rv);
   }
 
@@ -196,3 +201,4 @@
   PacPerfSuiteRunner runner(&resolver, "ProxyResolverV8");
   runner.RunAllTests();
 }
+
diff --git a/net/proxy/proxy_resolver_request_context.h b/net/proxy/proxy_resolver_request_context.h
new file mode 100644
index 0000000..fdcced1
--- /dev/null
+++ b/net/proxy/proxy_resolver_request_context.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2010 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_PROXY_PROXY_RESOLVER_REQUEST_CONTEXT_H_
+#define NET_PROXY_PROXY_RESOLVER_REQUEST_CONTEXT_H_
+
+namespace net {
+
+class HostCache;
+class BoundNetLog;
+
+// This data structure holds state related to an invocation of
+// "FindProxyForURL()". It is used to associate per-request
+// data that can be retrieved by the bindings.
+struct ProxyResolverRequestContext {
+  // All of these pointers are expected to remain valid for duration of
+  // this instance's lifetime.
+  ProxyResolverRequestContext(const BoundNetLog* net_log,
+                              HostCache* host_cache)
+    : net_log(net_log),
+      host_cache(host_cache) {
+  }
+
+  const BoundNetLog* net_log;
+  HostCache* host_cache;
+};
+
+}  // namespace net
+
+#endif  // NET_PROXY_PROXY_RESOLVER_REQUEST_CONTEXT_H_
diff --git a/net/proxy/proxy_resolver_script_data.cc b/net/proxy/proxy_resolver_script_data.cc
new file mode 100644
index 0000000..fd8c399
--- /dev/null
+++ b/net/proxy/proxy_resolver_script_data.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2010 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/proxy/proxy_resolver_script_data.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+
+namespace net {
+
+// static
+scoped_refptr<ProxyResolverScriptData> ProxyResolverScriptData::FromUTF8(
+    const std::string& utf8) {
+  return new ProxyResolverScriptData(TYPE_SCRIPT_CONTENTS,
+                                     GURL(),
+                                     UTF8ToUTF16(utf8));
+}
+
+// static
+scoped_refptr<ProxyResolverScriptData> ProxyResolverScriptData::FromUTF16(
+    const string16& utf16) {
+  return new ProxyResolverScriptData(TYPE_SCRIPT_CONTENTS, GURL(), utf16);
+}
+
+// static
+scoped_refptr<ProxyResolverScriptData> ProxyResolverScriptData::FromURL(
+    const GURL& url) {
+  return new ProxyResolverScriptData(TYPE_SCRIPT_URL, url, string16());
+}
+
+// static
+scoped_refptr<ProxyResolverScriptData>
+ProxyResolverScriptData::ForAutoDetect() {
+  return new ProxyResolverScriptData(TYPE_AUTO_DETECT, GURL(), string16());
+}
+
+const string16& ProxyResolverScriptData::utf16() const {
+  DCHECK_EQ(TYPE_SCRIPT_CONTENTS, type_);
+  return utf16_;
+}
+
+const GURL& ProxyResolverScriptData::url() const {
+  DCHECK_EQ(TYPE_SCRIPT_URL, type_);
+  return url_;
+}
+
+}  // namespace net
diff --git a/net/proxy/proxy_resolver_script_data.h b/net/proxy/proxy_resolver_script_data.h
new file mode 100644
index 0000000..f0bb2ee
--- /dev/null
+++ b/net/proxy/proxy_resolver_script_data.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2010 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_PROXY_PROXY_RESOLVER_SCRIPT_DATA_H_
+#define NET_PROXY_PROXY_RESOLVER_SCRIPT_DATA_H_
+
+#include "base/ref_counted.h"
+#include "base/string16.h"
+#include "googleurl/src/gurl.h"
+
+namespace net {
+
+// Reference-counted wrapper for passing around a PAC script specification.
+// The PAC script can be either specified via a URL, a deferred URL for
+// auto-detect, or the actual javascript program text.
+//
+// This is thread-safe so it can be used by multi-threaded implementations of
+// ProxyResolver to share the data between threads.
+class ProxyResolverScriptData
+    : public base::RefCountedThreadSafe<ProxyResolverScriptData> {
+ public:
+  enum Type {
+    TYPE_SCRIPT_CONTENTS,
+    TYPE_SCRIPT_URL,
+    TYPE_AUTO_DETECT,
+  };
+
+  // Creates a script data given the UTF8 bytes of the content.
+  static scoped_refptr<ProxyResolverScriptData> FromUTF8(
+      const std::string& utf8);
+
+  // Creates a script data given the UTF16 bytes of the content.
+  static scoped_refptr<ProxyResolverScriptData> FromUTF16(
+      const string16& utf16);
+
+  // Creates a script data given a URL to the PAC script.
+  static scoped_refptr<ProxyResolverScriptData> FromURL(const GURL& url);
+
+  // Creates a script data for using an automatically detected PAC URL.
+  static scoped_refptr<ProxyResolverScriptData> ForAutoDetect();
+
+  Type type() const {
+    return type_;
+  }
+
+  // Returns the contents of the script as UTF16.
+  // (only valid for type() == TYPE_SCRIPT_CONTENTS).
+  const string16& utf16() const;
+
+  // Returns the URL of the script.
+  // (only valid for type() == TYPE_SCRIPT_URL).
+  const GURL& url() const;
+
+ private:
+  ProxyResolverScriptData(Type type,
+                          const GURL& url,
+                          const string16& utf16)
+      : type_(type),
+        url_(url),
+        utf16_(utf16) {
+  }
+
+  const Type type_;
+  const GURL url_;
+  const string16 utf16_;
+};
+
+}  // namespace net
+
+#endif  // NET_PROXY_PROXY_RESOLVER_SCRIPT_DATA_H_
diff --git a/net/proxy/proxy_resolver_v8.cc b/net/proxy/proxy_resolver_v8.cc
index 4db6bc3..495bbe9 100644
--- a/net/proxy/proxy_resolver_v8.cc
+++ b/net/proxy/proxy_resolver_v8.cc
@@ -1,16 +1,21 @@
-// Copyright (c) 2009 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.
+// Copyright (c) 2010 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/proxy/proxy_resolver_v8.h"
 
+#include "base/basictypes.h"
 #include "base/logging.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "googleurl/src/gurl.h"
-#include "net/base/load_log.h"
+#include "googleurl/src/url_canon.h"
+#include "net/base/host_cache.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
 #include "net/proxy/proxy_info.h"
 #include "net/proxy/proxy_resolver_js_bindings.h"
+#include "net/proxy/proxy_resolver_request_context.h"
 #include "net/proxy/proxy_resolver_script.h"
 #include "v8/include/v8.h"
 
@@ -68,22 +73,100 @@
 // Pseudo-name for the PAC utility script.
 const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
 
-// Convert a V8 String to a std::string.
-std::string V8StringToStdString(v8::Handle<v8::String> s) {
-  int len = s->Utf8Length();
-  std::string result;
-  s->WriteUtf8(WriteInto(&result, len + 1), len);
+// External string wrapper so V8 can access the UTF16 string wrapped by
+// ProxyResolverScriptData.
+class V8ExternalStringFromScriptData
+    : public v8::String::ExternalStringResource {
+ public:
+  explicit V8ExternalStringFromScriptData(
+      const scoped_refptr<ProxyResolverScriptData>& script_data)
+      : script_data_(script_data) {}
+
+  virtual const uint16_t* data() const {
+    return reinterpret_cast<const uint16*>(script_data_->utf16().data());
+  }
+
+  virtual size_t length() const {
+    return script_data_->utf16().size();
+  }
+
+ private:
+  const scoped_refptr<ProxyResolverScriptData> script_data_;
+  DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
+};
+
+// External string wrapper so V8 can access a string literal.
+class V8ExternalASCIILiteral : public v8::String::ExternalAsciiStringResource {
+ public:
+  // |ascii| must be a NULL-terminated C string, and must remain valid
+  // throughout this object's lifetime.
+  V8ExternalASCIILiteral(const char* ascii, size_t length)
+      : ascii_(ascii), length_(length) {
+    DCHECK(IsStringASCII(ascii));
+  }
+
+  virtual const char* data() const {
+    return ascii_;
+  }
+
+  virtual size_t length() const {
+    return length_;
+  }
+
+ private:
+  const char* ascii_;
+  size_t length_;
+  DISALLOW_COPY_AND_ASSIGN(V8ExternalASCIILiteral);
+};
+
+// When creating a v8::String from a C++ string we have two choices: create
+// a copy, or create a wrapper that shares the same underlying storage.
+// For small strings it is better to just make a copy, whereas for large
+// strings there are savings by sharing the storage. This number identifies
+// the cutoff length for when to start wrapping rather than creating copies.
+const size_t kMaxStringBytesForCopy = 256;
+
+// Converts a V8 String to a UTF16 string16.
+string16 V8StringToUTF16(v8::Handle<v8::String> s) {
+  int len = s->Length();
+  string16 result;
+  // Note that the reinterpret cast is because on Windows string16 is an alias
+  // to wstring, and hence has character type wchar_t not uint16_t.
+  s->Write(reinterpret_cast<uint16_t*>(WriteInto(&result, len + 1)), 0, len);
   return result;
 }
 
-// Convert a std::string (UTF8) to a V8 string.
-v8::Local<v8::String> StdStringToV8String(const std::string& s) {
+// Converts an ASCII std::string to a V8 string.
+v8::Local<v8::String> ASCIIStringToV8String(const std::string& s) {
+  DCHECK(IsStringASCII(s));
   return v8::String::New(s.data(), s.size());
 }
 
-// String-ize a V8 object by calling its toString() method. Returns true
+// Converts a UTF16 string16 (warpped by a ProxyResolverScriptData) to a
+// V8 string.
+v8::Local<v8::String> ScriptDataToV8String(
+    const scoped_refptr<ProxyResolverScriptData>& s) {
+  if (s->utf16().size() * 2 <= kMaxStringBytesForCopy) {
+    return v8::String::New(
+        reinterpret_cast<const uint16_t*>(s->utf16().data()),
+        s->utf16().size());
+  }
+  return v8::String::NewExternal(new V8ExternalStringFromScriptData(s));
+}
+
+// Converts an ASCII string literal to a V8 string.
+v8::Local<v8::String> ASCIILiteralToV8String(const char* ascii) {
+  DCHECK(IsStringASCII(ascii));
+  size_t length = strlen(ascii);
+  if (length <= kMaxStringBytesForCopy)
+    return v8::String::New(ascii, length);
+  return v8::String::NewExternal(new V8ExternalASCIILiteral(ascii, length));
+}
+
+// Stringizes a V8 object by calling its toString() method. Returns true
 // on success. This may fail if the toString() throws an exception.
-bool V8ObjectToString(v8::Handle<v8::Value> object, std::string* result) {
+bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
+                           string16* utf16_result) {
   if (object.IsEmpty())
     return false;
 
@@ -91,10 +174,45 @@
   v8::Local<v8::String> str_object = object->ToString();
   if (str_object.IsEmpty())
     return false;
-  *result = V8StringToStdString(str_object);
+  *utf16_result = V8StringToUTF16(str_object);
   return true;
 }
 
+// Extracts an hostname argument from |args|. On success returns true
+// and fills |*hostname| with the result.
+bool GetHostnameArgument(const v8::Arguments& args, std::string* hostname) {
+  // The first argument should be a string.
+  if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
+    return false;
+
+  const string16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
+
+  // If the hostname is already in ASCII, simply return it as is.
+  if (IsStringASCII(hostname_utf16)) {
+    *hostname = UTF16ToASCII(hostname_utf16);
+    return true;
+  }
+
+  // Otherwise try to convert it from IDN to punycode.
+  const int kInitialBufferSize = 256;
+  url_canon::RawCanonOutputT<char16, kInitialBufferSize> punycode_output;
+  if (!url_canon::IDNToASCII(hostname_utf16.data(),
+                             hostname_utf16.length(),
+                             &punycode_output)) {
+    return false;
+  }
+
+  // |punycode_output| should now be ASCII; convert it to a std::string.
+  // (We could use UTF16ToASCII() instead, but that requires an extra string
+  // copy. Since ASCII is a subset of UTF8 the following is equivalent).
+  bool success = UTF16ToUTF8(punycode_output.data(),
+                             punycode_output.length(),
+                             hostname);
+  DCHECK(success);
+  DCHECK(IsStringASCII(*hostname));
+  return success;
+}
+
 }  // namespace
 
 // ProxyResolverV8::Context ---------------------------------------------------
@@ -102,7 +220,7 @@
 class ProxyResolverV8::Context {
  public:
   explicit Context(ProxyResolverJSBindings* js_bindings)
-      : js_bindings_(js_bindings), current_request_load_log_(NULL) {
+      : js_bindings_(js_bindings) {
     DCHECK(js_bindings != NULL);
   }
 
@@ -111,6 +229,12 @@
 
     v8_this_.Dispose();
     v8_context_.Dispose();
+
+    // Run the V8 garbage collector. We do this to be sure the
+    // ExternalStringResource objects we allocated get properly disposed.
+    // Otherwise when running the unit-tests they may get leaked.
+    // See crbug.com/48145.
+    PurgeMemory();
   }
 
   int ResolveProxy(const GURL& query_url, ProxyInfo* results) {
@@ -121,13 +245,14 @@
 
     v8::Local<v8::Value> function;
     if (!GetFindProxyForURL(&function)) {
-      js_bindings_->OnError(-1, "FindProxyForURL() is undefined.");
+      js_bindings_->OnError(
+          -1, ASCIIToUTF16("FindProxyForURL() is undefined."));
       return ERR_PAC_SCRIPT_FAILED;
     }
 
     v8::Handle<v8::Value> argv[] = {
-      StdStringToV8String(query_url.spec()),
-      StdStringToV8String(query_url.host()),
+      ASCIIStringToV8String(query_url.spec()),
+      ASCIIStringToV8String(query_url.host()),
     };
 
     v8::TryCatch try_catch;
@@ -140,18 +265,30 @@
     }
 
     if (!ret->IsString()) {
-      js_bindings_->OnError(-1, "FindProxyForURL() did not return a string.");
+      js_bindings_->OnError(
+          -1, ASCIIToUTF16("FindProxyForURL() did not return a string."));
       return ERR_PAC_SCRIPT_FAILED;
     }
 
-    std::string ret_str = V8StringToStdString(ret->ToString());
+    string16 ret_str = V8StringToUTF16(ret->ToString());
 
-    results->UsePacString(ret_str);
+    if (!IsStringASCII(ret_str)) {
+      // TODO(eroman): Rather than failing when a wide string is returned, we
+      //               could extend the parsing to handle IDNA hostnames by
+      //               converting them to ASCII punycode.
+      //               crbug.com/47234
+      string16 error_message =
+          ASCIIToUTF16("FindProxyForURL() returned a non-ASCII string "
+                       "(crbug.com/47234): ") + ret_str;
+      js_bindings_->OnError(-1, error_message);
+      return ERR_PAC_SCRIPT_FAILED;
+    }
 
+    results->UsePacString(UTF16ToASCII(ret_str));
     return OK;
   }
 
-  int InitV8(const std::string& pac_data_utf8) {
+  int InitV8(const scoped_refptr<ProxyResolverScriptData>& pac_script) {
     v8::Locker locked;
     v8::HandleScope scope;
 
@@ -161,28 +298,28 @@
     // Attach the javascript bindings.
     v8::Local<v8::FunctionTemplate> alert_template =
         v8::FunctionTemplate::New(&AlertCallback, v8_this_);
-    global_template->Set(v8::String::New("alert"), alert_template);
+    global_template->Set(ASCIILiteralToV8String("alert"), alert_template);
 
     v8::Local<v8::FunctionTemplate> my_ip_address_template =
         v8::FunctionTemplate::New(&MyIpAddressCallback, v8_this_);
-    global_template->Set(v8::String::New("myIpAddress"),
+    global_template->Set(ASCIILiteralToV8String("myIpAddress"),
         my_ip_address_template);
 
     v8::Local<v8::FunctionTemplate> dns_resolve_template =
         v8::FunctionTemplate::New(&DnsResolveCallback, v8_this_);
-    global_template->Set(v8::String::New("dnsResolve"),
+    global_template->Set(ASCIILiteralToV8String("dnsResolve"),
         dns_resolve_template);
 
     // Microsoft's PAC extensions (incomplete):
 
     v8::Local<v8::FunctionTemplate> dns_resolve_ex_template =
         v8::FunctionTemplate::New(&DnsResolveExCallback, v8_this_);
-    global_template->Set(v8::String::New("dnsResolveEx"),
+    global_template->Set(ASCIILiteralToV8String("dnsResolveEx"),
                          dns_resolve_ex_template);
 
     v8::Local<v8::FunctionTemplate> my_ip_address_ex_template =
         v8::FunctionTemplate::New(&MyIpAddressExCallback, v8_this_);
-    global_template->Set(v8::String::New("myIpAddressEx"),
+    global_template->Set(ASCIILiteralToV8String("myIpAddressEx"),
                          my_ip_address_ex_template);
 
     v8_context_ = v8::Context::New(NULL, global_template);
@@ -192,16 +329,18 @@
     // Add the PAC utility functions to the environment.
     // (This script should never fail, as it is a string literal!)
     // Note that the two string literals are concatenated.
-    int rv = RunScript(PROXY_RESOLVER_SCRIPT
-                       PROXY_RESOLVER_SCRIPT_EX,
-                       kPacUtilityResourceName);
+    int rv = RunScript(
+        ASCIILiteralToV8String(
+            PROXY_RESOLVER_SCRIPT
+            PROXY_RESOLVER_SCRIPT_EX),
+        kPacUtilityResourceName);
     if (rv != OK) {
       NOTREACHED();
       return rv;
     }
 
     // Add the user's PAC code to the environment.
-    rv = RunScript(pac_data_utf8, kPacResourceName);
+    rv = RunScript(ScriptDataToV8String(pac_script), kPacResourceName);
     if (rv != OK)
       return rv;
 
@@ -214,8 +353,8 @@
     return OK;
   }
 
-  void SetCurrentRequestLoadLog(LoadLog* load_log) {
-    current_request_load_log_ = load_log;
+  void SetCurrentRequestContext(ProxyResolverRequestContext* context) {
+    js_bindings_->set_current_request_context(context);
   }
 
   void PurgeMemory() {
@@ -230,7 +369,8 @@
 
  private:
   bool GetFindProxyForURL(v8::Local<v8::Value>* function) {
-    *function = v8_context_->Global()->Get(v8::String::New("FindProxyForURL"));
+    *function = v8_context_->Global()->Get(
+        ASCIILiteralToV8String("FindProxyForURL"));
     return (*function)->IsFunction();
   }
 
@@ -241,20 +381,20 @@
 
     // Otherwise dispatch to the bindings.
     int line_number = message->GetLineNumber();
-    std::string error_message;
-    V8ObjectToString(message->Get(), &error_message);
+    string16 error_message;
+    V8ObjectToUTF16String(message->Get(), &error_message);
     js_bindings_->OnError(line_number, error_message);
   }
 
-  // Compiles and runs |script_utf8| in the current V8 context.
+  // Compiles and runs |script| in the current V8 context.
   // Returns OK on success, otherwise an error code.
-  int RunScript(const std::string& script_utf8, const char* script_name) {
+  int RunScript(v8::Handle<v8::String> script, const char* script_name) {
     v8::TryCatch try_catch;
 
     // Compile the script.
-    v8::Local<v8::String> text = StdStringToV8String(script_utf8);
-    v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New(script_name));
-    v8::Local<v8::Script> code = v8::Script::Compile(text, &origin);
+    v8::ScriptOrigin origin =
+        v8::ScriptOrigin(ASCIILiteralToV8String(script_name));
+    v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
 
     // Execute.
     if (!code.IsEmpty())
@@ -276,11 +416,11 @@
 
     // Like firefox we assume "undefined" if no argument was specified, and
     // disregard any arguments beyond the first.
-    std::string message;
+    string16 message;
     if (args.Length() == 0) {
-      message = "undefined";
+      message = ASCIIToUTF16("undefined");
     } else {
-      if (!V8ObjectToString(args[0], &message))
+      if (!V8ObjectToUTF16String(args[0], &message))
         return v8::Undefined();  // toString() threw an exception.
     }
 
@@ -293,19 +433,20 @@
     Context* context =
         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
 
-    LoadLog::BeginEvent(context->current_request_load_log_,
-                        LoadLog::TYPE_PROXY_RESOLVER_V8_MY_IP_ADDRESS);
+    std::string result;
+    bool success;
 
-    // We shouldn't be called with any arguments, but will not complain if
-    // we are.
-    std::string result = context->js_bindings_->MyIpAddress();
+    {
+      v8::Unlocker unlocker;
 
-    LoadLog::EndEvent(context->current_request_load_log_,
-                      LoadLog::TYPE_PROXY_RESOLVER_V8_MY_IP_ADDRESS);
+      // We shouldn't be called with any arguments, but will not complain if
+      // we are.
+      success = context->js_bindings_->MyIpAddress(&result);
+    }
 
-    if (result.empty())
-      result = "127.0.0.1";
-    return StdStringToV8String(result);
+    if (!success)
+      return ASCIILiteralToV8String("127.0.0.1");
+    return ASCIIStringToV8String(result);
   }
 
   // V8 callback for when "myIpAddressEx()" is invoked by the PAC script.
@@ -314,17 +455,20 @@
     Context* context =
         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
 
-    LoadLog::BeginEvent(context->current_request_load_log_,
-                        LoadLog::TYPE_PROXY_RESOLVER_V8_MY_IP_ADDRESS_EX);
+    std::string ip_address_list;
+    bool success;
 
-    // We shouldn't be called with any arguments, but will not complain if
-    // we are.
-    std::string result = context->js_bindings_->MyIpAddressEx();
+    {
+      v8::Unlocker unlocker;
 
-    LoadLog::EndEvent(context->current_request_load_log_,
-                      LoadLog::TYPE_PROXY_RESOLVER_V8_MY_IP_ADDRESS_EX);
+      // We shouldn't be called with any arguments, but will not complain if
+      // we are.
+      success = context->js_bindings_->MyIpAddressEx(&ip_address_list);
+    }
 
-    return StdStringToV8String(result);
+    if (!success)
+      ip_address_list = std::string();
+    return ASCIIStringToV8String(ip_address_list);
   }
 
   // V8 callback for when "dnsResolve()" is invoked by the PAC script.
@@ -332,25 +476,20 @@
     Context* context =
         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
 
-    // We need at least one argument.
-    std::string host;
-    if (args.Length() == 0) {
-      host = "undefined";
-    } else {
-      if (!V8ObjectToString(args[0], &host))
-        return v8::Undefined();
+    // We need at least one string argument.
+    std::string hostname;
+    if (!GetHostnameArgument(args, &hostname))
+      return v8::Null();
+
+    std::string ip_address;
+    bool success;
+
+    {
+      v8::Unlocker unlocker;
+      success = context->js_bindings_->DnsResolve(hostname, &ip_address);
     }
 
-    LoadLog::BeginEvent(context->current_request_load_log_,
-                        LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE);
-
-    std::string result = context->js_bindings_->DnsResolve(host);
-
-    LoadLog::EndEvent(context->current_request_load_log_,
-                      LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE);
-
-    // DnsResolve() returns empty string on failure.
-    return result.empty() ? v8::Null() : StdStringToV8String(result);
+    return success ? ASCIIStringToV8String(ip_address) : v8::Null();
   }
 
   // V8 callback for when "dnsResolveEx()" is invoked by the PAC script.
@@ -358,28 +497,27 @@
     Context* context =
         static_cast<Context*>(v8::External::Cast(*args.Data())->Value());
 
-    // We need at least one argument.
-    std::string host;
-    if (args.Length() == 0) {
-      host = "undefined";
-    } else {
-      if (!V8ObjectToString(args[0], &host))
-        return v8::Undefined();
+    // We need at least one string argument.
+    std::string hostname;
+    if (!GetHostnameArgument(args, &hostname))
+      return v8::Undefined();
+
+    std::string ip_address_list;
+    bool success;
+
+    {
+      v8::Unlocker unlocker;
+      success = context->js_bindings_->DnsResolveEx(hostname,
+                                                    &ip_address_list);
     }
 
-    LoadLog::BeginEvent(context->current_request_load_log_,
-                        LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE_EX);
+    if (!success)
+      ip_address_list = std::string();
 
-    std::string result = context->js_bindings_->DnsResolveEx(host);
-
-    LoadLog::EndEvent(context->current_request_load_log_,
-                      LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE_EX);
-
-    return StdStringToV8String(result);
+    return ASCIIStringToV8String(ip_address_list);
   }
 
   ProxyResolverJSBindings* js_bindings_;
-  LoadLog* current_request_load_log_;
   v8::Persistent<v8::External> v8_this_;
   v8::Persistent<v8::Context> v8_context_;
 };
@@ -398,16 +536,29 @@
                                     ProxyInfo* results,
                                     CompletionCallback* /*callback*/,
                                     RequestHandle* /*request*/,
-                                    LoadLog* load_log) {
+                                    const BoundNetLog& net_log) {
   // If the V8 instance has not been initialized (either because
   // SetPacScript() wasn't called yet, or because it failed.
   if (!context_.get())
     return ERR_FAILED;
 
+  // Associate some short-lived context with this request. This context will be
+  // available to any of the javascript "bindings" that are subsequently invoked
+  // from the javascript.
+  //
+  // In particular, we create a HostCache that is aggressive about caching
+  // failed DNS resolves.
+  HostCache host_cache(
+      50,
+      base::TimeDelta::FromMinutes(5),
+      base::TimeDelta::FromMinutes(5));
+
+  ProxyResolverRequestContext request_context(&net_log, &host_cache);
+
   // Otherwise call into V8.
-  context_->SetCurrentRequestLoadLog(load_log);
+  context_->SetCurrentRequestContext(&request_context);
   int rv = context_->ResolveProxy(query_url, results);
-  context_->SetCurrentRequestLoadLog(NULL);
+  context_->SetCurrentRequestContext(NULL);
 
   return rv;
 }
@@ -421,16 +572,21 @@
   context_->PurgeMemory();
 }
 
-int ProxyResolverV8::SetPacScript(const GURL& /*url*/,
-                                  const std::string& bytes_utf8,
-                                  CompletionCallback* /*callback*/) {
+void ProxyResolverV8::Shutdown() {
+  js_bindings_->Shutdown();
+}
+
+int ProxyResolverV8::SetPacScript(
+    const scoped_refptr<ProxyResolverScriptData>& script_data,
+    CompletionCallback* /*callback*/) {
+  DCHECK(script_data.get());
   context_.reset();
-  if (bytes_utf8.empty())
+  if (script_data->utf16().empty())
     return ERR_PAC_SCRIPT_FAILED;
 
   // Try parsing the PAC script.
   scoped_ptr<Context> context(new Context(js_bindings_.get()));
-  int rv = context->InitV8(bytes_utf8);
+  int rv = context->InitV8(script_data);
   if (rv == OK)
     context_.reset(context.release());
   return rv;
diff --git a/net/proxy/proxy_resolver_v8.h b/net/proxy/proxy_resolver_v8.h
index 236beab..8d7a231 100644
--- a/net/proxy/proxy_resolver_v8.h
+++ b/net/proxy/proxy_resolver_v8.h
@@ -5,8 +5,6 @@
 #ifndef NET_PROXY_PROXY_RESOLVER_V8_H_
 #define NET_PROXY_PROXY_RESOLVER_V8_H_
 
-#include <string>
-
 #include "base/scoped_ptr.h"
 #include "net/proxy/proxy_resolver.h"
 
@@ -49,9 +47,13 @@
                              ProxyInfo* results,
                              CompletionCallback* /*callback*/,
                              RequestHandle* /*request*/,
-                             LoadLog* load_log);
+                             const BoundNetLog& net_log);
   virtual void CancelRequest(RequestHandle request);
   virtual void PurgeMemory();
+  virtual void Shutdown();
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& script_data,
+      CompletionCallback* /*callback*/);
 
   ProxyResolverJSBindings* js_bindings() const { return js_bindings_.get(); }
 
@@ -60,11 +62,6 @@
   // script. It corresponds with the data from the last call to
   // SetPacScript().
   class Context;
-
-  // ProxyResolver implementation:
-  virtual int SetPacScript(const GURL& /*pac_url*/,
-                           const std::string& bytes_utf8,
-                           CompletionCallback* /*callback*/);
   scoped_ptr<Context> context_;
 
   scoped_ptr<ProxyResolverJSBindings> js_bindings_;
diff --git a/net/proxy/proxy_resolver_v8_unittest.cc b/net/proxy/proxy_resolver_v8_unittest.cc
index ee536b0..f0bfa1d 100644
--- a/net/proxy/proxy_resolver_v8_unittest.cc
+++ b/net/proxy/proxy_resolver_v8_unittest.cc
@@ -3,11 +3,12 @@
 // found in the LICENSE file.
 
 #include "base/file_util.h"
-#include "base/string_util.h"
 #include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 #include "googleurl/src/gurl.h"
-#include "net/base/load_log_unittest.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log_unittest.h"
 #include "net/proxy/proxy_info.h"
 #include "net/proxy/proxy_resolver_js_bindings.h"
 #include "net/proxy/proxy_resolver_v8.h"
@@ -23,39 +24,46 @@
  public:
   MockJSBindings() : my_ip_address_count(0), my_ip_address_ex_count(0) {}
 
-  virtual void Alert(const std::string& message) {
+  virtual void Alert(const string16& message) {
     LOG(INFO) << "PAC-alert: " << message;  // Helpful when debugging.
-    alerts.push_back(message);
+    alerts.push_back(UTF16ToUTF8(message));
   }
 
-  virtual std::string MyIpAddress() {
+  virtual bool MyIpAddress(std::string* ip_address) {
     my_ip_address_count++;
-    return my_ip_address_result;
+    *ip_address = my_ip_address_result;
+    return !my_ip_address_result.empty();
   }
 
-  virtual std::string MyIpAddressEx() {
+  virtual bool MyIpAddressEx(std::string* ip_address_list) {
     my_ip_address_ex_count++;
-    return my_ip_address_ex_result;
+    *ip_address_list = my_ip_address_ex_result;
+    return !my_ip_address_ex_result.empty();
   }
 
-  virtual std::string DnsResolve(const std::string& host) {
+  virtual bool DnsResolve(const std::string& host, std::string* ip_address) {
     dns_resolves.push_back(host);
-    return dns_resolve_result;
+    *ip_address = dns_resolve_result;
+    return !dns_resolve_result.empty();
   }
 
-  virtual std::string DnsResolveEx(const std::string& host) {
+  virtual bool DnsResolveEx(const std::string& host,
+                            std::string* ip_address_list) {
     dns_resolves_ex.push_back(host);
-    return dns_resolve_ex_result;
+    *ip_address_list = dns_resolve_ex_result;
+    return !dns_resolve_ex_result.empty();
   }
 
-  virtual void OnError(int line_number, const std::string& message) {
+  virtual void OnError(int line_number, const string16& message) {
     // Helpful when debugging.
     LOG(INFO) << "PAC-error: [" << line_number << "] " << message;
 
-    errors.push_back(message);
+    errors.push_back(UTF16ToUTF8(message));
     errors_line_number.push_back(line_number);
   }
 
+  virtual void Shutdown() {}
+
   // Mock values to return.
   std::string my_ip_address_result;
   std::string my_ip_address_ex_result;
@@ -103,7 +111,8 @@
     }
 
     // Load the PAC script into the ProxyResolver.
-    return SetPacScriptByData(file_contents, NULL);
+    return SetPacScript(ProxyResolverScriptData::FromUTF8(file_contents),
+                        NULL);
   }
 };
 
@@ -118,8 +127,9 @@
   EXPECT_EQ(OK, result);
 
   ProxyInfo proxy_info;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, log);
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   log.bound());
 
   EXPECT_EQ(OK, result);
   EXPECT_TRUE(proxy_info.is_direct());
@@ -128,7 +138,7 @@
   EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
 
   // No bindings were called, so no log entries.
-  EXPECT_EQ(0u, log->entries().size());
+  EXPECT_EQ(0u, log.entries().size());
 }
 
 TEST(ProxyResolverV8Test, ReturnEmptyString) {
@@ -137,7 +147,8 @@
   EXPECT_EQ(OK, result);
 
   ProxyInfo proxy_info;
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
 
   EXPECT_EQ(OK, result);
   EXPECT_TRUE(proxy_info.is_direct());
@@ -157,7 +168,7 @@
   {
     ProxyInfo proxy_info;
     result = resolver.GetProxyForURL(GURL("http://query.com/path"),
-                                         &proxy_info, NULL, NULL, NULL);
+                                     &proxy_info, NULL, NULL, BoundNetLog());
     EXPECT_EQ(OK, result);
     EXPECT_EQ("http.query.com.path.query.com:80",
               proxy_info.proxy_server().ToURI());
@@ -165,7 +176,8 @@
   {
     ProxyInfo proxy_info;
     int result = resolver.GetProxyForURL(GURL("ftp://query.com:90/path"),
-                                         &proxy_info, NULL, NULL, NULL);
+                                         &proxy_info, NULL, NULL,
+                                         BoundNetLog());
     EXPECT_EQ(OK, result);
     // Note that FindProxyForURL(url, host) does not expect |host| to contain
     // the port number.
@@ -203,7 +215,8 @@
     EXPECT_EQ(OK, result);
 
     ProxyInfo proxy_info;
-    result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+    result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                     BoundNetLog());
 
     EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
 
@@ -223,7 +236,8 @@
   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
 
   ProxyInfo proxy_info;
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
 
   EXPECT_EQ(ERR_FAILED, result);
 }
@@ -235,7 +249,8 @@
   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
 
   ProxyInfo proxy_info;
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
 
   EXPECT_EQ(ERR_FAILED, result);
 
@@ -247,7 +262,7 @@
 
   EXPECT_EQ("Uncaught SyntaxError: Unexpected end of input",
             bindings->errors[0]);
-  EXPECT_EQ(-1, bindings->errors_line_number[0]);
+  EXPECT_EQ(0, bindings->errors_line_number[0]);
 }
 
 // Run a PAC script several times, which has side-effects.
@@ -258,7 +273,8 @@
   // The PAC script increments a counter each time we invoke it.
   for (int i = 0; i < 3; ++i) {
     ProxyInfo proxy_info;
-    result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+    result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                     BoundNetLog());
     EXPECT_EQ(OK, result);
     EXPECT_EQ(StringPrintf("sideffect_%d:80", i),
               proxy_info.proxy_server().ToURI());
@@ -271,7 +287,8 @@
 
   for (int i = 0; i < 3; ++i) {
     ProxyInfo proxy_info;
-    result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+    result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                     BoundNetLog());
     EXPECT_EQ(OK, result);
     EXPECT_EQ(StringPrintf("sideffect_%d:80", i),
               proxy_info.proxy_server().ToURI());
@@ -285,7 +302,8 @@
   EXPECT_EQ(OK, result);
 
   ProxyInfo proxy_info;
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
 
   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
 
@@ -297,18 +315,17 @@
   EXPECT_EQ(3, bindings->errors_line_number[0]);
 }
 
-// TODO(eroman): This test is disabed right now, since the parsing of
-// host/port doesn't check for non-ascii characters.
-TEST(ProxyResolverV8Test, DISABLED_ReturnUnicode) {
+TEST(ProxyResolverV8Test, ReturnUnicode) {
   ProxyResolverV8WithMockBindings resolver;
   int result = resolver.SetPacScriptFromDisk("return_unicode.js");
   EXPECT_EQ(OK, result);
 
   ProxyInfo proxy_info;
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
 
   // The result from this resolve was unparseable, because it
-  // wasn't ascii.
+  // wasn't ASCII.
   EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
 }
 
@@ -319,7 +336,8 @@
   EXPECT_EQ(OK, result);
 
   ProxyInfo proxy_info;
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
 
   // If the javascript side of this unit-test fails, it will throw a javascript
   // exception. Otherwise it will return "PROXY success:80".
@@ -337,7 +355,8 @@
   ProxyInfo proxy_info;
 
   // Resolve should fail, as we are not yet initialized with a script.
-  int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  int result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                       BoundNetLog());
   EXPECT_EQ(ERR_FAILED, result);
 
   // Initialize it.
@@ -345,20 +364,24 @@
   EXPECT_EQ(OK, result);
 
   // Resolve should now succeed.
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
   EXPECT_EQ(OK, result);
 
   // Clear it, by initializing with an empty string.
-  resolver.SetPacScriptByData(std::string(), NULL);
+  resolver.SetPacScript(
+      ProxyResolverScriptData::FromUTF16(string16()), NULL);
 
   // Resolve should fail again now.
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
   EXPECT_EQ(ERR_FAILED, result);
 
   // Load a good script once more.
   result = resolver.SetPacScriptFromDisk("direct.js");
   EXPECT_EQ(OK, result);
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
   EXPECT_EQ(OK, result);
 
   EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
@@ -368,16 +391,18 @@
 // Test marshalling/un-marshalling of values between C++/V8.
 TEST(ProxyResolverV8Test, V8Bindings) {
   ProxyResolverV8WithMockBindings resolver;
+  MockJSBindings* bindings = resolver.mock_js_bindings();
+  bindings->dns_resolve_result = "127.0.0.1";
   int result = resolver.SetPacScriptFromDisk("bindings.js");
   EXPECT_EQ(OK, result);
 
   ProxyInfo proxy_info;
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
 
   EXPECT_EQ(OK, result);
   EXPECT_TRUE(proxy_info.is_direct());
 
-  MockJSBindings* bindings = resolver.mock_js_bindings();
   EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
 
   // Alert was called 5 times.
@@ -388,20 +413,11 @@
   EXPECT_EQ("[object Object]", bindings->alerts[3]);
   EXPECT_EQ("exception from calling toString()", bindings->alerts[4]);
 
-  // DnsResolve was called 8 times.
-  ASSERT_EQ(8U, bindings->dns_resolves.size());
-  EXPECT_EQ("undefined", bindings->dns_resolves[0]);
-  EXPECT_EQ("null", bindings->dns_resolves[1]);
-  EXPECT_EQ("undefined", bindings->dns_resolves[2]);
-  EXPECT_EQ("", bindings->dns_resolves[3]);
-  EXPECT_EQ("[object Object]", bindings->dns_resolves[4]);
-  EXPECT_EQ("function fn() {}", bindings->dns_resolves[5]);
-
-  // TODO(eroman): This isn't quite right... should probably stringize
-  // to something like "['3']".
-  EXPECT_EQ("3", bindings->dns_resolves[6]);
-
-  EXPECT_EQ("arg1", bindings->dns_resolves[7]);
+  // DnsResolve was called 8 times, however only 2 of those were string
+  // parameters. (so 6 of them failed immediately).
+  ASSERT_EQ(2U, bindings->dns_resolves.size());
+  EXPECT_EQ("", bindings->dns_resolves[0]);
+  EXPECT_EQ("arg1", bindings->dns_resolves[1]);
 
   // MyIpAddress was called two times.
   EXPECT_EQ(2, bindings->my_ip_address_count);
@@ -415,38 +431,33 @@
   EXPECT_EQ("foobar", bindings->dns_resolves_ex[1]);
 }
 
-// Test that calls to the myIpAddress() and dnsResolve() bindings get
-// logged to the LoadLog parameter.
-TEST(ProxyResolverV8Test, LoadLog) {
+// Test calling a binding (myIpAddress()) from the script's global scope.
+// http://crbug.com/40026
+TEST(ProxyResolverV8Test, BindingCalledDuringInitialization) {
   ProxyResolverV8WithMockBindings resolver;
-  int result = resolver.SetPacScriptFromDisk("simple.js");
+
+  int result = resolver.SetPacScriptFromDisk("binding_from_global.js");
   EXPECT_EQ(OK, result);
 
+  MockJSBindings* bindings = resolver.mock_js_bindings();
+
+  // myIpAddress() got called during initialization of the script.
+  EXPECT_EQ(1, bindings->my_ip_address_count);
+
   ProxyInfo proxy_info;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, log);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
 
   EXPECT_EQ(OK, result);
   EXPECT_FALSE(proxy_info.is_direct());
-  EXPECT_EQ("c:100", proxy_info.proxy_server().ToURI());
+  EXPECT_EQ("127.0.0.1:80", proxy_info.proxy_server().ToURI());
 
-  // Note that dnsResolve() was never called directly, but it appears
-  // in the LoadLog. This is because it gets called indirectly by
-  // isInNet() and isResolvable().
-
-  EXPECT_EQ(6u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 0, LoadLog::TYPE_PROXY_RESOLVER_V8_MY_IP_ADDRESS));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log, 1, LoadLog::TYPE_PROXY_RESOLVER_V8_MY_IP_ADDRESS));
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 2, LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log, 3, LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE));
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 4, LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log, 5, LoadLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE));
+  // Check that no other bindings were called.
+  EXPECT_EQ(0U, bindings->errors.size());
+  ASSERT_EQ(0U, bindings->alerts.size());
+  ASSERT_EQ(0U, bindings->dns_resolves.size());
+  EXPECT_EQ(0, bindings->my_ip_address_ex_count);
+  ASSERT_EQ(0U, bindings->dns_resolves_ex.size());
 }
 
 // Try loading a PAC script that ends with a comment and has no terminal
@@ -459,8 +470,9 @@
   EXPECT_EQ(OK, result);
 
   ProxyInfo proxy_info;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, log);
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   log.bound());
 
   EXPECT_EQ(OK, result);
   EXPECT_FALSE(proxy_info.is_direct());
@@ -478,8 +490,9 @@
   EXPECT_EQ(OK, result);
 
   ProxyInfo proxy_info;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, log);
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   log.bound());
 
   EXPECT_EQ(OK, result);
   EXPECT_FALSE(proxy_info.is_direct());
@@ -496,12 +509,37 @@
   EXPECT_EQ(OK, result);
 
   ProxyInfo proxy_info;
-  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL, NULL);
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
 
   EXPECT_EQ(OK, result);
   EXPECT_FALSE(proxy_info.is_direct());
   EXPECT_EQ("success:80", proxy_info.proxy_server().ToURI());
 }
 
+TEST(ProxyResolverV8Test, DNSResolutionOfInternationDomainName) {
+  ProxyResolverV8WithMockBindings resolver;
+  int result = resolver.SetPacScriptFromDisk("international_domain_names.js");
+  EXPECT_EQ(OK, result);
+
+  // Execute FindProxyForURL().
+  ProxyInfo proxy_info;
+  result = resolver.GetProxyForURL(kQueryUrl, &proxy_info, NULL, NULL,
+                                   BoundNetLog());
+
+  EXPECT_EQ(OK, result);
+  EXPECT_TRUE(proxy_info.is_direct());
+
+  // Check that the international domain name was converted to punycode
+  // before passing it onto the bindings layer.
+  MockJSBindings* bindings = resolver.mock_js_bindings();
+
+  ASSERT_EQ(1u, bindings->dns_resolves.size());
+  EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves[0]);
+
+  ASSERT_EQ(1u, bindings->dns_resolves_ex.size());
+  EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves_ex[0]);
+}
+
 }  // namespace
 }  // namespace net
diff --git a/net/proxy/proxy_resolver_winhttp.cc b/net/proxy/proxy_resolver_winhttp.cc
index fd9efe3..d7aaae3 100644
--- a/net/proxy/proxy_resolver_winhttp.cc
+++ b/net/proxy/proxy_resolver_winhttp.cc
@@ -56,7 +56,7 @@
                                          ProxyInfo* results,
                                          CompletionCallback* /*callback*/,
                                          RequestHandle* /*request*/,
-                                         LoadLog* /*load_log*/) {
+                                         const BoundNetLog& /*net_log*/) {
   // If we don't have a WinHTTP session, then create a new one.
   if (!session_handle_ && !OpenWinHttpSession())
     return ERR_FAILED;
@@ -140,10 +140,14 @@
   NOTREACHED();
 }
 
-int ProxyResolverWinHttp::SetPacScript(const GURL& pac_url,
-                                       const std::string& /*pac_bytes*/,
-                                       CompletionCallback* /*callback*/) {
-  pac_url_ = pac_url.is_valid() ? pac_url : GURL("http://wpad/wpad.dat");
+int ProxyResolverWinHttp::SetPacScript(
+    const scoped_refptr<ProxyResolverScriptData>& script_data,
+    CompletionCallback* /*callback*/) {
+  if (script_data->type() == ProxyResolverScriptData::TYPE_AUTO_DETECT) {
+    pac_url_ = GURL("http://wpad/wpad.dat");
+  } else {
+    pac_url_ = script_data->url();
+  }
   return OK;
 }
 
diff --git a/net/proxy/proxy_resolver_winhttp.h b/net/proxy/proxy_resolver_winhttp.h
index ab1df0f..7ad40dc 100644
--- a/net/proxy/proxy_resolver_winhttp.h
+++ b/net/proxy/proxy_resolver_winhttp.h
@@ -26,14 +26,13 @@
                              ProxyInfo* results,
                              CompletionCallback* /*callback*/,
                              RequestHandle* /*request*/,
-                             LoadLog* /*load_log*/);
+                             const BoundNetLog& /*net_log*/);
   virtual void CancelRequest(RequestHandle request);
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& script_data,
+      CompletionCallback* /*callback*/);
 
  private:
-  // ProxyResolver implementation:
-  virtual int SetPacScript(const GURL& pac_url,
-                           const std::string& /*pac_bytes*/,
-                           CompletionCallback* /*callback*/);
   bool OpenWinHttpSession();
   void CloseWinHttpSession();
 
diff --git a/net/proxy/proxy_script_fetcher.cc b/net/proxy/proxy_script_fetcher.cc
index 7dc800e..719c380 100644
--- a/net/proxy/proxy_script_fetcher.cc
+++ b/net/proxy/proxy_script_fetcher.cc
@@ -1,6 +1,6 @@
-// Copyright (c) 2008 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.
+// Copyright (c) 2010 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/proxy/proxy_script_fetcher.h"
 
@@ -45,9 +45,12 @@
   return false;
 }
 
-// Convert |bytes| (which is encoded by |charset|) in place to UTF8.
+// Converts |bytes| (which is encoded by |charset|) to UTF16, saving the resul
+// to |*utf16|.
 // If |charset| is empty, then we don't know what it was and guess.
-void ConvertResponseToUTF8(const std::string& charset, std::string* bytes) {
+void ConvertResponseToUTF16(const std::string& charset,
+                            const std::string& bytes,
+                            string16* utf16) {
   const char* codepage;
 
   if (charset.empty()) {
@@ -61,12 +64,9 @@
   // We will be generous in the conversion -- if any characters lie
   // outside of |charset| (i.e. invalid), then substitute them with
   // U+FFFD rather than failing.
-  std::wstring tmp_wide;
-  base::CodepageToWide(*bytes, codepage,
-                       base::OnStringConversionError::SUBSTITUTE,
-                       &tmp_wide);
-  // TODO(eroman): would be nice to have a CodepageToUTF8() function.
-  *bytes = WideToUTF8(tmp_wide);
+  base::CodepageToUTF16(bytes, codepage,
+                        base::OnStringConversionError::SUBSTITUTE,
+                        utf16);
 }
 
 }  // namespace
@@ -83,7 +83,7 @@
 
   // ProxyScriptFetcher methods:
 
-  virtual int Fetch(const GURL& url, std::string* bytes,
+  virtual int Fetch(const GURL& url, string16* text,
                     CompletionCallback* callback);
   virtual void Cancel();
   virtual URLRequestContext* GetRequestContext();
@@ -103,7 +103,7 @@
   void ReadBody(URLRequest* request);
 
   // Called once the request has completed to notify the caller of
-  // |response_code_| and |response_bytes_|.
+  // |response_code_| and |response_text_|.
   void FetchCompleted();
 
   // Clear out the state for the current request.
@@ -140,9 +140,12 @@
   // Holds the error condition that was hit on the current request, or OK.
   int result_code_;
 
-  // Holds the bytes read so far. Will not exceed |max_response_bytes|. This
-  // buffer is owned by the owner of |callback|.
-  std::string* result_bytes_;
+  // Holds the bytes read so far. Will not exceed |max_response_bytes|.
+  std::string bytes_read_so_far_;
+
+  // This buffer is owned by the owner of |callback|, and will be filled with
+  // UTF16 response on completion.
+  string16* result_text_;
 };
 
 ProxyScriptFetcherImpl::ProxyScriptFetcherImpl(
@@ -155,7 +158,7 @@
       cur_request_id_(0),
       callback_(NULL),
       result_code_(OK),
-      result_bytes_(NULL) {
+      result_text_(NULL) {
   DCHECK(url_request_context);
 }
 
@@ -165,13 +168,13 @@
 }
 
 int ProxyScriptFetcherImpl::Fetch(const GURL& url,
-                                  std::string* bytes,
+                                  string16* text,
                                   CompletionCallback* callback) {
   // It is invalid to call Fetch() while a request is already in progress.
   DCHECK(!cur_request_.get());
 
   DCHECK(callback);
-  DCHECK(bytes);
+  DCHECK(text);
 
   cur_request_.reset(new URLRequest(url, this));
   cur_request_->set_context(url_request_context_);
@@ -186,8 +189,9 @@
 
   // Save the caller's info for notification on completion.
   callback_ = callback;
-  result_bytes_ = bytes;
-  result_bytes_->clear();
+  result_text_ = text;
+
+  bytes_read_so_far_.clear();
 
   // Post a task to timeout this request if it takes too long.
   cur_request_id_ = ++next_id_;
@@ -269,13 +273,13 @@
   DCHECK(request == cur_request_.get());
   if (num_bytes > 0) {
     // Enforce maximum size bound.
-    if (num_bytes + result_bytes_->size() >
+    if (num_bytes + bytes_read_so_far_.size() >
         static_cast<size_t>(max_response_bytes)) {
       result_code_ = ERR_FILE_TOO_BIG;
       request->Cancel();
       return;
     }
-    result_bytes_->append(buf_->data(), num_bytes);
+    bytes_read_so_far_.append(buf_->data(), num_bytes);
     ReadBody(request);
   } else {  // Error while reading, or EOF
     OnResponseCompleted(request);
@@ -305,13 +309,13 @@
 
 void ProxyScriptFetcherImpl::FetchCompleted() {
   if (result_code_ == OK) {
-    // The caller expects the response to be encoded as UTF8.
+    // The caller expects the response to be encoded as UTF16.
     std::string charset;
     cur_request_->GetCharset(&charset);
-    ConvertResponseToUTF8(charset, result_bytes_);
+    ConvertResponseToUTF16(charset, bytes_read_so_far_, result_text_);
   } else {
     // On error, the caller expects empty string for bytes.
-    result_bytes_->clear();
+    result_text_->clear();
   }
 
   int result_code = result_code_;
@@ -327,7 +331,7 @@
   cur_request_id_ = 0;
   callback_ = NULL;
   result_code_ = OK;
-  result_bytes_ = NULL;
+  result_text_ = NULL;
 }
 
 void ProxyScriptFetcherImpl::OnTimeout(int id) {
diff --git a/net/proxy/proxy_script_fetcher.h b/net/proxy/proxy_script_fetcher.h
index 09ac84e..8899cbb 100644
--- a/net/proxy/proxy_script_fetcher.h
+++ b/net/proxy/proxy_script_fetcher.h
@@ -1,6 +1,6 @@
-// Copyright (c) 2008 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.
+// Copyright (c) 2010 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.
 
 // ProxyScriptFetcher is an async interface for fetching a proxy auto config
 // script. It is specific to fetching a PAC script; enforces timeout, max-size,
@@ -9,8 +9,7 @@
 #ifndef NET_PROXY_PROXY_SCRIPT_FETCHER_H_
 #define NET_PROXY_PROXY_SCRIPT_FETCHER_H_
 
-#include <string>
-
+#include "base/string16.h"
 #include "net/base/completion_callback.h"
 #include "testing/gtest/include/gtest/gtest_prod.h"
 
@@ -25,8 +24,8 @@
   virtual ~ProxyScriptFetcher() {}
 
   // Downloads the given PAC URL, and invokes |callback| on completion.
-  // On success |callback| is executed with a result code of OK, and a
-  // string of the response bytes (as UTF8). On failure, the result bytes is
+  // On success |callback| is executed with a result code of OK, |*utf16_text|
+  // is filled with the response. On failure, the result text is
   // an empty string, and the result code is a network error. Some special
   // network errors that may occur are:
   //
@@ -39,7 +38,7 @@
   // deleting |this|), then no callback is invoked.
   //
   // Only one fetch is allowed to be outstanding at a time.
-  virtual int Fetch(const GURL& url, std::string* utf8_bytes,
+  virtual int Fetch(const GURL& url, string16* utf16_text,
                     CompletionCallback* callback) = 0;
 
   // Aborts the in-progress fetch (if any).
diff --git a/net/proxy/proxy_script_fetcher_unittest.cc b/net/proxy/proxy_script_fetcher_unittest.cc
index 741e7d4..e0e64c9 100644
--- a/net/proxy/proxy_script_fetcher_unittest.cc
+++ b/net/proxy/proxy_script_fetcher_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -24,7 +24,7 @@
 
 struct FetchResult {
   int code;
-  std::string bytes;
+  string16 text;
 };
 
 // A non-mock URL request which can access http:// and file:// urls.
@@ -32,14 +32,15 @@
  public:
   RequestContext() {
     net::ProxyConfig no_proxy;
-    host_resolver_ = net::CreateSystemHostResolver(NULL);
+    host_resolver_ =
+        net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism);
     proxy_service_ = net::ProxyService::CreateFixed(no_proxy);
     ssl_config_service_ = new net::SSLConfigServiceDefaults;
 
-    http_transaction_factory_ =
-        new net::HttpCache(net::HttpNetworkLayer::CreateFactory(
-            NULL, host_resolver_, proxy_service_, ssl_config_service_),
-            disk_cache::CreateInMemoryCacheBackend(0));
+    http_transaction_factory_ = new net::HttpCache(
+        net::HttpNetworkLayer::CreateFactory(host_resolver_, proxy_service_,
+            ssl_config_service_, NULL, NULL, NULL),
+        net::HttpCache::DefaultBackend::InMemory(0));
   }
 
  private:
@@ -70,22 +71,22 @@
       ProxyScriptFetcher::Create(context));
 
   { // Fetch a non-existent file.
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
     int result = pac_fetcher->Fetch(GetTestFileUrl("does-not-exist"),
-                                    &bytes, &callback);
+                                    &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(ERR_FILE_NOT_FOUND, callback.WaitForResult());
-    EXPECT_TRUE(bytes.empty());
+    EXPECT_TRUE(text.empty());
   }
   { // Fetch a file that exists.
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
     int result = pac_fetcher->Fetch(GetTestFileUrl("pac.txt"),
-                                    &bytes, &callback);
+                                    &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(OK, callback.WaitForResult());
-    EXPECT_EQ("-pac.txt-\n", bytes);
+    EXPECT_EQ(ASCIIToUTF16("-pac.txt-\n"), text);
   }
 }
 
@@ -101,30 +102,30 @@
 
   { // Fetch a PAC with mime type "text/plain"
     GURL url = server->TestServerPage("files/pac.txt");
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(OK, callback.WaitForResult());
-    EXPECT_EQ("-pac.txt-\n", bytes);
+    EXPECT_EQ(ASCIIToUTF16("-pac.txt-\n"), text);
   }
   { // Fetch a PAC with mime type "text/html"
     GURL url = server->TestServerPage("files/pac.html");
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(OK, callback.WaitForResult());
-    EXPECT_EQ("-pac.html-\n", bytes);
+    EXPECT_EQ(ASCIIToUTF16("-pac.html-\n"), text);
   }
   { // Fetch a PAC with mime type "application/x-ns-proxy-autoconfig"
     GURL url = server->TestServerPage("files/pac.nsproxy");
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(OK, callback.WaitForResult());
-    EXPECT_EQ("-pac.nsproxy-\n", bytes);
+    EXPECT_EQ(ASCIIToUTF16("-pac.nsproxy-\n"), text);
   }
 }
 
@@ -138,21 +139,21 @@
 
   { // Fetch a PAC which gives a 500 -- FAIL
     GURL url = server->TestServerPage("files/500.pac");
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(ERR_PAC_STATUS_NOT_OK, callback.WaitForResult());
-    EXPECT_TRUE(bytes.empty());
+    EXPECT_TRUE(text.empty());
   }
   { // Fetch a PAC which gives a 404 -- FAIL
     GURL url = server->TestServerPage("files/404.pac");
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(ERR_PAC_STATUS_NOT_OK, callback.WaitForResult());
-    EXPECT_TRUE(bytes.empty());
+    EXPECT_TRUE(text.empty());
   }
 }
 
@@ -167,12 +168,12 @@
   // Fetch PAC scripts via HTTP with a Content-Disposition header -- should
   // have no effect.
   GURL url = server->TestServerPage("files/downloadable.pac");
-  std::string bytes;
+  string16 text;
   TestCompletionCallback callback;
-  int result = pac_fetcher->Fetch(url, &bytes, &callback);
+  int result = pac_fetcher->Fetch(url, &text, &callback);
   EXPECT_EQ(ERR_IO_PENDING, result);
   EXPECT_EQ(OK, callback.WaitForResult());
-  EXPECT_EQ("-downloadable.pac-\n", bytes);
+  EXPECT_EQ(ASCIIToUTF16("-downloadable.pac-\n"), text);
 }
 
 TEST_F(ProxyScriptFetcherTest, NoCache) {
@@ -186,12 +187,12 @@
   // Fetch a PAC script whose HTTP headers make it cacheable for 1 hour.
   GURL url = server->TestServerPage("files/cacheable_1hr.pac");
   {
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(OK, callback.WaitForResult());
-    EXPECT_EQ("-cacheable_1hr.pac-\n", bytes);
+    EXPECT_EQ(ASCIIToUTF16("-cacheable_1hr.pac-\n"), text);
   }
 
   // Now kill the HTTP server.
@@ -202,9 +203,9 @@
   // running anymore. (If it were instead being loaded from cache, we would
   // get a success.
   {
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(ERR_CONNECTION_REFUSED, callback.WaitForResult());
   }
@@ -231,12 +232,12 @@
   // after 50 bytes have been read, and fail with a too large error.
   for (size_t i = 0; i < arraysize(urls); ++i) {
     const GURL& url = urls[i];
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(ERR_FILE_TOO_BIG, callback.WaitForResult());
-    EXPECT_TRUE(bytes.empty());
+    EXPECT_TRUE(text.empty());
   }
 
   // Restore the original size bound.
@@ -244,12 +245,12 @@
 
   { // Make sure we can still fetch regular URLs.
     GURL url = server->TestServerPage("files/pac.nsproxy");
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(OK, callback.WaitForResult());
-    EXPECT_EQ("-pac.nsproxy-\n", bytes);
+    EXPECT_EQ(ASCIIToUTF16("-pac.nsproxy-\n"), text);
   }
 }
 
@@ -268,12 +269,12 @@
   // Try fetching a URL which takes 1.2 seconds. We should abort the request
   // after 500 ms, and fail with a timeout error.
   { GURL url = server->TestServerPage("slow/proxy.pac?1.2");
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(ERR_TIMED_OUT, callback.WaitForResult());
-    EXPECT_TRUE(bytes.empty());
+    EXPECT_TRUE(text.empty());
   }
 
   // Restore the original timeout period.
@@ -281,12 +282,12 @@
 
   { // Make sure we can still fetch regular URLs.
     GURL url = server->TestServerPage("files/pac.nsproxy");
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(OK, callback.WaitForResult());
-    EXPECT_EQ("-pac.nsproxy-\n", bytes);
+    EXPECT_EQ(ASCIIToUTF16("-pac.nsproxy-\n"), text);
   }
 }
 
@@ -304,24 +305,24 @@
   // Test a response that is gzip-encoded -- should get inflated.
   {
     GURL url = server->TestServerPage("files/gzipped_pac");
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(OK, callback.WaitForResult());
-    EXPECT_EQ("This data was gzipped.\n", bytes);
+    EXPECT_EQ(ASCIIToUTF16("This data was gzipped.\n"), text);
   }
 
   // Test a response that was served as UTF-16 (BE). It should
   // be converted to UTF8.
   {
     GURL url = server->TestServerPage("files/utf16be_pac");
-    std::string bytes;
+    string16 text;
     TestCompletionCallback callback;
-    int result = pac_fetcher->Fetch(url, &bytes, &callback);
+    int result = pac_fetcher->Fetch(url, &text, &callback);
     EXPECT_EQ(ERR_IO_PENDING, result);
     EXPECT_EQ(OK, callback.WaitForResult());
-    EXPECT_EQ("This was encoded as UTF-16BE.\n", bytes);
+    EXPECT_EQ(ASCIIToUTF16("This was encoded as UTF-16BE.\n"), text);
   }
 }
 
diff --git a/net/proxy/proxy_server.cc b/net/proxy/proxy_server.cc
index 6c47fee..b78c347 100644
--- a/net/proxy/proxy_server.cc
+++ b/net/proxy/proxy_server.cc
@@ -34,6 +34,8 @@
     return ProxyServer::SCHEME_SOCKS5;
   if (LowerCaseEqualsASCII(begin, end, "direct"))
     return ProxyServer::SCHEME_DIRECT;
+  if (LowerCaseEqualsASCII(begin, end, "https"))
+    return ProxyServer::SCHEME_HTTPS;
 
   return ProxyServer::SCHEME_INVALID;
 }
@@ -53,6 +55,8 @@
     return ProxyServer::SCHEME_SOCKS5;
   if (LowerCaseEqualsASCII(begin, end, "direct"))
     return ProxyServer::SCHEME_DIRECT;
+  if (LowerCaseEqualsASCII(begin, end, "https"))
+    return ProxyServer::SCHEME_HTTPS;
   return ProxyServer::SCHEME_INVALID;
 }
 
@@ -84,6 +88,13 @@
   return host_ + ":" + IntToString(port_);
 }
 
+HostPortPair ProxyServer::host_port_pair() const {
+  // Doesn't make sense to call this if the URI scheme doesn't
+  // have concept of a host.
+  DCHECK(is_valid() && !is_direct());
+  return HostPortPair(host_, port_);
+}
+
 // static
 ProxyServer ProxyServer::FromURI(const std::string& uri,
                                  Scheme default_scheme) {
@@ -125,6 +136,8 @@
       return std::string("socks4://") + host_and_port();
     case SCHEME_SOCKS5:
       return std::string("socks5://") + host_and_port();
+    case SCHEME_HTTPS:
+      return std::string("https://") + host_and_port();
     default:
       // Got called with an invalid scheme.
       NOTREACHED();
@@ -137,6 +150,7 @@
   return FromPacString(pac_string.begin(), pac_string.end());
 }
 
+// static
 ProxyServer ProxyServer::FromPacString(std::string::const_iterator begin,
                                        std::string::const_iterator end) {
   // Trim the leading/trailing whitespace.
@@ -172,6 +186,8 @@
       return std::string("SOCKS ") + host_and_port();
     case SCHEME_SOCKS5:
       return std::string("SOCKS5 ") + host_and_port();
+    case SCHEME_HTTPS:
+      return std::string("HTTPS ") + host_and_port();
     default:
       // Got called with an invalid scheme.
       NOTREACHED();
@@ -187,6 +203,8 @@
     case SCHEME_SOCKS4:
     case SCHEME_SOCKS5:
       return 1080;
+    case SCHEME_HTTPS:
+      return 443;
     default:
       return -1;
   }
diff --git a/net/proxy/proxy_server.h b/net/proxy/proxy_server.h
index d079396..3d5593c 100644
--- a/net/proxy/proxy_server.h
+++ b/net/proxy/proxy_server.h
@@ -12,6 +12,7 @@
 #endif
 
 #include <string>
+#include "net/base/host_port_pair.h"
 
 namespace net {
 
@@ -28,6 +29,7 @@
     SCHEME_HTTP    = 1 << 2,
     SCHEME_SOCKS4  = 1 << 3,
     SCHEME_SOCKS5  = 1 << 4,
+    SCHEME_HTTPS   = 1 << 5,
   };
 
   // Default copy-constructor and assignment operator are OK!
@@ -51,6 +53,9 @@
   // Returns true if this ProxyServer is an HTTP proxy.
   bool is_http() const { return scheme_ == SCHEME_HTTP; }
 
+  // Returns true if this ProxyServer is an HTTPS proxy.
+  bool is_https() const { return scheme_ == SCHEME_HTTPS; }
+
   // Returns true if this ProxyServer is a SOCKS proxy.
   bool is_socks() const {
     return scheme_ == SCHEME_SOCKS4 || scheme_ == SCHEME_SOCKS5;
@@ -65,9 +70,14 @@
   int port() const;
 
   // Returns the <host>":"<port> string for the proxy server.
+  // TODO(willchan): Remove in favor of host_port_pair().
   std::string host_and_port() const;
 
-  // Parse from an input with format:
+  // TODO(willchan): Change to const HostPortPair& after refactoring |host_| and
+  // |port_| here.
+  HostPortPair host_port_pair() const;
+
+  // Parses from an input with format:
   //   [<scheme>"://"]<server>[":"<port>]
   //
   // Both <scheme> and <port> are optional. If <scheme> is omitted, it will be
@@ -81,6 +91,7 @@
   //   "socks4://foopy"   {scheme=SOCKS4, host="foopy", port=1080}
   //   "socks5://foopy"   {scheme=SOCKS5, host="foopy", port=1080}
   //   "http://foopy:17"  {scheme=HTTP, host="foopy", port=17}
+  //   "https://foopy:17" {scheme=HTTPS, host="foopy", port=17}
   //   "direct://"        {scheme=DIRECT}
   //   "foopy:X"          INVALID -- bad port.
   static ProxyServer FromURI(const std::string& uri, Scheme default_scheme);
@@ -88,7 +99,7 @@
                              std::string::const_iterator uri_end,
                              Scheme default_scheme);
 
-  // Format as a URI string. This does the reverse of FromURI.
+  // Formats as a URI string. This does the reverse of FromURI.
   std::string ToURI() const;
 
   // Parses from a PAC string result.
@@ -102,6 +113,7 @@
   //   "PROXY foopy:19"   {scheme=HTTP, host="foopy", port=19}
   //   "DIRECT"           {scheme=DIRECT}
   //   "SOCKS5 foopy"     {scheme=SOCKS5, host="foopy", port=1080}
+  //   "HTTPS foopy:123"  {scheme=HTTPS, host="foopy", port=123}
   //   "BLAH xxx:xx"      INVALID
   static ProxyServer FromPacString(const std::string& pac_string);
   static ProxyServer FromPacString(std::string::const_iterator pac_string_begin,
@@ -124,8 +136,7 @@
                                     CFStringRef port_key);
 #endif
 
-
-  // Format as a PAC result entry. This does the reverse of FromPacString().
+  // Formats as a PAC result entry. This does the reverse of FromPacString().
   std::string ToPacString() const;
 
   // Returns the default port number for a proxy server with the specified
@@ -139,7 +150,7 @@
   }
 
  private:
-  // Create a ProxyServer given a scheme, and host/port string. If parsing the
+  // Creates a ProxyServer given a scheme, and host/port string. If parsing the
   // host/port string fails, the returned instance will be invalid.
   static ProxyServer FromSchemeHostAndPort(
       Scheme scheme,
diff --git a/net/proxy/proxy_server_unittest.cc b/net/proxy/proxy_server_unittest.cc
index 0b06152..f91ad66 100644
--- a/net/proxy/proxy_server_unittest.cc
+++ b/net/proxy/proxy_server_unittest.cc
@@ -145,6 +145,35 @@
        "foopy:10",
        "SOCKS foopy:10"
     },
+
+    // HTTPS proxy URIs:
+    {
+       "https://foopy",     // No port
+       "https://foopy:443",
+       net::ProxyServer::SCHEME_HTTPS,
+       "foopy",
+       443,
+       "foopy:443",
+       "HTTPS foopy:443"
+    },
+    {
+       "https://foopy:10",  // Non-standard port
+       "https://foopy:10",
+       net::ProxyServer::SCHEME_HTTPS,
+       "foopy",
+       10,
+       "foopy:10",
+       "HTTPS foopy:10"
+    },
+    {
+       "https://1.2.3.4:10",  // IP Address
+       "https://1.2.3.4:10",
+       net::ProxyServer::SCHEME_HTTPS,
+       "1.2.3.4",
+       10,
+       "1.2.3.4:10",
+       "HTTPS 1.2.3.4:10"
+    },
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
@@ -189,7 +218,6 @@
     "direct://xyz",  // direct is not allowed a host/port.
     "http:/",  // ambiguous, but will fail because of bad port.
     "http:",  // ambiguous, but will fail because of bad port.
-    "https://blah",  // "https" is not a valid proxy scheme.
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
@@ -259,6 +287,14 @@
        " direct  ",
        "direct://",
     },
+    {
+       "https foopy",
+       "https://foopy:443",
+    },
+    {
+       "https foopy:10",
+       "https://foopy:10",
+    },
   };
 
   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
@@ -272,6 +308,7 @@
 TEST(ProxyServerTest, FromPACStringInvalid) {
   const char* tests[] = {
     "PROXY",  // missing host/port.
+    "HTTPS",  // missing host/port.
     "SOCKS",  // missing host/port.
     "DIRECT foopy:10",  // direct cannot have host/port.
   };
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc
index fb607ec..8f9d28c 100644
--- a/net/proxy/proxy_service.cc
+++ b/net/proxy/proxy_service.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -8,13 +8,16 @@
 
 #include "base/compiler_specific.h"
 #include "base/logging.h"
+#include "base/histogram.h"
 #include "base/message_loop.h"
 #include "base/string_util.h"
 #include "googleurl/src/gurl.h"
-#include "net/base/load_log.h"
+#include "net/base/forwarding_net_log.h"
+#include "net/base/net_log.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
 #include "net/proxy/init_proxy_resolver.h"
+#include "net/proxy/multi_threaded_proxy_resolver.h"
 #include "net/proxy/proxy_config_service_fixed.h"
 #include "net/proxy/proxy_script_fetcher.h"
 #if defined(OS_WIN)
@@ -29,7 +32,7 @@
 #include "net/proxy/proxy_resolver.h"
 #include "net/proxy/proxy_resolver_js_bindings.h"
 #include "net/proxy/proxy_resolver_v8.h"
-#include "net/proxy/single_threaded_proxy_resolver.h"
+#include "net/proxy/sync_host_resolver_bridge.h"
 #include "net/url_request/url_request_context.h"
 
 using base::TimeDelta;
@@ -37,7 +40,8 @@
 
 namespace net {
 
-static const size_t kMaxNumLoadLogEntries = 100;
+static const size_t kMaxNumNetLogEntries = 100;
+static const size_t kDefaultNumPacThreads = 4;
 
 // Config getter that fails every time.
 class ProxyConfigServiceNull : public ProxyConfigService {
@@ -58,7 +62,7 @@
                              ProxyInfo* results,
                              CompletionCallback* callback,
                              RequestHandle* request,
-                             LoadLog* load_log) {
+                             const BoundNetLog& net_log) {
     return ERR_NOT_IMPLEMENTED;
   }
 
@@ -66,14 +70,72 @@
     NOTREACHED();
   }
 
- private:
-  virtual int SetPacScript(const GURL& /*pac_url*/,
-                           const std::string& /*pac_bytes*/,
-                           CompletionCallback* /*callback*/) {
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& /*script_data*/,
+      CompletionCallback* /*callback*/) {
     return ERR_NOT_IMPLEMENTED;
   }
 };
 
+// This factory creates V8ProxyResolvers with appropriate javascript bindings.
+class ProxyResolverFactoryForV8 : public ProxyResolverFactory {
+ public:
+  // |async_host_resolver|, |io_loop| and |net_log| must remain
+  // valid for the duration of our lifetime.
+  // Both |async_host_resolver| and |net_log| will only be operated on
+  // |io_loop|.
+  ProxyResolverFactoryForV8(HostResolver* async_host_resolver,
+                            MessageLoop* io_loop,
+                            NetLog* net_log)
+      : ProxyResolverFactory(true /*expects_pac_bytes*/),
+        async_host_resolver_(async_host_resolver),
+        io_loop_(io_loop),
+        forwarding_net_log_(
+            net_log ? new ForwardingNetLog(net_log, io_loop) : NULL) {
+  }
+
+  virtual ProxyResolver* CreateProxyResolver() {
+    // Create a synchronous host resolver wrapper that operates
+    // |async_host_resolver_| on |io_loop_|.
+    SyncHostResolverBridge* sync_host_resolver =
+        new SyncHostResolverBridge(async_host_resolver_, io_loop_);
+
+    ProxyResolverJSBindings* js_bindings =
+        ProxyResolverJSBindings::CreateDefault(sync_host_resolver,
+                                               forwarding_net_log_.get());
+
+    // ProxyResolverV8 takes ownership of |js_bindings|.
+    return new ProxyResolverV8(js_bindings);
+  }
+
+ private:
+  scoped_refptr<HostResolver> async_host_resolver_;
+  MessageLoop* io_loop_;
+
+  // Thread-safe wrapper around a non-threadsafe NetLog implementation. This
+  // enables the proxy resolver to emit log messages from the PAC thread.
+  scoped_ptr<ForwardingNetLog> forwarding_net_log_;
+};
+
+// Creates ProxyResolvers using a non-V8 implementation.
+class ProxyResolverFactoryForNonV8 : public ProxyResolverFactory {
+ public:
+  ProxyResolverFactoryForNonV8()
+      : ProxyResolverFactory(false /*expects_pac_bytes*/) {}
+
+  virtual ProxyResolver* CreateProxyResolver() {
+#if defined(OS_WIN)
+    return new ProxyResolverWinHttp();
+#elif defined(OS_MACOSX)
+    return new ProxyResolverMac();
+#else
+    LOG(WARNING) << "PAC support disabled because there is no fallback "
+                    "non-V8 implementation";
+    return new ProxyResolverNull();
+#endif
+  }
+};
+
 // ProxyService::PacRequest ---------------------------------------------------
 
 class ProxyService::PacRequest
@@ -83,7 +145,7 @@
              const GURL& url,
              ProxyInfo* results,
              CompletionCallback* user_callback,
-             LoadLog* load_log)
+             const BoundNetLog& net_log)
       : service_(service),
         user_callback_(user_callback),
         ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(
@@ -92,7 +154,7 @@
         url_(url),
         resolve_job_(NULL),
         config_id_(ProxyConfig::INVALID_ID),
-        load_log_(load_log) {
+        net_log_(net_log) {
     DCHECK(user_callback);
   }
 
@@ -104,7 +166,7 @@
     config_id_ = service_->config_.id();
 
     return resolver()->GetProxyForURL(
-        url_, results_, &io_callback_, &resolve_job_, load_log_);
+        url_, results_, &io_callback_, &resolve_job_, net_log_);
   }
 
   bool is_started() const {
@@ -129,7 +191,7 @@
   }
 
   void Cancel() {
-    LoadLog::AddEvent(load_log_, LoadLog::TYPE_CANCELLED);
+    net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
 
     if (is_started())
       CancelResolveJob();
@@ -139,7 +201,7 @@
     user_callback_ = NULL;
     results_ = NULL;
 
-    LoadLog::EndEvent(load_log_, LoadLog::TYPE_PROXY_SERVICE);
+    net_log_.EndEvent(NetLog::TYPE_PROXY_SERVICE, NULL);
   }
 
   // Returns true if Cancel() has been called.
@@ -158,10 +220,10 @@
     resolve_job_ = NULL;
     config_id_ = ProxyConfig::INVALID_ID;
 
-    return service_->DidFinishResolvingProxy(results_, result_code, load_log_);
+    return service_->DidFinishResolvingProxy(results_, result_code, net_log_);
   }
 
-  LoadLog* load_log() const { return load_log_; }
+  BoundNetLog* net_log() { return &net_log_; }
 
  private:
   friend class base::RefCounted<ProxyService::PacRequest>;
@@ -192,52 +254,51 @@
   GURL url_;
   ProxyResolver::RequestHandle resolve_job_;
   ProxyConfig::ID config_id_;  // The config id when the resolve was started.
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
 };
 
 // ProxyService ---------------------------------------------------------------
 
 ProxyService::ProxyService(ProxyConfigService* config_service,
                            ProxyResolver* resolver,
-                           NetworkChangeNotifier* network_change_notifier)
+                           NetLog* net_log)
     : config_service_(config_service),
       resolver_(resolver),
       next_config_id_(1),
       should_use_proxy_resolver_(false),
       ALLOW_THIS_IN_INITIALIZER_LIST(init_proxy_resolver_callback_(
           this, &ProxyService::OnInitProxyResolverComplete)),
-      network_change_notifier_(network_change_notifier) {
-  // Register to receive network change notifications.
-  if (network_change_notifier_)
-    network_change_notifier_->AddObserver(this);
+      net_log_(net_log) {
+  NetworkChangeNotifier::AddObserver(this);
 }
 
 // static
 ProxyService* ProxyService::Create(
     ProxyConfigService* proxy_config_service,
     bool use_v8_resolver,
+    size_t num_pac_threads,
     URLRequestContext* url_request_context,
-    NetworkChangeNotifier* network_change_notifier,
+    NetLog* net_log,
     MessageLoop* io_loop) {
-  ProxyResolver* proxy_resolver;
+  if (num_pac_threads == 0)
+    num_pac_threads = kDefaultNumPacThreads;
 
+  ProxyResolverFactory* sync_resolver_factory;
   if (use_v8_resolver) {
-    // Send javascript errors and alerts to LOG(INFO).
-    HostResolver* host_resolver = url_request_context->host_resolver();
-    ProxyResolverJSBindings* js_bindings =
-        ProxyResolverJSBindings::CreateDefault(host_resolver, io_loop);
-
-    proxy_resolver = new ProxyResolverV8(js_bindings);
+    sync_resolver_factory =
+        new ProxyResolverFactoryForV8(
+            url_request_context->host_resolver(),
+            io_loop,
+            net_log);
   } else {
-    proxy_resolver = CreateNonV8ProxyResolver();
+    sync_resolver_factory = new ProxyResolverFactoryForNonV8();
   }
 
-  // Wrap the (synchronous) ProxyResolver implementation in a single-threaded
-  // runner. This will dispatch requests to a threadpool of size 1.
-  proxy_resolver = new SingleThreadedProxyResolver(proxy_resolver);
+  ProxyResolver* proxy_resolver =
+      new MultiThreadedProxyResolver(sync_resolver_factory, num_pac_threads);
 
-  ProxyService* proxy_service = new ProxyService(
-      proxy_config_service, proxy_resolver, network_change_notifier);
+  ProxyService* proxy_service =
+      new ProxyService(proxy_config_service, proxy_resolver, net_log);
 
   if (proxy_resolver->expects_pac_bytes()) {
     // Configure PAC script downloads to be issued using |url_request_context|.
@@ -251,14 +312,15 @@
 
 // static
 ProxyService* ProxyService::CreateFixed(const ProxyConfig& pc) {
-  return Create(new ProxyConfigServiceFixed(pc), false, NULL, NULL, NULL);
+  // TODO(eroman): This isn't quite right, won't work if |pc| specifies
+  //               a PAC script.
+  return Create(new ProxyConfigServiceFixed(pc), false, 0, NULL, NULL, NULL);
 }
 
 // static
 ProxyService* ProxyService::CreateNull() {
   // Use a configuration fetcher and proxy resolver which always fail.
-  return new ProxyService(new ProxyConfigServiceNull,
-                          new ProxyResolverNull,
+  return new ProxyService(new ProxyConfigServiceNull, new ProxyResolverNull,
                           NULL);
 }
 
@@ -266,10 +328,10 @@
                                ProxyInfo* result,
                                CompletionCallback* callback,
                                PacRequest** pac_request,
-                               LoadLog* load_log) {
+                               const BoundNetLog& net_log) {
   DCHECK(callback);
 
-  LoadLog::BeginEvent(load_log, LoadLog::TYPE_PROXY_SERVICE);
+  net_log.BeginEvent(NetLog::TYPE_PROXY_SERVICE, NULL);
 
   // Strip away any reference fragments and the username/password, as they
   // are not relevant to proxy resolution.
@@ -277,13 +339,13 @@
 
   // Check if the request can be completed right away. This is the case when
   // using a direct connection, or when the config is bad.
-  UpdateConfigIfOld(load_log);
+  UpdateConfigIfOld(net_log);
   int rv = TryToCompleteSynchronously(url, result);
   if (rv != ERR_IO_PENDING)
-    return DidFinishResolvingProxy(result, rv, load_log);
+    return DidFinishResolvingProxy(result, rv, net_log);
 
   scoped_refptr<PacRequest> req =
-      new PacRequest(this, url, result, callback, load_log);
+      new PacRequest(this, url, result, callback, net_log);
 
   bool resolver_is_ready = !IsInitializingProxyResolver();
 
@@ -293,8 +355,8 @@
     if (rv != ERR_IO_PENDING)
       return req->QueryDidComplete(rv);
   } else {
-    LoadLog::BeginEvent(req->load_log(),
-        LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC);
+    req->net_log()->BeginEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC,
+                               NULL);
   }
 
   DCHECK_EQ(ERR_IO_PENDING, rv);
@@ -319,52 +381,13 @@
     return ERR_IO_PENDING;
   }
 
-  if (!config_.proxy_rules.empty()) {
-    ApplyProxyRules(url, config_.proxy_rules, result);
-    return OK;
-  }
-
-  // otherwise, we have no proxy config
-  result->UseDirect();
+  // Use the manual proxy settings.
+  config_.proxy_rules().Apply(url, result);
   return OK;
 }
 
-void ProxyService::ApplyProxyRules(const GURL& url,
-                                   const ProxyConfig::ProxyRules& proxy_rules,
-                                   ProxyInfo* result) {
-  DCHECK(!proxy_rules.empty());
-
-  if (ShouldBypassProxyForURL(url)) {
-    result->UseDirect();
-    return;
-  }
-
-  switch (proxy_rules.type) {
-    case ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY:
-      result->UseProxyServer(proxy_rules.single_proxy);
-      break;
-    case ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME: {
-      const ProxyServer* entry = proxy_rules.MapUrlSchemeToProxy(url.scheme());
-      if (entry) {
-        result->UseProxyServer(*entry);
-      } else {
-        // We failed to find a matching proxy server for the current URL
-        // scheme. Default to direct.
-        result->UseDirect();
-      }
-      break;
-    }
-    default:
-      result->UseDirect();
-      NOTREACHED();
-      break;
-  }
-}
-
 ProxyService::~ProxyService() {
-  // Unregister to receive network change notifications.
-  if (network_change_notifier_)
-    network_change_notifier_->RemoveObserver(this);
+  NetworkChangeNotifier::RemoveObserver(this);
 
   // Cancel any inprogress requests.
   for (PendingRequests::iterator it = pending_requests_.begin();
@@ -372,6 +395,10 @@
        ++it) {
     (*it)->Cancel();
   }
+
+  // Make sure that InitProxyResolver gets destroyed BEFORE the
+  // CapturingNetLog it is using is deleted.
+  init_proxy_resolver_.reset();
 }
 
 void ProxyService::SuspendAllPendingRequests() {
@@ -382,8 +409,8 @@
     if (req->is_started()) {
       req->CancelResolveJob();
 
-      LoadLog::BeginEvent(req->load_log(),
-          LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC);
+      req->net_log()->BeginEvent(
+          NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, NULL);
     }
   }
 }
@@ -401,8 +428,8 @@
        ++it) {
     PacRequest* req = it->get();
     if (!req->is_started() && !req->was_cancelled()) {
-      LoadLog::EndEvent(req->load_log(),
-          LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC);
+      req->net_log()->EndEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC,
+                               NULL);
 
       // Note that we re-check for synchronous completion, in case we are
       // no longer using a ProxyResolver (can happen if we fell-back to manual).
@@ -433,14 +460,14 @@
                                             ProxyInfo* result,
                                             CompletionCallback* callback,
                                             PacRequest** pac_request,
-                                            LoadLog* load_log) {
+                                            const BoundNetLog& net_log) {
   // Check to see if we have a new config since ResolveProxy was called.  We
   // want to re-run ResolveProxy in two cases: 1) we have a new config, or 2) a
   // direct connection failed and we never tried the current config.
 
   bool re_resolve = result->config_id_ != config_.id();
   if (!re_resolve) {
-    UpdateConfig(load_log);
+    UpdateConfig(net_log);
     if (result->config_id_ != config_.id()) {
       // A new configuration!
       re_resolve = true;
@@ -450,7 +477,7 @@
     // If we have a new config or the config was never tried, we delete the
     // list of bad proxies and we try again.
     proxy_retry_info_.clear();
-    return ResolveProxy(url, result, callback, pac_request, load_log);
+    return ResolveProxy(url, result, callback, pac_request, net_log);
   }
 
   // We don't have new proxy settings to try, try to fallback to the next proxy
@@ -483,24 +510,33 @@
 
 int ProxyService::DidFinishResolvingProxy(ProxyInfo* result,
                                           int result_code,
-                                          LoadLog* load_log) {
+                                          const BoundNetLog& net_log) {
   // Log the result of the proxy resolution.
   if (result_code == OK) {
     // When full logging is enabled, dump the proxy list.
-    if (LoadLog::IsUnbounded(load_log)) {
-      LoadLog::AddString(
-          load_log,
-          std::string("Resolved proxy list: ") + result->ToPacString());
+    if (net_log.HasListener()) {
+      net_log.AddEvent(
+          NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST,
+          new NetLogStringParameter("pac_string", result->ToPacString()));
     }
+    result->DeprioritizeBadProxies(proxy_retry_info_);
   } else {
-    LoadLog::AddErrorCode(load_log, result_code);
+    net_log.AddEvent(
+        NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST,
+        new NetLogIntegerParameter("net_error", result_code));
+
+    // Fall-back to direct when the proxy resolver fails. This corresponds
+    // with a javascript runtime error in the PAC script.
+    //
+    // This implicit fall-back to direct matches Firefox 3.5 and
+    // Internet Explorer 8. For more information, see:
+    //
+    // http://www.chromium.org/developers/design-documents/proxy-settings-fallback
+    result->UseDirect();
+    result_code = OK;
   }
 
-  // Clean up the results list.
-  if (result_code == OK)
-    result->DeprioritizeBadProxies(proxy_retry_info_);
-
-  LoadLog::EndEvent(load_log, LoadLog::TYPE_PROXY_SERVICE);
+  net_log.EndEvent(NetLog::TYPE_PROXY_SERVICE, NULL);
   return result_code;
 }
 
@@ -528,7 +564,7 @@
 void ProxyService::ResetConfigService(
     ProxyConfigService* new_proxy_config_service) {
   config_service_.reset(new_proxy_config_service);
-  UpdateConfig(NULL);
+  UpdateConfig(BoundNetLog());
 }
 
 void ProxyService::PurgeMemory() {
@@ -541,7 +577,7 @@
   // start updating (normally this would happen lazily during the next
   // call to ResolveProxy()).
   config_.set_id(ProxyConfig::INVALID_ID);
-  UpdateConfig(NULL);
+  UpdateConfig(BoundNetLog());
 }
 
 // static
@@ -578,31 +614,18 @@
 #endif
 }
 
-// static
-ProxyResolver* ProxyService::CreateNonV8ProxyResolver() {
-#if defined(OS_WIN)
-  return new ProxyResolverWinHttp();
-#elif defined(OS_MACOSX)
-  return new ProxyResolverMac();
-#else
-  LOG(WARNING) << "PAC support disabled because there is no fallback "
-                  "non-V8 implementation";
-  return new ProxyResolverNull();
-#endif
-}
-
-void ProxyService::UpdateConfig(LoadLog* load_log) {
+void ProxyService::UpdateConfig(const BoundNetLog& net_log) {
   bool is_first_update = !config_has_been_initialized();
 
   ProxyConfig latest;
 
   // Fetch the proxy settings.
   TimeTicks start_time = TimeTicks::Now();
-  LoadLog::BeginEvent(load_log,
-      LoadLog::TYPE_PROXY_SERVICE_POLL_CONFIG_SERVICE_FOR_CHANGES);
+  net_log.BeginEvent(
+      NetLog::TYPE_PROXY_SERVICE_POLL_CONFIG_SERVICE_FOR_CHANGES, NULL);
   int rv = config_service_->GetProxyConfig(&latest);
-  LoadLog::EndEvent(load_log,
-      LoadLog::TYPE_PROXY_SERVICE_POLL_CONFIG_SERVICE_FOR_CHANGES);
+  net_log.EndEvent(NetLog::TYPE_PROXY_SERVICE_POLL_CONFIG_SERVICE_FOR_CHANGES,
+                   NULL);
   TimeTicks end_time = TimeTicks::Now();
 
   // Record how long the call to config_service_->GetConfig() above took.
@@ -661,115 +684,28 @@
   DCHECK(!init_proxy_resolver_.get());
 
   init_proxy_resolver_.reset(
-      new InitProxyResolver(resolver_.get(), proxy_script_fetcher_.get()));
-
-  init_proxy_resolver_log_ = new LoadLog(kMaxNumLoadLogEntries);
+      new InitProxyResolver(resolver_.get(), proxy_script_fetcher_.get(),
+                            net_log_));
 
   int rv = init_proxy_resolver_->Init(
-      config_, &init_proxy_resolver_callback_, init_proxy_resolver_log_);
+      config_, &init_proxy_resolver_callback_);
 
   if (rv != ERR_IO_PENDING)
     OnInitProxyResolverComplete(rv);
 }
 
-void ProxyService::UpdateConfigIfOld(LoadLog* load_log) {
+void ProxyService::UpdateConfigIfOld(const BoundNetLog& net_log) {
   // The overhead of calling ProxyConfigService::GetProxyConfig is very low.
   const TimeDelta kProxyConfigMaxAge = TimeDelta::FromSeconds(5);
 
   // Periodically check for a new config.
   if (!config_has_been_initialized() ||
       (TimeTicks::Now() - config_last_update_time_) > kProxyConfigMaxAge)
-    UpdateConfig(load_log);
+    UpdateConfig(net_log);
 }
 
-bool ProxyService::ShouldBypassProxyForURL(const GURL& url) {
-  std::string url_domain = url.scheme();
-  if (!url_domain.empty())
-    url_domain += "://";
-
-  url_domain += url.host();
-  // This isn't superfluous; GURL case canonicalization doesn't hit the embedded
-  // percent-encoded characters.
-  StringToLowerASCII(&url_domain);
-
-  // TODO(eroman): use GetHostAndPort().
-  std::string url_domain_and_port = url_domain + ":"
-      + IntToString(url.EffectiveIntPort());
-
-  if (config_.proxy_bypass_local_names && IsLocalName(url))
-    return true;
-
-  for(std::vector<std::string>::const_iterator i = config_.proxy_bypass.begin();
-      i != config_.proxy_bypass.end(); ++i) {
-    std::string bypass_url_domain = *i;
-
-    // The proxy server bypass list can contain entities with http/https
-    // If no scheme is specified then it indicates that all schemes are
-    // allowed for the current entry. For matching this we just use
-    // the protocol scheme of the url passed in.
-    size_t scheme_colon = bypass_url_domain.find("://");
-    if (scheme_colon == std::string::npos) {
-      std::string bypass_url_domain_with_scheme = url.scheme();
-      scheme_colon = bypass_url_domain_with_scheme.length();
-      bypass_url_domain_with_scheme += "://";
-      bypass_url_domain_with_scheme += bypass_url_domain;
-
-      bypass_url_domain = bypass_url_domain_with_scheme;
-    }
-    std::string* url_compare_reference = &url_domain;
-    size_t port_colon = bypass_url_domain.rfind(":");
-    if (port_colon > scheme_colon) {
-      // If our match pattern includes a colon followed by a digit,
-      // and either it's preceded by ']' (IPv6 with port)
-      // or has no other colon (IPv4),
-      // then match against <domain>:<port>.
-      // TODO(sdoyon): straighten this out, in particular the IPv6 brackets,
-      // and do the parsing in ProxyConfig when we do the CIDR matching
-      // mentioned below.
-      std::string::const_iterator domain_begin =
-          bypass_url_domain.begin() + scheme_colon + 3;  // after ://
-      std::string::const_iterator port_iter =
-          bypass_url_domain.begin() + port_colon;
-      std::string::const_iterator end = bypass_url_domain.end();
-      if ((port_iter + 1) < end && IsAsciiDigit(*(port_iter + 1)) &&
-          (*(port_iter - 1) == ']' ||
-           std::find(domain_begin, port_iter, ':') == port_iter))
-        url_compare_reference = &url_domain_and_port;
-    }
-
-    StringToLowerASCII(&bypass_url_domain);
-
-    if (MatchPatternASCII(*url_compare_reference, bypass_url_domain))
-      return true;
-
-    // Some systems (the Mac, for example) allow CIDR-style specification of
-    // proxy bypass for IP-specified hosts (e.g.  "10.0.0.0/8"; see
-    // http://www.tcd.ie/iss/internet/osx_proxy.php for a real-world example).
-    // That's kinda cool so we'll provide that for everyone.
-    // TODO(avi): implement here. See: http://crbug.com/9835.
-    // IP addresses ought to be canonicalized for comparison (whether
-    // with CIDR, port, or IP address alone).
-  }
-
-  return false;
-}
-
-// This matches IE's interpretation of the
-// "Bypass proxy server for local addresses" settings checkbox. Fully
-// qualified domain names or IP addresses are considered non-local,
-// regardless of what they map to.
-//
-// static
-bool ProxyService::IsLocalName(const GURL& url) {
-  const std::string& host = url.host();
-  if (host == "127.0.0.1" || host == "[::1]")
-    return true;
-  return host.find('.') == std::string::npos;
-}
 
 void ProxyService::OnIPAddressChanged() {
-  DCHECK(network_change_notifier_);
-
   // Mark the current configuration as being un-initialized.
   //
   // This will force us to re-fetch the configuration (and re-run all of
@@ -790,11 +726,11 @@
 
 int SyncProxyServiceHelper::ResolveProxy(const GURL& url,
                                          ProxyInfo* proxy_info,
-                                         LoadLog* load_log) {
+                                         const BoundNetLog& net_log) {
   DCHECK(io_message_loop_ != MessageLoop::current());
 
   io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
-      this, &SyncProxyServiceHelper::StartAsyncResolve, url, load_log));
+      this, &SyncProxyServiceHelper::StartAsyncResolve, url, net_log));
 
   event_.Wait();
 
@@ -805,11 +741,11 @@
 }
 
 int SyncProxyServiceHelper::ReconsiderProxyAfterError(
-    const GURL& url, ProxyInfo* proxy_info, LoadLog* load_log) {
+    const GURL& url, ProxyInfo* proxy_info, const BoundNetLog& net_log) {
   DCHECK(io_message_loop_ != MessageLoop::current());
 
   io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
-      this, &SyncProxyServiceHelper::StartAsyncReconsider, url, load_log));
+      this, &SyncProxyServiceHelper::StartAsyncReconsider, url, net_log));
 
   event_.Wait();
 
@@ -820,18 +756,18 @@
 }
 
 void SyncProxyServiceHelper::StartAsyncResolve(const GURL& url,
-                                               LoadLog* load_log) {
+                                               const BoundNetLog& net_log) {
   result_ = proxy_service_->ResolveProxy(
-      url, &proxy_info_, &callback_, NULL, load_log);
+      url, &proxy_info_, &callback_, NULL, net_log);
   if (result_ != net::ERR_IO_PENDING) {
     OnCompletion(result_);
   }
 }
 
 void SyncProxyServiceHelper::StartAsyncReconsider(const GURL& url,
-                                                  LoadLog* load_log) {
+                                                  const BoundNetLog& net_log) {
   result_ = proxy_service_->ReconsiderProxyAfterError(
-      url, &proxy_info_, &callback_, NULL, load_log);
+      url, &proxy_info_, &callback_, NULL, net_log);
   if (result_ != net::ERR_IO_PENDING) {
     OnCompletion(result_);
   }
diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h
index 8614d55..a702b38 100644
--- a/net/proxy/proxy_service.h
+++ b/net/proxy/proxy_service.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -13,6 +13,7 @@
 #include "base/waitable_event.h"
 #include "net/base/completion_callback.h"
 #include "net/base/network_change_notifier.h"
+#include "net/base/net_log.h"
 #include "net/proxy/proxy_server.h"
 #include "net/proxy/proxy_info.h"
 #include "testing/gtest/include/gtest/gtest_prod.h"
@@ -24,7 +25,6 @@
 namespace net {
 
 class InitProxyResolver;
-class LoadLog;
 class ProxyConfigService;
 class ProxyResolver;
 class ProxyScriptFetcher;
@@ -36,11 +36,11 @@
                      public NetworkChangeNotifier::Observer {
  public:
   // The instance takes ownership of |config_service| and |resolver|.
-  // If |network_change_notifier| is non-NULL, the proxy service will register
-  // with it to detect when the network setup has changed. This is used to
-  // decide when to re-configure the proxy discovery.
-  ProxyService(ProxyConfigService* config_service, ProxyResolver* resolver,
-               NetworkChangeNotifier* network_change_notifier);
+  // |net_log| is a possibly NULL destination to send log events to. It must
+  // remain alive for the lifetime of this ProxyService.
+  ProxyService(ProxyConfigService* config_service,
+               ProxyResolver* resolver,
+               NetLog* net_log);
 
   // Used internally to handle PAC queries.
   // TODO(eroman): consider naming this simply "Request".
@@ -64,12 +64,12 @@
   //   2.  PAC URL
   //   3.  named proxy
   //
-  // Profiling information for the request is saved to |load_log| if non-NULL.
+  // Profiling information for the request is saved to |net_log| if non-NULL.
   int ResolveProxy(const GURL& url,
                    ProxyInfo* results,
                    CompletionCallback* callback,
                    PacRequest** pac_request,
-                   LoadLog* load_log);
+                   const BoundNetLog& net_log);
 
   // This method is called after a failure to connect or resolve a host name.
   // It gives the proxy service an opportunity to reconsider the proxy to use.
@@ -82,12 +82,12 @@
   //
   // Returns ERR_FAILED if there is not another proxy config to try.
   //
-  // Profiling information for the request is saved to |load_log| if non-NULL.
+  // Profiling information for the request is saved to |net_log| if non-NULL.
   int ReconsiderProxyAfterError(const GURL& url,
                                 ProxyInfo* results,
                                 CompletionCallback* callback,
                                 PacRequest** pac_request,
-                                LoadLog* load_log);
+                                const BoundNetLog& net_log);
 
   // Call this method with a non-null |pac_request| to cancel the PAC request.
   void CancelPacRequest(PacRequest* pac_request);
@@ -108,13 +108,6 @@
   // Tells the resolver to purge any memory it does not need.
   void PurgeMemory();
 
-  // Returns the log for the most recent WPAD + PAC initialization.
-  // (This shows how much time was spent downloading and parsing the
-  // PAC scripts for the current configuration).
-  LoadLog* init_proxy_resolver_log() const {
-    return init_proxy_resolver_log_;
-  }
-
   // Returns true if we have called UpdateConfig() at least once.
   bool config_has_been_initialized() const {
     return config_.id() != ProxyConfig::INVALID_ID;
@@ -144,11 +137,25 @@
   // the proxy settings change. We take ownership of |proxy_config_service|.
   // Iff |use_v8_resolver| is true, then the V8 implementation is
   // used.
+  //
+  // |num_pac_threads| specifies the maximum number of threads to use for
+  // executing PAC scripts. Threads are created lazily on demand.
+  // If |0| is specified, then a default number of threads will be selected.
+  //
+  // Having more threads avoids stalling proxy resolve requests when the
+  // PAC script takes a while to run. This is particularly a problem when PAC
+  // scripts do synchronous DNS resolutions, since that can take on the order
+  // of seconds.
+  //
+  // However, the disadvantages of using more than 1 thread are:
+  //   (a) can cause compatibility issues for scripts that rely on side effects
+  //       between runs (such scripts should not be common though).
+  //   (b) increases the memory used by proxy resolving, as each thread will
+  //       duplicate its own script context.
+
   // |url_request_context| is only used when use_v8_resolver is true:
   // it specifies the URL request context that will be used if a PAC
   // script needs to be fetched.
-  // |network_change_notifier| may be NULL. Otherwise it will be used to
-  // signal the ProxyService when the network setup has changed.
   // |io_loop| points to the IO thread's message loop. It is only used
   // when pc is NULL.
   // ##########################################################################
@@ -159,8 +166,9 @@
   static ProxyService* Create(
       ProxyConfigService* proxy_config_service,
       bool use_v8_resolver,
+      size_t num_pac_threads,
       URLRequestContext* url_request_context,
-      NetworkChangeNotifier* network_change_notifier,
+      NetLog* net_log,
       MessageLoop* io_loop);
 
   // Convenience method that creates a proxy service using the
@@ -178,7 +186,6 @@
 
  private:
   friend class base::RefCountedThreadSafe<ProxyService>;
-  FRIEND_TEST(ProxyServiceTest, IsLocalName);
   FRIEND_TEST(ProxyServiceTest, UpdateConfigAfterFailedAutodetect);
   FRIEND_TEST(ProxyServiceTest, UpdateConfigFromPACToDirect);
   friend class PacRequest;
@@ -191,16 +198,12 @@
 
   ~ProxyService();
 
-  // Creates a proxy resolver appropriate for this platform that doesn't rely
-  // on V8.
-  static ProxyResolver* CreateNonV8ProxyResolver();
-
   // Identifies the proxy configuration.
   ProxyConfig::ID config_id() const { return config_.id(); }
 
   // Checks to see if the proxy configuration changed, and then updates config_
   // to reference the new configuration.
-  void UpdateConfig(LoadLog* load_log);
+  void UpdateConfig(const BoundNetLog& net_log);
 
   // Assign |config| as the current configuration.
   void SetConfig(const ProxyConfig& config);
@@ -210,7 +213,7 @@
   void StartInitProxyResolver();
 
   // Tries to update the configuration if it hasn't been checked in a while.
-  void UpdateConfigIfOld(LoadLog* load_log);
+  void UpdateConfigIfOld(const BoundNetLog& net_log);
 
   // Returns true if the proxy resolver is being initialized for PAC
   // (downloading PAC script(s) + testing).
@@ -228,12 +231,7 @@
   // Completing synchronously means we don't need to query ProxyResolver.
   int TryToCompleteSynchronously(const GURL& url, ProxyInfo* result);
 
-  // Set |result| with the proxy to use for |url|, based on |rules|.
-  void ApplyProxyRules(const GURL& url,
-                       const ProxyConfig::ProxyRules& rules,
-                       ProxyInfo* result);
-
-  // Cancel all of the requests sent to the ProxyResolver. These will be
+  // Cancels all of the requests sent to the ProxyResolver. These will be
   // restarted when calling ResumeAllPendingRequests().
   void SuspendAllPendingRequests();
 
@@ -251,18 +249,10 @@
   // bad entries from the results list.
   int DidFinishResolvingProxy(ProxyInfo* result,
                               int result_code,
-                              LoadLog* load_log);
+                              const BoundNetLog& net_log);
 
-  // Returns true if the URL passed in should not go through the proxy server.
-  // 1. If the proxy settings say to bypass local names, and |IsLocalName(url)|.
-  // 2. The URL matches one of the entities in the proxy bypass list.
-  bool ShouldBypassProxyForURL(const GURL& url);
-
-  // Returns true if |url| is to an intranet site (using non-FQDN as the
-  // heuristic).
-  static bool IsLocalName(const GURL& url);
-
-  // NetworkChangeNotifier::Observer methods:
+  // NetworkChangeNotifier::Observer
+  // When this is called, we re-fetch PAC scripts and re-run WPAD.
   virtual void OnIPAddressChanged();
 
   scoped_ptr<ProxyConfigService> config_service_;
@@ -301,12 +291,9 @@
   // |proxy_resolver_| must outlive |init_proxy_resolver_|.
   scoped_ptr<InitProxyResolver> init_proxy_resolver_;
 
-  // Log from the *last* time |init_proxy_resolver_.Init()| was called, or NULL.
-  scoped_refptr<LoadLog> init_proxy_resolver_log_;
-
-  // The (possibly NULL) network change notifier that we use to decide when
-  // to refetch PAC scripts or re-run WPAD.
-  NetworkChangeNotifier* const network_change_notifier_;
+  // This is the log where any events generated by |init_proxy_resolver_| are
+  // sent to.
+  NetLog* net_log_;
 
   DISALLOW_COPY_AND_ASSIGN(ProxyService);
 };
@@ -318,17 +305,20 @@
   SyncProxyServiceHelper(MessageLoop* io_message_loop,
                          ProxyService* proxy_service);
 
-  int ResolveProxy(const GURL& url, ProxyInfo* proxy_info, LoadLog* load_log);
+  int ResolveProxy(const GURL& url,
+                   ProxyInfo* proxy_info,
+                   const BoundNetLog& net_log);
   int ReconsiderProxyAfterError(const GURL& url,
-                                ProxyInfo* proxy_info, LoadLog* load_log);
+                                ProxyInfo* proxy_info,
+                                const BoundNetLog& net_log);
 
  private:
   friend class base::RefCountedThreadSafe<SyncProxyServiceHelper>;
 
   ~SyncProxyServiceHelper() {}
 
-  void StartAsyncResolve(const GURL& url, LoadLog* load_log);
-  void StartAsyncReconsider(const GURL& url, LoadLog* load_log);
+  void StartAsyncResolve(const GURL& url, const BoundNetLog& net_log);
+  void StartAsyncReconsider(const GURL& url, const BoundNetLog& net_log);
 
   void OnCompletion(int result);
 
diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc
index 2e57e51..a69b66e 100644
--- a/net/proxy/proxy_service_unittest.cc
+++ b/net/proxy/proxy_service_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -10,9 +10,8 @@
 #include "base/logging.h"
 #include "base/string_util.h"
 #include "googleurl/src/gurl.h"
-#include "net/base/load_log.h"
-#include "net/base/load_log_unittest.h"
-#include "net/base/mock_network_change_notifier.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "net/proxy/mock_proxy_resolver.h"
@@ -31,7 +30,7 @@
   MockProxyConfigService() {}  // Direct connect.
   explicit MockProxyConfigService(const ProxyConfig& pc) : config(pc) {}
   explicit MockProxyConfigService(const std::string& pac_url) {
-    config.pac_url = GURL(pac_url);
+    config.set_pac_url(GURL(pac_url));
   }
 
   virtual int GetProxyConfig(ProxyConfig* results) {
@@ -49,25 +48,25 @@
 class MockProxyScriptFetcher : public ProxyScriptFetcher {
  public:
   MockProxyScriptFetcher()
-      : pending_request_callback_(NULL), pending_request_bytes_(NULL) {
+      : pending_request_callback_(NULL), pending_request_text_(NULL) {
   }
 
   // ProxyScriptFetcher implementation.
   virtual int Fetch(const GURL& url,
-                    std::string* bytes,
+                    string16* text,
                     CompletionCallback* callback) {
     DCHECK(!has_pending_request());
 
     // Save the caller's information, and have them wait.
     pending_request_url_ = url;
     pending_request_callback_ = callback;
-    pending_request_bytes_ = bytes;
+    pending_request_text_ = text;
     return ERR_IO_PENDING;
   }
 
-  void NotifyFetchCompletion(int result, const std::string& bytes) {
+  void NotifyFetchCompletion(int result, const std::string& ascii_text) {
     DCHECK(has_pending_request());
-    *pending_request_bytes_ = bytes;
+    *pending_request_text_ = ASCIIToUTF16(ascii_text);
     CompletionCallback* callback = pending_request_callback_;
     pending_request_callback_ = NULL;
     callback->Run(result);
@@ -86,7 +85,7 @@
  private:
   GURL pending_request_url_;
   CompletionCallback* pending_request_callback_;
-  std::string* pending_request_bytes_;
+  string16* pending_request_text_;
 };
 
 TEST(ProxyServiceTest, Direct) {
@@ -98,18 +97,19 @@
 
   ProxyInfo info;
   TestCompletionCallback callback;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  int rv = service->ResolveProxy(url, &info, &callback, NULL, log);
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  int rv = service->ResolveProxy(url, &info, &callback, NULL, log.bound());
   EXPECT_EQ(OK, rv);
   EXPECT_TRUE(resolver->pending_requests().empty());
-  EXPECT_TRUE(NULL == service->init_proxy_resolver_log());
 
   EXPECT_TRUE(info.is_direct());
 
-  // Check the LoadLog was filled correctly.
-  EXPECT_EQ(5u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_PROXY_SERVICE));
-  EXPECT_TRUE(LogContainsEndEvent(*log, 4, LoadLog::TYPE_PROXY_SERVICE));
+  // Check the NetLog was filled correctly.
+  EXPECT_EQ(5u, log.entries().size());
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 0, NetLog::TYPE_PROXY_SERVICE));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 4, NetLog::TYPE_PROXY_SERVICE));
 }
 
 TEST(ProxyServiceTest, PAC) {
@@ -125,13 +125,13 @@
 
   ProxyInfo info;
   TestCompletionCallback callback;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  int rv = service->ResolveProxy(url, &info, &callback, NULL, log);
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+
+  int rv = service->ResolveProxy(url, &info, &callback, NULL, log.bound());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
-  EXPECT_FALSE(NULL == service->init_proxy_resolver_log());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -145,14 +145,16 @@
   EXPECT_FALSE(info.is_direct());
   EXPECT_EQ("foopy:80", info.proxy_server().ToURI());
 
-  // Check the LoadLog was filled correctly.
-  EXPECT_EQ(7u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_PROXY_SERVICE));
+  // Check the NetLog was filled correctly.
+  EXPECT_EQ(7u, log.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 3, LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC));
+      log.entries(), 0, NetLog::TYPE_PROXY_SERVICE));
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 3, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 4, LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC));
-  EXPECT_TRUE(LogContainsEndEvent(*log, 6, LoadLog::TYPE_PROXY_SERVICE));
+      log.entries(), 4, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 6, NetLog::TYPE_PROXY_SERVICE));
 }
 
 // Test that the proxy resolver does not see the URL's username/password
@@ -170,11 +172,11 @@
 
   ProxyInfo info;
   TestCompletionCallback callback;
-  int rv = service->ResolveProxy(url, &info, &callback, NULL, NULL);
+  int rv = service->ResolveProxy(url, &info, &callback, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -198,11 +200,11 @@
 
   ProxyInfo info;
   TestCompletionCallback callback1;
-  int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL);
+  int rv = service->ResolveProxy(url, &info, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -220,7 +222,8 @@
   // left to fallback to, since our proxy list was NOT terminated by
   // DIRECT.
   TestCompletionCallback callback2;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL,
+                                          BoundNetLog());
   // ReconsiderProxyAfterError returns error indicating nothing left.
   EXPECT_EQ(ERR_FAILED, rv);
   EXPECT_TRUE(info.is_empty());
@@ -255,11 +258,11 @@
 
   ProxyInfo info;
   TestCompletionCallback callback1;
-  int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL);
+  int rv = service->ResolveProxy(url, &info, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -275,27 +278,31 @@
 
   // Fallback 1.
   TestCompletionCallback callback2;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(OK, rv);
   EXPECT_FALSE(info.is_direct());
   EXPECT_EQ("foobar:10", info.proxy_server().ToURI());
 
   // Fallback 2.
   TestCompletionCallback callback3;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback3, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback3, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(OK, rv);
   EXPECT_TRUE(info.is_direct());
 
   // Fallback 3.
   TestCompletionCallback callback4;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback4, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback4, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(OK, rv);
   EXPECT_FALSE(info.is_direct());
   EXPECT_EQ("foobar:20", info.proxy_server().ToURI());
 
   // Fallback 4 -- Nothing to fall back to!
   TestCompletionCallback callback5;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback5, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback5, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(ERR_FAILED, rv);
   EXPECT_TRUE(info.is_empty());
 }
@@ -317,11 +324,11 @@
   GURL url("http://www.google.com/");
   ProxyInfo info;
   TestCompletionCallback callback1;
-  int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL);
+  int rv = service->ResolveProxy(url, &info, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -330,12 +337,15 @@
   // Fail the first resolve request in MockAsyncProxyResolver.
   resolver->pending_requests()[0]->CompleteNow(ERR_FAILED);
 
-  EXPECT_EQ(ERR_FAILED, callback1.WaitForResult());
+  // Although the proxy resolver failed the request, ProxyService implicitly
+  // falls-back to DIRECT.
+  EXPECT_EQ(OK, callback1.WaitForResult());
+  EXPECT_TRUE(info.is_direct());
 
   // The second resolve request will try to run through the proxy resolver,
   // regardless of whether the first request failed in it.
   TestCompletionCallback callback2;
-  rv = service->ResolveProxy(url, &info, &callback2, NULL, NULL);
+  rv = service->ResolveProxy(url, &info, &callback2, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -368,11 +378,11 @@
   // Get the proxy information.
   ProxyInfo info;
   TestCompletionCallback callback1;
-  int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL);
+  int rv = service->ResolveProxy(url, &info, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -390,14 +400,15 @@
 
   // Fake an error on the proxy.
   TestCompletionCallback callback2;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(OK, rv);
 
   // The second proxy should be specified.
   EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
 
   TestCompletionCallback callback3;
-  rv = service->ResolveProxy(url, &info, &callback3, NULL, NULL);
+  rv = service->ResolveProxy(url, &info, &callback3, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -415,7 +426,8 @@
 
   // We fake another error. It should now try the third one.
   TestCompletionCallback callback4;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback4, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback4, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(OK, rv);
   EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
 
@@ -423,14 +435,16 @@
   // proxy servers we thought were valid; next we try the proxy server
   // that was in our bad proxies map (foopy1:8080).
   TestCompletionCallback callback5;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback5, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback5, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(OK, rv);
   EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
 
   // Fake another error, the last proxy is gone, the list should now be empty,
   // so there is nothing left to try.
   TestCompletionCallback callback6;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback6, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback6, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(ERR_FAILED, rv);
   EXPECT_FALSE(info.is_direct());
   EXPECT_TRUE(info.is_empty());
@@ -454,11 +468,11 @@
   // Get the proxy information.
   ProxyInfo info;
   TestCompletionCallback callback1;
-  int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL);
+  int rv = service->ResolveProxy(url, &info, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -476,7 +490,8 @@
 
   // Fake an error on the proxy.
   TestCompletionCallback callback2;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(OK, rv);
 
   // Now we get back the second proxy.
@@ -484,7 +499,8 @@
 
   // Fake an error on this proxy as well.
   TestCompletionCallback callback3;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback3, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback3, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(OK, rv);
 
   // Finally, we get back DIRECT.
@@ -492,7 +508,8 @@
 
   // Now we tell the proxy service that even DIRECT failed.
   TestCompletionCallback callback4;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback4, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback4, NULL,
+                                          BoundNetLog());
   // There was nothing left to try after DIRECT, so we are out of
   // choices.
   EXPECT_EQ(ERR_FAILED, rv);
@@ -514,11 +531,11 @@
   // Get the proxy information.
   ProxyInfo info;
   TestCompletionCallback callback1;
-  int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL);
+  int rv = service->ResolveProxy(url, &info, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -536,14 +553,15 @@
 
   // Fake an error on the proxy, and also a new configuration on the proxy.
   config_service->config = ProxyConfig();
-  config_service->config.pac_url = GURL("http://foopy-new/proxy.pac");
+  config_service->config.set_pac_url(GURL("http://foopy-new/proxy.pac"));
 
   TestCompletionCallback callback2;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy-new/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -559,21 +577,23 @@
 
   // We fake another error. It should now ignore the first one.
   TestCompletionCallback callback3;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback3, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback3, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(OK, rv);
   EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
 
   // We simulate a new configuration.
   config_service->config = ProxyConfig();
-  config_service->config.pac_url = GURL("http://foopy-new2/proxy.pac");
+  config_service->config.set_pac_url(GURL("http://foopy-new2/proxy.pac"));
 
   // We fake another error. It should go back to the first proxy.
   TestCompletionCallback callback4;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback4, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback4, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy-new2/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -603,11 +623,11 @@
   // Get the proxy information.
   ProxyInfo info;
   TestCompletionCallback callback1;
-  int rv = service->ResolveProxy(url, &info, &callback1, NULL, NULL);
+  int rv = service->ResolveProxy(url, &info, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
   ASSERT_EQ(1u, resolver->pending_requests().size());
   EXPECT_EQ(url, resolver->pending_requests()[0]->url());
@@ -623,7 +643,8 @@
 
   // Fake a proxy error.
   TestCompletionCallback callback2;
-  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info, &callback2, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(OK, rv);
 
   // The first proxy is ignored, and the second one is selected.
@@ -633,7 +654,7 @@
   // Fake a PAC failure.
   ProxyInfo info2;
   TestCompletionCallback callback3;
-  rv = service->ResolveProxy(url, &info2, &callback3, NULL, NULL);
+  rv = service->ResolveProxy(url, &info2, &callback3, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -642,17 +663,19 @@
   // This simulates a javascript runtime error in the PAC script.
   resolver->pending_requests()[0]->CompleteNow(ERR_FAILED);
 
-  // No proxy servers are returned, since we failed.
-  EXPECT_EQ(ERR_FAILED, callback3.WaitForResult());
-  EXPECT_FALSE(info2.is_direct());
-  EXPECT_TRUE(info2.is_empty());
+  // Although the resolver failed, the ProxyService will implicitly fall-back
+  // to a DIRECT connection.
+  EXPECT_EQ(OK, callback3.WaitForResult());
+  EXPECT_TRUE(info2.is_direct());
+  EXPECT_FALSE(info2.is_empty());
 
   // The PAC script will work properly next time and successfully return a
   // proxy list. Since we have not marked the configuration as bad, it should
   // "just work" the next time we call it.
   ProxyInfo info3;
   TestCompletionCallback callback4;
-  rv = service->ReconsiderProxyAfterError(url, &info3, &callback4, NULL, NULL);
+  rv = service->ReconsiderProxyAfterError(url, &info3, &callback4, NULL,
+                                          BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -670,224 +693,46 @@
 }
 
 TEST(ProxyServiceTest, ProxyBypassList) {
-  // Test what happens when a proxy bypass list is specified.
+  // Test that the proxy bypass rules are consulted.
 
-  ProxyInfo info;
+  TestCompletionCallback callback[2];
+  ProxyInfo info[2];
   ProxyConfig config;
-  config.proxy_rules.ParseFromString("foopy1:8080;foopy2:9090");
-  config.auto_detect = false;
-  config.proxy_bypass_local_names = true;
+  config.proxy_rules().ParseFromString("foopy1:8080;foopy2:9090");
+  config.set_auto_detect(false);
+  config.proxy_rules().bypass_rules.ParseFromString("*.org");
 
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config),
-        new MockAsyncProxyResolver(),
-        NULL));
-    GURL url("http://www.google.com/");
-    // Get the proxy information.
-    TestCompletionCallback callback;
-    int rv = service->ResolveProxy(url, &info, &callback, NULL, NULL);
-    EXPECT_EQ(OK, rv);
-    EXPECT_FALSE(info.is_direct());
-  }
+  scoped_refptr<ProxyService> service(new ProxyService(
+      new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
 
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config),
-        new MockAsyncProxyResolver(),
-        NULL));
-    GURL test_url("http://local");
-    TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-    EXPECT_EQ(OK, rv);
-    EXPECT_TRUE(info.is_direct());
-  }
+  int rv;
+  GURL url1("http://www.webkit.org");
+  GURL url2("http://www.webkit.com");
 
-  config.proxy_bypass.clear();
-  config.proxy_bypass.push_back("*.org");
-  config.proxy_bypass_local_names = true;
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
-    GURL test_url("http://www.webkit.org");
-    TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-    EXPECT_EQ(OK, rv);
-    EXPECT_TRUE(info.is_direct());
-  }
+  // Request for a .org domain should bypass proxy.
+  rv = service->ResolveProxy(url1, &info[0], &callback[0], NULL, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_TRUE(info[0].is_direct());
 
-  config.proxy_bypass.clear();
-  config.proxy_bypass.push_back("*.org");
-  config.proxy_bypass.push_back("7*");
-  config.proxy_bypass_local_names = true;
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
-    GURL test_url("http://74.125.19.147");
-    TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-    EXPECT_EQ(OK, rv);
-    EXPECT_TRUE(info.is_direct());
-  }
-
-  config.proxy_bypass.clear();
-  config.proxy_bypass.push_back("*.org");
-  config.proxy_bypass_local_names = true;
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
-    GURL test_url("http://www.msn.com");
-    TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-    EXPECT_EQ(OK, rv);
-    EXPECT_FALSE(info.is_direct());
-  }
-
-  config.proxy_bypass.clear();
-  config.proxy_bypass.push_back("*.MSN.COM");
-  config.proxy_bypass_local_names = true;
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
-    GURL test_url("http://www.msnbc.msn.com");
-    TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-    EXPECT_EQ(OK, rv);
-    EXPECT_TRUE(info.is_direct());
-  }
-
-  config.proxy_bypass.clear();
-  config.proxy_bypass.push_back("*.msn.com");
-  config.proxy_bypass_local_names = true;
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
-    GURL test_url("HTTP://WWW.MSNBC.MSN.COM");
-    TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-    EXPECT_EQ(OK, rv);
-    EXPECT_TRUE(info.is_direct());
-  }
+  // Request for a .com domain hits the proxy.
+  rv = service->ResolveProxy(url2, &info[1], &callback[1], NULL, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_EQ("foopy1:8080", info[1].proxy_server().ToURI());
 }
 
-TEST(ProxyServiceTest, ProxyBypassListWithPorts) {
-  // Test port specification in bypass list entries.
-  ProxyInfo info;
-  ProxyConfig config;
-  config.proxy_rules.ParseFromString("foopy1:8080;foopy2:9090");
-  config.auto_detect = false;
-  config.proxy_bypass_local_names = false;
-
-  config.proxy_bypass.clear();
-  config.proxy_bypass.push_back("*.example.com:99");
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
-    {
-      GURL test_url("http://www.example.com:99");
-      TestCompletionCallback callback;
-      int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-      EXPECT_EQ(OK, rv);
-      EXPECT_TRUE(info.is_direct());
-    }
-    {
-      GURL test_url("http://www.example.com:100");
-      TestCompletionCallback callback;
-      int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-      EXPECT_EQ(OK, rv);
-      EXPECT_FALSE(info.is_direct());
-    }
-    {
-      GURL test_url("http://www.example.com");
-      TestCompletionCallback callback;
-      int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-      EXPECT_EQ(OK, rv);
-      EXPECT_FALSE(info.is_direct());
-    }
-  }
-
-  config.proxy_bypass.clear();
-  config.proxy_bypass.push_back("*.example.com:80");
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
-    GURL test_url("http://www.example.com");
-    TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-    EXPECT_EQ(OK, rv);
-    EXPECT_TRUE(info.is_direct());
-  }
-
-  config.proxy_bypass.clear();
-  config.proxy_bypass.push_back("*.example.com");
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
-    GURL test_url("http://www.example.com:99");
-    TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-    EXPECT_EQ(OK, rv);
-    EXPECT_TRUE(info.is_direct());
-  }
-
-  // IPv6 with port.
-  config.proxy_bypass.clear();
-  config.proxy_bypass.push_back("[3ffe:2a00:100:7031::1]:99");
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
-    {
-      GURL test_url("http://[3ffe:2a00:100:7031::1]:99/");
-      TestCompletionCallback callback;
-      int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-      EXPECT_EQ(OK, rv);
-      EXPECT_TRUE(info.is_direct());
-    }
-    {
-      GURL test_url("http://[3ffe:2a00:100:7031::1]/");
-      TestCompletionCallback callback;
-      int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-      EXPECT_EQ(OK, rv);
-      EXPECT_FALSE(info.is_direct());
-    }
-  }
-
-  // IPv6 without port. The bypass entry ought to work without the
-  // brackets, but the bypass matching logic in ProxyService is
-  // currently limited.
-  config.proxy_bypass.clear();
-  config.proxy_bypass.push_back("[3ffe:2a00:100:7031::1]");
-  {
-    scoped_refptr<ProxyService> service(new ProxyService(
-        new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
-    {
-      GURL test_url("http://[3ffe:2a00:100:7031::1]:99/");
-      TestCompletionCallback callback;
-      int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-      EXPECT_EQ(OK, rv);
-      EXPECT_TRUE(info.is_direct());
-    }
-    {
-      GURL test_url("http://[3ffe:2a00:100:7031::1]/");
-      TestCompletionCallback callback;
-      int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
-      EXPECT_EQ(OK, rv);
-      EXPECT_TRUE(info.is_direct());
-    }
-  }
-}
 
 TEST(ProxyServiceTest, PerProtocolProxyTests) {
   ProxyConfig config;
-  config.proxy_rules.ParseFromString("http=foopy1:8080;https=foopy2:8080");
-  config.auto_detect = false;
+  config.proxy_rules().ParseFromString("http=foopy1:8080;https=foopy2:8080");
+  config.set_auto_detect(false);
   {
     scoped_refptr<ProxyService> service(new ProxyService(
         new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
     GURL test_url("http://www.msn.com");
     ProxyInfo info;
     TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
+    int rv = service->ResolveProxy(test_url, &info, &callback, NULL,
+                                   BoundNetLog());
     EXPECT_EQ(OK, rv);
     EXPECT_FALSE(info.is_direct());
     EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
@@ -898,7 +743,8 @@
     GURL test_url("ftp://ftp.google.com");
     ProxyInfo info;
     TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
+    int rv = service->ResolveProxy(test_url, &info, &callback, NULL,
+                                   BoundNetLog());
     EXPECT_EQ(OK, rv);
     EXPECT_TRUE(info.is_direct());
     EXPECT_EQ("direct://", info.proxy_server().ToURI());
@@ -909,19 +755,21 @@
     GURL test_url("https://webbranch.techcu.com");
     ProxyInfo info;
     TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
+    int rv = service->ResolveProxy(test_url, &info, &callback, NULL,
+                                   BoundNetLog());
     EXPECT_EQ(OK, rv);
     EXPECT_FALSE(info.is_direct());
     EXPECT_EQ("foopy2:8080", info.proxy_server().ToURI());
   }
   {
-    config.proxy_rules.ParseFromString("foopy1:8080");
+    config.proxy_rules().ParseFromString("foopy1:8080");
     scoped_refptr<ProxyService> service(new ProxyService(
         new MockProxyConfigService(config), new MockAsyncProxyResolver, NULL));
     GURL test_url("http://www.microsoft.com");
     ProxyInfo info;
     TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
+    int rv = service->ResolveProxy(test_url, &info, &callback, NULL,
+                                   BoundNetLog());
     EXPECT_EQ(OK, rv);
     EXPECT_FALSE(info.is_direct());
     EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
@@ -932,10 +780,10 @@
 // fall back to the SOCKS proxy.
 TEST(ProxyServiceTest, DefaultProxyFallbackToSOCKS) {
   ProxyConfig config;
-  config.proxy_rules.ParseFromString("http=foopy1:8080;socks=foopy2:1080");
-  config.auto_detect = false;
+  config.proxy_rules().ParseFromString("http=foopy1:8080;socks=foopy2:1080");
+  config.set_auto_detect(false);
   EXPECT_EQ(ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
-            config.proxy_rules.type);
+            config.proxy_rules().type);
 
   {
     scoped_refptr<ProxyService> service(new ProxyService(
@@ -943,7 +791,8 @@
     GURL test_url("http://www.msn.com");
     ProxyInfo info;
     TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
+    int rv = service->ResolveProxy(test_url, &info, &callback, NULL,
+                                   BoundNetLog());
     EXPECT_EQ(OK, rv);
     EXPECT_FALSE(info.is_direct());
     EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
@@ -954,7 +803,8 @@
     GURL test_url("ftp://ftp.google.com");
     ProxyInfo info;
     TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
+    int rv = service->ResolveProxy(test_url, &info, &callback, NULL,
+                                   BoundNetLog());
     EXPECT_EQ(OK, rv);
     EXPECT_FALSE(info.is_direct());
     EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI());
@@ -965,7 +815,8 @@
     GURL test_url("https://webbranch.techcu.com");
     ProxyInfo info;
     TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
+    int rv = service->ResolveProxy(test_url, &info, &callback, NULL,
+                                   BoundNetLog());
     EXPECT_EQ(OK, rv);
     EXPECT_FALSE(info.is_direct());
     EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI());
@@ -976,7 +827,8 @@
     GURL test_url("unknown://www.microsoft.com");
     ProxyInfo info;
     TestCompletionCallback callback;
-    int rv = service->ResolveProxy(test_url, &info, &callback, NULL, NULL);
+    int rv = service->ResolveProxy(test_url, &info, &callback, NULL,
+                                   BoundNetLog());
     EXPECT_EQ(OK, rv);
     EXPECT_FALSE(info.is_direct());
     EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI());
@@ -998,7 +850,7 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://request1"), &info1, &callback1, NULL, NULL);
+      GURL("http://request1"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Nothing has been sent to the proxy resolver yet, since the proxy
@@ -1007,7 +859,7 @@
 
   // Successfully initialize the PAC script.
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -1017,7 +869,7 @@
   TestCompletionCallback callback2;
   ProxyService::PacRequest* request2;
   rv = service->ResolveProxy(
-      GURL("http://request2"), &info2, &callback2, &request2, NULL);
+      GURL("http://request2"), &info2, &callback2, &request2, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   ASSERT_EQ(2u, resolver->pending_requests().size());
   EXPECT_EQ(GURL("http://request2"), resolver->pending_requests()[1]->url());
@@ -1025,7 +877,7 @@
   ProxyInfo info3;
   TestCompletionCallback callback3;
   rv = service->ResolveProxy(
-      GURL("http://request3"), &info3, &callback3, NULL, NULL);
+      GURL("http://request3"), &info3, &callback3, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   ASSERT_EQ(3u, resolver->pending_requests().size());
   EXPECT_EQ(GURL("http://request3"), resolver->pending_requests()[2]->url());
@@ -1076,7 +928,7 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://request1"), &info1, &callback1, NULL, NULL);
+      GURL("http://request1"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // The first request should have triggered download of PAC script.
@@ -1086,13 +938,13 @@
   ProxyInfo info2;
   TestCompletionCallback callback2;
   rv = service->ResolveProxy(
-      GURL("http://request2"), &info2, &callback2, NULL, NULL);
+      GURL("http://request2"), &info2, &callback2, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   ProxyInfo info3;
   TestCompletionCallback callback3;
   rv = service->ResolveProxy(
-      GURL("http://request3"), &info3, &callback3, NULL, NULL);
+      GURL("http://request3"), &info3, &callback3, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Nothing has been sent to the resolver yet.
@@ -1105,7 +957,8 @@
 
   // Now that the PAC script is downloaded, it will have been sent to the proxy
   // resolver.
-  EXPECT_EQ("pac-v1", resolver->pending_set_pac_script_request()->pac_bytes());
+  EXPECT_EQ(ASCIIToUTF16("pac-v1"),
+            resolver->pending_set_pac_script_request()->script_data()->utf16());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(3u, resolver->pending_requests().size());
@@ -1155,7 +1008,7 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://request1"), &info1, &callback1, NULL, NULL);
+      GURL("http://request1"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // The first request should have triggered download of PAC script.
@@ -1165,7 +1018,7 @@
   ProxyInfo info2;
   TestCompletionCallback callback2;
   rv = service->ResolveProxy(
-      GURL("http://request2"), &info2, &callback2, NULL, NULL);
+      GURL("http://request2"), &info2, &callback2, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // At this point the ProxyService should be waiting for the
@@ -1185,7 +1038,8 @@
 
   // Now that the PAC script is downloaded, it will have been sent to the proxy
   // resolver.
-  EXPECT_EQ("pac-v1", resolver->pending_set_pac_script_request()->pac_bytes());
+  EXPECT_EQ(ASCIIToUTF16("pac-v1"),
+            resolver->pending_set_pac_script_request()->script_data()->utf16());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(2u, resolver->pending_requests().size());
@@ -1211,9 +1065,9 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   ProxyService::PacRequest* request1;
-  scoped_refptr<LoadLog> log1(new LoadLog(LoadLog::kUnbounded));
+  CapturingBoundNetLog log1(CapturingNetLog::kUnbounded);
   int rv = service->ResolveProxy(
-      GURL("http://request1"), &info1, &callback1, &request1, log1);
+      GURL("http://request1"), &info1, &callback1, &request1, log1.bound());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // The first request should have triggered download of PAC script.
@@ -1224,13 +1078,13 @@
   TestCompletionCallback callback2;
   ProxyService::PacRequest* request2;
   rv = service->ResolveProxy(
-      GURL("http://request2"), &info2, &callback2, &request2, NULL);
+      GURL("http://request2"), &info2, &callback2, &request2, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   ProxyInfo info3;
   TestCompletionCallback callback3;
   rv = service->ResolveProxy(
-      GURL("http://request3"), &info3, &callback3, NULL, NULL);
+      GURL("http://request3"), &info3, &callback3, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Nothing has been sent to the resolver yet.
@@ -1247,7 +1101,8 @@
 
   // Now that the PAC script is downloaded, it will have been sent to the
   // proxy resolver.
-  EXPECT_EQ("pac-v1", resolver->pending_set_pac_script_request()->pac_bytes());
+  EXPECT_EQ(ASCIIToUTF16("pac-v1"),
+            resolver->pending_set_pac_script_request()->script_data()->utf16());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -1265,24 +1120,26 @@
   EXPECT_FALSE(callback1.have_result());  // Cancelled.
   EXPECT_FALSE(callback2.have_result());  // Cancelled.
 
-  // Check the LoadLog for request 1 (which was cancelled) got filled properly.
-  EXPECT_EQ(6u, log1->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log1, 0, LoadLog::TYPE_PROXY_SERVICE));
+  // Check the NetLog for request 1 (which was cancelled) got filled properly.
+  EXPECT_EQ(6u, log1.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log1, 3, LoadLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC));
+      log1.entries(), 0, NetLog::TYPE_PROXY_SERVICE));
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log1.entries(), 3, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC));
   // Note that TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC is never completed before
   // the cancellation occured.
   EXPECT_TRUE(LogContainsEvent(
-      *log1, 4, LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE));
-  EXPECT_TRUE(LogContainsEndEvent(*log1, 5, LoadLog::TYPE_PROXY_SERVICE));
+      log1.entries(), 4, NetLog::TYPE_CANCELLED, NetLog::PHASE_NONE));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log1.entries(), 5, NetLog::TYPE_PROXY_SERVICE));
 }
 
 // Test that if auto-detect fails, we fall-back to the custom pac.
 TEST(ProxyServiceTest, FallbackFromAutodetectToCustomPac) {
   ProxyConfig config;
-  config.auto_detect = true;
-  config.pac_url = GURL("http://foopy/proxy.pac");
-  config.proxy_rules.ParseFromString("http=foopy:80");  // Won't be used.
+  config.set_auto_detect(true);
+  config.set_pac_url(GURL("http://foopy/proxy.pac"));
+  config.proxy_rules().ParseFromString("http=foopy:80");  // Won't be used.
 
   MockProxyConfigService* config_service = new MockProxyConfigService(config);
   MockAsyncProxyResolverExpectsBytes* resolver =
@@ -1298,14 +1155,14 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://request1"), &info1, &callback1, NULL, NULL);
+      GURL("http://request1"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   ProxyInfo info2;
   TestCompletionCallback callback2;
   ProxyService::PacRequest* request2;
   rv = service->ResolveProxy(
-      GURL("http://request2"), &info2, &callback2, &request2, NULL);
+      GURL("http://request2"), &info2, &callback2, &request2, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Check that nothing has been sent to the proxy resolver yet.
@@ -1322,8 +1179,8 @@
   EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
   fetcher->NotifyFetchCompletion(OK, "custom-pac-script");
 
-  EXPECT_EQ("custom-pac-script",
-            resolver->pending_set_pac_script_request()->pac_bytes());
+  EXPECT_EQ(ASCIIToUTF16("custom-pac-script"),
+            resolver->pending_set_pac_script_request()->script_data()->utf16());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   // Now finally, the pending requests should have been sent to the resolver
@@ -1351,9 +1208,9 @@
 // the auto-detect script fails parsing rather than downloading.
 TEST(ProxyServiceTest, FallbackFromAutodetectToCustomPac2) {
   ProxyConfig config;
-  config.auto_detect = true;
-  config.pac_url = GURL("http://foopy/proxy.pac");
-  config.proxy_rules.ParseFromString("http=foopy:80");  // Won't be used.
+  config.set_auto_detect(true);
+  config.set_pac_url(GURL("http://foopy/proxy.pac"));
+  config.proxy_rules().ParseFromString("http=foopy:80");  // Won't be used.
 
   MockProxyConfigService* config_service = new MockProxyConfigService(config);
   MockAsyncProxyResolverExpectsBytes* resolver =
@@ -1369,14 +1226,14 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://request1"), &info1, &callback1, NULL, NULL);
+      GURL("http://request1"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   ProxyInfo info2;
   TestCompletionCallback callback2;
   ProxyService::PacRequest* request2;
   rv = service->ResolveProxy(
-      GURL("http://request2"), &info2, &callback2, &request2, NULL);
+      GURL("http://request2"), &info2, &callback2, &request2, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Check that nothing has been sent to the proxy resolver yet.
@@ -1388,8 +1245,8 @@
   fetcher->NotifyFetchCompletion(OK, "invalid-script-contents");
 
   // Simulate a parse error.
-  EXPECT_EQ("invalid-script-contents",
-            resolver->pending_set_pac_script_request()->pac_bytes());
+  EXPECT_EQ(ASCIIToUTF16("invalid-script-contents"),
+            resolver->pending_set_pac_script_request()->script_data()->utf16());
   resolver->pending_set_pac_script_request()->CompleteNow(
       ERR_PAC_SCRIPT_FAILED);
 
@@ -1398,8 +1255,8 @@
   EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
   fetcher->NotifyFetchCompletion(OK, "custom-pac-script");
 
-  EXPECT_EQ("custom-pac-script",
-            resolver->pending_set_pac_script_request()->pac_bytes());
+  EXPECT_EQ(ASCIIToUTF16("custom-pac-script"),
+            resolver->pending_set_pac_script_request()->script_data()->utf16());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   // Now finally, the pending requests should have been sent to the resolver
@@ -1427,9 +1284,9 @@
 // are given, then we will try them in that order.
 TEST(ProxyServiceTest, FallbackFromAutodetectToCustomToManual) {
   ProxyConfig config;
-  config.auto_detect = true;
-  config.pac_url = GURL("http://foopy/proxy.pac");
-  config.proxy_rules.ParseFromString("http=foopy:80");
+  config.set_auto_detect(true);
+  config.set_pac_url(GURL("http://foopy/proxy.pac"));
+  config.proxy_rules().ParseFromString("http=foopy:80");
 
   MockProxyConfigService* config_service = new MockProxyConfigService(config);
   MockAsyncProxyResolverExpectsBytes* resolver =
@@ -1445,14 +1302,14 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://request1"), &info1, &callback1, NULL, NULL);
+      GURL("http://request1"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   ProxyInfo info2;
   TestCompletionCallback callback2;
   ProxyService::PacRequest* request2;
   rv = service->ResolveProxy(
-      GURL("http://request2"), &info2, &callback2, &request2, NULL);
+      GURL("http://request2"), &info2, &callback2, &request2, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Check that nothing has been sent to the proxy resolver yet.
@@ -1484,10 +1341,10 @@
 // Test that the bypass rules are NOT applied when using autodetect.
 TEST(ProxyServiceTest, BypassDoesntApplyToPac) {
   ProxyConfig config;
-  config.auto_detect = true;
-  config.pac_url = GURL("http://foopy/proxy.pac");
-  config.proxy_rules.ParseFromString("http=foopy:80");  // Not used.
-  config.proxy_bypass.push_back("www.google.com");
+  config.set_auto_detect(true);
+  config.set_pac_url(GURL("http://foopy/proxy.pac"));
+  config.proxy_rules().ParseFromString("http=foopy:80");  // Not used.
+  config.proxy_rules().bypass_rules.ParseFromString("www.google.com");
 
   MockProxyConfigService* config_service = new MockProxyConfigService(config);
   MockAsyncProxyResolverExpectsBytes* resolver =
@@ -1503,7 +1360,7 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://www.google.com"), &info1, &callback1, NULL, NULL);
+      GURL("http://www.google.com"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Check that nothing has been sent to the proxy resolver yet.
@@ -1514,8 +1371,8 @@
   EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
   fetcher->NotifyFetchCompletion(OK, "auto-detect");
 
-  EXPECT_EQ("auto-detect",
-            resolver->pending_set_pac_script_request()->pac_bytes());
+  EXPECT_EQ(ASCIIToUTF16("auto-detect"),
+            resolver->pending_set_pac_script_request()->script_data()->utf16());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -1534,7 +1391,7 @@
   ProxyInfo info2;
   TestCompletionCallback callback2;
   rv = service->ResolveProxy(
-      GURL("http://www.google.com"), &info2, &callback2, NULL, NULL);
+      GURL("http://www.google.com"), &info2, &callback2, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -1555,7 +1412,7 @@
 // being deleted prior to the InitProxyResolver).
 TEST(ProxyServiceTest, DeleteWhileInitProxyResolverHasOutstandingFetch) {
   ProxyConfig config;
-  config.pac_url = GURL("http://foopy/proxy.pac");
+  config.set_pac_url(GURL("http://foopy/proxy.pac"));
 
   MockProxyConfigService* config_service = new MockProxyConfigService(config);
   MockAsyncProxyResolverExpectsBytes* resolver =
@@ -1571,7 +1428,7 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://www.google.com"), &info1, &callback1, NULL, NULL);
+      GURL("http://www.google.com"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Check that nothing has been sent to the proxy resolver yet.
@@ -1603,11 +1460,11 @@
 
   ProxyInfo info;
   TestCompletionCallback callback;
-  int rv = service->ResolveProxy(url, &info, &callback, NULL, NULL);
+  int rv = service->ResolveProxy(url, &info, &callback, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(GURL("http://foopy/proxy.pac"),
-            resolver->pending_set_pac_script_request()->pac_url());
+            resolver->pending_set_pac_script_request()->script_data()->url());
 
   // Delete the ProxyService.
   service = NULL;
@@ -1615,72 +1472,30 @@
 
 TEST(ProxyServiceTest, ResetProxyConfigService) {
   ProxyConfig config1;
-  config1.proxy_rules.ParseFromString("foopy1:8080");
-  config1.auto_detect = false;
+  config1.proxy_rules().ParseFromString("foopy1:8080");
+  config1.set_auto_detect(false);
   scoped_refptr<ProxyService> service(new ProxyService(
       new MockProxyConfigService(config1),
-      new MockAsyncProxyResolverExpectsBytes,
-      NULL));
+      new MockAsyncProxyResolverExpectsBytes, NULL));
 
   ProxyInfo info;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://request1"), &info, &callback1, NULL, NULL);
+      GURL("http://request1"), &info, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(OK, rv);
   EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
 
   ProxyConfig config2;
-  config2.proxy_rules.ParseFromString("foopy2:8080");
-  config2.auto_detect = false;
+  config2.proxy_rules().ParseFromString("foopy2:8080");
+  config2.set_auto_detect(false);
   service->ResetConfigService(new MockProxyConfigService(config2));
   TestCompletionCallback callback2;
   rv = service->ResolveProxy(
-      GURL("http://request2"), &info, &callback2, NULL, NULL);
+      GURL("http://request2"), &info, &callback2, NULL, BoundNetLog());
   EXPECT_EQ(OK, rv);
   EXPECT_EQ("foopy2:8080", info.proxy_server().ToURI());
 }
 
-TEST(ProxyServiceTest, IsLocalName) {
-  const struct {
-    const char* url;
-    bool expected_is_local;
-  } tests[] = {
-    // Single-component hostnames are considered local.
-    {"http://localhost/x", true},
-    {"http://www", true},
-
-    // IPv4 loopback interface.
-    {"http://127.0.0.1/x", true},
-    {"http://127.0.0.1:80/x", true},
-
-    // IPv6 loopback interface.
-    {"http://[::1]:80/x", true},
-    {"http://[0:0::1]:6233/x", true},
-    {"http://[0:0:0:0:0:0:0:1]/x", true},
-
-    // Non-local URLs.
-    {"http://foo.com/", false},
-    {"http://localhost.i/", false},
-    {"http://www.google.com/", false},
-    {"http://192.168.0.1/", false},
-
-    // Try with different protocols.
-    {"ftp://127.0.0.1/x", true},
-    {"ftp://foobar.com/x", false},
-
-    // This is a bit of a gray-area, but GURL does not strip trailing dots
-    // in host-names, so the following are considered non-local.
-    {"http://www./x", false},
-    {"http://localhost./x", false},
-  };
-
-  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
-    SCOPED_TRACE(StringPrintf("Test[%" PRIuS "]: %s", i, tests[i].url));
-    bool is_local = ProxyService::IsLocalName(GURL(tests[i].url));
-    EXPECT_EQ(tests[i].expected_is_local, is_local);
-  }
-}
-
 // Check that after we have done the auto-detect test, and the configuration
 // is updated (with no change), we don't re-try the autodetect test.
 // Regression test for http://crbug.com/18526 -- the configuration was being
@@ -1688,7 +1503,7 @@
 // thought it had received a new configuration.
 TEST(ProxyServiceTest, UpdateConfigAfterFailedAutodetect) {
   ProxyConfig config;
-  config.auto_detect = true;
+  config.set_auto_detect(true);
 
   MockProxyConfigService* config_service = new MockProxyConfigService(config);
   MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver;
@@ -1700,14 +1515,15 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://www.google.com"), &info1, &callback1, NULL, NULL);
+      GURL("http://www.google.com"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Check that nothing has been sent to the proxy resolver yet.
   ASSERT_EQ(0u, resolver->pending_requests().size());
 
   // Fail the setting of autodetect script.
-  EXPECT_EQ(GURL(), resolver->pending_set_pac_script_request()->pac_url());
+  EXPECT_EQ(ProxyResolverScriptData::TYPE_AUTO_DETECT,
+            resolver->pending_set_pac_script_request()->script_data()->type());
   resolver->pending_set_pac_script_request()->CompleteNow(ERR_FAILED);
 
   // Verify that request ran as expected -- should have fallen back to direct.
@@ -1716,7 +1532,7 @@
 
   // Force the ProxyService to pull down a new proxy configuration.
   // (Even though the configuration isn't old/bad).
-  service->UpdateConfig(NULL);
+  service->UpdateConfig(BoundNetLog());
 
   // Start another request -- the effective configuration has not
   // changed, so we shouldn't re-run the autodetect step.
@@ -1724,7 +1540,7 @@
   ProxyInfo info2;
   TestCompletionCallback callback2;
   rv = service->ResolveProxy(
-      GURL("http://www.google.com"), &info2, &callback2, NULL, NULL);
+      GURL("http://www.google.com"), &info2, &callback2, NULL, BoundNetLog());
   EXPECT_EQ(OK, rv);
 
   EXPECT_TRUE(info2.is_direct());
@@ -1734,7 +1550,7 @@
 // that does NOT, we unset the variable |should_use_proxy_resolver_|.
 TEST(ProxyServiceTest, UpdateConfigFromPACToDirect) {
   ProxyConfig config;
-  config.auto_detect = true;
+  config.set_auto_detect(true);
 
   MockProxyConfigService* config_service = new MockProxyConfigService(config);
   MockAsyncProxyResolver* resolver = new MockAsyncProxyResolver;
@@ -1746,14 +1562,15 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://www.google.com"), &info1, &callback1, NULL, NULL);
+      GURL("http://www.google.com"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Check that nothing has been sent to the proxy resolver yet.
   ASSERT_EQ(0u, resolver->pending_requests().size());
 
   // Successfully set the autodetect script.
-  EXPECT_EQ(GURL(), resolver->pending_set_pac_script_request()->pac_url());
+  EXPECT_EQ(ProxyResolverScriptData::TYPE_AUTO_DETECT,
+            resolver->pending_set_pac_script_request()->script_data()->type());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   // Complete the pending request.
@@ -1770,15 +1587,15 @@
   //
   // This new configuration no longer has auto_detect set, so
   // requests should complete synchronously now as direct-connect.
-  config.auto_detect = false;
+  config.set_auto_detect(false);
   config_service->config = config;
-  service->UpdateConfig(NULL);
+  service->UpdateConfig(BoundNetLog());
 
   // Start another request -- the effective configuration has changed.
   ProxyInfo info2;
   TestCompletionCallback callback2;
   rv = service->ResolveProxy(
-      GURL("http://www.google.com"), &info2, &callback2, NULL, NULL);
+      GURL("http://www.google.com"), &info2, &callback2, NULL, BoundNetLog());
   EXPECT_EQ(OK, rv);
 
   EXPECT_TRUE(info2.is_direct());
@@ -1791,10 +1608,8 @@
   MockAsyncProxyResolverExpectsBytes* resolver =
       new MockAsyncProxyResolverExpectsBytes;
 
-  MockNetworkChangeNotifier network_change_notifier;
-
   scoped_refptr<ProxyService> service(
-      new ProxyService(config_service, resolver, &network_change_notifier));
+      new ProxyService(config_service, resolver, NULL));
 
   MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
   service->SetProxyScriptFetcher(fetcher);
@@ -1804,7 +1619,7 @@
   ProxyInfo info1;
   TestCompletionCallback callback1;
   int rv = service->ResolveProxy(
-      GURL("http://request1"), &info1, &callback1, NULL, NULL);
+      GURL("http://request1"), &info1, &callback1, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // The first request should have triggered initial download of PAC script.
@@ -1821,7 +1636,8 @@
 
   // Now that the PAC script is downloaded, the request will have been sent to
   // the proxy resolver.
-  EXPECT_EQ("pac-v1", resolver->pending_set_pac_script_request()->pac_bytes());
+  EXPECT_EQ(ASCIIToUTF16("pac-v1"),
+            resolver->pending_set_pac_script_request()->script_data()->utf16());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
@@ -1838,14 +1654,14 @@
   // Now simluate a change in the network. The ProxyConfigService is still
   // going to return the same PAC URL as before, but this URL needs to be
   // refetched on the new network.
-
-  network_change_notifier.NotifyIPAddressChange();
+  NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+  MessageLoop::current()->RunAllPending();  // Notification happens async.
 
   // Start a second request.
   ProxyInfo info2;
   TestCompletionCallback callback2;
   rv = service->ResolveProxy(
-      GURL("http://request2"), &info2, &callback2, NULL, NULL);
+      GURL("http://request2"), &info2, &callback2, NULL, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // This second request should have triggered the re-download of the PAC
@@ -1862,7 +1678,8 @@
 
   // Now that the PAC script is downloaded, the second request will have been
   // sent to the proxy resolver.
-  EXPECT_EQ("pac-v2", resolver->pending_set_pac_script_request()->pac_bytes());
+  EXPECT_EQ(ASCIIToUTF16("pac-v2"),
+            resolver->pending_set_pac_script_request()->script_data()->utf16());
   resolver->pending_set_pac_script_request()->CompleteNow(OK);
 
   ASSERT_EQ(1u, resolver->pending_requests().size());
diff --git a/net/proxy/sync_host_resolver_bridge.cc b/net/proxy/sync_host_resolver_bridge.cc
new file mode 100644
index 0000000..6c62c7d
--- /dev/null
+++ b/net/proxy/sync_host_resolver_bridge.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2010 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/proxy/sync_host_resolver_bridge.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/lock.h"
+#include "base/message_loop.h"
+#include "base/waitable_event.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+
+namespace net {
+
+// SyncHostResolverBridge::Core ----------------------------------------------
+
+class SyncHostResolverBridge::Core
+    : public base::RefCountedThreadSafe<SyncHostResolverBridge::Core> {
+ public:
+  Core(HostResolver* resolver, MessageLoop* host_resolver_loop);
+
+  int ResolveSynchronously(const HostResolver::RequestInfo& info,
+                           AddressList* addresses);
+
+  // Returns true if Shutdown() has been called.
+  bool HasShutdown() const {
+    AutoLock l(lock_);
+    return HasShutdownLocked();
+  }
+
+  // Called on |host_resolver_loop_|.
+  void Shutdown();
+
+ private:
+  friend class base::RefCountedThreadSafe<SyncHostResolverBridge::Core>;
+
+  bool HasShutdownLocked() const {
+    return has_shutdown_;
+  }
+
+  // Called on |host_resolver_loop_|.
+  void StartResolve(const HostResolver::RequestInfo& info,
+                    AddressList* addresses);
+
+  // Called on |host_resolver_loop_|.
+  void OnResolveCompletion(int result);
+
+  // Not called on |host_resolver_loop_|.
+  int WaitForResolveCompletion();
+
+  const scoped_refptr<HostResolver> host_resolver_;
+  MessageLoop* const host_resolver_loop_;
+  net::CompletionCallbackImpl<Core> callback_;
+  // The result from the current request (set on |host_resolver_loop_|).
+  int err_;
+  // The currently outstanding request to |host_resolver_|, or NULL.
+  HostResolver::RequestHandle outstanding_request_;
+
+  // Event to notify completion of resolve request.  We always Signal() on
+  // |host_resolver_loop_| and Wait() on a different thread.
+  base::WaitableEvent event_;
+
+  // True if Shutdown() has been called. Must hold |lock_| to access it.
+  bool has_shutdown_;
+
+  // Mutex to guard accesses to |has_shutdown_|.
+  mutable Lock lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+SyncHostResolverBridge::Core::Core(HostResolver* host_resolver,
+                                   MessageLoop* host_resolver_loop)
+    : host_resolver_(host_resolver),
+      host_resolver_loop_(host_resolver_loop),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          callback_(this, &Core::OnResolveCompletion)),
+      err_(0),
+      outstanding_request_(NULL),
+      event_(true, false),
+      has_shutdown_(false) {}
+
+int SyncHostResolverBridge::Core::ResolveSynchronously(
+    const HostResolver::RequestInfo& info,
+    net::AddressList* addresses) {
+  // Otherwise start an async resolve on the resolver's thread.
+  host_resolver_loop_->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(this, &Core::StartResolve,
+                        info, addresses));
+
+  return WaitForResolveCompletion();
+}
+
+void SyncHostResolverBridge::Core::StartResolve(
+    const HostResolver::RequestInfo& info,
+    net::AddressList* addresses) {
+  DCHECK_EQ(MessageLoop::current(), host_resolver_loop_);
+  DCHECK(!outstanding_request_);
+
+  if (HasShutdown())
+    return;
+
+  int error = host_resolver_->Resolve(
+      info, addresses, &callback_, &outstanding_request_, BoundNetLog());
+  if (error != ERR_IO_PENDING)
+    OnResolveCompletion(error);  // Completed synchronously.
+}
+
+void SyncHostResolverBridge::Core::OnResolveCompletion(int result) {
+  DCHECK_EQ(MessageLoop::current(), host_resolver_loop_);
+  err_ = result;
+  outstanding_request_ = NULL;
+  event_.Signal();
+}
+
+int SyncHostResolverBridge::Core::WaitForResolveCompletion() {
+  DCHECK_NE(MessageLoop::current(), host_resolver_loop_);
+  event_.Wait();
+
+  {
+    AutoLock l(lock_);
+    if (HasShutdownLocked())
+      return ERR_ABORTED;
+    event_.Reset();
+  }
+
+  return err_;
+}
+
+void SyncHostResolverBridge::Core::Shutdown() {
+  DCHECK_EQ(MessageLoop::current(), host_resolver_loop_);
+
+  if (outstanding_request_) {
+    host_resolver_->CancelRequest(outstanding_request_);
+    outstanding_request_ = NULL;
+  }
+
+  {
+    AutoLock l(lock_);
+    has_shutdown_ = true;
+  }
+
+  // Wake up the PAC thread in case it was waiting for resolve completion.
+  event_.Signal();
+}
+
+// SyncHostResolverBridge -----------------------------------------------------
+
+SyncHostResolverBridge::SyncHostResolverBridge(HostResolver* host_resolver,
+                                               MessageLoop* host_resolver_loop)
+    : host_resolver_loop_(host_resolver_loop),
+      core_(new Core(host_resolver, host_resolver_loop)) {
+  DCHECK(host_resolver_loop_);
+}
+
+SyncHostResolverBridge::~SyncHostResolverBridge() {
+  DCHECK(core_->HasShutdown());
+}
+
+int SyncHostResolverBridge::Resolve(const RequestInfo& info,
+                                    AddressList* addresses,
+                                    CompletionCallback* callback,
+                                    RequestHandle* out_req,
+                                    const BoundNetLog& net_log) {
+  DCHECK(!callback);
+  DCHECK(!out_req);
+
+  return core_->ResolveSynchronously(info, addresses);
+}
+
+void SyncHostResolverBridge::CancelRequest(RequestHandle req) {
+  NOTREACHED();
+}
+
+void SyncHostResolverBridge::AddObserver(Observer* observer) {
+  NOTREACHED();
+}
+
+void SyncHostResolverBridge::RemoveObserver(Observer* observer) {
+  NOTREACHED();
+}
+
+void SyncHostResolverBridge::Shutdown() {
+  DCHECK_EQ(MessageLoop::current(), host_resolver_loop_);
+  core_->Shutdown();
+}
+
+}  // namespace net
diff --git a/net/proxy/sync_host_resolver_bridge.h b/net/proxy/sync_host_resolver_bridge.h
new file mode 100644
index 0000000..b02d496
--- /dev/null
+++ b/net/proxy/sync_host_resolver_bridge.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2010 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_PROXY_SYNC_HOST_RESOLVER_BRIDGE_H_
+#define NET_PROXY_SYNC_HOST_RESOLVER_BRIDGE_H_
+
+#include "base/scoped_ptr.h"
+#include "net/base/host_resolver.h"
+
+class MessageLoop;
+
+namespace net {
+
+// Wrapper around HostResolver to give a sync API while running the resolver
+// in async mode on |host_resolver_loop|.
+class SyncHostResolverBridge : public HostResolver {
+ public:
+  SyncHostResolverBridge(HostResolver* host_resolver,
+                         MessageLoop* host_resolver_loop);
+
+  virtual ~SyncHostResolverBridge();
+
+  // HostResolver methods:
+  virtual int Resolve(const RequestInfo& info,
+                      AddressList* addresses,
+                      CompletionCallback* callback,
+                      RequestHandle* out_req,
+                      const BoundNetLog& net_log);
+  virtual void CancelRequest(RequestHandle req);
+  virtual void AddObserver(Observer* observer);
+  virtual void RemoveObserver(Observer* observer);
+
+  // The Shutdown() method should be called prior to destruction, from
+  // |host_resolver_loop_|. It aborts any in progress synchronous resolves, to
+  // prevent deadlocks from happening.
+  virtual void Shutdown();
+
+ private:
+  class Core;
+
+  MessageLoop* const host_resolver_loop_;
+  scoped_refptr<Core> core_;
+};
+
+}  // namespace net
+
+#endif  // NET_PROXY_SYNC_HOST_RESOLVER_BRIDGE_H_
diff --git a/net/proxy/sync_host_resolver_bridge_unittest.cc b/net/proxy/sync_host_resolver_bridge_unittest.cc
new file mode 100644
index 0000000..95ba446
--- /dev/null
+++ b/net/proxy/sync_host_resolver_bridge_unittest.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2010 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/proxy/sync_host_resolver_bridge.h"
+
+#include "base/thread.h"
+#include "base/waitable_event.h"
+#include "net/base/address_list.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/proxy/multi_threaded_proxy_resolver.h"
+#include "net/base/test_completion_callback.h"
+#include "net/proxy/proxy_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// TODO(eroman): This test should be moved into
+//               multi_threaded_proxy_resolver_unittest.cc.
+
+namespace net {
+
+namespace {
+
+// This implementation of HostResolver allows blocking until a resolve request
+// has been received. The resolve requests it receives will never be completed.
+class BlockableHostResolver : public HostResolver {
+ public:
+  BlockableHostResolver()
+      : event_(true, false),
+        was_request_cancelled_(false) {
+  }
+
+  virtual int Resolve(const RequestInfo& info,
+                      AddressList* addresses,
+                      CompletionCallback* callback,
+                      RequestHandle* out_req,
+                      const BoundNetLog& net_log) {
+    EXPECT_TRUE(callback);
+    EXPECT_TRUE(out_req);
+    *out_req = reinterpret_cast<RequestHandle*>(1);  // Magic value.
+
+    // Indicate to the caller that a request was received.
+    event_.Signal();
+
+    // We return ERR_IO_PENDING, as this request will NEVER be completed.
+    // Expectation is for the caller to later cancel the request.
+    return ERR_IO_PENDING;
+  }
+
+  virtual void CancelRequest(RequestHandle req) {
+    EXPECT_EQ(reinterpret_cast<RequestHandle*>(1), req);
+    was_request_cancelled_ = true;
+  }
+
+  virtual void AddObserver(Observer* observer) {
+    NOTREACHED();
+  }
+
+  virtual void RemoveObserver(Observer* observer) {
+    NOTREACHED();
+  }
+
+  // Waits until Resolve() has been called.
+  void WaitUntilRequestIsReceived() {
+    event_.Wait();
+  }
+
+  bool was_request_cancelled() const {
+    return was_request_cancelled_;
+  }
+
+ private:
+  // Event to notify when a resolve request was received.
+  base::WaitableEvent event_;
+  bool was_request_cancelled_;
+};
+
+// This implementation of ProxyResolver simply does a synchronous resolve
+// on |host_resolver| in response to GetProxyForURL().
+class SyncProxyResolver : public ProxyResolver {
+ public:
+  explicit SyncProxyResolver(SyncHostResolverBridge* host_resolver)
+      : ProxyResolver(false), host_resolver_(host_resolver) {}
+
+  virtual int GetProxyForURL(const GURL& url,
+                             ProxyInfo* results,
+                             CompletionCallback* callback,
+                             RequestHandle* request,
+                             const BoundNetLog& net_log) {
+    EXPECT_FALSE(callback);
+    EXPECT_FALSE(request);
+
+    // Do a synchronous host resolve.
+    HostResolver::RequestInfo info(url.host(), 80);
+    AddressList addresses;
+    int rv =
+        host_resolver_->Resolve(info, &addresses, NULL, NULL, BoundNetLog());
+
+    EXPECT_EQ(ERR_ABORTED, rv);
+
+    return rv;
+  }
+
+  virtual void CancelRequest(RequestHandle request) {
+    NOTREACHED();
+  }
+
+  virtual void Shutdown() {
+    host_resolver_->Shutdown();
+  }
+
+  virtual int SetPacScript(
+      const scoped_refptr<ProxyResolverScriptData>& script_data,
+      CompletionCallback* callback) {
+    return OK;
+  }
+
+ private:
+  scoped_refptr<SyncHostResolverBridge> host_resolver_;
+};
+
+class SyncProxyResolverFactory : public ProxyResolverFactory {
+ public:
+  explicit SyncProxyResolverFactory(SyncHostResolverBridge* sync_host_resolver)
+      : ProxyResolverFactory(false),
+        sync_host_resolver_(sync_host_resolver) {
+  }
+
+  virtual ProxyResolver* CreateProxyResolver() {
+    return new SyncProxyResolver(sync_host_resolver_);
+  }
+
+ private:
+  scoped_refptr<SyncHostResolverBridge> sync_host_resolver_;
+};
+
+// This helper thread is used to create the circumstances for the deadlock.
+// It is analagous to the "IO thread" which would be main thread running the
+// network stack.
+class IOThread : public base::Thread {
+ public:
+  IOThread() : base::Thread("IO-thread") {}
+
+  virtual ~IOThread() {
+    Stop();
+  }
+
+  const scoped_refptr<BlockableHostResolver>& async_resolver() {
+    return async_resolver_;
+  }
+
+ protected:
+  virtual void Init() {
+    async_resolver_ = new BlockableHostResolver();
+
+    // Create a synchronous host resolver that operates the async host
+    // resolver on THIS thread.
+    scoped_refptr<SyncHostResolverBridge> sync_resolver =
+        new SyncHostResolverBridge(async_resolver_, message_loop());
+
+    proxy_resolver_.reset(
+        new MultiThreadedProxyResolver(
+            new SyncProxyResolverFactory(sync_resolver),
+            1u));
+
+    // Initialize the resolver.
+    TestCompletionCallback callback;
+    proxy_resolver_->SetPacScript(ProxyResolverScriptData::FromURL(GURL()),
+                                  &callback);
+    EXPECT_EQ(OK, callback.WaitForResult());
+
+    // Start an asynchronous request to the proxy resolver
+    // (note that it will never complete).
+    proxy_resolver_->GetProxyForURL(GURL("http://test/"), &results_,
+                                    &callback_, &request_, BoundNetLog());
+  }
+
+  virtual void CleanUp() {
+    // Cancel the outstanding request (note however that this will not
+    // unblock the PAC thread though).
+    proxy_resolver_->CancelRequest(request_);
+
+    // Delete the single threaded proxy resolver.
+    proxy_resolver_.reset();
+
+    // (There may have been a completion posted back to origin thread, avoid
+    // leaking it by running).
+    MessageLoop::current()->RunAllPending();
+
+    // During the teardown sequence of the single threaded proxy resolver,
+    // the outstanding host resolve should have been cancelled.
+    EXPECT_TRUE(async_resolver_->was_request_cancelled());
+
+    async_resolver_ = NULL;
+  }
+
+ private:
+  // This (async) host resolver will outlive the thread that is operating it
+  // synchronously.
+  scoped_refptr<BlockableHostResolver> async_resolver_;
+
+  scoped_ptr<ProxyResolver> proxy_resolver_;
+
+  // Data for the outstanding request to the single threaded proxy resolver.
+  TestCompletionCallback callback_;
+  ProxyInfo results_;
+  ProxyResolver::RequestHandle request_;
+};
+
+// Test that a deadlock does not happen during shutdown when a host resolve
+// is outstanding on the SyncHostResolverBridge.
+// This is a regression test for http://crbug.com/41244.
+TEST(MultiThreadedProxyResolverTest, ShutdownIsCalledBeforeThreadJoin) {
+  IOThread io_thread;
+  base::Thread::Options options;
+  options.message_loop_type = MessageLoop::TYPE_IO;
+  ASSERT_TRUE(io_thread.StartWithOptions(options));
+
+  io_thread.async_resolver()->WaitUntilRequestIsReceived();
+
+  // Now upon exitting this scope, the IOThread is destroyed -- this will
+  // stop the IOThread, which will in turn delete the
+  // SingleThreadedProxyResolver, which in turn will stop its internal
+  // PAC thread (which is currently blocked waiting on the host resolve which
+  // is running on IOThread).  The IOThread::Cleanup() will verify that after
+  // the PAC thread is stopped, it cancels the request on the HostResolver.
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/run_testserver.target.mk b/net/run_testserver.target.mk
new file mode 100644
index 0000000..8de959b
--- /dev/null
+++ b/net/run_testserver.target.mk
@@ -0,0 +1,189 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := run_testserver
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I. \
+	-Itesting/gtest/include
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DUNIT_TEST' \
+	'-DGTEST_HAS_RTTI=0' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I. \
+	-Itesting/gtest/include
+
+OBJS := $(obj).target/$(TARGET)/net/tools/testserver/run_testserver.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/net/libnet.a $(obj).target/net/libnet_test_support.a $(obj).target/base/libbase.a $(obj).target/testing/libgtest.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-Wl,--gc-sections
+
+LIBS := -lrt \
+	-ldl \
+	-lgtk-x11-2.0 \
+	-lgdk-x11-2.0 \
+	-latk-1.0 \
+	-lgio-2.0 \
+	-lpangoft2-1.0 \
+	-lgdk_pixbuf-2.0 \
+	-lm \
+	-lpangocairo-1.0 \
+	-lcairo \
+	-lpango-1.0 \
+	-lfreetype \
+	-lfontconfig \
+	-lgobject-2.0 \
+	-lgmodule-2.0 \
+	-lgthread-2.0 \
+	-lglib-2.0 \
+	-lnss3 \
+	-lnssutil3 \
+	-lsmime3 \
+	-lplds4 \
+	-lplc4 \
+	-lnspr4 \
+	-lpthread \
+	-lz \
+	-lgconf-2
+
+$(builddir)/run_testserver: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(builddir)/run_testserver: LIBS := $(LIBS)
+$(builddir)/run_testserver: TOOLSET := $(TOOLSET)
+$(builddir)/run_testserver: $(OBJS) $(obj).target/net/libnet.a $(obj).target/net/libnet_test_support.a $(obj).target/base/libbase.a $(obj).target/testing/libgtest.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a FORCE_DO_CMD
+	$(call do_cmd,link)
+
+all_deps += $(builddir)/run_testserver
+# Add target alias
+.PHONY: run_testserver
+run_testserver: $(builddir)/run_testserver
+
+# Add executable to "all" target.
+.PHONY: all
+all: $(builddir)/run_testserver
+
diff --git a/net/server/http_listen_socket.cc b/net/server/http_listen_socket.cc
new file mode 100644
index 0000000..16c664e
--- /dev/null
+++ b/net/server/http_listen_socket.cc
@@ -0,0 +1,318 @@
+// Copyright (c) 2010 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.
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+#include <map>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/md5.h"
+#include "base/string_util.h"
+#include "net/server/http_listen_socket.h"
+#include "net/server/http_server_request_info.h"
+
+// must run in the IO thread
+HttpListenSocket::HttpListenSocket(SOCKET s,
+                                   HttpListenSocket::Delegate* delegate)
+    : ALLOW_THIS_IN_INITIALIZER_LIST(ListenSocket(s, this)),
+      delegate_(delegate),
+      is_web_socket_(false) {
+}
+
+// must run in the IO thread
+HttpListenSocket::~HttpListenSocket() {
+}
+
+void HttpListenSocket::Accept() {
+  SOCKET conn = ListenSocket::Accept(socket_);
+  DCHECK_NE(conn, ListenSocket::kInvalidSocket);
+  if (conn == ListenSocket::kInvalidSocket) {
+    // TODO
+  } else {
+    scoped_refptr<HttpListenSocket> sock =
+        new HttpListenSocket(conn, delegate_);
+    // it's up to the delegate to AddRef if it wants to keep it around
+    DidAccept(this, sock);
+  }
+}
+
+HttpListenSocket* HttpListenSocket::Listen(
+    const std::string& ip,
+    int port,
+    HttpListenSocket::Delegate* delegate) {
+  SOCKET s = ListenSocket::Listen(ip, port);
+  if (s == ListenSocket::kInvalidSocket) {
+    // TODO (ibrar): error handling
+  } else {
+    HttpListenSocket *serv = new HttpListenSocket(s, delegate);
+    serv->Listen();
+    return serv;
+  }
+  return NULL;
+}
+
+std::string GetHeaderValue(
+    HttpServerRequestInfo* request,
+    const std::string& header_name) {
+  HttpServerRequestInfo::HeadersMap::iterator it =
+      request->headers.find(header_name);
+  if (it != request->headers.end())
+    return it->second;
+  return "";
+}
+
+uint32 WebSocketKeyFingerprint(const std::string& str) {
+  std::string result;
+  const char* pChar = str.c_str();
+  int length = str.length();
+  int spaces = 0;
+  for (int i = 0; i < length; ++i) {
+    if (pChar[i] >= '0' && pChar[i] <= '9')
+      result.append(&pChar[i], 1);
+    else if (pChar[i] == ' ')
+      spaces++;
+  }
+  if (spaces == 0)
+    return 0;
+  int64 number = 0;
+  if (!StringToInt64(result, &number))
+    return 0;
+  return htonl(static_cast<uint32>(number / spaces));
+}
+
+void HttpListenSocket::AcceptWebSocket(HttpServerRequestInfo* request) {
+  std::string key1 = GetHeaderValue(request, "Sec-WebSocket-Key1");
+  std::string key2 = GetHeaderValue(request, "Sec-WebSocket-Key2");
+
+  uint32 fp1 = WebSocketKeyFingerprint(key1);
+  uint32 fp2 = WebSocketKeyFingerprint(key2);
+
+  char data[16];
+  memcpy(data, &fp1, 4);
+  memcpy(data + 4, &fp2, 4);
+  memcpy(data + 8, &request->data[0], 8);
+
+  MD5Digest digest;
+  MD5Sum(data, 16, &digest);
+
+  std::string origin = GetHeaderValue(request, "Origin");
+  std::string host = GetHeaderValue(request, "Host");
+  std::string location = "ws://" + host + request->path;
+  is_web_socket_ = true;
+  Send(StringPrintf("HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+                    "Upgrade: WebSocket\r\n"
+                    "Connection: Upgrade\r\n"
+                    "Sec-WebSocket-Origin: %s\r\n"
+                    "Sec-WebSocket-Location: %s\r\n"
+                    "\r\n",
+                    origin.c_str(),
+                    location.c_str()));
+  Send(reinterpret_cast<char*>(digest.a), 16);
+}
+
+void HttpListenSocket::SendOverWebSocket(const std::string& data) {
+  DCHECK(is_web_socket_);
+  char message_start = 0;
+  char message_end = -1;
+  Send(&message_start, 1);
+  Send(data);
+  Send(&message_end, 1);
+}
+
+//
+// HTTP Request Parser
+// This HTTP request parser uses a simple state machine to quickly parse
+// through the headers.  The parser is not 100% complete, as it is designed
+// for use in this simple test driver.
+//
+// Known issues:
+//   - does not handle whitespace on first HTTP line correctly.  Expects
+//     a single space between the method/url and url/protocol.
+
+// Input character types.
+enum header_parse_inputs {
+  INPUT_SPACE,
+  INPUT_CR,
+  INPUT_LF,
+  INPUT_COLON,
+  INPUT_00,
+  INPUT_FF,
+  INPUT_DEFAULT,
+  MAX_INPUTS,
+};
+
+// Parser states.
+enum header_parse_states {
+  ST_METHOD,     // Receiving the method
+  ST_URL,        // Receiving the URL
+  ST_PROTO,      // Receiving the protocol
+  ST_HEADER,     // Starting a Request Header
+  ST_NAME,       // Receiving a request header name
+  ST_SEPARATOR,  // Receiving the separator between header name and value
+  ST_VALUE,      // Receiving a request header value
+  ST_WS_READY,   // Ready to receive web socket frame
+  ST_WS_FRAME,   // Receiving WebSocket frame
+  ST_WS_CLOSE,   // Closing the connection WebSocket connection
+  ST_DONE,       // Parsing is complete and successful
+  ST_ERR,        // Parsing encountered invalid syntax.
+  MAX_STATES
+};
+
+// State transition table
+int parser_state[MAX_STATES][MAX_INPUTS] = {
+/* METHOD    */ { ST_URL,       ST_ERR,      ST_ERR,      ST_ERR,       ST_ERR,      ST_ERR,      ST_METHOD },
+/* URL       */ { ST_PROTO,     ST_ERR,      ST_ERR,      ST_URL,       ST_ERR,      ST_ERR,      ST_URL },
+/* PROTOCOL  */ { ST_ERR,       ST_HEADER,   ST_NAME,     ST_ERR,       ST_ERR,      ST_ERR,      ST_PROTO },
+/* HEADER    */ { ST_ERR,       ST_ERR,      ST_NAME,     ST_ERR,       ST_ERR,      ST_ERR,      ST_ERR },
+/* NAME      */ { ST_SEPARATOR, ST_DONE,     ST_ERR,      ST_SEPARATOR, ST_ERR,      ST_ERR,      ST_NAME },
+/* SEPARATOR */ { ST_SEPARATOR, ST_ERR,      ST_ERR,      ST_SEPARATOR, ST_ERR,      ST_ERR,      ST_VALUE },
+/* VALUE     */ { ST_VALUE,     ST_HEADER,   ST_NAME,     ST_VALUE,     ST_ERR,      ST_ERR,      ST_VALUE },
+/* WS_READY  */ { ST_ERR,       ST_ERR,      ST_ERR,      ST_ERR,       ST_WS_FRAME, ST_WS_CLOSE, ST_ERR},
+/* WS_FRAME  */ { ST_WS_FRAME,  ST_WS_FRAME, ST_WS_FRAME, ST_WS_FRAME,  ST_ERR,      ST_WS_READY, ST_WS_FRAME },
+/* WS_CLOSE  */ { ST_ERR,       ST_ERR,      ST_ERR,      ST_ERR,       ST_WS_CLOSE, ST_ERR,      ST_ERR },
+/* DONE      */ { ST_DONE,      ST_DONE,     ST_DONE,     ST_DONE,      ST_DONE,     ST_DONE,     ST_DONE },
+/* ERR       */ { ST_ERR,       ST_ERR,      ST_ERR,      ST_ERR,       ST_ERR,      ST_ERR,      ST_ERR }
+};
+
+// Convert an input character to the parser's input token.
+int charToInput(char ch) {
+  switch(ch) {
+    case ' ':
+      return INPUT_SPACE;
+    case '\r':
+      return INPUT_CR;
+    case '\n':
+      return INPUT_LF;
+    case ':':
+      return INPUT_COLON;
+    case 0x0:
+      return INPUT_00;
+    case static_cast<char>(-1):
+      return INPUT_FF;
+  }
+  return INPUT_DEFAULT;
+}
+
+HttpServerRequestInfo* HttpListenSocket::ParseHeaders() {
+  int pos = 0;
+  int data_len = recv_data_.length();
+  int state = is_web_socket_ ? ST_WS_READY : ST_METHOD;
+  scoped_ptr<HttpServerRequestInfo> info(new HttpServerRequestInfo());
+  std::string buffer;
+  std::string header_name;
+  std::string header_value;
+  while (pos < data_len) {
+    char ch = recv_data_[pos++];
+    int input = charToInput(ch);
+    int next_state = parser_state[state][input];
+
+    bool transition = (next_state != state);
+    if (transition) {
+      // Do any actions based on state transitions.
+      switch (state) {
+        case ST_METHOD:
+          info->method = buffer;
+          buffer.clear();
+          break;
+        case ST_URL:
+          info->path = buffer;
+          buffer.clear();
+          break;
+        case ST_PROTO:
+          // TODO(mbelshe): Deal better with parsing protocol.
+          DCHECK(buffer == "HTTP/1.1");
+          buffer.clear();
+          break;
+        case ST_NAME:
+          header_name = buffer;
+          buffer.clear();
+          break;
+        case ST_VALUE:
+          header_value = buffer;
+          // TODO(mbelshe): Deal better with duplicate headers
+          DCHECK(info->headers.find(header_name) == info->headers.end());
+          info->headers[header_name] = header_value;
+          buffer.clear();
+          break;
+        case ST_SEPARATOR:
+          buffer.append(&ch, 1);
+          break;
+        case ST_WS_FRAME:
+          recv_data_ = recv_data_.substr(pos);
+          info->data = buffer;
+          buffer.clear();
+          return info.release();
+          break;
+      }
+      state = next_state;
+    } else {
+      // Do any actions based on current state
+      switch (state) {
+        case ST_METHOD:
+        case ST_URL:
+        case ST_PROTO:
+        case ST_VALUE:
+        case ST_NAME:
+        case ST_WS_FRAME:
+          buffer.append(&ch, 1);
+          break;
+        case ST_DONE:
+          recv_data_ = recv_data_.substr(pos);
+          info->data = recv_data_;
+          recv_data_.clear();
+          return info.release();
+        case ST_WS_CLOSE:
+          is_web_socket_ = false;
+          return NULL;
+        case ST_ERR:
+          return NULL;
+      }
+    }
+  }
+  // No more characters, but we haven't finished parsing yet.
+  return NULL;
+}
+
+void HttpListenSocket::DidAccept(ListenSocket* server,
+                                 ListenSocket* connection) {
+  connection->AddRef();
+}
+
+void HttpListenSocket::DidRead(ListenSocket*,
+                               const char* data,
+                               int len) {
+  recv_data_.append(data, len);
+  while (recv_data_.length()) {
+    scoped_ptr<HttpServerRequestInfo> request(ParseHeaders());
+    if (!request.get())
+      break;
+
+    if (is_web_socket_) {
+      delegate_->OnWebSocketMessage(this, request->data);
+      continue;
+    }
+
+    std::string connection = GetHeaderValue(request.get(), "Connection");
+    if (connection == "Upgrade") {
+      // Is this WebSocket and if yes, upgrade the connection.
+      std::string key1 = GetHeaderValue(request.get(), "Sec-WebSocket-Key1");
+      std::string key2 = GetHeaderValue(request.get(), "Sec-WebSocket-Key2");
+      if (!key1.empty() && !key2.empty()) {
+        delegate_->OnWebSocketRequest(this, request.get());
+        continue;
+      }
+    }
+    delegate_->OnHttpRequest(this, request.get());
+  }
+}
+
+void HttpListenSocket::DidClose(ListenSocket* sock) {
+  sock->Release();
+  delegate_->OnClose(this);
+}
diff --git a/net/server/http_listen_socket.h b/net/server/http_listen_socket.h
new file mode 100644
index 0000000..5517af0
--- /dev/null
+++ b/net/server/http_listen_socket.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2010 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_SERVER_HTTP_LISTEN_SOCKET_H_
+#define NET_SERVER_HTTP_LISTEN_SOCKET_H_
+
+#include "net/base/listen_socket.h"
+
+class HttpServerRequestInfo;
+
+// Implements a simple HTTP listen socket on top of the raw socket interface.
+class HttpListenSocket : public ListenSocket,
+                         public ListenSocket::ListenSocketDelegate {
+ public:
+  class Delegate {
+   public:
+    virtual void OnHttpRequest(HttpListenSocket* socket,
+                               HttpServerRequestInfo* info) = 0;
+
+    virtual void OnWebSocketRequest(HttpListenSocket* socket,
+                                    HttpServerRequestInfo* info) = 0;
+
+    virtual void OnWebSocketMessage(HttpListenSocket* socket,
+                                    const std::string& data) = 0;
+
+    virtual void OnClose(HttpListenSocket* socket) = 0;
+   protected:
+    virtual ~Delegate() {}
+  };
+
+  static HttpListenSocket* Listen(const std::string& ip,
+                                  int port,
+                                  HttpListenSocket::Delegate* delegate);
+
+  void AcceptWebSocket(HttpServerRequestInfo* request);
+
+  void SendOverWebSocket(const std::string& data);
+
+  void Listen() { ListenSocket::Listen(); }
+  virtual void Accept();
+
+  // ListenSocketDelegate
+  virtual void DidAccept(ListenSocket* server, ListenSocket* connection);
+  virtual void DidRead(ListenSocket* connection, const char* data, int len);
+  virtual void DidClose(ListenSocket* sock);
+
+ private:
+  static const int kReadBufSize = 16 * 1024;
+  HttpListenSocket(SOCKET s, HttpListenSocket::Delegate* del);
+  virtual ~HttpListenSocket();
+
+  // Expects the raw data to be stored in recv_data_. If parsing is successful,
+  // will remove the data parsed from recv_data_, leaving only the unused
+  // recv data.
+  HttpServerRequestInfo* ParseHeaders();
+
+  HttpListenSocket::Delegate* delegate_;
+  bool is_web_socket_;
+  std::string recv_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpListenSocket);
+};
+
+#endif // NET_SERVER_HTTP_LISTEN_SOCKET_H_
diff --git a/net/server/http_server_request_info.h b/net/server/http_server_request_info.h
new file mode 100644
index 0000000..84f767f
--- /dev/null
+++ b/net/server/http_server_request_info.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2010 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_SERVER_HTTP_SERVER_REQUEST_INFO_H_
+#define NET_SERVER_HTTP_SERVER_REQUEST_INFO_H_
+
+#include <string>
+#include <map>
+
+#include "net/http/http_request_info.h"
+
+// Meta information about an HTTP request.
+// This is geared toward servers in that it keeps a map of the headers and
+// values rather than just a list of header strings (which net::HttpRequestInfo
+// does).
+class HttpServerRequestInfo {
+ public:
+  HttpServerRequestInfo() {}
+
+  // Request method.
+  std::string method;
+
+  // Request line.
+  std::string path;
+
+  // Request data.
+  std::string data;
+
+  // A map of the names -> values for HTTP headers.
+  typedef std::map<std::string, std::string> HeadersMap;
+  HeadersMap headers;
+};
+
+#endif  // NET_SERVER_HTTP_SERVER_REQUEST_INFO_H_
diff --git a/net/socket/client_socket.h b/net/socket/client_socket.h
index 28c7b4d..2bc1adb 100644
--- a/net/socket/client_socket.h
+++ b/net/socket/client_socket.h
@@ -1,25 +1,16 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_SOCKET_CLIENT_SOCKET_H_
 #define NET_SOCKET_CLIENT_SOCKET_H_
 
-#include "build/build_config.h"
-
-// For struct sockaddr and socklen_t.
-#if defined(OS_POSIX)
-#include <sys/types.h>
-#include <sys/socket.h>
-#elif defined(OS_WIN)
-#include <ws2tcpip.h>
-#endif
-
 #include "net/socket/socket.h"
 
 namespace net {
 
-class LoadLog;
+class AddressList;
+class BoundNetLog;
 
 class ClientSocket : public Socket {
  public:
@@ -37,7 +28,7 @@
   //
   // Connect may also be called again after a call to the Disconnect method.
   //
-  virtual int Connect(CompletionCallback* callback, LoadLog* load_log) = 0;
+  virtual int Connect(CompletionCallback* callback) = 0;
 
   // Called to disconnect a socket.  Does nothing if the socket is already
   // disconnected.  After calling Disconnect it is possible to call Connect
@@ -57,9 +48,11 @@
   // have been received.
   virtual bool IsConnectedAndIdle() const = 0;
 
-  // Identical to BSD socket call getpeername().
-  // Needed by ssl_client_socket_nss and ssl_client_socket_mac.
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen) = 0;
+  // Copies the peer address to |address| and returns a network error code.
+  virtual int GetPeerAddress(AddressList* address) const = 0;
+
+  // Gets the NetLog for this socket.
+  virtual const BoundNetLog& NetLog() const = 0;
 };
 
 }  // namespace net
diff --git a/net/socket/client_socket_factory.cc b/net/socket/client_socket_factory.cc
index 6a3a4cc..fbccfcb 100644
--- a/net/socket/client_socket_factory.cc
+++ b/net/socket/client_socket_factory.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -6,12 +6,14 @@
 
 #include "base/singleton.h"
 #include "build/build_config.h"
+#include "net/socket/client_socket_handle.h"
 #if defined(OS_WIN)
 #include "net/socket/ssl_client_socket_win.h"
 #elif defined(USE_NSS)
 #include "net/socket/ssl_client_socket_nss.h"
 #elif defined(OS_MACOSX)
 #include "net/socket/ssl_client_socket_mac.h"
+#include "net/socket/ssl_client_socket_nss.h"
 #endif
 #include "net/socket/tcp_client_socket.h"
 
@@ -20,7 +22,7 @@
 namespace {
 
 SSLClientSocket* DefaultSSLClientSocketFactory(
-    ClientSocket* transport_socket,
+    ClientSocketHandle* transport_socket,
     const std::string& hostname,
     const SSLConfig& ssl_config) {
 #if defined(OS_WIN)
@@ -28,25 +30,30 @@
 #elif defined(USE_NSS)
   return new SSLClientSocketNSS(transport_socket, hostname, ssl_config);
 #elif defined(OS_MACOSX)
-  return new SSLClientSocketMac(transport_socket, hostname, ssl_config);
+  // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using
+  // Mac OS X CDSA/CSSM yet (http://crbug.com/45369), so fall back on
+  // SSLClientSocketMac.
+  if (ssl_config.client_cert)
+    return new SSLClientSocketMac(transport_socket, hostname, ssl_config);
+
+  return new SSLClientSocketNSS(transport_socket, hostname, ssl_config);
 #else
   NOTIMPLEMENTED();
   return NULL;
 #endif
 }
 
-// True if we should use NSS instead of the system SSL library for SSL.
 SSLClientSocketFactory g_ssl_factory = DefaultSSLClientSocketFactory;
 
 class DefaultClientSocketFactory : public ClientSocketFactory {
  public:
   virtual ClientSocket* CreateTCPClientSocket(
-      const AddressList& addresses) {
-    return new TCPClientSocket(addresses);
+      const AddressList& addresses, NetLog* net_log) {
+    return new TCPClientSocket(addresses, net_log);
   }
 
   virtual SSLClientSocket* CreateSSLClientSocket(
-      ClientSocket* transport_socket,
+      ClientSocketHandle* transport_socket,
       const std::string& hostname,
       const SSLConfig& ssl_config) {
     return g_ssl_factory(transport_socket, hostname, ssl_config);
@@ -66,4 +73,14 @@
   g_ssl_factory = factory;
 }
 
+// Deprecated function (http://crbug.com/37810) that takes a ClientSocket.
+SSLClientSocket* ClientSocketFactory::CreateSSLClientSocket(
+    ClientSocket* transport_socket,
+    const std::string& hostname,
+    const SSLConfig& ssl_config) {
+  ClientSocketHandle* socket_handle = new ClientSocketHandle();
+  socket_handle->set_socket(transport_socket);
+  return CreateSSLClientSocket(socket_handle, hostname, ssl_config);
+}
+
 }  // namespace net
diff --git a/net/socket/client_socket_factory.h b/net/socket/client_socket_factory.h
index 988cf97..dddf1de 100644
--- a/net/socket/client_socket_factory.h
+++ b/net/socket/client_socket_factory.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -11,18 +11,14 @@
 
 class AddressList;
 class ClientSocket;
+class ClientSocketHandle;
+class NetLog;
 class SSLClientSocket;
 struct SSLConfig;
 
 // Callback function to create new SSLClientSocket objects.
 typedef SSLClientSocket* (*SSLClientSocketFactory)(
-    ClientSocket* transport_socket,
-    const std::string& hostname,
-    const SSLConfig& ssl_config);
-
-// Creates SSLClientSocketNSS objects.
-SSLClientSocket* SSLClientSocketNSSFactory(
-    ClientSocket* transport_socket,
+    ClientSocketHandle* transport_socket,
     const std::string& hostname,
     const SSLConfig& ssl_config);
 
@@ -33,13 +29,19 @@
   virtual ~ClientSocketFactory() {}
 
   virtual ClientSocket* CreateTCPClientSocket(
-      const AddressList& addresses) = 0;
+      const AddressList& addresses, NetLog* net_log) = 0;
 
   virtual SSLClientSocket* CreateSSLClientSocket(
-      ClientSocket* transport_socket,
+      ClientSocketHandle* transport_socket,
       const std::string& hostname,
       const SSLConfig& ssl_config) = 0;
 
+
+  // Deprecated function (http://crbug.com/37810) that takes a ClientSocket.
+  virtual SSLClientSocket* CreateSSLClientSocket(ClientSocket* transport_socket,
+                                                 const std::string& hostname,
+                                                 const SSLConfig& ssl_config);
+
   // Returns the default ClientSocketFactory.
   static ClientSocketFactory* GetDefaultFactory();
 
diff --git a/net/socket/client_socket_handle.cc b/net/socket/client_socket_handle.cc
index d283dd6..de2fd94 100644
--- a/net/socket/client_socket_handle.cc
+++ b/net/socket/client_socket_handle.cc
@@ -1,21 +1,24 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/socket/client_socket_handle.h"
 
 #include "base/compiler_specific.h"
+#include "base/histogram.h"
 #include "base/logging.h"
 #include "net/base/net_errors.h"
 #include "net/socket/client_socket_pool.h"
+#include "net/socket/client_socket_pool_histograms.h"
 
 namespace net {
 
 ClientSocketHandle::ClientSocketHandle()
-    : socket_(NULL),
+    : is_initialized_(false),
       is_reused_(false),
       ALLOW_THIS_IN_INITIALIZER_LIST(
-          callback_(this, &ClientSocketHandle::OnIOComplete)) {}
+          callback_(this, &ClientSocketHandle::OnIOComplete)),
+      is_ssl_error_(false) {}
 
 ClientSocketHandle::~ClientSocketHandle() {
   Reset();
@@ -23,31 +26,48 @@
 
 void ClientSocketHandle::Reset() {
   ResetInternal(true);
+  ResetErrorState();
 }
 
 void ClientSocketHandle::ResetInternal(bool cancel) {
   if (group_name_.empty())  // Was Init called?
     return;
-  if (socket_.get()) {
-    // If we've still got a socket, release it back to the ClientSocketPool so
-    // it can be deleted or reused.
-    pool_->ReleaseSocket(group_name_, release_socket());
+  if (is_initialized()) {
+    // Because of http://crbug.com/37810 we may not have a pool, but have
+    // just a raw socket.
+    socket_->NetLog().EndEvent(NetLog::TYPE_SOCKET_IN_USE, NULL);
+    if (pool_)
+      // If we've still got a socket, release it back to the ClientSocketPool so
+      // it can be deleted or reused.
+      pool_->ReleaseSocket(group_name_, release_socket(), pool_id_);
   } else if (cancel) {
-    // If we did not get initialized yet, so we've got a socket request pending.
+    // If we did not get initialized yet, we've got a socket request pending.
     // Cancel it.
     pool_->CancelRequest(group_name_, this);
   }
+  is_initialized_ = false;
   group_name_.clear();
   is_reused_ = false;
   user_callback_ = NULL;
   pool_ = NULL;
   idle_time_ = base::TimeDelta();
   init_time_ = base::TimeTicks();
+  setup_time_ = base::TimeDelta();
+  pool_id_ = -1;
+}
+
+void ClientSocketHandle::ResetErrorState() {
+  is_ssl_error_ = false;
+  ssl_error_response_info_ = HttpResponseInfo();
 }
 
 LoadState ClientSocketHandle::GetLoadState() const {
   CHECK(!is_initialized());
   CHECK(!group_name_.empty());
+  // Because of http://crbug.com/37810  we may not have a pool, but have
+  // just a raw socket.
+  if (!pool_)
+    return LOAD_STATE_IDLE;
   return pool_->GetLoadState(group_name_, this);
 }
 
@@ -59,9 +79,43 @@
 }
 
 void ClientSocketHandle::HandleInitCompletion(int result) {
-  CHECK(ERR_IO_PENDING != result);
-  if (result != OK)
-    ResetInternal(false);  // The request failed, so there's nothing to cancel.
+  CHECK_NE(ERR_IO_PENDING, result);
+  if (result != OK) {
+    if (!socket_.get())
+      ResetInternal(false);  // Nothing to cancel since the request failed.
+    else
+      is_initialized_ = true;
+    return;
+  }
+  is_initialized_ = true;
+  CHECK_NE(-1, pool_id_) << "Pool should have set |pool_id_| to a valid value.";
+  setup_time_ = base::TimeTicks::Now() - init_time_;
+
+  scoped_refptr<ClientSocketPoolHistograms> histograms = pool_->histograms();
+  histograms->AddSocketType(reuse_type());
+  switch (reuse_type()) {
+    case ClientSocketHandle::UNUSED:
+      histograms->AddRequestTime(setup_time());
+      break;
+    case ClientSocketHandle::UNUSED_IDLE:
+      histograms->AddUnusedIdleTime(idle_time());
+      break;
+    case ClientSocketHandle::REUSED_IDLE:
+      histograms->AddReusedIdleTime(idle_time());
+      break;
+    default:
+      NOTREACHED();
+      break;
+  }
+
+  // Broadcast that the socket has been acquired.
+  // TODO(eroman): This logging is not complete, in particular set_socket() and
+  // release() socket. It ends up working though, since those methods are being
+  // used to layer sockets (and the destination sources are the same).
+  DCHECK(socket_.get());
+  socket_->NetLog().BeginEvent(
+      NetLog::TYPE_SOCKET_IN_USE,
+      new NetLogSourceParameter("source_dependency", requesting_source_));
 }
 
 }  // namespace net
diff --git a/net/socket/client_socket_handle.h b/net/socket/client_socket_handle.h
index bf7bf8a..7fdf784 100644
--- a/net/socket/client_socket_handle.h
+++ b/net/socket/client_socket_handle.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -14,7 +14,9 @@
 #include "net/base/completion_callback.h"
 #include "net/base/load_states.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
 #include "net/base/request_priority.h"
+#include "net/http/http_response_info.h"
 #include "net/socket/client_socket.h"
 #include "net/socket/client_socket_pool.h"
 
@@ -53,17 +55,26 @@
   // This method returns ERR_IO_PENDING if it cannot complete synchronously, in
   // which case the consumer will be notified of completion via |callback|.
   //
+  // If the pool was not able to reuse an existing socket, the new socket
+  // may report a recoverable error.  In this case, the return value will
+  // indicate an error and the socket member will be set.  If it is determined
+  // that the error is not recoverable, the Disconnect method should be used
+  // on the socket, so that it does not get reused.
+  //
+  // A non-recoverable error may set additional state in the ClientSocketHandle
+  // to allow the caller to determine what went wrong.
+  //
   // Init may be called multiple times.
   //
-  // Profiling information for the request is saved to |load_log| if non-NULL.
+  // Profiling information for the request is saved to |net_log| if non-NULL.
   //
   template <typename SocketParams, typename PoolType>
   int Init(const std::string& group_name,
-           const SocketParams& socket_params,
+           const scoped_refptr<SocketParams>& socket_params,
            RequestPriority priority,
            CompletionCallback* callback,
-           PoolType* pool,
-           LoadLog* load_log);
+           const scoped_refptr<PoolType>& pool,
+           const BoundNetLog& net_log);
 
   // An initialized handle can be reset, which causes it to return to the
   // un-initialized state.  This releases the underlying socket, which in the
@@ -80,18 +91,39 @@
   LoadState GetLoadState() const;
 
   // Returns true when Init() has completed successfully.
-  bool is_initialized() const { return socket_ != NULL; }
+  bool is_initialized() const { return is_initialized_; }
 
   // Returns the time tick when Init() was called.
   base::TimeTicks init_time() const { return init_time_; }
 
+  // Returns the time between Init() and when is_initialized() becomes true.
+  base::TimeDelta setup_time() const { return setup_time_; }
+
   // Used by ClientSocketPool to initialize the ClientSocketHandle.
   void set_is_reused(bool is_reused) { is_reused_ = is_reused; }
   void set_socket(ClientSocket* s) { socket_.reset(s); }
   void set_idle_time(base::TimeDelta idle_time) { idle_time_ = idle_time; }
+  void set_pool_id(int id) { pool_id_ = id; }
+  void set_is_ssl_error(bool is_ssl_error) { is_ssl_error_ = is_ssl_error; }
+  void set_ssl_error_response_info(const HttpResponseInfo& ssl_error_state) {
+    ssl_error_response_info_ = ssl_error_state;
+  }
+
+  // Only valid if there is no |socket_|.
+  bool is_ssl_error() const {
+    DCHECK(socket_.get() == NULL);
+    return is_ssl_error_;
+  }
+  // On an ERR_PROXY_AUTH_REQUESTED error, the |headers| and |auth_challenge|
+  // fields are filled in. On an ERR_SSL_CLIENT_AUTH_CERT_NEEDED error,
+  // the |cert_request_info| field is set.
+  const HttpResponseInfo& ssl_error_response_info() const {
+    return ssl_error_response_info_;
+  }
 
   // These may only be used if is_initialized() is true.
   const std::string& group_name() const { return group_name_; }
+  int id() const { return pool_id_; }
   ClientSocket* socket() { return socket_.get(); }
   ClientSocket* release_socket() { return socket_.release(); }
   bool is_reused() const { return is_reused_; }
@@ -128,9 +160,14 @@
   void HandleInitCompletion(int result);
 
   // Resets the state of the ClientSocketHandle.  |cancel| indicates whether or
-  // not to try to cancel the request with the ClientSocketPool.
+  // not to try to cancel the request with the ClientSocketPool.  Does not
+  // reset the supplemental error state.
   void ResetInternal(bool cancel);
 
+  // Resets the supplemental error state.
+  void ResetErrorState();
+
+  bool is_initialized_;
   scoped_refptr<ClientSocketPool> pool_;
   scoped_ptr<ClientSocket> socket_;
   std::string group_name_;
@@ -138,7 +175,13 @@
   CompletionCallbackImpl<ClientSocketHandle> callback_;
   CompletionCallback* user_callback_;
   base::TimeDelta idle_time_;
+  int pool_id_;  // See ClientSocketPool::ReleaseSocket() for an explanation.
+  bool is_ssl_error_;
+  HttpResponseInfo ssl_error_response_info_;
   base::TimeTicks init_time_;
+  base::TimeDelta setup_time_;
+
+  NetLog::Source requesting_source_;
 
   DISALLOW_COPY_AND_ASSIGN(ClientSocketHandle);
 };
@@ -146,22 +189,25 @@
 // Template function implementation:
 template <typename SocketParams, typename PoolType>
 int ClientSocketHandle::Init(const std::string& group_name,
-                             const SocketParams& socket_params,
+                             const scoped_refptr<SocketParams>& socket_params,
                              RequestPriority priority,
                              CompletionCallback* callback,
-                             PoolType* pool,
-                             LoadLog* load_log) {
+                             const scoped_refptr<PoolType>& pool,
+                             const BoundNetLog& net_log) {
+  requesting_source_ = net_log.source();
+
   CHECK(!group_name.empty());
-  // Note that this will result in a link error if the SocketParams has not been
-  // registered for the PoolType via REGISTER_SOCKET_PARAMS_FOR_POOL (defined in
-  // client_socket_pool.h).
+  // Note that this will result in a compile error if the SocketParams has not
+  // been registered for the PoolType via REGISTER_SOCKET_PARAMS_FOR_POOL
+  // (defined in client_socket_pool.h).
   CheckIsValidSocketParamsForPool<PoolType, SocketParams>();
   ResetInternal(true);
+  ResetErrorState();
   pool_ = pool;
   group_name_ = group_name;
   init_time_ = base::TimeTicks::Now();
   int rv = pool_->RequestSocket(
-      group_name, &socket_params, priority, this, &callback_, load_log);
+      group_name, &socket_params, priority, this, &callback_, net_log);
   if (rv == ERR_IO_PENDING) {
     user_callback_ = callback;
   } else {
diff --git a/net/socket/client_socket_pool.cc b/net/socket/client_socket_pool.cc
new file mode 100644
index 0000000..4cefe8d
--- /dev/null
+++ b/net/socket/client_socket_pool.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2010 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/socket/client_socket_pool.h"

+

+namespace {

+

+// The maximum duration, in seconds, to keep used idle persistent sockets

+// alive.

+// TODO(ziadh): Change this timeout after getting histogram data on how long it

+// should be.

+int g_unused_idle_socket_timeout = 10;

+

+}  // namespace

+

+namespace net {

+

+// static

+int ClientSocketPool::unused_idle_socket_timeout() {

+  return g_unused_idle_socket_timeout;

+}

+

+// static

+void ClientSocketPool::set_unused_idle_socket_timeout(int timeout) {

+  g_unused_idle_socket_timeout = timeout;

+}

+

+}  // namespace net

diff --git a/net/socket/client_socket_pool.h b/net/socket/client_socket_pool.h
index 4bd1a58..646b5ed 100644
--- a/net/socket/client_socket_pool.h
+++ b/net/socket/client_socket_pool.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -9,7 +9,10 @@
 #include <map>
 #include <string>
 
+#include "base/basictypes.h"
 #include "base/ref_counted.h"
+#include "base/time.h"
+#include "base/template_util.h"
 #include "net/base/completion_callback.h"
 #include "net/base/host_resolver.h"
 #include "net/base/load_states.h"
@@ -19,6 +22,7 @@
 
 class ClientSocket;
 class ClientSocketHandle;
+class ClientSocketPoolHistograms;
 
 // A ClientSocketPool is used to restrict the number of sockets open at a time.
 // It also maintains a list of idle persistent sockets.
@@ -27,47 +31,64 @@
  public:
   // Requests a connected socket for a group_name.
   //
-  // There are four possible results from calling this function:
+  // There are five possible results from calling this function:
   // 1) RequestSocket returns OK and initializes |handle| with a reused socket.
   // 2) RequestSocket returns OK with a newly connected socket.
   // 3) RequestSocket returns ERR_IO_PENDING.  The handle will be added to a
   // wait list until a socket is available to reuse or a new socket finishes
   // connecting.  |priority| will determine the placement into the wait list.
   // 4) An error occurred early on, so RequestSocket returns an error code.
+  // 5) A recoverable error occurred while setting up the socket.  An error
+  // code is returned, but the |handle| is initialized with the new socket.
+  // The caller must recover from the error before using the connection, or
+  // Disconnect the socket before releasing or resetting the |handle|.
+  // The current recoverable errors are: the errors accepted by
+  // IsCertificateError(err) and PROXY_AUTH_REQUESTED when reported by
+  // HttpProxyClientSocketPool.
   //
   // If this function returns OK, then |handle| is initialized upon return.
   // The |handle|'s is_initialized method will return true in this case.  If a
   // ClientSocket was reused, then ClientSocketPool will call
   // |handle|->set_reused(true).  In either case, the socket will have been
   // allocated and will be connected.  A client might want to know whether or
-  // not the socket is reused in order to know whether or not he needs to
-  // perform SSL connection or tunnel setup or to request a new socket if he
-  // encounters an error with the reused socket.
+  // not the socket is reused in order to request a new socket if he encounters
+  // an error with the reused socket.
   //
   // If ERR_IO_PENDING is returned, then the callback will be used to notify the
   // client of completion.
   //
-  // Profiling information for the request is saved to |load_log| if non-NULL.
+  // Profiling information for the request is saved to |net_log| if non-NULL.
   virtual int RequestSocket(const std::string& group_name,
                             const void* params,
                             RequestPriority priority,
                             ClientSocketHandle* handle,
                             CompletionCallback* callback,
-                            LoadLog* load_log) = 0;
+                            const BoundNetLog& net_log) = 0;
 
   // Called to cancel a RequestSocket call that returned ERR_IO_PENDING.  The
   // same handle parameter must be passed to this method as was passed to the
   // RequestSocket call being cancelled.  The associated CompletionCallback is
-  // not run.
+  // not run.  However, for performance, we will let one ConnectJob complete
+  // and go idle.
   virtual void CancelRequest(const std::string& group_name,
-                             const ClientSocketHandle* handle) = 0;
+                             ClientSocketHandle* handle) = 0;
 
   // Called to release a socket once the socket is no longer needed.  If the
   // socket still has an established connection, then it will be added to the
   // set of idle sockets to be used to satisfy future RequestSocket calls.
-  // Otherwise, the ClientSocket is destroyed.
+  // Otherwise, the ClientSocket is destroyed.  |id| is used to differentiate
+  // between updated versions of the same pool instance.  The pool's id will
+  // change when it flushes, so it can use this |id| to discard sockets with
+  // mismatched ids.
   virtual void ReleaseSocket(const std::string& group_name,
-                             ClientSocket* socket) = 0;
+                             ClientSocket* socket,
+                             int id) = 0;
+
+  // This flushes all state from the ClientSocketPool.  This means that all
+  // idle and connecting sockets are discarded.  Active sockets being
+  // held by ClientSocketPool clients will be discarded when released back to
+  // the pool.
+  virtual void Flush() = 0;
 
   // Called to close any idle connections held by the connection manager.
   virtual void CloseIdleSockets() = 0;
@@ -82,10 +103,23 @@
   virtual LoadState GetLoadState(const std::string& group_name,
                                  const ClientSocketHandle* handle) const = 0;
 
+  // Returns the maximum amount of time to wait before retrying a connect.
+  static const int kMaxConnectRetryIntervalMs = 250;
+
+  // The set of histograms specific to this pool.  We can't use the standard
+  // UMA_HISTOGRAM_* macros because they are callsite static.
+  virtual scoped_refptr<ClientSocketPoolHistograms> histograms() const = 0;
+
+  static int unused_idle_socket_timeout();
+  static void set_unused_idle_socket_timeout(int timeout);
+
  protected:
   ClientSocketPool() {}
   virtual ~ClientSocketPool() {}
 
+  // Return the connection timeout for this pool.
+  virtual base::TimeDelta ConnectionTimeout() const = 0;
+
  private:
   friend class base::RefCounted<ClientSocketPool>;
 
@@ -97,16 +131,27 @@
 // will provide a definition of CheckIsValidSocketParamsForPool for the
 // ClientSocketPool subtype and SocketParams pair.  Trying to use a SocketParams
 // type that has not been registered with the corresponding ClientSocketPool
-// subtype will result in a link time error stating that
-// CheckIsValidSocketParamsForPool with those template parameters is undefined.
+// subtype will result in a compile-time error.
 template <typename PoolType, typename SocketParams>
-void CheckIsValidSocketParamsForPool();
+struct SocketParamTraits : public base::false_type {
+};
+
+template <typename PoolType, typename SocketParams>
+void CheckIsValidSocketParamsForPool() {
+  COMPILE_ASSERT(!base::is_pointer<scoped_refptr<SocketParams> >::value,
+                 socket_params_cannot_be_pointer);
+  COMPILE_ASSERT((SocketParamTraits<PoolType,
+                                    scoped_refptr<SocketParams> >::value),
+                 invalid_socket_params_for_pool);
+}
 
 // Provides an empty definition for CheckIsValidSocketParamsForPool() which
 // should be optimized out by the compiler.
-#define REGISTER_SOCKET_PARAMS_FOR_POOL(pool_type, socket_params)         \
-template<>                                                                \
-inline void CheckIsValidSocketParamsForPool<pool_type, socket_params>() {}
+#define REGISTER_SOCKET_PARAMS_FOR_POOL(pool_type, socket_params)             \
+template<>                                                                    \
+struct SocketParamTraits<pool_type, scoped_refptr<socket_params> >            \
+    : public base::true_type {                                                \
+}
 
 }  // namespace net
 
diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc
index 4a5c88c..e4a611b 100644
--- a/net/socket/client_socket_pool_base.cc
+++ b/net/socket/client_socket_pool_base.cc
@@ -1,14 +1,17 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/socket/client_socket_pool_base.h"
 
 #include "base/compiler_specific.h"
+#include "base/format_macros.h"
 #include "base/message_loop.h"
+#include "base/stats_counters.h"
 #include "base/stl_util-inl.h"
+#include "base/string_util.h"
 #include "base/time.h"
-#include "net/base/load_log.h"
+#include "net/base/net_log.h"
 #include "net/base/net_errors.h"
 #include "net/socket/client_socket_handle.h"
 
@@ -24,9 +27,6 @@
 // some conditions.  See http://crbug.com/4606.
 const int kCleanupInterval = 10;  // DO NOT INCREASE THIS TIMEOUT.
 
-// The maximum size of the ConnectJob's LoadLog.
-const int kMaxNumLoadLogEntries = 50;
-
 }  // namespace
 
 namespace net {
@@ -34,69 +34,101 @@
 ConnectJob::ConnectJob(const std::string& group_name,
                        base::TimeDelta timeout_duration,
                        Delegate* delegate,
-                       LoadLog* load_log)
+                       const BoundNetLog& net_log)
     : group_name_(group_name),
       timeout_duration_(timeout_duration),
       delegate_(delegate),
-      load_log_(load_log) {
+      net_log_(net_log),
+      idle_(true) {
   DCHECK(!group_name.empty());
   DCHECK(delegate);
+  net_log.BeginEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB, NULL);
 }
 
 ConnectJob::~ConnectJob() {
-  if (delegate_) {
-    // If the delegate was not NULLed, then NotifyDelegateOfCompletion has
-    // not been called yet (hence we are cancelling).
-    LoadLog::AddEvent(load_log_, LoadLog::TYPE_CANCELLED);
-    LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB);
-  }
+  net_log().EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB, NULL);
 }
 
 int ConnectJob::Connect() {
   if (timeout_duration_ != base::TimeDelta())
     timer_.Start(timeout_duration_, this, &ConnectJob::OnTimeout);
 
-  LoadLog::BeginEvent(load_log_, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB);
+  idle_ = false;
+
+  LogConnectStart();
 
   int rv = ConnectInternal();
 
   if (rv != ERR_IO_PENDING) {
+    LogConnectCompletion(rv);
     delegate_ = NULL;
-    LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB);
   }
 
   return rv;
 }
 
+void ConnectJob::set_socket(ClientSocket* socket) {
+  if (socket) {
+    net_log().AddEvent(NetLog::TYPE_CONNECT_JOB_SET_SOCKET,
+                       new NetLogSourceParameter("source_dependency",
+                                                 socket->NetLog().source()));
+  }
+  socket_.reset(socket);
+}
+
 void ConnectJob::NotifyDelegateOfCompletion(int rv) {
   // The delegate will delete |this|.
   Delegate *delegate = delegate_;
   delegate_ = NULL;
 
-  LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB);
-
+  LogConnectCompletion(rv);
   delegate->OnConnectJobComplete(rv, this);
 }
 
+void ConnectJob::ResetTimer(base::TimeDelta remaining_time) {
+  timer_.Stop();
+  timer_.Start(remaining_time, this, &ConnectJob::OnTimeout);
+}
+
+void ConnectJob::LogConnectStart() {
+  net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT,
+                       new NetLogStringParameter("group_name", group_name_));
+}
+
+void ConnectJob::LogConnectCompletion(int net_error) {
+  scoped_refptr<NetLog::EventParameters> params;
+  if (net_error != OK)
+    params = new NetLogIntegerParameter("net_error", net_error);
+  net_log().EndEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT, params);
+}
+
 void ConnectJob::OnTimeout() {
   // Make sure the socket is NULL before calling into |delegate|.
   set_socket(NULL);
 
-  LoadLog::AddEvent(load_log_,
-      LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT);
+  net_log_.AddEvent(NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT, NULL);
 
   NotifyDelegateOfCompletion(ERR_TIMED_OUT);
 }
 
 namespace internal {
 
+ClientSocketPoolBaseHelper::Request::Request(
+    ClientSocketHandle* handle,
+    CompletionCallback* callback,
+    RequestPriority priority,
+    const BoundNetLog& net_log)
+    : handle_(handle), callback_(callback), priority_(priority),
+      net_log_(net_log) {}
+
+ClientSocketPoolBaseHelper::Request::~Request() {}
+
 ClientSocketPoolBaseHelper::ClientSocketPoolBaseHelper(
     int max_sockets,
     int max_sockets_per_group,
     base::TimeDelta unused_idle_socket_timeout,
     base::TimeDelta used_idle_socket_timeout,
-    ConnectJobFactory* connect_job_factory,
-    NetworkChangeNotifier* network_change_notifier)
+    ConnectJobFactory* connect_job_factory)
     : idle_socket_count_(0),
       connecting_socket_count_(0),
       handed_out_socket_count_(0),
@@ -104,14 +136,14 @@
       max_sockets_per_group_(max_sockets_per_group),
       unused_idle_socket_timeout_(unused_idle_socket_timeout),
       used_idle_socket_timeout_(used_idle_socket_timeout),
-      may_have_stalled_group_(false),
       connect_job_factory_(connect_job_factory),
-      network_change_notifier_(network_change_notifier) {
+      backup_jobs_enabled_(false),
+      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+      pool_generation_number_(0) {
   DCHECK_LE(0, max_sockets_per_group);
   DCHECK_LE(max_sockets_per_group, max_sockets);
 
-  if (network_change_notifier_)
-    network_change_notifier_->AddObserver(this);
+  NetworkChangeNotifier::AddObserver(this);
 }
 
 ClientSocketPoolBaseHelper::~ClientSocketPoolBaseHelper() {
@@ -121,11 +153,11 @@
   // sockets or pending requests.  They should have all been cleaned up prior
   // to the manager being destroyed.
   CloseIdleSockets();
-  DCHECK(group_map_.empty());
+  CHECK(group_map_.empty());
+  DCHECK(pending_callback_map_.empty());
   DCHECK_EQ(0, connecting_socket_count_);
 
-  if (network_change_notifier_)
-    network_change_notifier_->RemoveObserver(this);
+  NetworkChangeNotifier::RemoveObserver(this);
 }
 
 // InsertRequestIntoQueue inserts the request into the queue based on
@@ -135,9 +167,6 @@
 // static
 void ClientSocketPoolBaseHelper::InsertRequestIntoQueue(
     const Request* r, RequestQueue* pending_requests) {
-  LoadLog::BeginEvent(r->load_log(),
-      LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE);
-
   RequestQueue::iterator it = pending_requests->begin();
   while (it != pending_requests->end() && r->priority() >= (*it)->priority())
     ++it;
@@ -149,10 +178,6 @@
 ClientSocketPoolBaseHelper::RemoveRequestFromQueue(
     RequestQueue::iterator it, RequestQueue* pending_requests) {
   const Request* req = *it;
-
-  LoadLog::EndEvent(req->load_log(),
-                    LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE);
-
   pending_requests->erase(it);
   return req;
 }
@@ -160,6 +185,23 @@
 int ClientSocketPoolBaseHelper::RequestSocket(
     const std::string& group_name,
     const Request* request) {
+  request->net_log().BeginEvent(NetLog::TYPE_SOCKET_POOL, NULL);
+  Group& group = group_map_[group_name];
+
+  int rv = RequestSocketInternal(group_name, request);
+  if (rv != ERR_IO_PENDING) {
+    request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
+    CHECK(!request->handle()->is_initialized());
+    delete request;
+  } else {
+    InsertRequestIntoQueue(request, &group.pending_requests);
+  }
+  return rv;
+}
+
+int ClientSocketPoolBaseHelper::RequestSocketInternal(
+    const std::string& group_name,
+    const Request* request) {
   DCHECK_GE(request->priority(), 0);
   CompletionCallback* const callback = request->callback();
   CHECK(callback);
@@ -167,77 +209,167 @@
   CHECK(handle);
   Group& group = group_map_[group_name];
 
-  // Can we make another active socket now?
-  if (ReachedMaxSocketsLimit() ||
-      !group.HasAvailableSocketSlot(max_sockets_per_group_)) {
-    if (ReachedMaxSocketsLimit()) {
-      // We could check if we really have a stalled group here, but it requires
-      // a scan of all groups, so just flip a flag here, and do the check later.
-      may_have_stalled_group_ = true;
+  // Try to reuse a socket.
+  if (AssignIdleSocketToGroup(&group, request))
+    return OK;
 
-      LoadLog::AddEvent(request->load_log(),
-          LoadLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS);
-    } else {
-      LoadLog::AddEvent(request->load_log(),
-          LoadLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP);
-    }
-    InsertRequestIntoQueue(request, &group.pending_requests);
+  // Can we make another active socket now?
+  if (!group.HasAvailableSocketSlot(max_sockets_per_group_)) {
+    request->net_log().AddEvent(
+        NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP, NULL);
     return ERR_IO_PENDING;
   }
 
-  // Try to reuse a socket.
-  while (!group.idle_sockets.empty()) {
-    IdleSocket idle_socket = group.idle_sockets.back();
-    group.idle_sockets.pop_back();
+  if (ReachedMaxSocketsLimit()) {
+    if (idle_socket_count() > 0) {
+      CloseOneIdleSocket();
+    } else {
+      // We could check if we really have a stalled group here, but it requires
+      // a scan of all groups, so just flip a flag here, and do the check later.
+      request->net_log().AddEvent(
+          NetLog::TYPE_SOCKET_POOL_STALLED_MAX_SOCKETS, NULL);
+      return ERR_IO_PENDING;
+    }
+  }
+
+  // We couldn't find a socket to reuse, so allocate and connect a new one.
+  scoped_ptr<ConnectJob> connect_job(
+      connect_job_factory_->NewConnectJob(group_name, *request, this));
+
+  int rv = connect_job->Connect();
+  if (rv == OK) {
+    LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
+    HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
+                  handle, base::TimeDelta(), &group, request->net_log());
+  } else if (rv == ERR_IO_PENDING) {
+    // If we don't have any sockets in this group, set a timer for potentially
+    // creating a new one.  If the SYN is lost, this backup socket may complete
+    // before the slow socket, improving end user latency.
+    if (group.IsEmpty() && !group.backup_job && backup_jobs_enabled_) {
+      group.backup_job = connect_job_factory_->NewConnectJob(group_name,
+                                                             *request,
+                                                             this);
+      StartBackupSocketTimer(group_name);
+    }
+
+    connecting_socket_count_++;
+
+    ConnectJob* job = connect_job.release();
+    group.jobs.insert(job);
+  } else {
+    LogBoundConnectJobToRequest(connect_job->net_log().source(), request);
+    connect_job->GetAdditionalErrorState(handle);
+    ClientSocket* error_socket = connect_job->ReleaseSocket();
+    if (error_socket) {
+      HandOutSocket(error_socket, false /* not reused */, handle,
+                    base::TimeDelta(), &group, request->net_log());
+    } else if (group.IsEmpty()) {
+      group_map_.erase(group_name);
+    }
+  }
+
+  return rv;
+}
+
+bool ClientSocketPoolBaseHelper::AssignIdleSocketToGroup(
+    Group* group, const Request* request) {
+  // Iterate through the list of idle sockets until we find one or exhaust
+  // the list.
+  while (!group->idle_sockets.empty()) {
+    IdleSocket idle_socket = group->idle_sockets.back();
+    group->idle_sockets.pop_back();
     DecrementIdleCount();
     if (idle_socket.socket->IsConnectedAndIdle()) {
       // We found one we can reuse!
       base::TimeDelta idle_time =
           base::TimeTicks::Now() - idle_socket.start_time;
       HandOutSocket(
-          idle_socket.socket, idle_socket.used, handle, idle_time, &group);
-      return OK;
+          idle_socket.socket, idle_socket.used, request->handle(), idle_time,
+          group, request->net_log());
+      return true;
     }
     delete idle_socket.socket;
   }
+  return false;
+}
 
-  // See if we already have enough connect jobs or sockets that will be released
-  // soon.
-  if (group.HasReleasingSockets()) {
-    InsertRequestIntoQueue(request, &group.pending_requests);
-    return ERR_IO_PENDING;
+// static
+void ClientSocketPoolBaseHelper::LogBoundConnectJobToRequest(
+    const NetLog::Source& connect_job_source, const Request* request) {
+  request->net_log().AddEvent(
+      NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
+      new NetLogSourceParameter("source_dependency", connect_job_source));
+}
+
+void ClientSocketPoolBaseHelper::StartBackupSocketTimer(
+    const std::string& group_name) {
+  CHECK(ContainsKey(group_map_, group_name));
+  Group& group = group_map_[group_name];
+
+  // Only allow one timer pending to create a backup socket.
+  if (group.backup_task)
+    return;
+
+  group.backup_task = method_factory_.NewRunnableMethod(
+      &ClientSocketPoolBaseHelper::OnBackupSocketTimerFired, group_name);
+  MessageLoop::current()->PostDelayedTask(FROM_HERE, group.backup_task,
+                                          ConnectRetryIntervalMs());
+}
+
+void ClientSocketPoolBaseHelper::OnBackupSocketTimerFired(
+    const std::string& group_name) {
+  CHECK(ContainsKey(group_map_, group_name));
+
+  Group& group = group_map_[group_name];
+
+  CHECK(group.backup_task);
+  group.backup_task = NULL;
+
+  CHECK(group.backup_job);
+
+  // If there are no more jobs pending, there is no work to do.
+  // If we've done our cleanups correctly, this should not happen.
+  if (group.jobs.empty()) {
+    NOTREACHED();
+    return;
   }
 
-  // We couldn't find a socket to reuse, so allocate and connect a new one.
-  scoped_refptr<LoadLog> job_load_log = new LoadLog(kMaxNumLoadLogEntries);
-
-  scoped_ptr<ConnectJob> connect_job(
-      connect_job_factory_->NewConnectJob(group_name, *request, this,
-                                          job_load_log));
-
-  int rv = connect_job->Connect();
-
-  if (rv != ERR_IO_PENDING && request->load_log())
-    request->load_log()->Append(job_load_log);
-
-  if (rv == OK) {
-    HandOutSocket(connect_job->ReleaseSocket(), false /* not reused */,
-                  handle, base::TimeDelta(), &group);
-  } else if (rv == ERR_IO_PENDING) {
-    connecting_socket_count_++;
-
-    ConnectJob* job = connect_job.release();
-    InsertRequestIntoQueue(request, &group.pending_requests);
-    group.jobs.insert(job);
-  } else if (group.IsEmpty()) {
-    group_map_.erase(group_name);
+  // If our backup job is waiting on DNS, or if we can't create any sockets
+  // right now due to limits, just reset the timer.
+  if (ReachedMaxSocketsLimit() ||
+      !group.HasAvailableSocketSlot(max_sockets_per_group_) ||
+      (*group.jobs.begin())->GetLoadState() == LOAD_STATE_RESOLVING_HOST) {
+    StartBackupSocketTimer(group_name);
+    return;
   }
 
-  return rv;
+  group.backup_job->net_log().AddEvent(NetLog::TYPE_SOCKET_BACKUP_CREATED,
+                                       NULL);
+  SIMPLE_STATS_COUNTER("socket.backup_created");
+  int rv = group.backup_job->Connect();
+  connecting_socket_count_++;
+  group.jobs.insert(group.backup_job);
+  ConnectJob* job = group.backup_job;
+  group.backup_job = NULL;
+  if (rv != ERR_IO_PENDING)
+    OnConnectJobComplete(rv, job);
 }
 
 void ClientSocketPoolBaseHelper::CancelRequest(
-    const std::string& group_name, const ClientSocketHandle* handle) {
+    const std::string& group_name, ClientSocketHandle* handle) {
+  PendingCallbackMap::iterator callback_it = pending_callback_map_.find(handle);
+  if (callback_it != pending_callback_map_.end()) {
+    int result = callback_it->second.result;
+    pending_callback_map_.erase(callback_it);
+    ClientSocket* socket = handle->release_socket();
+    if (socket) {
+      if (result != OK)
+        socket->Disconnect();
+      ReleaseSocket(handle->group_name(), socket, handle->id());
+    }
+    return;
+  }
+
   CHECK(ContainsKey(group_map_, group_name));
 
   Group& group = group_map_[group_name];
@@ -247,31 +379,20 @@
   for (; it != group.pending_requests.end(); ++it) {
     if ((*it)->handle() == handle) {
       const Request* req = RemoveRequestFromQueue(it, &group.pending_requests);
-      LoadLog::AddEvent(req->load_log(), LoadLog::TYPE_CANCELLED);
-      LoadLog::EndEvent(req->load_log(), LoadLog::TYPE_SOCKET_POOL);
+      req->net_log().AddEvent(NetLog::TYPE_CANCELLED, NULL);
+      req->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
       delete req;
-      if (group.jobs.size() > group.pending_requests.size() + 1) {
-        // TODO(willchan): Cancel the job in the earliest LoadState.
+
+      // We let the job run, unless we're at the socket limit.
+      if (group.jobs.size() && ReachedMaxSocketsLimit()) {
         RemoveConnectJob(*group.jobs.begin(), &group);
-        OnAvailableSocketSlot(group_name, &group);
+        CheckForStalledSocketGroups();
       }
-      return;
+      break;
     }
   }
 }
 
-void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
-                                               ClientSocket* socket) {
-  Group& group = group_map_[group_name];
-  group.num_releasing_sockets++;
-  DCHECK_LE(group.num_releasing_sockets, group.active_socket_count);
-  // Run this asynchronously to allow the caller to finish before we let
-  // another to begin doing work.  This also avoids nasty recursion issues.
-  // NOTE: We cannot refer to the handle argument after this method returns.
-  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
-      this, &ClientSocketPoolBaseHelper::DoReleaseSocket, group_name, socket));
-}
-
 void ClientSocketPoolBaseHelper::CloseIdleSockets() {
   CleanupIdleSockets(true);
 }
@@ -287,6 +408,9 @@
 LoadState ClientSocketPoolBaseHelper::GetLoadState(
     const std::string& group_name,
     const ClientSocketHandle* handle) const {
+  if (ContainsKey(pending_callback_map_, handle))
+    return LOAD_STATE_CONNECTING;
+
   if (!ContainsKey(group_map_, group_name)) {
     NOTREACHED() << "ClientSocketPool does not contain group: " << group_name
                  << " for handle: " << handle;
@@ -372,93 +496,88 @@
     timer_.Stop();
 }
 
-void ClientSocketPoolBaseHelper::DoReleaseSocket(const std::string& group_name,
-                                                 ClientSocket* socket) {
+void ClientSocketPoolBaseHelper::ReleaseSocket(const std::string& group_name,
+                                               ClientSocket* socket,
+                                               int id) {
   GroupMap::iterator i = group_map_.find(group_name);
   CHECK(i != group_map_.end());
 
   Group& group = i->second;
 
-  group.num_releasing_sockets--;
-  DCHECK_GE(group.num_releasing_sockets, 0);
-
-  CHECK(handed_out_socket_count_ > 0);
+  CHECK_GT(handed_out_socket_count_, 0);
   handed_out_socket_count_--;
 
-  CHECK(group.active_socket_count > 0);
+  CHECK_GT(group.active_socket_count, 0);
   group.active_socket_count--;
 
-  const bool can_reuse = socket->IsConnectedAndIdle();
+  const bool can_reuse = socket->IsConnectedAndIdle() &&
+      id == pool_generation_number_;
   if (can_reuse) {
+    // Add it to the idle list.
     AddIdleSocket(socket, true /* used socket */, &group);
+    OnAvailableSocketSlot(group_name, &group);
   } else {
     delete socket;
   }
 
-  const bool more_releasing_sockets = group.num_releasing_sockets > 0;
+  CheckForStalledSocketGroups();
+}
 
-  OnAvailableSocketSlot(group_name, &group);
-
-  // If there are no more releasing sockets, then we might have to process
-  // multiple available socket slots, since we stalled their processing until
-  // all sockets have been released.
-  if (more_releasing_sockets)
+void ClientSocketPoolBaseHelper::CheckForStalledSocketGroups() {
+  // If we have idle sockets, see if we can give one to the top-stalled group.
+  std::string top_group_name;
+  Group* top_group = NULL;
+  if (!FindTopStalledGroup(&top_group, &top_group_name))
     return;
 
-  while (true) {
-    // We can't activate more sockets since we're already at our global limit.
-    if (ReachedMaxSocketsLimit())
+  if (ReachedMaxSocketsLimit()) {
+    if (idle_socket_count() > 0) {
+      CloseOneIdleSocket();
+    } else {
+      // We can't activate more sockets since we're already at our global
+      // limit.
       return;
-    
-    // |group| might now be deleted.
-    i = group_map_.find(group_name);
-    if (i == group_map_.end())
-      return;
-
-    group = i->second;
-    
-    // If we already have enough ConnectJobs to satisfy the pending requests,
-    // don't bother starting up more.
-    if (group.pending_requests.size() <= group.jobs.size())
-      return;
-
-    if (!group.HasAvailableSocketSlot(max_sockets_per_group_))
-      return;
-
-    OnAvailableSocketSlot(group_name, &group);
+    }
   }
+
+  // Note:  we don't loop on waking stalled groups.  If the stalled group is at
+  //        its limit, may be left with other stalled groups that could be
+  //        woken.  This isn't optimal, but there is no starvation, so to avoid
+  //        the looping we leave it at this.
+  OnAvailableSocketSlot(top_group_name, top_group);
 }
 
 // Search for the highest priority pending request, amongst the groups that
 // are not at the |max_sockets_per_group_| limit. Note: for requests with
 // the same priority, the winner is based on group hash ordering (and not
 // insertion order).
-int ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
-                                                    std::string* group_name) {
+bool ClientSocketPoolBaseHelper::FindTopStalledGroup(Group** group,
+                                                     std::string* group_name) {
   Group* top_group = NULL;
   const std::string* top_group_name = NULL;
-  int stalled_group_count = 0;
+  bool has_stalled_group = false;
   for (GroupMap::iterator i = group_map_.begin();
        i != group_map_.end(); ++i) {
     Group& group = i->second;
     const RequestQueue& queue = group.pending_requests;
     if (queue.empty())
       continue;
-    bool has_slot = group.HasAvailableSocketSlot(max_sockets_per_group_);
-    if (has_slot)
-      stalled_group_count++;
-    bool has_higher_priority = !top_group ||
-        group.TopPendingPriority() < top_group->TopPendingPriority();
-    if (has_slot && has_higher_priority) {
-      top_group = &group;
-      top_group_name = &i->first;
+    if (group.IsStalled(max_sockets_per_group_)) {
+      has_stalled_group = true;
+      bool has_higher_priority = !top_group ||
+          group.TopPendingPriority() < top_group->TopPendingPriority();
+      if (has_higher_priority) {
+        top_group = &group;
+        top_group_name = &i->first;
+      }
     }
   }
+
   if (top_group) {
     *group = top_group;
     *group_name = *top_group_name;
   }
-  return stalled_group_count;
+  return has_stalled_group;
 }
 
 void ClientSocketPoolBaseHelper::OnConnectJobComplete(
@@ -471,104 +590,104 @@
 
   scoped_ptr<ClientSocket> socket(job->ReleaseSocket());
 
-  scoped_refptr<LoadLog> job_load_log(job->load_log());
-  RemoveConnectJob(job, &group);
-
-  LoadLog::EndEvent(job_load_log, LoadLog::TYPE_SOCKET_POOL);
+  BoundNetLog job_log = job->net_log();
 
   if (result == OK) {
     DCHECK(socket.get());
+    RemoveConnectJob(job, &group);
     if (!group.pending_requests.empty()) {
       scoped_ptr<const Request> r(RemoveRequestFromQueue(
           group.pending_requests.begin(), &group.pending_requests));
-      if (r->load_log())
-        r->load_log()->Append(job_load_log);
+      LogBoundConnectJobToRequest(job_log.source(), r.get());
       HandOutSocket(
           socket.release(), false /* unused socket */, r->handle(),
-          base::TimeDelta(), &group);
-      r->callback()->Run(result);
+          base::TimeDelta(), &group, r->net_log());
+      r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, NULL);
+      InvokeUserCallbackLater(r->handle(), r->callback(), result);
     } else {
       AddIdleSocket(socket.release(), false /* unused socket */, &group);
       OnAvailableSocketSlot(group_name, &group);
+      CheckForStalledSocketGroups();
     }
   } else {
-    DCHECK(!socket.get());
+    // If we got a socket, it must contain error information so pass that
+    // up so that the caller can retrieve it.
+    bool handed_out_socket = false;
     if (!group.pending_requests.empty()) {
       scoped_ptr<const Request> r(RemoveRequestFromQueue(
           group.pending_requests.begin(), &group.pending_requests));
-      if (r->load_log())
-        r->load_log()->Append(job_load_log);
-      r->callback()->Run(result);
+      LogBoundConnectJobToRequest(job_log.source(), r.get());
+      job->GetAdditionalErrorState(r->handle());
+      RemoveConnectJob(job, &group);
+      if (socket.get()) {
+        handed_out_socket = true;
+        HandOutSocket(socket.release(), false /* unused socket */, r->handle(),
+                      base::TimeDelta(), &group, r->net_log());
+      }
+      r->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL,
+                            new NetLogIntegerParameter("net_error", result));
+      InvokeUserCallbackLater(r->handle(), r->callback(), result);
+    } else {
+      RemoveConnectJob(job, &group);
     }
-    MaybeOnAvailableSocketSlot(group_name);
+    if (!handed_out_socket) {
+      OnAvailableSocketSlot(group_name, &group);
+      CheckForStalledSocketGroups();
+    }
   }
 }
 
 void ClientSocketPoolBaseHelper::OnIPAddressChanged() {
+  Flush();
+}
+
+void ClientSocketPoolBaseHelper::Flush() {
+  pool_generation_number_++;
+  CancelAllConnectJobs();
   CloseIdleSockets();
 }
 
-void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob *job,
+void ClientSocketPoolBaseHelper::RemoveConnectJob(const ConnectJob* job,
                                                   Group* group) {
-  CHECK(connecting_socket_count_ > 0);
+  CHECK_GT(connecting_socket_count_, 0);
   connecting_socket_count_--;
 
+  DCHECK(group);
+  DCHECK(ContainsKey(group->jobs, job));
+  group->jobs.erase(job);
+
+  // If we've got no more jobs for this group, then we no longer need a
+  // backup job either.
+  if (group->jobs.empty())
+    group->CleanupBackupJob();
+
   DCHECK(job);
   delete job;
-
-  if (group) {
-    DCHECK(ContainsKey(group->jobs, job));
-    group->jobs.erase(job);
-  }
-}
-
-void ClientSocketPoolBaseHelper::MaybeOnAvailableSocketSlot(
-    const std::string& group_name) {
-  GroupMap::iterator it = group_map_.find(group_name);
-  if (it != group_map_.end()) {
-    Group& group = it->second;
-    if (group.HasAvailableSocketSlot(max_sockets_per_group_))
-      OnAvailableSocketSlot(group_name, &group);
-  }
 }
 
 void ClientSocketPoolBaseHelper::OnAvailableSocketSlot(
     const std::string& group_name, Group* group) {
-  if (may_have_stalled_group_) {
-    std::string top_group_name;
-    Group* top_group = NULL;
-    int stalled_group_count = FindTopStalledGroup(&top_group, &top_group_name);
-    if (stalled_group_count <= 1)
-      may_have_stalled_group_ = false;
-    if (stalled_group_count >= 1)
-      ProcessPendingRequest(top_group_name, top_group);
-  } else if (!group->pending_requests.empty()) {
+  if (!group->pending_requests.empty())
     ProcessPendingRequest(group_name, group);
-    // |group| may no longer be valid after this point.  Be careful not to
-    // access it again.
-  } else if (group->IsEmpty()) {
-    // Delete |group| if no longer needed.  |group| will no longer be valid.
+
+  if (group->IsEmpty())
     group_map_.erase(group_name);
-  }
 }
 
 void ClientSocketPoolBaseHelper::ProcessPendingRequest(
     const std::string& group_name, Group* group) {
-  scoped_ptr<const Request> r(RemoveRequestFromQueue(
-        group->pending_requests.begin(), &group->pending_requests));
-
-  int rv = RequestSocket(group_name, r.get());
-
+  int rv = RequestSocketInternal(group_name,
+                                 *group->pending_requests.begin());
   if (rv != ERR_IO_PENDING) {
-    LoadLog::EndEvent(r->load_log(), LoadLog::TYPE_SOCKET_POOL);
-    r->callback()->Run(rv);
-    if (rv != OK) {
-      // |group| may be invalid after the callback, we need to search
-      // |group_map_| again.
-      MaybeOnAvailableSocketSlot(group_name);
-    }
-  } else {
-    r.release();
+    scoped_ptr<const Request> request(RemoveRequestFromQueue(
+          group->pending_requests.begin(), &group->pending_requests));
+
+    scoped_refptr<NetLog::EventParameters> params;
+    if (rv != OK)
+      params = new NetLogIntegerParameter("net_error", rv);
+    request->net_log().EndEvent(NetLog::TYPE_SOCKET_POOL, params);
+    InvokeUserCallbackLater(
+        request->handle(), request->callback(), rv);
   }
 }
 
@@ -577,11 +696,24 @@
     bool reused,
     ClientSocketHandle* handle,
     base::TimeDelta idle_time,
-    Group* group) {
+    Group* group,
+    const BoundNetLog& net_log) {
   DCHECK(socket);
   handle->set_socket(socket);
   handle->set_is_reused(reused);
   handle->set_idle_time(idle_time);
+  handle->set_pool_id(pool_generation_number_);
+
+  if (reused) {
+    net_log.AddEvent(
+        NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET,
+        new NetLogIntegerParameter(
+            "idle_ms", static_cast<int>(idle_time.InMilliseconds())));
+  }
+
+  net_log.AddEvent(NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET,
+                   new NetLogSourceParameter(
+                       "source_dependency", socket->NetLog().source()));
 
   handed_out_socket_count_++;
   group->active_socket_count++;
@@ -605,6 +737,11 @@
     connecting_socket_count_ -= group.jobs.size();
     STLDeleteElements(&group.jobs);
 
+    if (group.backup_task) {
+      group.backup_task->Cancel();
+      group.backup_task = NULL;
+    }
+
     // Delete group if no longer needed.
     if (group.IsEmpty()) {
       group_map_.erase(i++);
@@ -616,9 +753,61 @@
 
 bool ClientSocketPoolBaseHelper::ReachedMaxSocketsLimit() const {
   // Each connecting socket will eventually connect and be handed out.
-  int total = handed_out_socket_count_ + connecting_socket_count_;
+  int total = handed_out_socket_count_ + connecting_socket_count_ +
+      idle_socket_count();
   DCHECK_LE(total, max_sockets_);
-  return total == max_sockets_;
+  if (total < max_sockets_)
+    return false;
+  LOG(WARNING) << "ReachedMaxSocketsLimit: " << total << "/" << max_sockets_;
+  return true;
+}
+
+void ClientSocketPoolBaseHelper::CloseOneIdleSocket() {
+  CHECK_GT(idle_socket_count(), 0);
+
+  for (GroupMap::iterator i = group_map_.begin(); i != group_map_.end(); ++i) {
+    Group& group = i->second;
+
+    if (!group.idle_sockets.empty()) {
+      std::deque<IdleSocket>::iterator j = group.idle_sockets.begin();
+      delete j->socket;
+      group.idle_sockets.erase(j);
+      DecrementIdleCount();
+      if (group.IsEmpty())
+        group_map_.erase(i);
+
+      return;
+    }
+  }
+
+  LOG(DFATAL) << "No idle socket found to close!.";
+}
+
+void ClientSocketPoolBaseHelper::InvokeUserCallbackLater(
+    ClientSocketHandle* handle, CompletionCallback* callback, int rv) {
+  CHECK(!ContainsKey(pending_callback_map_, handle));
+  pending_callback_map_[handle] = CallbackResultPair(callback, rv);
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(
+          this,
+          &ClientSocketPoolBaseHelper::InvokeUserCallback,
+          handle));
+}
+
+void ClientSocketPoolBaseHelper::InvokeUserCallback(
+    ClientSocketHandle* handle) {
+  PendingCallbackMap::iterator it = pending_callback_map_.find(handle);
+
+  // Exit if the request has already been cancelled.
+  if (it == pending_callback_map_.end())
+    return;
+
+  CHECK(!handle->is_initialized());
+  CompletionCallback* callback = it->second.callback;
+  int result = it->second.result;
+  pending_callback_map_.erase(it);
+  callback->Run(result);
 }
 
 }  // namespace internal
diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h
index d4e9deb..ef4b115 100644
--- a/net/socket/client_socket_pool_base.h
+++ b/net/socket/client_socket_pool_base.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 //
@@ -28,15 +28,17 @@
 #include <string>
 
 #include "base/basictypes.h"
+#include "base/compiler_specific.h"
 #include "base/ref_counted.h"
 #include "base/scoped_ptr.h"
+#include "base/task.h"
 #include "base/time.h"
 #include "base/timer.h"
 #include "net/base/address_list.h"
 #include "net/base/completion_callback.h"
-#include "net/base/load_log.h"
 #include "net/base/load_states.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
 #include "net/base/network_change_notifier.h"
 #include "net/base/request_priority.h"
 #include "net/socket/client_socket.h"
@@ -67,12 +69,12 @@
   ConnectJob(const std::string& group_name,
              base::TimeDelta timeout_duration,
              Delegate* delegate,
-             LoadLog* load_log);
+             const BoundNetLog& net_log);
   virtual ~ConnectJob();
 
   // Accessors
   const std::string& group_name() const { return group_name_; }
-  LoadLog* load_log() { return load_log_; }
+  const BoundNetLog& net_log() { return net_log_; }
 
   // Releases |socket_| to the client.  On connection error, this should return
   // NULL.
@@ -88,14 +90,23 @@
 
   virtual LoadState GetLoadState() const = 0;
 
+  // If Connect returns an error (or OnConnectJobComplete reports an error
+  // result) this method will be called, allowing the pool to add
+  // additional error state to the ClientSocketHandle (post late-binding).
+  virtual void GetAdditionalErrorState(ClientSocketHandle* handle) {}
+
  protected:
-  void set_socket(ClientSocket* socket) { socket_.reset(socket); }
+  void set_socket(ClientSocket* socket);
   ClientSocket* socket() { return socket_.get(); }
   void NotifyDelegateOfCompletion(int rv);
+  void ResetTimer(base::TimeDelta remainingTime);
 
  private:
   virtual int ConnectInternal() = 0;
 
+  void LogConnectStart();
+  void LogConnectCompletion(int net_error);
+
   // Alerts the delegate that the ConnectJob has timed out.
   void OnTimeout();
 
@@ -105,7 +116,9 @@
   base::OneShotTimer<ConnectJob> timer_;
   Delegate* delegate_;
   scoped_ptr<ClientSocket> socket_;
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
+  // A ConnectJob is idle until Connect() has been called.
+  bool idle_;
 
   DISALLOW_COPY_AND_ASSIGN(ConnectJob);
 };
@@ -127,22 +140,20 @@
     Request(ClientSocketHandle* handle,
             CompletionCallback* callback,
             RequestPriority priority,
-            LoadLog* load_log)
-        : handle_(handle), callback_(callback), priority_(priority),
-          load_log_(load_log) {}
+            const BoundNetLog& net_log);
 
-    virtual ~Request() {}
+    virtual ~Request();
 
     ClientSocketHandle* handle() const { return handle_; }
     CompletionCallback* callback() const { return callback_; }
     RequestPriority priority() const { return priority_; }
-    LoadLog* load_log() const { return load_log_.get(); }
+    const BoundNetLog& net_log() const { return net_log_; }
 
    private:
     ClientSocketHandle* const handle_;
     CompletionCallback* const callback_;
     const RequestPriority priority_;
-    const scoped_refptr<LoadLog> load_log_;
+    BoundNetLog net_log_;
 
     DISALLOW_COPY_AND_ASSIGN(Request);
   };
@@ -155,8 +166,9 @@
     virtual ConnectJob* NewConnectJob(
         const std::string& group_name,
         const Request& request,
-        ConnectJob::Delegate* delegate,
-        LoadLog* load_log) const = 0;
+        ConnectJob::Delegate* delegate) const = 0;
+
+    virtual base::TimeDelta ConnectionTimeout() const = 0;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory);
@@ -167,21 +179,24 @@
       int max_sockets_per_group,
       base::TimeDelta unused_idle_socket_timeout,
       base::TimeDelta used_idle_socket_timeout,
-      ConnectJobFactory* connect_job_factory,
-      NetworkChangeNotifier* network_change_notifier);
+      ConnectJobFactory* connect_job_factory);
 
   // See ClientSocketPool::RequestSocket for documentation on this function.
-  // Note that |request| must be heap allocated.  If ERR_IO_PENDING is returned,
-  // then ClientSocketPoolBaseHelper takes ownership of |request|.
+  // ClientSocketPoolBaseHelper takes ownership of |request|, which must be
+  // heap allocated.
   int RequestSocket(const std::string& group_name, const Request* request);
 
   // See ClientSocketPool::CancelRequest for documentation on this function.
   void CancelRequest(const std::string& group_name,
-                     const ClientSocketHandle* handle);
+                     ClientSocketHandle* handle);
 
   // See ClientSocketPool::ReleaseSocket for documentation on this function.
   void ReleaseSocket(const std::string& group_name,
-                     ClientSocket* socket);
+                     ClientSocket* socket,
+                     int id);
+
+  // See ClientSocketPool::Flush for documentation on this function.
+  void Flush();
 
   // See ClientSocketPool::CloseIdleSockets for documentation on this function.
   void CloseIdleSockets();
@@ -199,15 +214,18 @@
   LoadState GetLoadState(const std::string& group_name,
                          const ClientSocketHandle* handle) const;
 
+  int ConnectRetryIntervalMs() const {
+    // TODO(mbelshe): Make this tuned dynamically based on measured RTT.
+    //                For now, just use the max retry interval.
+    return ClientSocketPool::kMaxConnectRetryIntervalMs;
+  }
+
   // ConnectJob::Delegate methods:
   virtual void OnConnectJobComplete(int result, ConnectJob* job);
 
   // NetworkChangeNotifier::Observer methods:
   virtual void OnIPAddressChanged();
 
-  // For testing.
-  bool may_have_stalled_group() const { return may_have_stalled_group_; }
-
   int NumConnectJobsInGroup(const std::string& group_name) const {
     return group_map_.find(group_name)->second.jobs.size();
   }
@@ -216,11 +234,15 @@
   // sockets that timed out or can't be reused.  Made public for testing.
   void CleanupIdleSockets(bool force);
 
+  base::TimeDelta ConnectionTimeout() const {
+    return connect_job_factory_->ConnectionTimeout();
+  }
+
+  void EnableBackupJobs() { backup_jobs_enabled_ = true; }
+
  private:
   friend class base::RefCounted<ClientSocketPoolBaseHelper>;
 
-  ~ClientSocketPoolBaseHelper();
-
   // Entry for a persistent socket which became idle at time |start_time|.
   struct IdleSocket {
     IdleSocket() : socket(NULL), used(false) {}
@@ -244,14 +266,17 @@
 
   // A Group is allocated per group_name when there are idle sockets or pending
   // requests.  Otherwise, the Group object is removed from the map.
-  // |active_socket_count| tracks the number of sockets held by clients.  Of
-  // this number of sockets held by clients, some of them may be released soon,
-  // since ReleaseSocket() was called of them, but the DoReleaseSocket() task
-  // has not run yet for them.  |num_releasing_sockets| tracks these values,
-  // which is useful for not starting up new ConnectJobs when sockets may become
-  // available really soon.
+  // |active_socket_count| tracks the number of sockets held by clients.
   struct Group {
-    Group() : active_socket_count(0), num_releasing_sockets(0) {}
+    Group()
+        : active_socket_count(0),
+          backup_job(NULL),
+          backup_task(NULL) {
+    }
+
+    ~Group() {
+      CleanupBackupJob();
+    }
 
     bool IsEmpty() const {
       return active_socket_count == 0 && idle_sockets.empty() && jobs.empty() &&
@@ -263,26 +288,53 @@
           max_sockets_per_group;
     }
 
-    bool HasReleasingSockets() const {
-      return num_releasing_sockets > 0;
+    bool IsStalled(int max_sockets_per_group) const {
+      return HasAvailableSocketSlot(max_sockets_per_group) &&
+          pending_requests.size() > jobs.size();
     }
 
     RequestPriority TopPendingPriority() const {
       return pending_requests.front()->priority();
     }
 
+    void CleanupBackupJob() {
+      if (backup_job) {
+        delete backup_job;
+        backup_job = NULL;
+      }
+      if (backup_task) {
+        backup_task->Cancel();
+        backup_task = NULL;
+      }
+    }
+
     std::deque<IdleSocket> idle_sockets;
     std::set<const ConnectJob*> jobs;
     RequestQueue pending_requests;
     int active_socket_count;  // number of active sockets used by clients
-    // Number of sockets being released within one loop through the MessageLoop.
-    int num_releasing_sockets;
+    // A backup job in case the connect for this group takes too long.
+    ConnectJob* backup_job;
+    CancelableTask* backup_task;
   };
 
   typedef std::map<std::string, Group> GroupMap;
 
   typedef std::set<const ConnectJob*> ConnectJobSet;
 
+  struct CallbackResultPair {
+    CallbackResultPair() : callback(NULL), result(OK) {}
+    CallbackResultPair(CompletionCallback* callback_in, int result_in)
+        : callback(callback_in), result(result_in) {}
+
+    CompletionCallback* callback;
+    int result;
+  };
+
+  typedef std::map<const ClientSocketHandle*, CallbackResultPair>
+      PendingCallbackMap;
+
+  ~ClientSocketPoolBaseHelper();
+
   static void InsertRequestIntoQueue(const Request* r,
                                      RequestQueue* pending_requests);
   static const Request* RemoveRequestFromQueue(RequestQueue::iterator it,
@@ -292,14 +344,11 @@
   void IncrementIdleCount();
   void DecrementIdleCount();
 
-  // Called via PostTask by ReleaseSocket.
-  void DoReleaseSocket(const std::string& group_name, ClientSocket* socket);
-
   // Scans the group map for groups which have an available socket slot and
-  // at least one pending request. Returns number of groups found, and if found
-  // at least one, fills |group| and |group_name| with data of the stalled group
+  // at least one pending request. Returns true if any groups are stalled, and
+  // if so, fills |group| and |group_name| with data of the stalled group
   // having highest priority.
-  int FindTopStalledGroup(Group** group, std::string* group_name);
+  bool FindTopStalledGroup(Group** group, std::string* group_name);
 
   // Called when timer_ fires.  This method scans the idle sockets removing
   // sockets that timed out or can't be reused.
@@ -310,14 +359,10 @@
   // Removes |job| from |connect_job_set_|.  Also updates |group| if non-NULL.
   void RemoveConnectJob(const ConnectJob* job, Group* group);
 
-  // Same as OnAvailableSocketSlot except it looks up the Group first to see if
-  // it's there.
-  void MaybeOnAvailableSocketSlot(const std::string& group_name);
-
-  // Might delete the Group from |group_map_|.
+  // Tries to see if we can handle any more requests for |group|.
   void OnAvailableSocketSlot(const std::string& group_name, Group* group);
 
-  // Process a request from a group's pending_requests queue.
+  // Process a pending socket request for a group.
   void ProcessPendingRequest(const std::string& group_name, Group* group);
 
   // Assigns |socket| to |handle| and updates |group|'s counters appropriately.
@@ -325,7 +370,8 @@
                      bool reused,
                      ClientSocketHandle* handle,
                      base::TimeDelta time_idle,
-                     Group* group);
+                     Group* group,
+                     const BoundNetLog& net_log);
 
   // Adds |socket| to the list of idle sockets for |group|.  |used| indicates
   // whether or not the socket has previously been used.
@@ -337,11 +383,57 @@
   void CancelAllConnectJobs();
 
   // Returns true if we can't create any more sockets due to the total limit.
-  // TODO(phajdan.jr): Also take idle sockets into account.
   bool ReachedMaxSocketsLimit() const;
 
+  // This is the internal implementation of RequestSocket().  It differs in that
+  // it does not handle logging into NetLog of the queueing status of
+  // |request|.
+  int RequestSocketInternal(const std::string& group_name,
+                            const Request* request);
+
+  // Assigns an idle socket for the group to the request.
+  // Returns |true| if an idle socket is available, false otherwise.
+  bool AssignIdleSocketToGroup(Group* group, const Request* request);
+
+  static void LogBoundConnectJobToRequest(
+      const NetLog::Source& connect_job_source, const Request* request);
+
+  // Set a timer to create a backup socket if it takes too long to create one.
+  void StartBackupSocketTimer(const std::string& group_name);
+
+  // Called when the backup socket timer fires.
+  void OnBackupSocketTimerFired(const std::string& group_name);
+
+  // Closes one idle socket.  Picks the first one encountered.
+  // TODO(willchan): Consider a better algorithm for doing this.  Perhaps we
+  // should keep an ordered list of idle sockets, and close them in order.
+  // Requires maintaining more state.  It's not clear if it's worth it since
+  // I'm not sure if we hit this situation often.
+  void CloseOneIdleSocket();
+
+  // Checks if there are stalled socket groups that should be notified
+  // for possible wakeup.
+  void CheckForStalledSocketGroups();
+
+  // Posts a task to call InvokeUserCallback() on the next iteration through the
+  // current message loop.  Inserts |callback| into |pending_callback_map_|,
+  // keyed by |handle|.
+  void InvokeUserCallbackLater(
+      ClientSocketHandle* handle, CompletionCallback* callback, int rv);
+
+  // Invokes the user callback for |handle|.  By the time this task has run,
+  // it's possible that the request has been cancelled, so |handle| may not
+  // exist in |pending_callback_map_|.  We look up the callback and result code
+  // in |pending_callback_map_|.
+  void InvokeUserCallback(ClientSocketHandle* handle);
+
   GroupMap group_map_;
 
+  // Map of the ClientSocketHandles for which we have a pending Task to invoke a
+  // callback.  This is necessary since, before we invoke said callback, it's
+  // possible that the request is cancelled.
+  PendingCallbackMap pending_callback_map_;
+
   // Timer used to periodically prune idle sockets that timed out or can't be
   // reused.
   base::RepeatingTimer<ClientSocketPoolBaseHelper> timer_;
@@ -365,34 +457,22 @@
   const base::TimeDelta unused_idle_socket_timeout_;
   const base::TimeDelta used_idle_socket_timeout_;
 
-  // Until the maximum number of sockets limit is reached, a group can only
-  // have pending requests if it exceeds the "max sockets per group" limit.
-  //
-  // This means when a socket is released, the only pending requests that can
-  // be started next belong to the same group.
-  //
-  // However once the |max_sockets_| limit is reached, this stops being true:
-  // groups can now have pending requests without having first reached the
-  // |max_sockets_per_group_| limit. So choosing the next request involves
-  // selecting the highest priority request across *all* groups.
-  //
-  // Since reaching the maximum number of sockets is an edge case, we make note
-  // of when it happens, and thus avoid doing the slower "scan all groups"
-  // in the common case.
-  bool may_have_stalled_group_;
-
   const scoped_ptr<ConnectJobFactory> connect_job_factory_;
 
-  NetworkChangeNotifier* const network_change_notifier_;
+  // TODO(vandebo) Remove when backup jobs move to TCPClientSocketPool
+  bool backup_jobs_enabled_;
+
+  // A factory to pin the backup_job tasks.
+  ScopedRunnableMethodFactory<ClientSocketPoolBaseHelper> method_factory_;
+
+  // A unique id for the pool.  It gets incremented every time we Flush() the
+  // pool.  This is so that when sockets get released back to the pool, we can
+  // make sure that they are discarded rather than reused.
+  int pool_generation_number_;
 };
 
 }  // namespace internal
 
-// The maximum duration, in seconds, to keep unused idle persistent sockets
-// alive.
-// TODO(willchan): Change this timeout after getting histogram data on how
-// long it should be.
-static const int kUnusedIdleSocketTimeout = 10;
 // The maximum duration, in seconds, to keep used idle persistent sockets alive.
 static const int kUsedIdleSocketTimeout = 300;  // 5 minutes
 
@@ -404,16 +484,16 @@
     Request(ClientSocketHandle* handle,
             CompletionCallback* callback,
             RequestPriority priority,
-            const SocketParams& params,
-            LoadLog* load_log)
+            const scoped_refptr<SocketParams>& params,
+            const BoundNetLog& net_log)
         : internal::ClientSocketPoolBaseHelper::Request(
-            handle, callback, priority, load_log),
+            handle, callback, priority, net_log),
           params_(params) {}
 
-    const SocketParams& params() const { return params_; }
+    const scoped_refptr<SocketParams>& params() const { return params_; }
 
    private:
-    SocketParams params_;
+    scoped_refptr<SocketParams> params_;
   };
 
   class ConnectJobFactory {
@@ -424,8 +504,9 @@
     virtual ConnectJob* NewConnectJob(
         const std::string& group_name,
         const Request& request,
-        ConnectJob::Delegate* delegate,
-        LoadLog* load_log) const = 0;
+        ConnectJob::Delegate* delegate) const = 0;
+
+    virtual base::TimeDelta ConnectionTimeout() const = 0;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(ConnectJobFactory);
@@ -440,15 +521,15 @@
   ClientSocketPoolBase(
       int max_sockets,
       int max_sockets_per_group,
+      const scoped_refptr<ClientSocketPoolHistograms>& histograms,
       base::TimeDelta unused_idle_socket_timeout,
       base::TimeDelta used_idle_socket_timeout,
-      ConnectJobFactory* connect_job_factory,
-      NetworkChangeNotifier* network_change_notifier)
-      : helper_(new internal::ClientSocketPoolBaseHelper(
+      ConnectJobFactory* connect_job_factory)
+      : histograms_(histograms),
+        helper_(new internal::ClientSocketPoolBaseHelper(
           max_sockets, max_sockets_per_group,
           unused_idle_socket_timeout, used_idle_socket_timeout,
-          new ConnectJobFactoryAdaptor(connect_job_factory),
-          network_change_notifier)) {}
+          new ConnectJobFactoryAdaptor(connect_job_factory))) {}
 
   virtual ~ClientSocketPoolBase() {}
 
@@ -458,29 +539,23 @@
   // ClientSocketPoolBaseHelper::RequestSocket().  Note that the memory
   // ownership is transferred in the asynchronous (ERR_IO_PENDING) case.
   int RequestSocket(const std::string& group_name,
-                    const SocketParams& params,
+                    const scoped_refptr<SocketParams>& params,
                     RequestPriority priority,
                     ClientSocketHandle* handle,
                     CompletionCallback* callback,
-                    LoadLog* load_log) {
-    scoped_ptr<Request> request(
-        new Request(handle, callback, priority, params, load_log));
-    LoadLog::BeginEvent(load_log, LoadLog::TYPE_SOCKET_POOL);
-    int rv = helper_->RequestSocket(group_name, request.get());
-    if (rv == ERR_IO_PENDING)
-      request.release();
-    else
-      LoadLog::EndEvent(load_log, LoadLog::TYPE_SOCKET_POOL);
-    return rv;
+                    const BoundNetLog& net_log) {
+    Request* request = new Request(handle, callback, priority, params, net_log);
+    return helper_->RequestSocket(group_name, request);
   }
 
   void CancelRequest(const std::string& group_name,
-                     const ClientSocketHandle* handle) {
+                     ClientSocketHandle* handle) {
     return helper_->CancelRequest(group_name, handle);
   }
 
-  void ReleaseSocket(const std::string& group_name, ClientSocket* socket) {
-    return helper_->ReleaseSocket(group_name, socket);
+  void ReleaseSocket(const std::string& group_name, ClientSocket* socket,
+                     int id) {
+    return helper_->ReleaseSocket(group_name, socket, id);
   }
 
   void CloseIdleSockets() { return helper_->CloseIdleSockets(); }
@@ -500,11 +575,6 @@
     return helper_->OnConnectJobComplete(result, job);
   }
 
-  // For testing.
-  bool may_have_stalled_group() const {
-    return helper_->may_have_stalled_group();
-  }
-
   int NumConnectJobsInGroup(const std::string& group_name) const {
     return helper_->NumConnectJobsInGroup(group_name);
   }
@@ -513,6 +583,18 @@
     return helper_->CleanupIdleSockets(force);
   }
 
+  base::TimeDelta ConnectionTimeout() const {
+    return helper_->ConnectionTimeout();
+  }
+
+  scoped_refptr<ClientSocketPoolHistograms> histograms() const {
+    return histograms_;
+  }
+
+  void EnableBackupJobs() { helper_->EnableBackupJobs(); }
+
+  void Flush() { helper_->Flush(); }
+
  private:
   // This adaptor class exists to bridge the
   // internal::ClientSocketPoolBaseHelper::ConnectJobFactory and
@@ -533,21 +615,26 @@
     virtual ConnectJob* NewConnectJob(
         const std::string& group_name,
         const internal::ClientSocketPoolBaseHelper::Request& request,
-        ConnectJob::Delegate* delegate,
-        LoadLog* load_log) const {
+        ConnectJob::Delegate* delegate) const {
       const Request* casted_request = static_cast<const Request*>(&request);
       return connect_job_factory_->NewConnectJob(
-          group_name, *casted_request, delegate, load_log);
+          group_name, *casted_request, delegate);
+    }
+
+    virtual base::TimeDelta ConnectionTimeout() const {
+      return connect_job_factory_->ConnectionTimeout();
     }
 
     const scoped_ptr<ConnectJobFactory> connect_job_factory_;
   };
 
-  // One might ask why ClientSocketPoolBaseHelper is also refcounted if its
-  // containing ClientSocketPool is already refcounted.  The reason is because
-  // DoReleaseSocket() posts a task.  If ClientSocketPool gets deleted between
-  // the posting of the task and the execution, then we'll hit the DCHECK that
-  // |ClientSocketPoolBaseHelper::group_map_| is empty.
+  // Histograms for the pool
+  const scoped_refptr<ClientSocketPoolHistograms> histograms_;
+
+  // The reason for reference counting here is because the operations on
+  // the ClientSocketPoolBaseHelper which release sockets can cause the
+  // ClientSocketPoolBase<T> reference to drop to zero.  While we're deep
+  // in cleanup code, we'll often hold a reference to |self|.
   scoped_refptr<internal::ClientSocketPoolBaseHelper> helper_;
 
   DISALLOW_COPY_AND_ASSIGN(ClientSocketPoolBase);
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index 9f875ad..55f1949 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -1,21 +1,25 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/socket/client_socket_pool_base.h"
 
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/message_loop.h"
 #include "base/platform_thread.h"
+#include "base/ref_counted.h"
 #include "base/scoped_vector.h"
-#include "net/base/load_log.h"
-#include "net/base/load_log_unittest.h"
+#include "base/string_util.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/net_errors.h"
 #include "net/base/request_priority.h"
 #include "net/base/test_completion_callback.h"
 #include "net/socket/client_socket.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/socket_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -27,7 +31,12 @@
 const int kDefaultMaxSocketsPerGroup = 2;
 const net::RequestPriority kDefaultPriority = MEDIUM;
 
-typedef ClientSocketPoolBase<const void*> TestClientSocketPoolBase;
+class TestSocketParams : public base::RefCounted<TestSocketParams> {
+ private:
+  friend class base::RefCounted<TestSocketParams>;
+  ~TestSocketParams() {}
+};
+typedef ClientSocketPoolBase<TestSocketParams> TestClientSocketPoolBase;
 
 class MockClientSocket : public ClientSocket {
  public:
@@ -43,12 +52,12 @@
       IOBuffer* /* buf */, int /* len */, CompletionCallback* /* callback */) {
     return ERR_UNEXPECTED;
   }
-  virtual bool SetReceiveBufferSize(int32 size) { return true; };
-  virtual bool SetSendBufferSize(int32 size) { return true; };
+  virtual bool SetReceiveBufferSize(int32 size) { return true; }
+  virtual bool SetSendBufferSize(int32 size) { return true; }
 
   // ClientSocket methods:
 
-  virtual int Connect(CompletionCallback* callback, LoadLog* load_log) {
+  virtual int Connect(CompletionCallback* callback) {
     connected_ = true;
     return OK;
   }
@@ -57,13 +66,17 @@
   virtual bool IsConnected() const { return connected_; }
   virtual bool IsConnectedAndIdle() const { return connected_; }
 
-  virtual int GetPeerName(struct sockaddr* /* name */,
-                          socklen_t* /* namelen */) {
+  virtual int GetPeerAddress(AddressList* /* address */) const {
     return ERR_UNEXPECTED;
   }
 
+  virtual const BoundNetLog& NetLog() const {
+    return net_log_;
+  }
+
  private:
   bool connected_;
+  BoundNetLog net_log_;
 
   DISALLOW_COPY_AND_ASSIGN(MockClientSocket);
 };
@@ -74,13 +87,14 @@
  public:
   MockClientSocketFactory() : allocation_count_(0) {}
 
-  virtual ClientSocket* CreateTCPClientSocket(const AddressList& addresses) {
+  virtual ClientSocket* CreateTCPClientSocket(const AddressList& addresses,
+                                              NetLog* /* net_log */) {
     allocation_count_++;
     return NULL;
   }
 
   virtual SSLClientSocket* CreateSSLClientSocket(
-      ClientSocket* transport_socket,
+      ClientSocketHandle* transport_socket,
       const std::string& hostname,
       const SSLConfig& ssl_config) {
     NOTIMPLEMENTED();
@@ -106,39 +120,61 @@
     kMockPendingFailingJob,
     kMockWaitingJob,
     kMockAdvancingLoadStateJob,
+    kMockRecoverableJob,
+    kMockPendingRecoverableJob,
+    kMockAdditionalErrorStateJob,
+    kMockPendingAdditionalErrorStateJob,
   };
 
+  // The kMockPendingJob uses a slight delay before allowing the connect
+  // to complete.
+  static const int kPendingConnectDelay = 2;
+
   TestConnectJob(JobType job_type,
                  const std::string& group_name,
                  const TestClientSocketPoolBase::Request& request,
                  base::TimeDelta timeout_duration,
                  ConnectJob::Delegate* delegate,
                  MockClientSocketFactory* client_socket_factory,
-                 LoadLog* load_log)
-      : ConnectJob(group_name, timeout_duration, delegate, load_log),
+                 NetLog* net_log)
+      : ConnectJob(group_name, timeout_duration, delegate,
+                   BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
         job_type_(job_type),
         client_socket_factory_(client_socket_factory),
         method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
-        load_state_(LOAD_STATE_IDLE) {}
+        load_state_(LOAD_STATE_IDLE),
+        store_additional_error_state_(false) {}
 
   void Signal() {
-    DoConnect(waiting_success_, true /* async */);
+    DoConnect(waiting_success_, true /* async */, false /* recoverable */);
   }
 
   virtual LoadState GetLoadState() const { return load_state_; }
 
+  virtual void GetAdditionalErrorState(ClientSocketHandle* handle) {
+    if (store_additional_error_state_) {
+      // Set all of the additional error state fields in some way.
+      handle->set_is_ssl_error(true);
+      HttpResponseInfo info;
+      info.headers = new HttpResponseHeaders("");
+      handle->set_ssl_error_response_info(info);
+    }
+  }
+
  private:
   // ConnectJob methods:
 
   virtual int ConnectInternal() {
     AddressList ignored;
-    client_socket_factory_->CreateTCPClientSocket(ignored);
+    client_socket_factory_->CreateTCPClientSocket(ignored, NULL);
     set_socket(new MockClientSocket());
     switch (job_type_) {
       case kMockJob:
-        return DoConnect(true /* successful */, false /* sync */);
+        return DoConnect(true /* successful */, false /* sync */,
+                         false /* recoverable */);
       case kMockFailingJob:
-        return DoConnect(false /* error */, false /* sync */);
+        return DoConnect(false /* error */, false /* sync */,
+                         false /* recoverable */);
       case kMockPendingJob:
         set_load_state(LOAD_STATE_CONNECTING);
 
@@ -158,8 +194,9 @@
             method_factory_.NewRunnableMethod(
                 &TestConnectJob::DoConnect,
                 true /* successful */,
-                true /* async */),
-            2);
+                true /* async */,
+                false /* recoverable */),
+            kPendingConnectDelay);
         return ERR_IO_PENDING;
       case kMockPendingFailingJob:
         set_load_state(LOAD_STATE_CONNECTING);
@@ -168,7 +205,8 @@
             method_factory_.NewRunnableMethod(
                 &TestConnectJob::DoConnect,
                 false /* error */,
-                true  /* async */),
+                true  /* async */,
+                false /* recoverable */),
             2);
         return ERR_IO_PENDING;
       case kMockWaitingJob:
@@ -176,10 +214,39 @@
         waiting_success_ = true;
         return ERR_IO_PENDING;
       case kMockAdvancingLoadStateJob:
+        MessageLoop::current()->PostTask(
+            FROM_HERE,
+            method_factory_.NewRunnableMethod(
+                &TestConnectJob::AdvanceLoadState, load_state_));
+        return ERR_IO_PENDING;
+      case kMockRecoverableJob:
+        return DoConnect(false /* error */, false /* sync */,
+                         true /* recoverable */);
+      case kMockPendingRecoverableJob:
+        set_load_state(LOAD_STATE_CONNECTING);
         MessageLoop::current()->PostDelayedTask(
             FROM_HERE,
             method_factory_.NewRunnableMethod(
-                &TestConnectJob::AdvanceLoadState, load_state_),
+                &TestConnectJob::DoConnect,
+                false /* error */,
+                true  /* async */,
+                true  /* recoverable */),
+            2);
+        return ERR_IO_PENDING;
+      case kMockAdditionalErrorStateJob:
+        store_additional_error_state_ = true;
+        return DoConnect(false /* error */, false /* sync */,
+                         false /* recoverable */);
+      case kMockPendingAdditionalErrorStateJob:
+        set_load_state(LOAD_STATE_CONNECTING);
+        store_additional_error_state_ = true;
+        MessageLoop::current()->PostDelayedTask(
+            FROM_HERE,
+            method_factory_.NewRunnableMethod(
+                &TestConnectJob::DoConnect,
+                false /* error */,
+                true  /* async */,
+                false /* recoverable */),
             2);
         return ERR_IO_PENDING;
       default:
@@ -191,12 +258,14 @@
 
   void set_load_state(LoadState load_state) { load_state_ = load_state; }
 
-  int DoConnect(bool succeed, bool was_async) {
-    int result = ERR_CONNECTION_FAILED;
+  int DoConnect(bool succeed, bool was_async, bool recoverable) {
+    int result = OK;
     if (succeed) {
-      result = OK;
-      socket()->Connect(NULL, NULL);
+      socket()->Connect(NULL);
+    } else if (recoverable) {
+      result = ERR_PROXY_AUTH_REQUESTED;
     } else {
+      result = ERR_CONNECTION_FAILED;
       set_socket(NULL);
     }
 
@@ -205,17 +274,21 @@
     return result;
   }
 
+  // This function helps simulate the progress of load states on a ConnectJob.
+  // Each time it is called it advances the load state and posts a task to be
+  // called again.  It stops at the last connecting load state (the one
+  // before LOAD_STATE_SENDING_REQUEST).
   void AdvanceLoadState(LoadState state) {
     int tmp = state;
     tmp++;
-    state = static_cast<LoadState>(tmp);
-    set_load_state(state);
-    // Post a delayed task so RunAllPending() won't run it.
-    MessageLoop::current()->PostDelayedTask(
-        FROM_HERE,
-        method_factory_.NewRunnableMethod(&TestConnectJob::AdvanceLoadState,
-                                          state),
-        1 /* 1ms delay */);
+    if (tmp < LOAD_STATE_SENDING_REQUEST) {
+      state = static_cast<LoadState>(tmp);
+      set_load_state(state);
+      MessageLoop::current()->PostTask(
+          FROM_HERE,
+          method_factory_.NewRunnableMethod(&TestConnectJob::AdvanceLoadState,
+                                            state));
+    }
   }
 
   bool waiting_success_;
@@ -223,6 +296,7 @@
   MockClientSocketFactory* const client_socket_factory_;
   ScopedRunnableMethodFactory<TestConnectJob> method_factory_;
   LoadState load_state_;
+  bool store_additional_error_state_;
 
   DISALLOW_COPY_AND_ASSIGN(TestConnectJob);
 };
@@ -247,15 +321,18 @@
   virtual ConnectJob* NewConnectJob(
       const std::string& group_name,
       const TestClientSocketPoolBase::Request& request,
-      ConnectJob::Delegate* delegate,
-      LoadLog* load_log) const {
+      ConnectJob::Delegate* delegate) const {
     return new TestConnectJob(job_type_,
                               group_name,
                               request,
                               timeout_duration_,
                               delegate,
                               client_socket_factory_,
-                              load_log);
+                              NULL);
+  }
+
+  virtual base::TimeDelta ConnectionTimeout() const {
+    return timeout_duration_;
   }
 
  private:
@@ -271,12 +348,13 @@
   TestClientSocketPool(
       int max_sockets,
       int max_sockets_per_group,
+      const scoped_refptr<ClientSocketPoolHistograms>& histograms,
       base::TimeDelta unused_idle_socket_timeout,
       base::TimeDelta used_idle_socket_timeout,
       TestClientSocketPoolBase::ConnectJobFactory* connect_job_factory)
-      : base_(max_sockets, max_sockets_per_group,
+      : base_(max_sockets, max_sockets_per_group, histograms,
               unused_idle_socket_timeout, used_idle_socket_timeout,
-              connect_job_factory, NULL) {}
+              connect_job_factory) {}
 
   virtual int RequestSocket(
       const std::string& group_name,
@@ -284,21 +362,28 @@
       net::RequestPriority priority,
       ClientSocketHandle* handle,
       CompletionCallback* callback,
-      LoadLog* load_log) {
-    return base_.RequestSocket(
-        group_name, params, priority, handle, callback, load_log);
+      const BoundNetLog& net_log) {
+    const scoped_refptr<TestSocketParams>* casted_socket_params =
+        static_cast<const scoped_refptr<TestSocketParams>*>(params);
+    return base_.RequestSocket(group_name, *casted_socket_params, priority,
+                               handle, callback, net_log);
   }
 
   virtual void CancelRequest(
       const std::string& group_name,
-      const ClientSocketHandle* handle) {
+      ClientSocketHandle* handle) {
     base_.CancelRequest(group_name, handle);
   }
 
   virtual void ReleaseSocket(
       const std::string& group_name,
-      ClientSocket* socket) {
-    base_.ReleaseSocket(group_name, socket);
+      ClientSocket* socket,
+      int id) {
+    base_.ReleaseSocket(group_name, socket, id);
+  }
+
+  virtual void Flush() {
+    base_.Flush();
   }
 
   virtual void CloseIdleSockets() {
@@ -316,6 +401,14 @@
     return base_.GetLoadState(group_name, handle);
   }
 
+  virtual base::TimeDelta ConnectionTimeout() const {
+    return base_.ConnectionTimeout();
+  }
+
+  virtual scoped_refptr<ClientSocketPoolHistograms> histograms() const {
+    return base_.histograms();
+  }
+
   const TestClientSocketPoolBase* base() const { return &base_; }
 
   int NumConnectJobsInGroup(const std::string& group_name) const {
@@ -324,6 +417,8 @@
 
   void CleanupTimedOutIdleSockets() { base_.CleanupIdleSockets(false); }
 
+  void EnableBackupJobs() { base_.EnableBackupJobs(); }
+
  private:
   ~TestClientSocketPool() {}
 
@@ -334,7 +429,7 @@
 
 }  // namespace
 
-REGISTER_SOCKET_PARAMS_FOR_POOL(TestClientSocketPool, const void*);
+REGISTER_SOCKET_PARAMS_FOR_POOL(TestClientSocketPool, TestSocketParams);
 
 namespace {
 
@@ -382,13 +477,16 @@
 
 class ClientSocketPoolBaseTest : public ClientSocketPoolTest {
  protected:
-  ClientSocketPoolBaseTest() {}
+  ClientSocketPoolBaseTest()
+      : params_(new TestSocketParams()),
+        histograms_(new ClientSocketPoolHistograms("ClientSocketPoolTest")) {}
 
   void CreatePool(int max_sockets, int max_sockets_per_group) {
     CreatePoolWithIdleTimeouts(
         max_sockets,
         max_sockets_per_group,
-        base::TimeDelta::FromSeconds(kUnusedIdleSocketTimeout),
+        base::TimeDelta::FromSeconds(
+            ClientSocketPool::unused_idle_socket_timeout()),
         base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout));
   }
 
@@ -400,6 +498,7 @@
     connect_job_factory_ = new TestConnectJobFactory(&client_socket_factory_);
     pool_ = new TestClientSocketPool(max_sockets,
                                      max_sockets_per_group,
+                                     histograms_,
                                      unused_idle_socket_timeout,
                                      used_idle_socket_timeout,
                                      connect_job_factory_);
@@ -407,8 +506,8 @@
 
   int StartRequest(const std::string& group_name,
                    net::RequestPriority priority) {
-    return StartRequestUsingPool<TestClientSocketPool, const void*>(
-        pool_.get(), group_name, priority, NULL);
+    return StartRequestUsingPool<TestClientSocketPool, TestSocketParams>(
+        pool_, group_name, priority, params_);
   }
 
   virtual void TearDown() {
@@ -423,36 +522,28 @@
     // to delete |requests_| because the pool is reference counted and requests
     // keep reference to it.
     // TODO(willchan): Remove this part when late binding becomes the default.
+    TestClientSocketPool* pool = pool_.get();
     pool_ = NULL;
     requests_.reset();
+    pool = NULL;
 
     ClientSocketPoolTest::TearDown();
   }
 
   MockClientSocketFactory client_socket_factory_;
   TestConnectJobFactory* connect_job_factory_;
+  scoped_refptr<TestSocketParams> params_;
   scoped_refptr<TestClientSocketPool> pool_;
+  scoped_refptr<ClientSocketPoolHistograms> histograms_;
 };
 
-// Helper function which explicitly specifies the template parameters, since
-// the compiler will infer (in this case, incorrectly) that NULL is of type int.
-int InitHandle(ClientSocketHandle* handle,
-               const std::string& group_name,
-               net::RequestPriority priority,
-               CompletionCallback* callback,
-               TestClientSocketPool* pool,
-               LoadLog* load_log) {
-  return handle->Init<const void*, TestClientSocketPool>(
-      group_name, NULL, priority, callback, pool, load_log);
-}
-
 // Even though a timeout is specified, it doesn't time out on a synchronous
 // completion.
 TEST_F(ClientSocketPoolBaseTest, ConnectJob_NoTimeoutOnSynchronousCompletion) {
   TestConnectJobDelegate delegate;
   ClientSocketHandle ignored;
   TestClientSocketPoolBase::Request request(
-      &ignored, NULL, kDefaultPriority, NULL, NULL);
+      &ignored, NULL, kDefaultPriority, params_, BoundNetLog());
   scoped_ptr<TestConnectJob> job(
       new TestConnectJob(TestConnectJob::kMockJob,
                          "a",
@@ -467,9 +558,10 @@
 TEST_F(ClientSocketPoolBaseTest, ConnectJob_TimedOut) {
   TestConnectJobDelegate delegate;
   ClientSocketHandle ignored;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
+
   TestClientSocketPoolBase::Request request(
-      &ignored, NULL, kDefaultPriority, NULL, NULL);
+      &ignored, NULL, kDefaultPriority, params_, BoundNetLog());
   // Deleted by TestConnectJobDelegate.
   TestConnectJob* job =
       new TestConnectJob(TestConnectJob::kMockPendingJob,
@@ -478,19 +570,26 @@
                          base::TimeDelta::FromMicroseconds(1),
                          &delegate,
                          &client_socket_factory_,
-                         log);
+                         &log);
   ASSERT_EQ(ERR_IO_PENDING, job->Connect());
   PlatformThread::Sleep(1);
   EXPECT_EQ(ERR_TIMED_OUT, delegate.WaitForResult());
 
-  EXPECT_EQ(3u, log->entries().size());
+  EXPECT_EQ(6u, log.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 0, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
+      log.entries(), 0, NetLog::TYPE_SOCKET_POOL_CONNECT_JOB));
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 1, NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT));
   EXPECT_TRUE(LogContainsEvent(
-      *log, 1, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT,
-      LoadLog::PHASE_NONE));
+      log.entries(), 2, NetLog::TYPE_CONNECT_JOB_SET_SOCKET,
+      NetLog::PHASE_NONE));
+  EXPECT_TRUE(LogContainsEvent(
+      log.entries(), 3, NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_TIMED_OUT,
+      NetLog::PHASE_NONE));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 2, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
+      log.entries(), 4, NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_CONNECT));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), 5, NetLog::TYPE_SOCKET_POOL_CONNECT_JOB));
 }
 
 TEST_F(ClientSocketPoolBaseTest, BasicSynchronous) {
@@ -498,45 +597,60 @@
 
   TestCompletionCallback callback;
   ClientSocketHandle handle;
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  EXPECT_EQ(OK, InitHandle(&handle, "a", kDefaultPriority,
-                           &callback, pool_.get(), log));
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+
+  EXPECT_EQ(OK, handle.Init("a", params_, kDefaultPriority, &callback, pool_,
+                            log.bound()));
   EXPECT_TRUE(handle.is_initialized());
   EXPECT_TRUE(handle.socket());
   handle.Reset();
 
-  EXPECT_EQ(4u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKET_POOL));
+  EXPECT_EQ(4u, log.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 1, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
+      log.entries(), 0, NetLog::TYPE_SOCKET_POOL));
+  EXPECT_TRUE(LogContainsEvent(
+      log.entries(), 1, NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
+      NetLog::PHASE_NONE));
+  EXPECT_TRUE(LogContainsEvent(
+      log.entries(), 2, NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET,
+      NetLog::PHASE_NONE));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 2, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
-  EXPECT_TRUE(LogContainsEndEvent(*log, 3, LoadLog::TYPE_SOCKET_POOL));
+      log.entries(), 3, NetLog::TYPE_SOCKET_POOL));
 }
 
 TEST_F(ClientSocketPoolBaseTest, InitConnectionFailure) {
   CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
 
   connect_job_factory_->set_job_type(TestConnectJob::kMockFailingJob);
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  TestSocketRequest req(&request_order_, &completion_count_);
-  EXPECT_EQ(ERR_CONNECTION_FAILED,
-            InitHandle(req.handle(), "a", kDefaultPriority, &req,
-                       pool_.get(), log));
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
 
-  EXPECT_EQ(4u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKET_POOL));
+  TestSocketRequest req(&request_order_, &completion_count_);
+  // Set the additional error state members to ensure that they get cleared.
+  req.handle()->set_is_ssl_error(true);
+  HttpResponseInfo info;
+  info.headers = new HttpResponseHeaders("");
+  req.handle()->set_ssl_error_response_info(info);
+  EXPECT_EQ(ERR_CONNECTION_FAILED, req.handle()->Init("a", params_,
+                                                      kDefaultPriority, &req,
+                                                      pool_, log.bound()));
+  EXPECT_FALSE(req.handle()->socket());
+  EXPECT_FALSE(req.handle()->is_ssl_error());
+  EXPECT_TRUE(req.handle()->ssl_error_response_info().headers.get() == NULL);
+
+  EXPECT_EQ(3u, log.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 1, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
+      log.entries(), 0, NetLog::TYPE_SOCKET_POOL));
+  EXPECT_TRUE(LogContainsEvent(
+      log.entries(), 1, NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
+      NetLog::PHASE_NONE));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 2, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
-  EXPECT_TRUE(LogContainsEndEvent(*log, 3, LoadLog::TYPE_SOCKET_POOL));
+      log.entries(), 2, NetLog::TYPE_SOCKET_POOL));
 }
 
 TEST_F(ClientSocketPoolBaseTest, TotalLimit) {
   CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
 
-  // TODO(eroman): Check that the LoadLog contains this event.
+  // TODO(eroman): Check that the NetLog contains this event.
 
   EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
   EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
@@ -551,7 +665,7 @@
   EXPECT_EQ(ERR_IO_PENDING, StartRequest("f", kDefaultPriority));
   EXPECT_EQ(ERR_IO_PENDING, StartRequest("g", kDefaultPriority));
 
-  ReleaseAllConnections(KEEP_ALIVE);
+  ReleaseAllConnections(NO_KEEP_ALIVE);
 
   EXPECT_EQ(static_cast<int>(requests_.size()),
             client_socket_factory_.allocation_count());
@@ -572,7 +686,7 @@
 TEST_F(ClientSocketPoolBaseTest, TotalLimitReachedNewGroup) {
   CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
 
-  // TODO(eroman): Check that the LoadLog contains this event.
+  // TODO(eroman): Check that the NetLog contains this event.
 
   // Reach all limits: max total sockets, and max sockets per group.
   EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
@@ -587,7 +701,7 @@
   // Now create a new group and verify that we don't starve it.
   EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kDefaultPriority));
 
-  ReleaseAllConnections(KEEP_ALIVE);
+  ReleaseAllConnections(NO_KEEP_ALIVE);
 
   EXPECT_EQ(static_cast<int>(requests_.size()),
             client_socket_factory_.allocation_count());
@@ -618,11 +732,8 @@
   EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", MEDIUM));
   EXPECT_EQ(ERR_IO_PENDING, StartRequest("b", HIGHEST));
 
-  ReleaseAllConnections(KEEP_ALIVE);
+  ReleaseAllConnections(NO_KEEP_ALIVE);
 
-  // We're re-using one socket for group "a", and one for "b".
-  EXPECT_EQ(static_cast<int>(requests_.size()) - 2,
-            client_socket_factory_.allocation_count());
   EXPECT_EQ(requests_.size() - kDefaultMaxSockets, completion_count_);
 
   // First 4 requests don't have to wait, and finish in order.
@@ -656,10 +767,9 @@
   EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", LOW));
   EXPECT_EQ(ERR_IO_PENDING, StartRequest("b", HIGHEST));
 
-  ReleaseAllConnections(KEEP_ALIVE);
+  ReleaseAllConnections(NO_KEEP_ALIVE);
 
-  // We're re-using one socket for group "a", and one for "b".
-  EXPECT_EQ(static_cast<int>(requests_.size()) - 2,
+  EXPECT_EQ(static_cast<int>(requests_.size()),
             client_socket_factory_.allocation_count());
   EXPECT_EQ(requests_.size() - kDefaultMaxSockets, completion_count_);
 
@@ -704,7 +814,7 @@
   connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
   EXPECT_EQ(ERR_IO_PENDING, StartRequest("e", kDefaultPriority));
 
-  ReleaseAllConnections(KEEP_ALIVE);
+  ReleaseAllConnections(NO_KEEP_ALIVE);
 
   EXPECT_EQ(static_cast<int>(requests_.size()),
             client_socket_factory_.allocation_count());
@@ -719,56 +829,196 @@
   EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(6));
 }
 
-// Inside ClientSocketPoolBase we have a may_have_stalled_group flag,
-// which tells it to use more expensive, but accurate, group selection
-// algorithm. Make sure it doesn't get stuck in the "on" state.
-TEST_F(ClientSocketPoolBaseTest, MayHaveStalledGroupReset) {
+TEST_F(ClientSocketPoolBaseTest, CorrectlyCountStalledGroups) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSockets);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+
+  connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
+
+  EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
+
+  EXPECT_EQ(ERR_IO_PENDING, StartRequest("b", kDefaultPriority));
+  EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kDefaultPriority));
+
+  EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
+
+  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
+  EXPECT_EQ(kDefaultMaxSockets + 1, client_socket_factory_.allocation_count());
+  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
+  EXPECT_EQ(kDefaultMaxSockets + 2, client_socket_factory_.allocation_count());
+  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
+  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
+  EXPECT_EQ(kDefaultMaxSockets + 2, client_socket_factory_.allocation_count());
+}
+
+TEST_F(ClientSocketPoolBaseTest, StallAndThenCancelAndTriggerAvailableSocket) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSockets);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  EXPECT_EQ(ERR_IO_PENDING, handle.Init("a", params_, kDefaultPriority,
+                                        &callback, pool_, BoundNetLog()));
+
+  ClientSocketHandle handles[4];
+  for (size_t i = 0; i < arraysize(handles); ++i) {
+    TestCompletionCallback callback;
+    EXPECT_EQ(ERR_IO_PENDING, handles[i].Init("b", params_, kDefaultPriority,
+                                              &callback, pool_, BoundNetLog()));
+  }
+
+  // One will be stalled, cancel all the handles now.
+  // This should hit the OnAvailableSocketSlot() code where we previously had
+  // stalled groups, but no longer have any.
+  for (size_t i = 0; i < arraysize(handles); ++i)
+    handles[i].Reset();
+}
+
+TEST_F(ClientSocketPoolBaseTest, CancelStalledSocketAtSocketLimit) {
   CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
 
-  EXPECT_FALSE(pool_->base()->may_have_stalled_group());
+  {
+    ClientSocketHandle handles[kDefaultMaxSockets];
+    TestCompletionCallback callbacks[kDefaultMaxSockets];
+    for (int i = 0; i < kDefaultMaxSockets; ++i) {
+      EXPECT_EQ(OK, handles[i].Init(IntToString(i), params_, kDefaultPriority,
+                                    &callbacks[i], pool_, BoundNetLog()));
+    }
 
-  // Reach group socket limit.
-  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
-  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
-  EXPECT_FALSE(pool_->base()->may_have_stalled_group());
+    // Force a stalled group.
+    ClientSocketHandle stalled_handle;
+    TestCompletionCallback callback;
+    EXPECT_EQ(ERR_IO_PENDING, stalled_handle.Init("foo", params_,
+                                                  kDefaultPriority, &callback,
+                                                  pool_, BoundNetLog()));
 
-  // Reach total limit, but don't request more sockets.
-  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
-  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
-  EXPECT_FALSE(pool_->base()->may_have_stalled_group());
+    // Cancel the stalled request.
+    stalled_handle.Reset();
 
-  // Request one more socket while we are at the maximum sockets limit.
-  // This should flip the may_have_stalled_group flag.
-  EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kDefaultPriority));
-  EXPECT_TRUE(pool_->base()->may_have_stalled_group());
+    EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
+    EXPECT_EQ(0, pool_->IdleSocketCount());
 
-  // After releasing first connection for "a", we're still at the
-  // maximum sockets limit, but every group's pending queue is empty,
-  // so we reset the flag.
-  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
-  EXPECT_FALSE(pool_->base()->may_have_stalled_group());
+    // Dropping out of scope will close all handles and return them to idle.
+  }
 
-  // Requesting additional socket while at the total limit should
-  // flip the flag back to "on".
-  EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kDefaultPriority));
-  EXPECT_TRUE(pool_->base()->may_have_stalled_group());
+  EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
+  EXPECT_EQ(kDefaultMaxSockets, pool_->IdleSocketCount());
+}
 
-  // We'll request one more socket to verify that we don't reset the flag
-  // too eagerly.
-  EXPECT_EQ(ERR_IO_PENDING, StartRequest("d", kDefaultPriority));
-  EXPECT_TRUE(pool_->base()->may_have_stalled_group());
+TEST_F(ClientSocketPoolBaseTest, CancelPendingSocketAtSocketLimit) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
 
-  // We're at the maximum socket limit, and still have one request pending
-  // for "d". Flag should be "on".
-  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
-  EXPECT_TRUE(pool_->base()->may_have_stalled_group());
+  {
+    ClientSocketHandle handles[kDefaultMaxSockets];
+    for (int i = 0; i < kDefaultMaxSockets; ++i) {
+      TestCompletionCallback callback;
+      EXPECT_EQ(ERR_IO_PENDING, handles[i].Init(IntToString(i), params_,
+                                                kDefaultPriority, &callback,
+                                                pool_, BoundNetLog()));
+    }
 
-  // Now every group's pending queue should be empty again.
-  EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
-  EXPECT_FALSE(pool_->base()->may_have_stalled_group());
+    // Force a stalled group.
+    connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+    ClientSocketHandle stalled_handle;
+    TestCompletionCallback callback;
+    EXPECT_EQ(ERR_IO_PENDING, stalled_handle.Init("foo", params_,
+                                                  kDefaultPriority, &callback,
+                                                  pool_, BoundNetLog()));
 
-  ReleaseAllConnections(KEEP_ALIVE);
-  EXPECT_FALSE(pool_->base()->may_have_stalled_group());
+    // Since it is stalled, it should have no connect jobs.
+    EXPECT_EQ(0, pool_->NumConnectJobsInGroup("foo"));
+
+    // Cancel the stalled request.
+    handles[0].Reset();
+
+    // Now we should have a connect job.
+    EXPECT_EQ(1, pool_->NumConnectJobsInGroup("foo"));
+
+    // The stalled socket should connect.
+    EXPECT_EQ(OK, callback.WaitForResult());
+
+    EXPECT_EQ(kDefaultMaxSockets + 1,
+              client_socket_factory_.allocation_count());
+    EXPECT_EQ(0, pool_->IdleSocketCount());
+    EXPECT_EQ(0, pool_->NumConnectJobsInGroup("foo"));
+
+    // Dropping out of scope will close all handles and return them to idle.
+  }
+
+  EXPECT_EQ(1, pool_->IdleSocketCount());
+}
+
+TEST_F(ClientSocketPoolBaseTest, WaitForStalledSocketAtSocketLimit) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+  ClientSocketHandle stalled_handle;
+  TestCompletionCallback callback;
+  {
+    ClientSocketHandle handles[kDefaultMaxSockets];
+    for (int i = 0; i < kDefaultMaxSockets; ++i) {
+      TestCompletionCallback callback;
+      EXPECT_EQ(OK, handles[i].Init(StringPrintf("Take 2: %d", i), params_,
+                                    kDefaultPriority, &callback, pool_,
+                                    BoundNetLog()));
+    }
+
+    EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
+    EXPECT_EQ(0, pool_->IdleSocketCount());
+
+    // Now we will hit the socket limit.
+    EXPECT_EQ(ERR_IO_PENDING, stalled_handle.Init("foo", params_,
+                                                  kDefaultPriority, &callback,
+                                                  pool_, BoundNetLog()));
+
+    // Dropping out of scope will close all handles and return them to idle.
+  }
+
+  // But if we wait for it, the released idle sockets will be closed in
+  // preference of the waiting request.
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  EXPECT_EQ(kDefaultMaxSockets + 1, client_socket_factory_.allocation_count());
+  EXPECT_EQ(3, pool_->IdleSocketCount());
+}
+
+// Regression test for http://crbug.com/40952.
+TEST_F(ClientSocketPoolBaseTest, CloseIdleSocketAtSocketLimitDeleteGroup) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  pool_->EnableBackupJobs();
+  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+  for (int i = 0; i < kDefaultMaxSockets; ++i) {
+    ClientSocketHandle handle;
+    TestCompletionCallback callback;
+    EXPECT_EQ(OK, handle.Init(IntToString(i), params_, kDefaultPriority,
+                              &callback, pool_, BoundNetLog()));
+  }
+
+  // Flush all the DoReleaseSocket tasks.
+  MessageLoop::current()->RunAllPending();
+
+  // Stall a group.  Set a pending job so it'll trigger a backup job if we don't
+  // reuse a socket.
+  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+
+  // "0" is special here, since it should be the first entry in the sorted map,
+  // which is the one which we would close an idle socket for.  We shouldn't
+  // close an idle socket though, since we should reuse the idle socket.
+  EXPECT_EQ(OK, handle.Init("0", params_, kDefaultPriority, &callback, pool_,
+                            BoundNetLog()));
+
+  EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
+  EXPECT_EQ(kDefaultMaxSockets - 1, pool_->IdleSocketCount());
 }
 
 TEST_F(ClientSocketPoolBaseTest, PendingRequests) {
@@ -829,9 +1079,8 @@
 
   connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
   TestSocketRequest req(&request_order_, &completion_count_);
-  EXPECT_EQ(ERR_IO_PENDING,
-            InitHandle(req.handle(), "a", kDefaultPriority, &req,
-                       pool_.get(), NULL));
+  EXPECT_EQ(ERR_IO_PENDING, req.handle()->Init("a", params_, kDefaultPriority,
+                                               &req, pool_, BoundNetLog()));
   req.handle()->Reset();
 }
 
@@ -843,16 +1092,14 @@
   TestCompletionCallback callback;
   TestSocketRequest req(&request_order_, &completion_count_);
 
-  EXPECT_EQ(ERR_IO_PENDING,
-            InitHandle(&handle, "a", kDefaultPriority, &callback,
-                       pool_.get(), NULL));
+  EXPECT_EQ(ERR_IO_PENDING, handle.Init("a", params_, kDefaultPriority,
+                                        &callback, pool_, BoundNetLog()));
 
   handle.Reset();
 
   TestCompletionCallback callback2;
-  EXPECT_EQ(ERR_IO_PENDING,
-            InitHandle(&handle, "a", kDefaultPriority, &callback2,
-                       pool_.get(), NULL));
+  EXPECT_EQ(ERR_IO_PENDING, handle.Init("a", params_, kDefaultPriority,
+                                        &callback2, pool_, BoundNetLog()));
 
   EXPECT_EQ(OK, callback2.WaitForResult());
   EXPECT_FALSE(callback.have_result());
@@ -925,9 +1172,9 @@
       }
       within_callback_ = true;
       TestCompletionCallback next_job_callback;
-      int rv = InitHandle(
-          handle_, "a", kDefaultPriority, &next_job_callback, pool_.get(),
-          NULL);
+      scoped_refptr<TestSocketParams> params = new TestSocketParams();
+      int rv = handle_->Init("a", params, kDefaultPriority, &next_job_callback,
+                             pool_, BoundNetLog());
       switch (next_job_type_) {
         case TestConnectJob::kMockJob:
           EXPECT_EQ(OK, rv);
@@ -976,8 +1223,8 @@
   RequestSocketCallback callback(
       &handle, pool_.get(), connect_job_factory_,
       TestConnectJob::kMockPendingJob);
-  int rv = InitHandle(&handle, "a", kDefaultPriority, &callback,
-                      pool_.get(), NULL);
+  int rv = handle.Init("a", params_, kDefaultPriority, &callback, pool_,
+                       BoundNetLog());
   ASSERT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(OK, callback.WaitForResult());
@@ -990,8 +1237,8 @@
   ClientSocketHandle handle;
   RequestSocketCallback callback(
       &handle, pool_.get(), connect_job_factory_, TestConnectJob::kMockJob);
-  int rv = InitHandle(&handle, "a", kDefaultPriority, &callback,
-                      pool_.get(), NULL);
+  int rv = handle.Init("a", params_, kDefaultPriority, &callback, pool_,
+                       BoundNetLog());
   ASSERT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(OK, callback.WaitForResult());
@@ -1052,15 +1299,15 @@
   connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
 
   TestSocketRequest req(&request_order_, &completion_count_);
-  int rv = InitHandle(req.handle(), "a", kDefaultPriority, &req,
-                      pool_.get(), NULL);
+  int rv = req.handle()->Init("a", params_, kDefaultPriority, &req, pool_,
+                              BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Cancel the active request.
   req.handle()->Reset();
 
-  rv = InitHandle(req.handle(), "a", kDefaultPriority, &req,
-                  pool_.get(), NULL);
+  rv = req.handle()->Init("a", params_, kDefaultPriority, &req, pool_,
+                          BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_EQ(OK, req.WaitForResult());
 
@@ -1089,35 +1336,29 @@
   // Create a stalled group with high priorities.
   EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kHighPriority));
   EXPECT_EQ(ERR_IO_PENDING, StartRequest("c", kHighPriority));
-  EXPECT_TRUE(pool_->base()->may_have_stalled_group());
 
-  // Release the first two sockets from "a", which will make room
-  // for requests from "c". After that "a" will have no active sockets
-  // and one pending request.
+  // Release the first two sockets from "a".  Because this is a keepalive,
+  // the first release will unblock the pending request for "a".  The
+  // second release will unblock a request for "c", becaue it is the next
+  // high priority socket.
   EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
   EXPECT_TRUE(ReleaseOneConnection(KEEP_ALIVE));
 
   // Closing idle sockets should not get us into trouble, but in the bug
   // we were hitting a CHECK here.
-  EXPECT_EQ(2, pool_->IdleSocketCountInGroup("a"));
-  pool_->CloseIdleSockets();
   EXPECT_EQ(0, pool_->IdleSocketCountInGroup("a"));
-}
+  pool_->CloseIdleSockets();
 
-class ClientSocketPoolBaseTest_LateBinding : public ClientSocketPoolBaseTest {
- protected:
-  virtual void SetUp() {
-    ClientSocketPoolBaseTest::SetUp();
-  }
-};
+  MessageLoop::current()->RunAllPending();  // Run the released socket wakeups
+}
 
 TEST_F(ClientSocketPoolBaseTest, BasicAsynchronous) {
   CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
 
   connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
   TestSocketRequest req(&request_order_, &completion_count_);
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  int rv = InitHandle(req.handle(), "a", LOWEST, &req, pool_.get(), log);
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  int rv = req.handle()->Init("a", params_, LOWEST, &req, pool_, log.bound());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req.handle()));
   EXPECT_EQ(OK, req.WaitForResult());
@@ -1125,18 +1366,17 @@
   EXPECT_TRUE(req.handle()->socket());
   req.handle()->Reset();
 
-  EXPECT_EQ(6u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKET_POOL));
+  EXPECT_EQ(4u, log.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
+      log.entries(), 0, NetLog::TYPE_SOCKET_POOL));
+  EXPECT_TRUE(LogContainsEvent(
+      log.entries(), 1, NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
+      NetLog::PHASE_NONE));
+  EXPECT_TRUE(LogContainsEvent(
+      log.entries(), 2, NetLog::TYPE_SOCKET_POOL_BOUND_TO_SOCKET,
+      NetLog::PHASE_NONE));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 2, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 3, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log, 4, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log, 5, LoadLog::TYPE_SOCKET_POOL));
+      log.entries(), 3, NetLog::TYPE_SOCKET_POOL));
 }
 
 TEST_F(ClientSocketPoolBaseTest,
@@ -1145,76 +1385,54 @@
 
   connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
   TestSocketRequest req(&request_order_, &completion_count_);
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  EXPECT_EQ(ERR_IO_PENDING,
-            InitHandle(req.handle(), "a", kDefaultPriority, &req,
-                       pool_.get(), log));
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  // Set the additional error state members to ensure that they get cleared.
+  req.handle()->set_is_ssl_error(true);
+  HttpResponseInfo info;
+  info.headers = new HttpResponseHeaders("");
+  req.handle()->set_ssl_error_response_info(info);
+  EXPECT_EQ(ERR_IO_PENDING, req.handle()->Init("a", params_, kDefaultPriority,
+                                               &req, pool_, log.bound()));
   EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req.handle()));
   EXPECT_EQ(ERR_CONNECTION_FAILED, req.WaitForResult());
+  EXPECT_FALSE(req.handle()->is_ssl_error());
+  EXPECT_TRUE(req.handle()->ssl_error_response_info().headers.get() == NULL);
 
-  EXPECT_EQ(6u, log->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKET_POOL));
+  EXPECT_EQ(3u, log.entries().size());
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
+      log.entries(), 0, NetLog::TYPE_SOCKET_POOL));
+  EXPECT_TRUE(LogContainsEvent(
+      log.entries(), 1, NetLog::TYPE_SOCKET_POOL_BOUND_TO_CONNECT_JOB,
+      NetLog::PHASE_NONE));
   EXPECT_TRUE(LogContainsEndEvent(
-      *log, 2, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 3, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log, 4, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
-  EXPECT_TRUE(LogContainsEndEvent(*log, 5, LoadLog::TYPE_SOCKET_POOL));
+      log.entries(), 2, NetLog::TYPE_SOCKET_POOL));
 }
 
 TEST_F(ClientSocketPoolBaseTest, TwoRequestsCancelOne) {
+  // TODO(eroman): Add back the log expectations! Removed them because the
+  //               ordering is difficult, and some may fire during destructor.
   CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
 
   connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
   TestSocketRequest req(&request_order_, &completion_count_);
   TestSocketRequest req2(&request_order_, &completion_count_);
 
-  scoped_refptr<LoadLog> log1(new LoadLog(LoadLog::kUnbounded));
-  EXPECT_EQ(ERR_IO_PENDING,
-            InitHandle(req.handle(), "a", kDefaultPriority, &req,
-                       pool_.get(), log1));
-  scoped_refptr<LoadLog> log2(new LoadLog(LoadLog::kUnbounded));
-  EXPECT_EQ(ERR_IO_PENDING,
-            InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
-                       pool_.get(), log2));
+  EXPECT_EQ(ERR_IO_PENDING, req.handle()->Init("a", params_, kDefaultPriority,
+                                               &req, pool_, BoundNetLog()));
+  CapturingBoundNetLog log2(CapturingNetLog::kUnbounded);
+  EXPECT_EQ(ERR_IO_PENDING, req2.handle()->Init("a", params_, kDefaultPriority,
+                                                &req2, pool_, BoundNetLog()));
 
   req.handle()->Reset();
 
-  EXPECT_EQ(5u, log1->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log1, 0, LoadLog::TYPE_SOCKET_POOL));
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *log1, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log1, 2, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
-  EXPECT_TRUE(LogContainsEvent(
-      *log1, 3, LoadLog::TYPE_CANCELLED, LoadLog::PHASE_NONE));
-  EXPECT_TRUE(LogContainsEndEvent(*log1, 4, LoadLog::TYPE_SOCKET_POOL));
 
   // At this point, request 2 is just waiting for the connect job to finish.
-  EXPECT_EQ(2u, log2->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log2, 0, LoadLog::TYPE_SOCKET_POOL));
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *log2, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
 
   EXPECT_EQ(OK, req2.WaitForResult());
   req2.handle()->Reset();
 
   // Now request 2 has actually finished.
-  EXPECT_EQ(6u, log2->entries().size());
-  EXPECT_TRUE(LogContainsBeginEvent(*log2, 0, LoadLog::TYPE_SOCKET_POOL));
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *log2, 1, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log1, 2, LoadLog::TYPE_SOCKET_POOL_WAITING_IN_QUEUE));
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *log2, 3, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *log2, 4, LoadLog::TYPE_SOCKET_POOL_CONNECT_JOB));
-  EXPECT_TRUE(LogContainsEndEvent(*log2, 5, LoadLog::TYPE_SOCKET_POOL));
-
+  // TODO(eroman): Add back log expectations.
 }
 
 TEST_F(ClientSocketPoolBaseTest, CancelRequestLimitsJobs) {
@@ -1236,7 +1454,7 @@
   EXPECT_EQ(kDefaultMaxSocketsPerGroup, pool_->NumConnectJobsInGroup("a"));
 
   requests_[0]->handle()->Reset();
-  EXPECT_EQ(kDefaultMaxSocketsPerGroup - 1, pool_->NumConnectJobsInGroup("a"));
+  EXPECT_EQ(kDefaultMaxSocketsPerGroup, pool_->NumConnectJobsInGroup("a"));
 }
 
 // When requests and ConnectJobs are not coupled, the request will get serviced
@@ -1248,8 +1466,8 @@
   connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
 
   TestSocketRequest req1(&request_order_, &completion_count_);
-  int rv = InitHandle(req1.handle(), "a", kDefaultPriority,
-                      &req1, pool_.get(), NULL);
+  int rv = req1.handle()->Init("a", params_, kDefaultPriority, &req1, pool_,
+                               BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_EQ(OK, req1.WaitForResult());
 
@@ -1258,18 +1476,18 @@
   connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
 
   TestSocketRequest req2(&request_order_, &completion_count_);
-  rv = InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
-                  pool_.get(), NULL);
+  rv = req2.handle()->Init("a", params_, kDefaultPriority, &req2, pool_,
+                           BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   TestSocketRequest req3(&request_order_, &completion_count_);
-  rv = InitHandle(
-      req3.handle(), "a", kDefaultPriority, &req3, pool_.get(), NULL);
+  rv = req3.handle()->Init("a", params_, kDefaultPriority, &req3, pool_,
+                           BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Both Requests 2 and 3 are pending.  We release socket 1 which should
   // service request 2.  Request 3 should still be waiting.
   req1.handle()->Reset();
-  MessageLoop::current()->RunAllPending();  // Run the DoReleaseSocket()
+  MessageLoop::current()->RunAllPending();  // Run the released socket wakeups
   ASSERT_TRUE(req2.handle()->socket());
   EXPECT_EQ(OK, req2.WaitForResult());
   EXPECT_FALSE(req3.handle()->socket());
@@ -1294,21 +1512,21 @@
   connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
 
   TestSocketRequest req1(&request_order_, &completion_count_);
-  int rv = InitHandle(
-      req1.handle(), "a", kDefaultPriority, &req1, pool_.get(), NULL);
+  int rv = req1.handle()->Init("a", params_, kDefaultPriority, &req1, pool_,
+                               BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   TestSocketRequest req2(&request_order_, &completion_count_);
-  rv = InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
-                  pool_.get(), NULL);
+  rv = req2.handle()->Init("a", params_, kDefaultPriority, &req2, pool_,
+                           BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // The pending job is sync.
   connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
 
   TestSocketRequest req3(&request_order_, &completion_count_);
-  rv = InitHandle(
-      req3.handle(), "a", kDefaultPriority, &req3, pool_.get(), NULL);
+  rv = req3.handle()->Init("a", params_, kDefaultPriority, &req3, pool_,
+                           BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_EQ(ERR_CONNECTION_FAILED, req1.WaitForResult());
@@ -1321,25 +1539,87 @@
   EXPECT_EQ(&req3, request_order_[2]);
 }
 
-TEST_F(ClientSocketPoolBaseTest, DISABLED_LoadState) {
+TEST_F(ClientSocketPoolBaseTest, LoadState) {
   CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
   connect_job_factory_->set_job_type(
       TestConnectJob::kMockAdvancingLoadStateJob);
 
   TestSocketRequest req1(&request_order_, &completion_count_);
-  int rv = InitHandle(
-      req1.handle(), "a", kDefaultPriority, &req1, pool_.get(), NULL);
+  int rv = req1.handle()->Init("a", params_, kDefaultPriority, &req1, pool_,
+                               BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_EQ(LOAD_STATE_IDLE, req1.handle()->GetLoadState());
 
   MessageLoop::current()->RunAllPending();
 
   TestSocketRequest req2(&request_order_, &completion_count_);
-  rv = InitHandle(req2.handle(), "a", kDefaultPriority, &req2,
-                  pool_.get(), NULL);
+  rv = req2.handle()->Init("a", params_, kDefaultPriority, &req2, pool_,
+                           BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
-  EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, req1.handle()->GetLoadState());
-  EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, req2.handle()->GetLoadState());
+  EXPECT_NE(LOAD_STATE_IDLE, req1.handle()->GetLoadState());
+  EXPECT_NE(LOAD_STATE_IDLE, req2.handle()->GetLoadState());
+}
+
+TEST_F(ClientSocketPoolBaseTest, Recoverable) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockRecoverableJob);
+
+  TestSocketRequest req(&request_order_, &completion_count_);
+  EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, req.handle()->Init("a", params_,
+                                                         kDefaultPriority,
+                                                         &req, pool_,
+                                                         BoundNetLog()));
+  EXPECT_TRUE(req.handle()->is_initialized());
+  EXPECT_TRUE(req.handle()->socket());
+  req.handle()->Reset();
+}
+
+TEST_F(ClientSocketPoolBaseTest, AsyncRecoverable) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+  connect_job_factory_->set_job_type(
+      TestConnectJob::kMockPendingRecoverableJob);
+  TestSocketRequest req(&request_order_, &completion_count_);
+  EXPECT_EQ(ERR_IO_PENDING, req.handle()->Init("a", params_, kDefaultPriority,
+                                               &req, pool_, BoundNetLog()));
+  EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req.handle()));
+  EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, req.WaitForResult());
+  EXPECT_TRUE(req.handle()->is_initialized());
+  EXPECT_TRUE(req.handle()->socket());
+  req.handle()->Reset();
+}
+
+TEST_F(ClientSocketPoolBaseTest, AdditionalErrorStateSynchronous) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  connect_job_factory_->set_job_type(
+      TestConnectJob::kMockAdditionalErrorStateJob);
+
+  TestSocketRequest req(&request_order_, &completion_count_);
+  EXPECT_EQ(ERR_CONNECTION_FAILED, req.handle()->Init("a", params_,
+                                                      kDefaultPriority, &req,
+                                                      pool_, BoundNetLog()));
+  EXPECT_FALSE(req.handle()->is_initialized());
+  EXPECT_FALSE(req.handle()->socket());
+  EXPECT_TRUE(req.handle()->is_ssl_error());
+  EXPECT_FALSE(req.handle()->ssl_error_response_info().headers.get() == NULL);
+  req.handle()->Reset();
+}
+
+TEST_F(ClientSocketPoolBaseTest, AdditionalErrorStateAsynchronous) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+  connect_job_factory_->set_job_type(
+      TestConnectJob::kMockPendingAdditionalErrorStateJob);
+  TestSocketRequest req(&request_order_, &completion_count_);
+  EXPECT_EQ(ERR_IO_PENDING, req.handle()->Init("a", params_, kDefaultPriority,
+                                               &req, pool_, BoundNetLog()));
+  EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req.handle()));
+  EXPECT_EQ(ERR_CONNECTION_FAILED, req.WaitForResult());
+  EXPECT_FALSE(req.handle()->is_initialized());
+  EXPECT_FALSE(req.handle()->socket());
+  EXPECT_TRUE(req.handle()->is_ssl_error());
+  EXPECT_FALSE(req.handle()->ssl_error_response_info().headers.get() == NULL);
+  req.handle()->Reset();
 }
 
 TEST_F(ClientSocketPoolBaseTest, CleanupTimedOutIdleSockets) {
@@ -1353,12 +1633,12 @@
   // Startup two mock pending connect jobs, which will sit in the MessageLoop.
 
   TestSocketRequest req(&request_order_, &completion_count_);
-  int rv = InitHandle(req.handle(), "a", LOWEST, &req, pool_.get(), NULL);
+  int rv = req.handle()->Init("a", params_, LOWEST, &req, pool_, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req.handle()));
 
   TestSocketRequest req2(&request_order_, &completion_count_);
-  rv = InitHandle(req2.handle(), "a", LOWEST, &req2, pool_.get(), NULL);
+  rv = req2.handle()->Init("a", params_, LOWEST, &req2, pool_, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_EQ(LOAD_STATE_CONNECTING, pool_->GetLoadState("a", req2.handle()));
 
@@ -1384,12 +1664,15 @@
   // used socket.  Request it to make sure that it's used.
 
   pool_->CleanupTimedOutIdleSockets();
-  rv = InitHandle(req.handle(), "a", LOWEST, &req, pool_.get(), NULL);
+  CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+  rv = req.handle()->Init("a", params_, LOWEST, &req, pool_, log.bound());
   EXPECT_EQ(OK, rv);
   EXPECT_TRUE(req.handle()->is_reused());
+  EXPECT_TRUE(LogContainsEntryWithType(
+      log.entries(), 1, NetLog::TYPE_SOCKET_POOL_REUSED_AN_EXISTING_SOCKET));
 }
 
-// Make sure that we process all pending requests even when we're stalling 
+// Make sure that we process all pending requests even when we're stalling
 // because of multiple releasing disconnected sockets.
 TEST_F(ClientSocketPoolBaseTest, MultipleReleasingDisconnectedSockets) {
   CreatePoolWithIdleTimeouts(
@@ -1402,19 +1685,19 @@
   // Startup 4 connect jobs.  Two of them will be pending.
 
   TestSocketRequest req(&request_order_, &completion_count_);
-  int rv = InitHandle(req.handle(), "a", LOWEST, &req, pool_.get(), NULL);
+  int rv = req.handle()->Init("a", params_, LOWEST, &req, pool_, BoundNetLog());
   EXPECT_EQ(OK, rv);
 
   TestSocketRequest req2(&request_order_, &completion_count_);
-  rv = InitHandle(req2.handle(), "a", LOWEST, &req2, pool_.get(), NULL);
+  rv = req2.handle()->Init("a", params_, LOWEST, &req2, pool_, BoundNetLog());
   EXPECT_EQ(OK, rv);
 
   TestSocketRequest req3(&request_order_, &completion_count_);
-  rv = InitHandle(req3.handle(), "a", LOWEST, &req3, pool_.get(), NULL);
+  rv = req3.handle()->Init("a", params_, LOWEST, &req3, pool_, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   TestSocketRequest req4(&request_order_, &completion_count_);
-  rv = InitHandle(req4.handle(), "a", LOWEST, &req4, pool_.get(), NULL);
+  rv = req4.handle()->Init("a", params_, LOWEST, &req4, pool_, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   // Release two disconnected sockets.
@@ -1430,6 +1713,390 @@
   EXPECT_FALSE(req4.handle()->is_reused());
 }
 
+// Regression test for http://crbug.com/42267.
+// When DoReleaseSocket() is processed for one socket, it is blocked because the
+// other stalled groups all have releasing sockets, so no progress can be made.
+TEST_F(ClientSocketPoolBaseTest, SocketLimitReleasingSockets) {
+  CreatePoolWithIdleTimeouts(
+      4 /* socket limit */, 4 /* socket limit per group */,
+      base::TimeDelta(),  // Time out unused sockets immediately.
+      base::TimeDelta::FromDays(1));  // Don't time out used sockets.
+
+  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+  // Max out the socket limit with 2 per group.
+
+  scoped_ptr<TestSocketRequest> req_a[4];
+  scoped_ptr<TestSocketRequest> req_b[4];
+
+  for (int i = 0; i < 2; ++i) {
+    req_a[i].reset(new TestSocketRequest(&request_order_, &completion_count_));
+    req_b[i].reset(new TestSocketRequest(&request_order_, &completion_count_));
+    EXPECT_EQ(OK, req_a[i]->handle()->Init("a", params_, LOWEST, req_a[i].get(),
+                                           pool_, BoundNetLog()));
+    EXPECT_EQ(OK, req_b[i]->handle()->Init("b", params_, LOWEST, req_b[i].get(),
+                                           pool_, BoundNetLog()));
+  }
+
+  // Make 4 pending requests, 2 per group.
+
+  for (int i = 2; i < 4; ++i) {
+    req_a[i].reset(new TestSocketRequest(&request_order_, &completion_count_));
+    req_b[i].reset(new TestSocketRequest(&request_order_, &completion_count_));
+    EXPECT_EQ(ERR_IO_PENDING, req_a[i]->handle()->Init("a", params_, LOWEST,
+                                                       req_a[i].get(), pool_,
+                                                       BoundNetLog()));
+    EXPECT_EQ(ERR_IO_PENDING, req_b[i]->handle()->Init("b", params_, LOWEST,
+                                                       req_b[i].get(), pool_,
+                                                       BoundNetLog()));
+  }
+
+  // Release b's socket first.  The order is important, because in
+  // DoReleaseSocket(), we'll process b's released socket, and since both b and
+  // a are stalled, but 'a' is lower lexicographically, we'll process group 'a'
+  // first, which has a releasing socket, so it refuses to start up another
+  // ConnectJob.  So, we used to infinite loop on this.
+  req_b[0]->handle()->socket()->Disconnect();
+  req_b[0]->handle()->Reset();
+  req_a[0]->handle()->socket()->Disconnect();
+  req_a[0]->handle()->Reset();
+
+  // Used to get stuck here.
+  MessageLoop::current()->RunAllPending();
+
+  req_b[1]->handle()->socket()->Disconnect();
+  req_b[1]->handle()->Reset();
+  req_a[1]->handle()->socket()->Disconnect();
+  req_a[1]->handle()->Reset();
+
+  for (int i = 2; i < 4; ++i) {
+    EXPECT_EQ(OK, req_b[i]->WaitForResult());
+    EXPECT_EQ(OK, req_a[i]->WaitForResult());
+  }
+}
+
+TEST_F(ClientSocketPoolBaseTest,
+       ReleasingDisconnectedSocketsMaintainsPriorityOrder) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+  EXPECT_EQ(ERR_IO_PENDING, StartRequest("a", kDefaultPriority));
+
+  EXPECT_EQ(OK, requests_[0]->WaitForResult());
+  EXPECT_EQ(OK, requests_[1]->WaitForResult());
+  EXPECT_EQ(2u, completion_count_);
+
+  // Releases one connection.
+  EXPECT_TRUE(ReleaseOneConnection(NO_KEEP_ALIVE));
+  EXPECT_EQ(OK, requests_[2]->WaitForResult());
+
+  EXPECT_TRUE(ReleaseOneConnection(NO_KEEP_ALIVE));
+  EXPECT_EQ(OK, requests_[3]->WaitForResult());
+  EXPECT_EQ(4u, completion_count_);
+
+  EXPECT_EQ(1, GetOrderOfRequest(1));
+  EXPECT_EQ(2, GetOrderOfRequest(2));
+  EXPECT_EQ(3, GetOrderOfRequest(3));
+  EXPECT_EQ(4, GetOrderOfRequest(4));
+
+  // Make sure we test order of all requests made.
+  EXPECT_EQ(kIndexOutOfBounds, GetOrderOfRequest(5));
+}
+
+class TestReleasingSocketRequest : public CallbackRunner< Tuple1<int> > {
+ public:
+  TestReleasingSocketRequest(TestClientSocketPool* pool, int expected_result,
+                             bool reset_releasing_handle)
+      : pool_(pool),
+        expected_result_(expected_result),
+        reset_releasing_handle_(reset_releasing_handle) {}
+
+  ClientSocketHandle* handle() { return &handle_; }
+
+  int WaitForResult() {
+    return callback_.WaitForResult();
+  }
+
+  virtual void RunWithParams(const Tuple1<int>& params) {
+    callback_.RunWithParams(params);
+    if (reset_releasing_handle_)
+                      handle_.Reset();
+    scoped_refptr<TestSocketParams> con_params = new TestSocketParams();
+    EXPECT_EQ(expected_result_, handle2_.Init("a", con_params, kDefaultPriority,
+                                              &callback2_, pool_,
+                                              BoundNetLog()));
+  }
+
+ private:
+  scoped_refptr<TestClientSocketPool> pool_;
+  int expected_result_;
+  bool reset_releasing_handle_;
+  ClientSocketHandle handle_;
+  ClientSocketHandle handle2_;
+  TestCompletionCallback callback_;
+  TestCompletionCallback callback2_;
+};
+
+
+TEST_F(ClientSocketPoolBaseTest, AdditionalErrorSocketsDontUseSlot) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+
+  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
+  EXPECT_EQ(OK, StartRequest("a", kDefaultPriority));
+  EXPECT_EQ(OK, StartRequest("b", kDefaultPriority));
+
+  EXPECT_EQ(static_cast<int>(requests_.size()),
+            client_socket_factory_.allocation_count());
+
+  connect_job_factory_->set_job_type(
+      TestConnectJob::kMockPendingAdditionalErrorStateJob);
+  TestReleasingSocketRequest req(pool_.get(), OK, false);
+  EXPECT_EQ(ERR_IO_PENDING, req.handle()->Init("a", params_, kDefaultPriority,
+                                               &req, pool_, BoundNetLog()));
+  // The next job should complete synchronously
+  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+
+  EXPECT_EQ(ERR_CONNECTION_FAILED, req.WaitForResult());
+  EXPECT_FALSE(req.handle()->is_initialized());
+  EXPECT_FALSE(req.handle()->socket());
+  EXPECT_TRUE(req.handle()->is_ssl_error());
+  EXPECT_FALSE(req.handle()->ssl_error_response_info().headers.get() == NULL);
+}
+
+// http://crbug.com/44724 regression test.
+// We start releasing the pool when we flush on network change.  When that
+// happens, the only active references are in the ClientSocketHandles.  When a
+// ConnectJob completes and calls back into the last ClientSocketHandle, that
+// callback can release the last reference and delete the pool.  After the
+// callback finishes, we go back to the stack frame within the now-deleted pool.
+// Executing any code that refers to members of the now-deleted pool can cause
+// crashes.
+TEST_F(ClientSocketPoolBaseTest, CallbackThatReleasesPool) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingFailingJob);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  EXPECT_EQ(ERR_IO_PENDING, handle.Init("a", params_, kDefaultPriority,
+                                        &callback, pool_, BoundNetLog()));
+
+  // Simulate flushing the pool.
+  pool_ = NULL;
+
+  // We'll call back into this now.
+  callback.WaitForResult();
+}
+
+TEST_F(ClientSocketPoolBaseTest, DoNotReuseSocketAfterFlush) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  EXPECT_EQ(ERR_IO_PENDING, handle.Init("a", params_, kDefaultPriority,
+                                        &callback, pool_, BoundNetLog()));
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_EQ(ClientSocketHandle::UNUSED, handle.reuse_type());
+
+  pool_->Flush();
+
+  handle.Reset();
+  MessageLoop::current()->RunAllPending();
+
+  EXPECT_EQ(ERR_IO_PENDING, handle.Init("a", params_, kDefaultPriority,
+                                        &callback, pool_, BoundNetLog()));
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_EQ(ClientSocketHandle::UNUSED, handle.reuse_type());
+}
+
+// Cancel a pending socket request while we're at max sockets,
+// and verify that the backup socket firing doesn't cause a crash.
+TEST_F(ClientSocketPoolBaseTest, BackupSocketCancelAtMaxSockets) {
+  // Max 4 sockets globally, max 4 sockets per group.
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSockets);
+  pool_->EnableBackupJobs();
+
+  // Create the first socket and set to ERR_IO_PENDING.  This creates a
+  // backup job.
+  connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  EXPECT_EQ(ERR_IO_PENDING, handle.Init("bar", params_, kDefaultPriority,
+                                        &callback, pool_, BoundNetLog()));
+
+  // Start (MaxSockets - 1) connected sockets to reach max sockets.
+  connect_job_factory_->set_job_type(TestConnectJob::kMockJob);
+  ClientSocketHandle handles[kDefaultMaxSockets];
+  for (int i = 1; i < kDefaultMaxSockets; ++i) {
+    TestCompletionCallback callback;
+    EXPECT_EQ(OK, handles[i].Init("bar", params_, kDefaultPriority, &callback,
+                                  pool_, BoundNetLog()));
+  }
+
+  MessageLoop::current()->RunAllPending();
+
+  // Cancel the pending request.
+  handle.Reset();
+
+  // Wait for the backup timer to fire (add some slop to ensure it fires)
+  PlatformThread::Sleep(ClientSocketPool::kMaxConnectRetryIntervalMs / 2 * 3);
+
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(kDefaultMaxSockets, client_socket_factory_.allocation_count());
+}
+
+// Test delayed socket binding for the case where we have two connects,
+// and while one is waiting on a connect, the other frees up.
+// The socket waiting on a connect should switch immediately to the freed
+// up socket.
+TEST_F(ClientSocketPoolBaseTest, DelayedSocketBindingWaitingForConnect) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+  ClientSocketHandle handle1;
+  TestCompletionCallback callback;
+  EXPECT_EQ(ERR_IO_PENDING, handle1.Init("a", params_, kDefaultPriority,
+                                         &callback, pool_, BoundNetLog()));
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  // No idle sockets, no pending jobs.
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+  EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+
+  // Create a second socket to the same host, but this one will wait.
+  connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
+  ClientSocketHandle handle2;
+  EXPECT_EQ(ERR_IO_PENDING, handle2.Init("a", params_, kDefaultPriority,
+                                         &callback, pool_, BoundNetLog()));
+  // No idle sockets, and one connecting job.
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+  EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+
+  // Return the first handle to the pool.  This will initiate the delayed
+  // binding.
+  handle1.Reset();
+
+  MessageLoop::current()->RunAllPending();
+
+  // Still no idle sockets, still one pending connect job.
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+  EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+
+  // The second socket connected, even though it was a Waiting Job.
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  // And we can see there is still one job waiting.
+  EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+
+  // Finally, signal the waiting Connect.
+  client_socket_factory_.SignalJobs();
+  EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+
+  MessageLoop::current()->RunAllPending();
+}
+
+// Test delayed socket binding when a group is at capacity and one
+// of the group's sockets frees up.
+TEST_F(ClientSocketPoolBaseTest, DelayedSocketBindingAtGroupCapacity) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+  ClientSocketHandle handle1;
+  TestCompletionCallback callback;
+  EXPECT_EQ(ERR_IO_PENDING, handle1.Init("a", params_, kDefaultPriority,
+                                         &callback, pool_, BoundNetLog()));
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  // No idle sockets, no pending jobs.
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+  EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+
+  // Create a second socket to the same host, but this one will wait.
+  connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
+  ClientSocketHandle handle2;
+  EXPECT_EQ(ERR_IO_PENDING, handle2.Init("a", params_, kDefaultPriority,
+                                         &callback, pool_, BoundNetLog()));
+  // No idle sockets, and one connecting job.
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+  EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+
+  // Return the first handle to the pool.  This will initiate the delayed
+  // binding.
+  handle1.Reset();
+
+  MessageLoop::current()->RunAllPending();
+
+  // Still no idle sockets, still one pending connect job.
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+  EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+
+  // The second socket connected, even though it was a Waiting Job.
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  // And we can see there is still one job waiting.
+  EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+
+  // Finally, signal the waiting Connect.
+  client_socket_factory_.SignalJobs();
+  EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+
+  MessageLoop::current()->RunAllPending();
+}
+
+// Test out the case where we have one socket connected, one
+// connecting, when the first socket finishes and goes idle.
+// Although the second connection is pending, th second request
+// should complete, by taking the first socket's idle socket.
+TEST_F(ClientSocketPoolBaseTest, DelayedSocketBindingAtStall) {
+  CreatePool(kDefaultMaxSockets, kDefaultMaxSocketsPerGroup);
+  connect_job_factory_->set_job_type(TestConnectJob::kMockPendingJob);
+
+  ClientSocketHandle handle1;
+  TestCompletionCallback callback;
+  EXPECT_EQ(ERR_IO_PENDING, handle1.Init("a", params_, kDefaultPriority,
+                                         &callback, pool_, BoundNetLog()));
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  // No idle sockets, no pending jobs.
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+  EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+
+  // Create a second socket to the same host, but this one will wait.
+  connect_job_factory_->set_job_type(TestConnectJob::kMockWaitingJob);
+  ClientSocketHandle handle2;
+  EXPECT_EQ(ERR_IO_PENDING, handle2.Init("a", params_, kDefaultPriority,
+                                         &callback, pool_, BoundNetLog()));
+  // No idle sockets, and one connecting job.
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+  EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+
+  // Return the first handle to the pool.  This will initiate the delayed
+  // binding.
+  handle1.Reset();
+
+  MessageLoop::current()->RunAllPending();
+
+  // Still no idle sockets, still one pending connect job.
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+  EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+
+  // The second socket connected, even though it was a Waiting Job.
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  // And we can see there is still one job waiting.
+  EXPECT_EQ(1, pool_->NumConnectJobsInGroup("a"));
+
+  // Finally, signal the waiting Connect.
+  client_socket_factory_.SignalJobs();
+  EXPECT_EQ(0, pool_->NumConnectJobsInGroup("a"));
+
+  MessageLoop::current()->RunAllPending();
+}
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/socket/client_socket_pool_histograms.cc b/net/socket/client_socket_pool_histograms.cc
new file mode 100644
index 0000000..e86543d
--- /dev/null
+++ b/net/socket/client_socket_pool_histograms.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 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/socket/client_socket_pool_histograms.h"
+
+#include <string>
+
+#include "base/histogram.h"
+#include "net/socket/client_socket_handle.h"
+
+namespace net {
+
+ClientSocketPoolHistograms::ClientSocketPoolHistograms(
+    const std::string& pool_name) {
+  // UMA_HISTOGRAM_ENUMERATION
+  socket_type_ = LinearHistogram::FactoryGet("Net.SocketType_" + pool_name, 1,
+      ClientSocketHandle::NUM_TYPES, ClientSocketHandle::NUM_TYPES + 1,
+      Histogram::kUmaTargetedHistogramFlag);
+  // UMA_HISTOGRAM_CUSTOM_TIMES
+  request_time_ = Histogram::FactoryTimeGet(
+      "Net.SocketRequestTime_" + pool_name,
+      base::TimeDelta::FromMilliseconds(1),
+      base::TimeDelta::FromMinutes(10),
+      100, Histogram::kUmaTargetedHistogramFlag);
+  // UMA_HISTOGRAM_CUSTOM_TIMES
+  unused_idle_time_ = Histogram::FactoryTimeGet(
+      "Net.SocketIdleTimeBeforeNextUse_UnusedSocket_" + pool_name,
+      base::TimeDelta::FromMilliseconds(1),
+      base::TimeDelta::FromMinutes(6),
+      100, Histogram::kUmaTargetedHistogramFlag);
+  // UMA_HISTOGRAM_CUSTOM_TIMES
+  reused_idle_time_ = Histogram::FactoryTimeGet(
+      "Net.SocketIdleTimeBeforeNextUse_ReusedSocket_" + pool_name,
+      base::TimeDelta::FromMilliseconds(1),
+      base::TimeDelta::FromMinutes(6),
+      100, Histogram::kUmaTargetedHistogramFlag);
+}
+
+void ClientSocketPoolHistograms::AddSocketType(int type) const {
+  socket_type_->Add(type);
+}
+
+void ClientSocketPoolHistograms::AddRequestTime(base::TimeDelta time) const {
+  request_time_->AddTime(time);
+}
+
+void ClientSocketPoolHistograms::AddUnusedIdleTime(base::TimeDelta time) const {
+  unused_idle_time_->AddTime(time);
+}
+
+void ClientSocketPoolHistograms::AddReusedIdleTime(base::TimeDelta time) const {
+  reused_idle_time_->AddTime(time);
+}
+
+}  // namespace net
diff --git a/net/socket/client_socket_pool_histograms.h b/net/socket/client_socket_pool_histograms.h
new file mode 100644
index 0000000..1aea112
--- /dev/null
+++ b/net/socket/client_socket_pool_histograms.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 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_SOCKET_CLIENT_SOCKET_POOL_HISTOGRAMS_H_
+#define NET_SOCKET_CLIENT_SOCKET_POOL_HISTOGRAMS_H_
+
+#include <string>
+
+#include "base/histogram.h"
+#include "base/ref_counted.h"
+
+namespace net {
+
+class ClientSocketPoolHistograms
+    : public base::RefCounted<ClientSocketPoolHistograms> {
+ public:
+  ClientSocketPoolHistograms(const std::string& pool_name);
+
+  void AddSocketType(int socket_reuse_type) const;
+  void AddRequestTime(base::TimeDelta time) const;
+  void AddUnusedIdleTime(base::TimeDelta time) const;
+  void AddReusedIdleTime(base::TimeDelta time) const;
+
+ private:
+  friend class base::RefCounted<ClientSocketPoolHistograms>;
+  ~ClientSocketPoolHistograms() {}
+
+  scoped_refptr<Histogram> socket_type_;
+  scoped_refptr<Histogram> request_time_;
+  scoped_refptr<Histogram> unused_idle_time_;
+  scoped_refptr<Histogram> reused_idle_time_;
+};
+
+}  // namespace net
+
+#endif  // NET_SOCKET_CLIENT_SOCKET_POOL_HISTOGRAMS_H_
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 283ae35..0d75516 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -1,23 +1,124 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/socket/socket_test_util.h"
 
 #include <algorithm>
+#include <vector>
+
 
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/message_loop.h"
+#include "base/time.h"
+#include "net/base/address_family.h"
+#include "net/base/auth.h"
+#include "net/base/host_resolver_proc.h"
 #include "net/base/ssl_info.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/socket.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#define NET_TRACE(level, s)   DLOG(level) << s << __FUNCTION__ << "() "
+
 namespace net {
 
-MockClientSocket::MockClientSocket()
+namespace {
+
+inline char AsciifyHigh(char x) {
+  char nybble = static_cast<char>((x >> 4) & 0x0F);
+  return nybble + ((nybble < 0x0A) ? '0' : 'A' - 10);
+}
+
+inline char AsciifyLow(char x) {
+  char nybble = static_cast<char>((x >> 0) & 0x0F);
+  return nybble + ((nybble < 0x0A) ? '0' : 'A' - 10);
+}
+
+inline char Asciify(char x) {
+  if ((x < 0) || !isprint(x))
+    return '.';
+  return x;
+}
+
+void DumpData(const char* data, int data_len) {
+  if (logging::LOG_INFO < logging::GetMinLogLevel())
+    return;
+  DLOG(INFO) << "Length:  " << data_len;
+  const char* pfx = "Data:    ";
+  if (!data || (data_len <= 0)) {
+    DLOG(INFO) << pfx << "<None>";
+  } else {
+    int i;
+    for (i = 0; i <= (data_len - 4); i += 4) {
+      DLOG(INFO) << pfx
+                 << AsciifyHigh(data[i + 0]) << AsciifyLow(data[i + 0])
+                 << AsciifyHigh(data[i + 1]) << AsciifyLow(data[i + 1])
+                 << AsciifyHigh(data[i + 2]) << AsciifyLow(data[i + 2])
+                 << AsciifyHigh(data[i + 3]) << AsciifyLow(data[i + 3])
+                 << "  '"
+                 << Asciify(data[i + 0])
+                 << Asciify(data[i + 1])
+                 << Asciify(data[i + 2])
+                 << Asciify(data[i + 3])
+                 << "'";
+      pfx = "         ";
+    }
+    // Take care of any 'trailing' bytes, if data_len was not a multiple of 4.
+    switch (data_len - i) {
+      case 3:
+        DLOG(INFO) << pfx
+                   << AsciifyHigh(data[i + 0]) << AsciifyLow(data[i + 0])
+                   << AsciifyHigh(data[i + 1]) << AsciifyLow(data[i + 1])
+                   << AsciifyHigh(data[i + 2]) << AsciifyLow(data[i + 2])
+                   << "    '"
+                   << Asciify(data[i + 0])
+                   << Asciify(data[i + 1])
+                   << Asciify(data[i + 2])
+                   << " '";
+        break;
+      case 2:
+        DLOG(INFO) << pfx
+                   << AsciifyHigh(data[i + 0]) << AsciifyLow(data[i + 0])
+                   << AsciifyHigh(data[i + 1]) << AsciifyLow(data[i + 1])
+                   << "      '"
+                   << Asciify(data[i + 0])
+                   << Asciify(data[i + 1])
+                   << "  '";
+        break;
+      case 1:
+        DLOG(INFO) << pfx
+                   << AsciifyHigh(data[i + 0]) << AsciifyLow(data[i + 0])
+                   << "        '"
+                   << Asciify(data[i + 0])
+                   << "   '";
+        break;
+    }
+  }
+}
+
+void DumpMockRead(const MockRead& r) {
+  if (logging::LOG_INFO < logging::GetMinLogLevel())
+    return;
+  DLOG(INFO) << "Async:   " << r.async;
+  DLOG(INFO) << "Result:  " << r.result;
+  DumpData(r.data, r.data_len);
+  const char* stop = (r.sequence_number & MockRead::STOPLOOP) ? " (STOP)" : "";
+  DLOG(INFO) << "Stage:   " << (r.sequence_number & ~MockRead::STOPLOOP)
+             << stop;
+  DLOG(INFO) << "Time:    " << r.time_stamp.ToInternalValue();
+}
+
+}  // namespace
+
+MockClientSocket::MockClientSocket(net::NetLog* net_log)
     : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
-      connected_(false) {
+      connected_(false),
+      net_log_(NetLog::Source(), net_log) {
 }
 
 void MockClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) {
@@ -47,9 +148,9 @@
   return connected_;
 }
 
-int MockClientSocket::GetPeerName(struct sockaddr* name, socklen_t* namelen) {
-  memset(reinterpret_cast<char *>(name), 0, *namelen);
-  return net::OK;
+int MockClientSocket::GetPeerAddress(AddressList* address) const {
+  return net::SystemHostResolverProc("localhost", ADDRESS_FAMILY_UNSPECIFIED,
+                                     0, address, NULL);
 }
 
 void MockClientSocket::RunCallbackAsync(net::CompletionCallback* callback,
@@ -66,8 +167,10 @@
 }
 
 MockTCPClientSocket::MockTCPClientSocket(const net::AddressList& addresses,
+                                         net::NetLog* net_log,
                                          net::SocketDataProvider* data)
-    : addresses_(addresses),
+    : MockClientSocket(net_log),
+      addresses_(addresses),
       data_(data),
       read_offset_(0),
       read_data_(false, net::ERR_UNEXPECTED),
@@ -80,8 +183,7 @@
   data_->Reset();
 }
 
-int MockTCPClientSocket::Connect(net::CompletionCallback* callback,
-                                 LoadLog* load_log) {
+int MockTCPClientSocket::Connect(net::CompletionCallback* callback) {
   if (connected_)
     return net::OK;
   connected_ = true;
@@ -92,6 +194,11 @@
   return data_->connect_data().result;
 }
 
+void MockTCPClientSocket::Disconnect() {
+  MockClientSocket::Disconnect();
+  pending_callback_ = NULL;
+}
+
 bool MockTCPClientSocket::IsConnected() const {
   return connected_ && !peer_closed_connection_;
 }
@@ -232,12 +339,14 @@
 };
 
 MockSSLClientSocket::MockSSLClientSocket(
-    net::ClientSocket* transport_socket,
+    net::ClientSocketHandle* transport_socket,
     const std::string& hostname,
     const net::SSLConfig& ssl_config,
     net::SSLSocketDataProvider* data)
-    : transport_(transport_socket),
-      data_(data) {
+    : MockClientSocket(transport_socket->socket()->NetLog().net_log()),
+      transport_(transport_socket),
+      data_(data),
+      is_npn_state_set_(false) {
   DCHECK(data_);
 }
 
@@ -245,15 +354,10 @@
   Disconnect();
 }
 
-void MockSSLClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) {
-  ssl_info->Reset();
-}
-
-int MockSSLClientSocket::Connect(net::CompletionCallback* callback,
-                                 LoadLog* load_log) {
+int MockSSLClientSocket::Connect(net::CompletionCallback* callback) {
   ConnectCallback* connect_callback = new ConnectCallback(
       this, callback, data_->connect.result);
-  int rv = transport_->Connect(connect_callback, load_log);
+  int rv = transport_->socket()->Connect(connect_callback);
   if (rv == net::OK) {
     delete connect_callback;
     if (data_->connect.async) {
@@ -269,26 +373,45 @@
 
 void MockSSLClientSocket::Disconnect() {
   MockClientSocket::Disconnect();
-  if (transport_ != NULL)
-    transport_->Disconnect();
+  if (transport_->socket() != NULL)
+    transport_->socket()->Disconnect();
 }
 
 int MockSSLClientSocket::Read(net::IOBuffer* buf, int buf_len,
                               net::CompletionCallback* callback) {
-  return transport_->Read(buf, buf_len, callback);
+  return transport_->socket()->Read(buf, buf_len, callback);
 }
 
 int MockSSLClientSocket::Write(net::IOBuffer* buf, int buf_len,
                                net::CompletionCallback* callback) {
-  return transport_->Write(buf, buf_len, callback);
+  return transport_->socket()->Write(buf, buf_len, callback);
+}
+
+void MockSSLClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) {
+  ssl_info->Reset();
+}
+
+SSLClientSocket::NextProtoStatus MockSSLClientSocket::GetNextProto(
+    std::string* proto) {
+  *proto = data_->next_proto;
+  return data_->next_proto_status;
+}
+
+bool MockSSLClientSocket::wasNpnNegotiated() const {
+  if (is_npn_state_set_)
+    return new_npn_value_;
+  return data_->was_npn_negotiated;
+}
+
+bool MockSSLClientSocket::setWasNpnNegotiated(bool negotiated) {
+  is_npn_state_set_ = true;
+  return new_npn_value_ = negotiated;
 }
 
 MockRead StaticSocketDataProvider::GetNextRead() {
-  MockRead rv = reads_[read_index_];
-  if (reads_[read_index_].result != OK ||
-      reads_[read_index_].data_len != 0)
-    read_index_++;  // Don't advance past an EOF.
-  return rv;
+  DCHECK(!at_read_eof());
+  reads_[read_index_].time_stamp = base::Time::Now();
+  return reads_[read_index_++];
 }
 
 MockWriteResult StaticSocketDataProvider::OnWrite(const std::string& data) {
@@ -297,9 +420,12 @@
     return MockWriteResult(false, data.length());
   }
 
+  DCHECK(!at_write_eof());
+
   // Check that what we are writing matches the expectation.
   // Then give the mocked return value.
   net::MockWrite* w = &writes_[write_index_++];
+  w->time_stamp = base::Time::Now();
   int result = w->result;
   if (w->data) {
     // Note - we can simulate a partial write here.  If the expected data
@@ -320,6 +446,26 @@
   return MockWriteResult(w->async, result);
 }
 
+const MockRead& StaticSocketDataProvider::PeekRead() const {
+  DCHECK(!at_read_eof());
+  return reads_[read_index_];
+}
+
+const MockWrite& StaticSocketDataProvider::PeekWrite() const {
+  DCHECK(!at_write_eof());
+  return writes_[write_index_];
+}
+
+const MockRead& StaticSocketDataProvider::PeekRead(size_t index) const {
+  DCHECK_LT(index, read_count_);
+  return reads_[index];
+}
+
+const MockWrite& StaticSocketDataProvider::PeekWrite(size_t index) const {
+  DCHECK_LT(index, write_count_);
+  return writes_[index];
+}
+
 void StaticSocketDataProvider::Reset() {
   read_index_ = 0;
   write_index_ = 0;
@@ -348,11 +494,153 @@
   reads_.clear();
 }
 
-void DynamicSocketDataProvider::SimulateRead(const char* data) {
+void DynamicSocketDataProvider::SimulateRead(const char* data,
+                                             const size_t length) {
   if (!allow_unconsumed_reads_) {
     EXPECT_TRUE(reads_.empty()) << "Unconsumed read: " << reads_.front().data;
   }
-  reads_.push_back(MockRead(data));
+  reads_.push_back(MockRead(true, data, length));
+}
+
+DelayedSocketData::DelayedSocketData(
+    int write_delay, MockRead* reads, size_t reads_count,
+    MockWrite* writes, size_t writes_count)
+    : StaticSocketDataProvider(reads, reads_count, writes, writes_count),
+      write_delay_(write_delay),
+      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+  DCHECK_GE(write_delay_, 0);
+}
+
+DelayedSocketData::DelayedSocketData(
+    const MockConnect& connect, int write_delay, MockRead* reads,
+    size_t reads_count, MockWrite* writes, size_t writes_count)
+    : StaticSocketDataProvider(reads, reads_count, writes, writes_count),
+      write_delay_(write_delay),
+      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+  DCHECK_GE(write_delay_, 0);
+  set_connect_data(connect);
+}
+
+MockRead DelayedSocketData::GetNextRead() {
+  if (write_delay_)
+    return MockRead(true, ERR_IO_PENDING);
+  return StaticSocketDataProvider::GetNextRead();
+}
+
+MockWriteResult DelayedSocketData::OnWrite(const std::string& data) {
+  MockWriteResult rv = StaticSocketDataProvider::OnWrite(data);
+  // Now that our write has completed, we can allow reads to continue.
+  if (!--write_delay_)
+    MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      factory_.NewRunnableMethod(&DelayedSocketData::CompleteRead), 100);
+  return rv;
+}
+
+void DelayedSocketData::Reset() {
+  set_socket(NULL);
+  factory_.RevokeAll();
+  StaticSocketDataProvider::Reset();
+}
+
+void DelayedSocketData::CompleteRead() {
+  if (socket())
+    socket()->OnReadComplete(GetNextRead());
+}
+
+OrderedSocketData::OrderedSocketData(
+    MockRead* reads, size_t reads_count, MockWrite* writes, size_t writes_count)
+    : StaticSocketDataProvider(reads, reads_count, writes, writes_count),
+      sequence_number_(0), loop_stop_stage_(0), callback_(NULL),
+      blocked_(false), ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+}
+
+OrderedSocketData::OrderedSocketData(
+    const MockConnect& connect,
+    MockRead* reads, size_t reads_count,
+    MockWrite* writes, size_t writes_count)
+    : StaticSocketDataProvider(reads, reads_count, writes, writes_count),
+      sequence_number_(0), loop_stop_stage_(0), callback_(NULL),
+      blocked_(false), ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+  set_connect_data(connect);
+}
+
+MockRead OrderedSocketData::GetNextRead() {
+  factory_.RevokeAll();
+  blocked_ = false;
+  const MockRead& next_read = StaticSocketDataProvider::PeekRead();
+  if (next_read.sequence_number & MockRead::STOPLOOP)
+    EndLoop();
+  if ((next_read.sequence_number & ~MockRead::STOPLOOP) <=
+      sequence_number_++) {
+    NET_TRACE(INFO, "  *** ") << "Stage " << sequence_number_ - 1
+                              << ": Read " << read_index();
+    DumpMockRead(next_read);
+    return StaticSocketDataProvider::GetNextRead();
+  }
+  NET_TRACE(INFO, "  *** ") << "Stage " << sequence_number_ - 1
+                            << ": I/O Pending";
+  MockRead result = MockRead(true, ERR_IO_PENDING);
+  DumpMockRead(result);
+  blocked_ = true;
+  return result;
+}
+
+MockWriteResult OrderedSocketData::OnWrite(const std::string& data) {
+  NET_TRACE(INFO, "  *** ") << "Stage " << sequence_number_
+                            << ": Write " << write_index();
+  DumpMockRead(PeekWrite());
+  ++sequence_number_;
+  if (blocked_) {
+    // TODO(willchan): This 100ms delay seems to work around some weirdness.  We
+    // should probably fix the weirdness.  One example is in SpdyStream,
+    // DoSendRequest() will return ERR_IO_PENDING, and there's a race.  If the
+    // SYN_REPLY causes OnResponseReceived() to get called before
+    // SpdyStream::ReadResponseHeaders() is called, we hit a NOTREACHED().
+    MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        factory_.NewRunnableMethod(&OrderedSocketData::CompleteRead), 100);
+  }
+  return StaticSocketDataProvider::OnWrite(data);
+}
+
+void OrderedSocketData::Reset() {
+  NET_TRACE(INFO, "  *** ") << "Stage "
+                            << sequence_number_ << ": Reset()";
+  sequence_number_ = 0;
+  loop_stop_stage_ = 0;
+  set_socket(NULL);
+  factory_.RevokeAll();
+  StaticSocketDataProvider::Reset();
+}
+
+void OrderedSocketData::EndLoop() {
+  // If we've already stopped the loop, don't do it again until we've advanced
+  // to the next sequence_number.
+  NET_TRACE(INFO, "  *** ") << "Stage " << sequence_number_ << ": EndLoop()";
+  if (loop_stop_stage_ > 0) {
+    const MockRead& next_read = StaticSocketDataProvider::PeekRead();
+    if ((next_read.sequence_number & ~MockRead::STOPLOOP) >
+        loop_stop_stage_) {
+      NET_TRACE(INFO, "  *** ") << "Stage " << sequence_number_
+                                << ": Clearing stop index";
+      loop_stop_stage_ = 0;
+    } else {
+      return;
+    }
+  }
+  // Record the sequence_number at which we stopped the loop.
+  NET_TRACE(INFO, "  *** ") << "Stage " << sequence_number_
+                            << ": Posting Quit at read " << read_index();
+  loop_stop_stage_ = sequence_number_;
+  if (callback_)
+    callback_->RunWithParams(Tuple1<int>(ERR_IO_PENDING));
+}
+
+void OrderedSocketData::CompleteRead() {
+  if (socket()) {
+    NET_TRACE(INFO, "  *** ") << "Stage " << sequence_number_;
+    socket()->OnReadComplete(GetNextRead());
+  }
 }
 
 void MockClientSocketFactory::AddSocketDataProvider(
@@ -371,27 +659,29 @@
 }
 
 MockTCPClientSocket* MockClientSocketFactory::GetMockTCPClientSocket(
-    int index) const {
+    size_t index) const {
+  DCHECK_LT(index, tcp_client_sockets_.size());
   return tcp_client_sockets_[index];
 }
 
 MockSSLClientSocket* MockClientSocketFactory::GetMockSSLClientSocket(
-    int index) const {
+    size_t index) const {
+  DCHECK_LT(index, ssl_client_sockets_.size());
   return ssl_client_sockets_[index];
 }
 
 ClientSocket* MockClientSocketFactory::CreateTCPClientSocket(
-    const AddressList& addresses) {
+    const AddressList& addresses, net::NetLog* net_log) {
   SocketDataProvider* data_provider = mock_data_.GetNext();
   MockTCPClientSocket* socket =
-      new MockTCPClientSocket(addresses, data_provider);
+      new MockTCPClientSocket(addresses, net_log, data_provider);
   data_provider->set_socket(socket);
   tcp_client_sockets_.push_back(socket);
   return socket;
 }
 
 SSLClientSocket* MockClientSocketFactory::CreateSSLClientSocket(
-    ClientSocket* transport_socket,
+    ClientSocketHandle* transport_socket,
     const std::string& hostname,
     const SSLConfig& ssl_config) {
   MockSSLClientSocket* socket =
@@ -469,4 +759,192 @@
   } while (released_one);
 }
 
+MockTCPClientSocketPool::MockConnectJob::MockConnectJob(
+    ClientSocket* socket,
+    ClientSocketHandle* handle,
+    CompletionCallback* callback)
+    : socket_(socket),
+      handle_(handle),
+      user_callback_(callback),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          connect_callback_(this, &MockConnectJob::OnConnect)) {
+}
+
+int MockTCPClientSocketPool::MockConnectJob::Connect() {
+  int rv = socket_->Connect(&connect_callback_);
+  if (rv == OK) {
+    user_callback_ = NULL;
+    OnConnect(OK);
+  }
+  return rv;
+}
+
+bool MockTCPClientSocketPool::MockConnectJob::CancelHandle(
+    const ClientSocketHandle* handle) {
+  if (handle != handle_)
+    return false;
+  socket_.reset();
+  handle_ = NULL;
+  user_callback_ = NULL;
+  return true;
+}
+
+void MockTCPClientSocketPool::MockConnectJob::OnConnect(int rv) {
+  if (!socket_.get())
+    return;
+  if (rv == OK) {
+    handle_->set_socket(socket_.release());
+  } else {
+    socket_.reset();
+  }
+
+  handle_ = NULL;
+
+  if (user_callback_) {
+    CompletionCallback* callback = user_callback_;
+    user_callback_ = NULL;
+    callback->Run(rv);
+  }
+}
+
+MockTCPClientSocketPool::MockTCPClientSocketPool(
+    int max_sockets,
+    int max_sockets_per_group,
+    const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+    ClientSocketFactory* socket_factory)
+    : TCPClientSocketPool(max_sockets, max_sockets_per_group, histograms,
+                          NULL, NULL, NULL),
+      client_socket_factory_(socket_factory),
+      release_count_(0),
+      cancel_count_(0) {
+}
+
+int MockTCPClientSocketPool::RequestSocket(const std::string& group_name,
+                                           const void* socket_params,
+                                           RequestPriority priority,
+                                           ClientSocketHandle* handle,
+                                           CompletionCallback* callback,
+                                           const BoundNetLog& net_log) {
+  ClientSocket* socket = client_socket_factory_->CreateTCPClientSocket(
+      AddressList(), net_log.net_log());
+  MockConnectJob* job = new MockConnectJob(socket, handle, callback);
+  job_list_.push_back(job);
+  handle->set_pool_id(1);
+  return job->Connect();
+}
+
+void MockTCPClientSocketPool::CancelRequest(const std::string& group_name,
+                                            ClientSocketHandle* handle) {
+  std::vector<MockConnectJob*>::iterator i;
+  for (i = job_list_.begin(); i != job_list_.end(); ++i) {
+    if ((*i)->CancelHandle(handle)) {
+      cancel_count_++;
+      break;
+    }
+  }
+}
+
+void MockTCPClientSocketPool::ReleaseSocket(const std::string& group_name,
+                                            ClientSocket* socket, int id) {
+  EXPECT_EQ(1, id);
+  release_count_++;
+  delete socket;
+}
+
+MockTCPClientSocketPool::~MockTCPClientSocketPool() {}
+
+MockSOCKSClientSocketPool::MockSOCKSClientSocketPool(
+    int max_sockets,
+    int max_sockets_per_group,
+    const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+    const scoped_refptr<TCPClientSocketPool>& tcp_pool)
+    : SOCKSClientSocketPool(max_sockets, max_sockets_per_group, histograms,
+                            NULL, tcp_pool, NULL),
+      tcp_pool_(tcp_pool) {
+}
+
+int MockSOCKSClientSocketPool::RequestSocket(const std::string& group_name,
+                                             const void* socket_params,
+                                             RequestPriority priority,
+                                             ClientSocketHandle* handle,
+                                             CompletionCallback* callback,
+                                             const BoundNetLog& net_log) {
+  return tcp_pool_->RequestSocket(group_name, socket_params, priority, handle,
+                                  callback, net_log);
+}
+
+void MockSOCKSClientSocketPool::CancelRequest(
+    const std::string& group_name,
+    ClientSocketHandle* handle) {
+  return tcp_pool_->CancelRequest(group_name, handle);
+}
+
+void MockSOCKSClientSocketPool::ReleaseSocket(const std::string& group_name,
+                                              ClientSocket* socket, int id) {
+  return tcp_pool_->ReleaseSocket(group_name, socket, id);
+}
+
+MockSOCKSClientSocketPool::~MockSOCKSClientSocketPool() {}
+
+MockHttpAuthController::MockHttpAuthController()
+    : HttpAuthController(HttpAuth::AUTH_PROXY, GURL(),
+                         scoped_refptr<HttpNetworkSession>(NULL)),
+      data_(NULL),
+      data_index_(0),
+      data_count_(0) {
+}
+
+void MockHttpAuthController::SetMockAuthControllerData(
+    struct MockHttpAuthControllerData* data, size_t count) {
+  data_ = data;
+  data_count_ = count;
+}
+
+int MockHttpAuthController::MaybeGenerateAuthToken(
+    const HttpRequestInfo* request,
+    CompletionCallback* callback,
+    const BoundNetLog& net_log) {
+  return OK;
+}
+
+void MockHttpAuthController::AddAuthorizationHeader(
+    HttpRequestHeaders* authorization_headers) {
+  authorization_headers->AddHeadersFromString(CurrentData().auth_header);
+}
+
+int MockHttpAuthController::HandleAuthChallenge(
+    scoped_refptr<HttpResponseHeaders> headers,
+    bool do_not_send_server_auth,
+    bool establishing_tunnel,
+    const BoundNetLog& net_log) {
+  return OK;
+}
+
+void MockHttpAuthController::ResetAuth(const std::wstring& username,
+                                       const std::wstring& password) {
+  data_index_++;
+}
+
+bool MockHttpAuthController::HaveAuth() const {
+  return CurrentData().auth_header.size() != 0;
+}
+
+bool MockHttpAuthController::HaveAuthHandler() const {
+  return HaveAuth();
+}
+
+const char kSOCKS5GreetRequest[] = { 0x05, 0x01, 0x00 };
+const int kSOCKS5GreetRequestLength = arraysize(kSOCKS5GreetRequest);
+
+const char kSOCKS5GreetResponse[] = { 0x05, 0x00 };
+const int kSOCKS5GreetResponseLength = arraysize(kSOCKS5GreetResponse);
+
+const char kSOCKS5OkRequest[] =
+    { 0x05, 0x01, 0x00, 0x03, 0x04, 'h', 'o', 's', 't', 0x00, 0x50 };
+const int kSOCKS5OkRequestLength = arraysize(kSOCKS5OkRequest);
+
+const char kSOCKS5OkResponse[] =
+    { 0x05, 0x00, 0x00, 0x01, 127, 0, 0, 1, 0x00, 0x50 };
+const int kSOCKS5OkResponseLength = arraysize(kSOCKS5OkResponse);
+
 }  // namespace net
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 90b4019..2bd0d23 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -1,26 +1,33 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_SOCKET_SOCKET_TEST_UTIL_H_
 #define NET_SOCKET_SOCKET_TEST_UTIL_H_
 
+#include <cstring>
 #include <deque>
 #include <string>
 #include <vector>
 
 #include "base/basictypes.h"
+#include "base/callback.h"
 #include "base/logging.h"
 #include "base/scoped_ptr.h"
 #include "base/scoped_vector.h"
 #include "net/base/address_list.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
 #include "net/base/ssl_config_service.h"
 #include "net/base/test_completion_callback.h"
+#include "net/http/http_auth_controller.h"
+#include "net/http/http_proxy_client_socket_pool.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
+#include "net/socket/socks_client_socket_pool.h"
 #include "net/socket/ssl_client_socket.h"
+#include "net/socket/tcp_client_socket_pool.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
@@ -35,7 +42,8 @@
 };
 
 class ClientSocket;
-class LoadLog;
+class HttpRequestHeaders;
+class HttpResponseHeaders;
 class MockClientSocket;
 class SSLClientSocket;
 
@@ -49,29 +57,55 @@
 };
 
 struct MockRead {
+  // Flag to indicate that the message loop should be terminated.
+  enum {
+    STOPLOOP = 1 << 31
+  };
+
   // Default
-  MockRead() : async(false), result(0), data(NULL), data_len(0) {}
+  MockRead() : async(false), result(0), data(NULL), data_len(0),
+      sequence_number(0), time_stamp(base::Time::Now()) {}
 
   // Read failure (no data).
   MockRead(bool async, int result) : async(async) , result(result), data(NULL),
-      data_len(0) { }
+      data_len(0), sequence_number(0), time_stamp(base::Time::Now()) { }
+
+  // Read failure (no data), with sequence information.
+  MockRead(bool async, int result, int seq) : async(async) , result(result),
+      data(NULL), data_len(0), sequence_number(seq),
+      time_stamp(base::Time::Now()) { }
 
   // Asynchronous read success (inferred data length).
   explicit MockRead(const char* data) : async(true),  result(0), data(data),
-      data_len(strlen(data)) { }
+      data_len(strlen(data)), sequence_number(0),
+      time_stamp(base::Time::Now()) { }
 
   // Read success (inferred data length).
   MockRead(bool async, const char* data) : async(async), result(0), data(data),
-      data_len(strlen(data)) { }
+      data_len(strlen(data)), sequence_number(0),
+      time_stamp(base::Time::Now()) { }
 
   // Read success.
   MockRead(bool async, const char* data, int data_len) : async(async),
-      result(0), data(data), data_len(data_len) { }
+      result(0), data(data), data_len(data_len), sequence_number(0),
+      time_stamp(base::Time::Now()) { }
+
+  // Read success with sequence information.
+  MockRead(bool async, const char* data, int data_len, int seq) : async(async),
+      result(0), data(data), data_len(data_len), sequence_number(seq),
+      time_stamp(base::Time::Now()) { }
 
   bool async;
   int result;
   const char* data;
   int data_len;
+
+  // For OrderedSocketData, which only allows reads to occur in a particular
+  // sequence.  If a read occurs before the given |sequence_number| is reached,
+  // an ERR_IO_PENDING is returned.
+  int sequence_number;      // The sequence number at which a read is allowed
+                            // to occur.
+  base::Time time_stamp;    // The time stamp at which the operation occurred.
 };
 
 // MockWrite uses the same member fields as MockRead, but with different
@@ -121,28 +155,44 @@
 // writes.
 class StaticSocketDataProvider : public SocketDataProvider {
  public:
-  StaticSocketDataProvider() : reads_(NULL), read_index_(0),
-      writes_(NULL), write_index_(0) {}
-  StaticSocketDataProvider(MockRead* r, MockWrite* w) : reads_(r),
-      read_index_(0), writes_(w), write_index_(0) {}
+  StaticSocketDataProvider() : reads_(NULL), read_index_(0), read_count_(0),
+      writes_(NULL), write_index_(0), write_count_(0) {}
+  StaticSocketDataProvider(MockRead* reads, size_t reads_count,
+                           MockWrite* writes, size_t writes_count)
+      : reads_(reads),
+        read_index_(0),
+        read_count_(reads_count),
+        writes_(writes),
+        write_index_(0),
+        write_count_(writes_count) {
+  }
 
   // SocketDataProvider methods:
   virtual MockRead GetNextRead();
   virtual MockWriteResult OnWrite(const std::string& data);
   virtual void Reset();
 
-  // If the test wishes to verify that all data is consumed, it can include
-  // a EOF MockRead or MockWrite, which is a zero-length Read or Write.
-  // The test can then call at_read_eof() or at_write_eof() to verify that
-  // all data has been consumed.
-  bool at_read_eof() const { return reads_[read_index_].data_len == 0; }
-  bool at_write_eof() const { return writes_[write_index_].data_len == 0; }
+  // These functions get access to the next available read and write data.
+  const MockRead& PeekRead() const;
+  const MockWrite& PeekWrite() const;
+  // These functions get random access to the read and write data, for timing.
+  const MockRead& PeekRead(size_t index) const;
+  const MockWrite& PeekWrite(size_t index) const;
+  size_t read_index() const { return read_index_; }
+  size_t write_index() const { return write_index_; }
+  size_t read_count() const { return read_count_; }
+  size_t write_count() const { return write_count_; }
+
+  bool at_read_eof() const { return read_index_ >= read_count_; }
+  bool at_write_eof() const { return write_index_ >= write_count_; }
 
  private:
   MockRead* reads_;
-  int read_index_;
+  size_t read_index_;
+  size_t read_count_;
   MockWrite* writes_;
-  int write_index_;
+  size_t write_index_;
+  size_t write_count_;
 
   DISALLOW_COPY_AND_ASSIGN(StaticSocketDataProvider);
 };
@@ -168,7 +218,10 @@
  protected:
   // The next time there is a read from this socket, it will return |data|.
   // Before calling SimulateRead next time, the previous data must be consumed.
-  void SimulateRead(const char* data);
+  void SimulateRead(const char* data, size_t length);
+  void SimulateRead(const char* data) {
+    SimulateRead(data, std::strlen(data));
+  }
 
  private:
   std::deque<MockRead> reads_;
@@ -186,9 +239,105 @@
 // SSLSocketDataProviders only need to keep track of the return code from calls
 // to Connect().
 struct SSLSocketDataProvider {
-  SSLSocketDataProvider(bool async, int result) : connect(async, result) { }
+  SSLSocketDataProvider(bool async, int result)
+      : connect(async, result),
+        next_proto_status(SSLClientSocket::kNextProtoUnsupported),
+        was_npn_negotiated(false) { }
 
   MockConnect connect;
+  SSLClientSocket::NextProtoStatus next_proto_status;
+  std::string next_proto;
+  bool was_npn_negotiated;
+};
+
+// A DataProvider where the client must write a request before the reads (e.g.
+// the response) will complete.
+class DelayedSocketData : public StaticSocketDataProvider,
+                          public base::RefCounted<DelayedSocketData> {
+ public:
+  // |write_delay| the number of MockWrites to complete before allowing
+  //               a MockRead to complete.
+  // |reads| the list of MockRead completions.
+  // |writes| the list of MockWrite completions.
+  // Note: All MockReads and MockWrites must be async.
+  // Note: The MockRead and MockWrite lists musts end with a EOF
+  //       e.g. a MockRead(true, 0, 0);
+  DelayedSocketData(int write_delay,
+                    MockRead* reads, size_t reads_count,
+                    MockWrite* writes, size_t writes_count);
+
+  // |connect| the result for the connect phase.
+  // |reads| the list of MockRead completions.
+  // |write_delay| the number of MockWrites to complete before allowing
+  //               a MockRead to complete.
+  // |writes| the list of MockWrite completions.
+  // Note: All MockReads and MockWrites must be async.
+  // Note: The MockRead and MockWrite lists musts end with a EOF
+  //       e.g. a MockRead(true, 0, 0);
+  DelayedSocketData(const MockConnect& connect, int write_delay,
+                    MockRead* reads, size_t reads_count,
+                    MockWrite* writes, size_t writes_count);
+
+  virtual MockRead GetNextRead();
+  virtual MockWriteResult OnWrite(const std::string& data);
+  virtual void Reset();
+  void CompleteRead();
+
+ private:
+  int write_delay_;
+  ScopedRunnableMethodFactory<DelayedSocketData> factory_;
+};
+
+// A DataProvider where the reads are ordered.
+// If a read is requested before its sequence number is reached, we return an
+// ERR_IO_PENDING (that way we don't have to explicitly add a MockRead just to
+// wait).
+// The sequence number is incremented on every read and write operation.
+// The message loop may be interrupted by setting the high bit of the sequence
+// number in the MockRead's sequence number.  When that MockRead is reached,
+// we post a Quit message to the loop.  This allows us to interrupt the reading
+// of data before a complete message has arrived, and provides support for
+// testing server push when the request is issued while the response is in the
+// middle of being received.
+class OrderedSocketData : public StaticSocketDataProvider,
+                          public base::RefCounted<OrderedSocketData> {
+ public:
+  // |reads| the list of MockRead completions.
+  // |writes| the list of MockWrite completions.
+  // Note: All MockReads and MockWrites must be async.
+  // Note: The MockRead and MockWrite lists musts end with a EOF
+  //       e.g. a MockRead(true, 0, 0);
+  OrderedSocketData(MockRead* reads, size_t reads_count,
+                    MockWrite* writes, size_t writes_count);
+
+  // |connect| the result for the connect phase.
+  // |reads| the list of MockRead completions.
+  // |writes| the list of MockWrite completions.
+  // Note: All MockReads and MockWrites must be async.
+  // Note: The MockRead and MockWrite lists musts end with a EOF
+  //       e.g. a MockRead(true, 0, 0);
+  OrderedSocketData(const MockConnect& connect,
+                    MockRead* reads, size_t reads_count,
+                    MockWrite* writes, size_t writes_count);
+
+  virtual MockRead GetNextRead();
+  virtual MockWriteResult OnWrite(const std::string& data);
+  virtual void Reset();
+  void SetCompletionCallback(CompletionCallback* callback) {
+    callback_ = callback;
+  }
+
+  // Posts a quit message to the current message loop, if one is running.
+  void EndLoop();
+
+  void CompleteRead();
+
+ private:
+  int sequence_number_;
+  int loop_stop_stage_;
+  CompletionCallback* callback_;
+  bool blocked_;
+  ScopedRunnableMethodFactory<OrderedSocketData> factory_;
 };
 
 // Holds an array of SocketDataProvider elements.  As Mock{TCP,SSL}ClientSocket
@@ -201,7 +350,7 @@
   }
 
   T* GetNext() {
-    DCHECK(next_index_ < data_providers_.size());
+    DCHECK_LT(next_index_, data_providers_.size());
     return data_providers_[next_index_++];
   }
 
@@ -239,16 +388,17 @@
 
   // Return |index|-th MockTCPClientSocket (starting from 0) that the factory
   // created.
-  MockTCPClientSocket* GetMockTCPClientSocket(int index) const;
+  MockTCPClientSocket* GetMockTCPClientSocket(size_t index) const;
 
   // Return |index|-th MockSSLClientSocket (starting from 0) that the factory
   // created.
-  MockSSLClientSocket* GetMockSSLClientSocket(int index) const;
+  MockSSLClientSocket* GetMockSSLClientSocket(size_t index) const;
 
   // ClientSocketFactory
-  virtual ClientSocket* CreateTCPClientSocket(const AddressList& addresses);
+  virtual ClientSocket* CreateTCPClientSocket(const AddressList& addresses,
+                                              NetLog* net_log);
   virtual SSLClientSocket* CreateSSLClientSocket(
-      ClientSocket* transport_socket,
+      ClientSocketHandle* transport_socket,
       const std::string& hostname,
       const SSLConfig& ssl_config);
 
@@ -263,14 +413,15 @@
 
 class MockClientSocket : public net::SSLClientSocket {
  public:
-  MockClientSocket();
+  explicit MockClientSocket(net::NetLog* net_log);
 
   // ClientSocket methods:
-  virtual int Connect(net::CompletionCallback* callback, LoadLog* load_log) = 0;
+  virtual int Connect(net::CompletionCallback* callback) = 0;
   virtual void Disconnect();
   virtual bool IsConnected() const;
   virtual bool IsConnectedAndIdle() const;
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen);
+  virtual int GetPeerAddress(AddressList* address) const;
+  virtual const BoundNetLog& NetLog() const { return net_log_;}
 
   // SSLClientSocket methods:
   virtual void GetSSLInfo(net::SSLInfo* ssl_info);
@@ -301,16 +452,18 @@
 
   // True if Connect completed successfully and Disconnect hasn't been called.
   bool connected_;
+
+  net::BoundNetLog net_log_;
 };
 
 class MockTCPClientSocket : public MockClientSocket {
  public:
-  MockTCPClientSocket(const net::AddressList& addresses,
+  MockTCPClientSocket(const net::AddressList& addresses, net::NetLog* net_log,
                       net::SocketDataProvider* socket);
 
   // ClientSocket methods:
-  virtual int Connect(net::CompletionCallback* callback,
-                      LoadLog* load_log);
+  virtual int Connect(net::CompletionCallback* callback);
+  virtual void Disconnect();
   virtual bool IsConnected() const;
   virtual bool IsConnectedAndIdle() const { return IsConnected(); }
 
@@ -348,15 +501,14 @@
 class MockSSLClientSocket : public MockClientSocket {
  public:
   MockSSLClientSocket(
-      net::ClientSocket* transport_socket,
+      net::ClientSocketHandle* transport_socket,
       const std::string& hostname,
       const net::SSLConfig& ssl_config,
       net::SSLSocketDataProvider* socket);
   ~MockSSLClientSocket();
 
-  virtual void GetSSLInfo(net::SSLInfo* ssl_info);
-
-  virtual int Connect(net::CompletionCallback* callback, LoadLog* load_log);
+  // ClientSocket methods:
+  virtual int Connect(net::CompletionCallback* callback);
   virtual void Disconnect();
 
   // Socket methods:
@@ -365,14 +517,22 @@
   virtual int Write(net::IOBuffer* buf, int buf_len,
                     net::CompletionCallback* callback);
 
+  // SSLClientSocket methods:
+  virtual void GetSSLInfo(net::SSLInfo* ssl_info);
+  virtual NextProtoStatus GetNextProto(std::string* proto);
+  virtual bool wasNpnNegotiated() const;
+  virtual bool setWasNpnNegotiated(bool negotiated);
+
   // This MockSocket does not implement the manual async IO feature.
   virtual void OnReadComplete(const MockRead& data) { NOTIMPLEMENTED(); }
 
  private:
   class ConnectCallback;
 
-  scoped_ptr<ClientSocket> transport_;
+  scoped_ptr<ClientSocketHandle> transport_;
   net::SSLSocketDataProvider* data_;
+  bool is_npn_state_set_;
+  bool new_npn_value_;
 };
 
 class TestSocketRequest : public CallbackRunner< Tuple1<int> > {
@@ -414,17 +574,17 @@
   virtual void TearDown();
 
   template <typename PoolType, typename SocketParams>
-  int StartRequestUsingPool(PoolType* socket_pool,
+  int StartRequestUsingPool(const scoped_refptr<PoolType>& socket_pool,
                             const std::string& group_name,
                             RequestPriority priority,
-                            const SocketParams& socket_params) {
-    DCHECK(socket_pool);
+                            const scoped_refptr<SocketParams>& socket_params) {
+    DCHECK(socket_pool.get());
     TestSocketRequest* request = new TestSocketRequest(&request_order_,
                                                        &completion_count_);
     requests_.push_back(request);
     int rv = request->handle()->Init(
         group_name, socket_params, priority, request,
-        socket_pool, NULL);
+        socket_pool, BoundNetLog());
     if (rv != ERR_IO_PENDING)
       request_order_.push_back(request);
     return rv;
@@ -448,6 +608,143 @@
   size_t completion_count_;
 };
 
+class MockTCPClientSocketPool : public TCPClientSocketPool {
+ public:
+  class MockConnectJob {
+   public:
+    MockConnectJob(ClientSocket* socket, ClientSocketHandle* handle,
+                   CompletionCallback* callback);
+
+    int Connect();
+    bool CancelHandle(const ClientSocketHandle* handle);
+
+   private:
+    void OnConnect(int rv);
+
+    scoped_ptr<ClientSocket> socket_;
+    ClientSocketHandle* handle_;
+    CompletionCallback* user_callback_;
+    CompletionCallbackImpl<MockConnectJob> connect_callback_;
+
+    DISALLOW_COPY_AND_ASSIGN(MockConnectJob);
+  };
+
+  MockTCPClientSocketPool(
+      int max_sockets,
+      int max_sockets_per_group,
+      const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+      ClientSocketFactory* socket_factory);
+
+  int release_count() const { return release_count_; };
+  int cancel_count() const { return cancel_count_; };
+
+  // TCPClientSocketPool methods.
+  virtual int RequestSocket(const std::string& group_name,
+                            const void* socket_params,
+                            RequestPriority priority,
+                            ClientSocketHandle* handle,
+                            CompletionCallback* callback,
+                            const BoundNetLog& net_log);
+
+  virtual void CancelRequest(const std::string& group_name,
+                             ClientSocketHandle* handle);
+  virtual void ReleaseSocket(const std::string& group_name,
+                             ClientSocket* socket, int id);
+
+ protected:
+  virtual ~MockTCPClientSocketPool();
+
+ private:
+  ClientSocketFactory* client_socket_factory_;
+  int release_count_;
+  int cancel_count_;
+  ScopedVector<MockConnectJob> job_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockTCPClientSocketPool);
+};
+
+class MockSOCKSClientSocketPool : public SOCKSClientSocketPool {
+ public:
+  MockSOCKSClientSocketPool(
+      int max_sockets,
+      int max_sockets_per_group,
+      const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+      const scoped_refptr<TCPClientSocketPool>& tcp_pool);
+
+  // SOCKSClientSocketPool methods.
+  virtual int RequestSocket(const std::string& group_name,
+                            const void* socket_params,
+                            RequestPriority priority,
+                            ClientSocketHandle* handle,
+                            CompletionCallback* callback,
+                            const BoundNetLog& net_log);
+
+  virtual void CancelRequest(const std::string& group_name,
+                             ClientSocketHandle* handle);
+  virtual void ReleaseSocket(const std::string& group_name,
+                             ClientSocket* socket, int id);
+
+ protected:
+  virtual ~MockSOCKSClientSocketPool();
+
+ private:
+  const scoped_refptr<TCPClientSocketPool> tcp_pool_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockSOCKSClientSocketPool);
+};
+
+struct MockHttpAuthControllerData {
+  MockHttpAuthControllerData(std::string header) : auth_header(header) {}
+
+  std::string auth_header;
+};
+
+class MockHttpAuthController : public HttpAuthController {
+ public:
+  MockHttpAuthController();
+  void SetMockAuthControllerData(struct MockHttpAuthControllerData* data,
+                                 size_t data_length);
+
+  // HttpAuthController methods.
+  virtual int MaybeGenerateAuthToken(const HttpRequestInfo* request,
+                                     CompletionCallback* callback,
+                                     const BoundNetLog& net_log);
+  virtual void AddAuthorizationHeader(
+      HttpRequestHeaders* authorization_headers);
+  virtual int HandleAuthChallenge(scoped_refptr<HttpResponseHeaders> headers,
+                                  bool do_not_send_server_auth,
+                                  bool establishing_tunnel,
+                                  const BoundNetLog& net_log);
+  virtual void ResetAuth(const std::wstring& username,
+                         const std::wstring& password);
+  virtual bool HaveAuthHandler() const;
+  virtual bool HaveAuth() const;
+
+ private:
+  virtual ~MockHttpAuthController() {}
+  const struct MockHttpAuthControllerData& CurrentData() const {
+    DCHECK(data_index_ < data_count_);
+    return data_[data_index_];
+  }
+
+  MockHttpAuthControllerData* data_;
+  size_t data_index_;
+  size_t data_count_;
+};
+
+// Constants for a successful SOCKS v5 handshake.
+extern const char kSOCKS5GreetRequest[];
+extern const int kSOCKS5GreetRequestLength;
+
+extern const char kSOCKS5GreetResponse[];
+extern const int kSOCKS5GreetResponseLength;
+
+extern const char kSOCKS5OkRequest[];
+extern const int kSOCKS5OkRequestLength;
+
+extern const char kSOCKS5OkResponse[];
+extern const int kSOCKS5OkResponseLength;
+
 }  // namespace net
 
 #endif  // NET_SOCKET_SOCKET_TEST_UTIL_H_
diff --git a/net/socket/socks5_client_socket.cc b/net/socket/socks5_client_socket.cc
index a4ba814..997fb25 100644
--- a/net/socket/socks5_client_socket.cc
+++ b/net/socket/socks5_client_socket.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -10,32 +10,13 @@
 #include "base/string_util.h"
 #include "base/trace_event.h"
 #include "net/base/io_buffer.h"
-#include "net/base/load_log.h"
+#include "net/base/net_log.h"
 #include "net/base/net_util.h"
 #include "net/base/sys_addrinfo.h"
+#include "net/socket/client_socket_handle.h"
 
 namespace net {
 
-namespace {
-
-// Returns a string description of |socks_error|, or NULL if |socks_error| is
-// not a valid SOCKS reply.
-const char* MapSOCKSReplyToErrorString(char socks_error) {
-  switch(socks_error) {
-    case 1: return "(1) General SOCKS server failure";
-    case 2: return "(2) Connection not allowed by ruleset";
-    case 3: return "(3) Network unreachable";
-    case 4: return "(4) Host unreachable";
-    case 5: return "(5) Connection refused";
-    case 6: return "(6) TTL expired";
-    case 7: return "(7) Command not supported";
-    case 8: return "(8) Address type not supported";
-    default: return NULL;
-  }
-}
-
-}  // namespace
-
 const unsigned int SOCKS5ClientSocket::kGreetReadHeaderSize = 2;
 const unsigned int SOCKS5ClientSocket::kWriteHeaderSize = 10;
 const unsigned int SOCKS5ClientSocket::kReadHeaderSize = 5;
@@ -46,7 +27,8 @@
 COMPILE_ASSERT(sizeof(struct in_addr) == 4, incorrect_system_size_of_IPv4);
 COMPILE_ASSERT(sizeof(struct in6_addr) == 16, incorrect_system_size_of_IPv6);
 
-SOCKS5ClientSocket::SOCKS5ClientSocket(ClientSocket* transport_socket,
+SOCKS5ClientSocket::SOCKS5ClientSocket(
+    ClientSocketHandle* transport_socket,
     const HostResolver::RequestInfo& req_info)
     : ALLOW_THIS_IN_INITIALIZER_LIST(
           io_callback_(this, &SOCKS5ClientSocket::OnIOComplete)),
@@ -57,17 +39,35 @@
       bytes_sent_(0),
       bytes_received_(0),
       read_header_size(kReadHeaderSize),
-      host_request_info_(req_info) {
+      host_request_info_(req_info),
+      net_log_(transport_socket->socket()->NetLog()) {
+}
+
+SOCKS5ClientSocket::SOCKS5ClientSocket(
+    ClientSocket* transport_socket,
+    const HostResolver::RequestInfo& req_info)
+    : ALLOW_THIS_IN_INITIALIZER_LIST(
+          io_callback_(this, &SOCKS5ClientSocket::OnIOComplete)),
+      transport_(new ClientSocketHandle()),
+      next_state_(STATE_NONE),
+      user_callback_(NULL),
+      completed_handshake_(false),
+      bytes_sent_(0),
+      bytes_received_(0),
+      read_header_size(kReadHeaderSize),
+      host_request_info_(req_info),
+      net_log_(transport_socket->NetLog()) {
+  transport_->set_socket(transport_socket);
 }
 
 SOCKS5ClientSocket::~SOCKS5ClientSocket() {
   Disconnect();
 }
 
-int SOCKS5ClientSocket::Connect(CompletionCallback* callback,
-                                LoadLog* load_log) {
+int SOCKS5ClientSocket::Connect(CompletionCallback* callback) {
   DCHECK(transport_.get());
-  DCHECK(transport_->IsConnected());
+  DCHECK(transport_->socket());
+  DCHECK(transport_->socket()->IsConnected());
   DCHECK_EQ(STATE_NONE, next_state_);
   DCHECK(!user_callback_);
 
@@ -75,8 +75,7 @@
   if (completed_handshake_)
     return OK;
 
-  load_log_ = load_log;
-  LoadLog::BeginEvent(load_log, LoadLog::TYPE_SOCKS5_CONNECT);
+  net_log_.BeginEvent(NetLog::TYPE_SOCKS5_CONNECT, NULL);
 
   next_state_ = STATE_GREET_WRITE;
   buffer_.clear();
@@ -85,29 +84,27 @@
   if (rv == ERR_IO_PENDING) {
     user_callback_ = callback;
   } else {
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_SOCKS5_CONNECT);
-    load_log_ = NULL;
+    net_log_.EndEvent(NetLog::TYPE_SOCKS5_CONNECT, NULL);
   }
   return rv;
 }
 
 void SOCKS5ClientSocket::Disconnect() {
   completed_handshake_ = false;
-  transport_->Disconnect();
+  transport_->socket()->Disconnect();
 
   // Reset other states to make sure they aren't mistakenly used later.
   // These are the states initialized by Connect().
   next_state_ = STATE_NONE;
   user_callback_ = NULL;
-  load_log_ = NULL;
 }
 
 bool SOCKS5ClientSocket::IsConnected() const {
-  return completed_handshake_ && transport_->IsConnected();
+  return completed_handshake_ && transport_->socket()->IsConnected();
 }
 
 bool SOCKS5ClientSocket::IsConnectedAndIdle() const {
-  return completed_handshake_ && transport_->IsConnectedAndIdle();
+  return completed_handshake_ && transport_->socket()->IsConnectedAndIdle();
 }
 
 // Read is called by the transport layer above to read. This can only be done
@@ -118,7 +115,7 @@
   DCHECK_EQ(STATE_NONE, next_state_);
   DCHECK(!user_callback_);
 
-  return transport_->Read(buf, buf_len, callback);
+  return transport_->socket()->Read(buf, buf_len, callback);
 }
 
 // Write is called by the transport layer. This can only be done if the
@@ -129,15 +126,15 @@
   DCHECK_EQ(STATE_NONE, next_state_);
   DCHECK(!user_callback_);
 
-  return transport_->Write(buf, buf_len, callback);
+  return transport_->socket()->Write(buf, buf_len, callback);
 }
 
 bool SOCKS5ClientSocket::SetReceiveBufferSize(int32 size) {
-  return transport_->SetReceiveBufferSize(size);
+  return transport_->socket()->SetReceiveBufferSize(size);
 }
 
 bool SOCKS5ClientSocket::SetSendBufferSize(int32 size) {
-  return transport_->SetSendBufferSize(size);
+  return transport_->socket()->SetSendBufferSize(size);
 }
 
 void SOCKS5ClientSocket::DoCallback(int result) {
@@ -155,8 +152,7 @@
   DCHECK_NE(STATE_NONE, next_state_);
   int rv = DoLoop(result);
   if (rv != ERR_IO_PENDING) {
-    LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKS5_CONNECT);
-    load_log_ = NULL;
+    net_log_.EndEvent(NetLog::TYPE_SOCKS5_CONNECT, NULL);
     DoCallback(rv);
   }
 }
@@ -170,39 +166,39 @@
     switch (state) {
       case STATE_GREET_WRITE:
         DCHECK_EQ(OK, rv);
-        LoadLog::BeginEvent(load_log_, LoadLog::TYPE_SOCKS5_GREET_WRITE);
+        net_log_.BeginEvent(NetLog::TYPE_SOCKS5_GREET_WRITE, NULL);
         rv = DoGreetWrite();
         break;
       case STATE_GREET_WRITE_COMPLETE:
         rv = DoGreetWriteComplete(rv);
-        LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKS5_GREET_WRITE);
+        net_log_.EndEvent(NetLog::TYPE_SOCKS5_GREET_WRITE, NULL);
         break;
       case STATE_GREET_READ:
         DCHECK_EQ(OK, rv);
-        LoadLog::BeginEvent(load_log_, LoadLog::TYPE_SOCKS5_GREET_READ);
+        net_log_.BeginEvent(NetLog::TYPE_SOCKS5_GREET_READ, NULL);
         rv = DoGreetRead();
         break;
       case STATE_GREET_READ_COMPLETE:
         rv = DoGreetReadComplete(rv);
-        LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKS5_GREET_READ);
+        net_log_.EndEvent(NetLog::TYPE_SOCKS5_GREET_READ, NULL);
         break;
       case STATE_HANDSHAKE_WRITE:
         DCHECK_EQ(OK, rv);
-        LoadLog::BeginEvent(load_log_, LoadLog::TYPE_SOCKS5_HANDSHAKE_WRITE);
+        net_log_.BeginEvent(NetLog::TYPE_SOCKS5_HANDSHAKE_WRITE, NULL);
         rv = DoHandshakeWrite();
         break;
       case STATE_HANDSHAKE_WRITE_COMPLETE:
         rv = DoHandshakeWriteComplete(rv);
-        LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKS5_HANDSHAKE_WRITE);
+        net_log_.EndEvent(NetLog::TYPE_SOCKS5_HANDSHAKE_WRITE, NULL);
         break;
       case STATE_HANDSHAKE_READ:
         DCHECK_EQ(OK, rv);
-        LoadLog::BeginEvent(load_log_, LoadLog::TYPE_SOCKS5_HANDSHAKE_READ);
+        net_log_.BeginEvent(NetLog::TYPE_SOCKS5_HANDSHAKE_READ, NULL);
         rv = DoHandshakeRead();
         break;
       case STATE_HANDSHAKE_READ_COMPLETE:
         rv = DoHandshakeReadComplete(rv);
-        LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKS5_HANDSHAKE_READ);
+        net_log_.EndEvent(NetLog::TYPE_SOCKS5_HANDSHAKE_READ, NULL);
         break;
       default:
         NOTREACHED() << "bad state";
@@ -220,10 +216,8 @@
   // Since we only have 1 byte to send the hostname length in, if the
   // URL has a hostname longer than 255 characters we can't send it.
   if (0xFF < host_request_info_.hostname().size()) {
-    LoadLog::AddStringLiteral(load_log_,
-        "Failed sending request because hostname is "
-        "longer than 255 characters");
-    return ERR_INVALID_URL;
+    net_log_.AddEvent(NetLog::TYPE_SOCKS_HOSTNAME_TOO_BIG, NULL);
+    return ERR_SOCKS_CONNECTION_FAILED;
   }
 
   if (buffer_.empty()) {
@@ -237,7 +231,8 @@
   handshake_buf_ = new IOBuffer(handshake_buf_len);
   memcpy(handshake_buf_->data(), &buffer_.data()[bytes_sent_],
          handshake_buf_len);
-  return transport_->Write(handshake_buf_, handshake_buf_len, &io_callback_);
+  return transport_->socket()->Write(handshake_buf_, handshake_buf_len,
+                                     &io_callback_);
 }
 
 int SOCKS5ClientSocket::DoGreetWriteComplete(int result) {
@@ -259,15 +254,19 @@
   next_state_ = STATE_GREET_READ_COMPLETE;
   size_t handshake_buf_len = kGreetReadHeaderSize - bytes_received_;
   handshake_buf_ = new IOBuffer(handshake_buf_len);
-  return transport_->Read(handshake_buf_, handshake_buf_len, &io_callback_);
+  return transport_->socket()->Read(handshake_buf_, handshake_buf_len,
+                                    &io_callback_);
 }
 
 int SOCKS5ClientSocket::DoGreetReadComplete(int result) {
   if (result < 0)
     return result;
 
-  if (result == 0)
-    return ERR_CONNECTION_CLOSED;  // Unexpected socket close
+  if (result == 0) {
+    net_log_.AddEvent(NetLog::TYPE_SOCKS_UNEXPECTEDLY_CLOSED_DURING_GREETING,
+                      NULL);
+    return ERR_SOCKS_CONNECTION_FAILED;
+  }
 
   bytes_received_ += result;
   buffer_.append(handshake_buf_->data(), result);
@@ -278,16 +277,14 @@
 
   // Got the greet data.
   if (buffer_[0] != kSOCKS5Version) {
-    LoadLog::AddStringLiteral(load_log_, "Unexpected SOCKS version");
-    LoadLog::AddString(load_log_, StringPrintf(
-        "buffer_[0] = 0x%x", static_cast<int>(buffer_[0])));
-    return ERR_INVALID_RESPONSE;
+    net_log_.AddEvent(NetLog::TYPE_SOCKS_UNEXPECTED_VERSION,
+                      new NetLogIntegerParameter("version", buffer_[0]));
+    return ERR_SOCKS_CONNECTION_FAILED;
   }
   if (buffer_[1] != 0x00) {
-    LoadLog::AddStringLiteral(load_log_, "Unexpected authentication method");
-    LoadLog::AddString(load_log_, StringPrintf(
-        "buffer_[1] = 0x%x", static_cast<int>(buffer_[1])));
-    return ERR_INVALID_RESPONSE;  // Unknown error
+    net_log_.AddEvent(NetLog::TYPE_SOCKS_UNEXPECTED_AUTH,
+                      new NetLogIntegerParameter("method", buffer_[1]));
+    return ERR_SOCKS_CONNECTION_FAILED;
   }
 
   buffer_.clear();
@@ -333,7 +330,8 @@
   handshake_buf_ = new IOBuffer(handshake_buf_len);
   memcpy(handshake_buf_->data(), &buffer_[bytes_sent_],
          handshake_buf_len);
-  return transport_->Write(handshake_buf_, handshake_buf_len, &io_callback_);
+  return transport_->socket()->Write(handshake_buf_, handshake_buf_len,
+                                     &io_callback_);
 }
 
 int SOCKS5ClientSocket::DoHandshakeWriteComplete(int result) {
@@ -366,7 +364,8 @@
 
   int handshake_buf_len = read_header_size - bytes_received_;
   handshake_buf_ = new IOBuffer(handshake_buf_len);
-  return transport_->Read(handshake_buf_, handshake_buf_len, &io_callback_);
+  return transport_->socket()->Read(handshake_buf_, handshake_buf_len,
+                                    &io_callback_);
 }
 
 int SOCKS5ClientSocket::DoHandshakeReadComplete(int result) {
@@ -374,8 +373,11 @@
     return result;
 
   // The underlying socket closed unexpectedly.
-  if (result == 0)
-    return ERR_CONNECTION_CLOSED;
+  if (result == 0) {
+    net_log_.AddEvent(NetLog::TYPE_SOCKS_UNEXPECTEDLY_CLOSED_DURING_HANDSHAKE,
+                      NULL);
+    return ERR_SOCKS_CONNECTION_FAILED;
+  }
 
   buffer_.append(handshake_buf_->data(), result);
   bytes_received_ += result;
@@ -384,24 +386,14 @@
   // and accordingly increase them
   if (bytes_received_ == kReadHeaderSize) {
     if (buffer_[0] != kSOCKS5Version || buffer_[2] != kNullByte) {
-      LoadLog::AddStringLiteral(load_log_, "Unexpected SOCKS version.");
-      LoadLog::AddString(load_log_, StringPrintf(
-          "buffer_[0] = 0x%x; buffer_[2] = 0x%x",
-          static_cast<int>(buffer_[0]),
-          static_cast<int>(buffer_[2])));
-      return ERR_INVALID_RESPONSE;
+      net_log_.AddEvent(NetLog::TYPE_SOCKS_UNEXPECTED_VERSION,
+                        new NetLogIntegerParameter("version", buffer_[0]));
+      return ERR_SOCKS_CONNECTION_FAILED;
     }
     if (buffer_[1] != 0x00) {
-      LoadLog::AddStringLiteral(load_log_,
-                                "SOCKS server returned a failure code:");
-      const char* error_string = MapSOCKSReplyToErrorString(buffer_[1]);
-      if (error_string) {
-        LoadLog::AddStringLiteral(load_log_, error_string);
-      } else {
-        LoadLog::AddString(load_log_, StringPrintf(
-            "buffer_[1] = 0x%x", static_cast<int>(buffer_[1])));
-      }
-      return ERR_FAILED;
+      net_log_.AddEvent(NetLog::TYPE_SOCKS_SERVER_ERROR,
+                        new NetLogIntegerParameter("error_code", buffer_[1]));
+      return ERR_SOCKS_CONNECTION_FAILED;
     }
 
     // We check the type of IP/Domain the server returns and accordingly
@@ -418,10 +410,9 @@
     else if (address_type == kEndPointResolvedIPv6)
       read_header_size += sizeof(struct in6_addr) - 1;
     else {
-      LoadLog::AddStringLiteral(load_log_, "Unknown address type in response");
-      LoadLog::AddString(load_log_, StringPrintf(
-          "buffer_[3] = 0x%x", static_cast<int>(buffer_[3])));
-      return ERR_INVALID_RESPONSE;
+      net_log_.AddEvent(NetLog::TYPE_SOCKS_UNKNOWN_ADDRESS_TYPE,
+                        new NetLogIntegerParameter("address_type", buffer_[3]));
+      return ERR_SOCKS_CONNECTION_FAILED;
     }
 
     read_header_size += 2;  // for the port.
@@ -443,9 +434,8 @@
   return OK;
 }
 
-int SOCKS5ClientSocket::GetPeerName(struct sockaddr* name,
-                                    socklen_t* namelen) {
-  return transport_->GetPeerName(name, namelen);
+int SOCKS5ClientSocket::GetPeerAddress(AddressList* address) const {
+  return transport_->socket()->GetPeerAddress(address);
 }
 
 }  // namespace net
diff --git a/net/socket/socks5_client_socket.h b/net/socket/socks5_client_socket.h
index dec1cc4..e6150f3 100644
--- a/net/socket/socks5_client_socket.h
+++ b/net/socket/socks5_client_socket.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -15,12 +15,14 @@
 #include "net/base/completion_callback.h"
 #include "net/base/host_resolver.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
 #include "net/socket/client_socket.h"
 #include "testing/gtest/include/gtest/gtest_prod.h"
 
 namespace net {
 
-class LoadLog;
+class ClientSocketHandle;
+class BoundNetLog;
 
 // This ClientSocket is used to setup a SOCKSv5 handshake with a socks proxy.
 // Currently no SOCKSv5 authentication is supported.
@@ -35,6 +37,10 @@
   // Although SOCKS 5 supports 3 different modes of addressing, we will
   // always pass it a hostname. This means the DNS resolving is done
   // proxy side.
+  SOCKS5ClientSocket(ClientSocketHandle* transport_socket,
+                     const HostResolver::RequestInfo& req_info);
+
+  // Deprecated constructor (http://crbug.com/37810) that takes a ClientSocket.
   SOCKS5ClientSocket(ClientSocket* transport_socket,
                      const HostResolver::RequestInfo& req_info);
 
@@ -44,10 +50,11 @@
   // ClientSocket methods:
 
   // Does the SOCKS handshake and completes the protocol.
-  virtual int Connect(CompletionCallback* callback, LoadLog* load_log);
+  virtual int Connect(CompletionCallback* callback);
   virtual void Disconnect();
   virtual bool IsConnected() const;
   virtual bool IsConnectedAndIdle() const;
+  virtual const BoundNetLog& NetLog() const { return net_log_; }
 
   // Socket methods:
   virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
@@ -56,7 +63,7 @@
   virtual bool SetReceiveBufferSize(int32 size);
   virtual bool SetSendBufferSize(int32 size);
 
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen);
+  virtual int GetPeerAddress(AddressList* address) const;
 
  private:
   enum State {
@@ -105,7 +112,7 @@
   CompletionCallbackImpl<SOCKS5ClientSocket> io_callback_;
 
   // Stores the underlying socket.
-  scoped_ptr<ClientSocket> transport_;
+  scoped_ptr<ClientSocketHandle> transport_;
 
   State next_state_;
 
@@ -133,7 +140,7 @@
 
   HostResolver::RequestInfo host_request_info_;
 
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
 
   DISALLOW_COPY_AND_ASSIGN(SOCKS5ClientSocket);
 };
diff --git a/net/socket/socks5_client_socket_unittest.cc b/net/socket/socks5_client_socket_unittest.cc
index b4eb2cf..736a725 100644
--- a/net/socket/socks5_client_socket_unittest.cc
+++ b/net/socket/socks5_client_socket_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -8,8 +8,8 @@
 #include <map>
 
 #include "net/base/address_list.h"
-#include "net/base/load_log.h"
-#include "net/base/load_log_unittest.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/mock_host_resolver.h"
 #include "net/base/sys_addrinfo.h"
 #include "net/base/test_completion_callback.h"
@@ -32,14 +32,18 @@
   SOCKS5ClientSocketTest();
   // Create a SOCKSClientSocket on top of a MockSocket.
   SOCKS5ClientSocket* BuildMockSocket(MockRead reads[],
+                                      size_t reads_count,
                                       MockWrite writes[],
+                                      size_t writes_count,
                                       const std::string& hostname,
-                                      int port);
+                                      int port,
+                                      NetLog* net_log);
 
   virtual void SetUp();
 
  protected:
   const uint16 kNwPort;
+  CapturingNetLog net_log_;
   scoped_ptr<SOCKS5ClientSocket> user_sock_;
   AddressList address_list_;
   ClientSocket* tcp_sock_;
@@ -52,7 +56,9 @@
 };
 
 SOCKS5ClientSocketTest::SOCKS5ClientSocketTest()
-  : kNwPort(htons(80)), host_resolver_(new MockHostResolver) {
+  : kNwPort(htons(80)),
+    net_log_(CapturingNetLog::kUnbounded),
+    host_resolver_(new MockHostResolver) {
 }
 
 // Set up platform before every test case
@@ -61,20 +67,25 @@
 
   // Resolve the "localhost" AddressList used by the TCP connection to connect.
   HostResolver::RequestInfo info("www.socks-proxy.com", 1080);
-  int rv = host_resolver_->Resolve(info, &address_list_, NULL, NULL, NULL);
+  int rv = host_resolver_->Resolve(info, &address_list_, NULL, NULL,
+                                   BoundNetLog());
   ASSERT_EQ(OK, rv);
 }
 
 SOCKS5ClientSocket* SOCKS5ClientSocketTest::BuildMockSocket(
     MockRead reads[],
+    size_t reads_count,
     MockWrite writes[],
+    size_t writes_count,
     const std::string& hostname,
-    int port) {
+    int port,
+    NetLog* net_log) {
   TestCompletionCallback callback;
-  data_.reset(new StaticSocketDataProvider(reads, writes));
-  tcp_sock_ = new MockTCPClientSocket(address_list_, data_.get());
+  data_.reset(new StaticSocketDataProvider(reads, reads_count,
+                                           writes, writes_count));
+  tcp_sock_ = new MockTCPClientSocket(address_list_, net_log, data_.get());
 
-  int rv = tcp_sock_->Connect(&callback, NULL);
+  int rv = tcp_sock_->Connect(&callback);
   EXPECT_EQ(ERR_IO_PENDING, rv);
   rv = callback.WaitForResult();
   EXPECT_EQ(OK, rv);
@@ -84,18 +95,12 @@
       HostResolver::RequestInfo(hostname, port));
 }
 
-const char kSOCKS5GreetRequest[] = { 0x05, 0x01, 0x00 };
-const char kSOCKS5GreetResponse[] = { 0x05, 0x00 };
-const char kSOCKS5OkResponse[] =
-    { 0x05, 0x00, 0x00, 0x01, 127, 0, 0, 1, 0x00, 0x50 };
-
-
 // Tests a complete SOCKS5 handshake and the disconnection.
 TEST_F(SOCKS5ClientSocketTest, CompleteHandshake) {
   const std::string payload_write = "random data";
   const std::string payload_read = "moar random data";
 
-  const char kSOCKS5OkRequest[] = {
+  const char kOkRequest[] = {
     0x05,  // Version
     0x01,  // Command (CONNECT)
     0x00,  // Reserved.
@@ -107,31 +112,34 @@
   };
 
   MockWrite data_writes[] = {
-      MockWrite(true, kSOCKS5GreetRequest, arraysize(kSOCKS5GreetRequest)),
-      MockWrite(true, kSOCKS5OkRequest, arraysize(kSOCKS5OkRequest)),
+      MockWrite(true, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength),
+      MockWrite(true, kOkRequest, arraysize(kOkRequest)),
       MockWrite(true, payload_write.data(), payload_write.size()) };
   MockRead data_reads[] = {
-      MockRead(true, kSOCKS5GreetResponse, arraysize(kSOCKS5GreetResponse)),
-      MockRead(true, kSOCKS5OkResponse, arraysize(kSOCKS5OkResponse)),
+      MockRead(true, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength),
+      MockRead(true, kSOCKS5OkResponse, kSOCKS5OkResponseLength),
       MockRead(true, payload_read.data(), payload_read.size()) };
 
-  user_sock_.reset(BuildMockSocket(data_reads, data_writes, "localhost", 80));
+  user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                   data_writes, arraysize(data_writes),
+                                   "localhost", 80, &net_log_));
 
   // At this state the TCP connection is completed but not the SOCKS handshake.
   EXPECT_TRUE(tcp_sock_->IsConnected());
   EXPECT_FALSE(user_sock_->IsConnected());
 
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  int rv = user_sock_->Connect(&callback_, log);
+  int rv = user_sock_->Connect(&callback_);
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_FALSE(user_sock_->IsConnected());
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS5_CONNECT));
+  EXPECT_TRUE(LogContainsBeginEvent(net_log_.entries(), 0,
+                                    NetLog::TYPE_SOCKS5_CONNECT));
 
   rv = callback_.WaitForResult();
 
   EXPECT_EQ(OK, rv);
   EXPECT_TRUE(user_sock_->IsConnected());
-  EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS5_CONNECT));
+  EXPECT_TRUE(LogContainsEndEvent(net_log_.entries(), -1,
+                                  NetLog::TYPE_SOCKS5_CONNECT));
 
   scoped_refptr<IOBuffer> buffer = new IOBuffer(payload_write.size());
   memcpy(buffer->data(), payload_write.data(), payload_write.size());
@@ -169,17 +177,19 @@
 
   for (int i = 0; i < 2; ++i) {
     MockWrite data_writes[] = {
-        MockWrite(false, kSOCKS5GreetRequest, arraysize(kSOCKS5GreetRequest)),
+        MockWrite(false, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength),
         MockWrite(false, request.data(), request.size())
     };
     MockRead data_reads[] = {
-        MockRead(false, kSOCKS5GreetResponse, arraysize(kSOCKS5GreetResponse)),
-        MockRead(false, kSOCKS5OkResponse, arraysize(kSOCKS5OkResponse))
+        MockRead(false, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength),
+        MockRead(false, kSOCKS5OkResponse, kSOCKS5OkResponseLength)
     };
 
-    user_sock_.reset(BuildMockSocket(data_reads, data_writes, hostname, 80));
+    user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                     data_writes, arraysize(data_writes),
+                                     hostname, 80, NULL));
 
-    int rv = user_sock_->Connect(&callback_, NULL);
+    int rv = user_sock_->Connect(&callback_);
     EXPECT_EQ(OK, rv);
     EXPECT_TRUE(user_sock_->IsConnected());
 
@@ -197,20 +207,21 @@
   // Create a SOCKS socket, with mock transport socket.
   MockWrite data_writes[] = {MockWrite()};
   MockRead data_reads[] = {MockRead()};
-  user_sock_.reset(BuildMockSocket(data_reads, data_writes,
-                                   large_host_name, 80));
+  user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                   data_writes, arraysize(data_writes),
+                                   large_host_name, 80, NULL));
 
   // Try to connect -- should fail (without having read/written anything to
   // the transport socket first) because the hostname is too long.
   TestCompletionCallback callback;
-  int rv = user_sock_->Connect(&callback, NULL);
-  EXPECT_EQ(ERR_INVALID_URL, rv);
+  int rv = user_sock_->Connect(&callback);
+  EXPECT_EQ(ERR_SOCKS_CONNECTION_FAILED, rv);
 }
 
 TEST_F(SOCKS5ClientSocketTest, PartialReadWrites) {
   const std::string hostname = "www.google.com";
 
-  const char kSOCKS5OkRequest[] = {
+  const char kOkRequest[] = {
     0x05,  // Version
     0x01,  // Command (CONNECT)
     0x00,  // Reserved.
@@ -228,19 +239,22 @@
     MockWrite data_writes[] = {
         MockWrite(true, arraysize(partial1)),
         MockWrite(true, partial2, arraysize(partial2)),
-        MockWrite(true, kSOCKS5OkRequest, arraysize(kSOCKS5OkRequest)) };
+        MockWrite(true, kOkRequest, arraysize(kOkRequest)) };
     MockRead data_reads[] = {
-        MockRead(true, kSOCKS5GreetResponse, arraysize(kSOCKS5GreetResponse)),
-        MockRead(true, kSOCKS5OkResponse, arraysize(kSOCKS5OkResponse)) };
-    user_sock_.reset(BuildMockSocket(data_reads, data_writes, hostname, 80));
-    scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-    int rv = user_sock_->Connect(&callback_, log);
+        MockRead(true, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength),
+        MockRead(true, kSOCKS5OkResponse, kSOCKS5OkResponseLength) };
+    user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                     data_writes, arraysize(data_writes),
+                                     hostname, 80, &net_log_));
+    int rv = user_sock_->Connect(&callback_);
     EXPECT_EQ(ERR_IO_PENDING, rv);
-    EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS5_CONNECT));
+    EXPECT_TRUE(LogContainsBeginEvent(net_log_.entries(), 0,
+                NetLog::TYPE_SOCKS5_CONNECT));
     rv = callback_.WaitForResult();
     EXPECT_EQ(OK, rv);
     EXPECT_TRUE(user_sock_->IsConnected());
-    EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS5_CONNECT));
+    EXPECT_TRUE(LogContainsEndEvent(net_log_.entries(), -1,
+                NetLog::TYPE_SOCKS5_CONNECT));
   }
 
   // Test for partial greet response read
@@ -248,69 +262,78 @@
     const char partial1[] = { 0x05 };
     const char partial2[] = { 0x00 };
     MockWrite data_writes[] = {
-        MockWrite(true, kSOCKS5GreetRequest, arraysize(kSOCKS5GreetRequest)),
-        MockWrite(true, kSOCKS5OkRequest, arraysize(kSOCKS5OkRequest)) };
+        MockWrite(true, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength),
+        MockWrite(true, kOkRequest, arraysize(kOkRequest)) };
     MockRead data_reads[] = {
         MockRead(true, partial1, arraysize(partial1)),
         MockRead(true, partial2, arraysize(partial2)),
-        MockRead(true, kSOCKS5OkResponse, arraysize(kSOCKS5OkResponse)) };
-    user_sock_.reset(BuildMockSocket(data_reads, data_writes, hostname, 80));
-    scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-    int rv = user_sock_->Connect(&callback_, log);
+        MockRead(true, kSOCKS5OkResponse, kSOCKS5OkResponseLength) };
+    user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                     data_writes, arraysize(data_writes),
+                                     hostname, 80, &net_log_));
+    int rv = user_sock_->Connect(&callback_);
     EXPECT_EQ(ERR_IO_PENDING, rv);
-    EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS5_CONNECT));
+    EXPECT_TRUE(LogContainsBeginEvent(net_log_.entries(), 0,
+                                      NetLog::TYPE_SOCKS5_CONNECT));
     rv = callback_.WaitForResult();
     EXPECT_EQ(OK, rv);
     EXPECT_TRUE(user_sock_->IsConnected());
-    EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS5_CONNECT));
+    EXPECT_TRUE(LogContainsEndEvent(net_log_.entries(), -1,
+                                    NetLog::TYPE_SOCKS5_CONNECT));
   }
 
   // Test for partial handshake request write.
   {
     const int kSplitPoint = 3;  // Break handshake write into two parts.
     MockWrite data_writes[] = {
-        MockWrite(true, kSOCKS5GreetRequest, arraysize(kSOCKS5GreetRequest)),
-        MockWrite(true, kSOCKS5OkRequest, kSplitPoint),
-        MockWrite(true, kSOCKS5OkRequest + kSplitPoint,
-                        arraysize(kSOCKS5OkRequest) - kSplitPoint)
+        MockWrite(true, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength),
+        MockWrite(true, kOkRequest, kSplitPoint),
+        MockWrite(true, kOkRequest + kSplitPoint,
+                  arraysize(kOkRequest) - kSplitPoint)
     };
     MockRead data_reads[] = {
-        MockRead(true, kSOCKS5GreetResponse, arraysize(kSOCKS5GreetResponse)),
-        MockRead(true, kSOCKS5OkResponse, arraysize(kSOCKS5OkResponse)) };
-    user_sock_.reset(BuildMockSocket(data_reads, data_writes, hostname, 80));
-    scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-    int rv = user_sock_->Connect(&callback_, log);
+        MockRead(true, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength),
+        MockRead(true, kSOCKS5OkResponse, kSOCKS5OkResponseLength) };
+    user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                     data_writes, arraysize(data_writes),
+                                     hostname, 80, &net_log_));
+    int rv = user_sock_->Connect(&callback_);
     EXPECT_EQ(ERR_IO_PENDING, rv);
-    EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS5_CONNECT));
+    EXPECT_TRUE(LogContainsBeginEvent(net_log_.entries(), 0,
+                                      NetLog::TYPE_SOCKS5_CONNECT));
     rv = callback_.WaitForResult();
     EXPECT_EQ(OK, rv);
     EXPECT_TRUE(user_sock_->IsConnected());
-    EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS5_CONNECT));
+    EXPECT_TRUE(LogContainsEndEvent(net_log_.entries(), -1,
+                                    NetLog::TYPE_SOCKS5_CONNECT));
   }
 
   // Test for partial handshake response read
   {
     const int kSplitPoint = 6;  // Break the handshake read into two parts.
     MockWrite data_writes[] = {
-        MockWrite(true, kSOCKS5GreetRequest, arraysize(kSOCKS5GreetRequest)),
-        MockWrite(true, kSOCKS5OkRequest, arraysize(kSOCKS5OkRequest))
+        MockWrite(true, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength),
+        MockWrite(true, kOkRequest, arraysize(kOkRequest))
     };
     MockRead data_reads[] = {
-        MockRead(true, kSOCKS5GreetResponse, arraysize(kSOCKS5GreetResponse)),
+        MockRead(true, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength),
         MockRead(true, kSOCKS5OkResponse, kSplitPoint),
         MockRead(true, kSOCKS5OkResponse + kSplitPoint,
-                 arraysize(kSOCKS5OkResponse) - kSplitPoint)
+                 kSOCKS5OkResponseLength - kSplitPoint)
     };
 
-    user_sock_.reset(BuildMockSocket(data_reads, data_writes, hostname, 80));
-    scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-    int rv = user_sock_->Connect(&callback_, log);
+    user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                     data_writes, arraysize(data_writes),
+                                     hostname, 80, &net_log_));
+    int rv = user_sock_->Connect(&callback_);
     EXPECT_EQ(ERR_IO_PENDING, rv);
-    EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS5_CONNECT));
+    EXPECT_TRUE(LogContainsBeginEvent(net_log_.entries(), 0,
+                                      NetLog::TYPE_SOCKS5_CONNECT));
     rv = callback_.WaitForResult();
     EXPECT_EQ(OK, rv);
     EXPECT_TRUE(user_sock_->IsConnected());
-    EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS5_CONNECT));
+    EXPECT_TRUE(LogContainsEndEvent(net_log_.entries(), -1,
+                                    NetLog::TYPE_SOCKS5_CONNECT));
   }
 }
 
diff --git a/net/socket/socks_client_socket.cc b/net/socket/socks_client_socket.cc
index 5850da3..32c4e3b 100644
--- a/net/socket/socks_client_socket.cc
+++ b/net/socket/socks_client_socket.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -8,9 +8,10 @@
 #include "base/compiler_specific.h"
 #include "base/trace_event.h"
 #include "net/base/io_buffer.h"
-#include "net/base/load_log.h"
+#include "net/base/net_log.h"
 #include "net/base/net_util.h"
 #include "net/base/sys_addrinfo.h"
+#include "net/socket/client_socket_handle.h"
 
 namespace net {
 
@@ -59,7 +60,7 @@
 COMPILE_ASSERT(sizeof(SOCKS4ServerResponse) == kReadHeaderSize,
                socks4_server_response_struct_wrong_size);
 
-SOCKSClientSocket::SOCKSClientSocket(ClientSocket* transport_socket,
+SOCKSClientSocket::SOCKSClientSocket(ClientSocketHandle* transport_socket,
                                      const HostResolver::RequestInfo& req_info,
                                      HostResolver* host_resolver)
     : ALLOW_THIS_IN_INITIALIZER_LIST(
@@ -72,17 +73,36 @@
       bytes_sent_(0),
       bytes_received_(0),
       host_resolver_(host_resolver),
-      host_request_info_(req_info) {
+      host_request_info_(req_info),
+      net_log_(transport_socket->socket()->NetLog()) {
+}
+
+SOCKSClientSocket::SOCKSClientSocket(ClientSocket* transport_socket,
+                                     const HostResolver::RequestInfo& req_info,
+                                     HostResolver* host_resolver)
+    : ALLOW_THIS_IN_INITIALIZER_LIST(
+          io_callback_(this, &SOCKSClientSocket::OnIOComplete)),
+      transport_(new ClientSocketHandle()),
+      next_state_(STATE_NONE),
+      socks_version_(kSOCKS4Unresolved),
+      user_callback_(NULL),
+      completed_handshake_(false),
+      bytes_sent_(0),
+      bytes_received_(0),
+      host_resolver_(host_resolver),
+      host_request_info_(req_info),
+      net_log_(transport_socket->NetLog()) {
+  transport_->set_socket(transport_socket);
 }
 
 SOCKSClientSocket::~SOCKSClientSocket() {
   Disconnect();
 }
 
-int SOCKSClientSocket::Connect(CompletionCallback* callback,
-                               LoadLog* load_log) {
+int SOCKSClientSocket::Connect(CompletionCallback* callback) {
   DCHECK(transport_.get());
-  DCHECK(transport_->IsConnected());
+  DCHECK(transport_->socket());
+  DCHECK(transport_->socket()->IsConnected());
   DCHECK_EQ(STATE_NONE, next_state_);
   DCHECK(!user_callback_);
 
@@ -91,16 +111,14 @@
     return OK;
 
   next_state_ = STATE_RESOLVE_HOST;
-  load_log_ = load_log;
 
-  LoadLog::BeginEvent(load_log, LoadLog::TYPE_SOCKS_CONNECT);
+  net_log_.BeginEvent(NetLog::TYPE_SOCKS_CONNECT, NULL);
 
   int rv = DoLoop(OK);
   if (rv == ERR_IO_PENDING) {
     user_callback_ = callback;
   } else {
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_SOCKS_CONNECT);
-    load_log_ = NULL;
+    net_log_.EndEvent(NetLog::TYPE_SOCKS_CONNECT, NULL);
   }
   return rv;
 }
@@ -108,21 +126,20 @@
 void SOCKSClientSocket::Disconnect() {
   completed_handshake_ = false;
   host_resolver_.Cancel();
-  transport_->Disconnect();
+  transport_->socket()->Disconnect();
 
   // Reset other states to make sure they aren't mistakenly used later.
   // These are the states initialized by Connect().
   next_state_ = STATE_NONE;
   user_callback_ = NULL;
-  load_log_ = NULL;
 }
 
 bool SOCKSClientSocket::IsConnected() const {
-  return completed_handshake_ && transport_->IsConnected();
+  return completed_handshake_ && transport_->socket()->IsConnected();
 }
 
 bool SOCKSClientSocket::IsConnectedAndIdle() const {
-  return completed_handshake_ && transport_->IsConnectedAndIdle();
+  return completed_handshake_ && transport_->socket()->IsConnectedAndIdle();
 }
 
 // Read is called by the transport layer above to read. This can only be done
@@ -133,7 +150,7 @@
   DCHECK_EQ(STATE_NONE, next_state_);
   DCHECK(!user_callback_);
 
-  return transport_->Read(buf, buf_len, callback);
+  return transport_->socket()->Read(buf, buf_len, callback);
 }
 
 // Write is called by the transport layer. This can only be done if the
@@ -144,15 +161,15 @@
   DCHECK_EQ(STATE_NONE, next_state_);
   DCHECK(!user_callback_);
 
-  return transport_->Write(buf, buf_len, callback);
+  return transport_->socket()->Write(buf, buf_len, callback);
 }
 
 bool SOCKSClientSocket::SetReceiveBufferSize(int32 size) {
-  return transport_->SetReceiveBufferSize(size);
+  return transport_->socket()->SetReceiveBufferSize(size);
 }
 
 bool SOCKSClientSocket::SetSendBufferSize(int32 size) {
-  return transport_->SetSendBufferSize(size);
+  return transport_->socket()->SetSendBufferSize(size);
 }
 
 void SOCKSClientSocket::DoCallback(int result) {
@@ -171,8 +188,7 @@
   DCHECK_NE(STATE_NONE, next_state_);
   int rv = DoLoop(result);
   if (rv != ERR_IO_PENDING) {
-    LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKS_CONNECT);
-    load_log_ = NULL;
+    net_log_.EndEvent(NetLog::TYPE_SOCKS_CONNECT, NULL);
     DoCallback(rv);
   }
 }
@@ -219,7 +235,7 @@
 
   next_state_ = STATE_RESOLVE_HOST_COMPLETE;
   return host_resolver_.Resolve(
-      host_request_info_, &addresses_, &io_callback_, load_log_);
+      host_request_info_, &addresses_, &io_callback_, net_log_);
 }
 
 int SOCKSClientSocket::DoResolveHostComplete(int result) {
@@ -306,7 +322,8 @@
   handshake_buf_ = new IOBuffer(handshake_buf_len);
   memcpy(handshake_buf_->data(), &buffer_[bytes_sent_],
          handshake_buf_len);
-  return transport_->Write(handshake_buf_, handshake_buf_len, &io_callback_);
+  return transport_->socket()->Write(handshake_buf_, handshake_buf_len,
+                                     &io_callback_);
 }
 
 int SOCKSClientSocket::DoHandshakeWriteComplete(int result) {
@@ -342,7 +359,8 @@
 
   int handshake_buf_len = kReadHeaderSize - bytes_received_;
   handshake_buf_ = new IOBuffer(handshake_buf_len);
-  return transport_->Read(handshake_buf_, handshake_buf_len, &io_callback_);
+  return transport_->socket()->Read(handshake_buf_, handshake_buf_len,
+                                    &io_callback_);
 }
 
 int SOCKSClientSocket::DoHandshakeReadComplete(int result) {
@@ -355,8 +373,10 @@
   if (result == 0)
     return ERR_CONNECTION_CLOSED;
 
-  if (bytes_received_ + result > kReadHeaderSize)
-    return ERR_INVALID_RESPONSE;
+  if (bytes_received_ + result > kReadHeaderSize) {
+    // TODO(eroman): Describe failure in NetLog.
+    return ERR_SOCKS_CONNECTION_FAILED;
+  }
 
   buffer_.append(handshake_buf_->data(), result);
   bytes_received_ += result;
@@ -370,36 +390,34 @@
 
   if (response->reserved_null != 0x00) {
     LOG(ERROR) << "Unknown response from SOCKS server.";
-    return ERR_INVALID_RESPONSE;
+    return ERR_SOCKS_CONNECTION_FAILED;
   }
 
-  // TODO(arindam): Add SOCKS specific failure codes in net_error_list.h
   switch (response->code) {
     case kServerResponseOk:
       completed_handshake_ = true;
       return OK;
     case kServerResponseRejected:
       LOG(ERROR) << "SOCKS request rejected or failed";
-      return ERR_FAILED;
+      return ERR_SOCKS_CONNECTION_FAILED;
     case kServerResponseNotReachable:
       LOG(ERROR) << "SOCKS request failed because client is not running "
                  << "identd (or not reachable from the server)";
-      return ERR_NAME_NOT_RESOLVED;
+      return ERR_SOCKS_CONNECTION_HOST_UNREACHABLE;
     case kServerResponseMismatchedUserId:
       LOG(ERROR) << "SOCKS request failed because client's identd could "
                  << "not confirm the user ID string in the request";
-      return ERR_FAILED;
+      return ERR_SOCKS_CONNECTION_FAILED;
     default:
       LOG(ERROR) << "SOCKS server sent unknown response";
-      return ERR_INVALID_RESPONSE;
+      return ERR_SOCKS_CONNECTION_FAILED;
   }
 
   // Note: we ignore the last 6 bytes as specified by the SOCKS protocol
 }
 
-int SOCKSClientSocket::GetPeerName(struct sockaddr* name,
-                                   socklen_t* namelen) {
-  return transport_->GetPeerName(name, namelen);
+int SOCKSClientSocket::GetPeerAddress(AddressList* address) const {
+  return transport_->socket()->GetPeerAddress(address);
 }
 
 }  // namespace net
diff --git a/net/socket/socks_client_socket.h b/net/socket/socks_client_socket.h
index dc0b287..f99bff0 100644
--- a/net/socket/socks_client_socket.h
+++ b/net/socket/socks_client_socket.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -15,12 +15,14 @@
 #include "net/base/completion_callback.h"
 #include "net/base/host_resolver.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
 #include "net/socket/client_socket.h"
 #include "testing/gtest/include/gtest/gtest_prod.h"
 
 namespace net {
 
-class LoadLog;
+class ClientSocketHandle;
+class BoundNetLog;
 
 // The SOCKS client socket implementation
 class SOCKSClientSocket : public ClientSocket {
@@ -30,6 +32,11 @@
   //
   // |req_info| contains the hostname and port to which the socket above will
   // communicate to via the socks layer. For testing the referrer is optional.
+  SOCKSClientSocket(ClientSocketHandle* transport_socket,
+                    const HostResolver::RequestInfo& req_info,
+                    HostResolver* host_resolver);
+
+  // Deprecated constructor (http://crbug.com/37810) that takes a ClientSocket.
   SOCKSClientSocket(ClientSocket* transport_socket,
                     const HostResolver::RequestInfo& req_info,
                     HostResolver* host_resolver);
@@ -40,10 +47,11 @@
   // ClientSocket methods:
 
   // Does the SOCKS handshake and completes the protocol.
-  virtual int Connect(CompletionCallback* callback, LoadLog* load_log);
+  virtual int Connect(CompletionCallback* callback);
   virtual void Disconnect();
   virtual bool IsConnected() const;
   virtual bool IsConnectedAndIdle() const;
+  virtual const BoundNetLog& NetLog() const { return net_log_; }
 
   // Socket methods:
   virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
@@ -52,7 +60,7 @@
   virtual bool SetReceiveBufferSize(int32 size);
   virtual bool SetSendBufferSize(int32 size);
 
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen);
+  virtual int GetPeerAddress(AddressList* address) const;
 
  private:
   FRIEND_TEST(SOCKSClientSocketTest, CompleteHandshake);
@@ -95,7 +103,7 @@
   CompletionCallbackImpl<SOCKSClientSocket> io_callback_;
 
   // Stores the underlying socket.
-  scoped_ptr<ClientSocket> transport_;
+  scoped_ptr<ClientSocketHandle> transport_;
 
   State next_state_;
   SocksVersion socks_version_;
@@ -125,7 +133,7 @@
   AddressList addresses_;
   HostResolver::RequestInfo host_request_info_;
 
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
 
   DISALLOW_COPY_AND_ASSIGN(SOCKSClientSocket);
 };
diff --git a/net/socket/socks_client_socket_pool.cc b/net/socket/socks_client_socket_pool.cc
new file mode 100644
index 0000000..d1c75b6
--- /dev/null
+++ b/net/socket/socks_client_socket_pool.cc
@@ -0,0 +1,235 @@
+// Copyright (c) 2010 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/socket/socks_client_socket_pool.h"
+
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_base.h"
+#include "net/socket/socks5_client_socket.h"
+#include "net/socket/socks_client_socket.h"
+
+namespace net {
+
+SOCKSSocketParams::SOCKSSocketParams(
+    const scoped_refptr<TCPSocketParams>& proxy_server,
+    bool socks_v5,
+    const HostPortPair& host_port_pair,
+    RequestPriority priority,
+    const GURL& referrer)
+    : tcp_params_(proxy_server),
+      destination_(host_port_pair.host, host_port_pair.port),
+      socks_v5_(socks_v5) {
+  // The referrer is used by the DNS prefetch system to correlate resolutions
+  // with the page that triggered them. It doesn't impact the actual addresses
+  // that we resolve to.
+  destination_.set_referrer(referrer);
+  destination_.set_priority(priority);
+}
+
+SOCKSSocketParams::~SOCKSSocketParams() {}
+
+// SOCKSConnectJobs will time out after this many seconds.  Note this is on
+// top of the timeout for the transport socket.
+static const int kSOCKSConnectJobTimeoutInSeconds = 30;
+
+SOCKSConnectJob::SOCKSConnectJob(
+    const std::string& group_name,
+    const scoped_refptr<SOCKSSocketParams>& socks_params,
+    const base::TimeDelta& timeout_duration,
+    const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+    const scoped_refptr<HostResolver>& host_resolver,
+    Delegate* delegate,
+    NetLog* net_log)
+    : ConnectJob(group_name, timeout_duration, delegate,
+                 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
+      socks_params_(socks_params),
+      tcp_pool_(tcp_pool),
+      resolver_(host_resolver),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          callback_(this, &SOCKSConnectJob::OnIOComplete)) {
+}
+
+SOCKSConnectJob::~SOCKSConnectJob() {
+  // We don't worry about cancelling the tcp socket since the destructor in
+  // scoped_ptr<ClientSocketHandle> tcp_socket_handle_ will take care of it.
+}
+
+LoadState SOCKSConnectJob::GetLoadState() const {
+  switch (next_state_) {
+    case kStateTCPConnect:
+    case kStateTCPConnectComplete:
+      return tcp_socket_handle_->GetLoadState();
+    case kStateSOCKSConnect:
+    case kStateSOCKSConnectComplete:
+      return LOAD_STATE_CONNECTING;
+    default:
+      NOTREACHED();
+      return LOAD_STATE_IDLE;
+  }
+}
+
+int SOCKSConnectJob::ConnectInternal() {
+  next_state_ = kStateTCPConnect;
+  return DoLoop(OK);
+}
+
+void SOCKSConnectJob::OnIOComplete(int result) {
+  int rv = DoLoop(result);
+  if (rv != ERR_IO_PENDING)
+    NotifyDelegateOfCompletion(rv);  // Deletes |this|
+}
+
+int SOCKSConnectJob::DoLoop(int result) {
+  DCHECK_NE(next_state_, kStateNone);
+
+  int rv = result;
+  do {
+    State state = next_state_;
+    next_state_ = kStateNone;
+    switch (state) {
+      case kStateTCPConnect:
+        DCHECK_EQ(OK, rv);
+        rv = DoTCPConnect();
+        break;
+      case kStateTCPConnectComplete:
+        rv = DoTCPConnectComplete(rv);
+        break;
+      case kStateSOCKSConnect:
+        DCHECK_EQ(OK, rv);
+        rv = DoSOCKSConnect();
+        break;
+      case kStateSOCKSConnectComplete:
+        rv = DoSOCKSConnectComplete(rv);
+        break;
+      default:
+        NOTREACHED() << "bad state";
+        rv = ERR_FAILED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != kStateNone);
+
+  return rv;
+}
+
+int SOCKSConnectJob::DoTCPConnect() {
+  next_state_ = kStateTCPConnectComplete;
+  tcp_socket_handle_.reset(new ClientSocketHandle());
+  return tcp_socket_handle_->Init(group_name(), socks_params_->tcp_params(),
+                                  socks_params_->destination().priority(),
+                                  &callback_, tcp_pool_, net_log());
+}
+
+int SOCKSConnectJob::DoTCPConnectComplete(int result) {
+  if (result != OK)
+    return result;
+
+  // Reset the timer to just the length of time allowed for SOCKS handshake
+  // so that a fast TCP connection plus a slow SOCKS failure doesn't take
+  // longer to timeout than it should.
+  ResetTimer(base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds));
+  next_state_ = kStateSOCKSConnect;
+  return result;
+}
+
+int SOCKSConnectJob::DoSOCKSConnect() {
+  next_state_ = kStateSOCKSConnectComplete;
+
+  // Add a SOCKS connection on top of the tcp socket.
+  if (socks_params_->is_socks_v5()) {
+    socket_.reset(new SOCKS5ClientSocket(tcp_socket_handle_.release(),
+                                         socks_params_->destination()));
+  } else {
+    socket_.reset(new SOCKSClientSocket(tcp_socket_handle_.release(),
+                                        socks_params_->destination(),
+                                        resolver_));
+  }
+  return socket_->Connect(&callback_);
+}
+
+int SOCKSConnectJob::DoSOCKSConnectComplete(int result) {
+  if (result != OK) {
+    socket_->Disconnect();
+    return result;
+  }
+
+  set_socket(socket_.release());
+  return result;
+}
+
+ConnectJob* SOCKSClientSocketPool::SOCKSConnectJobFactory::NewConnectJob(
+    const std::string& group_name,
+    const PoolBase::Request& request,
+    ConnectJob::Delegate* delegate) const {
+  return new SOCKSConnectJob(group_name, request.params(), ConnectionTimeout(),
+                             tcp_pool_, host_resolver_, delegate, net_log_);
+}
+
+base::TimeDelta
+SOCKSClientSocketPool::SOCKSConnectJobFactory::ConnectionTimeout() const {
+  return tcp_pool_->ConnectionTimeout() +
+      base::TimeDelta::FromSeconds(kSOCKSConnectJobTimeoutInSeconds);
+}
+
+SOCKSClientSocketPool::SOCKSClientSocketPool(
+    int max_sockets,
+    int max_sockets_per_group,
+    const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+    const scoped_refptr<HostResolver>& host_resolver,
+    const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+    NetLog* net_log)
+    : base_(max_sockets, max_sockets_per_group, histograms,
+            base::TimeDelta::FromSeconds(
+                ClientSocketPool::unused_idle_socket_timeout()),
+            base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
+            new SOCKSConnectJobFactory(tcp_pool, host_resolver, net_log)) {
+}
+
+SOCKSClientSocketPool::~SOCKSClientSocketPool() {}
+
+int SOCKSClientSocketPool::RequestSocket(const std::string& group_name,
+                                         const void* socket_params,
+                                         RequestPriority priority,
+                                         ClientSocketHandle* handle,
+                                         CompletionCallback* callback,
+                                         const BoundNetLog& net_log) {
+  const scoped_refptr<SOCKSSocketParams>* casted_socket_params =
+      static_cast<const scoped_refptr<SOCKSSocketParams>*>(socket_params);
+
+  return base_.RequestSocket(group_name, *casted_socket_params, priority,
+                             handle, callback, net_log);
+}
+
+void SOCKSClientSocketPool::CancelRequest(const std::string& group_name,
+                                          ClientSocketHandle* handle) {
+  base_.CancelRequest(group_name, handle);
+}
+
+void SOCKSClientSocketPool::ReleaseSocket(const std::string& group_name,
+                                          ClientSocket* socket, int id) {
+  base_.ReleaseSocket(group_name, socket, id);
+}
+
+void SOCKSClientSocketPool::Flush() {
+  base_.Flush();
+}
+
+void SOCKSClientSocketPool::CloseIdleSockets() {
+  base_.CloseIdleSockets();
+}
+
+int SOCKSClientSocketPool::IdleSocketCountInGroup(
+    const std::string& group_name) const {
+  return base_.IdleSocketCountInGroup(group_name);
+}
+
+LoadState SOCKSClientSocketPool::GetLoadState(
+    const std::string& group_name, const ClientSocketHandle* handle) const {
+  return base_.GetLoadState(group_name, handle);
+}
+
+}  // namespace net
diff --git a/net/socket/socks_client_socket_pool.h b/net/socket/socks_client_socket_pool.h
new file mode 100644
index 0000000..4387aa7
--- /dev/null
+++ b/net/socket/socks_client_socket_pool.h
@@ -0,0 +1,193 @@
+// Copyright (c) 2010 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_SOCKET_SOCKS_CLIENT_SOCKET_POOL_H_
+#define NET_SOCKET_SOCKS_CLIENT_SOCKET_POOL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/host_resolver.h"
+#include "net/proxy/proxy_server.h"
+#include "net/socket/client_socket_pool_base.h"
+#include "net/socket/client_socket_pool_histograms.h"
+#include "net/socket/client_socket_pool.h"
+#include "net/socket/tcp_client_socket_pool.h"
+
+namespace net {
+
+class ClientSocketFactory;
+class ConnectJobFactory;
+
+class SOCKSSocketParams : public base::RefCounted<SOCKSSocketParams> {
+ public:
+  SOCKSSocketParams(const scoped_refptr<TCPSocketParams>& proxy_server,
+                    bool socks_v5, const HostPortPair& host_port_pair,
+                    RequestPriority priority, const GURL& referrer);
+
+  const scoped_refptr<TCPSocketParams>& tcp_params() const {
+    return tcp_params_;
+  }
+  const HostResolver::RequestInfo& destination() const { return destination_; }
+  bool is_socks_v5() const { return socks_v5_; }
+
+ private:
+  friend class base::RefCounted<SOCKSSocketParams>;
+  ~SOCKSSocketParams();
+
+  // The tcp connection must point toward the proxy server.
+  const scoped_refptr<TCPSocketParams> tcp_params_;
+  // This is the HTTP destination.
+  HostResolver::RequestInfo destination_;
+  const bool socks_v5_;
+
+  DISALLOW_COPY_AND_ASSIGN(SOCKSSocketParams);
+};
+
+// SOCKSConnectJob handles the handshake to a socks server after setting up
+// an underlying transport socket.
+class SOCKSConnectJob : public ConnectJob {
+ public:
+  SOCKSConnectJob(const std::string& group_name,
+                  const scoped_refptr<SOCKSSocketParams>& params,
+                  const base::TimeDelta& timeout_duration,
+                  const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+                  const scoped_refptr<HostResolver> &host_resolver,
+                  Delegate* delegate,
+                  NetLog* net_log);
+  virtual ~SOCKSConnectJob();
+
+  // ConnectJob methods.
+  virtual LoadState GetLoadState() const;
+
+ private:
+  enum State {
+    kStateTCPConnect,
+    kStateTCPConnectComplete,
+    kStateSOCKSConnect,
+    kStateSOCKSConnectComplete,
+    kStateNone,
+  };
+
+  // Begins the tcp connection and the SOCKS handshake.  Returns OK on success
+  // and ERR_IO_PENDING if it cannot immediately service the request.
+  // Otherwise, it returns a net error code.
+  virtual int ConnectInternal();
+
+  void OnIOComplete(int result);
+
+  // Runs the state transition loop.
+  int DoLoop(int result);
+
+  int DoTCPConnect();
+  int DoTCPConnectComplete(int result);
+  int DoSOCKSConnect();
+  int DoSOCKSConnectComplete(int result);
+
+  scoped_refptr<SOCKSSocketParams> socks_params_;
+  const scoped_refptr<TCPClientSocketPool> tcp_pool_;
+  const scoped_refptr<HostResolver> resolver_;
+
+  State next_state_;
+  CompletionCallbackImpl<SOCKSConnectJob> callback_;
+  scoped_ptr<ClientSocketHandle> tcp_socket_handle_;
+  scoped_ptr<ClientSocket> socket_;
+
+  DISALLOW_COPY_AND_ASSIGN(SOCKSConnectJob);
+};
+
+class SOCKSClientSocketPool : public ClientSocketPool {
+ public:
+  SOCKSClientSocketPool(
+      int max_sockets,
+      int max_sockets_per_group,
+      const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+      const scoped_refptr<HostResolver>& host_resolver,
+      const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+      NetLog* net_log);
+
+  // ClientSocketPool methods:
+  virtual int RequestSocket(const std::string& group_name,
+                            const void* connect_params,
+                            RequestPriority priority,
+                            ClientSocketHandle* handle,
+                            CompletionCallback* callback,
+                            const BoundNetLog& net_log);
+
+  virtual void CancelRequest(const std::string& group_name,
+                             ClientSocketHandle* handle);
+
+  virtual void ReleaseSocket(const std::string& group_name,
+                             ClientSocket* socket,
+                             int id);
+
+  virtual void Flush();
+
+  virtual void CloseIdleSockets();
+
+  virtual int IdleSocketCount() const {
+    return base_.idle_socket_count();
+  }
+
+  virtual int IdleSocketCountInGroup(const std::string& group_name) const;
+
+  virtual LoadState GetLoadState(const std::string& group_name,
+                                 const ClientSocketHandle* handle) const;
+
+  virtual base::TimeDelta ConnectionTimeout() const {
+    return base_.ConnectionTimeout();
+  }
+
+  virtual scoped_refptr<ClientSocketPoolHistograms> histograms() const {
+    return base_.histograms();
+  };
+
+ protected:
+  virtual ~SOCKSClientSocketPool();
+
+ private:
+  typedef ClientSocketPoolBase<SOCKSSocketParams> PoolBase;
+
+  class SOCKSConnectJobFactory : public PoolBase::ConnectJobFactory {
+   public:
+    SOCKSConnectJobFactory(const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+                           HostResolver* host_resolver,
+                           NetLog* net_log)
+        : tcp_pool_(tcp_pool),
+          host_resolver_(host_resolver),
+          net_log_(net_log) {}
+
+    virtual ~SOCKSConnectJobFactory() {}
+
+    // ClientSocketPoolBase::ConnectJobFactory methods.
+    virtual ConnectJob* NewConnectJob(
+        const std::string& group_name,
+        const PoolBase::Request& request,
+        ConnectJob::Delegate* delegate) const;
+
+    virtual base::TimeDelta ConnectionTimeout() const;
+
+   private:
+    const scoped_refptr<TCPClientSocketPool> tcp_pool_;
+    const scoped_refptr<HostResolver> host_resolver_;
+    NetLog* net_log_;
+
+    DISALLOW_COPY_AND_ASSIGN(SOCKSConnectJobFactory);
+  };
+
+  PoolBase base_;
+
+  DISALLOW_COPY_AND_ASSIGN(SOCKSClientSocketPool);
+};
+
+REGISTER_SOCKET_PARAMS_FOR_POOL(SOCKSClientSocketPool, SOCKSSocketParams);
+
+}  // namespace net
+
+#endif  // NET_SOCKET_SOCKS_CLIENT_SOCKET_POOL_H_
diff --git a/net/socket/socks_client_socket_pool_unittest.cc b/net/socket/socks_client_socket_pool_unittest.cc
new file mode 100644
index 0000000..14e2bee
--- /dev/null
+++ b/net/socket/socks_client_socket_pool_unittest.cc
@@ -0,0 +1,261 @@
+// Copyright (c) 2010 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/socket/socks_client_socket_pool.h"
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/time.h"
+#include "net/base/mock_host_resolver.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_histograms.h"
+#include "net/socket/socket_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+const int kMaxSockets = 32;
+const int kMaxSocketsPerGroup = 6;
+
+class SOCKSClientSocketPoolTest : public ClientSocketPoolTest {
+ protected:
+  class SOCKS5MockData {
+   public:
+    explicit SOCKS5MockData(bool async) {
+      writes_.reset(new MockWrite[3]);
+      writes_[0] = MockWrite(async, kSOCKS5GreetRequest,
+                             kSOCKS5GreetRequestLength);
+      writes_[1] = MockWrite(async, kSOCKS5OkRequest, kSOCKS5OkRequestLength);
+      writes_[2] = MockWrite(async, 0);
+
+      reads_.reset(new MockRead[3]);
+      reads_[0] = MockRead(async, kSOCKS5GreetResponse,
+                           kSOCKS5GreetResponseLength);
+      reads_[1] = MockRead(async, kSOCKS5OkResponse, kSOCKS5OkResponseLength);
+      reads_[2] = MockRead(async, 0);
+
+      data_.reset(new StaticSocketDataProvider(reads_.get(), 3,
+                                               writes_.get(), 3));
+    }
+
+    SocketDataProvider* data_provider() { return data_.get(); }
+
+   private:
+    scoped_ptr<StaticSocketDataProvider> data_;
+    scoped_array<MockWrite> writes_;
+    scoped_array<MockWrite> reads_;
+  };
+
+  SOCKSClientSocketPoolTest()
+      : ignored_tcp_socket_params_(new TCPSocketParams(
+            HostPortPair("proxy", 80), MEDIUM, GURL(), false)),
+        tcp_histograms_(new ClientSocketPoolHistograms("MockTCP")),
+        tcp_socket_pool_(new MockTCPClientSocketPool(kMaxSockets,
+            kMaxSocketsPerGroup, tcp_histograms_, &tcp_client_socket_factory_)),
+        ignored_socket_params_(new SOCKSSocketParams(
+            ignored_tcp_socket_params_, true, HostPortPair("host", 80), MEDIUM,
+            GURL())),
+        socks_histograms_(new ClientSocketPoolHistograms("SOCKSUnitTest")),
+        pool_(new SOCKSClientSocketPool(kMaxSockets, kMaxSocketsPerGroup,
+            socks_histograms_, NULL, tcp_socket_pool_, NULL)) {
+  }
+
+  int StartRequest(const std::string& group_name, RequestPriority priority) {
+    return StartRequestUsingPool(
+        pool_, group_name, priority, ignored_socket_params_);
+  }
+
+  scoped_refptr<TCPSocketParams> ignored_tcp_socket_params_;
+  scoped_refptr<ClientSocketPoolHistograms> tcp_histograms_;
+  MockClientSocketFactory tcp_client_socket_factory_;
+  scoped_refptr<MockTCPClientSocketPool> tcp_socket_pool_;
+
+  scoped_refptr<SOCKSSocketParams> ignored_socket_params_;
+  scoped_refptr<ClientSocketPoolHistograms> socks_histograms_;
+  scoped_refptr<SOCKSClientSocketPool> pool_;
+};
+
+TEST_F(SOCKSClientSocketPoolTest, Simple) {
+  SOCKS5MockData data(false);
+  data.data_provider()->set_connect_data(MockConnect(false, 0));
+  tcp_client_socket_factory_.AddSocketDataProvider(data.data_provider());
+
+  ClientSocketHandle handle;
+  int rv = handle.Init("a", ignored_socket_params_, LOW, NULL, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(SOCKSClientSocketPoolTest, Async) {
+  SOCKS5MockData data(true);
+  tcp_client_socket_factory_.AddSocketDataProvider(data.data_provider());
+
+  TestCompletionCallback callback;
+  ClientSocketHandle handle;
+  int rv = handle.Init("a", ignored_socket_params_, LOW, &callback, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(SOCKSClientSocketPoolTest, TCPConnectError) {
+  scoped_ptr<SocketDataProvider> socket_data(new StaticSocketDataProvider());
+  socket_data->set_connect_data(MockConnect(false, ERR_CONNECTION_REFUSED));
+  tcp_client_socket_factory_.AddSocketDataProvider(socket_data.get());
+
+  ClientSocketHandle handle;
+  int rv = handle.Init("a", ignored_socket_params_, LOW, NULL, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(ERR_CONNECTION_REFUSED, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+}
+
+TEST_F(SOCKSClientSocketPoolTest, AsyncTCPConnectError) {
+  scoped_ptr<SocketDataProvider> socket_data(new StaticSocketDataProvider());
+  socket_data->set_connect_data(MockConnect(true, ERR_CONNECTION_REFUSED));
+  tcp_client_socket_factory_.AddSocketDataProvider(socket_data.get());
+
+  TestCompletionCallback callback;
+  ClientSocketHandle handle;
+  int rv = handle.Init("a", ignored_socket_params_, LOW, &callback, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_CONNECTION_REFUSED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+}
+
+TEST_F(SOCKSClientSocketPoolTest, SOCKSConnectError) {
+  MockRead failed_read[] = {
+    MockRead(false, 0),
+  };
+  scoped_ptr<SocketDataProvider> socket_data(new StaticSocketDataProvider(
+        failed_read, arraysize(failed_read), NULL, 0));
+  socket_data->set_connect_data(MockConnect(false, 0));
+  tcp_client_socket_factory_.AddSocketDataProvider(socket_data.get());
+
+  ClientSocketHandle handle;
+  EXPECT_EQ(0, tcp_socket_pool_->release_count());
+  int rv = handle.Init("a", ignored_socket_params_, LOW, NULL, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(ERR_SOCKS_CONNECTION_FAILED, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_EQ(1, tcp_socket_pool_->release_count());
+}
+
+TEST_F(SOCKSClientSocketPoolTest, AsyncSOCKSConnectError) {
+  MockRead failed_read[] = {
+    MockRead(true, 0),
+  };
+  scoped_ptr<SocketDataProvider> socket_data(new StaticSocketDataProvider(
+        failed_read, arraysize(failed_read), NULL, 0));
+  socket_data->set_connect_data(MockConnect(false, 0));
+  tcp_client_socket_factory_.AddSocketDataProvider(socket_data.get());
+
+  TestCompletionCallback callback;
+  ClientSocketHandle handle;
+  EXPECT_EQ(0, tcp_socket_pool_->release_count());
+  int rv = handle.Init("a", ignored_socket_params_, LOW, &callback, pool_,
+                       BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_SOCKS_CONNECTION_FAILED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_EQ(1, tcp_socket_pool_->release_count());
+}
+
+TEST_F(SOCKSClientSocketPoolTest, CancelDuringTCPConnect) {
+  SOCKS5MockData data(false);
+  tcp_client_socket_factory_.AddSocketDataProvider(data.data_provider());
+  // We need two connections because the pool base lets one cancelled
+  // connect job proceed for potential future use.
+  SOCKS5MockData data2(false);
+  tcp_client_socket_factory_.AddSocketDataProvider(data2.data_provider());
+
+  EXPECT_EQ(0, tcp_socket_pool_->cancel_count());
+  int rv = StartRequest("a", LOW);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  rv = StartRequest("a", LOW);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  pool_->CancelRequest("a", requests_[0]->handle());
+  pool_->CancelRequest("a", requests_[1]->handle());
+  // Requests in the connect phase don't actually get cancelled.
+  EXPECT_EQ(0, tcp_socket_pool_->cancel_count());
+
+  // Now wait for the TCP sockets to connect.
+  MessageLoop::current()->RunAllPending();
+
+  EXPECT_EQ(kRequestNotFound, GetOrderOfRequest(1));
+  EXPECT_EQ(kRequestNotFound, GetOrderOfRequest(2));
+  EXPECT_EQ(0, tcp_socket_pool_->cancel_count());
+  EXPECT_EQ(2, pool_->IdleSocketCount());
+
+  requests_[0]->handle()->Reset();
+  requests_[1]->handle()->Reset();
+}
+
+TEST_F(SOCKSClientSocketPoolTest, CancelDuringSOCKSConnect) {
+  SOCKS5MockData data(true);
+  data.data_provider()->set_connect_data(MockConnect(false, 0));
+  tcp_client_socket_factory_.AddSocketDataProvider(data.data_provider());
+  // We need two connections because the pool base lets one cancelled
+  // connect job proceed for potential future use.
+  SOCKS5MockData data2(true);
+  data2.data_provider()->set_connect_data(MockConnect(false, 0));
+  tcp_client_socket_factory_.AddSocketDataProvider(data2.data_provider());
+
+  EXPECT_EQ(0, tcp_socket_pool_->cancel_count());
+  EXPECT_EQ(0, tcp_socket_pool_->release_count());
+  int rv = StartRequest("a", LOW);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  rv = StartRequest("a", LOW);
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  pool_->CancelRequest("a", requests_[0]->handle());
+  pool_->CancelRequest("a", requests_[1]->handle());
+  EXPECT_EQ(0, tcp_socket_pool_->cancel_count());
+  // Requests in the connect phase don't actually get cancelled.
+  EXPECT_EQ(0, tcp_socket_pool_->release_count());
+
+  // Now wait for the async data to reach the SOCKS connect jobs.
+  MessageLoop::current()->RunAllPending();
+
+  EXPECT_EQ(kRequestNotFound, GetOrderOfRequest(1));
+  EXPECT_EQ(kRequestNotFound, GetOrderOfRequest(2));
+  EXPECT_EQ(0, tcp_socket_pool_->cancel_count());
+  EXPECT_EQ(0, tcp_socket_pool_->release_count());
+  EXPECT_EQ(2, pool_->IdleSocketCount());
+
+  requests_[0]->handle()->Reset();
+  requests_[1]->handle()->Reset();
+}
+
+// It would be nice to also test the timeouts in SOCKSClientSocketPool.
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/socket/socks_client_socket_unittest.cc b/net/socket/socks_client_socket_unittest.cc
index dfa078f..aaabc4b 100644
--- a/net/socket/socks_client_socket_unittest.cc
+++ b/net/socket/socks_client_socket_unittest.cc
@@ -1,12 +1,12 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/socket/socks_client_socket.h"
 
 #include "net/base/address_list.h"
-#include "net/base/load_log.h"
-#include "net/base/load_log_unittest.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/mock_host_resolver.h"
 #include "net/base/test_completion_callback.h"
 #include "net/base/winsock_init.h"
@@ -29,9 +29,11 @@
  public:
   SOCKSClientSocketTest();
   // Create a SOCKSClientSocket on top of a MockSocket.
-  SOCKSClientSocket* BuildMockSocket(MockRead reads[], MockWrite writes[],
+  SOCKSClientSocket* BuildMockSocket(MockRead reads[], size_t reads_count,
+                                     MockWrite writes[], size_t writes_count,
                                      HostResolver* host_resolver,
-                                     const std::string& hostname, int port);
+                                     const std::string& hostname, int port,
+                                     NetLog* net_log);
   virtual void SetUp();
 
  protected:
@@ -57,16 +59,20 @@
 
 SOCKSClientSocket* SOCKSClientSocketTest::BuildMockSocket(
     MockRead reads[],
+    size_t reads_count,
     MockWrite writes[],
+    size_t writes_count,
     HostResolver* host_resolver,
     const std::string& hostname,
-    int port) {
+    int port,
+    NetLog* net_log) {
 
   TestCompletionCallback callback;
-  data_.reset(new StaticSocketDataProvider(reads, writes));
-  tcp_sock_ = new MockTCPClientSocket(address_list_, data_.get());
+  data_.reset(new StaticSocketDataProvider(reads, reads_count,
+                                           writes, writes_count));
+  tcp_sock_ = new MockTCPClientSocket(address_list_, net_log, data_.get());
 
-  int rv = tcp_sock_->Connect(&callback, NULL);
+  int rv = tcp_sock_->Connect(&callback);
   EXPECT_EQ(ERR_IO_PENDING, rv);
   rv = callback.WaitForResult();
   EXPECT_EQ(OK, rv);
@@ -88,7 +94,7 @@
                       AddressList* addresses,
                       CompletionCallback* callback,
                       RequestHandle* out_req,
-                      LoadLog* load_log) {
+                      const BoundNetLog& net_log) {
     EXPECT_FALSE(HasOutstandingRequest());
     outstanding_request_ = reinterpret_cast<RequestHandle>(1);
     *out_req = outstanding_request_;
@@ -103,7 +109,6 @@
 
   virtual void AddObserver(Observer* observer) {}
   virtual void RemoveObserver(Observer* observer) {}
-  virtual void Shutdown() {}
 
   bool HasOutstandingRequest() {
     return outstanding_request_ != NULL;
@@ -126,26 +131,28 @@
   MockRead data_reads[] = {
       MockRead(true, kSOCKSOkReply, arraysize(kSOCKSOkReply)),
       MockRead(true, payload_read.data(), payload_read.size()) };
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
 
-  user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
-                                   "localhost", 80));
+  user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                   data_writes, arraysize(data_writes),
+                                   host_resolver_, "localhost", 80, &log));
 
   // At this state the TCP connection is completed but not the SOCKS handshake.
   EXPECT_TRUE(tcp_sock_->IsConnected());
   EXPECT_FALSE(user_sock_->IsConnected());
 
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  int rv = user_sock_->Connect(&callback_, log);
+  int rv = user_sock_->Connect(&callback_);
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_TRUE(
-      LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS_CONNECT));
+      LogContainsBeginEvent(log.entries(), 0, NetLog::TYPE_SOCKS_CONNECT));
   EXPECT_FALSE(user_sock_->IsConnected());
   rv = callback_.WaitForResult();
 
   EXPECT_EQ(OK, rv);
   EXPECT_TRUE(user_sock_->IsConnected());
   EXPECT_EQ(SOCKSClientSocket::kSOCKS4, user_sock_->socks_version_);
-  EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS_CONNECT));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), -1, NetLog::TYPE_SOCKS_CONNECT));
 
   scoped_refptr<IOBuffer> buffer = new IOBuffer(payload_write.size());
   memcpy(buffer->data(), payload_write.data(), payload_write.size());
@@ -176,12 +183,12 @@
     // Failure of the server response code
     {
       { 0x01, 0x5A, 0x00, 0x00, 0, 0, 0, 0 },
-      ERR_INVALID_RESPONSE,
+      ERR_SOCKS_CONNECTION_FAILED,
     },
     // Failure of the null byte
     {
       { 0x00, 0x5B, 0x00, 0x00, 0, 0, 0, 0 },
-      ERR_FAILED,
+      ERR_SOCKS_CONNECTION_FAILED,
     },
   };
 
@@ -192,19 +199,22 @@
         MockWrite(false, kSOCKSOkRequest, arraysize(kSOCKSOkRequest)) };
     MockRead data_reads[] = {
         MockRead(false, tests[i].fail_reply, arraysize(tests[i].fail_reply)) };
+    CapturingNetLog log(CapturingNetLog::kUnbounded);
 
-    user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
-                                     "localhost", 80));
-    scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+    user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                     data_writes, arraysize(data_writes),
+                                     host_resolver_, "localhost", 80, &log));
 
-    int rv = user_sock_->Connect(&callback_, log);
+    int rv = user_sock_->Connect(&callback_);
     EXPECT_EQ(ERR_IO_PENDING, rv);
-    EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS_CONNECT));
+    EXPECT_TRUE(LogContainsBeginEvent(
+        log.entries(), 0, NetLog::TYPE_SOCKS_CONNECT));
     rv = callback_.WaitForResult();
     EXPECT_EQ(tests[i].fail_code, rv);
     EXPECT_FALSE(user_sock_->IsConnected());
     EXPECT_TRUE(tcp_sock_->IsConnected());
-    EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS_CONNECT));
+    EXPECT_TRUE(LogContainsEndEvent(
+        log.entries(), -1, NetLog::TYPE_SOCKS_CONNECT));
   }
 }
 
@@ -219,19 +229,21 @@
   MockRead data_reads[] = {
       MockRead(true, kSOCKSPartialReply1, arraysize(kSOCKSPartialReply1)),
       MockRead(true, kSOCKSPartialReply2, arraysize(kSOCKSPartialReply2)) };
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
 
-  user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
-                                   "localhost", 80));
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+  user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                   data_writes, arraysize(data_writes),
+                                   host_resolver_, "localhost", 80, &log));
 
-  int rv = user_sock_->Connect(&callback_, log);
+  int rv = user_sock_->Connect(&callback_);
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_TRUE(LogContainsBeginEvent(
-      *log, 0, LoadLog::TYPE_SOCKS_CONNECT));
+      log.entries(), 0, NetLog::TYPE_SOCKS_CONNECT));
   rv = callback_.WaitForResult();
   EXPECT_EQ(OK, rv);
   EXPECT_TRUE(user_sock_->IsConnected());
-  EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS_CONNECT));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), -1, NetLog::TYPE_SOCKS_CONNECT));
 }
 
 // Tests scenario when the client sends the handshake request in
@@ -249,18 +261,21 @@
                 arraysize(kSOCKSPartialRequest2)) };
   MockRead data_reads[] = {
       MockRead(true, kSOCKSOkReply, arraysize(kSOCKSOkReply)) };
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
 
-  user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
-                                   "localhost", 80));
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+  user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                   data_writes, arraysize(data_writes),
+                                   host_resolver_, "localhost", 80, &log));
 
-  int rv = user_sock_->Connect(&callback_, log);
+  int rv = user_sock_->Connect(&callback_);
   EXPECT_EQ(ERR_IO_PENDING, rv);
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS_CONNECT));
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 0, NetLog::TYPE_SOCKS_CONNECT));
   rv = callback_.WaitForResult();
   EXPECT_EQ(OK, rv);
   EXPECT_TRUE(user_sock_->IsConnected());
-  EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS_CONNECT));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), -1, NetLog::TYPE_SOCKS_CONNECT));
 }
 
 // Tests the case when the server sends a smaller sized handshake data
@@ -272,18 +287,21 @@
       MockRead(true, kSOCKSOkReply, arraysize(kSOCKSOkReply) - 2),
       // close connection unexpectedly
       MockRead(false, 0) };
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
 
-  user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
-                                   "localhost", 80));
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+  user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                   data_writes, arraysize(data_writes),
+                                   host_resolver_, "localhost", 80, &log));
 
-  int rv = user_sock_->Connect(&callback_, log);
+  int rv = user_sock_->Connect(&callback_);
   EXPECT_EQ(ERR_IO_PENDING, rv);
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS_CONNECT));
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 0, NetLog::TYPE_SOCKS_CONNECT));
   rv = callback_.WaitForResult();
   EXPECT_EQ(ERR_CONNECTION_CLOSED, rv);
   EXPECT_FALSE(user_sock_->IsConnected());
-  EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS_CONNECT));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), -1, NetLog::TYPE_SOCKS_CONNECT));
 }
 
 // Tries to connect to an unknown DNS and on failure should revert to SOCKS4A.
@@ -300,19 +318,22 @@
       MockWrite(false, request.data(), request.size()) };
   MockRead data_reads[] = {
       MockRead(false, kSOCKSOkReply, arraysize(kSOCKSOkReply)) };
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
 
-  user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
-                                   hostname, 80));
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+  user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                   data_writes, arraysize(data_writes),
+                                   host_resolver_, hostname, 80, &log));
 
-  int rv = user_sock_->Connect(&callback_, log);
+  int rv = user_sock_->Connect(&callback_);
   EXPECT_EQ(ERR_IO_PENDING, rv);
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS_CONNECT));
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 0, NetLog::TYPE_SOCKS_CONNECT));
   rv = callback_.WaitForResult();
   EXPECT_EQ(OK, rv);
   EXPECT_TRUE(user_sock_->IsConnected());
   EXPECT_EQ(SOCKSClientSocket::kSOCKS4a, user_sock_->socks_version_);
-  EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS_CONNECT));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), -1, NetLog::TYPE_SOCKS_CONNECT));
 }
 
 // Tries to connect to a domain that resolves to IPv6.
@@ -320,7 +341,8 @@
 TEST_F(SOCKSClientSocketTest, SOCKS4AIfDomainInIPv6) {
   const char hostname[] = "an.ipv6.address";
 
-  host_resolver_->rules()->AddIPv6Rule(hostname, "2001:db8:8714:3a90::12");
+  host_resolver_->rules()->AddIPLiteralRule(hostname,
+                                            "2001:db8:8714:3a90::12", "");
 
   std::string request(kSOCKS4aInitialRequest,
                       arraysize(kSOCKS4aInitialRequest));
@@ -330,19 +352,22 @@
       MockWrite(false, request.data(), request.size()) };
   MockRead data_reads[] = {
       MockRead(false, kSOCKSOkReply, arraysize(kSOCKSOkReply)) };
+  CapturingNetLog log(CapturingNetLog::kUnbounded);
 
-  user_sock_.reset(BuildMockSocket(data_reads, data_writes, host_resolver_,
-                                   hostname, 80));
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
+  user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                   data_writes, arraysize(data_writes),
+                                   host_resolver_, hostname, 80, &log));
 
-  int rv = user_sock_->Connect(&callback_, log);
+  int rv = user_sock_->Connect(&callback_);
   EXPECT_EQ(ERR_IO_PENDING, rv);
-  EXPECT_TRUE(LogContainsBeginEvent(*log, 0, LoadLog::TYPE_SOCKS_CONNECT));
+  EXPECT_TRUE(LogContainsBeginEvent(
+      log.entries(), 0, NetLog::TYPE_SOCKS_CONNECT));
   rv = callback_.WaitForResult();
   EXPECT_EQ(OK, rv);
   EXPECT_TRUE(user_sock_->IsConnected());
   EXPECT_EQ(SOCKSClientSocket::kSOCKS4a, user_sock_->socks_version_);
-  EXPECT_TRUE(LogContainsEndEvent(*log, -1, LoadLog::TYPE_SOCKS_CONNECT));
+  EXPECT_TRUE(LogContainsEndEvent(
+      log.entries(), -1, NetLog::TYPE_SOCKS_CONNECT));
 }
 
 // Calls Disconnect() while a host resolve is in progress. The outstanding host
@@ -355,11 +380,12 @@
   MockWrite data_writes[] = { MockWrite(false, "", 0) };
   MockRead data_reads[] = { MockRead(false, "", 0) };
 
-  user_sock_.reset(BuildMockSocket(data_reads, data_writes, hanging_resolver,
-                                   "foo", 80));
+  user_sock_.reset(BuildMockSocket(data_reads, arraysize(data_reads),
+                                   data_writes, arraysize(data_writes),
+                                   hanging_resolver, "foo", 80, NULL));
 
   // Start connecting (will get stuck waiting for the host to resolve).
-  int rv = user_sock_->Connect(&callback_, NULL);
+  int rv = user_sock_->Connect(&callback_);
   EXPECT_EQ(ERR_IO_PENDING, rv);
 
   EXPECT_FALSE(user_sock_->IsConnected());
diff --git a/net/socket/ssl_client_socket.h b/net/socket/ssl_client_socket.h
index 71184bc..9c34282 100644
--- a/net/socket/ssl_client_socket.h
+++ b/net/socket/ssl_client_socket.h
@@ -5,6 +5,10 @@
 #ifndef NET_SOCKET_SSL_CLIENT_SOCKET_H_
 #define NET_SOCKET_SSL_CLIENT_SOCKET_H_
 
+#include <string>
+
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
 #include "net/socket/client_socket.h"
 
 namespace net {
@@ -20,6 +24,8 @@
 //
 class SSLClientSocket : public ClientSocket {
  public:
+  SSLClientSocket() : was_npn_negotiated_(false) {
+  }
   // Next Protocol Negotiation (NPN) allows a TLS client and server to come to
   // an agreement about the application level protocol to speak over a
   // connection.
@@ -37,7 +43,7 @@
   enum NextProto {
     kProtoUnknown = 0,
     kProtoHTTP11 = 1,
-    kProtoSPDY = 2,
+    kProtoSPDY1 = 2,
   };
 
   // Gets the SSL connection information of the socket.
@@ -58,14 +64,43 @@
   virtual NextProtoStatus GetNextProto(std::string* proto) = 0;
 
   static NextProto NextProtoFromString(const std::string& proto_string) {
-    if (proto_string == "http1.1") {
+    if (proto_string == "http1.1" || proto_string == "http/1.1") {
       return kProtoHTTP11;
-    } else if (proto_string == "spdy") {
-      return kProtoSPDY;
+    } else if (proto_string == "spdy" || proto_string == "spdy/1") {
+      return kProtoSPDY1;
     } else {
       return kProtoUnknown;
     }
   }
+
+  static bool IgnoreCertError(int error, int load_flags) {
+    if (error == OK || load_flags & LOAD_IGNORE_ALL_CERT_ERRORS)
+      return true;
+
+    if (error == ERR_CERT_COMMON_NAME_INVALID &&
+        (load_flags & LOAD_IGNORE_CERT_COMMON_NAME_INVALID))
+      return true;
+    if(error == ERR_CERT_DATE_INVALID &&
+            (load_flags & LOAD_IGNORE_CERT_DATE_INVALID))
+      return true;
+    if(error == ERR_CERT_AUTHORITY_INVALID &&
+            (load_flags & LOAD_IGNORE_CERT_AUTHORITY_INVALID))
+      return true;
+
+    return false;
+  }
+
+  virtual bool wasNpnNegotiated() const {
+    return was_npn_negotiated_;
+  }
+
+  virtual bool setWasNpnNegotiated(bool negotiated) {
+    return was_npn_negotiated_ = negotiated;
+  }
+
+ private:
+  // True if NPN was responded to, independent of selecting SPDY or HTTP.
+  bool was_npn_negotiated_;
 };
 
 }  // namespace net
diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc
index 2fb43d1..c3c7d7a 100644
--- a/net/socket/ssl_client_socket_mac.cc
+++ b/net/socket/ssl_client_socket_mac.cc
@@ -1,19 +1,26 @@
-// Copyright (c) 2008-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/socket/ssl_client_socket_mac.h"
 
 #include <CoreServices/CoreServices.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
 
 #include "base/scoped_cftyperef.h"
 #include "base/singleton.h"
 #include "base/string_util.h"
+#include "net/base/address_list.h"
 #include "net/base/cert_verifier.h"
 #include "net/base/io_buffer.h"
-#include "net/base/load_log.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/ssl_cert_request_info.h"
+#include "net/base/ssl_connection_status_flags.h"
 #include "net/base/ssl_info.h"
+#include "net/socket/client_socket_handle.h"
 
 // Welcome to Mac SSL. We've been waiting for you.
 //
@@ -93,19 +100,28 @@
 
 namespace {
 
+// Pause if we have 2MB of data in flight, resume once we're down below 1MB.
+const unsigned int kWriteSizePauseLimit = 2 * 1024 * 1024;
+const unsigned int kWriteSizeResumeLimit = 1 * 1024 * 1024;
+
+// You can change this to LOG(WARNING) during development.
+#define SSL_LOG LOG(INFO) << "SSL: "
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
 // Declarations needed to call the 10.5.7 and later SSLSetSessionOption()
 // function when building with the 10.5.0 SDK.
 typedef enum {
-  kSSLSessionOptionBreakOnServerAuthFlag
-} SSLSetSessionOptionType;
+  kSSLSessionOptionBreakOnServerAuth,
+  kSSLSessionOptionBreakOnCertRequested,
+} SSLSessionOption;
 
 enum {
-  errSSLServerAuthCompletedFlag = -9841
+  errSSLServerAuthCompleted = -9841,
+  errSSLClientCertRequested = -9842,
 };
 
 // When compiled against the Mac OS X 10.5 SDK, define symbolic constants for
 // cipher suites added in Mac OS X 10.6.
-#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
 enum {
   // ECC cipher suites from RFC 4492.
   TLS_ECDH_ECDSA_WITH_NULL_SHA           = 0xC001,
@@ -137,7 +153,7 @@
 #endif
 
 typedef OSStatus (*SSLSetSessionOptionFuncPtr)(SSLContextRef,
-                                               SSLSetSessionOptionType,
+                                               SSLSessionOption,
                                                Boolean);
 // For an explanation of the Mac OS X error codes, please refer to:
 // http://developer.apple.com/mac/library/documentation/Security/Reference/secureTransportRef/Reference/reference.html
@@ -173,13 +189,18 @@
     case errSSLXCertChainInvalid:
     case errSSLBadCert:
       return ERR_CERT_INVALID;
-    case errSSLPeerCertRevoked:
-      return ERR_CERT_REVOKED;
 
     case errSSLClosedGraceful:
     case noErr:
       return OK;
 
+    case errSSLPeerCertUnknown...errSSLPeerBadCert:
+    case errSSLPeerInsufficientSecurity...errSSLPeerUnknownCA:
+      // (Note that all errSSLPeer* codes indicate errors reported by the
+      // peer, so the cert-related ones refer to my _client_ cert.)
+      LOG(WARNING) << "Server rejected client cert (OSStatus=" << status << ")";
+      return ERR_BAD_SSL_CLIENT_AUTH_CERT;
+
     case errSSLBadRecordMac:
     case errSSLBufferOverflow:
     case errSSLDecryptionFail:
@@ -400,29 +421,31 @@
 
   DCHECK_GT(CFArrayGetCount(certs), 0);
 
-  SecCertificateRef server_cert = static_cast<SecCertificateRef>(
-      const_cast<void*>(CFArrayGetValueAtIndex(certs, 0)));
-  CFRetain(server_cert);
-  X509Certificate *x509_cert = X509Certificate::CreateFromHandle(
-      server_cert, X509Certificate::SOURCE_FROM_NETWORK);
-  if (!x509_cert)
-    return NULL;
-
   // Add each of the intermediate certificates in the server's chain to the
   // server's X509Certificate object. This makes them available to
   // X509Certificate::Verify() for chain building.
-  // TODO(wtc): Since X509Certificate::CreateFromHandle may return a cached
-  // X509Certificate object, we may be adding intermediate CA certificates to
-  // it repeatedly!
+  std::vector<SecCertificateRef> intermediate_ca_certs;
   CFIndex certs_length = CFArrayGetCount(certs);
   for (CFIndex i = 1; i < certs_length; ++i) {
     SecCertificateRef cert_ref = reinterpret_cast<SecCertificateRef>(
         const_cast<void*>(CFArrayGetValueAtIndex(certs, i)));
-    CFRetain(cert_ref);
-    x509_cert->AddIntermediateCertificate(cert_ref);
+    intermediate_ca_certs.push_back(cert_ref);
   }
 
-  return x509_cert;
+  SecCertificateRef server_cert = static_cast<SecCertificateRef>(
+      const_cast<void*>(CFArrayGetValueAtIndex(certs, 0)));
+  return X509Certificate::CreateFromHandle(
+      server_cert, X509Certificate::SOURCE_FROM_NETWORK, intermediate_ca_certs);
+}
+
+// Dynamically look up a pointer to a function exported by a bundle.
+template <typename FNTYPE>
+FNTYPE LookupFunction(CFStringRef bundleName, CFStringRef fnName) {
+  CFBundleRef bundle = CFBundleGetBundleWithIdentifier(bundleName);
+  if (!bundle)
+    return NULL;
+  return reinterpret_cast<FNTYPE>(
+      CFBundleGetFunctionPointerForName(bundle, fnName));
 }
 
 // A class that wraps an array of enabled cipher suites that can be passed to
@@ -475,7 +498,7 @@
 
 //-----------------------------------------------------------------------------
 
-SSLClientSocketMac::SSLClientSocketMac(ClientSocket* transport_socket,
+SSLClientSocketMac::SSLClientSocketMac(ClientSocketHandle* transport_socket,
                                        const std::string& hostname,
                                        const SSLConfig& ssl_config)
     : handshake_io_callback_(this, &SSLClientSocketMac::OnHandshakeIOComplete),
@@ -494,35 +517,35 @@
       next_handshake_state_(STATE_NONE),
       completed_handshake_(false),
       handshake_interrupted_(false),
+      client_cert_requested_(false),
       ssl_context_(NULL),
-      pending_send_error_(OK) {
+      pending_send_error_(OK),
+      net_log_(transport_socket->socket()->NetLog()) {
 }
 
 SSLClientSocketMac::~SSLClientSocketMac() {
   Disconnect();
 }
 
-int SSLClientSocketMac::Connect(CompletionCallback* callback,
-                                LoadLog* load_log) {
+int SSLClientSocketMac::Connect(CompletionCallback* callback) {
   DCHECK(transport_.get());
   DCHECK(next_handshake_state_ == STATE_NONE);
   DCHECK(!user_connect_callback_);
 
-  LoadLog::BeginEvent(load_log, LoadLog::TYPE_SSL_CONNECT);
+  net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT, NULL);
 
   int rv = InitializeSSLContext();
   if (rv != OK) {
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_SSL_CONNECT);
+    net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
     return rv;
   }
 
   next_handshake_state_ = STATE_HANDSHAKE_START;
   rv = DoHandshakeLoop(OK);
   if (rv == ERR_IO_PENDING) {
-    load_log_ = load_log;
     user_connect_callback_ = callback;
   } else {
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_SSL_CONNECT);
+    net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
   }
   return rv;
 }
@@ -534,11 +557,12 @@
     SSLClose(ssl_context_);
     SSLDisposeContext(ssl_context_);
     ssl_context_ = NULL;
+    SSL_LOG << "----- Disposed SSLContext";
   }
 
   // Shut down anything that may call us back.
   verifier_.reset();
-  transport_->Disconnect();
+  transport_->socket()->Disconnect();
 }
 
 bool SSLClientSocketMac::IsConnected() const {
@@ -548,7 +572,7 @@
   // layer (HttpNetworkTransaction) needs to handle a persistent connection
   // closed by the server when we send a request anyway, a false positive in
   // exchange for simpler code is a good trade-off.
-  return completed_handshake_ && transport_->IsConnected();
+  return completed_handshake_ && transport_->socket()->IsConnected();
 }
 
 bool SSLClientSocketMac::IsConnectedAndIdle() const {
@@ -557,13 +581,14 @@
   // Strictly speaking, we should check if we have received the close_notify
   // alert message from the server, and return false in that case.  Although
   // the close_notify alert message means EOF in the SSL layer, it is just
-  // bytes to the transport layer below, so transport_->IsConnectedAndIdle()
-  // returns the desired false when we receive close_notify.
-  return completed_handshake_ && transport_->IsConnectedAndIdle();
+  // bytes to the transport layer below, so
+  // transport_->socket()->IsConnectedAndIdle() returns the desired false
+  // when we receive close_notify.
+  return completed_handshake_ && transport_->socket()->IsConnectedAndIdle();
 }
 
-int SSLClientSocketMac::GetPeerName(struct sockaddr* name, socklen_t* namelen) {
-  return transport_->GetPeerName(name, namelen);
+int SSLClientSocketMac::GetPeerAddress(AddressList* address) const {
+  return transport_->socket()->GetPeerAddress(address);
 }
 
 int SSLClientSocketMac::Read(IOBuffer* buf, int buf_len,
@@ -605,11 +630,11 @@
 }
 
 bool SSLClientSocketMac::SetReceiveBufferSize(int32 size) {
-  return transport_->SetReceiveBufferSize(size);
+  return transport_->socket()->SetReceiveBufferSize(size);
 }
 
 bool SSLClientSocketMac::SetSendBufferSize(int32 size) {
-  return transport_->SetSendBufferSize(size);
+  return transport_->socket()->SetSendBufferSize(size);
 }
 
 void SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) {
@@ -630,11 +655,43 @@
   OSStatus status = SSLGetNegotiatedCipher(ssl_context_, &suite);
   if (!status)
     ssl_info->security_bits = KeySizeOfCipherSuite(suite);
+
+  if (ssl_config_.ssl3_fallback)
+    ssl_info->connection_status |= SSL_CONNECTION_SSL3_FALLBACK;
 }
 
 void SSLClientSocketMac::GetSSLCertRequestInfo(
     SSLCertRequestInfo* cert_request_info) {
-  // TODO(wtc): implement this.
+  // I'm being asked for available client certs (identities).
+  // First, get the cert issuer names allowed by the server.
+  std::vector<CertPrincipal> valid_issuers;
+  CFArrayRef valid_issuer_names = NULL;
+  if (SSLCopyDistinguishedNames(ssl_context_, &valid_issuer_names) == noErr &&
+      valid_issuer_names != NULL) {
+    SSL_LOG << "Server has " << CFArrayGetCount(valid_issuer_names)
+            << " valid issuer names";
+    int n = CFArrayGetCount(valid_issuer_names);
+    for (int i = 0; i < n; i++) {
+      // Parse each name into a CertPrincipal object.
+      CFDataRef issuer = reinterpret_cast<CFDataRef>(
+          CFArrayGetValueAtIndex(valid_issuer_names, i));
+      CertPrincipal p;
+      if (p.ParseDistinguishedName(CFDataGetBytePtr(issuer),
+                                   CFDataGetLength(issuer))) {
+        valid_issuers.push_back(p);
+      }
+    }
+    CFRelease(valid_issuer_names);
+  }
+
+  // Now get the available client certs whose issuers are allowed by the server.
+  cert_request_info->host_and_port = hostname_;
+  cert_request_info->client_certs.clear();
+  X509Certificate::GetSSLClientCertificates(hostname_,
+                                            valid_issuers,
+                                            &cert_request_info->client_certs);
+  SSL_LOG << "Asking user to choose between "
+          << cert_request_info->client_certs.size() << " client certs...";
 }
 
 SSLClientSocket::NextProtoStatus
@@ -643,7 +700,36 @@
   return kNextProtoUnsupported;
 }
 
+OSStatus SSLClientSocketMac::EnableBreakOnAuth(bool enabled) {
+  // SSLSetSessionOption() was introduced in Mac OS X 10.5.7. It allows us
+  // to perform certificate validation during the handshake, which is
+  // required in order to properly enable session resumption.
+  //
+  // With the kSSLSessionOptionBreakOnServerAuth option set, SSLHandshake()
+  // will return errSSLServerAuthCompleted after receiving the server's
+  // Certificate during the handshake. That gives us an opportunity to verify
+  // the server certificate and then re-enter that handshake (assuming the
+  // certificate successfully validated).
+  // If the server also requests a client cert, SSLHandshake() will return
+  // errSSLClientCertRequested in addition to (or in some cases *instead of*)
+  // errSSLServerAuthCompleted.
+  static SSLSetSessionOptionFuncPtr ssl_set_session_options =
+      LookupFunction<SSLSetSessionOptionFuncPtr>(CFSTR("com.apple.security"),
+                                                 CFSTR("SSLSetSessionOption"));
+  if (!ssl_set_session_options)
+    return unimpErr;  // Return this if the API isn't available
+  OSStatus err = ssl_set_session_options(ssl_context_,
+                                         kSSLSessionOptionBreakOnServerAuth,
+                                         enabled);
+  if (err)
+    return err;
+  return ssl_set_session_options(ssl_context_,
+                                 kSSLSessionOptionBreakOnCertRequested,
+                                 enabled);
+}
+
 int SSLClientSocketMac::InitializeSSLContext() {
+  SSL_LOG << "----- InitializeSSLContext";
   OSStatus status = noErr;
 
   status = SSLNewContext(false, &ssl_context_);
@@ -683,65 +769,63 @@
   if (status)
     return NetErrorFromOSStatus(status);
 
+  // Passing the domain name enables the server_name TLS extension (SNI).
+  status = SSLSetPeerDomainName(ssl_context_,
+                                hostname_.data(),
+                                hostname_.length());
+  if (status)
+    return NetErrorFromOSStatus(status);
+
   // Disable certificate verification within Secure Transport; we'll
   // be handling that ourselves.
   status = SSLSetEnableCertVerify(ssl_context_, false);
   if (status)
     return NetErrorFromOSStatus(status);
 
-  // SSLSetSessionOption() was introduced in Mac OS X 10.5.7. It allows us
-  // to perform certificate validation during the handshake, which is
-  // required in order to properly enable session resumption.
-  //
-  // With the kSSLSessionOptionBreakOnServerAuth option set, SSLHandshake()
-  // will return errSSLServerAuthCompleted after receiving the server's
-  // Certificate during the handshake. That gives us an opportunity to verify
-  // the server certificate and then re-enter that handshake (assuming the
-  // certificate successfully validated).
-  //
-  // If SSLSetSessionOption() is not present, we do not enable session
-  // resumption, because in that case we are verifying the server's certificate
-  // after the handshake completes (but before any application data is
-  // exchanged). If we were to enable session resumption in this situation,
-  // the session would be cached before we verified the certificate, leaving
-  // the potential for a session in which the certificate failed to validate
-  // to still be able to be resumed.
-  CFBundleRef bundle =
-      CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
-  if (bundle) {
-    SSLSetSessionOptionFuncPtr ssl_set_session_options =
-        reinterpret_cast<SSLSetSessionOptionFuncPtr>(
-            CFBundleGetFunctionPointerForName(bundle,
-                CFSTR("SSLSetSessionOption")));
-    if (ssl_set_session_options) {
-      status = ssl_set_session_options(ssl_context_,
-                                       kSSLSessionOptionBreakOnServerAuthFlag,
-                                       true);
-      if (status)
-        return NetErrorFromOSStatus(status);
+  if (ssl_config_.send_client_cert) {
+    // Provide the client cert up-front if we have one, even though we'll get
+    // notified later when the server requests it, and set it again; this is
+    // seemingly redundant but works around a problem with SecureTransport
+    // and provides correct behavior on both 10.5 and 10.6:
+    // http://lists.apple.com/archives/apple-cdsa/2010/Feb/msg00058.html
+    // http://code.google.com/p/chromium/issues/detail?id=38905
+    SSL_LOG << "Setting client cert in advance because send_client_cert is set";
+    status = SetClientCert();
+    if (status)
+      return NetErrorFromOSStatus(status);
+  }
 
-      // Concatenate the hostname and peer address to use as the peer ID. To
-      // resume a session, we must connect to the same server on the same port
-      // using the same hostname (i.e., localhost and 127.0.0.1 are considered
-      // different peers, which puts us through certificate validation again
-      // and catches hostname/certificate name mismatches.
-      struct sockaddr_storage addr;
-      socklen_t addr_length = sizeof(struct sockaddr_storage);
-      memset(&addr, 0, sizeof(addr));
-      if (!transport_->GetPeerName(reinterpret_cast<struct sockaddr*>(&addr),
-                                   &addr_length)) {
-        // Assemble the socket hostname and address into a single buffer.
-        std::vector<char> peer_id(hostname_.begin(), hostname_.end());
-        peer_id.insert(peer_id.end(), reinterpret_cast<char*>(&addr),
-                       reinterpret_cast<char*>(&addr) + addr_length);
+  status = EnableBreakOnAuth(true);
+  if (status == noErr) {
+    // Only enable session resumption if break-on-auth is available,
+    // because without break-on-auth we are verifying the server's certificate
+    // after the handshake completes (but before any application data is
+    // exchanged). If we were to enable session resumption in this situation,
+    // the session would be cached before we verified the certificate, leaving
+    // the potential for a session in which the certificate failed to validate
+    // to still be able to be resumed.
 
-        // SSLSetPeerID() treats peer_id as a binary blob, and makes its
-        // own copy.
-        status = SSLSetPeerID(ssl_context_, &peer_id[0], peer_id.size());
-        if (status)
-          return NetErrorFromOSStatus(status);
-      }
-    }
+    // Concatenate the hostname and peer address to use as the peer ID. To
+    // resume a session, we must connect to the same server on the same port
+    // using the same hostname (i.e., localhost and 127.0.0.1 are considered
+    // different peers, which puts us through certificate validation again
+    // and catches hostname/certificate name mismatches.
+    AddressList address;
+    int rv = transport_->socket()->GetPeerAddress(&address);
+    if (rv != OK)
+      return rv;
+    const struct addrinfo* ai = address.head();
+    std::string peer_id(hostname_);
+    peer_id += std::string(reinterpret_cast<char*>(ai->ai_addr),
+                           ai->ai_addrlen);
+
+    // SSLSetPeerID() treats peer_id as a binary blob, and makes its
+    // own copy.
+    status = SSLSetPeerID(ssl_context_, peer_id.data(), peer_id.length());
+    if (status)
+      return NetErrorFromOSStatus(status);
+  } else if (status != unimpErr) {  // it's OK if the API isn't available
+    return NetErrorFromOSStatus(status);
   }
 
   return OK;
@@ -787,8 +871,7 @@
   DCHECK(next_handshake_state_ != STATE_NONE);
   int rv = DoHandshakeLoop(result);
   if (rv != ERR_IO_PENDING) {
-    LoadLog::EndEvent(load_log_, LoadLog::TYPE_SSL_CONNECT);
-    load_log_ = NULL;
+    net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
     DoConnectCallback(rv);
   }
 }
@@ -804,8 +887,7 @@
   if (next_handshake_state_ != STATE_NONE) {
     int rv = DoHandshakeLoop(result);
     if (rv != ERR_IO_PENDING) {
-      LoadLog::EndEvent(load_log_, LoadLog::TYPE_SSL_CONNECT);
-      load_log_ = NULL;
+      net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
       DoConnectCallback(rv);
     }
     return;
@@ -834,8 +916,13 @@
   if (!send_buffer_.empty())
     SSLWriteCallback(this, NULL, NULL);
 
-  // Since SSLWriteCallback() lies to return noErr even if transport_->Write()
-  // returns ERR_IO_PENDING, we don't need to call any callbacks here.
+  // If paused because too much data is in flight, try writing again and make
+  // the promised callback.
+  if (user_write_buf_ && send_buffer_.size() < kWriteSizeResumeLimit) {
+    int rv = DoPayloadWrite();
+    if (rv != ERR_IO_PENDING)
+      DoWriteCallback(rv);
+  }
 }
 
 // This is the main loop driving the state machine. Most calls coming from the
@@ -874,27 +961,48 @@
 
 int SSLClientSocketMac::DoHandshakeStart() {
   OSStatus status = SSLHandshake(ssl_context_);
-  if (status == errSSLWouldBlock)
-    next_handshake_state_ = STATE_HANDSHAKE_START;
 
-  if (status == noErr || status == errSSLServerAuthCompletedFlag) {
-    // TODO(hawk): we verify the certificate chain even on resumed sessions
-    // so that we have the certificate status (valid, expired but overridden
-    // by the user, EV, etc.) available. Eliminate this step once we have
-    // a certificate validation result cache.
-    next_handshake_state_ = STATE_VERIFY_CERT;
-    if (status == errSSLServerAuthCompletedFlag) {
-      // Override errSSLServerAuthCompletedFlag as it's not actually an error,
+  switch (status) {
+    case errSSLWouldBlock:
+      next_handshake_state_ = STATE_HANDSHAKE_START;
+      break;
+
+    case noErr:
+      // TODO(hawk): we verify the certificate chain even on resumed sessions
+      // so that we have the certificate status (valid, expired but overridden
+      // by the user, EV, etc.) available. Eliminate this step once we have
+      // a certificate validation result cache. (Also applies to the
+      // errSSLServerAuthCompleted case below.)
+      SSL_LOG << "Handshake completed (DoHandshakeStart), next verify cert";
+      next_handshake_state_ = STATE_VERIFY_CERT;
+      HandshakeFinished();
+      break;
+
+    case errSSLServerAuthCompleted:
+      // Override errSSLServerAuthCompleted as it's not actually an error,
       // but rather an indication that we're only half way through the
       // handshake.
+      SSL_LOG << "Server auth completed (DoHandshakeStart)";
+      next_handshake_state_ = STATE_VERIFY_CERT;
       handshake_interrupted_ = true;
       status = noErr;
-    }
-  }
+      break;
 
-  if (status == errSSLClosedGraceful) {
-    // The server unexpectedly closed on us.
-    return ERR_SSL_PROTOCOL_ERROR;
+    case errSSLClientCertRequested:
+      SSL_LOG << "Received client cert request in DoHandshakeStart";
+      // If we get this instead of errSSLServerAuthCompleted, the latter is
+      // implicit, and we should begin verification as well.
+      next_handshake_state_ = STATE_VERIFY_CERT;
+      handshake_interrupted_ = true;
+      status = noErr;
+      // We don't want to send a client cert now, because we haven't
+      // verified the server's cert yet. Remember it for later.
+      client_cert_requested_ = true;
+      break;
+
+    case errSSLClosedGraceful:
+      // The server unexpectedly closed on us.
+      return ERR_SSL_PROTOCOL_ERROR;
   }
 
   int net_error = NetErrorFromOSStatus(status);
@@ -912,6 +1020,7 @@
   if (!server_cert_)
     return ERR_UNEXPECTED;
 
+  SSL_LOG << "DoVerifyCert...";
   int flags = 0;
   if (ssl_config_.rev_checking_enabled)
     flags |= X509Certificate::VERIFY_REV_CHECKING_ENABLED;
@@ -927,9 +1036,20 @@
   DCHECK(verifier_.get());
   verifier_.reset();
 
+  SSL_LOG << "...DoVerifyCertComplete (result=" << result << ")";
   if (IsCertificateError(result) && ssl_config_.IsAllowedBadCert(server_cert_))
     result = OK;
 
+  if (result == OK && client_cert_requested_) {
+    if (!ssl_config_.send_client_cert) {
+      // Caller hasn't specified a client cert, so let it know the server's
+      // asking for one, and abort the connection.
+      return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
+    }
+    // (We already called SetClientCert during InitializeSSLContext;
+    // no need to do so again.)
+  }
+
   if (handshake_interrupted_) {
     // With session resumption enabled the full handshake (i.e., the handshake
     // in a non-resumed session) occurs in two steps. Continue on to the second
@@ -939,6 +1059,7 @@
   } else {
     // If the session was resumed or session resumption was disabled, we're
     // done with the handshake.
+    SSL_LOG << "Handshake finished! (DoVerifyCertComplete)";
     completed_handshake_ = true;
     DCHECK(next_handshake_state_ == STATE_NONE);
   }
@@ -946,23 +1067,79 @@
   return result;
 }
 
+int SSLClientSocketMac::SetClientCert() {
+  if (!ssl_config_.send_client_cert || !ssl_config_.client_cert)
+    return noErr;
+
+  scoped_cftyperef<CFArrayRef> cert_refs(
+      ssl_config_.client_cert->CreateClientCertificateChain());
+  SSL_LOG << "SSLSetCertificate(" << CFArrayGetCount(cert_refs) << " certs)";
+  OSStatus result = SSLSetCertificate(ssl_context_, cert_refs);
+  if (result)
+    LOG(ERROR) << "SSLSetCertificate returned OSStatus " << result;
+  return result;
+}
+
 int SSLClientSocketMac::DoHandshakeFinish() {
   OSStatus status = SSLHandshake(ssl_context_);
 
-  if (status == errSSLWouldBlock)
-    next_handshake_state_ = STATE_HANDSHAKE_FINISH;
-
-  if (status == errSSLClosedGraceful)
-    return ERR_SSL_PROTOCOL_ERROR;
-
-  if (status == noErr) {
-    completed_handshake_ = true;
-    DCHECK(next_handshake_state_ == STATE_NONE);
+  switch (status) {
+    case errSSLWouldBlock:
+      next_handshake_state_ = STATE_HANDSHAKE_FINISH;
+      break;
+    case errSSLClientCertRequested:
+      SSL_LOG << "Server requested client cert (DoHandshakeFinish)";
+      if (!ssl_config_.send_client_cert)
+        return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
+      // (We already called SetClientCert during InitializeSSLContext.)
+      status = noErr;
+      next_handshake_state_ = STATE_HANDSHAKE_FINISH;
+      break;
+    case errSSLClosedGraceful:
+      return ERR_SSL_PROTOCOL_ERROR;
+    case errSSLClosedAbort:
+    case errSSLPeerHandshakeFail: {
+      // See if the server aborted due to client cert checking.
+      SSLClientCertificateState client_state;
+      if (SSLGetClientCertificateState(ssl_context_, &client_state) == noErr &&
+          client_state > kSSLClientCertNone) {
+        if (client_state == kSSLClientCertRequested &&
+            !ssl_config_.send_client_cert) {
+          SSL_LOG << "Server requested SSL cert during handshake";
+          return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
+        }
+        LOG(WARNING) << "Server aborted SSL handshake; client_state="
+                     << client_state;
+        return ERR_BAD_SSL_CLIENT_AUTH_CERT;
+      }
+      break;
+    }
+    case noErr:
+      SSL_LOG << "Handshake finished! (DoHandshakeFinish)";
+      HandshakeFinished();
+      completed_handshake_ = true;
+      DCHECK(next_handshake_state_ == STATE_NONE);
+      break;
+    default:
+      break;
   }
 
   return NetErrorFromOSStatus(status);
 }
 
+void SSLClientSocketMac::HandshakeFinished() {
+  // After the handshake's finished, disable breaking on server or client
+  // auth. Otherwise it might be triggered during a subsequent renegotiation,
+  // and SecureTransport doesn't handle that very well (there's usually no way
+  // to proceed without aborting the connection, at least not on 10.5.)
+  SSL_LOG << "HandshakeFinished()";
+  OSStatus status = EnableBreakOnAuth(false);
+  if (status != noErr)
+    SSL_LOG << "EnableBreakOnAuth failed: " << status;
+  // Note- this will actually always return an error, up through OS 10.6.3,
+  // because the option can't be changed after the context opens.
+}
+
 int SSLClientSocketMac::DoPayloadRead() {
   size_t processed = 0;
   OSStatus status = SSLRead(ssl_context_,
@@ -979,17 +1156,39 @@
   if (processed > 0)
     return processed;
 
-  if (status == errSSLClosedNoNotify) {
-    // TODO(wtc): Unless we have received the close_notify alert, we need to
-    // return an error code indicating that the SSL connection ended
-    // uncleanly, a potential truncation attack.  See http://crbug.com/18586.
-    return OK;
-  }
+  switch (status) {
+    case errSSLClosedNoNotify:
+      // TODO(wtc): Unless we have received the close_notify alert, we need to
+      // return an error code indicating that the SSL connection ended
+      // uncleanly, a potential truncation attack.  See http://crbug.com/18586.
+      return OK;
 
-  return NetErrorFromOSStatus(status);
+    case errSSLServerAuthCompleted:
+    case errSSLClientCertRequested:
+      // Server wants to renegotiate, probably to ask for a client cert,
+      // but SecureTransport doesn't support renegotiation so we have to close.
+      if (ssl_config_.send_client_cert) {
+        // We already gave SecureTransport a client cert. At this point there's
+        // nothing we can do; the renegotiation will fail regardless, due to
+        // bugs in Apple's SecureTransport library.
+        SSL_LOG << "Server renegotiating (status=" << status
+                << "), but I've already set a client cert. Fatal error.";
+        return ERR_SSL_PROTOCOL_ERROR;
+      }
+      // Tell my caller the server wants a client cert so it can reconnect.
+      SSL_LOG << "Server renegotiating; assuming it wants a client cert...";
+      return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
+
+    default:
+      return NetErrorFromOSStatus(status);
+  }
 }
 
 int SSLClientSocketMac::DoPayloadWrite() {
+  // Too much data in flight?
+  if (send_buffer_.size() > kWriteSizePauseLimit)
+    return ERR_IO_PENDING;
+
   size_t processed = 0;
   OSStatus status = SSLWrite(ssl_context_,
                              user_write_buf_->data(),
@@ -1024,9 +1223,9 @@
   int rv = 1;  // any old value to spin the loop below
   while (rv > 0 && total_read < *data_length) {
     us->read_io_buf_ = new IOBuffer(*data_length - total_read);
-    rv = us->transport_->Read(us->read_io_buf_,
-                              *data_length - total_read,
-                              &us->transport_read_callback_);
+    rv = us->transport_->socket()->Read(us->read_io_buf_,
+                                        *data_length - total_read,
+                                        &us->transport_read_callback_);
 
     if (rv >= 0) {
       us->recv_buffer_.insert(us->recv_buffer_.end(),
@@ -1086,9 +1285,9 @@
     us->write_io_buf_ = new IOBuffer(us->send_buffer_.size());
     memcpy(us->write_io_buf_->data(), &us->send_buffer_[0],
            us->send_buffer_.size());
-    rv = us->transport_->Write(us->write_io_buf_,
-                               us->send_buffer_.size(),
-                               &us->transport_write_callback_);
+    rv = us->transport_->socket()->Write(us->write_io_buf_,
+                                         us->send_buffer_.size(),
+                                         &us->transport_write_callback_);
     if (rv > 0) {
       us->send_buffer_.erase(us->send_buffer_.begin(),
                              us->send_buffer_.begin() + rv);
diff --git a/net/socket/ssl_client_socket_mac.h b/net/socket/ssl_client_socket_mac.h
index 3176116..dc2ed65 100644
--- a/net/socket/ssl_client_socket_mac.h
+++ b/net/socket/ssl_client_socket_mac.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -13,13 +13,14 @@
 #include "base/scoped_ptr.h"
 #include "net/base/cert_verify_result.h"
 #include "net/base/completion_callback.h"
+#include "net/base/net_log.h"
 #include "net/base/ssl_config_service.h"
 #include "net/socket/ssl_client_socket.h"
 
 namespace net {
 
 class CertVerifier;
-class LoadLog;
+class ClientSocketHandle;
 
 // An SSL client socket implemented with Secure Transport.
 class SSLClientSocketMac : public SSLClientSocket {
@@ -28,7 +29,7 @@
   // The given hostname will be compared with the name(s) in the server's
   // certificate during the SSL handshake. ssl_config specifies the SSL
   // settings.
-  SSLClientSocketMac(ClientSocket* transport_socket,
+  SSLClientSocketMac(ClientSocketHandle* transport_socket,
                      const std::string& hostname,
                      const SSLConfig& ssl_config);
   ~SSLClientSocketMac();
@@ -39,11 +40,12 @@
   virtual NextProtoStatus GetNextProto(std::string* proto);
 
   // ClientSocket methods:
-  virtual int Connect(CompletionCallback* callback, LoadLog* load_log);
+  virtual int Connect(CompletionCallback* callback);
   virtual void Disconnect();
   virtual bool IsConnected() const;
   virtual bool IsConnectedAndIdle() const;
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen);
+  virtual int GetPeerAddress(AddressList* address) const;
+  virtual const BoundNetLog& NetLog() const { return net_log_; }
 
   // Socket methods:
   virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
@@ -55,6 +57,8 @@
   // Initializes the SSLContext.  Returns a net error code.
   int InitializeSSLContext();
 
+  OSStatus EnableBreakOnAuth(bool enabled);
+
   void DoConnectCallback(int result);
   void DoReadCallback(int result);
   void DoWriteCallback(int result);
@@ -70,6 +74,9 @@
   int DoVerifyCert();
   int DoVerifyCertComplete(int result);
   int DoHandshakeFinish();
+  void HandshakeFinished();
+
+  int SetClientCert();
 
   static OSStatus SSLReadCallback(SSLConnectionRef connection,
                                   void* data,
@@ -82,7 +89,7 @@
   CompletionCallbackImpl<SSLClientSocketMac> transport_read_callback_;
   CompletionCallbackImpl<SSLClientSocketMac> transport_write_callback_;
 
-  scoped_ptr<ClientSocket> transport_;
+  scoped_ptr<ClientSocketHandle> transport_;
   std::string hostname_;
   SSLConfig ssl_config_;
 
@@ -113,6 +120,7 @@
 
   bool completed_handshake_;
   bool handshake_interrupted_;
+  bool client_cert_requested_;
   SSLContextRef ssl_context_;
 
   // These buffers hold data retrieved from/sent to the underlying transport
@@ -125,7 +133,7 @@
   scoped_refptr<IOBuffer> read_io_buf_;
   scoped_refptr<IOBuffer> write_io_buf_;
 
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
 };
 
 }  // namespace net
diff --git a/net/socket/ssl_client_socket_mac_factory.cc b/net/socket/ssl_client_socket_mac_factory.cc
new file mode 100644
index 0000000..ec41345
--- /dev/null
+++ b/net/socket/ssl_client_socket_mac_factory.cc
@@ -0,0 +1,18 @@
+// Copyright (c) 2010 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/socket/client_socket_factory.h"
+
+#include "net/socket/ssl_client_socket_mac.h"
+
+namespace net {
+
+SSLClientSocket* SSLClientSocketMacFactory(
+    ClientSocketHandle* transport_socket,
+    const std::string& hostname,
+    const SSLConfig& ssl_config) {
+  return new SSLClientSocketMac(transport_socket, hostname, ssl_config);
+}
+
+}  // namespace net
diff --git a/net/socket/ssl_client_socket_mac_factory.h b/net/socket/ssl_client_socket_mac_factory.h
new file mode 100644
index 0000000..dafc40f
--- /dev/null
+++ b/net/socket/ssl_client_socket_mac_factory.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2010 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_SOCKET_SSL_CLIENT_SOCKET_MAC_FACTORY_H_
+#define NET_SOCKET_SSL_CLIENT_SOCKET_MAC_FACTORY_H_
+
+#include "net/socket/client_socket_factory.h"
+
+namespace net {
+
+// Creates SSLClientSocketMac objects.
+SSLClientSocket* SSLClientSocketMacFactory(
+    ClientSocketHandle* transport_socket,
+    const std::string& hostname,
+    const SSLConfig& ssl_config);
+
+}  // namespace net
+
+#endif  // NET_SOCKET_SSL_CLIENT_SOCKET_MAC_FACTORY_H_
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index 10690ee..0a81a48 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -60,17 +60,22 @@
 #include <pk11pub.h>
 
 #include "base/compiler_specific.h"
+#include "base/histogram.h"
 #include "base/logging.h"
 #include "base/nss_util.h"
 #include "base/singleton.h"
 #include "base/string_util.h"
+#include "net/base/address_list.h"
 #include "net/base/cert_verifier.h"
 #include "net/base/io_buffer.h"
-#include "net/base/load_log.h"
+#include "net/base/net_log.h"
 #include "net/base/net_errors.h"
 #include "net/base/ssl_cert_request_info.h"
+#include "net/base/ssl_connection_status_flags.h"
 #include "net/base/ssl_info.h"
+#include "net/base/sys_addrinfo.h"
 #include "net/ocsp/nss_ocsp.h"
+#include "net/socket/client_socket_handle.h"
 
 static const int kRecvBufferSize = 4096;
 
@@ -111,6 +116,9 @@
     // Use late binding to avoid scary but benign warning
     // "Symbol `SSL_ImplementedCiphers' has different size in shared object,
     //  consider re-linking"
+    // TODO(wtc): Use the new SSL_GetImplementedCiphers and
+    // SSL_GetNumImplementedCiphers functions when we require NSS 3.12.6.
+    // See https://bugzilla.mozilla.org/show_bug.cgi?id=496993.
     const PRUint16* pSSL_ImplementedCiphers = static_cast<const PRUint16*>(
         dlsym(RTLD_DEFAULT, "SSL_ImplementedCiphers"));
     if (pSSL_ImplementedCiphers == NULL) {
@@ -175,11 +183,21 @@
     case PR_ADDRESS_NOT_AVAILABLE_ERROR:
       return ERR_ADDRESS_INVALID;
 
+    case SSL_ERROR_SSL_DISABLED:
+      return ERR_NO_SSL_VERSIONS_ENABLED;
     case SSL_ERROR_NO_CYPHER_OVERLAP:
     case SSL_ERROR_UNSUPPORTED_VERSION:
       return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
     case SSL_ERROR_HANDSHAKE_FAILURE_ALERT:
+    case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT:
+    case SSL_ERROR_ILLEGAL_PARAMETER_ALERT:
       return ERR_SSL_PROTOCOL_ERROR;
+    case SSL_ERROR_DECOMPRESSION_FAILURE_ALERT:
+      return ERR_SSL_DECOMPRESSION_FAILURE_ALERT;
+    case SSL_ERROR_BAD_MAC_ALERT:
+      return ERR_SSL_BAD_RECORD_MAC_ALERT;
+    case SSL_ERROR_UNSAFE_NEGOTIATION:
+      return ERR_SSL_UNSAFE_NEGOTIATION;
 
     default: {
       if (IS_SSL_ERROR(err)) {
@@ -211,6 +229,50 @@
   }
 }
 
+#if defined(OS_WIN)
+
+// A certificate for COMODO EV SGC CA, issued by AddTrust External CA Root,
+// causes CertGetCertificateChain to report CERT_TRUST_IS_NOT_VALID_FOR_USAGE.
+// It seems to be caused by the szOID_APPLICATION_CERT_POLICIES extension in
+// that certificate.
+//
+// This function is used in the workaround for http://crbug.com/43538
+bool IsProblematicComodoEVCACert(const CERTCertificate& cert) {
+  // Issuer:
+  // CN = AddTrust External CA Root
+  // OU = AddTrust External TTP Network
+  // O = AddTrust AB
+  // C = SE
+  static const uint8 kIssuer[] = {
+    0x30, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+    0x06, 0x13, 0x02, 0x53, 0x45, 0x31, 0x14, 0x30, 0x12, 0x06,
+    0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b, 0x41, 0x64, 0x64, 0x54,
+    0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31, 0x26, 0x30,
+    0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+    0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74,
+    0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20,
+    0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30,
+    0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x41, 0x64,
+    0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74,
+    0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+    0x6f, 0x6f, 0x74
+  };
+
+  // Serial number: 79:0A:83:4D:48:40:6B:AB:6C:35:2A:D5:1F:42:83:FE.
+  static const uint8 kSerialNumber[] = {
+    0x79, 0x0a, 0x83, 0x4d, 0x48, 0x40, 0x6b, 0xab, 0x6c, 0x35,
+    0x2a, 0xd5, 0x1f, 0x42, 0x83, 0xfe
+  };
+
+  return cert.derIssuer.len == sizeof(kIssuer) &&
+         memcmp(cert.derIssuer.data, kIssuer, cert.derIssuer.len) == 0 &&
+         cert.serialNumber.len == sizeof(kSerialNumber) &&
+         memcmp(cert.serialNumber.data, kSerialNumber,
+                cert.serialNumber.len) == 0;
+}
+
+#endif
+
 }  // namespace
 
 #if defined(OS_WIN)
@@ -218,7 +280,7 @@
 HCERTSTORE SSLClientSocketNSS::cert_store_ = NULL;
 #endif
 
-SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket,
+SSLClientSocketNSS::SSLClientSocketNSS(ClientSocketHandle* transport_socket,
                                        const std::string& hostname,
                                        const SSLConfig& ssl_config)
     : ALLOW_THIS_IN_INITIALIZER_LIST(buffer_send_callback_(
@@ -239,10 +301,12 @@
       user_write_buf_len_(0),
       server_cert_nss_(NULL),
       client_auth_cert_needed_(false),
+      handshake_callback_called_(false),
       completed_handshake_(false),
       next_handshake_state_(STATE_NONE),
       nss_fd_(NULL),
-      nss_bufs_(NULL) {
+      nss_bufs_(NULL),
+      net_log_(transport_socket->socket()->NetLog()) {
   EnterFunction("");
 }
 
@@ -257,7 +321,9 @@
   // Initialize the NSS SSL library in a threadsafe way.  This also
   // initializes the NSS base library.
   EnsureNSSSSLInit();
-#if !defined(OS_WIN)
+  if (!NSS_IsInitialized())
+    return ERR_UNEXPECTED;
+#if !defined(OS_MACOSX) && !defined(OS_WIN)
   // We must call EnsureOCSPInit() here, on the IO thread, to get the IO loop
   // by MessageLoopForIO::current().
   // X509Certificate::Verify() runs on a worker thread of CertVerifier.
@@ -268,8 +334,7 @@
   return OK;
 }
 
-int SSLClientSocketNSS::Connect(CompletionCallback* callback,
-                                LoadLog* load_log) {
+int SSLClientSocketNSS::Connect(CompletionCallback* callback) {
   EnterFunction("");
   DCHECK(transport_.get());
   DCHECK(next_handshake_state_ == STATE_NONE);
@@ -279,15 +344,17 @@
   DCHECK(!user_read_buf_);
   DCHECK(!user_write_buf_);
 
-  LoadLog::BeginEvent(load_log, LoadLog::TYPE_SSL_CONNECT);
+  net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT, NULL);
 
-  if (Init() != OK) {
-    NOTREACHED() << "Couldn't initialize nss";
+  int rv = Init();
+  if (rv != OK) {
+    net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
+    return rv;
   }
 
-  int rv = InitializeSSLOptions();
+  rv = InitializeSSLOptions();
   if (rv != OK) {
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_SSL_CONNECT);
+    net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
     return rv;
   }
 
@@ -295,9 +362,8 @@
   rv = DoHandshakeLoop(OK);
   if (rv == ERR_IO_PENDING) {
     user_connect_callback_ = callback;
-    load_log_ = load_log;
   } else {
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_SSL_CONNECT);
+    net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
   }
 
   LeaveFunction("");
@@ -313,14 +379,25 @@
   }
 
   // Tell NSS who we're connected to
+  AddressList peer_address;
+  int err = transport_->socket()->GetPeerAddress(&peer_address);
+  if (err != OK)
+    return err;
+
+  const struct addrinfo* ai = peer_address.head();
+
   PRNetAddr peername;
-  socklen_t len = sizeof(PRNetAddr);
-  int err = transport_->GetPeerName((struct sockaddr *)&peername, &len);
-  if (err) {
-    DLOG(ERROR) << "GetPeerName failed";
-    // TODO(wtc): Change GetPeerName to return a network error code.
-    return ERR_UNEXPECTED;
-  }
+  memset(&peername, 0, sizeof(peername));
+  DCHECK_LE(ai->ai_addrlen, sizeof(peername));
+  size_t len = std::min(static_cast<size_t>(ai->ai_addrlen), sizeof(peername));
+  memcpy(&peername, ai->ai_addr, len);
+
+  // Adjust the address family field for BSD, whose sockaddr
+  // structure has a one-byte length and one-byte address family
+  // field at the beginning.  PRNetAddr has a two-byte address
+  // family field at the beginning.
+  peername.raw.family = ai->ai_addr->sa_family;
+
   memio_SetPeerName(nss_fd_, &peername);
 
   // Grab pointer to buffers
@@ -381,20 +458,31 @@
      LOG(INFO) << "SSL_ENABLE_DEFLATE failed.  Old system nss?";
 #endif
 
+#ifdef SSL_ENABLE_FALSE_START
+  rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_FALSE_START, PR_TRUE);
+  if (rv != SECSuccess)
+     LOG(INFO) << "SSL_ENABLE_FALSE_START failed.  Old system nss?";
+#endif
+
 #ifdef SSL_ENABLE_RENEGOTIATION
-  // We allow servers to request renegotiation. Since we're a client,
-  // prohibiting this is rather a waste of time. Only servers are in a position
-  // to prevent renegotiation attacks.
-  // http://extendedsubset.com/?p=8
-  //
-  // This should be changed when NSS 3.12.6 comes out with support for the
-  // renegotiation info extension.
-  // http://code.google.com/p/chromium/issues/detail?id=31647
-  rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_RENEGOTIATION,
-                     SSL_RENEGOTIATE_UNRESTRICTED);
+  if (SSLConfigService::IsKnownStrictTLSServer(hostname_)) {
+    rv = SSL_OptionSet(nss_fd_, SSL_REQUIRE_SAFE_NEGOTIATION, PR_TRUE);
+    if (rv != SECSuccess)
+       LOG(INFO) << "SSL_REQUIRE_SAFE_NEGOTIATION failed.";
+    rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_RENEGOTIATION,
+                       SSL_RENEGOTIATE_REQUIRES_XTN);
+  } else {
+    // We allow servers to request renegotiation. Since we're a client,
+    // prohibiting this is rather a waste of time. Only servers are in a
+    // position to prevent renegotiation attacks.
+    // http://extendedsubset.com/?p=8
+
+    rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_RENEGOTIATION,
+                       SSL_RENEGOTIATE_UNRESTRICTED);
+  }
   if (rv != SECSuccess)
      LOG(INFO) << "SSL_ENABLE_RENEGOTIATION failed.";
-#endif
+#endif  // SSL_ENABLE_RENEGOTIATION
 
 #ifdef SSL_NEXT_PROTO_NEGOTIATED
   if (!ssl_config_.next_protos.empty()) {
@@ -429,9 +517,10 @@
   // Set the peer ID for session reuse.  This is necessary when we create an
   // SSL tunnel through a proxy -- GetPeerName returns the proxy's address
   // rather than the destination server's address in that case.
-  // TODO(wtc): port in peername is not the server's port when a proxy is used.
+  // TODO(wtc): port in |peer_address| is not the server's port when a proxy is
+  // used.
   std::string peer_id = StringPrintf("%s:%d", hostname_.c_str(),
-                                     PR_ntohs(PR_NetAddrInetPort(&peername)));
+                                     peer_address.GetPort());
   rv = SSL_SetSockPeerID(nss_fd_, const_cast<char*>(peer_id.c_str()));
   if (rv != SECSuccess)
     LOG(INFO) << "SSL_SetSockPeerID failed: peer_id=" << peer_id;
@@ -462,7 +551,7 @@
   // Shut down anything that may call us back (through buffer_send_callback_,
   // buffer_recv_callback, or handshake_io_callback_).
   verifier_.reset();
-  transport_->Disconnect();
+  transport_->socket()->Disconnect();
 
   // Reset object state
   transport_send_busy_   = false;
@@ -496,7 +585,7 @@
   // closed by the server when we send a request anyway, a false positive in
   // exchange for simpler code is a good trade-off.
   EnterFunction("");
-  bool ret = completed_handshake_ && transport_->IsConnected();
+  bool ret = completed_handshake_ && transport_->socket()->IsConnected();
   LeaveFunction("");
   return ret;
 }
@@ -507,16 +596,17 @@
   // Strictly speaking, we should check if we have received the close_notify
   // alert message from the server, and return false in that case.  Although
   // the close_notify alert message means EOF in the SSL layer, it is just
-  // bytes to the transport layer below, so transport_->IsConnectedAndIdle()
-  // returns the desired false when we receive close_notify.
+  // bytes to the transport layer below, so
+  // transport_->socket()->IsConnectedAndIdle() returns the desired false
+  // when we receive close_notify.
   EnterFunction("");
-  bool ret = completed_handshake_ && transport_->IsConnectedAndIdle();
+  bool ret = completed_handshake_ && transport_->socket()->IsConnectedAndIdle();
   LeaveFunction("");
   return ret;
 }
 
-int SSLClientSocketNSS::GetPeerName(struct sockaddr* name, socklen_t* namelen) {
-  return transport_->GetPeerName(name, namelen);
+int SSLClientSocketNSS::GetPeerAddress(AddressList* address) const {
+  return transport_->socket()->GetPeerAddress(address);
 }
 
 int SSLClientSocketNSS::Read(IOBuffer* buf, int buf_len,
@@ -534,9 +624,9 @@
 
   int rv = DoReadLoop(OK);
 
-  if (rv == ERR_IO_PENDING)
+  if (rv == ERR_IO_PENDING) {
     user_read_callback_ = callback;
-  else {
+  } else {
     user_read_buf_ = NULL;
     user_read_buf_len_ = 0;
   }
@@ -559,9 +649,9 @@
 
   int rv = DoWriteLoop(OK);
 
-  if (rv == ERR_IO_PENDING)
+  if (rv == ERR_IO_PENDING) {
     user_write_callback_ = callback;
-  else {
+  } else {
     user_write_buf_ = NULL;
     user_write_buf_len_ = 0;
   }
@@ -570,13 +660,36 @@
 }
 
 bool SSLClientSocketNSS::SetReceiveBufferSize(int32 size) {
-  return transport_->SetReceiveBufferSize(size);
+  return transport_->socket()->SetReceiveBufferSize(size);
 }
 
 bool SSLClientSocketNSS::SetSendBufferSize(int32 size) {
-  return transport_->SetSendBufferSize(size);
+  return transport_->socket()->SetSendBufferSize(size);
 }
 
+#if defined(OS_WIN)
+// static
+X509Certificate::OSCertHandle SSLClientSocketNSS::CreateOSCert(
+    const SECItem& der_cert) {
+  // TODO(wtc): close cert_store_ at shutdown.
+  if (!cert_store_)
+    cert_store_ = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL);
+
+  X509Certificate::OSCertHandle cert_handle = NULL;
+  BOOL ok = CertAddEncodedCertificateToStore(
+      cert_store_, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+      der_cert.data, der_cert.len, CERT_STORE_ADD_USE_EXISTING, &cert_handle);
+  return ok ? cert_handle : NULL;
+}
+#elif defined(OS_MACOSX)
+// static
+X509Certificate::OSCertHandle SSLClientSocketNSS::CreateOSCert(
+    const SECItem& der_cert) {
+  return X509Certificate::CreateOSCertHandleFromBytes(
+      reinterpret_cast<char*>(der_cert.data), der_cert.len);
+}
+#endif
+
 X509Certificate *SSLClientSocketNSS::UpdateServerCert() {
   // We set the server_cert_ from OwnAuthCertHandler(), but this handler
   // does not necessarily get called if we are continuing a cached SSL
@@ -584,59 +697,74 @@
   if (server_cert_ == NULL) {
     server_cert_nss_ = SSL_PeerCertificate(nss_fd_);
     if (server_cert_nss_) {
-#if defined(OS_WIN)
-      // TODO(wtc): close cert_store_ at shutdown.
-      if (!cert_store_)
-        cert_store_ = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL);
-
-      PCCERT_CONTEXT cert_context = NULL;
-      BOOL ok = CertAddEncodedCertificateToStore(
-          cert_store_,
-          X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
-          server_cert_nss_->derCert.data,
-          server_cert_nss_->derCert.len,
-          CERT_STORE_ADD_USE_EXISTING,
-          &cert_context);
-      DCHECK(ok);
-      server_cert_ = X509Certificate::CreateFromHandle(
-          cert_context, X509Certificate::SOURCE_FROM_NETWORK);
-
-      // Add each of the intermediate certificates in the server's chain to
-      // the server's X509Certificate object. This makes them available to
-      // X509Certificate::Verify() for chain building.
-      // TODO(wtc): Since X509Certificate::CreateFromHandle may return a
-      // cached X509Certificate object, we may be adding intermediate CA
-      // certificates to it repeatedly!
+#if defined(OS_MACOSX) || defined(OS_WIN)
+      // Get each of the intermediate certificates in the server's chain.
+      // These will be added to the server's X509Certificate object, making
+      // them available to X509Certificate::Verify() for chain building.
+      X509Certificate::OSCertHandles intermediate_ca_certs;
+      X509Certificate::OSCertHandle cert_handle = NULL;
       CERTCertList* cert_list = CERT_GetCertChainFromCert(
           server_cert_nss_, PR_Now(), certUsageSSLCA);
       if (cert_list) {
         for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
              !CERT_LIST_END(node, cert_list);
              node = CERT_LIST_NEXT(node)) {
-          cert_context = NULL;
-          ok = CertAddEncodedCertificateToStore(
-              cert_store_,
-              X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
-              node->cert->derCert.data,
-              node->cert->derCert.len,
-              CERT_STORE_ADD_USE_EXISTING,
-              &cert_context);
-          DCHECK(ok);
-          if (node->cert != server_cert_nss_)
-            server_cert_->AddIntermediateCertificate(cert_context);
+          if (node->cert == server_cert_nss_)
+            continue;
+#if defined(OS_WIN)
+          // Work around http://crbug.com/43538 by not importing the
+          // problematic COMODO EV SGC CA certificate.  CryptoAPI will
+          // download a good certificate for that CA, issued by COMODO
+          // Certification Authority, using the AIA extension in the server
+          // certificate.
+          if (IsProblematicComodoEVCACert(*node->cert))
+            continue;
+#endif
+          cert_handle = CreateOSCert(node->cert->derCert);
+          DCHECK(cert_handle);
+          intermediate_ca_certs.push_back(cert_handle);
         }
         CERT_DestroyCertList(cert_list);
       }
+
+      // Finally create the X509Certificate object.
+      cert_handle = CreateOSCert(server_cert_nss_->derCert);
+      DCHECK(cert_handle);
+      server_cert_ = X509Certificate::CreateFromHandle(
+          cert_handle,
+          X509Certificate::SOURCE_FROM_NETWORK,
+          intermediate_ca_certs);
+      X509Certificate::FreeOSCertHandle(cert_handle);
+      for (size_t i = 0; i < intermediate_ca_certs.size(); ++i)
+        X509Certificate::FreeOSCertHandle(intermediate_ca_certs[i]);
 #else
       server_cert_ = X509Certificate::CreateFromHandle(
-          CERT_DupCertificate(server_cert_nss_),
-          X509Certificate::SOURCE_FROM_NETWORK);
+          server_cert_nss_,
+          X509Certificate::SOURCE_FROM_NETWORK,
+          X509Certificate::OSCertHandles());
 #endif
     }
   }
   return server_cert_;
 }
 
+// Log an informational message if the server does not support secure
+// renegotiation (RFC 5746).
+void SSLClientSocketNSS::CheckSecureRenegotiation() const {
+  // SSL_HandshakeNegotiatedExtension was added in NSS 3.12.6.
+  // Since SSL_MAX_EXTENSIONS was added at the same time, we can test
+  // SSL_MAX_EXTENSIONS for the presence of SSL_HandshakeNegotiatedExtension.
+#if defined(SSL_MAX_EXTENSIONS)
+  PRBool received_renego_info;
+  if (SSL_HandshakeNegotiatedExtension(nss_fd_, ssl_renegotiation_info_xtn,
+                                       &received_renego_info) == SECSuccess &&
+      !received_renego_info) {
+    LOG(INFO) << "The server " << hostname_
+              << " does not support the TLS renegotiation_info extension.";
+  }
+#endif
+}
+
 void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
   EnterFunction("");
   ssl_info->Reset();
@@ -659,19 +787,41 @@
       LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError()
                   << " for cipherSuite " << channel_info.cipherSuite;
     }
+    ssl_info->connection_status |=
+        (((int)channel_info.cipherSuite) & SSL_CONNECTION_CIPHERSUITE_MASK) <<
+        SSL_CONNECTION_CIPHERSUITE_SHIFT;
+
+    ssl_info->connection_status |=
+        (((int)channel_info.compressionMethod) &
+         SSL_CONNECTION_COMPRESSION_MASK) <<
+        SSL_CONNECTION_COMPRESSION_SHIFT;
+
     UpdateServerCert();
   }
   ssl_info->cert_status = server_cert_verify_result_.cert_status;
   DCHECK(server_cert_ != NULL);
   ssl_info->cert = server_cert_;
 
+  PRBool peer_supports_renego_ext;
+  ok = SSL_HandshakeNegotiatedExtension(nss_fd_, ssl_renegotiation_info_xtn,
+                                        &peer_supports_renego_ext);
+  if (ok == SECSuccess) {
+    if (!peer_supports_renego_ext)
+      ssl_info->connection_status |= SSL_CONNECTION_NO_RENEGOTIATION_EXTENSION;
+    UMA_HISTOGRAM_ENUMERATION("Net.RenegotiationExtensionSupported",
+                              (int)peer_supports_renego_ext, 2);
+  }
+
+  if (ssl_config_.ssl3_fallback)
+    ssl_info->connection_status |= SSL_CONNECTION_SSL3_FALLBACK;
+
   LeaveFunction("");
 }
 
 void SSLClientSocketNSS::GetSSLCertRequestInfo(
     SSLCertRequestInfo* cert_request_info) {
   EnterFunction("");
-  cert_request_info->host_and_port = hostname_;
+  cert_request_info->host_and_port = hostname_;  // TODO(wtc): no port!
   cert_request_info->client_certs = client_certs_;
   LeaveFunction(cert_request_info->client_certs.size());
 }
@@ -764,8 +914,7 @@
   EnterFunction(result);
   int rv = DoHandshakeLoop(result);
   if (rv != ERR_IO_PENDING) {
-    LoadLog::EndEvent(load_log_, net::LoadLog::TYPE_SSL_CONNECT);
-    load_log_ = NULL;
+    net_log_.EndEvent(net::NetLog::TYPE_SSL_CONNECT, NULL);
     DoConnectCallback(rv);
   }
   LeaveFunction("");
@@ -773,7 +922,7 @@
 
 void SSLClientSocketNSS::OnSendComplete(int result) {
   EnterFunction(result);
-  if (next_handshake_state_ != STATE_NONE) {
+  if (next_handshake_state_ == STATE_HANDSHAKE) {
     // In handshake phase.
     OnHandshakeIOComplete(result);
     LeaveFunction("");
@@ -805,7 +954,7 @@
 
 void SSLClientSocketNSS::OnRecvComplete(int result) {
   EnterFunction(result);
-  if (next_handshake_state_ != STATE_NONE) {
+  if (next_handshake_state_ == STATE_HANDSHAKE) {
     // In handshake phase.
     OnHandshakeIOComplete(result);
     LeaveFunction("");
@@ -853,6 +1002,8 @@
       return PR_HOST_UNREACHABLE_ERROR;  // Also PR_NETWORK_UNREACHABLE_ERROR.
     case ERR_ADDRESS_INVALID:
       return PR_ADDRESS_NOT_AVAILABLE_ERROR;
+    case ERR_NAME_NOT_RESOLVED:
+      return PR_DIRECTORY_LOOKUP_ERROR;
     default:
       LOG(WARNING) << "MapErrorToNSS " << result
                    << " mapped to PR_UNKNOWN_ERROR";
@@ -892,7 +1043,8 @@
 
     scoped_refptr<IOBuffer> send_buffer = new IOBuffer(nb);
     memcpy(send_buffer->data(), buf, nb);
-    int rv = transport_->Write(send_buffer, nb, &buffer_send_callback_);
+    int rv = transport_->socket()->Write(send_buffer, nb,
+                                         &buffer_send_callback_);
     if (rv == ERR_IO_PENDING) {
       transport_send_busy_ = true;
       break;
@@ -932,7 +1084,7 @@
     rv = ERR_IO_PENDING;
   } else {
     recv_buffer_ = new IOBuffer(nb);
-    rv = transport_->Read(recv_buffer_, nb, &buffer_recv_callback_);
+    rv = transport_->socket()->Read(recv_buffer_, nb, &buffer_recv_callback_);
     if (rv == ERR_IO_PENDING) {
       transport_recv_busy_ = true;
     } else {
@@ -1067,15 +1219,122 @@
     CERTDistNames* ca_names,
     CERTCertificate** result_certificate,
     SECKEYPrivateKey** result_private_key) {
-#if defined(OS_WIN)
-  // Not implemented.  Send no client certificate.
-  PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
-  return SECFailure;
-#else
   SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg);
 
   that->client_auth_cert_needed_ = !that->ssl_config_.send_client_cert;
 
+#if defined(OS_WIN)
+  if (that->ssl_config_.send_client_cert) {
+    // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using
+    // CryptoAPI yet (http://crbug.com/37560), so client_cert must be NULL.
+    DCHECK(!that->ssl_config_.client_cert);
+    // Send no client certificate.
+    return SECFailure;
+  }
+
+  that->client_certs_.clear();
+
+  std::vector<CERT_NAME_BLOB> issuer_list(ca_names->nnames);
+  for (int i = 0; i < ca_names->nnames; ++i) {
+    issuer_list[i].cbData = ca_names->names[i].len;
+    issuer_list[i].pbData = ca_names->names[i].data;
+  }
+
+  // Client certificates of the user are in the "MY" system certificate store.
+  HCERTSTORE my_cert_store = CertOpenSystemStore(NULL, L"MY");
+  if (!my_cert_store) {
+    LOG(ERROR) << "Could not open the \"MY\" system certificate store: "
+               << GetLastError();
+    return SECFailure;
+  }
+
+  // Enumerate the client certificates.
+  CERT_CHAIN_FIND_BY_ISSUER_PARA find_by_issuer_para;
+  memset(&find_by_issuer_para, 0, sizeof(find_by_issuer_para));
+  find_by_issuer_para.cbSize = sizeof(find_by_issuer_para);
+  find_by_issuer_para.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
+  find_by_issuer_para.cIssuer = ca_names->nnames;
+  find_by_issuer_para.rgIssuer = ca_names->nnames ? &issuer_list[0] : NULL;
+
+  PCCERT_CHAIN_CONTEXT chain_context = NULL;
+
+  // TODO(wtc): close cert_store_ at shutdown.
+  if (!cert_store_)
+    cert_store_ = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL);
+
+  for (;;) {
+    // Find a certificate chain.
+    chain_context = CertFindChainInStore(my_cert_store,
+                                         X509_ASN_ENCODING,
+                                         0,
+                                         CERT_CHAIN_FIND_BY_ISSUER,
+                                         &find_by_issuer_para,
+                                         chain_context);
+    if (!chain_context) {
+      DWORD err = GetLastError();
+      if (err != CRYPT_E_NOT_FOUND)
+        DLOG(ERROR) << "CertFindChainInStore failed: " << err;
+      break;
+    }
+
+    // Get the leaf certificate.
+    PCCERT_CONTEXT cert_context =
+        chain_context->rgpChain[0]->rgpElement[0]->pCertContext;
+    // Copy it to our own certificate store, so that we can close the "MY"
+    // certificate store before returning from this function.
+    PCCERT_CONTEXT cert_context2;
+    BOOL ok = CertAddCertificateContextToStore(cert_store_, cert_context,
+                                               CERT_STORE_ADD_USE_EXISTING,
+                                               &cert_context2);
+    if (!ok) {
+      NOTREACHED();
+      continue;
+    }
+    scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
+        cert_context2, X509Certificate::SOURCE_LONE_CERT_IMPORT,
+        X509Certificate::OSCertHandles());
+    X509Certificate::FreeOSCertHandle(cert_context2);
+    that->client_certs_.push_back(cert);
+  }
+
+  BOOL ok = CertCloseStore(my_cert_store, CERT_CLOSE_STORE_CHECK_FLAG);
+  DCHECK(ok);
+
+  // Tell NSS to suspend the client authentication.  We will then abort the
+  // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED.
+  return SECWouldBlock;
+#elif defined(OS_MACOSX)
+  if (that->ssl_config_.send_client_cert) {
+    // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using
+    // CDSA/CSSM yet (http://crbug.com/45369), so client_cert must be NULL.
+    DCHECK(!that->ssl_config_.client_cert);
+    // Send no client certificate.
+    return SECFailure;
+  }
+
+  that->client_certs_.clear();
+
+  // First, get the cert issuer names allowed by the server.
+  std::vector<CertPrincipal> valid_issuers;
+  int n = ca_names->nnames;
+  for (int i = 0; i < n; i++) {
+    // Parse each name into a CertPrincipal object.
+    CertPrincipal p;
+    if (p.ParseDistinguishedName(ca_names->names[i].data,
+                                 ca_names->names[i].len)) {
+      valid_issuers.push_back(p);
+    }
+  }
+
+  // Now get the available client certs whose issuers are allowed by the server.
+  X509Certificate::GetSSLClientCertificates(that->hostname_,
+                                            valid_issuers,
+                                            &that->client_certs_);
+
+  // Tell NSS to suspend the client authentication.  We will then abort the
+  // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED.
+  return SECWouldBlock;
+#else
   CERTCertificate* cert = NULL;
   SECKEYPrivateKey* privkey = NULL;
   void* wincx  = SSL_RevealPinArg(socket);
@@ -1111,13 +1370,15 @@
         continue;
       // Only check unexpired certs.
       if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_TRUE) ==
-          secCertTimeValid &&
-          NSS_CmpCertChainWCANames(cert, ca_names) == SECSuccess) {
+          secCertTimeValid && (!ca_names->nnames ||
+          NSS_CmpCertChainWCANames(cert, ca_names) == SECSuccess)) {
         privkey = PK11_FindKeyByAnyCert(cert, wincx);
         if (privkey) {
           X509Certificate* x509_cert = X509Certificate::CreateFromHandle(
-              cert, X509Certificate::SOURCE_LONE_CERT_IMPORT);
+              cert, X509Certificate::SOURCE_LONE_CERT_IMPORT,
+              net::X509Certificate::OSCertHandles());
           that->client_certs_.push_back(x509_cert);
+          CERT_DestroyCertificate(cert);
           SECKEY_DestroyPrivateKey(privkey);
           continue;
         }
@@ -1127,7 +1388,9 @@
     CERT_FreeNicknames(names);
   }
 
-  return SECFailure;
+  // Tell NSS to suspend the client authentication.  We will then abort the
+  // handshake by returning ERR_SSL_CLIENT_AUTH_CERT_NEEDED.
+  return SECWouldBlock;
 #endif
 }
 
@@ -1139,7 +1402,11 @@
                                            void* arg) {
   SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg);
 
+  that->set_handshake_callback_called();
+
   that->UpdateServerCert();
+
+  that->CheckSecureRenegotiation();
 }
 
 int SSLClientSocketNSS::DoHandshake() {
@@ -1158,9 +1425,15 @@
       LOG(WARNING) << "Couldn't invalidate SSL session: " << PR_GetError();
     }
   } else if (rv == SECSuccess) {
-    // SSL handshake is completed.  Let's verify the certificate.
-    GotoState(STATE_VERIFY_CERT);
-    // Done!
+    if (handshake_callback_called_) {
+      // SSL handshake is completed.  Let's verify the certificate.
+      GotoState(STATE_VERIFY_CERT);
+      // Done!
+    } else {
+      // SSL_ForceHandshake returned SECSuccess prematurely.
+      rv = SECFailure;
+      net_error = ERR_SSL_PROTOCOL_ERROR;
+    }
   } else {
     PRErrorCode prerr = PR_GetError();
     net_error = MapHandshakeError(prerr);
@@ -1182,7 +1455,21 @@
   DCHECK(server_cert_);
   GotoState(STATE_VERIFY_CERT_COMPLETE);
   int flags = 0;
-  if (ssl_config_.rev_checking_enabled)
+
+  /* Disable revocation checking for SPDY. This is a hack, but we ignore
+   * certificate errors for SPDY anyway so it's no loss in security. This lets
+   * us benchmark as if we had OCSP stapling.
+   *
+   * http://crbug.com/32020
+   */
+  unsigned char buf[255];
+  int state;
+  unsigned int len;
+  SECStatus rv = SSL_GetNextProto(nss_fd_, &state, buf, &len, sizeof(buf));
+  bool spdy = (rv == SECSuccess && state == SSL_NEXT_PROTO_NEGOTIATED &&
+              len == 4 && memcmp(buf, "spdy", 4) == 0);
+
+  if (ssl_config_.rev_checking_enabled && !spdy)
     flags |= X509Certificate::VERIFY_REV_CHECKING_ENABLED;
   if (ssl_config_.verify_ev_cert)
     flags |= X509Certificate::VERIFY_EV_CERT;
@@ -1247,7 +1534,7 @@
   }
 
   completed_handshake_ = true;
-  // TODO(ukai): we may not need this call because it is now harmless to have an
+  // TODO(ukai): we may not need this call because it is now harmless to have a
   // session with a bad cert.
   InvalidateSessionIfBadCertificate();
   // Exit DoHandshakeLoop and return the result to the caller to Connect.
@@ -1258,7 +1545,7 @@
 int SSLClientSocketNSS::DoPayloadRead() {
   EnterFunction(user_read_buf_len_);
   DCHECK(user_read_buf_);
-  DCHECK(user_read_buf_len_ > 0);
+  DCHECK_GT(user_read_buf_len_, 0);
   int rv = PR_Read(nss_fd_, user_read_buf_->data(), user_read_buf_len_);
   if (client_auth_cert_needed_) {
     // We don't need to invalidate the non-client-authenticated SSL session
@@ -1291,6 +1578,7 @@
   }
   PRErrorCode prerr = PR_GetError();
   if (prerr == PR_WOULD_BLOCK_ERROR) {
+    LeaveFunction("");
     return ERR_IO_PENDING;
   }
   LeaveFunction("");
diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h
index 7e59ea8..60544ea 100644
--- a/net/socket/ssl_client_socket_nss.h
+++ b/net/socket/ssl_client_socket_nss.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -16,14 +16,17 @@
 #include "base/scoped_ptr.h"
 #include "net/base/cert_verify_result.h"
 #include "net/base/completion_callback.h"
+#include "net/base/net_log.h"
 #include "net/base/nss_memio.h"
 #include "net/base/ssl_config_service.h"
+#include "net/base/x509_certificate.h"
 #include "net/socket/ssl_client_socket.h"
 
 namespace net {
 
+class BoundNetLog;
 class CertVerifier;
-class LoadLog;
+class ClientSocketHandle;
 class X509Certificate;
 
 // An SSL client socket implemented with Mozilla NSS.
@@ -33,7 +36,7 @@
   // The given hostname will be compared with the name(s) in the server's
   // certificate during the SSL handshake.  ssl_config specifies the SSL
   // settings.
-  SSLClientSocketNSS(ClientSocket* transport_socket,
+  SSLClientSocketNSS(ClientSocketHandle* transport_socket,
                      const std::string& hostname,
                      const SSLConfig& ssl_config);
   ~SSLClientSocketNSS();
@@ -44,11 +47,12 @@
   virtual NextProtoStatus GetNextProto(std::string* proto);
 
   // ClientSocket methods:
-  virtual int Connect(CompletionCallback* callback, LoadLog* load_log);
+  virtual int Connect(CompletionCallback* callback);
   virtual void Disconnect();
   virtual bool IsConnected() const;
   virtual bool IsConnectedAndIdle() const;
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen);
+  virtual int GetPeerAddress(AddressList* address) const;
+  virtual const BoundNetLog& NetLog() const { return net_log_; }
 
   // Socket methods:
   virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
@@ -56,12 +60,19 @@
   virtual bool SetReceiveBufferSize(int32 size);
   virtual bool SetSendBufferSize(int32 size);
 
+  void set_handshake_callback_called() { handshake_callback_called_ = true; }
+
  private:
   // Initializes NSS SSL options.  Returns a net error code.
   int InitializeSSLOptions();
 
   void InvalidateSessionIfBadCertificate();
+#if defined(OS_MACOSX) || defined(OS_WIN)
+  // Creates an OS certificate from a DER-encoded certificate.
+  static X509Certificate::OSCertHandle CreateOSCert(const SECItem& der_cert);
+#endif
   X509Certificate* UpdateServerCert();
+  void CheckSecureRenegotiation() const;
   void DoReadCallback(int result);
   void DoWriteCallback(int result);
   void DoConnectCallback(int result);
@@ -107,7 +118,7 @@
   scoped_refptr<IOBuffer> recv_buffer_;
 
   CompletionCallbackImpl<SSLClientSocketNSS> handshake_io_callback_;
-  scoped_ptr<ClientSocket> transport_;
+  scoped_ptr<ClientSocketHandle> transport_;
   std::string hostname_;
   SSLConfig ssl_config_;
 
@@ -137,6 +148,10 @@
 
   scoped_ptr<CertVerifier> verifier_;
 
+  // True if NSS has called HandshakeCallback.
+  bool handshake_callback_called_;
+
+  // True if the SSL handshake has been completed.
   bool completed_handshake_;
 
   enum State {
@@ -153,12 +168,15 @@
   // Buffers for the network end of the SSL state machine
   memio_Private* nss_bufs_;
 
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
 
 #if defined(OS_WIN)
-  // A CryptoAPI in-memory certificate store that we import server
-  // certificates into so that we can verify and display the certificates
-  // using CryptoAPI.
+  // A CryptoAPI in-memory certificate store.  We use it for two purposes:
+  // 1. Import server certificates into this store so that we can verify and
+  //    display the certificates using CryptoAPI.
+  // 2. Copy client certificates from the "MY" system certificate store into
+  //    this store so that we can close the system store when we finish
+  //    searching for client certificates.
   static HCERTSTORE cert_store_;
 #endif
 };
diff --git a/net/socket/ssl_client_socket_nss_factory.cc b/net/socket/ssl_client_socket_nss_factory.cc
index cb5333d..99fb632 100644
--- a/net/socket/ssl_client_socket_nss_factory.cc
+++ b/net/socket/ssl_client_socket_nss_factory.cc
@@ -4,7 +4,11 @@
 
 #include "net/socket/client_socket_factory.h"
 
+#include "build/build_config.h"
 #include "net/socket/ssl_client_socket_nss.h"
+#if defined(OS_WIN)
+#include "net/socket/ssl_client_socket_win.h"
+#endif
 
 // This file is only used on platforms where NSS is not the system SSL
 // library.  When compiled, this file is the only object module that pulls
@@ -14,9 +18,17 @@
 namespace net {
 
 SSLClientSocket* SSLClientSocketNSSFactory(
-    ClientSocket* transport_socket,
+    ClientSocketHandle* transport_socket,
     const std::string& hostname,
     const SSLConfig& ssl_config) {
+  // TODO(wtc): SSLClientSocketNSS can't do SSL client authentication using
+  // CryptoAPI yet (http://crbug.com/37560), so we fall back on
+  // SSLClientSocketWin.
+#if defined(OS_WIN)
+  if (ssl_config.client_cert)
+    return new SSLClientSocketWin(transport_socket, hostname, ssl_config);
+#endif
+
   return new SSLClientSocketNSS(transport_socket, hostname, ssl_config);
 }
 
diff --git a/net/socket/ssl_client_socket_nss_factory.h b/net/socket/ssl_client_socket_nss_factory.h
new file mode 100644
index 0000000..b3b99b9
--- /dev/null
+++ b/net/socket/ssl_client_socket_nss_factory.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2010 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_SOCKET_SSL_CLIENT_SOCKET_NSS_FACTORY_H_
+#define NET_SOCKET_SSL_CLIENT_SOCKET_NSS_FACTORY_H_
+
+#include "net/socket/client_socket_factory.h"
+
+namespace net {
+
+// Creates SSLClientSocketNSS objects.
+SSLClientSocket* SSLClientSocketNSSFactory(
+    ClientSocketHandle* transport_socket,
+    const std::string& hostname,
+    const SSLConfig& ssl_config);
+
+}  // namespace net
+
+#endif  // NET_SOCKET_SSL_CLIENT_SOCKET_NSS_FACTORY_H_
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
new file mode 100644
index 0000000..3fd960c
--- /dev/null
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -0,0 +1,426 @@
+// Copyright (c) 2010 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/socket/ssl_client_socket_pool.h"
+
+#include "net/base/net_errors.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+
+namespace net {
+
+SSLSocketParams::SSLSocketParams(
+    const scoped_refptr<TCPSocketParams>& tcp_params,
+    const scoped_refptr<HttpProxySocketParams>& http_proxy_params,
+    const scoped_refptr<SOCKSSocketParams>& socks_params,
+    ProxyServer::Scheme proxy,
+    const std::string& hostname,
+    const SSLConfig& ssl_config,
+    int load_flags,
+    bool want_spdy)
+    : tcp_params_(tcp_params),
+      http_proxy_params_(http_proxy_params),
+      socks_params_(socks_params),
+      proxy_(proxy),
+      hostname_(hostname),
+      ssl_config_(ssl_config),
+      load_flags_(load_flags),
+      want_spdy_(want_spdy) {
+  switch (proxy_) {
+    case ProxyServer::SCHEME_DIRECT:
+      DCHECK(tcp_params_.get() != NULL);
+      DCHECK(http_proxy_params_.get() == NULL);
+      DCHECK(socks_params_.get() == NULL);
+      break;
+    case ProxyServer::SCHEME_HTTP:
+      DCHECK(tcp_params_.get() == NULL);
+      DCHECK(http_proxy_params_.get() != NULL);
+      DCHECK(socks_params_.get() == NULL);
+      break;
+    case ProxyServer::SCHEME_SOCKS4:
+    case ProxyServer::SCHEME_SOCKS5:
+      DCHECK(tcp_params_.get() == NULL);
+      DCHECK(http_proxy_params_.get() == NULL);
+      DCHECK(socks_params_.get() != NULL);
+      break;
+    default:
+      LOG(DFATAL) << "unknown proxy type";
+      break;
+  }
+}
+
+SSLSocketParams::~SSLSocketParams() {}
+
+// Timeout for the SSL handshake portion of the connect.
+static const int kSSLHandshakeTimeoutInSeconds = 30;
+
+SSLConnectJob::SSLConnectJob(
+    const std::string& group_name,
+    const scoped_refptr<SSLSocketParams>& params,
+    const base::TimeDelta& timeout_duration,
+    const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+    const scoped_refptr<HttpProxyClientSocketPool>& http_proxy_pool,
+    const scoped_refptr<SOCKSClientSocketPool>& socks_pool,
+    ClientSocketFactory* client_socket_factory,
+    const scoped_refptr<HostResolver>& host_resolver,
+    Delegate* delegate,
+    NetLog* net_log)
+    : ConnectJob(group_name, timeout_duration, delegate,
+                 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
+      params_(params),
+      tcp_pool_(tcp_pool),
+      http_proxy_pool_(http_proxy_pool),
+      socks_pool_(socks_pool),
+      client_socket_factory_(client_socket_factory),
+      resolver_(host_resolver),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          callback_(this, &SSLConnectJob::OnIOComplete)) {}
+
+SSLConnectJob::~SSLConnectJob() {}
+
+LoadState SSLConnectJob::GetLoadState() const {
+  switch (next_state_) {
+    case STATE_TUNNEL_CONNECT_COMPLETE:
+      if (transport_socket_handle_->socket())
+        return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
+      // else, fall through.
+    case STATE_TCP_CONNECT:
+    case STATE_TCP_CONNECT_COMPLETE:
+    case STATE_SOCKS_CONNECT:
+    case STATE_SOCKS_CONNECT_COMPLETE:
+    case STATE_TUNNEL_CONNECT:
+      return transport_socket_handle_->GetLoadState();
+    case STATE_SSL_CONNECT:
+    case STATE_SSL_CONNECT_COMPLETE:
+      return LOAD_STATE_SSL_HANDSHAKE;
+    default:
+      NOTREACHED();
+      return LOAD_STATE_IDLE;
+  }
+}
+
+int SSLConnectJob::ConnectInternal() {
+  DetermineFirstState();
+  return DoLoop(OK);
+}
+
+void SSLConnectJob::DetermineFirstState() {
+  switch (params_->proxy()) {
+    case ProxyServer::SCHEME_DIRECT:
+      next_state_ = STATE_TCP_CONNECT;
+      break;
+    case ProxyServer::SCHEME_HTTP:
+      next_state_ = STATE_TUNNEL_CONNECT;
+      break;
+    case ProxyServer::SCHEME_SOCKS4:
+    case ProxyServer::SCHEME_SOCKS5:
+      next_state_ = STATE_SOCKS_CONNECT;
+      break;
+    default:
+      NOTREACHED() << "unknown proxy type";
+      break;
+  }
+}
+
+void SSLConnectJob::OnIOComplete(int result) {
+  int rv = DoLoop(result);
+  if (rv != ERR_IO_PENDING)
+    NotifyDelegateOfCompletion(rv);  // Deletes |this|.
+}
+
+int SSLConnectJob::DoLoop(int result) {
+  DCHECK_NE(next_state_, STATE_NONE);
+
+  int rv = result;
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_TCP_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoTCPConnect();
+        break;
+      case STATE_TCP_CONNECT_COMPLETE:
+        rv = DoTCPConnectComplete(rv);
+        break;
+      case STATE_SOCKS_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoSOCKSConnect();
+        break;
+      case STATE_SOCKS_CONNECT_COMPLETE:
+        rv = DoSOCKSConnectComplete(rv);
+        break;
+      case STATE_TUNNEL_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoTunnelConnect();
+        break;
+      case STATE_TUNNEL_CONNECT_COMPLETE:
+        rv = DoTunnelConnectComplete(rv);
+        break;
+      case STATE_SSL_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoSSLConnect();
+        break;
+      case STATE_SSL_CONNECT_COMPLETE:
+        rv = DoSSLConnectComplete(rv);
+        break;
+      default:
+        NOTREACHED() << "bad state";
+        rv = ERR_FAILED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+
+  return rv;
+}
+
+int SSLConnectJob::DoTCPConnect() {
+  DCHECK(tcp_pool_.get());
+  next_state_ = STATE_TCP_CONNECT_COMPLETE;
+  transport_socket_handle_.reset(new ClientSocketHandle());
+  scoped_refptr<TCPSocketParams> tcp_params = params_->tcp_params();
+  return transport_socket_handle_->Init(group_name(), tcp_params,
+                                        tcp_params->destination().priority(),
+                                        &callback_, tcp_pool_, net_log());
+}
+
+int SSLConnectJob::DoTCPConnectComplete(int result) {
+  if (result == OK)
+    next_state_ = STATE_SSL_CONNECT;
+
+  return result;
+}
+
+int SSLConnectJob::DoSOCKSConnect() {
+  DCHECK(socks_pool_.get());
+  next_state_ = STATE_SOCKS_CONNECT_COMPLETE;
+  transport_socket_handle_.reset(new ClientSocketHandle());
+  scoped_refptr<SOCKSSocketParams> socks_params = params_->socks_params();
+  return transport_socket_handle_->Init(group_name(), socks_params,
+                                        socks_params->destination().priority(),
+                                        &callback_, socks_pool_, net_log());
+}
+
+int SSLConnectJob::DoSOCKSConnectComplete(int result) {
+  if (result == OK)
+    next_state_ = STATE_SSL_CONNECT;
+
+  return result;
+}
+
+int SSLConnectJob::DoTunnelConnect() {
+  DCHECK(http_proxy_pool_.get());
+  next_state_ = STATE_TUNNEL_CONNECT_COMPLETE;
+  transport_socket_handle_.reset(new ClientSocketHandle());
+  scoped_refptr<HttpProxySocketParams> http_proxy_params =
+      params_->http_proxy_params();
+  return transport_socket_handle_->Init(
+      group_name(), http_proxy_params,
+      http_proxy_params->tcp_params()->destination().priority(), &callback_,
+      http_proxy_pool_, net_log());
+}
+
+int SSLConnectJob::DoTunnelConnectComplete(int result) {
+  ClientSocket* socket = transport_socket_handle_->socket();
+  HttpProxyClientSocket* tunnel_socket =
+      static_cast<HttpProxyClientSocket*>(socket);
+
+  if (result == ERR_RETRY_CONNECTION) {
+    DetermineFirstState();
+    transport_socket_handle_->socket()->Disconnect();
+    return OK;
+  }
+
+  // Extract the information needed to prompt for the proxy authentication.
+  // so that when ClientSocketPoolBaseHelper calls |GetAdditionalErrorState|,
+  // we can easily set the state.
+  if (result == ERR_PROXY_AUTH_REQUESTED)
+    error_response_info_ = *tunnel_socket->GetResponseInfo();
+
+  if (result < 0)
+    return result;
+
+  if (tunnel_socket->NeedsRestartWithAuth()) {
+    // We must have gotten an 'idle' tunnel socket that is waiting for auth.
+    // The HttpAuthController should have new credentials, we just need
+    // to retry.
+    next_state_ = STATE_TUNNEL_CONNECT_COMPLETE;
+    return tunnel_socket->RestartWithAuth(&callback_);
+  }
+
+  next_state_ = STATE_SSL_CONNECT;
+  return result;
+}
+
+void SSLConnectJob::GetAdditionalErrorState(ClientSocketHandle * handle) {
+  handle->set_ssl_error_response_info(error_response_info_);
+  if (!ssl_connect_start_time_.is_null())
+    handle->set_is_ssl_error(true);
+}
+
+int SSLConnectJob::DoSSLConnect() {
+  next_state_ = STATE_SSL_CONNECT_COMPLETE;
+  // Reset the timeout to just the time allowed for the SSL handshake.
+  ResetTimer(base::TimeDelta::FromSeconds(kSSLHandshakeTimeoutInSeconds));
+  ssl_connect_start_time_ = base::TimeTicks::Now();
+
+  ssl_socket_.reset(client_socket_factory_->CreateSSLClientSocket(
+        transport_socket_handle_.release(), params_->hostname(),
+        params_->ssl_config()));
+  return ssl_socket_->Connect(&callback_);
+}
+
+int SSLConnectJob::DoSSLConnectComplete(int result) {
+  SSLClientSocket::NextProtoStatus status =
+      SSLClientSocket::kNextProtoUnsupported;
+  std::string proto;
+  // GetNextProto will fail and and trigger a NOTREACHED if we pass in a socket
+  // that hasn't had SSL_ImportFD called on it. If we get a certificate error
+  // here, then we know that we called SSL_ImportFD.
+  if (result == OK || IsCertificateError(result))
+    status = ssl_socket_->GetNextProto(&proto);
+
+  bool using_spdy = false;
+  if (status == SSLClientSocket::kNextProtoNegotiated) {
+    ssl_socket_->setWasNpnNegotiated(true);
+    if (SSLClientSocket::NextProtoFromString(proto) ==
+        SSLClientSocket::kProtoSPDY1) {
+          using_spdy = true;
+    }
+  }
+  if (params_->want_spdy() && !using_spdy)
+    return ERR_NPN_NEGOTIATION_FAILED;
+
+  if (result == OK ||
+      ssl_socket_->IgnoreCertError(result, params_->load_flags())) {
+    DCHECK(ssl_connect_start_time_ != base::TimeTicks());
+    base::TimeDelta connect_duration =
+        base::TimeTicks::Now() - ssl_connect_start_time_;
+    if (using_spdy)
+      UMA_HISTOGRAM_CUSTOM_TIMES("Net.SpdyConnectionLatency",
+                                 connect_duration,
+                                 base::TimeDelta::FromMilliseconds(1),
+                                 base::TimeDelta::FromMinutes(10),
+                                 100);
+    else
+      UMA_HISTOGRAM_CUSTOM_TIMES("Net.SSL_Connection_Latency",
+                                 connect_duration,
+                                 base::TimeDelta::FromMilliseconds(1),
+                                 base::TimeDelta::FromMinutes(10),
+                                 100);
+  }
+
+  if (result == OK || IsCertificateError(result)) {
+    set_socket(ssl_socket_.release());
+  } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
+    error_response_info_.cert_request_info = new SSLCertRequestInfo;
+    ssl_socket_->GetSSLCertRequestInfo(error_response_info_.cert_request_info);
+  }
+
+  return result;
+}
+
+ConnectJob* SSLClientSocketPool::SSLConnectJobFactory::NewConnectJob(
+    const std::string& group_name,
+    const PoolBase::Request& request,
+    ConnectJob::Delegate* delegate) const {
+  return new SSLConnectJob(group_name, request.params(), ConnectionTimeout(),
+                           tcp_pool_, http_proxy_pool_, socks_pool_,
+                           client_socket_factory_, host_resolver_, delegate,
+                           net_log_);
+}
+
+SSLClientSocketPool::SSLConnectJobFactory::SSLConnectJobFactory(
+    const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+    const scoped_refptr<HttpProxyClientSocketPool>& http_proxy_pool,
+    const scoped_refptr<SOCKSClientSocketPool>& socks_pool,
+    ClientSocketFactory* client_socket_factory,
+    HostResolver* host_resolver,
+    NetLog* net_log)
+    : tcp_pool_(tcp_pool),
+      http_proxy_pool_(http_proxy_pool),
+      socks_pool_(socks_pool),
+      client_socket_factory_(client_socket_factory),
+      host_resolver_(host_resolver),
+      net_log_(net_log) {
+  base::TimeDelta max_transport_timeout = base::TimeDelta();
+  base::TimeDelta pool_timeout;
+  if (tcp_pool_)
+    max_transport_timeout = tcp_pool_->ConnectionTimeout();
+  if (socks_pool_) {
+    pool_timeout = socks_pool_->ConnectionTimeout();
+    if (pool_timeout > max_transport_timeout)
+      max_transport_timeout = pool_timeout;
+  }
+  if (http_proxy_pool_) {
+    pool_timeout = http_proxy_pool_->ConnectionTimeout();
+    if (pool_timeout > max_transport_timeout)
+      max_transport_timeout = pool_timeout;
+  }
+  timeout_ = max_transport_timeout +
+      base::TimeDelta::FromSeconds(kSSLHandshakeTimeoutInSeconds);
+}
+
+SSLClientSocketPool::SSLClientSocketPool(
+    int max_sockets,
+    int max_sockets_per_group,
+    const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+    const scoped_refptr<HostResolver>& host_resolver,
+    ClientSocketFactory* client_socket_factory,
+    const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+    const scoped_refptr<HttpProxyClientSocketPool>& http_proxy_pool,
+    const scoped_refptr<SOCKSClientSocketPool>& socks_pool,
+    NetLog* net_log)
+    : base_(max_sockets, max_sockets_per_group, histograms,
+            base::TimeDelta::FromSeconds(
+                ClientSocketPool::unused_idle_socket_timeout()),
+            base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
+            new SSLConnectJobFactory(tcp_pool, http_proxy_pool, socks_pool,
+                                     client_socket_factory, host_resolver,
+                                     net_log)) {}
+
+SSLClientSocketPool::~SSLClientSocketPool() {}
+
+int SSLClientSocketPool::RequestSocket(const std::string& group_name,
+                                       const void* socket_params,
+                                       RequestPriority priority,
+                                       ClientSocketHandle* handle,
+                                       CompletionCallback* callback,
+                                       const BoundNetLog& net_log) {
+  const scoped_refptr<SSLSocketParams>* casted_socket_params =
+      static_cast<const scoped_refptr<SSLSocketParams>*>(socket_params);
+
+  return base_.RequestSocket(group_name, *casted_socket_params, priority,
+                             handle, callback, net_log);
+}
+
+void SSLClientSocketPool::CancelRequest(const std::string& group_name,
+                                        ClientSocketHandle* handle) {
+  base_.CancelRequest(group_name, handle);
+}
+
+void SSLClientSocketPool::ReleaseSocket(const std::string& group_name,
+                                        ClientSocket* socket, int id) {
+  base_.ReleaseSocket(group_name, socket, id);
+}
+
+void SSLClientSocketPool::Flush() {
+  base_.Flush();
+}
+
+void SSLClientSocketPool::CloseIdleSockets() {
+  base_.CloseIdleSockets();
+}
+
+int SSLClientSocketPool::IdleSocketCountInGroup(
+    const std::string& group_name) const {
+  return base_.IdleSocketCountInGroup(group_name);
+}
+
+LoadState SSLClientSocketPool::GetLoadState(
+    const std::string& group_name, const ClientSocketHandle* handle) const {
+  return base_.GetLoadState(group_name, handle);
+}
+
+}  // namespace net
diff --git a/net/socket/ssl_client_socket_pool.h b/net/socket/ssl_client_socket_pool.h
new file mode 100644
index 0000000..fd5bbb3
--- /dev/null
+++ b/net/socket/ssl_client_socket_pool.h
@@ -0,0 +1,247 @@
+// Copyright (c) 2010 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_SOCKET_SSL_CLIENT_SOCKET_POOL_H_
+#define NET_SOCKET_SSL_CLIENT_SOCKET_POOL_H_
+
+#include <string>
+
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "net/base/host_resolver.h"
+#include "net/base/ssl_config_service.h"
+#include "net/http/http_proxy_client_socket.h"
+#include "net/http/http_proxy_client_socket_pool.h"
+#include "net/proxy/proxy_server.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_pool_base.h"
+#include "net/socket/client_socket_pool_histograms.h"
+#include "net/socket/client_socket_pool.h"
+#include "net/socket/socks_client_socket_pool.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/socket/tcp_client_socket_pool.h"
+
+namespace net {
+
+class ClientSocketFactory;
+class ConnectJobFactory;
+
+// SSLSocketParams only needs the socket params for the transport socket
+// that will be used (denoted by |proxy|).
+class SSLSocketParams : public base::RefCounted<SSLSocketParams> {
+ public:
+  SSLSocketParams(const scoped_refptr<TCPSocketParams>& tcp_params,
+                  const scoped_refptr<HttpProxySocketParams>& http_proxy_params,
+                  const scoped_refptr<SOCKSSocketParams>& socks_params,
+                  ProxyServer::Scheme proxy,
+                  const std::string& hostname,
+                  const SSLConfig& ssl_config,
+                  int load_flags,
+                  bool want_spdy);
+
+  const scoped_refptr<TCPSocketParams>& tcp_params() { return tcp_params_; }
+  const scoped_refptr<HttpProxySocketParams>& http_proxy_params () {
+    return http_proxy_params_;
+  }
+  const scoped_refptr<SOCKSSocketParams>& socks_params() {
+    return socks_params_;
+  }
+  ProxyServer::Scheme proxy() const { return proxy_; }
+  const std::string& hostname() const { return hostname_; }
+  const SSLConfig& ssl_config() const { return ssl_config_; }
+  int load_flags() const { return load_flags_; }
+  bool want_spdy() const { return want_spdy_; }
+
+ private:
+  friend class base::RefCounted<SSLSocketParams>;
+  ~SSLSocketParams();
+
+  const scoped_refptr<TCPSocketParams> tcp_params_;
+  const scoped_refptr<HttpProxySocketParams> http_proxy_params_;
+  const scoped_refptr<SOCKSSocketParams> socks_params_;
+  const ProxyServer::Scheme proxy_;
+  const std::string hostname_;
+  const SSLConfig ssl_config_;
+  const int load_flags_;
+  const bool want_spdy_;
+
+  DISALLOW_COPY_AND_ASSIGN(SSLSocketParams);
+};
+
+// SSLConnectJob handles the SSL handshake after setting up the underlying
+// connection as specified in the params.
+class SSLConnectJob : public ConnectJob {
+ public:
+  SSLConnectJob(
+      const std::string& group_name,
+      const scoped_refptr<SSLSocketParams>& params,
+      const base::TimeDelta& timeout_duration,
+      const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+      const scoped_refptr<HttpProxyClientSocketPool>& http_proxy_pool,
+      const scoped_refptr<SOCKSClientSocketPool>& socks_pool,
+      ClientSocketFactory* client_socket_factory,
+      const scoped_refptr<HostResolver>& host_resolver,
+      Delegate* delegate,
+      NetLog* net_log);
+  virtual ~SSLConnectJob();
+
+  // ConnectJob methods.
+  virtual LoadState GetLoadState() const;
+
+  virtual void GetAdditionalErrorState(ClientSocketHandle * handle);
+
+ private:
+  enum State {
+    STATE_TCP_CONNECT,
+    STATE_TCP_CONNECT_COMPLETE,
+    STATE_SOCKS_CONNECT,
+    STATE_SOCKS_CONNECT_COMPLETE,
+    STATE_TUNNEL_CONNECT,
+    STATE_TUNNEL_CONNECT_COMPLETE,
+    STATE_SSL_CONNECT,
+    STATE_SSL_CONNECT_COMPLETE,
+    STATE_NONE,
+  };
+
+  // Starts the SSL connection process.  Returns OK on success and
+  // ERR_IO_PENDING if it cannot immediately service the request.
+  // Otherwise, it returns a net error code.
+  virtual int ConnectInternal();
+
+  void DetermineFirstState();
+
+  void OnIOComplete(int result);
+
+  // Runs the state transition loop.
+  int DoLoop(int result);
+
+  int DoTCPConnect();
+  int DoTCPConnectComplete(int result);
+  int DoSOCKSConnect();
+  int DoSOCKSConnectComplete(int result);
+  int DoTunnelConnect();
+  int DoTunnelConnectComplete(int result);
+  int DoSSLConnect();
+  int DoSSLConnectComplete(int result);
+
+  scoped_refptr<SSLSocketParams> params_;
+  const scoped_refptr<TCPClientSocketPool> tcp_pool_;
+  const scoped_refptr<HttpProxyClientSocketPool> http_proxy_pool_;
+  const scoped_refptr<SOCKSClientSocketPool> socks_pool_;
+  ClientSocketFactory* const client_socket_factory_;
+  const scoped_refptr<HostResolver> resolver_;
+
+  State next_state_;
+  CompletionCallbackImpl<SSLConnectJob> callback_;
+  scoped_ptr<ClientSocketHandle> transport_socket_handle_;
+  scoped_ptr<SSLClientSocket> ssl_socket_;
+
+  // The time the DoSSLConnect() method was called.
+  base::TimeTicks ssl_connect_start_time_;
+
+  HttpResponseInfo error_response_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(SSLConnectJob);
+};
+
+class SSLClientSocketPool : public ClientSocketPool {
+ public:
+  // Only the pools that will be used are required. i.e. if you never
+  // try to create an SSL over SOCKS socket, |socks_pool| may be NULL.
+  SSLClientSocketPool(
+      int max_sockets,
+      int max_sockets_per_group,
+      const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+      const scoped_refptr<HostResolver>& host_resolver,
+      ClientSocketFactory* client_socket_factory,
+      const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+      const scoped_refptr<HttpProxyClientSocketPool>& http_proxy_pool,
+      const scoped_refptr<SOCKSClientSocketPool>& socks_pool,
+      NetLog* net_log);
+
+  // ClientSocketPool methods:
+  virtual int RequestSocket(const std::string& group_name,
+                            const void* connect_params,
+                            RequestPriority priority,
+                            ClientSocketHandle* handle,
+                            CompletionCallback* callback,
+                            const BoundNetLog& net_log);
+
+  virtual void CancelRequest(const std::string& group_name,
+                             ClientSocketHandle* handle);
+
+  virtual void ReleaseSocket(const std::string& group_name,
+                             ClientSocket* socket,
+                             int id);
+
+  virtual void Flush();
+
+  virtual void CloseIdleSockets();
+
+  virtual int IdleSocketCount() const {
+    return base_.idle_socket_count();
+  }
+
+  virtual int IdleSocketCountInGroup(const std::string& group_name) const;
+
+  virtual LoadState GetLoadState(const std::string& group_name,
+                                 const ClientSocketHandle* handle) const;
+
+  virtual base::TimeDelta ConnectionTimeout() const {
+    return base_.ConnectionTimeout();
+  }
+
+  virtual scoped_refptr<ClientSocketPoolHistograms> histograms() const {
+    return base_.histograms();
+  };
+
+ protected:
+  virtual ~SSLClientSocketPool();
+
+ private:
+  typedef ClientSocketPoolBase<SSLSocketParams> PoolBase;
+
+  class SSLConnectJobFactory : public PoolBase::ConnectJobFactory {
+   public:
+    SSLConnectJobFactory(
+        const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+        const scoped_refptr<HttpProxyClientSocketPool>& http_proxy_pool,
+        const scoped_refptr<SOCKSClientSocketPool>& socks_pool,
+        ClientSocketFactory* client_socket_factory,
+        HostResolver* host_resolver,
+        NetLog* net_log);
+
+    virtual ~SSLConnectJobFactory() {}
+
+    // ClientSocketPoolBase::ConnectJobFactory methods.
+    virtual ConnectJob* NewConnectJob(
+        const std::string& group_name,
+        const PoolBase::Request& request,
+        ConnectJob::Delegate* delegate) const;
+
+    virtual base::TimeDelta ConnectionTimeout() const { return timeout_; }
+
+   private:
+    const scoped_refptr<TCPClientSocketPool> tcp_pool_;
+    const scoped_refptr<HttpProxyClientSocketPool> http_proxy_pool_;
+    const scoped_refptr<SOCKSClientSocketPool> socks_pool_;
+    ClientSocketFactory* const client_socket_factory_;
+    const scoped_refptr<HostResolver> host_resolver_;
+    base::TimeDelta timeout_;
+    NetLog* net_log_;
+
+    DISALLOW_COPY_AND_ASSIGN(SSLConnectJobFactory);
+  };
+
+  PoolBase base_;
+
+  DISALLOW_COPY_AND_ASSIGN(SSLClientSocketPool);
+};
+
+REGISTER_SOCKET_PARAMS_FOR_POOL(SSLClientSocketPool, SSLSocketParams);
+
+}  // namespace net
+
+#endif  // NET_SOCKET_SSL_CLIENT_SOCKET_POOL_H_
diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc
new file mode 100644
index 0000000..972f7d8
--- /dev/null
+++ b/net/socket/ssl_client_socket_pool_unittest.cc
@@ -0,0 +1,723 @@
+// Copyright (c) 2010 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/http/http_proxy_client_socket_pool.h"
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/time.h"
+#include "net/base/auth.h"
+#include "net/base/mock_host_resolver.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/base/ssl_config_service_defaults.h"
+#include "net/http/http_auth_controller.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_histograms.h"
+#include "net/socket/socket_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+const int kMaxSockets = 32;
+const int kMaxSocketsPerGroup = 6;
+
+class SSLClientSocketPoolTest : public ClientSocketPoolTest {
+ protected:
+  SSLClientSocketPoolTest()
+      : direct_tcp_socket_params_(new TCPSocketParams(
+            HostPortPair("host", 443), MEDIUM, GURL(), false)),
+        tcp_socket_pool_(new MockTCPClientSocketPool(
+            kMaxSockets,
+            kMaxSocketsPerGroup,
+            make_scoped_refptr(new ClientSocketPoolHistograms("MockTCP")),
+            &socket_factory_)),
+        proxy_tcp_socket_params_(new TCPSocketParams(
+            HostPortPair("proxy", 443), MEDIUM, GURL(), false)),
+        http_proxy_socket_pool_(new HttpProxyClientSocketPool(
+            kMaxSockets,
+            kMaxSocketsPerGroup,
+            make_scoped_refptr(new ClientSocketPoolHistograms("MockHttpProxy")),
+            new MockHostResolver,
+            tcp_socket_pool_,
+            NULL)),
+        socks_socket_params_(new SOCKSSocketParams(
+            proxy_tcp_socket_params_, true, HostPortPair("sockshost", 443),
+            MEDIUM, GURL())),
+        socks_socket_pool_(new MockSOCKSClientSocketPool(
+            kMaxSockets,
+            kMaxSocketsPerGroup,
+            make_scoped_refptr(new ClientSocketPoolHistograms("MockSOCKS")),
+            tcp_socket_pool_)) {
+    scoped_refptr<SSLConfigService> ssl_config_service(
+        new SSLConfigServiceDefaults);
+    ssl_config_service->GetSSLConfig(&ssl_config_);
+  }
+
+  void CreatePool(bool tcp_pool, bool http_proxy_pool, bool socks_pool) {
+    pool_ = new SSLClientSocketPool(
+        kMaxSockets,
+        kMaxSocketsPerGroup,
+        make_scoped_refptr(new ClientSocketPoolHistograms("SSLUnitTest")),
+        NULL,
+        &socket_factory_,
+        tcp_pool ? tcp_socket_pool_ : NULL,
+        http_proxy_pool ? http_proxy_socket_pool_ : NULL,
+        socks_pool ? socks_socket_pool_ : NULL,
+        NULL);
+  }
+
+  scoped_refptr<SSLSocketParams> SSLParams(
+      ProxyServer::Scheme proxy, struct MockHttpAuthControllerData* auth_data,
+      size_t auth_data_len, bool want_spdy) {
+
+    scoped_refptr<HttpProxySocketParams> http_proxy_params;
+    if (proxy == ProxyServer::SCHEME_HTTP) {
+      scoped_refptr<MockHttpAuthController> auth_controller =
+         new MockHttpAuthController();
+      auth_controller->SetMockAuthControllerData(auth_data, auth_data_len);
+      http_proxy_params = new HttpProxySocketParams(proxy_tcp_socket_params_,
+                                                    GURL("http://host"),
+                                                    HostPortPair("host", 80),
+                                                    auth_controller, true);
+    }
+
+    return make_scoped_refptr(new SSLSocketParams(
+        proxy == ProxyServer::SCHEME_DIRECT ? direct_tcp_socket_params_ : NULL,
+        http_proxy_params,
+        proxy == ProxyServer::SCHEME_SOCKS5 ? socks_socket_params_ : NULL,
+        proxy,
+        "host",
+        ssl_config_,
+        0,
+        want_spdy));
+  }
+
+  MockClientSocketFactory socket_factory_;
+
+  scoped_refptr<TCPSocketParams> direct_tcp_socket_params_;
+  scoped_refptr<MockTCPClientSocketPool> tcp_socket_pool_;
+
+  scoped_refptr<TCPSocketParams> proxy_tcp_socket_params_;
+  scoped_refptr<HttpProxySocketParams> http_proxy_socket_params_;
+  scoped_refptr<HttpProxyClientSocketPool> http_proxy_socket_pool_;
+
+  scoped_refptr<SOCKSSocketParams> socks_socket_params_;
+  scoped_refptr<MockSOCKSClientSocketPool> socks_socket_pool_;
+
+  SSLConfig ssl_config_;
+  scoped_refptr<SSLClientSocketPool> pool_;
+};
+
+TEST_F(SSLClientSocketPoolTest, TCPFail) {
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(false, ERR_CONNECTION_FAILED));
+  socket_factory_.AddSocketDataProvider(&data);
+
+  CreatePool(true /* tcp pool */, false, false);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  int rv = handle.Init("a", params, MEDIUM, NULL, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_CONNECTION_FAILED, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_FALSE(handle.is_ssl_error());
+}
+
+TEST_F(SSLClientSocketPoolTest, TCPFailAsync) {
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(true, ERR_CONNECTION_FAILED));
+  socket_factory_.AddSocketDataProvider(&data);
+
+  CreatePool(true /* tcp pool */, false, false);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_CONNECTION_FAILED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_FALSE(handle.is_ssl_error());
+}
+
+TEST_F(SSLClientSocketPoolTest, BasicDirect) {
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(false, OK));
+  socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(false, OK);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(true /* tcp pool */, false, false);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(SSLClientSocketPoolTest, BasicDirectAsync) {
+  StaticSocketDataProvider data;
+  socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(true, OK);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(true /* tcp pool */, false, false);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(SSLClientSocketPoolTest, DirectCertError) {
+  StaticSocketDataProvider data;
+  socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(true, ERR_CERT_COMMON_NAME_INVALID);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(true /* tcp pool */, false, false);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_CERT_COMMON_NAME_INVALID, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(SSLClientSocketPoolTest, DirectSSLError) {
+  StaticSocketDataProvider data;
+  socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(true, ERR_SSL_PROTOCOL_ERROR);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(true /* tcp pool */, false, false);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_SSL_PROTOCOL_ERROR, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_TRUE(handle.is_ssl_error());
+}
+
+TEST_F(SSLClientSocketPoolTest, DirectWithNPN) {
+  StaticSocketDataProvider data;
+  socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(true, OK);
+  ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+  ssl.next_proto = "http/1.1";
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(true /* tcp pool */, false, false);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+  SSLClientSocket* ssl_socket = static_cast<SSLClientSocket*>(handle.socket());
+  EXPECT_TRUE(ssl_socket->wasNpnNegotiated());
+}
+
+TEST_F(SSLClientSocketPoolTest, DirectNoSPDY) {
+  StaticSocketDataProvider data;
+  socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(true, OK);
+  ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+  ssl.next_proto = "http/1.1";
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(true /* tcp pool */, false, false);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT,
+                                                    NULL, 0, true);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_NPN_NEGOTIATION_FAILED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_TRUE(handle.is_ssl_error());
+}
+
+TEST_F(SSLClientSocketPoolTest, DirectGotSPDY) {
+  StaticSocketDataProvider data;
+  socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(true, OK);
+  ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+  ssl.next_proto = "spdy/1";
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(true /* tcp pool */, false, false);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT,
+                                                    NULL, 0, true);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+
+  SSLClientSocket* ssl_socket = static_cast<SSLClientSocket*>(handle.socket());
+  EXPECT_TRUE(ssl_socket->wasNpnNegotiated());
+  std::string proto;
+  ssl_socket->GetNextProto(&proto);
+  EXPECT_EQ(SSLClientSocket::NextProtoFromString(proto),
+            SSLClientSocket::kProtoSPDY1);
+}
+
+TEST_F(SSLClientSocketPoolTest, DirectGotBonusSPDY) {
+  StaticSocketDataProvider data;
+  socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(true, OK);
+  ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+  ssl.next_proto = "spdy/1";
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(true /* tcp pool */, false, false);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+
+  SSLClientSocket* ssl_socket = static_cast<SSLClientSocket*>(handle.socket());
+  EXPECT_TRUE(ssl_socket->wasNpnNegotiated());
+  std::string proto;
+  ssl_socket->GetNextProto(&proto);
+  EXPECT_EQ(SSLClientSocket::NextProtoFromString(proto),
+            SSLClientSocket::kProtoSPDY1);
+}
+
+TEST_F(SSLClientSocketPoolTest, SOCKSFail) {
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(false, ERR_CONNECTION_FAILED));
+  socket_factory_.AddSocketDataProvider(&data);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_SOCKS5,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_CONNECTION_FAILED, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_FALSE(handle.is_ssl_error());
+}
+
+TEST_F(SSLClientSocketPoolTest, SOCKSFailAsync) {
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(true, ERR_CONNECTION_FAILED));
+  socket_factory_.AddSocketDataProvider(&data);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_SOCKS5,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_CONNECTION_FAILED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_FALSE(handle.is_ssl_error());
+}
+
+TEST_F(SSLClientSocketPoolTest, SOCKSBasic) {
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(false, OK));
+  socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(false, OK);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_SOCKS5,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(SSLClientSocketPoolTest, SOCKSBasicAsync) {
+  StaticSocketDataProvider data;
+  socket_factory_.AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(true, OK);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_SOCKS5,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(SSLClientSocketPoolTest, HttpProxyFail) {
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(false, ERR_CONNECTION_FAILED));
+  socket_factory_.AddSocketDataProvider(&data);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_CONNECTION_FAILED, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_FALSE(handle.is_ssl_error());
+}
+
+TEST_F(SSLClientSocketPoolTest, HttpProxyFailAsync) {
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(true, ERR_CONNECTION_FAILED));
+  socket_factory_.AddSocketDataProvider(&data);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP,
+                                                    NULL, 0, false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_CONNECTION_FAILED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_FALSE(handle.is_ssl_error());
+}
+
+TEST_F(SSLClientSocketPoolTest, HttpProxyBasic) {
+  MockWrite writes[] = {
+      MockWrite(false,
+                "CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+  };
+  MockRead reads[] = {
+      MockRead(false, "HTTP/1.1 200 Connection Established\r\n\r\n"),
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), writes,
+                                arraysize(writes));
+  data.set_connect_data(MockConnect(false, OK));
+  socket_factory_.AddSocketDataProvider(&data);
+  MockHttpAuthControllerData auth_data[] = {
+      MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+  };
+  SSLSocketDataProvider ssl(false, OK);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP,
+                                                    auth_data,
+                                                    arraysize(auth_data),
+                                                    false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(OK, rv);
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(SSLClientSocketPoolTest, HttpProxyBasicAsync) {
+  MockWrite writes[] = {
+      MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+  };
+  MockRead reads[] = {
+      MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), writes,
+                                arraysize(writes));
+  socket_factory_.AddSocketDataProvider(&data);
+  MockHttpAuthControllerData auth_data[] = {
+      MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+  };
+  SSLSocketDataProvider ssl(true, OK);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP,
+                                                    auth_data,
+                                                    arraysize(auth_data),
+                                                    false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(SSLClientSocketPoolTest, NeedProxyAuth) {
+  MockWrite writes[] = {
+      MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+  };
+  MockRead reads[] = {
+      MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
+      MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+      MockRead("Content-Length: 10\r\n\r\n"),
+      MockRead("0123456789"),
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), writes,
+                                arraysize(writes));
+  socket_factory_.AddSocketDataProvider(&data);
+  MockHttpAuthControllerData auth_data[] = {
+      MockHttpAuthControllerData(""),
+  };
+  SSLSocketDataProvider ssl(true, OK);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP,
+                                                    auth_data,
+                                                    arraysize(auth_data),
+                                                    false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_FALSE(handle.is_ssl_error());
+  const HttpResponseInfo& tunnel_info = handle.ssl_error_response_info();
+  EXPECT_EQ(tunnel_info.headers->response_code(), 407);
+}
+
+TEST_F(SSLClientSocketPoolTest, DoProxyAuth) {
+  MockWrite writes[] = {
+      MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+      MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+  };
+  MockRead reads[] = {
+      MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
+      MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+      MockRead("Content-Length: 10\r\n\r\n"),
+      MockRead("0123456789"),
+      MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), writes,
+                                arraysize(writes));
+  socket_factory_.AddSocketDataProvider(&data);
+  MockHttpAuthControllerData auth_data[] = {
+      MockHttpAuthControllerData(""),
+      MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+  };
+  SSLSocketDataProvider ssl(true, OK);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP,
+                                                    auth_data,
+                                                    arraysize(auth_data),
+                                                    false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_FALSE(handle.is_ssl_error());
+  const HttpResponseInfo& tunnel_info = handle.ssl_error_response_info();
+  EXPECT_EQ(tunnel_info.headers->response_code(), 407);
+
+  params->http_proxy_params()->auth_controller()->ResetAuth(std::wstring(),
+                                                            std::wstring());
+  rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  // Test that http://crbug.com/49325 doesn't regress.
+  EXPECT_EQ(handle.GetLoadState(), LOAD_STATE_ESTABLISHING_PROXY_TUNNEL);
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(SSLClientSocketPoolTest, DoProxyAuthNoKeepAlive) {
+  MockWrite writes1[] = {
+      MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n\r\n"),
+  };
+  MockWrite writes2[] = {
+      MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+                "Host: host\r\n"
+                "Proxy-Connection: keep-alive\r\n"
+                "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+  };
+  MockRead reads1[] = {
+      MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
+      MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n\r\n"),
+      MockRead("Content0123456789"),
+  };
+  MockRead reads2[] = {
+      MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+  };
+  StaticSocketDataProvider data1(reads1, arraysize(reads1), writes1,
+                                arraysize(writes1));
+  socket_factory_.AddSocketDataProvider(&data1);
+  StaticSocketDataProvider data2(reads2, arraysize(reads2), writes2,
+                                arraysize(writes2));
+  socket_factory_.AddSocketDataProvider(&data2);
+  MockHttpAuthControllerData auth_data[] = {
+      MockHttpAuthControllerData(""),
+      MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+  };
+  SSLSocketDataProvider ssl(true, OK);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  CreatePool(false, true /* http proxy pool */, true /* socks pool */);
+  scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP,
+                                                    auth_data,
+                                                    arraysize(auth_data),
+                                                    false);
+
+  ClientSocketHandle handle;
+  TestCompletionCallback callback;
+  int rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_FALSE(handle.is_ssl_error());
+  const HttpResponseInfo& tunnel_info = handle.ssl_error_response_info();
+  EXPECT_EQ(tunnel_info.headers->response_code(), 407);
+
+  params->http_proxy_params()->auth_controller()->ResetAuth(std::wstring(),
+                                                            std::wstring());
+  rv = handle.Init("a", params, MEDIUM, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+}
+
+// It would be nice to also test the timeouts in SSLClientSocketPool.
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index f44f1ab..a62927e 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -7,14 +7,15 @@
 #include "net/base/address_list.h"
 #include "net/base/host_resolver.h"
 #include "net/base/io_buffer.h"
-#include "net/base/load_log.h"
-#include "net/base/load_log_unittest.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/net_errors.h"
 #include "net/base/ssl_config_service.h"
 #include "net/base/test_completion_callback.h"
 #include "net/socket/client_socket_factory.h"
-#include "net/socket/ssl_test_util.h"
+#include "net/socket/socket_test_util.h"
 #include "net/socket/tcp_client_socket.h"
+#include "net/test/test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
@@ -25,7 +26,8 @@
 class SSLClientSocketTest : public PlatformTest {
  public:
   SSLClientSocketTest()
-    : resolver_(net::CreateSystemHostResolver(NULL)),
+      : resolver_(net::CreateSystemHostResolver(
+            net::HostResolver::kDefaultParallelism)),
         socket_factory_(net::ClientSocketFactory::GetDefaultFactory()) {
   }
 
@@ -65,11 +67,12 @@
   TestCompletionCallback callback;
 
   net::HostResolver::RequestInfo info(server_.kHostName, server_.kOKHTTPSPort);
-  int rv = resolver_->Resolve(info, &addr, NULL, NULL, NULL);
+  int rv = resolver_->Resolve(info, &addr, NULL, NULL, net::BoundNetLog());
   EXPECT_EQ(net::OK, rv);
 
-  net::ClientSocket *transport = new net::TCPClientSocket(addr);
-  rv = transport->Connect(&callback, NULL);
+  net::CapturingNetLog log(net::CapturingNetLog::kUnbounded);
+  net::ClientSocket* transport = new net::TCPClientSocket(addr, &log);
+  rv = transport->Connect(&callback);
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   EXPECT_EQ(net::OK, rv);
@@ -80,15 +83,14 @@
 
   EXPECT_FALSE(sock->IsConnected());
 
-  scoped_refptr<net::LoadLog> log(new net::LoadLog(net::LoadLog::kUnbounded));
-  rv = sock->Connect(&callback, log);
+  rv = sock->Connect(&callback);
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 0, net::LoadLog::TYPE_SSL_CONNECT));
+      log.entries(), 5, net::NetLog::TYPE_SSL_CONNECT));
   if (rv != net::OK) {
     ASSERT_EQ(net::ERR_IO_PENDING, rv);
     EXPECT_FALSE(sock->IsConnected());
     EXPECT_FALSE(net::LogContainsEndEvent(
-        *log, -1, net::LoadLog::TYPE_SSL_CONNECT));
+        log.entries(), -1, net::NetLog::TYPE_SSL_CONNECT));
 
     rv = callback.WaitForResult();
     EXPECT_EQ(net::OK, rv);
@@ -96,7 +98,7 @@
 
   EXPECT_TRUE(sock->IsConnected());
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, -1, net::LoadLog::TYPE_SSL_CONNECT));
+      log.entries(), -1, net::NetLog::TYPE_SSL_CONNECT));
 
   sock->Disconnect();
   EXPECT_FALSE(sock->IsConnected());
@@ -109,11 +111,12 @@
   TestCompletionCallback callback;
 
   net::HostResolver::RequestInfo info(server_.kHostName, server_.kBadHTTPSPort);
-  int rv = resolver_->Resolve(info, &addr, NULL, NULL, NULL);
+  int rv = resolver_->Resolve(info, &addr, NULL, NULL, net::BoundNetLog());
   EXPECT_EQ(net::OK, rv);
 
-  net::ClientSocket *transport = new net::TCPClientSocket(addr);
-  rv = transport->Connect(&callback, NULL);
+  net::CapturingNetLog log(net::CapturingNetLog::kUnbounded);
+  net::ClientSocket* transport = new net::TCPClientSocket(addr, &log);
+  rv = transport->Connect(&callback);
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   EXPECT_EQ(net::OK, rv);
@@ -124,15 +127,14 @@
 
   EXPECT_FALSE(sock->IsConnected());
 
-  scoped_refptr<net::LoadLog> log(new net::LoadLog(net::LoadLog::kUnbounded));
-  rv = sock->Connect(&callback, log);
+  rv = sock->Connect(&callback);
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 0, net::LoadLog::TYPE_SSL_CONNECT));
+      log.entries(), 5, net::NetLog::TYPE_SSL_CONNECT));
   if (rv != net::OK) {
     ASSERT_EQ(net::ERR_IO_PENDING, rv);
     EXPECT_FALSE(sock->IsConnected());
     EXPECT_FALSE(net::LogContainsEndEvent(
-        *log, -1, net::LoadLog::TYPE_SSL_CONNECT));
+        log.entries(), -1, net::NetLog::TYPE_SSL_CONNECT));
 
     rv = callback.WaitForResult();
     EXPECT_EQ(net::ERR_CERT_DATE_INVALID, rv);
@@ -143,7 +145,7 @@
   // leave it connected.
 
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, -1, net::LoadLog::TYPE_SSL_CONNECT));
+      log.entries(), -1, net::NetLog::TYPE_SSL_CONNECT));
 }
 
 TEST_F(SSLClientSocketTest, ConnectMismatched) {
@@ -154,11 +156,12 @@
 
   net::HostResolver::RequestInfo info(server_.kMismatchedHostName,
                                       server_.kOKHTTPSPort);
-  int rv = resolver_->Resolve(info, &addr, NULL, NULL, NULL);
+  int rv = resolver_->Resolve(info, &addr, NULL, NULL, net::BoundNetLog());
   EXPECT_EQ(net::OK, rv);
 
-  net::ClientSocket *transport = new net::TCPClientSocket(addr);
-  rv = transport->Connect(&callback, NULL);
+  net::CapturingNetLog log(net::CapturingNetLog::kUnbounded);
+  net::ClientSocket* transport = new net::TCPClientSocket(addr, &log);
+  rv = transport->Connect(&callback);
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   EXPECT_EQ(net::OK, rv);
@@ -169,15 +172,15 @@
 
   EXPECT_FALSE(sock->IsConnected());
 
-  scoped_refptr<net::LoadLog> log(new net::LoadLog(net::LoadLog::kUnbounded));
-  rv = sock->Connect(&callback, log);
+  rv = sock->Connect(&callback);
+
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 0, net::LoadLog::TYPE_SSL_CONNECT));
+      log.entries(), 5, net::NetLog::TYPE_SSL_CONNECT));
   if (rv != net::ERR_CERT_COMMON_NAME_INVALID) {
     ASSERT_EQ(net::ERR_IO_PENDING, rv);
     EXPECT_FALSE(sock->IsConnected());
     EXPECT_FALSE(net::LogContainsEndEvent(
-        *log, -1, net::LoadLog::TYPE_SSL_CONNECT));
+        log.entries(), -1, net::NetLog::TYPE_SSL_CONNECT));
 
     rv = callback.WaitForResult();
     EXPECT_EQ(net::ERR_CERT_COMMON_NAME_INVALID, rv);
@@ -188,7 +191,7 @@
   // leave it connected.
 
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, -1, net::LoadLog::TYPE_SSL_CONNECT));
+      log.entries(), -1, net::NetLog::TYPE_SSL_CONNECT));
 }
 
 // TODO(wtc): Add unit tests for IsConnectedAndIdle:
@@ -203,14 +206,14 @@
   TestCompletionCallback callback;
 
   net::HostResolver::RequestInfo info(server_.kHostName, server_.kOKHTTPSPort);
-  int rv = resolver_->Resolve(info, &addr, &callback, NULL, NULL);
+  int rv = resolver_->Resolve(info, &addr, &callback, NULL, net::BoundNetLog());
   EXPECT_EQ(net::ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
   EXPECT_EQ(net::OK, rv);
 
-  net::ClientSocket *transport = new net::TCPClientSocket(addr);
-  rv = transport->Connect(&callback, NULL);
+  net::ClientSocket* transport = new net::TCPClientSocket(addr, NULL);
+  rv = transport->Connect(&callback);
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   EXPECT_EQ(net::OK, rv);
@@ -220,7 +223,7 @@
                                              server_.kHostName,
                                              kDefaultSSLConfig));
 
-  rv = sock->Connect(&callback, NULL);
+  rv = sock->Connect(&callback);
   if (rv != net::OK) {
     ASSERT_EQ(net::ERR_IO_PENDING, rv);
 
@@ -265,14 +268,14 @@
   TestCompletionCallback callback2;  // Used for Write only.
 
   net::HostResolver::RequestInfo info(server_.kHostName, server_.kOKHTTPSPort);
-  int rv = resolver_->Resolve(info, &addr, &callback, NULL, NULL);
+  int rv = resolver_->Resolve(info, &addr, &callback, NULL, net::BoundNetLog());
   EXPECT_EQ(net::ERR_IO_PENDING, rv);
 
   rv = callback.WaitForResult();
   EXPECT_EQ(net::OK, rv);
 
-  net::ClientSocket *transport = new net::TCPClientSocket(addr);
-  rv = transport->Connect(&callback, NULL);
+  net::ClientSocket* transport = new net::TCPClientSocket(addr, NULL);
+  rv = transport->Connect(&callback);
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   EXPECT_EQ(net::OK, rv);
@@ -282,7 +285,7 @@
                                              server_.kHostName,
                                              kDefaultSSLConfig));
 
-  rv = sock->Connect(&callback, NULL);
+  rv = sock->Connect(&callback);
   if (rv != net::OK) {
     ASSERT_EQ(net::ERR_IO_PENDING, rv);
 
@@ -327,11 +330,11 @@
   TestCompletionCallback callback;
 
   net::HostResolver::RequestInfo info(server_.kHostName, server_.kOKHTTPSPort);
-  int rv = resolver_->Resolve(info, &addr, NULL, NULL, NULL);
+  int rv = resolver_->Resolve(info, &addr, NULL, NULL, net::BoundNetLog());
   EXPECT_EQ(net::OK, rv);
 
-  net::ClientSocket *transport = new net::TCPClientSocket(addr);
-  rv = transport->Connect(&callback, NULL);
+  net::ClientSocket* transport = new net::TCPClientSocket(addr, NULL);
+  rv = transport->Connect(&callback);
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   EXPECT_EQ(net::OK, rv);
@@ -340,7 +343,7 @@
       socket_factory_->CreateSSLClientSocket(transport,
           server_.kHostName, kDefaultSSLConfig));
 
-  rv = sock->Connect(&callback, NULL);
+  rv = sock->Connect(&callback);
   if (rv != net::OK) {
     ASSERT_EQ(net::ERR_IO_PENDING, rv);
 
@@ -381,11 +384,11 @@
   TestCompletionCallback callback;
 
   net::HostResolver::RequestInfo info(server_.kHostName, server_.kOKHTTPSPort);
-  int rv = resolver_->Resolve(info, &addr, NULL, NULL, NULL);
+  int rv = resolver_->Resolve(info, &addr, NULL, NULL, net::BoundNetLog());
   EXPECT_EQ(net::OK, rv);
 
-  net::ClientSocket *transport = new net::TCPClientSocket(addr);
-  rv = transport->Connect(&callback, NULL);
+  net::ClientSocket* transport = new net::TCPClientSocket(addr, NULL);
+  rv = transport->Connect(&callback);
   if (rv == net::ERR_IO_PENDING)
     rv = callback.WaitForResult();
   EXPECT_EQ(net::OK, rv);
@@ -394,7 +397,7 @@
       socket_factory_->CreateSSLClientSocket(transport,
           server_.kHostName, kDefaultSSLConfig));
 
-  rv = sock->Connect(&callback, NULL);
+  rv = sock->Connect(&callback);
   if (rv != net::OK) {
     ASSERT_EQ(net::ERR_IO_PENDING, rv);
 
@@ -424,3 +427,46 @@
 
   EXPECT_GT(rv, 0);
 }
+
+// Regression test for http://crbug.com/42538
+TEST_F(SSLClientSocketTest, PrematureApplicationData) {
+  net::AddressList addr;
+  TestCompletionCallback callback;
+
+  static const unsigned char application_data[] = {
+    0x17, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00, 0x46, 0x03, 0x01, 0x4b,
+    0xc2, 0xf8, 0xb2, 0xc1, 0x56, 0x42, 0xb9, 0x57, 0x7f, 0xde, 0x87, 0x46,
+    0xf7, 0xa3, 0x52, 0x42, 0x21, 0xf0, 0x13, 0x1c, 0x9c, 0x83, 0x88, 0xd6,
+    0x93, 0x0c, 0xf6, 0x36, 0x30, 0x05, 0x7e, 0x20, 0xb5, 0xb5, 0x73, 0x36,
+    0x53, 0x83, 0x0a, 0xfc, 0x17, 0x63, 0xbf, 0xa0, 0xe4, 0x42, 0x90, 0x0d,
+    0x2f, 0x18, 0x6d, 0x20, 0xd8, 0x36, 0x3f, 0xfc, 0xe6, 0x01, 0xfa, 0x0f,
+    0xa5, 0x75, 0x7f, 0x09, 0x00, 0x04, 0x00, 0x16, 0x03, 0x01, 0x11, 0x57,
+    0x0b, 0x00, 0x11, 0x53, 0x00, 0x11, 0x50, 0x00, 0x06, 0x22, 0x30, 0x82,
+    0x06, 0x1e, 0x30, 0x82, 0x05, 0x06, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02,
+    0x0a
+  };
+
+  // All reads and writes complete synchronously (async=false).
+  net::MockRead data_reads[] = {
+    net::MockRead(false, reinterpret_cast<const char*>(application_data),
+                  arraysize(application_data)),
+    net::MockRead(false, net::OK),
+  };
+
+  net::StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                     NULL, 0);
+
+  net::ClientSocket* transport =
+      new net::MockTCPClientSocket(addr, NULL, &data);
+  int rv = transport->Connect(&callback);
+  if (rv == net::ERR_IO_PENDING)
+    rv = callback.WaitForResult();
+  EXPECT_EQ(net::OK, rv);
+
+  scoped_ptr<net::SSLClientSocket> sock(
+      socket_factory_->CreateSSLClientSocket(
+          transport, server_.kHostName, kDefaultSSLConfig));
+
+  rv = sock->Connect(&callback);
+  EXPECT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv);
+}
diff --git a/net/socket/ssl_client_socket_win.cc b/net/socket/ssl_client_socket_win.cc
index 6e8d86d..0484ebd 100644
--- a/net/socket/ssl_client_socket_win.cc
+++ b/net/socket/ssl_client_socket_win.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -14,10 +14,12 @@
 #include "net/base/cert_verifier.h"
 #include "net/base/connection_type_histograms.h"
 #include "net/base/io_buffer.h"
-#include "net/base/load_log.h"
+#include "net/base/net_log.h"
 #include "net/base/net_errors.h"
 #include "net/base/ssl_cert_request_info.h"
+#include "net/base/ssl_connection_status_flags.h"
 #include "net/base/ssl_info.h"
+#include "net/socket/client_socket_handle.h"
 
 #pragma comment(lib, "secur32.lib")
 
@@ -58,6 +60,7 @@
     case SEC_E_ALGORITHM_MISMATCH:
       return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
     case SEC_E_INVALID_HANDLE:
+    case SEC_E_INVALID_TOKEN:
       return ERR_UNEXPECTED;
     case SEC_E_OK:
       return OK;
@@ -67,13 +70,6 @@
   }
 }
 
-// Returns true if the two CERT_CONTEXTs contain the same certificate.
-bool SameCert(PCCERT_CONTEXT a, PCCERT_CONTEXT b) {
-  return a == b ||
-         (a->cbCertEncoded == b->cbCertEncoded &&
-         memcmp(a->pbCertEncoded, b->pbCertEncoded, b->cbCertEncoded) == 0);
-}
-
 //-----------------------------------------------------------------------------
 
 // A bitmask consisting of these bit flags encodes which versions of the SSL
@@ -298,7 +294,7 @@
 //   64: >= SSL record trailer (16 or 20 have been observed)
 static const int kRecvBufferSize = (5 + 16*1024 + 64);
 
-SSLClientSocketWin::SSLClientSocketWin(ClientSocket* transport_socket,
+SSLClientSocketWin::SSLClientSocketWin(ClientSocketHandle* transport_socket,
                                        const std::string& hostname,
                                        const SSLConfig& ssl_config)
     : ALLOW_THIS_IN_INITIALIZER_LIST(
@@ -328,7 +324,8 @@
       writing_first_token_(false),
       ignore_ok_result_(false),
       renegotiating_(false),
-      need_more_data_(false) {
+      need_more_data_(false),
+      net_log_(transport_socket->socket()->NetLog()) {
   memset(&stream_sizes_, 0, sizeof(stream_sizes_));
   memset(in_buffers_, 0, sizeof(in_buffers_));
   memset(&send_buffer_, 0, sizeof(send_buffer_));
@@ -340,6 +337,8 @@
 }
 
 void SSLClientSocketWin::GetSSLInfo(SSLInfo* ssl_info) {
+  ssl_info->Reset();
+
   if (!server_cert_)
     return;
 
@@ -354,6 +353,9 @@
     // normalized.
     ssl_info->security_bits = connection_info.dwCipherStrength;
   }
+
+  if (ssl_config_.ssl3_fallback)
+    ssl_info->connection_status |= SSL_CONNECTION_SSL3_FALLBACK;
 }
 
 void SSLClientSocketWin::GetSSLCertRequestInfo(
@@ -376,6 +378,8 @@
   // Client certificates of the user are in the "MY" system certificate store.
   HCERTSTORE my_cert_store = CertOpenSystemStore(NULL, L"MY");
   if (!my_cert_store) {
+    LOG(ERROR) << "Could not open the \"MY\" system certificate store: "
+               << GetLastError();
     FreeContextBuffer(issuer_list.aIssuers);
     return;
   }
@@ -417,8 +421,10 @@
       continue;
     }
     scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
-        cert_context2, X509Certificate::SOURCE_LONE_CERT_IMPORT);
+        cert_context2, X509Certificate::SOURCE_LONE_CERT_IMPORT,
+        X509Certificate::OSCertHandles());
     cert_request_info->client_certs.push_back(cert);
+    CertFreeCertificateContext(cert_context2);
   }
 
   FreeContextBuffer(issuer_list.aIssuers);
@@ -433,17 +439,16 @@
   return kNextProtoUnsupported;
 }
 
-int SSLClientSocketWin::Connect(CompletionCallback* callback,
-                                LoadLog* load_log) {
+int SSLClientSocketWin::Connect(CompletionCallback* callback) {
   DCHECK(transport_.get());
   DCHECK(next_state_ == STATE_NONE);
   DCHECK(!user_connect_callback_);
 
-  LoadLog::BeginEvent(load_log, LoadLog::TYPE_SSL_CONNECT);
+  net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT, NULL);
 
   int rv = InitializeSSLContext();
   if (rv != OK) {
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_SSL_CONNECT);
+    net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
     return rv;
   }
 
@@ -452,9 +457,8 @@
   rv = DoLoop(OK);
   if (rv == ERR_IO_PENDING) {
     user_connect_callback_ = callback;
-    load_log_ = load_log;
   } else {
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_SSL_CONNECT);
+    net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
   }
   return rv;
 }
@@ -526,7 +530,7 @@
 
   // Shut down anything that may call us back.
   verifier_.reset();
-  transport_->Disconnect();
+  transport_->socket()->Disconnect();
 
   if (send_buffer_.pvBuffer)
     FreeSendBuffer();
@@ -552,7 +556,7 @@
   // layer (HttpNetworkTransaction) needs to handle a persistent connection
   // closed by the server when we send a request anyway, a false positive in
   // exchange for simpler code is a good trade-off.
-  return completed_handshake() && transport_->IsConnected();
+  return completed_handshake() && transport_->socket()->IsConnected();
 }
 
 bool SSLClientSocketWin::IsConnectedAndIdle() const {
@@ -561,13 +565,14 @@
   // Strictly speaking, we should check if we have received the close_notify
   // alert message from the server, and return false in that case.  Although
   // the close_notify alert message means EOF in the SSL layer, it is just
-  // bytes to the transport layer below, so transport_->IsConnectedAndIdle()
-  // returns the desired false when we receive close_notify.
-  return completed_handshake() && transport_->IsConnectedAndIdle();
+  // bytes to the transport layer below, so
+  // transport_->socket()->IsConnectedAndIdle() returns the desired false
+  // when we receive close_notify.
+  return completed_handshake() && transport_->socket()->IsConnectedAndIdle();
 }
 
-int SSLClientSocketWin::GetPeerName(struct sockaddr* name, socklen_t* namelen) {
-  return transport_->GetPeerName(name, namelen);
+int SSLClientSocketWin::GetPeerAddress(AddressList* address) const {
+  return transport_->socket()->GetPeerAddress(address);
 }
 
 int SSLClientSocketWin::Read(IOBuffer* buf, int buf_len,
@@ -634,28 +639,23 @@
 }
 
 bool SSLClientSocketWin::SetReceiveBufferSize(int32 size) {
-  return transport_->SetReceiveBufferSize(size);
+  return transport_->socket()->SetReceiveBufferSize(size);
 }
 
 bool SSLClientSocketWin::SetSendBufferSize(int32 size) {
-  return transport_->SetSendBufferSize(size);
+  return transport_->socket()->SetSendBufferSize(size);
 }
 
 void SSLClientSocketWin::OnHandshakeIOComplete(int result) {
   int rv = DoLoop(result);
 
-  // The SSL handshake has some round trips.  Any error, other than waiting
-  // for IO, means that we've failed and need to notify the caller.
+  // The SSL handshake has some round trips.  We need to notify the caller of
+  // success or any error, other than waiting for IO.
   if (rv != ERR_IO_PENDING) {
-    LoadLog::EndEvent(load_log_, LoadLog::TYPE_SSL_CONNECT);
-    load_log_ = NULL;
-
-    // If there is no connect callback available to call, it had better be
-    // because we are renegotiating (which occurs because we are in the middle
-    // of a Read when the renegotiation process starts).  We need to inform the
-    // caller of the SSL error, so we complete the Read here.
+    // If there is no connect callback available to call, we are renegotiating
+    // (which occurs because we are in the middle of a Read when the
+    // renegotiation process starts).  So we complete the Read here.
     if (!user_connect_callback_) {
-      DCHECK(renegotiating_);
       CompletionCallback* c = user_read_callback_;
       user_read_callback_ = NULL;
       user_read_buf_ = NULL;
@@ -663,6 +663,7 @@
       c->Run(rv);
       return;
     }
+    net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
     CompletionCallback* c = user_connect_callback_;
     user_connect_callback_ = NULL;
     c->Run(rv);
@@ -757,8 +758,8 @@
   DCHECK(!transport_read_buf_);
   transport_read_buf_ = new IOBuffer(buf_len);
 
-  return transport_->Read(transport_read_buf_, buf_len,
-                          &handshake_io_callback_);
+  return transport_->socket()->Read(transport_read_buf_, buf_len,
+                                    &handshake_io_callback_);
 }
 
 int SSLClientSocketWin::DoHandshakeReadComplete(int result) {
@@ -833,6 +834,12 @@
       &out_flags,
       &expiry);
 
+  if (isc_status_ == SEC_E_INVALID_TOKEN) {
+    // Peer sent us an SSL record type that's invalid during SSL handshake.
+    // TODO(wtc): move this to MapSecurityError after sufficient testing.
+    return ERR_SSL_PROTOCOL_ERROR;
+  }
+
   if (send_buffer_.cbBuffer != 0 &&
       (isc_status_ == SEC_E_OK ||
        isc_status_ == SEC_I_CONTINUE_NEEDED ||
@@ -882,6 +889,13 @@
   if (isc_status_ == SEC_I_INCOMPLETE_CREDENTIALS)
     return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
 
+  if (isc_status_ == SEC_I_NO_RENEGOTIATION) {
+    // Received a no_renegotiation alert message.  Although this is just a
+    // warning, SChannel doesn't seem to allow us to continue after this
+    // point, so we have to return an error.  See http://crbug.com/36835.
+    return ERR_SSL_NO_RENEGOTIATION;
+  }
+
   DCHECK(isc_status_ == SEC_I_CONTINUE_NEEDED);
   if (in_buffers_[1].BufferType == SECBUFFER_EXTRA) {
     memmove(recv_buffer_.get(),
@@ -911,8 +925,8 @@
   transport_write_buf_ = new IOBuffer(buf_len);
   memcpy(transport_write_buf_->data(), buf, buf_len);
 
-  return transport_->Write(transport_write_buf_, buf_len,
-                           &handshake_io_callback_);
+  return transport_->socket()->Write(transport_write_buf_, buf_len,
+                                     &handshake_io_callback_);
 }
 
 int SSLClientSocketWin::DoHandshakeWriteComplete(int result) {
@@ -977,7 +991,8 @@
       ssl_config_.IsAllowedBadCert(server_cert_))
     result = OK;
 
-  LogConnectionTypeMetrics(result >= 0);
+  if (result == OK)
+    LogConnectionTypeMetrics();
   if (renegotiating_) {
     DidCompleteRenegotiation();
     return result;
@@ -1005,7 +1020,8 @@
     DCHECK(!transport_read_buf_);
     transport_read_buf_ = new IOBuffer(buf_len);
 
-    rv = transport_->Read(transport_read_buf_, buf_len, &read_callback_);
+    rv = transport_->socket()->Read(transport_read_buf_, buf_len,
+                                    &read_callback_);
     if (rv != ERR_IO_PENDING)
       rv = DoPayloadReadComplete(rv);
     if (rv <= 0)
@@ -1240,7 +1256,8 @@
   transport_write_buf_ = new IOBuffer(buf_len);
   memcpy(transport_write_buf_->data(), buf, buf_len);
 
-  int rv = transport_->Write(transport_write_buf_, buf_len, &write_callback_);
+  int rv = transport_->socket()->Write(transport_write_buf_, buf_len,
+                                       &write_callback_);
   if (rv != ERR_IO_PENDING)
     rv = DoPayloadWriteComplete(rv);
   return rv;
@@ -1276,7 +1293,8 @@
   // The user had a read in progress, which was usurped by the renegotiation.
   // Restart the read sequence.
   next_state_ = STATE_COMPLETED_HANDSHAKE;
-  DCHECK(result == OK);
+  if (result != OK)
+    return result;
   return DoPayloadRead();
 }
 
@@ -1296,39 +1314,43 @@
     return MapSecurityError(status);
   }
   if (renegotiating_ &&
-      SameCert(server_cert_->os_cert_handle(), server_cert_handle)) {
+      X509Certificate::IsSameOSCert(server_cert_->os_cert_handle(),
+                                    server_cert_handle)) {
     // We already verified the server certificate.  Either it is good or the
     // user has accepted the certificate error.
-    CertFreeCertificateContext(server_cert_handle);
     DidCompleteRenegotiation();
   } else {
     server_cert_ = X509Certificate::CreateFromHandle(
-        server_cert_handle, X509Certificate::SOURCE_FROM_NETWORK);
+        server_cert_handle, X509Certificate::SOURCE_FROM_NETWORK,
+        X509Certificate::OSCertHandles());
 
     next_state_ = STATE_VERIFY_CERT;
   }
+  CertFreeCertificateContext(server_cert_handle);
   return OK;
 }
 
 // Called when a renegotiation is completed.  |result| is the verification
 // result of the server certificate received during renegotiation.
 void SSLClientSocketWin::DidCompleteRenegotiation() {
+  DCHECK(!user_connect_callback_);
+  DCHECK(user_read_callback_);
   renegotiating_ = false;
   next_state_ = STATE_COMPLETED_RENEGOTIATION;
 }
 
-void SSLClientSocketWin::LogConnectionTypeMetrics(bool success) const {
-  UpdateConnectionTypeHistograms(CONNECTION_SSL, success);
+void SSLClientSocketWin::LogConnectionTypeMetrics() const {
+  UpdateConnectionTypeHistograms(CONNECTION_SSL);
   if (server_cert_verify_result_.has_md5)
-    UpdateConnectionTypeHistograms(CONNECTION_SSL_MD5, success);
+    UpdateConnectionTypeHistograms(CONNECTION_SSL_MD5);
   if (server_cert_verify_result_.has_md2)
-    UpdateConnectionTypeHistograms(CONNECTION_SSL_MD2, success);
+    UpdateConnectionTypeHistograms(CONNECTION_SSL_MD2);
   if (server_cert_verify_result_.has_md4)
-    UpdateConnectionTypeHistograms(CONNECTION_SSL_MD4, success);
+    UpdateConnectionTypeHistograms(CONNECTION_SSL_MD4);
   if (server_cert_verify_result_.has_md5_ca)
-    UpdateConnectionTypeHistograms(CONNECTION_SSL_MD5_CA, success);
+    UpdateConnectionTypeHistograms(CONNECTION_SSL_MD5_CA);
   if (server_cert_verify_result_.has_md2_ca)
-    UpdateConnectionTypeHistograms(CONNECTION_SSL_MD2_CA, success);
+    UpdateConnectionTypeHistograms(CONNECTION_SSL_MD2_CA);
 }
 
 void SSLClientSocketWin::FreeSendBuffer() {
diff --git a/net/socket/ssl_client_socket_win.h b/net/socket/ssl_client_socket_win.h
index c5d6cf7..b4a0bad 100644
--- a/net/socket/ssl_client_socket_win.h
+++ b/net/socket/ssl_client_socket_win.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -16,13 +16,15 @@
 #include "base/scoped_ptr.h"
 #include "net/base/cert_verify_result.h"
 #include "net/base/completion_callback.h"
+#include "net/base/net_log.h"
 #include "net/base/ssl_config_service.h"
 #include "net/socket/ssl_client_socket.h"
 
 namespace net {
 
 class CertVerifier;
-class LoadLog;
+class ClientSocketHandle;
+class BoundNetLog;
 
 // An SSL client socket implemented with the Windows Schannel.
 class SSLClientSocketWin : public SSLClientSocket {
@@ -31,7 +33,7 @@
   // The given hostname will be compared with the name(s) in the server's
   // certificate during the SSL handshake.  ssl_config specifies the SSL
   // settings.
-  SSLClientSocketWin(ClientSocket* transport_socket,
+  SSLClientSocketWin(ClientSocketHandle* transport_socket,
                      const std::string& hostname,
                      const SSLConfig& ssl_config);
   ~SSLClientSocketWin();
@@ -42,11 +44,12 @@
   virtual NextProtoStatus GetNextProto(std::string* proto);
 
   // ClientSocket methods:
-  virtual int Connect(CompletionCallback* callback, LoadLog* load_log);
+  virtual int Connect(CompletionCallback* callback);
   virtual void Disconnect();
   virtual bool IsConnected() const;
   virtual bool IsConnectedAndIdle() const;
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen);
+  virtual int GetPeerAddress(AddressList* address) const;
+  virtual const BoundNetLog& NetLog() const { return net_log_; }
 
   // Socket methods:
   virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
@@ -86,7 +89,7 @@
   int DidCallInitializeSecurityContext();
   int DidCompleteHandshake();
   void DidCompleteRenegotiation();
-  void LogConnectionTypeMetrics(bool success) const;
+  void LogConnectionTypeMetrics() const;
   void FreeSendBuffer();
 
   // Internal callbacks as async operations complete.
@@ -94,7 +97,7 @@
   CompletionCallbackImpl<SSLClientSocketWin> read_callback_;
   CompletionCallbackImpl<SSLClientSocketWin> write_callback_;
 
-  scoped_ptr<ClientSocket> transport_;
+  scoped_ptr<ClientSocketHandle> transport_;
   std::string hostname_;
   SSLConfig ssl_config_;
 
@@ -164,8 +167,6 @@
   // state.
   bool writing_first_token_;
 
-  bool completed_handshake_;
-
   // Only used in the STATE_HANDSHAKE_READ_COMPLETE and
   // STATE_PAYLOAD_READ_COMPLETE states.  True if a 'result' argument of OK
   // should be ignored, to prevent it from being interpreted as EOF.
@@ -183,7 +184,7 @@
   // True when the decrypter needs more data in order to decrypt.
   bool need_more_data_;
 
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
 };
 
 }  // namespace net
diff --git a/net/socket/tcp_client_socket_libevent.cc b/net/socket/tcp_client_socket_libevent.cc
index 2c1c73d..93593d9 100644
--- a/net/socket/tcp_client_socket_libevent.cc
+++ b/net/socket/tcp_client_socket_libevent.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -9,14 +9,20 @@
 #include <netdb.h>
 #include <sys/socket.h>
 #include <netinet/tcp.h>
+#if defined(OS_POSIX)
+#include <netinet/in.h>
+#endif
 
 #include "base/eintr_wrapper.h"
+#include "base/logging.h"
 #include "base/message_loop.h"
+#include "base/stats_counters.h"
 #include "base/string_util.h"
-#include "base/trace_event.h"
+#include "net/base/address_list_net_log_param.h"
 #include "net/base/io_buffer.h"
-#include "net/base/load_log.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
 #if defined(USE_SYSTEM_LIBEVENT)
 #include <event.h>
 #else
@@ -29,15 +35,6 @@
 
 const int kInvalidSocket = -1;
 
-// Return 0 on success, -1 on failure.
-// Too small a function to bother putting in a library?
-int SetNonBlocking(int fd) {
-  int flags = fcntl(fd, F_GETFL, 0);
-  if (-1 == flags)
-    return flags;
-  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-}
-
 // DisableNagle turns off buffering in the kernel. By default, TCP sockets will
 // wait up to 200ms for more data to complete a packet before transmitting.
 // After calling this function, the kernel will not wait. See TCP_NODELAY in
@@ -98,142 +95,180 @@
   }
 }
 
-// Given os_error, an errno from a connect() attempt, returns true if
-// connect() should be retried with another address.
-bool ShouldTryNextAddress(int os_error) {
-  switch (os_error) {
-    case EADDRNOTAVAIL:
-    case EAFNOSUPPORT:
-    case ECONNREFUSED:
-    case ECONNRESET:
-    case EACCES:
-    case EPERM:
-    case ENETUNREACH:
-    case EHOSTUNREACH:
-    case ENETDOWN:
-    case ETIMEDOUT:
-      return true;
-    default:
-      return false;
-  }
-}
-
 }  // namespace
 
 //-----------------------------------------------------------------------------
 
-TCPClientSocketLibevent::TCPClientSocketLibevent(const AddressList& addresses)
+TCPClientSocketLibevent::TCPClientSocketLibevent(const AddressList& addresses,
+                                                 net::NetLog* net_log)
     : socket_(kInvalidSocket),
       addresses_(addresses),
-      current_ai_(addresses_.head()),
-      waiting_connect_(false),
+      current_ai_(NULL),
       read_watcher_(this),
       write_watcher_(this),
       read_callback_(NULL),
-      write_callback_(NULL) {
+      write_callback_(NULL),
+      next_connect_state_(CONNECT_STATE_NONE),
+      connect_os_error_(0),
+      net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)) {
+  net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, NULL);
 }
 
 TCPClientSocketLibevent::~TCPClientSocketLibevent() {
   Disconnect();
+  net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL);
 }
 
-int TCPClientSocketLibevent::Connect(CompletionCallback* callback,
-                                     LoadLog* load_log) {
+int TCPClientSocketLibevent::Connect(CompletionCallback* callback) {
+  DCHECK(CalledOnValidThread());
+
   // If already connected, then just return OK.
   if (socket_ != kInvalidSocket)
     return OK;
 
-  DCHECK(!waiting_connect_);
-  DCHECK(!load_log_);
+  static StatsCounter connects("tcp.connect");
+  connects.Increment();
 
-  TRACE_EVENT_BEGIN("socket.connect", this, "");
+  DCHECK(!waiting_connect());
 
-  LoadLog::BeginEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
+  net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT,
+                      new AddressListNetLogParam(addresses_));
 
-  int rv = DoConnect();
+  // We will try to connect to each address in addresses_. Start with the
+  // first one in the list.
+  next_connect_state_ = CONNECT_STATE_CONNECT;
+  current_ai_ = addresses_.head();
 
+  int rv = DoConnectLoop(OK);
   if (rv == ERR_IO_PENDING) {
     // Synchronous operation not supported.
     DCHECK(callback);
-
-    load_log_ = load_log;
-    waiting_connect_ = true;
     write_callback_ = callback;
   } else {
-    TRACE_EVENT_END("socket.connect", this, "");
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
+    LogConnectCompletion(rv);
   }
 
   return rv;
 }
 
+int TCPClientSocketLibevent::DoConnectLoop(int result) {
+  DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE);
+
+  int rv = result;
+  do {
+    ConnectState state = next_connect_state_;
+    next_connect_state_ = CONNECT_STATE_NONE;
+    switch (state) {
+      case CONNECT_STATE_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoConnect();
+        break;
+      case CONNECT_STATE_CONNECT_COMPLETE:
+        rv = DoConnectComplete(rv);
+        break;
+      default:
+        LOG(DFATAL) << "bad state";
+        rv = ERR_UNEXPECTED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE);
+
+  return rv;
+}
+
 int TCPClientSocketLibevent::DoConnect() {
-  while (true) {
-    DCHECK(current_ai_);
+  DCHECK(current_ai_);
 
-    int rv = CreateSocket(current_ai_);
-    if (rv != OK)
-      return rv;
+  DCHECK_EQ(0, connect_os_error_);
 
-    if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr,
-                              static_cast<int>(current_ai_->ai_addrlen)))) {
-      // Connected without waiting!
-      return OK;
-    }
+  net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
+                      new NetLogStringParameter(
+                          "address", NetAddressToStringWithPort(current_ai_)));
 
-    int os_error = errno;
-    if (os_error == EINPROGRESS)
-      break;
+  next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
 
-    close(socket_);
-    socket_ = kInvalidSocket;
+  // Create a non-blocking socket.
+  connect_os_error_ = CreateSocket(current_ai_);
+  if (connect_os_error_)
+    return MapPosixError(connect_os_error_);
 
-    if (current_ai_->ai_next && ShouldTryNextAddress(os_error)) {
-      // connect() can fail synchronously for an address even on a
-      // non-blocking socket.  As an example, this can happen when there is
-      // no route to the host.  Retry using the next address in the list.
-      current_ai_ = current_ai_->ai_next;
-    } else {
-      DLOG(INFO) << "connect failed: " << os_error;
-      return MapConnectError(os_error);
-    }
+  // Connect the socket.
+  if (!HANDLE_EINTR(connect(socket_, current_ai_->ai_addr,
+                            static_cast<int>(current_ai_->ai_addrlen)))) {
+    // Connected without waiting!
+    return OK;
   }
 
-  // Initialize write_socket_watcher_ and link it to our MessagePump.
-  // POLLOUT is set if the connection is established.
-  // POLLIN is set if the connection fails.
+  // Check if the connect() failed synchronously.
+  connect_os_error_ = errno;
+  if (connect_os_error_ != EINPROGRESS)
+    return MapPosixError(connect_os_error_);
+
+  // Otherwise the connect() is going to complete asynchronously, so watch
+  // for its completion.
   if (!MessageLoopForIO::current()->WatchFileDescriptor(
           socket_, true, MessageLoopForIO::WATCH_WRITE, &write_socket_watcher_,
           &write_watcher_)) {
-    DLOG(INFO) << "WatchFileDescriptor failed: " << errno;
-    close(socket_);
-    socket_ = kInvalidSocket;
-    return MapPosixError(errno);
+    connect_os_error_ = errno;
+    DLOG(INFO) << "WatchFileDescriptor failed: " << connect_os_error_;
+    return MapPosixError(connect_os_error_);
   }
 
   return ERR_IO_PENDING;
 }
 
+int TCPClientSocketLibevent::DoConnectComplete(int result) {
+  // Log the end of this attempt (and any OS error it threw).
+  int os_error = connect_os_error_;
+  connect_os_error_ = 0;
+  scoped_refptr<NetLog::EventParameters> params;
+  if (result != OK)
+    params = new NetLogIntegerParameter("os_error", os_error);
+  net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, params);
+
+  write_socket_watcher_.StopWatchingFileDescriptor();
+
+  if (result == OK)
+    return OK;  // Done!
+
+  // Close whatever partially connected socket we currently have.
+  DoDisconnect();
+
+  // Try to fall back to the next address in the list.
+  if (current_ai_->ai_next) {
+    next_connect_state_ = CONNECT_STATE_CONNECT;
+    current_ai_ = current_ai_->ai_next;
+    return OK;
+  }
+
+  // Otherwise there is nothing to fall back to, so give up.
+  return result;
+}
+
 void TCPClientSocketLibevent::Disconnect() {
+  DCHECK(CalledOnValidThread());
+
+  DoDisconnect();
+  current_ai_ = NULL;
+}
+
+void TCPClientSocketLibevent::DoDisconnect() {
   if (socket_ == kInvalidSocket)
     return;
 
-  TRACE_EVENT_INSTANT("socket.disconnect", this, "");
-
   bool ok = read_socket_watcher_.StopWatchingFileDescriptor();
   DCHECK(ok);
   ok = write_socket_watcher_.StopWatchingFileDescriptor();
   DCHECK(ok);
-  close(socket_);
+  if (HANDLE_EINTR(close(socket_)) < 0)
+    PLOG(ERROR) << "close";
   socket_ = kInvalidSocket;
-  waiting_connect_ = false;
-
-  // Reset for next time.
-  current_ai_ = addresses_.head();
 }
 
 bool TCPClientSocketLibevent::IsConnected() const {
-  if (socket_ == kInvalidSocket || waiting_connect_)
+  DCHECK(CalledOnValidThread());
+
+  if (socket_ == kInvalidSocket || waiting_connect())
     return false;
 
   // Check if connection is alive.
@@ -248,7 +283,9 @@
 }
 
 bool TCPClientSocketLibevent::IsConnectedAndIdle() const {
-  if (socket_ == kInvalidSocket || waiting_connect_)
+  DCHECK(CalledOnValidThread());
+
+  if (socket_ == kInvalidSocket || waiting_connect())
     return false;
 
   // Check if connection is alive and we haven't received any data
@@ -266,17 +303,21 @@
 int TCPClientSocketLibevent::Read(IOBuffer* buf,
                                   int buf_len,
                                   CompletionCallback* callback) {
+  DCHECK(CalledOnValidThread());
   DCHECK_NE(kInvalidSocket, socket_);
-  DCHECK(!waiting_connect_);
+  DCHECK(!waiting_connect());
   DCHECK(!read_callback_);
   // Synchronous operation not supported
   DCHECK(callback);
   DCHECK_GT(buf_len, 0);
 
-  TRACE_EVENT_BEGIN("socket.read", this, "");
   int nread = HANDLE_EINTR(read(socket_, buf->data(), buf_len));
   if (nread >= 0) {
-    TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", nread));
+    static StatsCounter read_bytes("tcp.read_bytes");
+    read_bytes.Add(nread);
+
+    net_log_.AddEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED,
+                      new NetLogIntegerParameter("num_bytes", nread));
     return nread;
   }
   if (errno != EAGAIN && errno != EWOULDBLOCK) {
@@ -300,17 +341,20 @@
 int TCPClientSocketLibevent::Write(IOBuffer* buf,
                                    int buf_len,
                                    CompletionCallback* callback) {
+  DCHECK(CalledOnValidThread());
   DCHECK_NE(kInvalidSocket, socket_);
-  DCHECK(!waiting_connect_);
+  DCHECK(!waiting_connect());
   DCHECK(!write_callback_);
   // Synchronous operation not supported
   DCHECK(callback);
   DCHECK_GT(buf_len, 0);
 
-  TRACE_EVENT_BEGIN("socket.write", this, "");
   int nwrite = HANDLE_EINTR(write(socket_, buf->data(), buf_len));
   if (nwrite >= 0) {
-    TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", nwrite));
+    static StatsCounter write_bytes("tcp.write_bytes");
+    write_bytes.Add(nwrite);
+    net_log_.AddEvent(NetLog::TYPE_SOCKET_BYTES_SENT,
+                      new NetLogIntegerParameter("num_bytes", nwrite));
     return nwrite;
   }
   if (errno != EAGAIN && errno != EWOULDBLOCK)
@@ -323,7 +367,6 @@
     return MapPosixError(errno);
   }
 
-
   write_buf_ = buf;
   write_buf_len_ = buf_len;
   write_callback_ = callback;
@@ -331,6 +374,7 @@
 }
 
 bool TCPClientSocketLibevent::SetReceiveBufferSize(int32 size) {
+  DCHECK(CalledOnValidThread());
   int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
       reinterpret_cast<const char*>(&size),
       sizeof(size));
@@ -339,6 +383,7 @@
 }
 
 bool TCPClientSocketLibevent::SetSendBufferSize(int32 size) {
+  DCHECK(CalledOnValidThread());
   int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
       reinterpret_cast<const char*>(&size),
       sizeof(size));
@@ -350,10 +395,10 @@
 int TCPClientSocketLibevent::CreateSocket(const addrinfo* ai) {
   socket_ = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
   if (socket_ == kInvalidSocket)
-    return MapPosixError(errno);
+    return errno;
 
   if (SetNonBlocking(socket_)) {
-    const int err = MapPosixError(errno);
+    const int err = errno;
     close(socket_);
     socket_ = kInvalidSocket;
     return err;
@@ -363,7 +408,14 @@
   // tcp_client_socket_win.cc after searching for "NODELAY".
   DisableNagle(socket_);  // If DisableNagle fails, we don't care.
 
-  return OK;
+  return 0;
+}
+
+void TCPClientSocketLibevent::LogConnectCompletion(int net_error) {
+  scoped_refptr<NetLog::EventParameters> params;
+  if (net_error != OK)
+    params = new NetLogIntegerParameter("net_error", net_error);
+  net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT, params);
 }
 
 void TCPClientSocketLibevent::DoReadCallback(int rv) {
@@ -387,39 +439,25 @@
 }
 
 void TCPClientSocketLibevent::DidCompleteConnect() {
-  int result = ERR_UNEXPECTED;
+  DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
 
-  // Check to see if connect succeeded
+  // Get the error that connect() completed with.
   int os_error = 0;
   socklen_t len = sizeof(os_error);
   if (getsockopt(socket_, SOL_SOCKET, SO_ERROR, &os_error, &len) < 0)
     os_error = errno;
 
+  // TODO(eroman): Is this check really necessary?
   if (os_error == EINPROGRESS || os_error == EALREADY) {
     NOTREACHED();  // This indicates a bug in libevent or our code.
-    result = ERR_IO_PENDING;
-  } else if (current_ai_->ai_next && ShouldTryNextAddress(os_error)) {
-    // This address failed, try next one in list.
-    const addrinfo* next = current_ai_->ai_next;
-    Disconnect();
-    current_ai_ = next;
-    scoped_refptr<LoadLog> load_log;
-    load_log.swap(load_log_);
-    TRACE_EVENT_END("socket.connect", this, "");
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
-    result = Connect(write_callback_, load_log);
-  } else {
-    result = MapConnectError(os_error);
-    bool ok = write_socket_watcher_.StopWatchingFileDescriptor();
-    DCHECK(ok);
-    waiting_connect_ = false;
-    TRACE_EVENT_END("socket.connect", this, "");
-    LoadLog::EndEvent(load_log_, LoadLog::TYPE_TCP_CONNECT);
-    load_log_ = NULL;
+    return;
   }
 
-  if (result != ERR_IO_PENDING) {
-    DoWriteCallback(result);
+  connect_os_error_ = os_error;
+  int rv = DoConnectLoop(MapConnectError(os_error));
+  if (rv != ERR_IO_PENDING) {
+    LogConnectCompletion(rv);
+    DoWriteCallback(rv);
   }
 }
 
@@ -430,9 +468,9 @@
 
   int result;
   if (bytes_transferred >= 0) {
-    TRACE_EVENT_END("socket.read", this,
-                    StringPrintf("%d bytes", bytes_transferred));
     result = bytes_transferred;
+    net_log_.AddEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED,
+                      new NetLogIntegerParameter("num_bytes", result));
   } else {
     result = MapPosixError(errno);
   }
@@ -454,8 +492,8 @@
   int result;
   if (bytes_transferred >= 0) {
     result = bytes_transferred;
-    TRACE_EVENT_END("socket.write", this,
-                    StringPrintf("%d bytes", bytes_transferred));
+    net_log_.AddEvent(NetLog::TYPE_SOCKET_BYTES_SENT,
+                      new NetLogIntegerParameter("num_bytes", result));
   } else {
     result = MapPosixError(errno);
   }
@@ -468,9 +506,13 @@
   }
 }
 
-int TCPClientSocketLibevent::GetPeerName(struct sockaddr* name,
-                                         socklen_t* namelen) {
-  return ::getpeername(socket_, name, namelen);
+int TCPClientSocketLibevent::GetPeerAddress(AddressList* address) const {
+  DCHECK(CalledOnValidThread());
+  DCHECK(address);
+  if (!current_ai_)
+    return ERR_UNEXPECTED;
+  address->Copy(current_ai_, false);
+  return OK;
 }
 
 }  // namespace net
diff --git a/net/socket/tcp_client_socket_libevent.h b/net/socket/tcp_client_socket_libevent.h
index b054805..f0bed43 100644
--- a/net/socket/tcp_client_socket_libevent.h
+++ b/net/socket/tcp_client_socket_libevent.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -6,34 +6,38 @@
 #define NET_SOCKET_TCP_CLIENT_SOCKET_LIBEVENT_H_
 
 #include "base/message_loop.h"
+#include "base/non_thread_safe.h"
 #include "base/ref_counted.h"
 #include "base/scoped_ptr.h"
 #include "net/base/address_list.h"
 #include "net/base/completion_callback.h"
+#include "net/base/net_log.h"
 #include "net/socket/client_socket.h"
 
 struct event;  // From libevent
 
 namespace net {
 
-class LoadLog;
+class BoundNetLog;
 
 // A client socket that uses TCP as the transport layer.
-class TCPClientSocketLibevent : public ClientSocket {
+class TCPClientSocketLibevent : public ClientSocket, NonThreadSafe {
  public:
   // The IP address(es) and port number to connect to.  The TCP socket will try
   // each IP address in the list until it succeeds in establishing a
   // connection.
-  explicit TCPClientSocketLibevent(const AddressList& addresses);
+  explicit TCPClientSocketLibevent(const AddressList& addresses,
+                                   net::NetLog* net_log);
 
   virtual ~TCPClientSocketLibevent();
 
   // ClientSocket methods:
-  virtual int Connect(CompletionCallback* callback, LoadLog* load_log);
+  virtual int Connect(CompletionCallback* callback);
   virtual void Disconnect();
   virtual bool IsConnected() const;
   virtual bool IsConnectedAndIdle() const;
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen);
+  virtual int GetPeerAddress(AddressList* address) const;
+  virtual const BoundNetLog& NetLog() const { return net_log_; }
 
   // Socket methods:
   // Multiple outstanding requests are not supported.
@@ -44,6 +48,13 @@
   virtual bool SetSendBufferSize(int32 size);
 
  private:
+  // State machine for connecting the socket.
+  enum ConnectState {
+    CONNECT_STATE_CONNECT,
+    CONNECT_STATE_CONNECT_COMPLETE,
+    CONNECT_STATE_NONE,
+  };
+
   class ReadWatcher : public MessageLoopForIO::Watcher {
    public:
     explicit ReadWatcher(TCPClientSocketLibevent* socket) : socket_(socket) {}
@@ -72,7 +83,7 @@
     virtual void OnFileCanReadWithoutBlocking(int /* fd */) {}
 
     virtual void OnFileCanWriteWithoutBlocking(int /* fd */) {
-      if (socket_->waiting_connect_) {
+      if (socket_->waiting_connect()) {
         socket_->DidCompleteConnect();
       } else if (socket_->write_callback_) {
         socket_->DidCompleteWrite();
@@ -85,8 +96,14 @@
     DISALLOW_COPY_AND_ASSIGN(WriteWatcher);
   };
 
-  // Performs the actual connect().  Returns a net error code.
+  // State machine used by Connect().
+  int DoConnectLoop(int result);
   int DoConnect();
+  int DoConnectComplete(int result);
+
+  // Helper used by Disconnect(), which disconnects minus the logging and
+  // resetting of current_ai_.
+  void DoDisconnect();
 
   void DoReadCallback(int rv);
   void DoWriteCallback(int rv);
@@ -94,8 +111,17 @@
   void DidCompleteWrite();
   void DidCompleteConnect();
 
+  // Returns true if a Connect() is in progress.
+  bool waiting_connect() const {
+    return next_connect_state_ != CONNECT_STATE_NONE;
+  }
+
+  // Returns the OS error code (or 0 on success).
   int CreateSocket(const struct addrinfo* ai);
 
+  // Helper to add a TCP_CONNECT (end) event to the NetLog.
+  void LogConnectCompletion(int net_error);
+
   int socket_;
 
   // The list of addresses we should try in order to establish a connection.
@@ -104,9 +130,6 @@
   // Where we are in above list, or NULL if all addrinfos have been tried.
   const struct addrinfo* current_ai_;
 
-  // Whether we're currently waiting for connect() to complete
-  bool waiting_connect_;
-
   // The socket's libevent wrappers
   MessageLoopForIO::FileDescriptorWatcher read_socket_watcher_;
   MessageLoopForIO::FileDescriptorWatcher write_socket_watcher_;
@@ -129,7 +152,13 @@
   // External callback; called when write is complete.
   CompletionCallback* write_callback_;
 
-  scoped_refptr<LoadLog> load_log_;
+  // The next state for the Connect() state machine.
+  ConnectState next_connect_state_;
+
+  // The OS error that CONNECT_STATE_CONNECT last completed with.
+  int connect_os_error_;
+
+  BoundNetLog net_log_;
 
   DISALLOW_COPY_AND_ASSIGN(TCPClientSocketLibevent);
 };
diff --git a/net/socket/tcp_client_socket_pool.cc b/net/socket/tcp_client_socket_pool.cc
index 3ea6700..2caa3fb 100644
--- a/net/socket/tcp_client_socket_pool.cc
+++ b/net/socket/tcp_client_socket_pool.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -9,7 +9,7 @@
 #include "base/message_loop.h"
 #include "base/string_util.h"
 #include "base/time.h"
-#include "net/base/load_log.h"
+#include "net/base/net_log.h"
 #include "net/base/net_errors.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
@@ -20,6 +20,23 @@
 
 namespace net {
 
+TCPSocketParams::TCPSocketParams(const HostPortPair& host_port_pair,
+                                 RequestPriority priority, const GURL& referrer,
+                                 bool disable_resolver_cache)
+    : destination_(host_port_pair.host, host_port_pair.port) {
+  Initialize(priority, referrer, disable_resolver_cache);
+}
+
+// TODO(willchan): Update all unittests so we don't need this.
+TCPSocketParams::TCPSocketParams(const std::string& host, int port,
+                                 RequestPriority priority, const GURL& referrer,
+                                 bool disable_resolver_cache)
+    : destination_(host, port) {
+  Initialize(priority, referrer, disable_resolver_cache);
+}
+
+TCPSocketParams::~TCPSocketParams() {}
+
 // TCPConnectJobs will time out after this many seconds.  Note this is the total
 // time, including both host resolution and TCP connect() times.
 //
@@ -29,18 +46,19 @@
 // timeout. Even worse, the per-connect timeout threshold varies greatly
 // between systems (anywhere from 20 seconds to 190 seconds).
 // See comment #12 at http://crbug.com/23364 for specifics.
-static const int kTCPConnectJobTimeoutInSeconds = 240; // 4 minutes.
+static const int kTCPConnectJobTimeoutInSeconds = 240;  // 4 minutes.
 
 TCPConnectJob::TCPConnectJob(
     const std::string& group_name,
-    const HostResolver::RequestInfo& resolve_info,
+    const scoped_refptr<TCPSocketParams>& params,
     base::TimeDelta timeout_duration,
     ClientSocketFactory* client_socket_factory,
     HostResolver* host_resolver,
     Delegate* delegate,
-    LoadLog* load_log)
-    : ConnectJob(group_name, timeout_duration, delegate, load_log),
-      resolve_info_(resolve_info),
+    NetLog* net_log)
+    : ConnectJob(group_name, timeout_duration, delegate,
+                 BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
+      params_(params),
       client_socket_factory_(client_socket_factory),
       ALLOW_THIS_IN_INITIALIZER_LIST(
           callback_(this,
@@ -112,7 +130,8 @@
 
 int TCPConnectJob::DoResolveHost() {
   next_state_ = kStateResolveHostComplete;
-  return resolver_.Resolve(resolve_info_, &addresses_, &callback_, load_log());
+  return resolver_.Resolve(params_->destination(), &addresses_, &callback_,
+                           net_log());
 }
 
 int TCPConnectJob::DoResolveHostComplete(int result) {
@@ -123,9 +142,10 @@
 
 int TCPConnectJob::DoTCPConnect() {
   next_state_ = kStateTCPConnectComplete;
-  set_socket(client_socket_factory_->CreateTCPClientSocket(addresses_));
+  set_socket(client_socket_factory_->CreateTCPClientSocket(
+        addresses_, net_log().net_log()));
   connect_start_time_ = base::TimeTicks::Now();
-  return socket()->Connect(&callback_, load_log());
+  return socket()->Connect(&callback_);
 }
 
 int TCPConnectJob::DoTCPConnectComplete(int result) {
@@ -158,60 +178,75 @@
 ConnectJob* TCPClientSocketPool::TCPConnectJobFactory::NewConnectJob(
     const std::string& group_name,
     const PoolBase::Request& request,
-    ConnectJob::Delegate* delegate,
-    LoadLog* load_log) const {
-  return new TCPConnectJob(
-      group_name, request.params(),
-      base::TimeDelta::FromSeconds(kTCPConnectJobTimeoutInSeconds),
-      client_socket_factory_, host_resolver_, delegate, load_log);
+    ConnectJob::Delegate* delegate) const {
+  return new TCPConnectJob(group_name, request.params(), ConnectionTimeout(),
+                           client_socket_factory_, host_resolver_, delegate,
+                           net_log_);
+}
+
+base::TimeDelta
+    TCPClientSocketPool::TCPConnectJobFactory::ConnectionTimeout() const {
+  return base::TimeDelta::FromSeconds(kTCPConnectJobTimeoutInSeconds);
 }
 
 TCPClientSocketPool::TCPClientSocketPool(
     int max_sockets,
     int max_sockets_per_group,
+    const scoped_refptr<ClientSocketPoolHistograms>& histograms,
     HostResolver* host_resolver,
     ClientSocketFactory* client_socket_factory,
-    NetworkChangeNotifier* network_change_notifier)
-    : base_(max_sockets, max_sockets_per_group,
-            base::TimeDelta::FromSeconds(kUnusedIdleSocketTimeout),
+    NetLog* net_log)
+    : base_(max_sockets, max_sockets_per_group, histograms,
+            base::TimeDelta::FromSeconds(
+                ClientSocketPool::unused_idle_socket_timeout()),
             base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
-            new TCPConnectJobFactory(client_socket_factory, host_resolver),
-            network_change_notifier) {}
+            new TCPConnectJobFactory(client_socket_factory,
+                                     host_resolver, net_log)) {
+  base_.EnableBackupJobs();
+}
 
 TCPClientSocketPool::~TCPClientSocketPool() {}
 
 int TCPClientSocketPool::RequestSocket(
     const std::string& group_name,
-    const void* resolve_info,
+    const void* params,
     RequestPriority priority,
     ClientSocketHandle* handle,
     CompletionCallback* callback,
-    LoadLog* load_log) {
-  const HostResolver::RequestInfo* casted_resolve_info =
-      static_cast<const HostResolver::RequestInfo*>(resolve_info);
+    const BoundNetLog& net_log) {
+  const scoped_refptr<TCPSocketParams>* casted_params =
+      static_cast<const scoped_refptr<TCPSocketParams>*>(params);
 
-  if (LoadLog::IsUnbounded(load_log)) {
-    LoadLog::AddString(
-        load_log,
-        StringPrintf("Requested TCP socket to: %s [port %d]",
-                     casted_resolve_info->hostname().c_str(),
-                     casted_resolve_info->port()));
+  if (net_log.HasListener()) {
+    // TODO(eroman): Split out the host and port parameters.
+    net_log.AddEvent(
+        NetLog::TYPE_TCP_CLIENT_SOCKET_POOL_REQUESTED_SOCKET,
+        new NetLogStringParameter(
+            "host_and_port",
+            StringPrintf("%s [port %d]",
+                         casted_params->get()->destination().hostname().c_str(),
+                         casted_params->get()->destination().port())));
   }
 
-  return base_.RequestSocket(
-      group_name, *casted_resolve_info, priority, handle, callback, load_log);
+  return base_.RequestSocket(group_name, *casted_params, priority, handle,
+                             callback, net_log);
 }
 
 void TCPClientSocketPool::CancelRequest(
     const std::string& group_name,
-    const ClientSocketHandle* handle) {
+    ClientSocketHandle* handle) {
   base_.CancelRequest(group_name, handle);
 }
 
 void TCPClientSocketPool::ReleaseSocket(
     const std::string& group_name,
-    ClientSocket* socket) {
-  base_.ReleaseSocket(group_name, socket);
+    ClientSocket* socket,
+    int id) {
+  base_.ReleaseSocket(group_name, socket, id);
+}
+
+void TCPClientSocketPool::Flush() {
+  base_.Flush();
 }
 
 void TCPClientSocketPool::CloseIdleSockets() {
diff --git a/net/socket/tcp_client_socket_pool.h b/net/socket/tcp_client_socket_pool.h
index fb1a93b..1ee50d2 100644
--- a/net/socket/tcp_client_socket_pool.h
+++ b/net/socket/tcp_client_socket_pool.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -12,25 +12,58 @@
 #include "base/scoped_ptr.h"
 #include "base/time.h"
 #include "base/timer.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/host_resolver.h"
 #include "net/socket/client_socket_pool_base.h"
+#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/client_socket_pool.h"
 
 namespace net {
 
 class ClientSocketFactory;
 
+class TCPSocketParams : public base::RefCounted<TCPSocketParams> {
+ public:
+  TCPSocketParams(const HostPortPair& host_port_pair, RequestPriority priority,
+                  const GURL& referrer, bool disable_resolver_cache);
+
+  // TODO(willchan): Update all unittests so we don't need this.
+  TCPSocketParams(const std::string& host, int port, RequestPriority priority,
+                  const GURL& referrer, bool disable_resolver_cache);
+
+  const HostResolver::RequestInfo& destination() const { return destination_; }
+
+ private:
+  friend class base::RefCounted<TCPSocketParams>;
+  ~TCPSocketParams();
+
+  void Initialize(RequestPriority priority, const GURL& referrer,
+                  bool disable_resolver_cache) {
+    // The referrer is used by the DNS prefetch system to correlate resolutions
+    // with the page that triggered them. It doesn't impact the actual addresses
+    // that we resolve to.
+    destination_.set_referrer(referrer);
+    destination_.set_priority(priority);
+    if (disable_resolver_cache)
+      destination_.set_allow_cached_response(false);
+  }
+
+  HostResolver::RequestInfo destination_;
+
+  DISALLOW_COPY_AND_ASSIGN(TCPSocketParams);
+};
+
 // TCPConnectJob handles the host resolution necessary for socket creation
 // and the tcp connect.
 class TCPConnectJob : public ConnectJob {
  public:
   TCPConnectJob(const std::string& group_name,
-                const HostResolver::RequestInfo& resolve_info,
+                const scoped_refptr<TCPSocketParams>& params,
                 base::TimeDelta timeout_duration,
                 ClientSocketFactory* client_socket_factory,
                 HostResolver* host_resolver,
                 Delegate* delegate,
-                LoadLog* load_log);
+                NetLog* net_log);
   virtual ~TCPConnectJob();
 
   // ConnectJob methods.
@@ -60,7 +93,7 @@
   int DoTCPConnect();
   int DoTCPConnectComplete(int result);
 
-  const HostResolver::RequestInfo resolve_info_;
+  scoped_refptr<TCPSocketParams> params_;
   ClientSocketFactory* const client_socket_factory_;
   CompletionCallbackImpl<TCPConnectJob> callback_;
   SingleRequestHostResolver resolver_;
@@ -81,9 +114,10 @@
   TCPClientSocketPool(
       int max_sockets,
       int max_sockets_per_group,
+      const scoped_refptr<ClientSocketPoolHistograms>& histograms,
       HostResolver* host_resolver,
       ClientSocketFactory* client_socket_factory,
-      NetworkChangeNotifier* network_change_notifier);
+      NetLog* net_log);
 
   // ClientSocketPool methods:
 
@@ -92,13 +126,16 @@
                             RequestPriority priority,
                             ClientSocketHandle* handle,
                             CompletionCallback* callback,
-                            LoadLog* load_log);
+                            const BoundNetLog& net_log);
 
   virtual void CancelRequest(const std::string& group_name,
-                             const ClientSocketHandle* handle);
+                             ClientSocketHandle* handle);
 
   virtual void ReleaseSocket(const std::string& group_name,
-                             ClientSocket* socket);
+                             ClientSocket* socket,
+                             int id);
+
+  virtual void Flush();
 
   virtual void CloseIdleSockets();
 
@@ -111,19 +148,29 @@
   virtual LoadState GetLoadState(const std::string& group_name,
                                  const ClientSocketHandle* handle) const;
 
+  virtual base::TimeDelta ConnectionTimeout() const {
+    return base_.ConnectionTimeout();
+  }
+
+  virtual scoped_refptr<ClientSocketPoolHistograms> histograms() const {
+    return base_.histograms();
+  }
+
  protected:
   virtual ~TCPClientSocketPool();
 
  private:
-  typedef ClientSocketPoolBase<HostResolver::RequestInfo> PoolBase;
+  typedef ClientSocketPoolBase<TCPSocketParams> PoolBase;
 
   class TCPConnectJobFactory
       : public PoolBase::ConnectJobFactory {
    public:
     TCPConnectJobFactory(ClientSocketFactory* client_socket_factory,
-                         HostResolver* host_resolver)
+                         HostResolver* host_resolver,
+                         NetLog* net_log)
         : client_socket_factory_(client_socket_factory),
-          host_resolver_(host_resolver) {}
+          host_resolver_(host_resolver),
+          net_log_(net_log) {}
 
     virtual ~TCPConnectJobFactory() {}
 
@@ -132,12 +179,14 @@
     virtual ConnectJob* NewConnectJob(
         const std::string& group_name,
         const PoolBase::Request& request,
-        ConnectJob::Delegate* delegate,
-        LoadLog* load_log) const;
+        ConnectJob::Delegate* delegate) const;
+
+    virtual base::TimeDelta ConnectionTimeout() const;
 
    private:
     ClientSocketFactory* const client_socket_factory_;
     const scoped_refptr<HostResolver> host_resolver_;
+    NetLog* net_log_;
 
     DISALLOW_COPY_AND_ASSIGN(TCPConnectJobFactory);
   };
@@ -147,7 +196,7 @@
   DISALLOW_COPY_AND_ASSIGN(TCPClientSocketPool);
 };
 
-REGISTER_SOCKET_PARAMS_FOR_POOL(TCPClientSocketPool, HostResolver::RequestInfo)
+REGISTER_SOCKET_PARAMS_FOR_POOL(TCPClientSocketPool, TCPSocketParams);
 
 }  // namespace net
 
diff --git a/net/socket/tcp_client_socket_pool_unittest.cc b/net/socket/tcp_client_socket_pool_unittest.cc
index 719d280..9516f9f 100644
--- a/net/socket/tcp_client_socket_pool_unittest.cc
+++ b/net/socket/tcp_client_socket_pool_unittest.cc
@@ -1,18 +1,19 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/socket/tcp_client_socket_pool.h"
 
+#include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/message_loop.h"
 #include "net/base/mock_host_resolver.h"
-#include "net/base/mock_network_change_notifier.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "net/socket/client_socket.h"
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_histograms.h"
 #include "net/socket/socket_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -29,7 +30,7 @@
   MockClientSocket() : connected_(false) {}
 
   // ClientSocket methods:
-  virtual int Connect(CompletionCallback* callback, LoadLog* /* load_log */) {
+  virtual int Connect(CompletionCallback* callback) {
     connected_ = true;
     return OK;
   }
@@ -42,9 +43,12 @@
   virtual bool IsConnectedAndIdle() const {
     return connected_;
   }
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen) {
+  virtual int GetPeerAddress(AddressList* address) const {
     return ERR_UNEXPECTED;
   }
+  virtual const BoundNetLog& NetLog() const {
+    return net_log_;
+  }
 
   // Socket methods:
   virtual int Read(IOBuffer* buf, int buf_len,
@@ -60,6 +64,7 @@
 
  private:
   bool connected_;
+  BoundNetLog net_log_;
 };
 
 class MockFailingClientSocket : public ClientSocket {
@@ -67,7 +72,7 @@
   MockFailingClientSocket() {}
 
   // ClientSocket methods:
-  virtual int Connect(CompletionCallback* callback, LoadLog* /* load_log */) {
+  virtual int Connect(CompletionCallback* callback) {
     return ERR_CONNECTION_FAILED;
   }
 
@@ -79,9 +84,12 @@
   virtual bool IsConnectedAndIdle() const {
     return false;
   }
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen) {
+  virtual int GetPeerAddress(AddressList* address) const {
     return ERR_UNEXPECTED;
   }
+  virtual const BoundNetLog& NetLog() const {
+    return net_log_;
+  }
 
   // Socket methods:
   virtual int Read(IOBuffer* buf, int buf_len,
@@ -95,21 +103,30 @@
   }
   virtual bool SetReceiveBufferSize(int32 size) { return true; }
   virtual bool SetSendBufferSize(int32 size) { return true; }
+
+ private:
+  BoundNetLog net_log_;
 };
 
 class MockPendingClientSocket : public ClientSocket {
  public:
-  MockPendingClientSocket(bool should_connect)
+  // |should_connect| indicates whether the socket should successfully complete
+  // or fail.
+  // |should_stall| indicates that this socket should never connect.
+  // |delay_ms| is the delay, in milliseconds, before simulating a connect.
+  MockPendingClientSocket(bool should_connect, bool should_stall, int delay_ms)
       : method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
         should_connect_(should_connect),
+        should_stall_(should_stall),
+        delay_ms_(delay_ms),
         is_connected_(false) {}
 
   // ClientSocket methods:
-  virtual int Connect(CompletionCallback* callback, LoadLog* /* load_log */) {
-    MessageLoop::current()->PostTask(
+  virtual int Connect(CompletionCallback* callback) {
+    MessageLoop::current()->PostDelayedTask(
         FROM_HERE,
         method_factory_.NewRunnableMethod(
-           &MockPendingClientSocket::DoCallback, callback));
+           &MockPendingClientSocket::DoCallback, callback), delay_ms_);
     return ERR_IO_PENDING;
   }
 
@@ -121,9 +138,12 @@
   virtual bool IsConnectedAndIdle() const {
     return is_connected_;
   }
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen) {
+  virtual int GetPeerAddress(AddressList* address) const{
     return ERR_UNEXPECTED;
   }
+  virtual const BoundNetLog& NetLog() const {
+    return net_log_;
+  }
 
   // Socket methods:
   virtual int Read(IOBuffer* buf, int buf_len,
@@ -140,6 +160,9 @@
 
  private:
   void DoCallback(CompletionCallback* callback) {
+    if (should_stall_)
+      return;
+
     if (should_connect_) {
       is_connected_ = true;
       callback->Run(OK);
@@ -151,7 +174,10 @@
 
   ScopedRunnableMethodFactory<MockPendingClientSocket> method_factory_;
   bool should_connect_;
+  bool should_stall_;
+  int delay_ms_;
   bool is_connected_;
+  BoundNetLog net_log_;
 };
 
 class MockClientSocketFactory : public ClientSocketFactory {
@@ -161,22 +187,41 @@
     MOCK_FAILING_CLIENT_SOCKET,
     MOCK_PENDING_CLIENT_SOCKET,
     MOCK_PENDING_FAILING_CLIENT_SOCKET,
+    // A delayed socket will pause before connecting through the message loop.
+    MOCK_DELAYED_CLIENT_SOCKET,
+    // A stalled socket that never connects at all.
+    MOCK_STALLED_CLIENT_SOCKET,
   };
 
   MockClientSocketFactory()
-      : allocation_count_(0), client_socket_type_(MOCK_CLIENT_SOCKET) {}
+      : allocation_count_(0), client_socket_type_(MOCK_CLIENT_SOCKET),
+        client_socket_types_(NULL), client_socket_index_(0),
+        client_socket_index_max_(0) {}
 
-  virtual ClientSocket* CreateTCPClientSocket(const AddressList& addresses) {
+  virtual ClientSocket* CreateTCPClientSocket(const AddressList& addresses,
+                                              NetLog* /* net_log */) {
     allocation_count_++;
-    switch (client_socket_type_) {
+
+    ClientSocketType type = client_socket_type_;
+    if (client_socket_types_ &&
+        client_socket_index_ < client_socket_index_max_) {
+      type = client_socket_types_[client_socket_index_++];
+    }
+
+    switch (type) {
       case MOCK_CLIENT_SOCKET:
         return new MockClientSocket();
       case MOCK_FAILING_CLIENT_SOCKET:
         return new MockFailingClientSocket();
       case MOCK_PENDING_CLIENT_SOCKET:
-        return new MockPendingClientSocket(true);
+        return new MockPendingClientSocket(true, false, 0);
       case MOCK_PENDING_FAILING_CLIENT_SOCKET:
-        return new MockPendingClientSocket(false);
+        return new MockPendingClientSocket(false, false, 0);
+      case MOCK_DELAYED_CLIENT_SOCKET:
+        return new MockPendingClientSocket(true, false,
+            ClientSocketPool::kMaxConnectRetryIntervalMs);
+      case MOCK_STALLED_CLIENT_SOCKET:
+        return new MockPendingClientSocket(true, true, 0);
       default:
         NOTREACHED();
         return new MockClientSocket();
@@ -184,7 +229,7 @@
   }
 
   virtual SSLClientSocket* CreateSSLClientSocket(
-      ClientSocket* transport_socket,
+      ClientSocketHandle* transport_socket,
       const std::string& hostname,
       const SSLConfig& ssl_config) {
     NOTIMPLEMENTED();
@@ -193,44 +238,62 @@
 
   int allocation_count() const { return allocation_count_; }
 
+  // Set the default ClientSocketType.
   void set_client_socket_type(ClientSocketType type) {
     client_socket_type_ = type;
   }
 
+  // Set a list of ClientSocketTypes to be used.
+  void set_client_socket_types(ClientSocketType* type_list, int num_types) {
+    DCHECK_GT(num_types, 0);
+    client_socket_types_ = type_list;
+    client_socket_index_ = 0;
+    client_socket_index_max_ = num_types;
+  }
+
  private:
   int allocation_count_;
   ClientSocketType client_socket_type_;
+  ClientSocketType* client_socket_types_;
+  int client_socket_index_;
+  int client_socket_index_max_;
 };
 
 class TCPClientSocketPoolTest : public ClientSocketPoolTest {
  protected:
   TCPClientSocketPoolTest()
-      : ignored_request_info_("ignored", 80),
+      : params_(new TCPSocketParams(HostPortPair("www.google.com", 80),
+                                    kDefaultPriority, GURL(), false)),
+        low_params_(new TCPSocketParams(HostPortPair("www.google.com", 80),
+                                        LOW, GURL(), false)),
+        histograms_(new ClientSocketPoolHistograms("TCPUnitTest")),
         host_resolver_(new MockHostResolver),
         pool_(new TCPClientSocketPool(kMaxSockets,
                                       kMaxSocketsPerGroup,
+                                      histograms_,
                                       host_resolver_,
                                       &client_socket_factory_,
-                                      &notifier_)) {
+                                      NULL)) {
   }
 
   int StartRequest(const std::string& group_name, RequestPriority priority) {
-    return StartRequestUsingPool(
-        pool_.get(), group_name, priority, ignored_request_info_);
+    scoped_refptr<TCPSocketParams> params = new TCPSocketParams(
+        HostPortPair("www.google.com", 80), MEDIUM, GURL(), false);
+    return StartRequestUsingPool(pool_, group_name, priority, params);
   }
 
-  HostResolver::RequestInfo ignored_request_info_;
+  scoped_refptr<TCPSocketParams> params_;
+  scoped_refptr<TCPSocketParams> low_params_;
+  scoped_refptr<ClientSocketPoolHistograms> histograms_;
   scoped_refptr<MockHostResolver> host_resolver_;
   MockClientSocketFactory client_socket_factory_;
-  MockNetworkChangeNotifier notifier_;
   scoped_refptr<TCPClientSocketPool> pool_;
 };
 
 TEST_F(TCPClientSocketPoolTest, Basic) {
   TestCompletionCallback callback;
   ClientSocketHandle handle;
-  HostResolver::RequestInfo info("www.google.com", 80);
-  int rv = handle.Init("a", info, LOW, &callback, pool_.get(), NULL);
+  int rv = handle.Init("a", low_params_, LOW, &callback, pool_, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_FALSE(handle.is_initialized());
   EXPECT_FALSE(handle.socket());
@@ -245,10 +308,11 @@
 TEST_F(TCPClientSocketPoolTest, InitHostResolutionFailure) {
   host_resolver_->rules()->AddSimulatedFailure("unresolvable.host.name");
   TestSocketRequest req(&request_order_, &completion_count_);
-  HostResolver::RequestInfo info("unresolvable.host.name", 80);
+  scoped_refptr<TCPSocketParams> dest = new TCPSocketParams(
+          "unresolvable.host.name", 80, kDefaultPriority, GURL(), false);
   EXPECT_EQ(ERR_IO_PENDING,
-            req.handle()->Init(
-                "a", info, kDefaultPriority, &req, pool_.get(), NULL));
+            req.handle()->Init("a", dest, kDefaultPriority, &req, pool_,
+                               BoundNetLog()));
   EXPECT_EQ(ERR_NAME_NOT_RESOLVED, req.WaitForResult());
 }
 
@@ -256,17 +320,15 @@
   client_socket_factory_.set_client_socket_type(
       MockClientSocketFactory::MOCK_FAILING_CLIENT_SOCKET);
   TestSocketRequest req(&request_order_, &completion_count_);
-  HostResolver::RequestInfo info("a", 80);
-  EXPECT_EQ(ERR_IO_PENDING,
-            req.handle()->Init(
-                "a", info, kDefaultPriority, &req, pool_.get(), NULL));
+  EXPECT_EQ(ERR_IO_PENDING, req.handle()->Init("a", params_, kDefaultPriority,
+                                               &req, pool_, BoundNetLog()));
   EXPECT_EQ(ERR_CONNECTION_FAILED, req.WaitForResult());
 
   // Make the host resolutions complete synchronously this time.
   host_resolver_->set_synchronous_mode(true);
-  EXPECT_EQ(ERR_CONNECTION_FAILED,
-            req.handle()->Init(
-                "a", info, kDefaultPriority, &req, pool_.get(), NULL));
+  EXPECT_EQ(ERR_CONNECTION_FAILED, req.handle()->Init("a", params_,
+                                                      kDefaultPriority, &req,
+                                                      pool_, BoundNetLog()));
 }
 
 TEST_F(TCPClientSocketPoolTest, PendingRequests) {
@@ -370,10 +432,8 @@
 // ClientSocketPool which will crash if the group was not cleared properly.
 TEST_F(TCPClientSocketPoolTest, CancelRequestClearGroup) {
   TestSocketRequest req(&request_order_, &completion_count_);
-  HostResolver::RequestInfo info("www.google.com", 80);
-  EXPECT_EQ(ERR_IO_PENDING,
-            req.handle()->Init(
-                "a", info, kDefaultPriority, &req, pool_.get(), NULL));
+  EXPECT_EQ(ERR_IO_PENDING, req.handle()->Init("a", params_, kDefaultPriority,
+                                               &req, pool_, BoundNetLog()));
   req.handle()->Reset();
 
   // There is a race condition here.  If the worker pool doesn't post the task
@@ -389,13 +449,10 @@
   TestSocketRequest req(&request_order_, &completion_count_);
   TestSocketRequest req2(&request_order_, &completion_count_);
 
-  HostResolver::RequestInfo info("www.google.com", 80);
-  EXPECT_EQ(ERR_IO_PENDING,
-            req.handle()->Init(
-                "a", info, kDefaultPriority, &req, pool_.get(), NULL));
-  EXPECT_EQ(ERR_IO_PENDING,
-            req2.handle()->Init(
-                "a", info, kDefaultPriority, &req2, pool_.get(), NULL));
+  EXPECT_EQ(ERR_IO_PENDING, req.handle()->Init("a", params_, kDefaultPriority,
+                                               &req, pool_, BoundNetLog()));
+  EXPECT_EQ(ERR_IO_PENDING, req2.handle()->Init("a", params_, kDefaultPriority,
+                                                &req2, pool_, BoundNetLog()));
 
   req.handle()->Reset();
 
@@ -410,17 +467,14 @@
   TestCompletionCallback callback;
   TestSocketRequest req(&request_order_, &completion_count_);
 
-  HostResolver::RequestInfo info("www.google.com", 80);
-  EXPECT_EQ(ERR_IO_PENDING,
-            handle.Init(
-                "a", info, kDefaultPriority, &callback, pool_.get(), NULL));
+  EXPECT_EQ(ERR_IO_PENDING, handle.Init("a", params_, kDefaultPriority,
+                                        &callback, pool_, BoundNetLog()));
 
   handle.Reset();
 
   TestCompletionCallback callback2;
-  EXPECT_EQ(ERR_IO_PENDING,
-            handle.Init(
-                "a", info, kDefaultPriority, &callback2, pool_.get(), NULL));
+  EXPECT_EQ(ERR_IO_PENDING, handle.Init("a", params_, kDefaultPriority,
+                                        &callback2, pool_, BoundNetLog()));
 
   host_resolver_->set_synchronous_mode(true);
   // At this point, handle has two ConnectingSockets out for it.  Due to the
@@ -518,9 +572,10 @@
         MessageLoop::current()->RunAllPending();
       }
       within_callback_ = true;
-      int rv = handle_->Init(
-          "a", HostResolver::RequestInfo("www.google.com", 80), LOWEST,
-          this, pool_.get(), NULL);
+      scoped_refptr<TCPSocketParams> dest = new TCPSocketParams(
+          HostPortPair("www.google.com", 80), LOWEST, GURL(), false);
+      int rv = handle_->Init("a", dest, LOWEST, this, pool_,
+                             BoundNetLog());
       EXPECT_EQ(OK, rv);
     }
   }
@@ -539,9 +594,10 @@
 TEST_F(TCPClientSocketPoolTest, RequestTwice) {
   ClientSocketHandle handle;
   RequestSocketCallback callback(&handle, pool_.get());
-  int rv = handle.Init(
-      "a", HostResolver::RequestInfo("www.google.com", 80), LOWEST,
-      &callback, pool_.get(), NULL);
+  scoped_refptr<TCPSocketParams> dest = new TCPSocketParams(
+      HostPortPair("www.google.com", 80), LOWEST, GURL(), false);
+  int rv = handle.Init("a", dest, LOWEST, &callback, pool_,
+                       BoundNetLog());
   ASSERT_EQ(ERR_IO_PENDING, rv);
 
   // The callback is going to request "www.google.com". We want it to complete
@@ -603,8 +659,7 @@
 TEST_F(TCPClientSocketPoolTest, ResetIdleSocketsOnIPAddressChange) {
   TestCompletionCallback callback;
   ClientSocketHandle handle;
-  HostResolver::RequestInfo info("www.google.com", 80);
-  int rv = handle.Init("a", info, LOW, &callback, pool_.get(), NULL);
+  int rv = handle.Init("a", low_params_, LOW, &callback, pool_, BoundNetLog());
   EXPECT_EQ(ERR_IO_PENDING, rv);
   EXPECT_FALSE(handle.is_initialized());
   EXPECT_FALSE(handle.socket());
@@ -622,10 +677,207 @@
   EXPECT_EQ(1, pool_->IdleSocketCount());
 
   // After an IP address change, we should have 0 idle sockets.
-  notifier_.NotifyIPAddressChange();
+  NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+  MessageLoop::current()->RunAllPending();  // Notification happens async.
+
   EXPECT_EQ(0, pool_->IdleSocketCount());
 }
 
+TEST_F(TCPClientSocketPoolTest, BackupSocketConnect) {
+  // Case 1 tests the first socket stalling, and the backup connecting.
+  MockClientSocketFactory::ClientSocketType case1_types[] = {
+    // The first socket will not connect.
+    MockClientSocketFactory::MOCK_STALLED_CLIENT_SOCKET,
+    // The second socket will connect more quickly.
+    MockClientSocketFactory::MOCK_CLIENT_SOCKET
+  };
+
+  // Case 2 tests the first socket being slow, so that we start the
+  // second connect, but the second connect stalls, and we still
+  // complete the first.
+  MockClientSocketFactory::ClientSocketType case2_types[] = {
+    // The first socket will connect, although delayed.
+    MockClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET,
+    // The second socket will not connect.
+    MockClientSocketFactory::MOCK_STALLED_CLIENT_SOCKET
+  };
+
+  MockClientSocketFactory::ClientSocketType* cases[2] = {
+    case1_types,
+    case2_types
+  };
+
+  for (size_t index = 0; index < arraysize(cases); ++index) {
+    client_socket_factory_.set_client_socket_types(cases[index], 2);
+
+    EXPECT_EQ(0, pool_->IdleSocketCount());
+
+    TestCompletionCallback callback;
+    ClientSocketHandle handle;
+    int rv = handle.Init("b", low_params_, LOW, &callback, pool_,
+                         BoundNetLog());
+    EXPECT_EQ(ERR_IO_PENDING, rv);
+    EXPECT_FALSE(handle.is_initialized());
+    EXPECT_FALSE(handle.socket());
+
+    // Create the first socket, set the timer.
+    MessageLoop::current()->RunAllPending();
+
+    // Wait for the backup socket timer to fire.
+    PlatformThread::Sleep(ClientSocketPool::kMaxConnectRetryIntervalMs * 2);
+
+    // Let the appropriate socket connect.
+    MessageLoop::current()->RunAllPending();
+
+    EXPECT_EQ(OK, callback.WaitForResult());
+    EXPECT_TRUE(handle.is_initialized());
+    EXPECT_TRUE(handle.socket());
+
+    // One socket is stalled, the other is active.
+    EXPECT_EQ(0, pool_->IdleSocketCount());
+    handle.Reset();
+
+    // Close all pending connect jobs and existing sockets.
+    pool_->Flush();
+
+    // TODO(mbelshe): Flush has a bug.  UIt doesn't clean out pending connect
+    // jobs.  When they complete, they become idle sockets.  For now, continue
+    // to replace the pool_.  But we really need to fix Flush().
+    pool_ = new TCPClientSocketPool(kMaxSockets, kMaxSocketsPerGroup,
+        histograms_, host_resolver_, &client_socket_factory_, NULL);
+  }
+}
+
+// Test the case where a socket took long enough to start the creation
+// of the backup socket, but then we cancelled the request after that.
+TEST_F(TCPClientSocketPoolTest, BackupSocketCancel) {
+  client_socket_factory_.set_client_socket_type(
+      MockClientSocketFactory::MOCK_STALLED_CLIENT_SOCKET);
+
+  enum { CANCEL_BEFORE_WAIT, CANCEL_AFTER_WAIT };
+
+  for (int index = CANCEL_BEFORE_WAIT; index < CANCEL_AFTER_WAIT; ++index) {
+    EXPECT_EQ(0, pool_->IdleSocketCount());
+
+    TestCompletionCallback callback;
+    ClientSocketHandle handle;
+    int rv = handle.Init("c", low_params_, LOW, &callback, pool_,
+                         BoundNetLog());
+    EXPECT_EQ(ERR_IO_PENDING, rv);
+    EXPECT_FALSE(handle.is_initialized());
+    EXPECT_FALSE(handle.socket());
+
+    // Create the first socket, set the timer.
+    MessageLoop::current()->RunAllPending();
+
+    if (index == CANCEL_AFTER_WAIT) {
+      // Wait for the backup socket timer to fire.
+      PlatformThread::Sleep(ClientSocketPool::kMaxConnectRetryIntervalMs);
+    }
+
+    // Let the appropriate socket connect.
+    MessageLoop::current()->RunAllPending();
+
+    handle.Reset();
+
+    EXPECT_FALSE(callback.have_result());
+    EXPECT_FALSE(handle.is_initialized());
+    EXPECT_FALSE(handle.socket());
+
+    // One socket is stalled, the other is active.
+    EXPECT_EQ(0, pool_->IdleSocketCount());
+  }
+}
+
+// Test the case where a socket took long enough to start the creation
+// of the backup socket and never completes, and then the backup
+// connection fails.
+TEST_F(TCPClientSocketPoolTest, BackupSocketFailAfterStall) {
+  MockClientSocketFactory::ClientSocketType case_types[] = {
+    // The first socket will not connect.
+    MockClientSocketFactory::MOCK_STALLED_CLIENT_SOCKET,
+    // The second socket will fail immediately.
+    MockClientSocketFactory::MOCK_FAILING_CLIENT_SOCKET
+  };
+
+  client_socket_factory_.set_client_socket_types(case_types, 2);
+
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+
+  TestCompletionCallback callback;
+  ClientSocketHandle handle;
+  int rv = handle.Init("b", low_params_, LOW, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  // Create the first socket, set the timer.
+  MessageLoop::current()->RunAllPending();
+
+  // Wait for the backup socket timer to fire.
+  PlatformThread::Sleep(ClientSocketPool::kMaxConnectRetryIntervalMs);
+
+  // Let the second connect be synchronous. Otherwise, the emulated
+  // host resolution takes an extra trip through the message loop.
+  host_resolver_->set_synchronous_mode(true);
+
+  // Let the appropriate socket connect.
+  MessageLoop::current()->RunAllPending();
+
+  EXPECT_EQ(ERR_CONNECTION_FAILED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+  handle.Reset();
+
+  // Reset for the next case.
+  host_resolver_->set_synchronous_mode(false);
+}
+
+// Test the case where a socket took long enough to start the creation
+// of the backup socket and eventually completes, but the backup socket
+// fails.
+TEST_F(TCPClientSocketPoolTest, BackupSocketFailAfterDelay) {
+  MockClientSocketFactory::ClientSocketType case_types[] = {
+    // The first socket will connect, although delayed.
+    MockClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET,
+    // The second socket will not connect.
+    MockClientSocketFactory::MOCK_FAILING_CLIENT_SOCKET
+  };
+
+  client_socket_factory_.set_client_socket_types(case_types, 2);
+
+  EXPECT_EQ(0, pool_->IdleSocketCount());
+
+  TestCompletionCallback callback;
+  ClientSocketHandle handle;
+  int rv = handle.Init("b", low_params_, LOW, &callback, pool_, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  // Create the first socket, set the timer.
+  MessageLoop::current()->RunAllPending();
+
+  // Wait for the backup socket timer to fire.
+  PlatformThread::Sleep(ClientSocketPool::kMaxConnectRetryIntervalMs);
+
+  // Let the second connect be synchronous. Otherwise, the emulated
+  // host resolution takes an extra trip through the message loop.
+  host_resolver_->set_synchronous_mode(true);
+
+  // Let the appropriate socket connect.
+  MessageLoop::current()->RunAllPending();
+
+  EXPECT_EQ(ERR_CONNECTION_FAILED, callback.WaitForResult());
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+  handle.Reset();
+
+  // Reset for the next case.
+  host_resolver_->set_synchronous_mode(false);
+}
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/socket/tcp_client_socket_unittest.cc b/net/socket/tcp_client_socket_unittest.cc
index e1e08ab..638e887 100644
--- a/net/socket/tcp_client_socket_unittest.cc
+++ b/net/socket/tcp_client_socket_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -9,8 +9,8 @@
 #include "net/base/host_resolver.h"
 #include "net/base/io_buffer.h"
 #include "net/base/listen_socket.h"
-#include "net/base/load_log.h"
-#include "net/base/load_log_unittest.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "net/base/winsock_init.h"
@@ -26,14 +26,14 @@
 class TCPClientSocketTest
     : public PlatformTest, public ListenSocket::ListenSocketDelegate {
  public:
-  TCPClientSocketTest() {
+  TCPClientSocketTest() : net_log_(CapturingNetLog::kUnbounded) {
   }
 
   // Implement ListenSocketDelegate methods
   virtual void DidAccept(ListenSocket* server, ListenSocket* connection) {
     connected_sock_ = connection;
   }
-  virtual void DidRead(ListenSocket*, const std::string& s) {
+  virtual void DidRead(ListenSocket*, const char* str, int len) {
     // TODO(dkegel): this might not be long enough to tickle some bugs.
     connected_sock_->Send(kServerReply,
                           arraysize(kServerReply) - 1,
@@ -59,6 +59,7 @@
 
  protected:
   int listen_port_;
+  CapturingNetLog net_log_;
   scoped_ptr<TCPClientSocket> sock_;
 
  private:
@@ -88,33 +89,32 @@
   listen_port_ = port;
 
   AddressList addr;
-  scoped_refptr<HostResolver> resolver(CreateSystemHostResolver(NULL));
+  scoped_refptr<HostResolver> resolver(
+      CreateSystemHostResolver(HostResolver::kDefaultParallelism));
   HostResolver::RequestInfo info("localhost", listen_port_);
-  int rv = resolver->Resolve(info, &addr, NULL, NULL, NULL);
-  CHECK(rv == OK);
-  sock_.reset(new TCPClientSocket(addr));
+  int rv = resolver->Resolve(info, &addr, NULL, NULL, BoundNetLog());
+  CHECK_EQ(rv, OK);
+  sock_.reset(new TCPClientSocket(addr, &net_log_));
 }
 
 TEST_F(TCPClientSocketTest, Connect) {
   TestCompletionCallback callback;
   EXPECT_FALSE(sock_->IsConnected());
 
-  scoped_refptr<LoadLog> log(new LoadLog(LoadLog::kUnbounded));
-  int rv = sock_->Connect(&callback, log);
+  int rv = sock_->Connect(&callback);
   EXPECT_TRUE(net::LogContainsBeginEvent(
-      *log, 0, net::LoadLog::TYPE_TCP_CONNECT));
+      net_log_.entries(), 0, net::NetLog::TYPE_SOCKET_ALIVE));
+  EXPECT_TRUE(net::LogContainsBeginEvent(
+      net_log_.entries(), 1, net::NetLog::TYPE_TCP_CONNECT));
   if (rv != OK) {
     ASSERT_EQ(rv, ERR_IO_PENDING);
-    EXPECT_FALSE(net::LogContainsEndEvent(
-        *log, -1, net::LoadLog::TYPE_TCP_CONNECT));
-
     rv = callback.WaitForResult();
     EXPECT_EQ(rv, OK);
   }
 
   EXPECT_TRUE(sock_->IsConnected());
   EXPECT_TRUE(net::LogContainsEndEvent(
-      *log, -1, net::LoadLog::TYPE_TCP_CONNECT));
+      net_log_.entries(), -1, net::NetLog::TYPE_TCP_CONNECT));
 
   sock_->Disconnect();
   EXPECT_FALSE(sock_->IsConnected());
@@ -126,7 +126,7 @@
 
 TEST_F(TCPClientSocketTest, Read) {
   TestCompletionCallback callback;
-  int rv = sock_->Connect(&callback, NULL);
+  int rv = sock_->Connect(&callback);
   if (rv != OK) {
     ASSERT_EQ(rv, ERR_IO_PENDING);
 
@@ -171,7 +171,7 @@
 
 TEST_F(TCPClientSocketTest, Read_SmallChunks) {
   TestCompletionCallback callback;
-  int rv = sock_->Connect(&callback, NULL);
+  int rv = sock_->Connect(&callback);
   if (rv != OK) {
     ASSERT_EQ(rv, ERR_IO_PENDING);
 
@@ -216,7 +216,7 @@
 
 TEST_F(TCPClientSocketTest, Read_Interrupted) {
   TestCompletionCallback callback;
-  int rv = sock_->Connect(&callback, NULL);
+  int rv = sock_->Connect(&callback);
   if (rv != OK) {
     ASSERT_EQ(ERR_IO_PENDING, rv);
 
@@ -250,7 +250,7 @@
 
 TEST_F(TCPClientSocketTest, DISABLED_FullDuplex_ReadFirst) {
   TestCompletionCallback callback;
-  int rv = sock_->Connect(&callback, NULL);
+  int rv = sock_->Connect(&callback);
   if (rv != OK) {
     ASSERT_EQ(rv, ERR_IO_PENDING);
 
@@ -292,7 +292,7 @@
 
 TEST_F(TCPClientSocketTest, DISABLED_FullDuplex_WriteFirst) {
   TestCompletionCallback callback;
-  int rv = sock_->Connect(&callback, NULL);
+  int rv = sock_->Connect(&callback);
   if (rv != OK) {
     ASSERT_EQ(ERR_IO_PENDING, rv);
 
diff --git a/net/socket/tcp_client_socket_win.cc b/net/socket/tcp_client_socket_win.cc
index 3046659..3faf41b 100644
--- a/net/socket/tcp_client_socket_win.cc
+++ b/net/socket/tcp_client_socket_win.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -10,11 +10,13 @@
 #include "base/stats_counters.h"
 #include "base/string_util.h"
 #include "base/sys_info.h"
-#include "base/trace_event.h"
+#include "net/base/address_list_net_log_param.h"
 #include "net/base/connection_type_histograms.h"
 #include "net/base/io_buffer.h"
-#include "net/base/load_log.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/base/sys_addrinfo.h"
 #include "net/base/winsock_init.h"
 
 namespace net {
@@ -34,7 +36,7 @@
   DWORD wait_rv = WaitForSingleObject(hEvent, 0);
   if (wait_rv == WAIT_TIMEOUT)
     return false;  // The event object is not signaled.
-  CHECK(wait_rv == WAIT_OBJECT_0);
+  CHECK_EQ(WAIT_OBJECT_0, wait_rv);
   BOOL ok = WSAResetEvent(hEvent);
   CHECK(ok);
   return true;
@@ -97,24 +99,6 @@
   }
 }
 
-// Given os_error, a WSAGetLastError() error code from a connect() attempt,
-// returns true if connect() should be retried with another address.
-bool ShouldTryNextAddress(int os_error) {
-  switch (os_error) {
-    case WSAEADDRNOTAVAIL:
-    case WSAEAFNOSUPPORT:
-    case WSAECONNREFUSED:
-    case WSAEACCES:
-    case WSAENETUNREACH:
-    case WSAEHOSTUNREACH:
-    case WSAENETDOWN:
-    case WSAETIMEDOUT:
-      return true;
-    default:
-      return false;
-  }
-}
-
 }  // namespace
 
 //-----------------------------------------------------------------------------
@@ -146,6 +130,7 @@
   WSABUF write_buffer_;
   scoped_refptr<IOBuffer> read_iobuffer_;
   scoped_refptr<IOBuffer> write_iobuffer_;
+  int write_buffer_length_;
 
   // Throttle the read size based on our current slow start state.
   // Returns the throttled read size.
@@ -211,7 +196,8 @@
 
 TCPClientSocketWin::Core::Core(
     TCPClientSocketWin* socket)
-    : socket_(socket),
+    : write_buffer_length_(0),
+      socket_(socket),
       ALLOW_THIS_IN_INITIALIZER_LIST(reader_(this)),
       ALLOW_THIS_IN_INITIALIZER_LIST(writer_(this)),
       slow_start_throttle_(kInitialSlowStartThrottle) {
@@ -248,7 +234,7 @@
     HANDLE object) {
   DCHECK_EQ(object, core_->read_overlapped_.hEvent);
   if (core_->socket_) {
-    if (core_->socket_->waiting_connect_) {
+    if (core_->socket_->waiting_connect()) {
       core_->socket_->DidCompleteConnect();
     } else {
       core_->socket_->DidCompleteRead();
@@ -269,62 +255,96 @@
 
 //-----------------------------------------------------------------------------
 
-TCPClientSocketWin::TCPClientSocketWin(const AddressList& addresses)
+TCPClientSocketWin::TCPClientSocketWin(const AddressList& addresses,
+                                       net::NetLog* net_log)
     : socket_(INVALID_SOCKET),
       addresses_(addresses),
-      current_ai_(addresses_.head()),
-      waiting_connect_(false),
+      current_ai_(NULL),
       waiting_read_(false),
       waiting_write_(false),
       read_callback_(NULL),
-      write_callback_(NULL) {
+      write_callback_(NULL),
+      next_connect_state_(CONNECT_STATE_NONE),
+      connect_os_error_(0),
+      net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SOCKET)) {
+  net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, NULL);
   EnsureWinsockInit();
 }
 
 TCPClientSocketWin::~TCPClientSocketWin() {
   Disconnect();
+  net_log_.EndEvent(NetLog::TYPE_SOCKET_ALIVE, NULL);
 }
 
-int TCPClientSocketWin::Connect(CompletionCallback* callback,
-                                LoadLog* load_log) {
+int TCPClientSocketWin::Connect(CompletionCallback* callback) {
+  DCHECK(CalledOnValidThread());
+
   // If already connected, then just return OK.
   if (socket_ != INVALID_SOCKET)
     return OK;
 
-  DCHECK(!load_log_);
-
   static StatsCounter connects("tcp.connect");
   connects.Increment();
 
-  TRACE_EVENT_BEGIN("socket.connect", this, "");
+  net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT,
+                      new AddressListNetLogParam(addresses_));
 
-  LoadLog::BeginEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
+  // We will try to connect to each address in addresses_. Start with the
+  // first one in the list.
+  next_connect_state_ = CONNECT_STATE_CONNECT;
+  current_ai_ = addresses_.head();
 
-  int rv = DoConnect();
-
+  int rv = DoConnectLoop(OK);
   if (rv == ERR_IO_PENDING) {
     // Synchronous operation not supported.
     DCHECK(callback);
-
-    load_log_ = load_log;
-    waiting_connect_ = true;
     read_callback_ = callback;
   } else {
-    TRACE_EVENT_END("socket.connect", this, "");
-    LoadLog::EndEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
-    UpdateConnectionTypeHistograms(CONNECTION_ANY, rv >= 0);
+    LogConnectCompletion(rv);
   }
 
   return rv;
 }
 
+int TCPClientSocketWin::DoConnectLoop(int result) {
+  DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE);
+
+  int rv = result;
+  do {
+    ConnectState state = next_connect_state_;
+    next_connect_state_ = CONNECT_STATE_NONE;
+    switch (state) {
+      case CONNECT_STATE_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoConnect();
+        break;
+      case CONNECT_STATE_CONNECT_COMPLETE:
+        rv = DoConnectComplete(rv);
+        break;
+      default:
+        LOG(DFATAL) << "bad state";
+        rv = ERR_UNEXPECTED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE);
+
+  return rv;
+}
+
 int TCPClientSocketWin::DoConnect() {
   const struct addrinfo* ai = current_ai_;
   DCHECK(ai);
+  DCHECK_EQ(0, connect_os_error_);
 
-  int rv = CreateSocket(ai);
-  if (rv != OK)
-    return rv;
+  net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
+                      new NetLogStringParameter(
+                          "address", NetAddressToStringWithPort(current_ai_)));
+
+  next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
+
+  connect_os_error_ = CreateSocket(ai);
+  if (connect_os_error_ != 0)
+    return MapWinsockError(connect_os_error_);
 
   DCHECK(!core_);
   core_ = new Core(this);
@@ -356,6 +376,7 @@
     int os_error = WSAGetLastError();
     if (os_error != WSAEWOULDBLOCK) {
       LOG(ERROR) << "connect failed: " << os_error;
+      connect_os_error_ = os_error;
       return MapConnectError(os_error);
     }
   }
@@ -364,12 +385,43 @@
   return ERR_IO_PENDING;
 }
 
+int TCPClientSocketWin::DoConnectComplete(int result) {
+  // Log the end of this attempt (and any OS error it threw).
+  int os_error = connect_os_error_;
+  connect_os_error_ = 0;
+  scoped_refptr<NetLog::EventParameters> params;
+  if (result != OK)
+    params = new NetLogIntegerParameter("os_error", os_error);
+  net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT, params);
+
+  if (result == OK)
+    return OK;  // Done!
+
+  // Close whatever partially connected socket we currently have.
+  DoDisconnect();
+
+  // Try to fall back to the next address in the list.
+  if (current_ai_->ai_next) {
+    next_connect_state_ = CONNECT_STATE_CONNECT;
+    current_ai_ = current_ai_->ai_next;
+    return OK;
+  }
+
+  // Otherwise there is nothing to fall back to, so give up.
+  return result;
+}
+
 void TCPClientSocketWin::Disconnect() {
+  DoDisconnect();
+  current_ai_ = NULL;
+}
+
+void TCPClientSocketWin::DoDisconnect() {
+  DCHECK(CalledOnValidThread());
+
   if (socket_ == INVALID_SOCKET)
     return;
 
-  TRACE_EVENT_INSTANT("socket.disconnect", this, "");
-
   // Note: don't use CancelIo to cancel pending IO because it doesn't work
   // when there is a Winsock layered service provider.
 
@@ -383,10 +435,7 @@
   closesocket(socket_);
   socket_ = INVALID_SOCKET;
 
-  // Reset for next time.
-  current_ai_ = addresses_.head();
-
-  if (waiting_connect_) {
+  if (waiting_connect()) {
     // We closed the socket, so this notification will never come.
     // From MSDN' WSAEventSelect documentation:
     // "Closing a socket with closesocket also cancels the association and
@@ -396,14 +445,15 @@
 
   waiting_read_ = false;
   waiting_write_ = false;
-  waiting_connect_ = false;
 
   core_->Detach();
   core_ = NULL;
 }
 
 bool TCPClientSocketWin::IsConnected() const {
-  if (socket_ == INVALID_SOCKET || waiting_connect_)
+  DCHECK(CalledOnValidThread());
+
+  if (socket_ == INVALID_SOCKET || waiting_connect())
     return false;
 
   // Check if connection is alive.
@@ -418,7 +468,9 @@
 }
 
 bool TCPClientSocketWin::IsConnectedAndIdle() const {
-  if (socket_ == INVALID_SOCKET || waiting_connect_)
+  DCHECK(CalledOnValidThread());
+
+  if (socket_ == INVALID_SOCKET || waiting_connect())
     return false;
 
   // Check if connection is alive and we haven't received any data
@@ -433,14 +485,19 @@
   return true;
 }
 
-int TCPClientSocketWin::GetPeerName(struct sockaddr* name,
-                                    socklen_t* namelen) {
-  return getpeername(socket_, name, namelen);
+int TCPClientSocketWin::GetPeerAddress(AddressList* address) const {
+  DCHECK(CalledOnValidThread());
+  DCHECK(address);
+  if (!current_ai_)
+    return ERR_FAILED;
+  address->Copy(current_ai_, false);
+  return OK;
 }
 
 int TCPClientSocketWin::Read(IOBuffer* buf,
                              int buf_len,
                              CompletionCallback* callback) {
+  DCHECK(CalledOnValidThread());
   DCHECK_NE(socket_, INVALID_SOCKET);
   DCHECK(!waiting_read_);
   DCHECK(!read_callback_);
@@ -451,16 +508,14 @@
   core_->read_buffer_.len = buf_len;
   core_->read_buffer_.buf = buf->data();
 
-  TRACE_EVENT_BEGIN("socket.read", this, "");
   // TODO(wtc): Remove the CHECK after enough testing.
-  CHECK(WaitForSingleObject(core_->read_overlapped_.hEvent, 0) == WAIT_TIMEOUT);
+  CHECK_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
+           WaitForSingleObject(core_->read_overlapped_.hEvent, 0));
   DWORD num, flags = 0;
   int rv = WSARecv(socket_, &core_->read_buffer_, 1, &num, &flags,
                    &core_->read_overlapped_, NULL);
   if (rv == 0) {
     if (ResetEventIfSignaled(core_->read_overlapped_.hEvent)) {
-      TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", num));
-
       // Because of how WSARecv fills memory when used asynchronously, Purify
       // isn't able to detect that it's been initialized, so it scans for 0xcd
       // in the buffer and reports UMRs (uninitialized memory reads) for those
@@ -470,6 +525,8 @@
       base::MemoryDebug::MarkAsInitialized(core_->read_buffer_.buf, num);
       static StatsCounter read_bytes("tcp.read_bytes");
       read_bytes.Add(num);
+      net_log_.AddEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED,
+                        new NetLogIntegerParameter("num_bytes", num));
       return static_cast<int>(num);
     }
   } else {
@@ -487,6 +544,7 @@
 int TCPClientSocketWin::Write(IOBuffer* buf,
                               int buf_len,
                               CompletionCallback* callback) {
+  DCHECK(CalledOnValidThread());
   DCHECK_NE(socket_, INVALID_SOCKET);
   DCHECK(!waiting_write_);
   DCHECK(!write_callback_);
@@ -498,20 +556,29 @@
 
   core_->write_buffer_.len = buf_len;
   core_->write_buffer_.buf = buf->data();
+  core_->write_buffer_length_ = buf_len;
 
-  TRACE_EVENT_BEGIN("socket.write", this, "");
   // TODO(wtc): Remove the CHECK after enough testing.
-  CHECK(
-      WaitForSingleObject(core_->write_overlapped_.hEvent, 0) == WAIT_TIMEOUT);
+  CHECK_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
+           WaitForSingleObject(core_->write_overlapped_.hEvent, 0));
   DWORD num;
   int rv = WSASend(socket_, &core_->write_buffer_, 1, &num, 0,
                    &core_->write_overlapped_, NULL);
   if (rv == 0) {
     if (ResetEventIfSignaled(core_->write_overlapped_.hEvent)) {
-      TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", num));
+      rv = static_cast<int>(num);
+      if (rv > buf_len || rv < 0) {
+        // It seems that some winsock interceptors report that more was written
+        // than was available. Treat this as an error.  http://crbug.com/27870
+        LOG(ERROR) << "Detected broken LSP: Asked to write " << buf_len
+                   << " bytes, but " << rv << " bytes reported.";
+        return ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES;
+      }
       static StatsCounter write_bytes("tcp.write_bytes");
-      write_bytes.Add(num);
-      return static_cast<int>(num);
+      write_bytes.Add(rv);
+      net_log_.AddEvent(NetLog::TYPE_SOCKET_BYTES_SENT,
+                        new NetLogIntegerParameter("num_bytes", rv));
+      return rv;
     }
   } else {
     int os_error = WSAGetLastError();
@@ -526,6 +593,7 @@
 }
 
 bool TCPClientSocketWin::SetReceiveBufferSize(int32 size) {
+  DCHECK(CalledOnValidThread());
   int rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
                       reinterpret_cast<const char*>(&size), sizeof(size));
   DCHECK(!rv) << "Could not set socket receive buffer size: " << GetLastError();
@@ -533,6 +601,7 @@
 }
 
 bool TCPClientSocketWin::SetSendBufferSize(int32 size) {
+  DCHECK(CalledOnValidThread());
   int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
                       reinterpret_cast<const char*>(&size), sizeof(size));
   DCHECK(!rv) << "Could not set socket send buffer size: " << GetLastError();
@@ -545,7 +614,7 @@
   if (socket_ == INVALID_SOCKET) {
     int os_error = WSAGetLastError();
     LOG(ERROR) << "WSASocket failed: " << os_error;
-    return MapWinsockError(os_error);
+    return os_error;
   }
 
   // Increase the socket buffer sizes from the default sizes for WinXP.  In
@@ -594,7 +663,17 @@
       reinterpret_cast<const char*>(&kDisableNagle), sizeof(kDisableNagle));
   DCHECK(!rv) << "Could not disable nagle";
 
-  return OK;
+  // Disregard any failure in disabling nagle.
+  return 0;
+}
+
+void TCPClientSocketWin::LogConnectCompletion(int net_error) {
+  scoped_refptr<NetLog::EventParameters> params;
+  if (net_error != OK)
+    params = new NetLogIntegerParameter("net_error", net_error);
+  net_log_.EndEvent(NetLog::TYPE_TCP_CONNECT, params);
+  if (net_error == OK)
+    UpdateConnectionTypeHistograms(CONNECTION_ANY);
 }
 
 void TCPClientSocketWin::DoReadCallback(int rv) {
@@ -624,46 +703,30 @@
 }
 
 void TCPClientSocketWin::DidCompleteConnect() {
-  DCHECK(waiting_connect_);
+  DCHECK_EQ(next_connect_state_, CONNECT_STATE_CONNECT_COMPLETE);
   int result;
 
-  waiting_connect_ = false;
-
   WSANETWORKEVENTS events;
   int rv = WSAEnumNetworkEvents(socket_, core_->read_overlapped_.hEvent,
                                 &events);
+  int os_error = 0;
   if (rv == SOCKET_ERROR) {
     NOTREACHED();
-    result = MapWinsockError(WSAGetLastError());
+    os_error = WSAGetLastError();
+    result = MapWinsockError(os_error);
   } else if (events.lNetworkEvents & FD_CONNECT) {
-    int os_error = events.iErrorCode[FD_CONNECT_BIT];
-    if (current_ai_->ai_next && ShouldTryNextAddress(os_error)) {
-      // Try using the next address.
-      const struct addrinfo* next = current_ai_->ai_next;
-      Disconnect();
-      current_ai_ = next;
-      scoped_refptr<LoadLog> load_log;
-      load_log.swap(load_log_);
-      TRACE_EVENT_END("socket.connect", this, "");
-      LoadLog::EndEvent(load_log, LoadLog::TYPE_TCP_CONNECT);
-      result = Connect(read_callback_, load_log);
-    } else {
-      result = MapConnectError(os_error);
-      TRACE_EVENT_END("socket.connect", this, "");
-      LoadLog::EndEvent(load_log_, LoadLog::TYPE_TCP_CONNECT);
-      load_log_ = NULL;
-    }
+    os_error = events.iErrorCode[FD_CONNECT_BIT];
+    result = MapConnectError(os_error);
   } else {
     NOTREACHED();
     result = ERR_UNEXPECTED;
-    TRACE_EVENT_END("socket.connect", this, "");
-    LoadLog::EndEvent(load_log_, LoadLog::TYPE_TCP_CONNECT);
-    load_log_ = NULL;
   }
 
-  if (result != ERR_IO_PENDING) {
-    UpdateConnectionTypeHistograms(CONNECTION_ANY, result >= 0);
-    DoReadCallback(result);
+  connect_os_error_ = os_error;
+  rv = DoConnectLoop(result);
+  if (rv != ERR_IO_PENDING) {
+    LogConnectCompletion(rv);
+    DoReadCallback(rv);
   }
 }
 
@@ -673,9 +736,12 @@
   BOOL ok = WSAGetOverlappedResult(socket_, &core_->read_overlapped_,
                                    &num_bytes, FALSE, &flags);
   WSAResetEvent(core_->read_overlapped_.hEvent);
-  TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", num_bytes));
   waiting_read_ = false;
   core_->read_iobuffer_ = NULL;
+  if (ok) {
+    net_log_.AddEvent(NetLog::TYPE_SOCKET_BYTES_RECEIVED,
+                      new NetLogIntegerParameter("num_bytes", num_bytes));
+  }
   DoReadCallback(ok ? num_bytes : MapWinsockError(WSAGetLastError()));
 }
 
@@ -686,10 +752,26 @@
   BOOL ok = WSAGetOverlappedResult(socket_, &core_->write_overlapped_,
                                    &num_bytes, FALSE, &flags);
   WSAResetEvent(core_->write_overlapped_.hEvent);
-  TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", num_bytes));
   waiting_write_ = false;
+  int rv;
+  if (!ok) {
+    rv = MapWinsockError(WSAGetLastError());
+  } else {
+    rv = static_cast<int>(num_bytes);
+    if (rv > core_->write_buffer_length_ || rv < 0) {
+      // It seems that some winsock interceptors report that more was written
+      // than was available. Treat this as an error.  http://crbug.com/27870
+      LOG(ERROR) << "Detected broken LSP: Asked to write "
+                 << core_->write_buffer_length_ << " bytes, but " << rv
+                 << " bytes reported.";
+      rv = ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES;
+    } else {
+      net_log_.AddEvent(NetLog::TYPE_SOCKET_BYTES_SENT,
+                        new NetLogIntegerParameter("num_bytes", rv));
+    }
+  }
   core_->write_iobuffer_ = NULL;
-  DoWriteCallback(ok ? num_bytes : MapWinsockError(WSAGetLastError()));
+  DoWriteCallback(rv);
 }
 
 }  // namespace net
diff --git a/net/socket/tcp_client_socket_win.h b/net/socket/tcp_client_socket_win.h
index 9ad1632..4ac57a8 100644
--- a/net/socket/tcp_client_socket_win.h
+++ b/net/socket/tcp_client_socket_win.h
@@ -1,34 +1,39 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_SOCKET_TCP_CLIENT_SOCKET_WIN_H_
 #define NET_SOCKET_TCP_CLIENT_SOCKET_WIN_H_
 
+#include <winsock2.h>
+
+#include "base/non_thread_safe.h"
 #include "base/object_watcher.h"
 #include "net/base/address_list.h"
 #include "net/base/completion_callback.h"
+#include "net/base/net_log.h"
 #include "net/socket/client_socket.h"
 
 namespace net {
 
-class LoadLog;
+class BoundNetLog;
 
-class TCPClientSocketWin : public ClientSocket {
+class TCPClientSocketWin : public ClientSocket, NonThreadSafe {
  public:
   // The IP address(es) and port number to connect to.  The TCP socket will try
   // each IP address in the list until it succeeds in establishing a
   // connection.
-  explicit TCPClientSocketWin(const AddressList& addresses);
+  TCPClientSocketWin(const AddressList& addresses, net::NetLog* net_log);
 
   ~TCPClientSocketWin();
 
   // ClientSocket methods:
-  virtual int Connect(CompletionCallback* callback, LoadLog* load_log);
+  virtual int Connect(CompletionCallback* callback);
   virtual void Disconnect();
   virtual bool IsConnected() const;
   virtual bool IsConnectedAndIdle() const;
-  virtual int GetPeerName(struct sockaddr* name, socklen_t* namelen);
+  virtual int GetPeerAddress(AddressList* address) const;
+  virtual const BoundNetLog& NetLog() const { return net_log_; }
 
   // Socket methods:
   // Multiple outstanding requests are not supported.
@@ -40,12 +45,35 @@
   virtual bool SetSendBufferSize(int32 size);
 
  private:
+  // State machine for connecting the socket.
+  enum ConnectState {
+    CONNECT_STATE_CONNECT,
+    CONNECT_STATE_CONNECT_COMPLETE,
+    CONNECT_STATE_NONE,
+  };
+
   class Core;
 
-  // Performs the actual connect().  Returns a net error code.
+  // State machine used by Connect().
+  int DoConnectLoop(int result);
   int DoConnect();
+  int DoConnectComplete(int result);
 
+  // Helper used by Disconnect(), which disconnects minus the logging and
+  // resetting of current_ai_.
+  void DoDisconnect();
+
+  // Returns true if a Connect() is in progress.
+  bool waiting_connect() const {
+    return next_connect_state_ != CONNECT_STATE_NONE;
+  }
+
+  // Returns the OS error code (or 0 on success).
   int CreateSocket(const struct addrinfo* ai);
+
+  // Called after Connect() has completed with |net_error|.
+  void LogConnectCompletion(int net_error);
+
   void DoReadCallback(int rv);
   void DoWriteCallback(int rv);
   void DidCompleteConnect();
@@ -61,7 +89,6 @@
   const struct addrinfo* current_ai_;
 
   // The various states that the socket could be in.
-  bool waiting_connect_;
   bool waiting_read_;
   bool waiting_write_;
 
@@ -76,7 +103,13 @@
   // External callback; called when write is complete.
   CompletionCallback* write_callback_;
 
-  scoped_refptr<LoadLog> load_log_;
+  // The next state for the Connect() state machine.
+  ConnectState next_connect_state_;
+
+  // The OS error that CONNECT_STATE_CONNECT last completed with.
+  int connect_os_error_;
+
+  BoundNetLog net_log_;
 
   DISALLOW_COPY_AND_ASSIGN(TCPClientSocketWin);
 };
diff --git a/net/socket/tcp_pinger.h b/net/socket/tcp_pinger.h
index ae543c5..96fa4fd 100644
--- a/net/socket/tcp_pinger.h
+++ b/net/socket/tcp_pinger.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -9,6 +9,7 @@
 #include "base/ref_counted.h"
 #include "base/scoped_ptr.h"
 #include "base/task.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
 #include "base/thread.h"
 #include "base/waitable_event.h"
 #include "net/base/address_list.h"
@@ -85,8 +86,8 @@
     }
 
     void DoConnect() {
-      sock_.reset(new TCPClientSocket(addr_));
-      int rv = sock_->Connect(&connect_callback_, NULL);
+      sock_.reset(new TCPClientSocket(addr_, NULL));
+      int rv = sock_->Connect(&connect_callback_);
       // Regardless of success or failure, if we're done now,
       // signal the customer.
       if (rv != ERR_IO_PENDING)
@@ -106,7 +107,10 @@
 
     int TimedWaitForResult(base::TimeDelta tryTimeout) {
       event_.TimedWait(tryTimeout);
-      return net_error_;
+      // In case of timeout, the value of net_error_ should be ERR_IO_PENDING.
+      // However, a harmless data race can happen if TimedWait times out right
+      // before event_.Signal() is called in ConnectDone().
+      return ANNOTATE_UNPROTECTED_READ(net_error_);
     }
 
     int WaitForResult() {
diff --git a/net/socket/tcp_pinger_unittest.cc b/net/socket/tcp_pinger_unittest.cc
index 2cffa8a..c415ac3 100644
--- a/net/socket/tcp_pinger_unittest.cc
+++ b/net/socket/tcp_pinger_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -28,7 +28,7 @@
     LOG(INFO) << "TCPPinger accepted connection";
     connected_sock_ = connection;
   }
-  virtual void DidRead(ListenSocket*, const std::string& s) {
+  virtual void DidRead(ListenSocket*, const char* str, int len) {
     // Not really needed yet, as TCPPinger doesn't support Read
     connected_sock_->Send(std::string("HTTP/1.1 404 Not Found"), true);
     connected_sock_ = NULL;
@@ -66,10 +66,10 @@
 TEST_F(TCPPingerTest, Ping) {
   net::AddressList addr;
   scoped_refptr<net::HostResolver> resolver(
-      net::CreateSystemHostResolver(NULL));
+      net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism));
 
   net::HostResolver::RequestInfo info("localhost", listen_port_);
-  int rv = resolver->Resolve(info, &addr, NULL, NULL, NULL);
+  int rv = resolver->Resolve(info, &addr, NULL, NULL, net::BoundNetLog());
   EXPECT_EQ(rv, net::OK);
 
   net::TCPPinger pinger(addr);
@@ -80,13 +80,13 @@
 TEST_F(TCPPingerTest, PingFail) {
   net::AddressList addr;
   scoped_refptr<net::HostResolver> resolver(
-      net::CreateSystemHostResolver(NULL));
+      net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism));
 
   // "Kill" "server"
   listen_sock_ = NULL;
 
   net::HostResolver::RequestInfo info("localhost", listen_port_);
-  int rv = resolver->Resolve(info, &addr, NULL, NULL, NULL);
+  int rv = resolver->Resolve(info, &addr, NULL, NULL, net::BoundNetLog());
   EXPECT_EQ(rv, net::OK);
 
   net::TCPPinger pinger(addr);
diff --git a/net/socket_stream/socket_stream.cc b/net/socket_stream/socket_stream.cc
index 2a01f17..d1ead09 100644
--- a/net/socket_stream/socket_stream.cc
+++ b/net/socket_stream/socket_stream.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 //
@@ -18,6 +18,8 @@
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/http_request_info.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
 #include "net/socket/client_socket_factory.h"
@@ -26,7 +28,6 @@
 #include "net/socket/socks_client_socket.h"
 #include "net/socket/tcp_client_socket.h"
 #include "net/socket_stream/socket_stream_metrics.h"
-#include "net/socket_stream/socket_stream_throttle.h"
 #include "net/url_request/url_request.h"
 
 static const int kMaxPendingSendAllowed = 32768;  // 32 kilobytes.
@@ -34,15 +35,19 @@
 
 namespace net {
 
+SocketStream::ResponseHeaders::ResponseHeaders() : IOBuffer() {}
+SocketStream::ResponseHeaders::~ResponseHeaders() { data_ = NULL; }
+
 void SocketStream::ResponseHeaders::Realloc(size_t new_size) {
   headers_.reset(static_cast<char*>(realloc(headers_.release(), new_size)));
 }
 
 SocketStream::SocketStream(const GURL& url, Delegate* delegate)
-    : url_(url),
-      delegate_(delegate),
+    : delegate_(delegate),
+      url_(url),
       max_pending_send_allowed_(kMaxPendingSendAllowed),
       next_state_(STATE_NONE),
+      http_auth_handler_factory_(NULL),
       factory_(ClientSocketFactory::GetDefaultFactory()),
       proxy_mode_(kDirectConnection),
       proxy_url_(url),
@@ -58,23 +63,19 @@
       current_write_buf_(NULL),
       write_buf_offset_(0),
       write_buf_size_(0),
-      throttle_(
-          SocketStreamThrottle::GetSocketStreamThrottleForScheme(
-              url.scheme())),
-      metrics_(new SocketStreamMetrics(url)),
-      ALLOW_THIS_IN_INITIALIZER_LIST(
-          request_tracker_node_(this)) {
+      closing_(false),
+      metrics_(new SocketStreamMetrics(url)) {
   DCHECK(MessageLoop::current()) <<
       "The current MessageLoop must exist";
   DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) <<
       "The current MessageLoop must be TYPE_IO";
   DCHECK(delegate_);
-  DCHECK(throttle_);
 }
 
 SocketStream::~SocketStream() {
   set_context(NULL);
   DCHECK(!delegate_);
+  DCHECK(!pac_request_);
 }
 
 SocketStream::UserData* SocketStream::GetUserData(
@@ -95,21 +96,27 @@
   context_ = context;
 
   if (prev_context != context) {
-    if (prev_context)
-      prev_context->socket_stream_tracker()->Remove(this);
+    if (prev_context && pac_request_) {
+      prev_context->proxy_service()->CancelPacRequest(pac_request_);
+      pac_request_ = NULL;
+    }
+
+    net_log_.EndEvent(NetLog::TYPE_REQUEST_ALIVE, NULL);
+    net_log_ = BoundNetLog();
+
     if (context) {
-      if (!load_log_) {
-        // Create the LoadLog -- we waited until now to create it so we know
-        // what constraints the URLRequestContext is enforcing on log levels.
-        load_log_ = context->socket_stream_tracker()->CreateLoadLog();
-      }
-      context->socket_stream_tracker()->Add(this);
+      net_log_ = BoundNetLog::Make(
+          context->net_log(),
+          NetLog::SOURCE_SOCKET_STREAM);
+
+      net_log_.BeginEvent(NetLog::TYPE_REQUEST_ALIVE, NULL);
     }
   }
 
-  if (context_)
+  if (context_) {
     host_resolver_ = context_->host_resolver();
-
+    http_auth_handler_factory_ = context_->http_auth_handler_factory();
+  }
 }
 
 void SocketStream::Connect() {
@@ -125,7 +132,9 @@
   // Open a connection asynchronously, so that delegate won't be called
   // back before returning Connect().
   next_state_ = STATE_RESOLVE_PROXY;
-  LoadLog::BeginEvent(load_log_, LoadLog::TYPE_SOCKET_STREAM_CONNECT);
+  net_log_.BeginEvent(
+      NetLog::TYPE_SOCKET_STREAM_CONNECT,
+      new NetLogStringParameter("url", url_.possibly_invalid_spec()));
   MessageLoop::current()->PostTask(
       FROM_HERE,
       NewRunnableMethod(this, &SocketStream::DoLoop, OK));
@@ -171,10 +180,13 @@
       "The current MessageLoop must exist";
   DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) <<
       "The current MessageLoop must be TYPE_IO";
-  if (!socket_.get() || !socket_->IsConnected() || next_state_ == STATE_NONE)
+  // If next_state_ is STATE_NONE, the socket was not opened, or already
+  // closed.  So, return immediately.
+  // Otherwise, it might call Finish() more than once, so breaks balance
+  // of AddRef() and Release() in Connect() and Finish(), respectively.
+  if (next_state_ == STATE_NONE)
     return;
-  socket_->Disconnect();
-  next_state_ = STATE_CLOSE;
+  closing_ = true;
   // Close asynchronously, so that delegate won't be called
   // back before returning Close().
   MessageLoop::current()->PostTask(
@@ -188,7 +200,7 @@
       "The current MessageLoop must exist";
   DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) <<
       "The current MessageLoop must be TYPE_IO";
-  DCHECK(auth_handler_);
+  DCHECK(auth_handler_.get());
   if (!socket_.get()) {
     LOG(ERROR) << "Socket is closed before restarting with auth.";
     return;
@@ -211,7 +223,9 @@
   if (!delegate_)
     return;
   delegate_ = NULL;
-  LoadLog::AddEvent(load_log_, LoadLog::TYPE_CANCELLED);
+  net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
+  // We don't need to send pending data when client detach the delegate.
+  pending_write_bufs_.clear();
   Close();
 }
 
@@ -234,7 +248,6 @@
   if (delegate) {
     delegate->OnClose(this);
   }
-  throttle_->OnClose(this);
   Release();
 }
 
@@ -250,7 +263,7 @@
 }
 
 void SocketStream::CopyAddrInfo(struct addrinfo* head) {
-  addresses_.Copy(head);
+  addresses_.Copy(head, true);
 }
 
 int SocketStream::DidEstablishConnection() {
@@ -261,7 +274,7 @@
   next_state_ = STATE_READ_WRITE;
   metrics_->OnConnected();
 
-  LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKET_STREAM_CONNECT);
+  net_log_.EndEvent(NetLog::TYPE_SOCKET_STREAM_CONNECT, NULL);
   if (delegate_)
     delegate_->OnConnected(this, max_pending_send_allowed_);
 
@@ -271,25 +284,22 @@
 int SocketStream::DidReceiveData(int result) {
   DCHECK(read_buf_);
   DCHECK_GT(result, 0);
-  LoadLog::AddEvent(load_log_, LoadLog::TYPE_SOCKET_STREAM_RECEIVED);
+  net_log_.AddEvent(NetLog::TYPE_SOCKET_STREAM_RECEIVED, NULL);
   int len = result;
   metrics_->OnRead(len);
-  result = throttle_->OnRead(this, read_buf_->data(), len, &io_callback_);
   if (delegate_) {
     // Notify recevied data to delegate.
     delegate_->OnReceivedData(this, read_buf_->data(), len);
   }
   read_buf_ = NULL;
-  return result;
+  return OK;
 }
 
 int SocketStream::DidSendData(int result) {
   DCHECK_GT(result, 0);
-  LoadLog::AddEvent(load_log_, LoadLog::TYPE_SOCKET_STREAM_SENT);
+  net_log_.AddEvent(NetLog::TYPE_SOCKET_STREAM_SENT, NULL);
   int len = result;
   metrics_->OnWrite(len);
-  result = throttle_->OnWrite(this, current_write_buf_->data(), len,
-                              &io_callback_);
   current_write_buf_ = NULL;
   if (delegate_)
     delegate_->OnSentData(this, len);
@@ -308,7 +318,7 @@
   } else {
     write_buf_offset_ += len;
   }
-  return result;
+  return OK;
 }
 
 void SocketStream::OnIOCompleted(int result) {
@@ -409,7 +419,8 @@
     // close the connection.
     if (state != STATE_READ_WRITE && result < ERR_IO_PENDING) {
       DCHECK_EQ(next_state_, STATE_CLOSE);
-      LoadLog::EndEvent(load_log_, LoadLog::TYPE_SOCKET_STREAM_CONNECT);
+      net_log_.EndEvent(NetLog::TYPE_SOCKET_STREAM_CONNECT,
+                        new NetLogIntegerParameter("net_error", result));
     }
   } while (result != ERR_IO_PENDING);
 }
@@ -424,12 +435,10 @@
   }
 
   return proxy_service()->ResolveProxy(
-      proxy_url_, &proxy_info_, &io_callback_, &pac_request_, load_log_);
+      proxy_url_, &proxy_info_, &io_callback_, &pac_request_, net_log_);
 }
 
 int SocketStream::DoResolveProxyComplete(int result) {
-  next_state_ = STATE_RESOLVE_HOST;
-
   pac_request_ = NULL;
   if (result != OK) {
     LOG(ERROR) << "Failed to resolve proxy: " << result;
@@ -453,12 +462,20 @@
     }
   }
 
+  if (proxy_info_.is_empty()) {
+    // No proxies/direct to choose from. This happens when we don't support any
+    // of the proxies in the returned list.
+    return ERR_NO_SUPPORTED_PROXIES;
+  }
+
+  next_state_ = STATE_RESOLVE_HOST;
   return OK;
 }
 
 int SocketStream::DoResolveHost() {
   next_state_ = STATE_RESOLVE_HOST_COMPLETE;
 
+  DCHECK(!proxy_info_.is_empty());
   if (proxy_info_.is_direct())
     proxy_mode_ = kDirectConnection;
   else if (proxy_info_.proxy_server().is_socks())
@@ -483,13 +500,13 @@
   DCHECK(host_resolver_.get());
   resolver_.reset(new SingleRequestHostResolver(host_resolver_.get()));
   return resolver_->Resolve(resolve_info, &addresses_, &io_callback_,
-                            load_log_);
+                            net_log_);
 }
 
 int SocketStream::DoResolveHostComplete(int result) {
   if (result == OK) {
     next_state_ = STATE_TCP_CONNECT;
-    result = throttle_->OnStartOpenConnection(this, &io_callback_);
+    result = delegate_->OnStartOpenConnection(this, &io_callback_);
     if (result == net::ERR_IO_PENDING)
       metrics_->OnWaitConnection();
   } else {
@@ -502,9 +519,10 @@
 int SocketStream::DoTcpConnect() {
   next_state_ = STATE_TCP_CONNECT_COMPLETE;
   DCHECK(factory_);
-  socket_.reset(factory_->CreateTCPClientSocket(addresses_));
+  socket_.reset(factory_->CreateTCPClientSocket(addresses_,
+                                                 net_log_.net_log()));
   metrics_->OnStartConnection();
-  return socket_->Connect(&io_callback_, load_log_);
+  return socket_->Connect(&io_callback_);
 }
 
 int SocketStream::DoTcpConnectComplete(int result) {
@@ -540,15 +558,23 @@
     std::string authorization_headers;
 
     if (!auth_handler_.get()) {
-      // First attempt.  Find auth from the proxy address.
+      // Do preemptive authentication.
       HttpAuthCache::Entry* entry = auth_cache_.LookupByPath(
           ProxyAuthOrigin(), std::string());
-      if (entry && !entry->handler()->is_connection_based()) {
-        auth_identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
-        auth_identity_.invalid = false;
-        auth_identity_.username = entry->username();
-        auth_identity_.password = entry->password();
-        auth_handler_ = entry->handler();
+      if (entry) {
+        scoped_ptr<HttpAuthHandler> handler_preemptive;
+        int rv_create = http_auth_handler_factory_->
+            CreatePreemptiveAuthHandlerFromString(
+                entry->auth_challenge(), HttpAuth::AUTH_PROXY,
+                ProxyAuthOrigin(), entry->IncrementNonceCount(),
+                net_log_, &handler_preemptive);
+        if (rv_create == OK) {
+          auth_identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
+          auth_identity_.invalid = false;
+          auth_identity_.username = entry->username();
+          auth_identity_.password = entry->password();
+          auth_handler_.swap(handler_preemptive);
+        }
       }
     }
 
@@ -556,14 +582,21 @@
     // HttpRequestInfo.
     // TODO(ukai): Add support other authentication scheme.
     if (auth_handler_.get() && auth_handler_->scheme() == "basic") {
-      std::string credentials = auth_handler_->GenerateCredentials(
-          auth_identity_.username,
-          auth_identity_.password,
+      HttpRequestInfo request_info;
+      std::string auth_token;
+      int rv = auth_handler_->GenerateAuthToken(
+          &auth_identity_.username,
+          &auth_identity_.password,
+          &request_info,
           NULL,
-          &proxy_info_);
+          &auth_token);
+      // TODO(cbentzel): Support async auth handlers.
+      DCHECK_NE(ERR_IO_PENDING, rv);
+      if (rv != OK)
+        return rv;
       authorization_headers.append(
           HttpAuth::GetAuthorizationHeaderName(HttpAuth::AUTH_PROXY) +
-          ": " + credentials + "\r\n");
+          ": " + auth_token + "\r\n");
     }
 
     tunnel_request_headers_->headers_ = StringPrintf(
@@ -676,8 +709,9 @@
       return OK;
     case 407:  // Proxy Authentication Required.
       result = HandleAuthChallenge(headers.get());
-      if (result == ERR_PROXY_AUTH_REQUESTED &&
+      if (result == ERR_PROXY_AUTH_UNSUPPORTED &&
           auth_handler_.get() && delegate_) {
+        DCHECK(!proxy_info_.is_empty());
         auth_info_ = new AuthChallengeInfo;
         auth_info_->is_proxy = true;
         auth_info_->host_and_port =
@@ -707,13 +741,14 @@
   HostResolver::RequestInfo req_info(url_.HostNoBrackets(),
                                      url_.EffectiveIntPort());
 
+  DCHECK(!proxy_info_.is_empty());
   if (proxy_info_.proxy_server().scheme() == ProxyServer::SCHEME_SOCKS5)
     s = new SOCKS5ClientSocket(s, req_info);
   else
     s = new SOCKSClientSocket(s, req_info, host_resolver_.get());
   socket_.reset(s);
   metrics_->OnSOCKSProxy();
-  return socket_->Connect(&io_callback_, load_log_);
+  return socket_->Connect(&io_callback_);
 }
 
 int SocketStream::DoSOCKSConnectComplete(int result) {
@@ -736,7 +771,7 @@
       socket_.release(), url_.HostNoBrackets(), ssl_config_));
   next_state_ = STATE_SSL_CONNECT_COMPLETE;
   metrics_->OnSSLConnection();
-  return socket_->Connect(&io_callback_, load_log_);
+  return socket_->Connect(&io_callback_);
 }
 
 int SocketStream::DoSSLConnectComplete(int result) {
@@ -793,6 +828,15 @@
     return ERR_CONNECTION_CLOSED;
   }
 
+  // If client has requested close(), and there's nothing to write, then
+  // let's close the socket.
+  // We don't care about receiving data after the socket is closed.
+  if (closing_ && !write_buf_ && pending_write_bufs_.empty()) {
+    socket_->Disconnect();
+    next_state_ = STATE_CLOSE;
+    return OK;
+  }
+
   next_state_ = STATE_READ_WRITE;
 
   if (!read_buf_) {
@@ -840,6 +884,7 @@
 }
 
 GURL SocketStream::ProxyAuthOrigin() const {
+  DCHECK(!proxy_info_.is_empty());
   return GURL("http://" + proxy_info_.proxy_server().host_and_port());
 }
 
@@ -855,35 +900,36 @@
     if (auth_identity_.source != HttpAuth::IDENT_SRC_PATH_LOOKUP)
       auth_cache_.Remove(auth_origin,
                          auth_handler_->realm(),
+                         auth_handler_->scheme(),
                          auth_identity_.username,
                          auth_identity_.password);
-    auth_handler_ = NULL;
+    auth_handler_.reset();
     auth_identity_ = HttpAuth::Identity();
   }
 
   auth_identity_.invalid = true;
-  HttpAuth::ChooseBestChallenge(headers, HttpAuth::AUTH_PROXY, auth_origin,
-                                &auth_handler_);
-  if (!auth_handler_) {
+  std::set<std::string> disabled_schemes;
+  HttpAuth::ChooseBestChallenge(http_auth_handler_factory_, headers,
+                                HttpAuth::AUTH_PROXY,
+                                auth_origin, disabled_schemes,
+                                net_log_, &auth_handler_);
+  if (!auth_handler_.get()) {
     LOG(ERROR) << "Can't perform auth to the proxy " << auth_origin;
     return ERR_TUNNEL_CONNECTION_FAILED;
   }
   if (auth_handler_->NeedsIdentity()) {
-    HttpAuthCache::Entry* entry = auth_cache_.LookupByRealm(
-        auth_origin, auth_handler_->realm());
+    // We only support basic authentication scheme now.
+    // TODO(ukai): Support other authentication scheme.
+    HttpAuthCache::Entry* entry =
+      auth_cache_.Lookup(auth_origin, auth_handler_->realm(), "basic");
     if (entry) {
-      if (entry->handler()->scheme() != "basic") {
-        // We only support basic authentication scheme now.
-        // TODO(ukai): Support other authentication scheme.
-        return ERR_TUNNEL_CONNECTION_FAILED;
-      }
       auth_identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
       auth_identity_.invalid = false;
       auth_identity_.username = entry->username();
       auth_identity_.password = entry->password();
       // Restart with auth info.
     }
-    return ERR_PROXY_AUTH_REQUESTED;
+    return ERR_PROXY_AUTH_UNSUPPORTED;
   } else {
     auth_identity_.invalid = false;
   }
@@ -899,8 +945,12 @@
 
 void SocketStream::DoRestartWithAuth() {
   DCHECK_EQ(next_state_, STATE_AUTH_REQUIRED);
-  auth_cache_.Add(ProxyAuthOrigin(), auth_handler_,
-                  auth_identity_.username, auth_identity_.password,
+  auth_cache_.Add(ProxyAuthOrigin(),
+                  auth_handler_->realm(),
+                  auth_handler_->scheme(),
+                  auth_handler_->challenge(),
+                  auth_identity_.username,
+                  auth_identity_.password,
                   std::string());
 
   tunnel_request_headers_ = NULL;
@@ -939,10 +989,5 @@
   return context_->proxy_service();
 }
 
-void SocketStream::GetInfoForTracker(
-    RequestTracker<SocketStream>::RecentRequestInfo* info) const {
-  info->original_url = url_;
-  info->load_log = load_log_;
-}
-
 }  // namespace net
+
diff --git a/net/socket_stream/socket_stream.h b/net/socket_stream/socket_stream.h
index cf6a6b0..d0e8b2e 100644
--- a/net/socket_stream/socket_stream.h
+++ b/net/socket_stream/socket_stream.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -17,12 +17,13 @@
 #include "net/base/address_list.h"
 #include "net/base/completion_callback.h"
 #include "net/base/io_buffer.h"
+#include "net/base/net_log.h"
+#include "net/base/net_errors.h"
 #include "net/http/http_auth.h"
 #include "net/http/http_auth_cache.h"
 #include "net/http/http_auth_handler.h"
 #include "net/proxy/proxy_service.h"
 #include "net/socket/tcp_client_socket.h"
-#include "net/url_request/request_tracker.h"
 #include "net/url_request/url_request_context.h"
 
 namespace net {
@@ -30,11 +31,10 @@
 class AuthChallengeInfo;
 class ClientSocketFactory;
 class HostResolver;
-class LoadLog;
+class HttpAuthHandlerFactory;
 class SSLConfigService;
 class SingleRequestHostResolver;
 class SocketStreamMetrics;
-class SocketStreamThrottle;
 
 // SocketStream is used to implement Web Sockets.
 // It provides plain full-duplex stream with proxy and SSL support.
@@ -57,6 +57,11 @@
    public:
     virtual ~Delegate() {}
 
+    virtual int OnStartOpenConnection(SocketStream* socket,
+                                      CompletionCallback* callback) {
+      return OK;
+    }
+
     // Called when socket stream has been connected.  The socket stream accepts
     // at most |max_pending_send_allowed| so that a client of the socket stream
     // should keep track of how much it has pending and shouldn't go over
@@ -100,6 +105,7 @@
   void SetUserData(const void* key, UserData* data);
 
   const GURL& url() const { return url_; }
+  bool is_secure() const;
   const AddressList& address_list() const { return addresses_; }
   Delegate* delegate() const { return delegate_; }
   int max_pending_send_allowed() const { return max_pending_send_allowed_; }
@@ -107,32 +113,32 @@
   URLRequestContext* context() const { return context_.get(); }
   void set_context(URLRequestContext* context);
 
-  LoadLog* load_log() const { return load_log_; }
+  BoundNetLog* net_log() { return &net_log_; }
 
   // Opens the connection on the IO thread.
   // Once the connection is established, calls delegate's OnConnected.
-  void Connect();
+  virtual void Connect();
 
   // Requests to send |len| bytes of |data| on the connection.
   // Returns true if |data| is buffered in the job.
   // Returns false if size of buffered data would exceeds
   // |max_pending_send_allowed_| and |data| is not sent at all.
-  bool SendData(const char* data, int len);
+  virtual bool SendData(const char* data, int len);
 
   // Requests to close the connection.
   // Once the connection is closed, calls delegate's OnClose.
-  void Close();
+  virtual void Close();
 
   // Restarts with authentication info.
   // Should be used for response of OnAuthRequired.
-  void RestartWithAuth(
+  virtual void RestartWithAuth(
       const std::wstring& username,
       const std::wstring& password);
 
   // Detach delegate.  Call before delegate is deleted.
   // Once delegate is detached, close the socket stream and never call delegate
   // back.
-  void DetachDelegate();
+  virtual void DetachDelegate();
 
   // Sets an alternative HostResolver. For testing purposes only.
   void SetHostResolver(HostResolver* host_resolver);
@@ -141,6 +147,12 @@
   // |factory|.  For testing purposes only.
   void SetClientSocketFactory(ClientSocketFactory* factory);
 
+ protected:
+  friend class base::RefCountedThreadSafe<SocketStream>;
+  virtual ~SocketStream();
+
+  Delegate* delegate_;
+
  private:
   class RequestHeaders : public IOBuffer {
    public:
@@ -158,7 +170,7 @@
 
   class ResponseHeaders : public IOBuffer {
    public:
-    ResponseHeaders() : IOBuffer() {}
+    ResponseHeaders();
 
     void SetDataOffset(size_t offset) { data_ = headers_.get() + offset; }
     char* headers() const { return headers_.get(); }
@@ -166,7 +178,7 @@
     void Realloc(size_t new_size);
 
    private:
-     ~ResponseHeaders() { data_ = NULL; }
+     ~ResponseHeaders();
 
     scoped_ptr_malloc<char> headers_;
   };
@@ -199,9 +211,6 @@
   };
 
   typedef std::deque< scoped_refptr<IOBufferWithSize> > PendingDataQueue;
-  friend class RequestTracker<SocketStream>;
-  friend class base::RefCountedThreadSafe<SocketStream>;
-  ~SocketStream();
 
   friend class WebSocketThrottleTest;
 
@@ -247,17 +256,12 @@
 
   int HandleCertificateError(int result);
 
-  bool is_secure() const;
   SSLConfigService* ssl_config_service() const;
   ProxyService* proxy_service() const;
 
-  void GetInfoForTracker(
-      RequestTracker<SocketStream>::RecentRequestInfo* info) const;
-
-  scoped_refptr<LoadLog> load_log_;
+  BoundNetLog net_log_;
 
   GURL url_;
-  Delegate* delegate_;
   int max_pending_send_allowed_;
   scoped_refptr<URLRequestContext> context_;
 
@@ -266,6 +270,7 @@
 
   State next_state_;
   scoped_refptr<HostResolver> host_resolver_;
+  HttpAuthHandlerFactory* http_auth_handler_factory_;
   ClientSocketFactory* factory_;
 
   ProxyMode proxy_mode_;
@@ -275,7 +280,7 @@
   ProxyInfo proxy_info_;
 
   HttpAuthCache auth_cache_;
-  scoped_refptr<HttpAuthHandler> auth_handler_;
+  scoped_ptr<HttpAuthHandler> auth_handler_;
   HttpAuth::Identity auth_identity_;
   scoped_refptr<AuthChallengeInfo> auth_info_;
 
@@ -313,12 +318,10 @@
   int write_buf_size_;
   PendingDataQueue pending_write_bufs_;
 
-  SocketStreamThrottle* throttle_;
+  bool closing_;
 
   scoped_ptr<SocketStreamMetrics> metrics_;
 
-  RequestTracker<SocketStream>::Node request_tracker_node_;
-
   DISALLOW_COPY_AND_ASSIGN(SocketStream);
 };
 
diff --git a/net/socket_stream/socket_stream_job.cc b/net/socket_stream/socket_stream_job.cc
new file mode 100644
index 0000000..c8849a5
--- /dev/null
+++ b/net/socket_stream/socket_stream_job.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2010 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/socket_stream/socket_stream_job.h"
+
+#include "net/socket_stream/socket_stream_job_manager.h"
+
+namespace net {
+
+static SocketStreamJobManager* GetJobManager() {
+  return Singleton<SocketStreamJobManager>::get();
+}
+
+// static
+SocketStreamJob::ProtocolFactory* SocketStreamJob::RegisterProtocolFactory(
+    const std::string& scheme, ProtocolFactory* factory) {
+  return GetJobManager()->RegisterProtocolFactory(scheme, factory);
+}
+
+// static
+SocketStreamJob* SocketStreamJob::CreateSocketStreamJob(
+    const GURL& url, SocketStream::Delegate* delegate) {
+  return GetJobManager()->CreateJob(url, delegate);
+}
+
+}  // namespace net
diff --git a/net/socket_stream/socket_stream_job.h b/net/socket_stream/socket_stream_job.h
new file mode 100644
index 0000000..618620c
--- /dev/null
+++ b/net/socket_stream/socket_stream_job.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2010 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_SOCKET_STREAM_SOCKET_STREAM_JOB_H_
+#define NET_SOCKET_STREAM_SOCKET_STREAM_JOB_H_
+
+#include <string>
+
+#include "base/ref_counted.h"
+#include "net/socket_stream/socket_stream.h"
+
+class GURL;
+
+namespace net {
+
+// SocketStreamJob represents full-duplex communication over SocketStream.
+// If a protocol (e.g. WebSocket protocol) needs to inspect/modify data
+// over SocketStream, you can implement protocol specific job (e.g.
+// WebSocketJob) to do some work on data over SocketStream.
+// Registers the protocol specific SocketStreamJob by RegisterProtocolFactory
+// and call CreateSocketStreamJob to create SocketStreamJob for the URL.
+class SocketStreamJob : public base::RefCountedThreadSafe<SocketStreamJob> {
+ public:
+  // Callback function implemented by protocol handlers to create new jobs.
+  typedef SocketStreamJob* (ProtocolFactory)(const GURL& url,
+                                             SocketStream::Delegate* delegate);
+
+  static ProtocolFactory* RegisterProtocolFactory(const std::string& scheme,
+                                                  ProtocolFactory* factory);
+
+  static SocketStreamJob* CreateSocketStreamJob(
+      const GURL& url, SocketStream::Delegate* delegate);
+
+  SocketStreamJob() {}
+  void InitSocketStream(SocketStream* socket) {
+    socket_ = socket;
+  }
+
+  virtual SocketStream::UserData *GetUserData(const void* key) const {
+    return socket_->GetUserData(key);
+  }
+  virtual void SetUserData(const void* key, SocketStream::UserData* data) {
+    socket_->SetUserData(key, data);
+  }
+
+  URLRequestContext* context() const {
+    return socket_->context();
+  }
+  void set_context(URLRequestContext* context) {
+    socket_->set_context(context);
+  }
+
+  virtual void Connect() {
+    socket_->Connect();
+  }
+
+  virtual bool SendData(const char* data, int len) {
+    return socket_->SendData(data, len);
+  }
+
+  virtual void Close() {
+    socket_->Close();
+  }
+
+  virtual void RestartWithAuth(
+      const std::wstring& username,
+      const std::wstring& password) {
+    socket_->RestartWithAuth(username, password);
+  }
+
+  virtual void DetachDelegate() {
+    socket_->DetachDelegate();
+  }
+
+ protected:
+  friend class base::RefCountedThreadSafe<SocketStreamJob>;
+  virtual ~SocketStreamJob() {}
+
+  scoped_refptr<SocketStream> socket_;
+
+  DISALLOW_COPY_AND_ASSIGN(SocketStreamJob);
+};
+
+}  // namespace net
+
+#endif  // NET_SOCKET_STREAM_SOCKET_STREAM_JOB_H_
diff --git a/net/socket_stream/socket_stream_job_manager.cc b/net/socket_stream/socket_stream_job_manager.cc
new file mode 100644
index 0000000..7dd0d6b
--- /dev/null
+++ b/net/socket_stream/socket_stream_job_manager.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 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/socket_stream/socket_stream_job_manager.h"
+
+namespace net {
+
+SocketStreamJobManager::SocketStreamJobManager() {
+}
+
+SocketStreamJobManager::~SocketStreamJobManager() {
+}
+
+SocketStreamJob* SocketStreamJobManager::CreateJob(
+    const GURL& url, SocketStream::Delegate* delegate) const {
+  // If url is invalid, create plain SocketStreamJob, which will close
+  // the socket immediately.
+  if (!url.is_valid()) {
+    SocketStreamJob* job = new SocketStreamJob();
+    job->InitSocketStream(new SocketStream(url, delegate));
+    return job;
+  }
+
+  const std::string& scheme = url.scheme();  // already lowercase
+
+  AutoLock locked(lock_);
+  FactoryMap::const_iterator found = factories_.find(scheme);
+  if (found != factories_.end()) {
+    SocketStreamJob* job = found->second(url, delegate);
+    if (job)
+      return job;
+  }
+  SocketStreamJob* job = new SocketStreamJob();
+  job->InitSocketStream(new SocketStream(url, delegate));
+  return job;
+}
+
+SocketStreamJob::ProtocolFactory*
+SocketStreamJobManager::RegisterProtocolFactory(
+    const std::string& scheme, SocketStreamJob::ProtocolFactory* factory) {
+  AutoLock locked(lock_);
+
+  SocketStreamJob::ProtocolFactory* old_factory;
+  FactoryMap::iterator found = factories_.find(scheme);
+  if (found != factories_.end()) {
+    old_factory = found->second;
+  } else {
+    old_factory = NULL;
+  }
+  if (factory) {
+    factories_[scheme] = factory;
+  } else if (found != factories_.end()) {
+    factories_.erase(found);
+  }
+  return old_factory;
+}
+
+}  // namespace net
diff --git a/net/socket_stream/socket_stream_job_manager.h b/net/socket_stream/socket_stream_job_manager.h
new file mode 100644
index 0000000..17ff833
--- /dev/null
+++ b/net/socket_stream/socket_stream_job_manager.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 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_SOCKET_STREAM_SOCKET_STREAM_JOB_MANAGER_H_
+#define NET_SOCKET_STREAM_SOCKET_STREAM_JOB_MANAGER_H_
+
+#include <map>
+#include <string>
+
+#include "net/socket_stream/socket_stream.h"
+#include "net/socket_stream/socket_stream_job.h"
+
+class GURL;
+
+namespace net {
+
+class SocketStreamJobManager {
+ public:
+  SocketStreamJobManager();
+  ~SocketStreamJobManager();
+
+  SocketStreamJob* CreateJob(
+      const GURL& url, SocketStream::Delegate* delegate) const;
+
+  SocketStreamJob::ProtocolFactory* RegisterProtocolFactory(
+      const std::string& scheme, SocketStreamJob::ProtocolFactory* factory);
+
+ private:
+  typedef std::map<std::string, SocketStreamJob::ProtocolFactory*> FactoryMap;
+
+  mutable Lock lock_;
+  FactoryMap factories_;
+
+  DISALLOW_COPY_AND_ASSIGN(SocketStreamJobManager);
+};
+
+}  // namespace net
+
+#endif  // NET_SOCKET_STREAM_SOCKET_STREAM_JOB_MANAGER_H_
diff --git a/net/socket_stream/socket_stream_metrics.cc b/net/socket_stream/socket_stream_metrics.cc
index 625a491..71239af 100644
--- a/net/socket_stream/socket_stream_metrics.cc
+++ b/net/socket_stream/socket_stream_metrics.cc
@@ -70,16 +70,18 @@
 
 void SocketStreamMetrics::OnClose() {
   base::TimeTicks closed_time = base::TimeTicks::Now();
-  UMA_HISTOGRAM_LONG_TIMES("Net.SocketStream.Duration",
-                           closed_time - connect_establish_time_);
-  UMA_HISTOGRAM_COUNTS("Net.SocketStream.ReceivedBytes",
-                       received_bytes_);
-  UMA_HISTOGRAM_COUNTS("Net.SocketStream.ReceivedCounts",
-                       received_counts_);
-  UMA_HISTOGRAM_COUNTS("Net.SocketStream.SentBytes",
-                       sent_bytes_);
-  UMA_HISTOGRAM_COUNTS("Net.SocketStream.SentCounts",
-                       sent_counts_);
+  if (!connect_establish_time_.is_null()) {
+    UMA_HISTOGRAM_LONG_TIMES("Net.SocketStream.Duration",
+                             closed_time - connect_establish_time_);
+    UMA_HISTOGRAM_COUNTS("Net.SocketStream.ReceivedBytes",
+                         received_bytes_);
+    UMA_HISTOGRAM_COUNTS("Net.SocketStream.ReceivedCounts",
+                         received_counts_);
+    UMA_HISTOGRAM_COUNTS("Net.SocketStream.SentBytes",
+                         sent_bytes_);
+    UMA_HISTOGRAM_COUNTS("Net.SocketStream.SentCounts",
+                         sent_counts_);
+  }
 }
 
 void SocketStreamMetrics::CountProtocolType(ProtocolType protocol_type) {
diff --git a/net/socket_stream/socket_stream_unittest.cc b/net/socket_stream/socket_stream_unittest.cc
index 76d59e9..d32654a 100644
--- a/net/socket_stream/socket_stream_unittest.cc
+++ b/net/socket_stream/socket_stream_unittest.cc
@@ -5,9 +5,10 @@
 #include <string>
 #include <vector>
 
-#include "net/base/load_log.h"
-#include "net/base/load_log_unittest.h"
+#include "base/callback.h"
 #include "net/base/mock_host_resolver.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/test_completion_callback.h"
 #include "net/socket/socket_test_util.h"
 #include "net/socket_stream/socket_stream.h"
@@ -146,8 +147,140 @@
 namespace net {
 
 class SocketStreamTest : public PlatformTest {
+ public:
+  virtual ~SocketStreamTest() {}
+  virtual void SetUp() {
+    mock_socket_factory_.reset();
+    handshake_request_ = kWebSocketHandshakeRequest;
+    handshake_response_ = kWebSocketHandshakeResponse;
+  }
+  virtual void TearDown() {
+    mock_socket_factory_.reset();
+  }
+
+  virtual void SetWebSocketHandshakeMessage(
+      const char* request, const char* response) {
+    handshake_request_ = request;
+    handshake_response_ = response;
+  }
+  virtual void AddWebSocketMessage(const std::string& message) {
+    messages_.push_back(message);
+  }
+
+  virtual MockClientSocketFactory* GetMockClientSocketFactory() {
+    mock_socket_factory_.reset(new MockClientSocketFactory);
+    return mock_socket_factory_.get();
+  }
+
+  virtual void DoSendWebSocketHandshake(SocketStreamEvent* event) {
+    event->socket->SendData(
+        handshake_request_.data(), handshake_request_.size());
+  }
+
+  virtual void DoCloseFlushPendingWriteTest(SocketStreamEvent* event) {
+    // handshake response received.
+    for (size_t i = 0; i < messages_.size(); i++) {
+      std::vector<char> frame;
+      frame.push_back('\0');
+      frame.insert(frame.end(), messages_[i].begin(), messages_[i].end());
+      frame.push_back('\xff');
+      EXPECT_TRUE(event->socket->SendData(&frame[0], frame.size()));
+    }
+    // Actual ClientSocket close must happen after all frames queued by
+    // SendData above are sent out.
+    event->socket->Close();
+  }
+
+  static const char* kWebSocketHandshakeRequest;
+  static const char* kWebSocketHandshakeResponse;
+
+ private:
+  std::string handshake_request_;
+  std::string handshake_response_;
+  std::vector<std::string> messages_;
+
+  scoped_ptr<MockClientSocketFactory> mock_socket_factory_;
 };
 
+const char* SocketStreamTest::kWebSocketHandshakeRequest =
+    "GET /demo HTTP/1.1\r\n"
+    "Host: example.com\r\n"
+    "Connection: Upgrade\r\n"
+    "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+    "Sec-WebSocket-Protocol: sample\r\n"
+    "Upgrade: WebSocket\r\n"
+    "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+    "Origin: http://example.com\r\n"
+    "\r\n"
+    "^n:ds[4U";
+
+const char* SocketStreamTest::kWebSocketHandshakeResponse =
+    "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+    "Upgrade: WebSocket\r\n"
+    "Connection: Upgrade\r\n"
+    "Sec-WebSocket-Origin: http://example.com\r\n"
+    "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+    "Sec-WebSocket-Protocol: sample\r\n"
+    "\r\n"
+    "8jKS'y:G*Co,Wxa-";
+
+TEST_F(SocketStreamTest, CloseFlushPendingWrite) {
+  TestCompletionCallback callback;
+
+  scoped_ptr<SocketStreamEventRecorder> delegate(
+      new SocketStreamEventRecorder(&callback));
+  // Necessary for NewCallback.
+  SocketStreamTest* test = this;
+  delegate->SetOnConnected(NewCallback(
+      test, &SocketStreamTest::DoSendWebSocketHandshake));
+  delegate->SetOnReceivedData(NewCallback(
+      test, &SocketStreamTest::DoCloseFlushPendingWriteTest));
+
+  scoped_refptr<SocketStream> socket_stream =
+      new SocketStream(GURL("ws://example.com/demo"), delegate.get());
+
+  socket_stream->set_context(new TestURLRequestContext());
+  socket_stream->SetHostResolver(new MockHostResolver());
+
+  MockWrite data_writes[] = {
+    MockWrite(SocketStreamTest::kWebSocketHandshakeRequest),
+    MockWrite(true, "\0message1\xff", 10),
+    MockWrite(true, "\0message2\xff", 10)
+  };
+  MockRead data_reads[] = {
+    MockRead(SocketStreamTest::kWebSocketHandshakeResponse),
+    // Server doesn't close the connection after handshake.
+    MockRead(true, ERR_IO_PENDING)
+  };
+  AddWebSocketMessage("message1");
+  AddWebSocketMessage("message2");
+
+  scoped_refptr<DelayedSocketData> data_provider(
+      new DelayedSocketData(1,
+                            data_reads, arraysize(data_reads),
+                            data_writes, arraysize(data_writes)));
+
+  MockClientSocketFactory* mock_socket_factory =
+      GetMockClientSocketFactory();
+  mock_socket_factory->AddSocketDataProvider(data_provider.get());
+
+  socket_stream->SetClientSocketFactory(mock_socket_factory);
+
+  socket_stream->Connect();
+
+  callback.WaitForResult();
+
+  const std::vector<SocketStreamEvent>& events = delegate->GetSeenEvents();
+  EXPECT_EQ(6U, events.size());
+
+  EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[0].event_type);
+  EXPECT_EQ(SocketStreamEvent::EVENT_SENT_DATA, events[1].event_type);
+  EXPECT_EQ(SocketStreamEvent::EVENT_RECEIVED_DATA, events[2].event_type);
+  EXPECT_EQ(SocketStreamEvent::EVENT_SENT_DATA, events[3].event_type);
+  EXPECT_EQ(SocketStreamEvent::EVENT_SENT_DATA, events[4].event_type);
+  EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[5].event_type);
+}
+
 TEST_F(SocketStreamTest, BasicAuthProxy) {
   MockClientSocketFactory mock_socket_factory;
   MockWrite data_writes1[] = {
@@ -160,7 +293,8 @@
     MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
     MockRead("\r\n"),
   };
-  StaticSocketDataProvider data1(data_reads1, data_writes1);
+  StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1),
+                                 data_writes1, arraysize(data_writes1));
   mock_socket_factory.AddSocketDataProvider(&data1);
 
   MockWrite data_writes2[] = {
@@ -174,7 +308,8 @@
     MockRead("Proxy-agent: Apache/2.2.8\r\n"),
     MockRead("\r\n"),
   };
-  StaticSocketDataProvider data2(data_reads2, data_writes2);
+  StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2),
+                                 data_writes2, arraysize(data_writes2));
   mock_socket_factory.AddSocketDataProvider(&data2);
 
   TestCompletionCallback callback;
@@ -208,12 +343,7 @@
   EXPECT_EQ(SocketStreamEvent::EVENT_CONNECTED, events[1].event_type);
   EXPECT_EQ(SocketStreamEvent::EVENT_CLOSE, events[2].event_type);
 
-  // The first and last entries of the LoadLog should be for
-  // SOCKET_STREAM_CONNECT.
-  EXPECT_TRUE(LogContainsBeginEvent(
-      *socket_stream->load_log(), 0, LoadLog::TYPE_SOCKET_STREAM_CONNECT));
-  EXPECT_TRUE(LogContainsEndEvent(
-      *socket_stream->load_log(), -1, LoadLog::TYPE_SOCKET_STREAM_CONNECT));
+  // TODO(eroman): Add back NetLogTest here...
 }
 
 }  // namespace net
diff --git a/net/spdy/spdy_bitmasks.h b/net/spdy/spdy_bitmasks.h
new file mode 100644
index 0000000..03752e3
--- /dev/null
+++ b/net/spdy/spdy_bitmasks.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 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_SPDY_SPDY_BITMASKS_H_
+#define NET_SPDY_SPDY_BITMASKS_H_
+
+namespace spdy {
+
+// StreamId mask from the SpdyHeader
+const unsigned int kStreamIdMask = 0x7fffffff;
+
+// Control flag mask from the SpdyHeader
+const unsigned int kControlFlagMask = 0x8000;
+
+// Priority mask from the SYN_FRAME
+const unsigned int kPriorityMask = 0xc0;
+
+// Mask the lower 24 bits.
+const unsigned int kLengthMask = 0xffffff;
+
+// Mask the Id from a SETTINGS id.
+const unsigned int kSettingsIdMask = 0xffffff;
+
+// Legal flags on data packets.
+const int kDataFlagsMask = 0x03;
+
+// Legal flags on control packets.
+const int kControlFlagsMask = 0x03;
+
+}  // namespace spdy
+
+#endif  // NET_SPDY_SPDY_BITMASKS_H_
diff --git a/net/spdy/spdy_frame_builder.cc b/net/spdy/spdy_frame_builder.cc
new file mode 100644
index 0000000..7d21b82
--- /dev/null
+++ b/net/spdy/spdy_frame_builder.cc
@@ -0,0 +1,181 @@
+// Copyright (c) 2009 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 <limits>
+
+#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_protocol.h"
+
+namespace spdy {
+
+// We mark a read only SpdyFrameBuilder with a special capacity_.
+static const size_t kCapacityReadOnly = std::numeric_limits<size_t>::max();
+
+SpdyFrameBuilder::SpdyFrameBuilder()
+    : buffer_(NULL),
+      capacity_(0),
+      length_(0),
+      variable_buffer_offset_(0) {
+  Resize(kInitialPayload);
+}
+
+SpdyFrameBuilder::SpdyFrameBuilder(const char* data, int data_len)
+    : buffer_(const_cast<char*>(data)),
+      capacity_(kCapacityReadOnly),
+      length_(data_len),
+      variable_buffer_offset_(0) {
+}
+
+SpdyFrameBuilder::~SpdyFrameBuilder() {
+  if (capacity_ != kCapacityReadOnly)
+    delete[] buffer_;
+}
+
+bool SpdyFrameBuilder::ReadUInt16(void** iter, uint16* result) const {
+  DCHECK(iter);
+  if (!*iter)
+    *iter = const_cast<char*>(buffer_);
+
+  if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+    return false;
+
+  *result = ntohs(*(reinterpret_cast<uint16*>(*iter)));
+
+  UpdateIter(iter, sizeof(*result));
+  return true;
+}
+
+bool SpdyFrameBuilder::ReadUInt32(void** iter, uint32* result) const {
+  DCHECK(iter);
+  if (!*iter)
+    *iter = const_cast<char*>(buffer_);
+
+  if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+    return false;
+
+  *result = ntohl(*(reinterpret_cast<uint32*>(*iter)));
+
+  UpdateIter(iter, sizeof(*result));
+  return true;
+}
+
+bool SpdyFrameBuilder::ReadString(void** iter, std::string* result) const {
+  DCHECK(iter);
+
+  uint16 len;
+  if (!ReadUInt16(iter, &len))
+    return false;
+
+  if (!IteratorHasRoomFor(*iter, len))
+    return false;
+
+  char* chars = reinterpret_cast<char*>(*iter);
+  result->assign(chars, len);
+
+  UpdateIter(iter, len);
+  return true;
+}
+
+bool SpdyFrameBuilder::ReadBytes(void** iter, const char** data,
+                                 uint16 length) const {
+  DCHECK(iter);
+  DCHECK(data);
+
+  if (!IteratorHasRoomFor(*iter, length))
+    return false;
+
+  *data = reinterpret_cast<const char*>(*iter);
+
+  UpdateIter(iter, length);
+  return true;
+}
+
+bool SpdyFrameBuilder::ReadData(void** iter, const char** data,
+                                uint16* length) const {
+  DCHECK(iter);
+  DCHECK(data);
+  DCHECK(length);
+
+  if (!ReadUInt16(iter, length))
+    return false;
+
+  return ReadBytes(iter, data, *length);
+}
+
+char* SpdyFrameBuilder::BeginWrite(size_t length) {
+  size_t offset = length_;
+  size_t needed_size = length_ + length;
+  if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size)))
+    return NULL;
+
+#ifdef ARCH_CPU_64_BITS
+  DCHECK_LE(length, std::numeric_limits<uint32>::max());
+#endif
+
+  return buffer_ + offset;
+}
+
+void SpdyFrameBuilder::EndWrite(char* dest, int length) {
+}
+
+bool SpdyFrameBuilder::WriteBytes(const void* data, uint16 data_len) {
+  DCHECK(capacity_ != kCapacityReadOnly);
+
+  char* dest = BeginWrite(data_len);
+  if (!dest)
+    return false;
+
+  memcpy(dest, data, data_len);
+
+  EndWrite(dest, data_len);
+  length_ += data_len;
+  return true;
+}
+
+bool SpdyFrameBuilder::WriteString(const std::string& value) {
+  if (value.size() > 0xffff)
+    return false;
+
+  if (!WriteUInt16(static_cast<int>(value.size())))
+    return false;
+
+  return WriteBytes(value.data(), static_cast<uint16>(value.size()));
+}
+
+char* SpdyFrameBuilder::BeginWriteData(uint16 length) {
+  DCHECK_EQ(variable_buffer_offset_, 0U) <<
+    "There can only be one variable buffer in a SpdyFrameBuilder";
+
+  if (!WriteUInt16(length))
+    return NULL;
+
+  char *data_ptr = BeginWrite(length);
+  if (!data_ptr)
+    return NULL;
+
+  variable_buffer_offset_ = data_ptr - buffer_ - sizeof(int);
+
+  // EndWrite doesn't necessarily have to be called after the write operation,
+  // so we call it here to pad out what the caller will eventually write.
+  EndWrite(data_ptr, length);
+  return data_ptr;
+}
+
+bool SpdyFrameBuilder::Resize(size_t new_capacity) {
+  if (new_capacity < capacity_)
+    return true;
+
+  char* p = new char[new_capacity];
+  if (buffer_) {
+    memcpy(p, buffer_, capacity_);
+    delete[] buffer_;
+  }
+  if (!p && new_capacity > 0)
+    return false;
+  buffer_ = p;
+  capacity_ = new_capacity;
+  return true;
+}
+
+}  // namespace spdy
diff --git a/net/spdy/spdy_frame_builder.h b/net/spdy/spdy_frame_builder.h
new file mode 100644
index 0000000..5b70437
--- /dev/null
+++ b/net/spdy/spdy_frame_builder.h
@@ -0,0 +1,163 @@
+// Copyright (c) 2009 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_SPDY_FRAME_BUILDER_H_
+#define NET_SPDY_FRAME_BUILDER_H_
+
+#ifdef WIN32
+#include <winsock2.h>  // for htonl() functions
+#else
+#include <arpa/inet.h>
+#endif
+
+#include <string>
+
+#include "base/logging.h"
+#include "net/spdy/spdy_protocol.h"
+
+namespace spdy {
+
+// This class provides facilities for basic binary value packing and unpacking
+// into Spdy frames.
+//
+// The SpdyFrameBuilder supports appending primitive values (int, string, etc)
+// to a frame instance.  The SpdyFrameBuilder grows its internal memory buffer
+// dynamically to hold the sequence of primitive values.   The internal memory
+// buffer is exposed as the "data" of the SpdyFrameBuilder.
+//
+// When reading from a SpdyFrameBuilder the consumer must know what value types
+// to read and in what order to read them as the SpdyFrameBuilder does not keep
+// track of the type of data written to it.
+class SpdyFrameBuilder {
+ public:
+  SpdyFrameBuilder();
+  ~SpdyFrameBuilder();
+
+  // Initializes a SpdyFrameBuilder from a const block of data.  The data is
+  // not copied; instead the data is merely referenced by this
+  // SpdyFrameBuilder.  Only const methods should be used when initialized
+  // this way.
+  SpdyFrameBuilder(const char* data, int data_len);
+
+  // Returns the size of the SpdyFrameBuilder's data.
+  int length() const { return length_; }
+
+  // Takes the buffer from the SpdyFrameBuilder.
+  SpdyFrame* take() {
+    SpdyFrame* rv = new SpdyFrame(buffer_, true);
+    buffer_ = NULL;
+    capacity_ = 0;
+    length_ = 0;
+    return rv;
+  }
+
+  // Methods for reading the payload of the SpdyFrameBuilder.  To read from the
+  // start of the SpdyFrameBuilder, initialize *iter to NULL.  If successful,
+  // these methods return true.  Otherwise, false is returned to indicate that
+  // the result could not be extracted.
+  bool ReadUInt16(void** iter, uint16* result) const;
+  bool ReadUInt32(void** iter, uint32* result) const;
+  bool ReadString(void** iter, std::string* result) const;
+  bool ReadBytes(void** iter, const char** data, uint16 length) const;
+  bool ReadData(void** iter, const char** data, uint16* length) const;
+
+  // Methods for adding to the payload.  These values are appended to the end
+  // of the SpdyFrameBuilder payload.  When reading values, you must read them
+  // in the order they were added.  Note - binary integers are converted from
+  // host to network form.
+  bool WriteUInt16(uint16 value) {
+    value = htons(value);
+    return WriteBytes(&value, sizeof(value));
+  }
+  bool WriteUInt32(uint32 value) {
+    value = htonl(value);
+    return WriteBytes(&value, sizeof(value));
+  }
+  bool WriteString(const std::string& value);
+  bool WriteBytes(const void* data, uint16 data_len);
+
+  // Write an integer to a particular offset in the data buffer.
+  bool WriteUInt32ToOffset(int offset, uint32 value) {
+    value = htonl(value);
+    return WriteBytesToOffset(offset, &value, sizeof(value));
+  }
+
+  // Write to a particular offset in the data buffer.
+  bool WriteBytesToOffset(int offset, const void* data, uint32 data_len) {
+    if (offset + data_len > length_)
+      return false;
+    char *ptr = buffer_ + offset;
+    memcpy(ptr, data, data_len);
+    return true;
+  }
+
+  // Allows the caller to write data directly into the SpdyFrameBuilder.
+  // This saves a copy when the data is not already available in a buffer.
+  // The caller must not write more than the length it declares it will.
+  // Use ReadData to get the data.
+  // Returns NULL on failure.
+  //
+  // The returned pointer will only be valid until the next write operation
+  // on this SpdyFrameBuilder.
+  char* BeginWriteData(uint16 length);
+
+  // Returns true if the given iterator could point to data with the given
+  // length. If there is no room for the given data before the end of the
+  // payload, returns false.
+  bool IteratorHasRoomFor(const void* iter, int len) const {
+    const char* end_of_region = reinterpret_cast<const char*>(iter) + len;
+    if (len < 0 ||
+        iter < buffer_ ||
+        iter > end_of_payload() ||
+        iter > end_of_region ||
+        end_of_region > end_of_payload())
+      return false;
+
+    // Watch out for overflow in pointer calculation, which wraps.
+    return (iter <= end_of_region) && (end_of_region <= end_of_payload());
+  }
+
+ protected:
+  size_t capacity() const {
+    return capacity_;
+  }
+
+  const char* end_of_payload() const { return buffer_ + length_; }
+
+  // Resizes the buffer for use when writing the specified amount of data. The
+  // location that the data should be written at is returned, or NULL if there
+  // was an error. Call EndWrite with the returned offset and the given length
+  // to pad out for the next write.
+  char* BeginWrite(size_t length);
+
+  // Completes the write operation by padding the data with NULL bytes until it
+  // is padded. Should be paired with BeginWrite, but it does not necessarily
+  // have to be called after the data is written.
+  void EndWrite(char* dest, int length);
+
+  // Resize the capacity, note that the input value should include the size of
+  // the header: new_capacity = sizeof(Header) + desired_payload_capacity.
+  // A new failure will cause a Resize failure... and caller should check
+  // the return result for true (i.e., successful resizing).
+  bool Resize(size_t new_capacity);
+
+  // Moves the iterator by the given number of bytes.
+  static void UpdateIter(void** iter, int bytes) {
+    *iter = static_cast<char*>(*iter) + bytes;
+  }
+
+  // Initial size of the payload.
+  static const int kInitialPayload = 1024;
+
+ private:
+  char* buffer_;
+  size_t capacity_;  // Allocation size of payload (or -1 if buffer is const).
+  size_t length_;    // current length of the buffer
+  size_t variable_buffer_offset_;  // IF non-zero, then offset to a buffer.
+};
+
+}  // namespace spdy
+
+#endif  // NET_SPDY_FRAME_BUILDER_H_
+
diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc
new file mode 100644
index 0000000..cd622b8
--- /dev/null
+++ b/net/spdy/spdy_framer.cc
@@ -0,0 +1,1113 @@
+// Copyright (c) 2010 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/spdy/spdy_framer.h"
+
+#include "base/scoped_ptr.h"
+#include "base/stats_counters.h"
+
+#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_bitmasks.h"
+
+#if defined(USE_SYSTEM_ZLIB)
+#include <zlib.h>
+#else
+#include "third_party/zlib/zlib.h"
+#endif
+
+namespace spdy {
+
+// The initial size of the control frame buffer; this is used internally
+// as we parse through control frames.
+static const size_t kControlFrameBufferInitialSize = 32 * 1024;
+// The maximum size of the control frame buffer that we support.
+// TODO(mbelshe): We should make this stream-based so there are no limits.
+static const size_t kControlFrameBufferMaxSize = 64 * 1024;
+
+// By default is compression on or off.
+bool SpdyFramer::compression_default_ = true;
+
+#ifdef DEBUG_SPDY_STATE_CHANGES
+#define CHANGE_STATE(newstate) \
+{ \
+  do { \
+    LOG(INFO) << "Changing state from: " \
+      << StateToString(state_) \
+      << " to " << StateToString(newstate) << "\n"; \
+    state_ = newstate; \
+  } while (false); \
+}
+#else
+#define CHANGE_STATE(newstate) (state_ = newstate)
+#endif
+
+SpdyFramer::SpdyFramer()
+    : state_(SPDY_RESET),
+      error_code_(SPDY_NO_ERROR),
+      remaining_payload_(0),
+      remaining_control_payload_(0),
+      current_frame_buffer_(NULL),
+      current_frame_len_(0),
+      current_frame_capacity_(0),
+      enable_compression_(compression_default_),
+      visitor_(NULL) {
+}
+
+SpdyFramer::~SpdyFramer() {
+  if (header_compressor_.get()) {
+    deflateEnd(header_compressor_.get());
+  }
+  if (header_decompressor_.get()) {
+    inflateEnd(header_decompressor_.get());
+  }
+  CleanupStreamCompressorsAndDecompressors();
+  delete [] current_frame_buffer_;
+}
+
+void SpdyFramer::Reset() {
+  state_ = SPDY_RESET;
+  error_code_ = SPDY_NO_ERROR;
+  remaining_payload_ = 0;
+  remaining_control_payload_ = 0;
+  current_frame_len_ = 0;
+  if (current_frame_capacity_ != kControlFrameBufferInitialSize) {
+    delete [] current_frame_buffer_;
+    current_frame_buffer_ = 0;
+    current_frame_capacity_ = 0;
+    ExpandControlFrameBuffer(kControlFrameBufferInitialSize);
+  }
+}
+
+const char* SpdyFramer::StateToString(int state) {
+  switch (state) {
+    case SPDY_ERROR:
+      return "ERROR";
+    case SPDY_DONE:
+      return "DONE";
+    case SPDY_AUTO_RESET:
+      return "AUTO_RESET";
+    case SPDY_RESET:
+      return "RESET";
+    case SPDY_READING_COMMON_HEADER:
+      return "READING_COMMON_HEADER";
+    case SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER:
+      return "INTERPRET_CONTROL_FRAME_COMMON_HEADER";
+    case SPDY_CONTROL_FRAME_PAYLOAD:
+      return "CONTROL_FRAME_PAYLOAD";
+    case SPDY_IGNORE_REMAINING_PAYLOAD:
+      return "IGNORE_REMAINING_PAYLOAD";
+    case SPDY_FORWARD_STREAM_FRAME:
+      return "FORWARD_STREAM_FRAME";
+  }
+  return "UNKNOWN_STATE";
+}
+
+size_t SpdyFramer::BytesSafeToRead() const {
+  switch (state_) {
+    case SPDY_ERROR:
+    case SPDY_DONE:
+    case SPDY_AUTO_RESET:
+    case SPDY_RESET:
+      return 0;
+    case SPDY_READING_COMMON_HEADER:
+      DCHECK_LT(current_frame_len_, SpdyFrame::size());
+      return SpdyFrame::size() - current_frame_len_;
+    case SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER:
+      return 0;
+    case SPDY_CONTROL_FRAME_PAYLOAD:
+    case SPDY_IGNORE_REMAINING_PAYLOAD:
+    case SPDY_FORWARD_STREAM_FRAME:
+      return remaining_payload_;
+  }
+  // We should never get to here.
+  return 0;
+}
+
+void SpdyFramer::set_error(SpdyError error) {
+  DCHECK(visitor_);
+  error_code_ = error;
+  CHANGE_STATE(SPDY_ERROR);
+  visitor_->OnError(this);
+}
+
+const char* SpdyFramer::ErrorCodeToString(int error_code) {
+  switch (error_code) {
+    case SPDY_NO_ERROR:
+      return "NO_ERROR";
+    case SPDY_INVALID_CONTROL_FRAME:
+      return "INVALID_CONTROL_FRAME";
+    case SPDY_CONTROL_PAYLOAD_TOO_LARGE:
+      return "CONTROL_PAYLOAD_TOO_LARGE";
+    case SPDY_ZLIB_INIT_FAILURE:
+      return "ZLIB_INIT_FAILURE";
+    case SPDY_UNSUPPORTED_VERSION:
+      return "UNSUPPORTED_VERSION";
+    case SPDY_DECOMPRESS_FAILURE:
+      return "DECOMPRESS_FAILURE";
+    case SPDY_COMPRESS_FAILURE:
+      return "COMPRESS_FAILURE";
+  }
+  return "UNKNOWN_ERROR";
+}
+
+size_t SpdyFramer::ProcessInput(const char* data, size_t len) {
+  DCHECK(visitor_);
+  DCHECK(data);
+
+  size_t original_len = len;
+  while (len != 0) {
+    switch (state_) {
+      case SPDY_ERROR:
+      case SPDY_DONE:
+        goto bottom;
+
+      case SPDY_AUTO_RESET:
+      case SPDY_RESET:
+        Reset();
+        CHANGE_STATE(SPDY_READING_COMMON_HEADER);
+        continue;
+
+      case SPDY_READING_COMMON_HEADER: {
+        int bytes_read = ProcessCommonHeader(data, len);
+        len -= bytes_read;
+        data += bytes_read;
+        continue;
+      }
+
+      // Arguably, this case is not necessary, as no bytes are consumed here.
+      // I felt it was a nice partitioning, however (which probably indicates
+      // that it should be refactored into its own function!)
+      case SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER:
+        ProcessControlFrameHeader();
+        continue;
+
+      case SPDY_CONTROL_FRAME_PAYLOAD: {
+        int bytes_read = ProcessControlFramePayload(data, len);
+        len -= bytes_read;
+        data += bytes_read;
+      }
+        // intentional fallthrough
+      case SPDY_IGNORE_REMAINING_PAYLOAD:
+        // control frame has too-large payload
+        // intentional fallthrough
+      case SPDY_FORWARD_STREAM_FRAME: {
+        int bytes_read = ProcessDataFramePayload(data, len);
+        len -= bytes_read;
+        data += bytes_read;
+        continue;
+      }
+      default:
+        break;
+    }
+  }
+ bottom:
+  return original_len - len;
+}
+
+size_t SpdyFramer::ProcessCommonHeader(const char* data, size_t len) {
+  // This should only be called when we're in the SPDY_READING_COMMON_HEADER
+  // state.
+  DCHECK_EQ(state_, SPDY_READING_COMMON_HEADER);
+
+  int original_len = len;
+  SpdyFrame current_frame(current_frame_buffer_, false);
+
+  do {
+    if (current_frame_len_ < SpdyFrame::size()) {
+      size_t bytes_desired = SpdyFrame::size() - current_frame_len_;
+      size_t bytes_to_append = std::min(bytes_desired, len);
+      char* header_buffer = current_frame_buffer_;
+      memcpy(&header_buffer[current_frame_len_], data, bytes_to_append);
+      current_frame_len_ += bytes_to_append;
+      data += bytes_to_append;
+      len -= bytes_to_append;
+      // Check for an empty data frame.
+      if (current_frame_len_ == SpdyFrame::size() &&
+          !current_frame.is_control_frame() &&
+          current_frame.length() == 0) {
+        if (current_frame.flags() & CONTROL_FLAG_FIN) {
+          SpdyDataFrame data_frame(current_frame_buffer_, false);
+          visitor_->OnStreamFrameData(data_frame.stream_id(), NULL, 0);
+        }
+        CHANGE_STATE(SPDY_AUTO_RESET);
+      }
+      break;
+    }
+    remaining_payload_ = current_frame.length();
+
+    // This is just a sanity check for help debugging early frame errors.
+    if (remaining_payload_ > 1000000u) {
+      LOG(WARNING) <<
+          "Unexpectedly large frame.  Spdy session is likely corrupt.";
+    }
+
+    // if we're here, then we have the common header all received.
+    if (!current_frame.is_control_frame())
+      CHANGE_STATE(SPDY_FORWARD_STREAM_FRAME);
+    else
+      CHANGE_STATE(SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER);
+  } while (false);
+
+  return original_len - len;
+}
+
+void SpdyFramer::ProcessControlFrameHeader() {
+  DCHECK_EQ(SPDY_NO_ERROR, error_code_);
+  DCHECK_LE(SpdyFrame::size(), current_frame_len_);
+  SpdyControlFrame current_control_frame(current_frame_buffer_, false);
+
+  // We check version before we check validity: version can never be 'invalid',
+  // it can only be unsupported.
+  if (current_control_frame.version() != kSpdyProtocolVersion) {
+    set_error(SPDY_UNSUPPORTED_VERSION);
+    return;
+  }
+
+  // Next up, check to see if we have valid data.  This should be after version
+  // checking (otherwise if the the type were out of bounds due to a version
+  // upgrade we would misclassify the error) and before checking the type
+  // (type can definitely be out of bounds)
+  if (!current_control_frame.AppearsToBeAValidControlFrame()) {
+    set_error(SPDY_INVALID_CONTROL_FRAME);
+    return;
+  }
+
+  // Do some sanity checking on the control frame sizes.
+  switch (current_control_frame.type()) {
+    case SYN_STREAM:
+      if (current_control_frame.length() <
+          SpdySynStreamControlFrame::size() - SpdyControlFrame::size())
+        set_error(SPDY_INVALID_CONTROL_FRAME);
+      break;
+    case SYN_REPLY:
+      if (current_control_frame.length() <
+          SpdySynReplyControlFrame::size() - SpdyControlFrame::size())
+        set_error(SPDY_INVALID_CONTROL_FRAME);
+      break;
+    case RST_STREAM:
+      if (current_control_frame.length() !=
+          SpdyRstStreamControlFrame::size() - SpdyFrame::size())
+        set_error(SPDY_INVALID_CONTROL_FRAME);
+      break;
+    case NOOP:
+      // NOOP.  Swallow it.
+      CHANGE_STATE(SPDY_AUTO_RESET);
+      return;
+    case GOAWAY:
+      if (current_control_frame.length() !=
+          SpdyGoAwayControlFrame::size() - SpdyFrame::size())
+        set_error(SPDY_INVALID_CONTROL_FRAME);
+      break;
+    case SETTINGS:
+      if (current_control_frame.length() <
+          SpdySettingsControlFrame::size() - SpdyControlFrame::size())
+        set_error(SPDY_INVALID_CONTROL_FRAME);
+      break;
+    case WINDOW_UPDATE:
+      if (current_control_frame.length() !=
+          SpdyWindowUpdateControlFrame::size() - SpdyFrame::size())
+        set_error(SPDY_INVALID_CONTROL_FRAME);
+      break;
+    default:
+      LOG(WARNING) << "Valid spdy control frame with unknown type: "
+                   << current_control_frame.type();
+      DCHECK(false);
+      set_error(SPDY_INVALID_CONTROL_FRAME);
+      break;
+  }
+
+  remaining_control_payload_ = current_control_frame.length();
+  if (remaining_control_payload_ > kControlFrameBufferMaxSize) {
+    set_error(SPDY_CONTROL_PAYLOAD_TOO_LARGE);
+    return;
+  }
+
+  ExpandControlFrameBuffer(remaining_control_payload_);
+  CHANGE_STATE(SPDY_CONTROL_FRAME_PAYLOAD);
+}
+
+size_t SpdyFramer::ProcessControlFramePayload(const char* data, size_t len) {
+  size_t original_len = len;
+  do {
+    if (remaining_control_payload_) {
+      size_t amount_to_consume = std::min(remaining_control_payload_, len);
+      memcpy(&current_frame_buffer_[current_frame_len_], data,
+             amount_to_consume);
+      current_frame_len_ += amount_to_consume;
+      data += amount_to_consume;
+      len -= amount_to_consume;
+      remaining_control_payload_ -= amount_to_consume;
+      remaining_payload_ -= amount_to_consume;
+      if (remaining_control_payload_)
+        break;
+    }
+    SpdyControlFrame control_frame(current_frame_buffer_, false);
+    visitor_->OnControl(&control_frame);
+
+    // If this is a FIN, tell the caller.
+    if (control_frame.type() == SYN_REPLY &&
+        control_frame.flags() & CONTROL_FLAG_FIN) {
+      visitor_->OnStreamFrameData(reinterpret_cast<SpdySynReplyControlFrame*>(
+                                      &control_frame)->stream_id(),
+                                  NULL, 0);
+    }
+
+    CHANGE_STATE(SPDY_IGNORE_REMAINING_PAYLOAD);
+  } while (false);
+  return original_len - len;
+}
+
+size_t SpdyFramer::ProcessDataFramePayload(const char* data, size_t len) {
+  size_t original_len = len;
+
+  SpdyDataFrame current_data_frame(current_frame_buffer_, false);
+  if (remaining_payload_) {
+    size_t amount_to_forward = std::min(remaining_payload_, len);
+    if (amount_to_forward && state_ != SPDY_IGNORE_REMAINING_PAYLOAD) {
+      if (current_data_frame.flags() & DATA_FLAG_COMPRESSED) {
+        z_stream* decompressor =
+            GetStreamDecompressor(current_data_frame.stream_id());
+        if (!decompressor)
+          return 0;
+
+        size_t decompressed_max_size = amount_to_forward * 100;
+        scoped_array<char> decompressed(new char[decompressed_max_size]);
+        decompressor->next_in = reinterpret_cast<Bytef*>(
+            const_cast<char*>(data));
+        decompressor->avail_in = amount_to_forward;
+        decompressor->next_out =
+            reinterpret_cast<Bytef*>(decompressed.get());
+        decompressor->avail_out = decompressed_max_size;
+
+        int rv = inflate(decompressor, Z_SYNC_FLUSH);
+        if (rv != Z_OK) {
+          LOG(WARNING) << "inflate failure: " << rv;
+          set_error(SPDY_DECOMPRESS_FAILURE);
+          return 0;
+        }
+        size_t decompressed_size = decompressed_max_size -
+                                   decompressor->avail_out;
+
+        // Only inform the visitor if there is data.
+        if (decompressed_size)
+          visitor_->OnStreamFrameData(current_data_frame.stream_id(),
+                                      decompressed.get(),
+                                      decompressed_size);
+        amount_to_forward -= decompressor->avail_in;
+      } else {
+        // The data frame was not compressed.
+        // Only inform the visitor if there is data.
+        if (amount_to_forward)
+          visitor_->OnStreamFrameData(current_data_frame.stream_id(),
+                                      data, amount_to_forward);
+      }
+    }
+    data += amount_to_forward;
+    len -= amount_to_forward;
+    remaining_payload_ -= amount_to_forward;
+
+    // If the FIN flag is set, and there is no more data in this data
+    // frame, inform the visitor of EOF via a 0-length data frame.
+    if (!remaining_payload_ &&
+        current_data_frame.flags() & DATA_FLAG_FIN) {
+      visitor_->OnStreamFrameData(current_data_frame.stream_id(), NULL, 0);
+      CleanupDecompressorForStream(current_data_frame.stream_id());
+    }
+  } else {
+    CHANGE_STATE(SPDY_AUTO_RESET);
+  }
+  return original_len - len;
+}
+
+void SpdyFramer::ExpandControlFrameBuffer(size_t size) {
+  DCHECK_LT(size, kControlFrameBufferMaxSize);
+  if (size < current_frame_capacity_)
+    return;
+
+  int alloc_size = size + SpdyFrame::size();
+  char* new_buffer = new char[alloc_size];
+  memcpy(new_buffer, current_frame_buffer_, current_frame_len_);
+  delete [] current_frame_buffer_;
+  current_frame_capacity_ = alloc_size;
+  current_frame_buffer_ = new_buffer;
+}
+
+bool SpdyFramer::ParseHeaderBlock(const SpdyFrame* frame,
+                                  SpdyHeaderBlock* block) {
+  SpdyControlFrame control_frame(frame->data(), false);
+  uint32 type = control_frame.type();
+  if (type != SYN_STREAM && type != SYN_REPLY)
+    return false;
+
+  // Find the header data within the control frame.
+  scoped_ptr<SpdyFrame> decompressed_frame(DecompressFrame(*frame));
+  if (!decompressed_frame.get())
+    return false;
+
+  const char *header_data = NULL;
+  int header_length = 0;
+
+  switch (type) {
+    case SYN_STREAM:
+      {
+        SpdySynStreamControlFrame syn_frame(decompressed_frame->data(), false);
+        header_data = syn_frame.header_block();
+        header_length = syn_frame.header_block_len();
+      }
+      break;
+    case SYN_REPLY:
+      {
+        SpdySynReplyControlFrame syn_frame(decompressed_frame->data(), false);
+        header_data = syn_frame.header_block();
+        header_length = syn_frame.header_block_len();
+      }
+      break;
+  }
+
+  SpdyFrameBuilder builder(header_data, header_length);
+  void* iter = NULL;
+  uint16 num_headers;
+  if (builder.ReadUInt16(&iter, &num_headers)) {
+    int index = 0;
+    for ( ; index < num_headers; ++index) {
+      std::string name;
+      std::string value;
+      if (!builder.ReadString(&iter, &name))
+        break;
+      if (!builder.ReadString(&iter, &value))
+        break;
+      if (!name.size() || !value.size())
+        return false;
+      if (block->find(name) == block->end()) {
+        (*block)[name] = value;
+      } else {
+        return false;
+      }
+    }
+    return index == num_headers &&
+        iter == header_data + header_length;
+  }
+  return false;
+}
+
+/* static */
+bool SpdyFramer::ParseSettings(const SpdySettingsControlFrame* frame,
+                               SpdySettings* settings) {
+  DCHECK_EQ(frame->type(), SETTINGS);
+  DCHECK(settings);
+
+  SpdyFrameBuilder parser(frame->header_block(), frame->header_block_len());
+  void* iter = NULL;
+  for (size_t index = 0; index < frame->num_entries(); ++index) {
+    uint32 id;
+    uint32 value;
+    if (!parser.ReadUInt32(&iter, &id))
+      return false;
+    if (!parser.ReadUInt32(&iter, &value))
+      return false;
+    settings->insert(settings->end(), std::make_pair(id, value));
+  }
+  return true;
+}
+
+SpdySynStreamControlFrame* SpdyFramer::CreateSynStream(
+    SpdyStreamId stream_id, SpdyStreamId associated_stream_id, int priority,
+    SpdyControlFlags flags, bool compressed, SpdyHeaderBlock* headers) {
+  SpdyFrameBuilder frame;
+
+  DCHECK_GT(stream_id, static_cast<SpdyStreamId>(0));
+  DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
+  DCHECK_EQ(0u, associated_stream_id & ~kStreamIdMask);
+
+  frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion);
+  frame.WriteUInt16(SYN_STREAM);
+  frame.WriteUInt32(0);  // Placeholder for the length and flags
+  frame.WriteUInt32(stream_id);
+  frame.WriteUInt32(associated_stream_id);
+  frame.WriteUInt16(ntohs(priority) << 6);  // Priority.
+
+  frame.WriteUInt16(headers->size());  // Number of headers.
+  SpdyHeaderBlock::iterator it;
+  for (it = headers->begin(); it != headers->end(); ++it) {
+    bool wrote_header;
+    wrote_header = frame.WriteString(it->first);
+    wrote_header &= frame.WriteString(it->second);
+    DCHECK(wrote_header);
+  }
+
+  // Write the length and flags.
+  size_t length = frame.length() - SpdyFrame::size();
+  DCHECK_EQ(0u, length & ~static_cast<size_t>(kLengthMask));
+  FlagsAndLength flags_length;
+  flags_length.length_ = htonl(static_cast<uint32>(length));
+  DCHECK_EQ(0, flags & ~kControlFlagsMask);
+  flags_length.flags_[0] = flags;
+  frame.WriteBytesToOffset(4, &flags_length, sizeof(flags_length));
+
+  scoped_ptr<SpdyFrame> syn_frame(frame.take());
+  if (compressed) {
+    return reinterpret_cast<SpdySynStreamControlFrame*>(
+        CompressFrame(*syn_frame.get()));
+  }
+  return reinterpret_cast<SpdySynStreamControlFrame*>(syn_frame.release());
+}
+
+/* static */
+SpdyRstStreamControlFrame* SpdyFramer::CreateRstStream(SpdyStreamId stream_id,
+                                                       SpdyStatusCodes status) {
+  DCHECK_GT(stream_id, 0u);
+  DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
+  DCHECK_NE(status, INVALID);
+  DCHECK_LT(status, NUM_STATUS_CODES);
+
+  SpdyFrameBuilder frame;
+  frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion);
+  frame.WriteUInt16(RST_STREAM);
+  frame.WriteUInt32(8);
+  frame.WriteUInt32(stream_id);
+  frame.WriteUInt32(status);
+  return reinterpret_cast<SpdyRstStreamControlFrame*>(frame.take());
+}
+
+/* static */
+SpdyGoAwayControlFrame* SpdyFramer::CreateGoAway(
+    SpdyStreamId last_accepted_stream_id) {
+  DCHECK_EQ(0u, last_accepted_stream_id & ~kStreamIdMask);
+
+  SpdyFrameBuilder frame;
+  frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion);
+  frame.WriteUInt16(GOAWAY);
+  size_t go_away_size = SpdyGoAwayControlFrame::size() - SpdyFrame::size();
+  frame.WriteUInt32(go_away_size);
+  frame.WriteUInt32(last_accepted_stream_id);
+  return reinterpret_cast<SpdyGoAwayControlFrame*>(frame.take());
+}
+
+/* static */
+SpdyWindowUpdateControlFrame* SpdyFramer::CreateWindowUpdate(
+    SpdyStreamId stream_id,
+    uint32 delta_window_size) {
+  DCHECK_GT(stream_id, 0u);
+  DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
+  DCHECK_GT(delta_window_size, 0u);
+  DCHECK_LT(delta_window_size, 0x80000000u);  // 2^31
+
+  SpdyFrameBuilder frame;
+  frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion);
+  frame.WriteUInt16(WINDOW_UPDATE);
+  size_t window_update_size = SpdyWindowUpdateControlFrame::size() -
+      SpdyFrame::size();
+  frame.WriteUInt32(window_update_size);
+  frame.WriteUInt32(stream_id);
+  frame.WriteUInt32(delta_window_size);
+  return reinterpret_cast<SpdyWindowUpdateControlFrame*>(frame.take());
+}
+
+/* static */
+SpdySettingsControlFrame* SpdyFramer::CreateSettings(
+    const SpdySettings& values) {
+  SpdyFrameBuilder frame;
+  frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion);
+  frame.WriteUInt16(SETTINGS);
+  size_t settings_size = SpdySettingsControlFrame::size() - SpdyFrame::size() +
+      8 * values.size();
+  frame.WriteUInt32(settings_size);
+  frame.WriteUInt32(values.size());
+  SpdySettings::const_iterator it = values.begin();
+  while (it != values.end()) {
+    frame.WriteUInt32(it->first.id_);
+    frame.WriteUInt32(it->second);
+    ++it;
+  }
+  return reinterpret_cast<SpdySettingsControlFrame*>(frame.take());
+}
+
+SpdySynReplyControlFrame* SpdyFramer::CreateSynReply(SpdyStreamId stream_id,
+    SpdyControlFlags flags, bool compressed, SpdyHeaderBlock* headers) {
+  DCHECK_GT(stream_id, 0u);
+  DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
+
+  SpdyFrameBuilder frame;
+
+  frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion);
+  frame.WriteUInt16(SYN_REPLY);
+  frame.WriteUInt32(0);  // Placeholder for the length and flags.
+  frame.WriteUInt32(stream_id);
+  frame.WriteUInt16(0);  // Unused
+
+  frame.WriteUInt16(headers->size());  // Number of headers.
+  SpdyHeaderBlock::iterator it;
+  for (it = headers->begin(); it != headers->end(); ++it) {
+    bool wrote_header;
+    wrote_header = frame.WriteString(it->first);
+    wrote_header &= frame.WriteString(it->second);
+    DCHECK(wrote_header);
+  }
+
+  // Write the length and flags.
+  size_t length = frame.length() - SpdyFrame::size();
+  DCHECK_EQ(0u, length & ~static_cast<size_t>(kLengthMask));
+  FlagsAndLength flags_length;
+  flags_length.length_ = htonl(static_cast<uint32>(length));
+  DCHECK_EQ(0, flags & ~kControlFlagsMask);
+  flags_length.flags_[0] = flags;
+  frame.WriteBytesToOffset(4, &flags_length, sizeof(flags_length));
+
+  scoped_ptr<SpdyFrame> reply_frame(frame.take());
+  if (compressed) {
+    return reinterpret_cast<SpdySynReplyControlFrame*>(
+        CompressFrame(*reply_frame.get()));
+  }
+  return reinterpret_cast<SpdySynReplyControlFrame*>(reply_frame.release());
+}
+
+SpdyDataFrame* SpdyFramer::CreateDataFrame(SpdyStreamId stream_id,
+                                           const char* data,
+                                           uint32 len, SpdyDataFlags flags) {
+  SpdyFrameBuilder frame;
+
+  DCHECK_GT(stream_id, 0u);
+  DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
+  frame.WriteUInt32(stream_id);
+
+  DCHECK_EQ(0u, len & ~static_cast<size_t>(kLengthMask));
+  FlagsAndLength flags_length;
+  flags_length.length_ = htonl(len);
+  DCHECK_EQ(0, flags & ~kDataFlagsMask);
+  flags_length.flags_[0] = flags;
+  frame.WriteBytes(&flags_length, sizeof(flags_length));
+
+  frame.WriteBytes(data, len);
+  scoped_ptr<SpdyFrame> data_frame(frame.take());
+  SpdyDataFrame* rv;
+  if (flags & DATA_FLAG_COMPRESSED) {
+    rv = reinterpret_cast<SpdyDataFrame*>(CompressFrame(*data_frame.get()));
+  } else {
+    rv = reinterpret_cast<SpdyDataFrame*>(data_frame.release());
+  }
+
+  if (flags & DATA_FLAG_FIN) {
+    CleanupCompressorForStream(stream_id);
+  }
+
+  return rv;
+}
+
+/* static */
+SpdyControlFrame* SpdyFramer::CreateNopFrame() {
+  SpdyFrameBuilder frame;
+  frame.WriteUInt16(kControlFlagMask | kSpdyProtocolVersion);
+  frame.WriteUInt16(NOOP);
+  frame.WriteUInt32(0);
+  return reinterpret_cast<SpdyControlFrame*>(frame.take());
+}
+
+// The following compression setting are based on Brian Olson's analysis. See
+// https://groups.google.com/group/spdy-dev/browse_thread/thread/dfaf498542fac792
+// for more details.
+static const int kCompressorLevel = 9;
+static const int kCompressorWindowSizeInBits = 11;
+static const int kCompressorMemLevel = 1;
+
+// This is just a hacked dictionary to use for shrinking HTTP-like headers.
+// TODO(mbelshe): Use a scientific methodology for computing the dictionary.
+const char SpdyFramer::kDictionary[] =
+  "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
+  "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
+  "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
+  "-agent10010120020120220320420520630030130230330430530630740040140240340440"
+  "5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
+  "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
+  "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
+  "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
+  "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
+  "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
+  "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
+  "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
+  ".1statusversionurl";
+const int SpdyFramer::kDictionarySize = arraysize(kDictionary);
+
+static uLong dictionary_id = 0;
+
+z_stream* SpdyFramer::GetHeaderCompressor() {
+  if (header_compressor_.get())
+    return header_compressor_.get();  // Already initialized.
+
+  header_compressor_.reset(new z_stream);
+  memset(header_compressor_.get(), 0, sizeof(z_stream));
+
+  int success = deflateInit2(header_compressor_.get(),
+                             kCompressorLevel,
+                             Z_DEFLATED,
+                             kCompressorWindowSizeInBits,
+                             kCompressorMemLevel,
+                             Z_DEFAULT_STRATEGY);
+  if (success == Z_OK)
+    success = deflateSetDictionary(header_compressor_.get(),
+                                   reinterpret_cast<const Bytef*>(kDictionary),
+                                   kDictionarySize);
+  if (success != Z_OK) {
+    LOG(WARNING) << "deflateSetDictionary failure: " << success;
+    header_compressor_.reset(NULL);
+    return NULL;
+  }
+  return header_compressor_.get();
+}
+
+z_stream* SpdyFramer::GetHeaderDecompressor() {
+  if (header_decompressor_.get())
+    return header_decompressor_.get();  // Already initialized.
+
+  header_decompressor_.reset(new z_stream);
+  memset(header_decompressor_.get(), 0, sizeof(z_stream));
+
+  // Compute the id of our dictionary so that we know we're using the
+  // right one when asked for it.
+  if (dictionary_id == 0) {
+    dictionary_id = adler32(0L, Z_NULL, 0);
+    dictionary_id = adler32(dictionary_id,
+                            reinterpret_cast<const Bytef*>(kDictionary),
+                            kDictionarySize);
+  }
+
+  int success = inflateInit(header_decompressor_.get());
+  if (success != Z_OK) {
+    LOG(WARNING) << "inflateInit failure: " << success;
+    header_decompressor_.reset(NULL);
+    return NULL;
+  }
+  return header_decompressor_.get();
+}
+
+z_stream* SpdyFramer::GetStreamCompressor(SpdyStreamId stream_id) {
+  CompressorMap::iterator it = stream_compressors_.find(stream_id);
+  if (it != stream_compressors_.end())
+    return it->second;  // Already initialized.
+
+  scoped_ptr<z_stream> compressor(new z_stream);
+  memset(compressor.get(), 0, sizeof(z_stream));
+
+  int success = deflateInit2(compressor.get(),
+                             kCompressorLevel,
+                             Z_DEFLATED,
+                             kCompressorWindowSizeInBits,
+                             kCompressorMemLevel,
+                             Z_DEFAULT_STRATEGY);
+  if (success != Z_OK) {
+    LOG(WARNING) << "deflateInit failure: " << success;
+    return NULL;
+  }
+  return stream_compressors_[stream_id] = compressor.release();
+}
+
+z_stream* SpdyFramer::GetStreamDecompressor(SpdyStreamId stream_id) {
+  CompressorMap::iterator it = stream_decompressors_.find(stream_id);
+  if (it != stream_decompressors_.end())
+    return it->second;  // Already initialized.
+
+  scoped_ptr<z_stream> decompressor(new z_stream);
+  memset(decompressor.get(), 0, sizeof(z_stream));
+
+  int success = inflateInit(decompressor.get());
+  if (success != Z_OK) {
+    LOG(WARNING) << "inflateInit failure: " << success;
+    return NULL;
+  }
+  return stream_decompressors_[stream_id] = decompressor.release();
+}
+
+bool SpdyFramer::GetFrameBoundaries(const SpdyFrame& frame,
+                                    int* payload_length,
+                                    int* header_length,
+                                    const char** payload) const {
+  size_t frame_size;
+  if (frame.is_control_frame()) {
+    const SpdyControlFrame& control_frame =
+        reinterpret_cast<const SpdyControlFrame&>(frame);
+    switch (control_frame.type()) {
+      case SYN_STREAM:
+        {
+          const SpdySynStreamControlFrame& syn_frame =
+              reinterpret_cast<const SpdySynStreamControlFrame&>(frame);
+          frame_size = SpdySynStreamControlFrame::size();
+          *payload_length = syn_frame.header_block_len();
+          *header_length = frame_size;
+          *payload = frame.data() + *header_length;
+        }
+        break;
+      case SYN_REPLY:
+        {
+          const SpdySynReplyControlFrame& syn_frame =
+              reinterpret_cast<const SpdySynReplyControlFrame&>(frame);
+          frame_size = SpdySynReplyControlFrame::size();
+          *payload_length = syn_frame.header_block_len();
+          *header_length = frame_size;
+          *payload = frame.data() + *header_length;
+        }
+        break;
+      default:
+        // TODO(mbelshe): set an error?
+        return false;  // We can't compress this frame!
+    }
+  } else {
+    frame_size = SpdyFrame::size();
+    *header_length = frame_size;
+    *payload_length = frame.length();
+    *payload = frame.data() + SpdyFrame::size();
+  }
+  return true;
+}
+
+SpdyFrame* SpdyFramer::CompressFrame(const SpdyFrame& frame) {
+  if (frame.is_control_frame()) {
+    return CompressControlFrame(
+        reinterpret_cast<const SpdyControlFrame&>(frame));
+  }
+  return CompressDataFrame(reinterpret_cast<const SpdyDataFrame&>(frame));
+}
+
+SpdyFrame* SpdyFramer::DecompressFrame(const SpdyFrame& frame) {
+  if (frame.is_control_frame()) {
+    return DecompressControlFrame(
+        reinterpret_cast<const SpdyControlFrame&>(frame));
+  }
+  return DecompressDataFrame(reinterpret_cast<const SpdyDataFrame&>(frame));
+}
+
+SpdyControlFrame* SpdyFramer::CompressControlFrame(
+    const SpdyControlFrame& frame) {
+  z_stream* compressor = GetHeaderCompressor();
+  if (!compressor)
+    return NULL;
+  return reinterpret_cast<SpdyControlFrame*>(
+      CompressFrameWithZStream(frame, compressor));
+}
+
+SpdyControlFrame* SpdyFramer::DecompressControlFrame(
+    const SpdyControlFrame& frame) {
+  z_stream* decompressor = GetHeaderDecompressor();
+  if (!decompressor)
+    return NULL;
+  return reinterpret_cast<SpdyControlFrame*>(
+      DecompressFrameWithZStream(frame, decompressor));
+}
+
+SpdyDataFrame* SpdyFramer::CompressDataFrame(const SpdyDataFrame& frame) {
+  z_stream* compressor = GetStreamCompressor(frame.stream_id());
+  if (!compressor)
+    return NULL;
+  return reinterpret_cast<SpdyDataFrame*>(
+      CompressFrameWithZStream(frame, compressor));
+}
+
+SpdyDataFrame* SpdyFramer::DecompressDataFrame(const SpdyDataFrame& frame) {
+  z_stream* decompressor = GetStreamDecompressor(frame.stream_id());
+  if (!decompressor)
+    return NULL;
+  return reinterpret_cast<SpdyDataFrame*>(
+      DecompressFrameWithZStream(frame, decompressor));
+}
+
+SpdyFrame* SpdyFramer::CompressFrameWithZStream(const SpdyFrame& frame,
+                                                z_stream* compressor) {
+  int payload_length;
+  int header_length;
+  const char* payload;
+
+  static StatsCounter compressed_frames("spdy.CompressedFrames");
+  static StatsCounter pre_compress_bytes("spdy.PreCompressSize");
+  static StatsCounter post_compress_bytes("spdy.PostCompressSize");
+
+  if (!enable_compression_)
+    return DuplicateFrame(frame);
+
+  if (!GetFrameBoundaries(frame, &payload_length, &header_length, &payload))
+    return NULL;
+
+  // Create an output frame.
+  int compressed_max_size = deflateBound(compressor, payload_length);
+  int new_frame_size = header_length + compressed_max_size;
+  scoped_ptr<SpdyFrame> new_frame(new SpdyFrame(new_frame_size));
+  memcpy(new_frame->data(), frame.data(), frame.length() + SpdyFrame::size());
+
+  compressor->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(payload));
+  compressor->avail_in = payload_length;
+  compressor->next_out = reinterpret_cast<Bytef*>(new_frame->data()) +
+                          header_length;
+  compressor->avail_out = compressed_max_size;
+
+  // Data packets have a 'compressed' flag.
+  if (!new_frame->is_control_frame()) {
+    SpdyDataFrame* data_frame =
+        reinterpret_cast<SpdyDataFrame*>(new_frame.get());
+    data_frame->set_flags(data_frame->flags() | DATA_FLAG_COMPRESSED);
+  }
+
+  int rv = deflate(compressor, Z_SYNC_FLUSH);
+  if (rv != Z_OK) {  // How can we know that it compressed everything?
+    // This shouldn't happen, right?
+    LOG(WARNING) << "deflate failure: " << rv;
+    return NULL;
+  }
+
+  int compressed_size = compressed_max_size - compressor->avail_out;
+  new_frame->set_length(header_length + compressed_size - SpdyFrame::size());
+
+  pre_compress_bytes.Add(payload_length);
+  post_compress_bytes.Add(new_frame->length());
+
+  compressed_frames.Increment();
+
+  return new_frame.release();
+}
+
+SpdyFrame* SpdyFramer::DecompressFrameWithZStream(const SpdyFrame& frame,
+                                                  z_stream* decompressor) {
+  int payload_length;
+  int header_length;
+  const char* payload;
+
+  static StatsCounter decompressed_frames("spdy.DecompressedFrames");
+  static StatsCounter pre_decompress_bytes("spdy.PreDeCompressSize");
+  static StatsCounter post_decompress_bytes("spdy.PostDeCompressSize");
+
+  if (!enable_compression_)
+    return DuplicateFrame(frame);
+
+  if (!GetFrameBoundaries(frame, &payload_length, &header_length, &payload))
+    return NULL;
+
+  if (!frame.is_control_frame()) {
+    const SpdyDataFrame& data_frame =
+        reinterpret_cast<const SpdyDataFrame&>(frame);
+    if ((data_frame.flags() & DATA_FLAG_COMPRESSED) == 0)
+      return DuplicateFrame(frame);
+  }
+
+  // Create an output frame.  Assume it does not need to be longer than
+  // the input data.
+  int decompressed_max_size = kControlFrameBufferInitialSize;
+  int new_frame_size = header_length + decompressed_max_size;
+  scoped_ptr<SpdyFrame> new_frame(new SpdyFrame(new_frame_size));
+  memcpy(new_frame->data(), frame.data(), frame.length() + SpdyFrame::size());
+
+  decompressor->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(payload));
+  decompressor->avail_in = payload_length;
+  decompressor->next_out = reinterpret_cast<Bytef*>(new_frame->data()) +
+      header_length;
+  decompressor->avail_out = decompressed_max_size;
+
+  int rv = inflate(decompressor, Z_SYNC_FLUSH);
+  if (rv == Z_NEED_DICT) {
+    // Need to try again with the right dictionary.
+    if (decompressor->adler == dictionary_id) {
+      rv = inflateSetDictionary(decompressor, (const Bytef*)kDictionary,
+                                kDictionarySize);
+      if (rv == Z_OK)
+        rv = inflate(decompressor, Z_SYNC_FLUSH);
+    }
+  }
+  if (rv != Z_OK) {  // How can we know that it decompressed everything?
+    LOG(WARNING) << "inflate failure: " << rv;
+    return NULL;
+  }
+
+  // Unset the compressed flag for data frames.
+  if (!new_frame->is_control_frame()) {
+    SpdyDataFrame* data_frame =
+        reinterpret_cast<SpdyDataFrame*>(new_frame.get());
+    data_frame->set_flags(data_frame->flags() & ~DATA_FLAG_COMPRESSED);
+  }
+
+  int decompressed_size = decompressed_max_size - decompressor->avail_out;
+  new_frame->set_length(header_length + decompressed_size - SpdyFrame::size());
+
+  // If there is data left, then we're in trouble.  This API assumes everything
+  // was consumed.
+  CHECK_EQ(decompressor->avail_in, 0u);
+
+  pre_decompress_bytes.Add(frame.length());
+  post_decompress_bytes.Add(new_frame->length());
+
+  decompressed_frames.Increment();
+
+  return new_frame.release();
+}
+
+void SpdyFramer::CleanupCompressorForStream(SpdyStreamId id) {
+  CompressorMap::iterator it = stream_compressors_.find(id);
+  if (it != stream_compressors_.end()) {
+    z_stream* compressor = it->second;
+    deflateEnd(compressor);
+    delete compressor;
+    stream_compressors_.erase(it);
+  }
+}
+
+void SpdyFramer::CleanupDecompressorForStream(SpdyStreamId id) {
+  CompressorMap::iterator it = stream_decompressors_.find(id);
+  if (it != stream_decompressors_.end()) {
+    z_stream* decompressor = it->second;
+    inflateEnd(decompressor);
+    delete decompressor;
+    stream_decompressors_.erase(it);
+  }
+}
+
+void SpdyFramer::CleanupStreamCompressorsAndDecompressors() {
+  CompressorMap::iterator it;
+
+  it = stream_compressors_.begin();
+  while (it != stream_compressors_.end()) {
+    z_stream* compressor = it->second;
+    deflateEnd(compressor);
+    delete compressor;
+    ++it;
+  }
+  stream_compressors_.clear();
+
+  it = stream_decompressors_.begin();
+  while (it != stream_decompressors_.end()) {
+    z_stream* decompressor = it->second;
+    inflateEnd(decompressor);
+    delete decompressor;
+    ++it;
+  }
+  stream_decompressors_.clear();
+}
+
+SpdyFrame* SpdyFramer::DuplicateFrame(const SpdyFrame& frame) {
+  int size = SpdyFrame::size() + frame.length();
+  SpdyFrame* new_frame = new SpdyFrame(size);
+  memcpy(new_frame->data(), frame.data(), size);
+  return new_frame;
+}
+
+bool SpdyFramer::IsCompressible(const SpdyFrame& frame) const {
+  // The important frames to compress are those which contain large
+  // amounts of compressible data - namely the headers in the SYN_STREAM
+  // and SYN_REPLY.
+  // TODO(mbelshe): Reconcile this with the spec when the spec is
+  // explicit about which frames compress and which do not.
+  if (frame.is_control_frame()) {
+    const SpdyControlFrame& control_frame =
+        reinterpret_cast<const SpdyControlFrame&>(frame);
+    return control_frame.type() == SYN_STREAM ||
+           control_frame.type() == SYN_REPLY;
+  }
+
+  const SpdyDataFrame& data_frame =
+      reinterpret_cast<const SpdyDataFrame&>(frame);
+  return (data_frame.flags() & DATA_FLAG_COMPRESSED) != 0;
+}
+
+void SpdyFramer::set_enable_compression(bool value) {
+  enable_compression_ = value;
+}
+
+void SpdyFramer::set_enable_compression_default(bool value) {
+  compression_default_ = value;
+}
+
+}  // namespace spdy
diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h
new file mode 100644
index 0000000..f188373
--- /dev/null
+++ b/net/spdy/spdy_framer.h
@@ -0,0 +1,334 @@
+// Copyright (c) 2010 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_SPDY_SPDY_FRAMER_H_
+#define NET_SPDY_SPDY_FRAMER_H_
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include <list>
+#include <map>
+#include <string>
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "net/spdy/spdy_protocol.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+typedef struct z_stream_s z_stream;  // Forward declaration for zlib.
+
+namespace net {
+class HttpNetworkLayer;
+class HttpNetworkTransactionTest;
+class SpdyNetworkTransactionTest;
+class SpdySessionTest;
+class SpdyStreamTest;
+class SpdyHttpStreamTest;
+}
+
+namespace spdy {
+
+class SpdyFramer;
+class SpdyFramerTest;
+
+namespace test {
+class TestSpdyVisitor;
+void FramerSetEnableCompressionHelper(SpdyFramer* framer, bool compress);
+}  // namespace test
+
+// A datastructure for holding a set of headers from either a
+// SYN_STREAM or SYN_REPLY frame.
+typedef std::map<std::string, std::string> SpdyHeaderBlock;
+
+// A datastructure for holding a set of ID/value pairs for a SETTINGS frame.
+typedef std::pair<spdy::SettingsFlagsAndId, uint32> SpdySetting;
+typedef std::list<SpdySetting> SpdySettings;
+
+// SpdyFramerVisitorInterface is a set of callbacks for the SpdyFramer.
+// Implement this interface to receive event callbacks as frames are
+// decoded from the framer.
+class SpdyFramerVisitorInterface {
+ public:
+  virtual ~SpdyFramerVisitorInterface() {}
+
+  // Called if an error is detected in the SpdyFrame protocol.
+  virtual void OnError(SpdyFramer* framer) = 0;
+
+  // Called when a Control Frame is received.
+  virtual void OnControl(const SpdyControlFrame* frame) = 0;
+
+  // Called when data is received.
+  // |stream_id| The stream receiving data.
+  // |data| A buffer containing the data received.
+  // |len| The length of the data buffer.
+  // When the other side has finished sending data on this stream,
+  // this method will be called with a zero-length buffer.
+  virtual void OnStreamFrameData(SpdyStreamId stream_id,
+                                 const char* data,
+                                 size_t len) = 0;
+};
+
+class SpdyFramer {
+ public:
+  // SPDY states.
+  // TODO(mbelshe): Can we move these into the implementation
+  //                and avoid exposing through the header.  (Needed for test)
+  enum SpdyState {
+    SPDY_ERROR,
+    SPDY_DONE,
+    SPDY_RESET,
+    SPDY_AUTO_RESET,
+    SPDY_READING_COMMON_HEADER,
+    SPDY_INTERPRET_CONTROL_FRAME_COMMON_HEADER,
+    SPDY_CONTROL_FRAME_PAYLOAD,
+    SPDY_IGNORE_REMAINING_PAYLOAD,
+    SPDY_FORWARD_STREAM_FRAME
+  };
+
+  // SPDY error codes.
+  enum SpdyError {
+    SPDY_NO_ERROR,
+    SPDY_INVALID_CONTROL_FRAME,      // Control frame is mal-formatted.
+    SPDY_CONTROL_PAYLOAD_TOO_LARGE,  // Control frame payload was too large.
+    SPDY_ZLIB_INIT_FAILURE,          // The Zlib library could not initialize.
+    SPDY_UNSUPPORTED_VERSION,        // Control frame has unsupported version.
+    SPDY_DECOMPRESS_FAILURE,         // There was an error decompressing.
+    SPDY_COMPRESS_FAILURE,           // There was an error compressing.
+
+    LAST_ERROR,  // Must be the last entry in the enum.
+  };
+
+  // Create a new Framer.
+  SpdyFramer();
+  virtual ~SpdyFramer();
+
+  // Set callbacks to be called from the framer.  A visitor must be set, or
+  // else the framer will likely crash.  It is acceptable for the visitor
+  // to do nothing.  If this is called multiple times, only the last visitor
+  // will be used.
+  void set_visitor(SpdyFramerVisitorInterface* visitor) {
+    visitor_ = visitor;
+  }
+
+  // Pass data into the framer for parsing.
+  // Returns the number of bytes consumed. It is safe to pass more bytes in
+  // than may be consumed.
+  size_t ProcessInput(const char* data, size_t len);
+
+  // Resets the framer state after a frame has been successfully decoded.
+  // TODO(mbelshe): can we make this private?
+  void Reset();
+
+  // Check the state of the framer.
+  SpdyError error_code() const { return error_code_; }
+  SpdyState state() const { return state_; }
+
+  bool MessageFullyRead() {
+    return state_ == SPDY_DONE || state_ == SPDY_AUTO_RESET;
+  }
+  bool HasError() { return state_ == SPDY_ERROR; }
+
+  // Further parsing utilities.
+  // Given a control frame, parse out a SpdyHeaderBlock.  Only
+  // valid for SYN_STREAM and SYN_REPLY frames.
+  // Returns true if successfully parsed, false otherwise.
+  bool ParseHeaderBlock(const SpdyFrame* frame, SpdyHeaderBlock* block);
+
+  // Create a SpdySynStreamControlFrame.
+  // |stream_id| is the id for this stream.
+  // |associated_stream_id| is the associated stream id for this stream.
+  // |priority| is the priority (0-3) for this stream.
+  // |flags| is the flags to use with the data.
+  //    To mark this frame as the last frame, enable CONTROL_FLAG_FIN.
+  // |compressed| specifies whether the frame should be compressed.
+  // |headers| is the header block to include in the frame.
+  SpdySynStreamControlFrame* CreateSynStream(SpdyStreamId stream_id,
+                                             SpdyStreamId associated_stream_id,
+                                             int priority,
+                                             SpdyControlFlags flags,
+                                             bool compressed,
+                                             SpdyHeaderBlock* headers);
+
+  static SpdyRstStreamControlFrame* CreateRstStream(SpdyStreamId stream_id,
+                                                    SpdyStatusCodes status);
+
+  // Creates an instance of SpdyGoAwayControlFrame. The GOAWAY frame is used
+  // prior to the shutting down of the TCP connection, and includes the
+  // stream_id of the last stream the sender of the frame is willing to process
+  // to completion.
+  static SpdyGoAwayControlFrame* CreateGoAway(
+      SpdyStreamId last_accepted_stream_id);
+
+  // Creates an instance of SpdyWindowUpdateControlFrame. The WINDOW_UPDATE
+  // frame is used to implement per stream flow control in SPDY.
+  static SpdyWindowUpdateControlFrame* CreateWindowUpdate(
+      SpdyStreamId stream_id, uint32 delta_window_size);
+
+  // Creates an instance of SpdySettingsControlFrame. The SETTINGS frame is
+  // used to communicate name/value pairs relevant to the communication channel.
+  // TODO(mbelshe): add the name/value pairs!!
+  static SpdySettingsControlFrame* CreateSettings(const SpdySettings& values);
+
+  // Given a SpdySettingsControlFrame, extract the settings.
+  // Returns true on successful parse, false otherwise.
+  static bool ParseSettings(const SpdySettingsControlFrame* frame,
+      SpdySettings* settings);
+
+  // Create a SpdySynReplyControlFrame.
+  // |stream_id| is the stream for this frame.
+  // |flags| is the flags to use with the data.
+  //    To mark this frame as the last frame, enable CONTROL_FLAG_FIN.
+  // |compressed| specifies whether the frame should be compressed.
+  // |headers| is the header block to include in the frame.
+  SpdySynReplyControlFrame* CreateSynReply(SpdyStreamId stream_id,
+                                           SpdyControlFlags flags,
+                                           bool compressed,
+                                           SpdyHeaderBlock* headers);
+
+  // Create a data frame.
+  // |stream_id| is the stream  for this frame
+  // |data| is the data to be included in the frame.
+  // |len| is the length of the data
+  // |flags| is the flags to use with the data.
+  //    To create a compressed frame, enable DATA_FLAG_COMPRESSED.
+  //    To mark this frame as the last data frame, enable DATA_FLAG_FIN.
+  SpdyDataFrame* CreateDataFrame(SpdyStreamId stream_id, const char* data,
+                                 uint32 len, SpdyDataFlags flags);
+
+  static SpdyControlFrame* CreateNopFrame();
+
+  // NOTES about frame compression.
+  // We want spdy to compress headers across the entire session.  As long as
+  // the session is over TCP, frames are sent serially.  The client & server
+  // can each compress frames in the same order and then compress them in that
+  // order, and the remote can do the reverse.  However, we ultimately want
+  // the creation of frames to be less sensitive to order so that they can be
+  // placed over a UDP based protocol and yet still benefit from some
+  // compression.  We don't know of any good compression protocol which does
+  // not build its state in a serial (stream based) manner....  For now, we're
+  // using zlib anyway.
+
+  // Compresses a SpdyFrame.
+  // On success, returns a new SpdyFrame with the payload compressed.
+  // Compression state is maintained as part of the SpdyFramer.
+  // Returned frame must be freed with "delete".
+  // On failure, returns NULL.
+  SpdyFrame* CompressFrame(const SpdyFrame& frame);
+
+  // Decompresses a SpdyFrame.
+  // On success, returns a new SpdyFrame with the payload decompressed.
+  // Compression state is maintained as part of the SpdyFramer.
+  // Returned frame must be freed with "delete".
+  // On failure, returns NULL.
+  SpdyFrame* DecompressFrame(const SpdyFrame& frame);
+
+  // Create a copy of a frame.
+  // Returned frame must be freed with "delete".
+  SpdyFrame* DuplicateFrame(const SpdyFrame& frame);
+
+  // Returns true if a frame could be compressed.
+  bool IsCompressible(const SpdyFrame& frame) const;
+
+  // For debugging.
+  static const char* StateToString(int state);
+  static const char* ErrorCodeToString(int error_code);
+
+  // Export the compression dictionary
+  static const char kDictionary[];
+  static const int kDictionarySize;
+
+ protected:
+  FRIEND_TEST(SpdyFramerTest, DataCompression);
+  FRIEND_TEST(SpdyFramerTest, UnclosedStreamDataCompressors);
+  friend class net::SpdyNetworkTransactionTest;
+  friend class net::HttpNetworkTransactionTest;
+  friend class net::HttpNetworkLayer;  // This is temporary for the server.
+  friend class net::SpdySessionTest;
+  friend class net::SpdyHttpStreamTest;
+  friend class net::SpdyStreamTest;
+  friend class test::TestSpdyVisitor;
+  friend void test::FramerSetEnableCompressionHelper(SpdyFramer* framer,
+                                                     bool compress);
+
+  // For ease of testing we can tweak compression on/off.
+  void set_enable_compression(bool value);
+  static void set_enable_compression_default(bool value);
+
+ private:
+  typedef std::map<SpdyStreamId, z_stream*> CompressorMap;
+
+  // Internal breakout from ProcessInput.  Returns the number of bytes
+  // consumed from the data.
+  size_t ProcessCommonHeader(const char* data, size_t len);
+  void ProcessControlFrameHeader();
+  size_t ProcessControlFramePayload(const char* data, size_t len);
+  size_t ProcessDataFramePayload(const char* data, size_t len);
+
+  // Get (and lazily initialize) the ZLib state.
+  z_stream* GetHeaderCompressor();
+  z_stream* GetHeaderDecompressor();
+  z_stream* GetStreamCompressor(SpdyStreamId id);
+  z_stream* GetStreamDecompressor(SpdyStreamId id);
+
+  // Compression helpers
+  SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame);
+  SpdyDataFrame* CompressDataFrame(const SpdyDataFrame& frame);
+  SpdyControlFrame* DecompressControlFrame(const SpdyControlFrame& frame);
+  SpdyDataFrame* DecompressDataFrame(const SpdyDataFrame& frame);
+  SpdyFrame* CompressFrameWithZStream(const SpdyFrame& frame,
+                                      z_stream* compressor);
+  SpdyFrame* DecompressFrameWithZStream(const SpdyFrame& frame,
+                                        z_stream* decompressor);
+  void CleanupCompressorForStream(SpdyStreamId id);
+  void CleanupDecompressorForStream(SpdyStreamId id);
+  void CleanupStreamCompressorsAndDecompressors();
+
+  // Not used (yet)
+  size_t BytesSafeToRead() const;
+
+  // Set the error code and moves the framer into the error state.
+  void set_error(SpdyError error);
+
+  // Expands the control frame buffer to accomodate a particular payload size.
+  void ExpandControlFrameBuffer(size_t size);
+
+  // Given a frame, breakdown the variable payload length, the static header
+  // header length, and variable payload pointer.
+  bool GetFrameBoundaries(const SpdyFrame& frame, int* payload_length,
+                          int* header_length, const char** payload) const;
+
+  int num_stream_compressors() const { return stream_compressors_.size(); }
+  int num_stream_decompressors() const { return stream_decompressors_.size(); }
+
+  SpdyState state_;
+  SpdyError error_code_;
+  size_t remaining_payload_;
+  size_t remaining_control_payload_;
+
+  char* current_frame_buffer_;
+  size_t current_frame_len_;  // Number of bytes read into the current_frame_.
+  size_t current_frame_capacity_;
+
+  bool enable_compression_;  // Controls all compression
+  // SPDY header compressors.
+  scoped_ptr<z_stream> header_compressor_;
+  scoped_ptr<z_stream> header_decompressor_;
+
+  // Per-stream data compressors.
+  CompressorMap stream_compressors_;
+  CompressorMap stream_decompressors_;
+
+  SpdyFramerVisitorInterface* visitor_;
+
+  static bool compression_default_;
+};
+
+}  // namespace spdy
+
+#endif  // NET_SPDY_SPDY_FRAMER_H_
diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc
new file mode 100644
index 0000000..4e976ab
--- /dev/null
+++ b/net/spdy/spdy_framer_test.cc
@@ -0,0 +1,1183 @@
+// Copyright (c) 2010 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 <algorithm>
+#include <iostream>
+
+#include "base/scoped_ptr.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "testing/platform_test.h"
+
+namespace spdy {
+
+namespace test {
+
+std::string HexDumpWithMarks(const unsigned char* data, int length,
+                             const bool* marks, int mark_length) {
+  static const char kHexChars[] = "0123456789ABCDEF";
+  static const int kColumns = 4;
+
+  std::string hex;
+  for (const unsigned char* row = data; length > 0;
+       row += kColumns, length -= kColumns) {
+    for (const unsigned char *p = row; p < row + 4; ++p) {
+      if (p < row + length) {
+        const bool mark =
+            (marks && (p - data) < mark_length && marks[p - data]);
+        hex += mark ? '*' : ' ';
+        hex += kHexChars[(*p & 0xf0) >> 4];
+        hex += kHexChars[*p & 0x0f];
+        hex += mark ? '*' : ' ';
+      } else {
+        hex += "    ";
+      }
+    }
+    hex = hex + "  ";
+
+    for (const unsigned char *p = row; p < row + 4 && p < row + length; ++p)
+      hex += (*p >= 0x20 && *p <= 0x7f) ? (*p) : '.';
+
+    hex = hex + '\n';
+  }
+  return hex;
+}
+
+void CompareCharArraysWithHexError(
+    const std::string& description,
+    const unsigned char* actual,
+    const int actual_len,
+    const unsigned char* expected,
+    const int expected_len) {
+  const int min_len = actual_len > expected_len ? expected_len : actual_len;
+  const int max_len = actual_len > expected_len ? actual_len : expected_len;
+  scoped_array<bool> marks(new bool[max_len]);
+  bool identical = (actual_len == expected_len);
+  for (int i = 0; i < min_len; ++i) {
+    if (actual[i] != expected[i]) {
+      marks[i] = true;
+      identical = false;
+    } else {
+      marks[i] = false;
+    }
+  }
+  for (int i = min_len; i < max_len; ++i) {
+    marks[i] = true;
+  }
+  if (identical) return;
+  ADD_FAILURE()
+      << "Description:\n"
+      << description
+      << "\n\nExpected:\n"
+      << HexDumpWithMarks(expected, expected_len, marks.get(), max_len)
+      << "\nActual:\n"
+      << HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
+}
+
+void FramerSetEnableCompressionHelper(SpdyFramer* framer, bool compress) {
+  framer->set_enable_compression(compress);
+}
+
+class TestSpdyVisitor : public SpdyFramerVisitorInterface  {
+ public:
+  TestSpdyVisitor()
+    : error_count_(0),
+      syn_frame_count_(0),
+      syn_reply_frame_count_(0),
+      data_bytes_(0),
+      fin_frame_count_(0),
+      fin_flag_count_(0),
+      zero_length_data_frame_count_(0) {
+  }
+
+  void OnError(SpdyFramer* f) {
+    error_count_++;
+  }
+
+  void OnStreamFrameData(SpdyStreamId stream_id,
+                         const char* data,
+                         size_t len) {
+    if (len == 0)
+      ++zero_length_data_frame_count_;
+
+    data_bytes_ += len;
+    std::cerr << "OnStreamFrameData(" << stream_id << ", \"";
+    if (len > 0) {
+      for (size_t i = 0 ; i < len; ++i) {
+        std::cerr << std::hex << (0xFF & (unsigned int)data[i]) << std::dec;
+      }
+    }
+    std::cerr << "\", " << len << ")\n";
+  }
+
+  void OnControl(const SpdyControlFrame* frame) {
+    SpdyHeaderBlock headers;
+    bool parsed_headers = false;
+    switch (frame->type()) {
+      case SYN_STREAM:
+        parsed_headers = framer_.ParseHeaderBlock(frame, &headers);
+        DCHECK(parsed_headers);
+        syn_frame_count_++;
+        break;
+      case SYN_REPLY:
+        parsed_headers = framer_.ParseHeaderBlock(frame, &headers);
+        DCHECK(parsed_headers);
+        syn_reply_frame_count_++;
+        break;
+      case RST_STREAM:
+        fin_frame_count_++;
+        break;
+      default:
+        DCHECK(false);  // Error!
+    }
+    if (frame->flags() & CONTROL_FLAG_FIN)
+      ++fin_flag_count_;
+  }
+
+  // Convenience function which runs a framer simulation with particular input.
+  void SimulateInFramer(const unsigned char* input, size_t size) {
+    framer_.set_enable_compression(false);
+    framer_.set_visitor(this);
+    size_t input_remaining = size;
+    const char* input_ptr = reinterpret_cast<const char*>(input);
+    while (input_remaining > 0 &&
+           framer_.error_code() == SpdyFramer::SPDY_NO_ERROR) {
+      // To make the tests more interesting, we feed random (amd small) chunks
+      // into the framer.  This simulates getting strange-sized reads from
+      // the socket.
+      const size_t kMaxReadSize = 32;
+      size_t bytes_read =
+          (rand() % std::min(input_remaining, kMaxReadSize)) + 1;
+      size_t bytes_processed = framer_.ProcessInput(input_ptr, bytes_read);
+      input_remaining -= bytes_processed;
+      input_ptr += bytes_processed;
+      if (framer_.state() == SpdyFramer::SPDY_DONE)
+        framer_.Reset();
+    }
+  }
+
+  SpdyFramer framer_;
+  // Counters from the visitor callbacks.
+  int error_count_;
+  int syn_frame_count_;
+  int syn_reply_frame_count_;
+  int data_bytes_;
+  int fin_frame_count_;  // The count of RST_STREAM type frames received.
+  int fin_flag_count_;  // The count of frames with the FIN flag set.
+  int zero_length_data_frame_count_;  // The count of zero-length data frames.
+};
+
+}  // namespace test
+
+}  // namespace spdy
+
+using spdy::SpdyControlFlags;
+using spdy::SpdyControlFrame;
+using spdy::SpdyDataFrame;
+using spdy::SpdyFrame;
+using spdy::SpdyFrameBuilder;
+using spdy::SpdyFramer;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdySynStreamControlFrame;
+using spdy::kControlFlagMask;
+using spdy::CONTROL_FLAG_NONE;
+using spdy::DATA_FLAG_COMPRESSED;
+using spdy::DATA_FLAG_FIN;
+using spdy::SYN_STREAM;
+using spdy::test::CompareCharArraysWithHexError;
+using spdy::test::FramerSetEnableCompressionHelper;
+using spdy::test::TestSpdyVisitor;
+
+namespace spdy {
+
+class SpdyFramerTest : public PlatformTest {
+ public:
+  virtual void TearDown() {}
+
+ protected:
+  void CompareFrame(const std::string& description,
+                    const SpdyFrame& actual_frame,
+                    const unsigned char* expected,
+                    const int expected_len) {
+    const unsigned char* actual =
+        reinterpret_cast<const unsigned char*>(actual_frame.data());
+    int actual_len = actual_frame.length() + SpdyFrame::size();
+    CompareCharArraysWithHexError(
+        description, actual, actual_len, expected, expected_len);
+  }
+};
+
+// Test that we can encode and decode a SpdyHeaderBlock.
+TEST_F(SpdyFramerTest, HeaderBlock) {
+  SpdyHeaderBlock headers;
+  headers["alpha"] = "beta";
+  headers["gamma"] = "charlie";
+  SpdyFramer framer;
+
+  // Encode the header block into a SynStream frame.
+  scoped_ptr<SpdySynStreamControlFrame> frame(
+      framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, &headers));
+  EXPECT_TRUE(frame.get() != NULL);
+
+  SpdyHeaderBlock new_headers;
+  EXPECT_TRUE(framer.ParseHeaderBlock(frame.get(), &new_headers));
+
+  EXPECT_EQ(headers.size(), new_headers.size());
+  EXPECT_EQ(headers["alpha"], new_headers["alpha"]);
+  EXPECT_EQ(headers["gamma"], new_headers["gamma"]);
+}
+
+TEST_F(SpdyFramerTest, OutOfOrderHeaders) {
+  SpdyFrameBuilder frame;
+
+  frame.WriteUInt16(kControlFlagMask | 1);
+  frame.WriteUInt16(SYN_STREAM);
+  frame.WriteUInt32(0);  // Placeholder for the length.
+  frame.WriteUInt32(3);  // stream_id
+  frame.WriteUInt32(0);  // associated stream id
+  frame.WriteUInt16(0);  // Priority.
+
+  frame.WriteUInt16(2);  // Number of headers.
+  SpdyHeaderBlock::iterator it;
+  frame.WriteString("gamma");
+  frame.WriteString("gamma");
+  frame.WriteString("alpha");
+  frame.WriteString("alpha");
+  // write the length
+  frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::size());
+
+  SpdyHeaderBlock new_headers;
+  scoped_ptr<SpdyFrame> control_frame(frame.take());
+  SpdyFramer framer;
+  FramerSetEnableCompressionHelper(&framer, false);
+  EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers));
+}
+
+TEST_F(SpdyFramerTest, WrongNumberOfHeaders) {
+  SpdyFrameBuilder frame1;
+  SpdyFrameBuilder frame2;
+
+  // a frame with smaller number of actual headers
+  frame1.WriteUInt16(kControlFlagMask | 1);
+  frame1.WriteUInt16(SYN_STREAM);
+  frame1.WriteUInt32(0);  // Placeholder for the length.
+  frame1.WriteUInt32(3);  // stream_id
+  frame1.WriteUInt32(0);  // associated stream id
+  frame1.WriteUInt16(0);  // Priority.
+
+  frame1.WriteUInt16(1);  // Wrong number of headers (underflow)
+  frame1.WriteString("gamma");
+  frame1.WriteString("gamma");
+  frame1.WriteString("alpha");
+  frame1.WriteString("alpha");
+  // write the length
+  frame1.WriteUInt32ToOffset(4, frame1.length() - SpdyFrame::size());
+
+  // a frame with larger number of actual headers
+  frame2.WriteUInt16(kControlFlagMask | 1);
+  frame2.WriteUInt16(SYN_STREAM);
+  frame2.WriteUInt32(0);  // Placeholder for the length.
+  frame2.WriteUInt32(3);  // stream_id
+  frame2.WriteUInt32(0);  // associated stream id
+  frame2.WriteUInt16(0);  // Priority.
+
+  frame2.WriteUInt16(100);  // Wrong number of headers (overflow)
+  frame2.WriteString("gamma");
+  frame2.WriteString("gamma");
+  frame2.WriteString("alpha");
+  frame2.WriteString("alpha");
+  // write the length
+  frame2.WriteUInt32ToOffset(4, frame2.length() - SpdyFrame::size());
+
+  SpdyHeaderBlock new_headers;
+  scoped_ptr<SpdyFrame> syn_frame1(frame1.take());
+  scoped_ptr<SpdyFrame> syn_frame2(frame2.take());
+  SpdyFramer framer;
+  FramerSetEnableCompressionHelper(&framer, false);
+  EXPECT_FALSE(framer.ParseHeaderBlock(syn_frame1.get(), &new_headers));
+  EXPECT_FALSE(framer.ParseHeaderBlock(syn_frame2.get(), &new_headers));
+}
+
+TEST_F(SpdyFramerTest, DuplicateHeader) {
+  SpdyFrameBuilder frame;
+
+  frame.WriteUInt16(kControlFlagMask | 1);
+  frame.WriteUInt16(SYN_STREAM);
+  frame.WriteUInt32(0);  // Placeholder for the length.
+  frame.WriteUInt32(3);  // stream_id
+  frame.WriteUInt32(0);  // associated stream id
+  frame.WriteUInt16(0);  // Priority.
+
+  frame.WriteUInt16(2);  // Number of headers.
+  SpdyHeaderBlock::iterator it;
+  frame.WriteString("name");
+  frame.WriteString("value1");
+  frame.WriteString("name");
+  frame.WriteString("value2");
+  // write the length
+  frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::size());
+
+  SpdyHeaderBlock new_headers;
+  scoped_ptr<SpdyFrame> control_frame(frame.take());
+  SpdyFramer framer;
+  FramerSetEnableCompressionHelper(&framer, false);
+  // This should fail because duplicate headers are verboten by the spec.
+  EXPECT_FALSE(framer.ParseHeaderBlock(control_frame.get(), &new_headers));
+}
+
+TEST_F(SpdyFramerTest, MultiValueHeader) {
+  SpdyFrameBuilder frame;
+
+  frame.WriteUInt16(kControlFlagMask | 1);
+  frame.WriteUInt16(SYN_STREAM);
+  frame.WriteUInt32(0);  // Placeholder for the length.
+  frame.WriteUInt32(3);  // stream_id
+  frame.WriteUInt32(0);  // associated stream id
+  frame.WriteUInt16(0);  // Priority.
+
+  frame.WriteUInt16(1);  // Number of headers.
+  SpdyHeaderBlock::iterator it;
+  frame.WriteString("name");
+  std::string value("value1\0value2");
+  frame.WriteString(value);
+  // write the length
+  frame.WriteUInt32ToOffset(4, frame.length() - SpdyFrame::size());
+
+  SpdyHeaderBlock new_headers;
+  scoped_ptr<SpdyFrame> control_frame(frame.take());
+  SpdyFramer framer;
+  FramerSetEnableCompressionHelper(&framer, false);
+  EXPECT_TRUE(framer.ParseHeaderBlock(control_frame.get(), &new_headers));
+  EXPECT_TRUE(new_headers.find("name") != new_headers.end());
+  EXPECT_EQ(value, new_headers.find("name")->second);
+}
+
+TEST_F(SpdyFramerTest, ZeroLengthHeader) {
+  SpdyHeaderBlock header1;
+  SpdyHeaderBlock header2;
+  SpdyHeaderBlock header3;
+
+  header1[""] = "value2";
+  header2["name3"] = "";
+  header3[""] = "";
+
+  SpdyFramer framer;
+  SpdyHeaderBlock parsed_headers;
+
+  scoped_ptr<SpdySynStreamControlFrame> frame1(
+      framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, &header1));
+  EXPECT_TRUE(frame1.get() != NULL);
+  EXPECT_FALSE(framer.ParseHeaderBlock(frame1.get(), &parsed_headers));
+
+  scoped_ptr<SpdySynStreamControlFrame> frame2(
+      framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, &header2));
+  EXPECT_TRUE(frame2.get() != NULL);
+  EXPECT_FALSE(framer.ParseHeaderBlock(frame2.get(), &parsed_headers));
+
+  scoped_ptr<SpdySynStreamControlFrame> frame3(
+      framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true, &header3));
+  EXPECT_TRUE(frame3.get() != NULL);
+  EXPECT_FALSE(framer.ParseHeaderBlock(frame3.get(), &parsed_headers));
+}
+
+TEST_F(SpdyFramerTest, BasicCompression) {
+  SpdyHeaderBlock headers;
+  headers["server"] = "SpdyServer 1.0";
+  headers["date"] = "Mon 12 Jan 2009 12:12:12 PST";
+  headers["status"] = "200";
+  headers["version"] = "HTTP/1.1";
+  headers["content-type"] = "text/html";
+  headers["content-length"] = "12";
+
+  SpdyFramer framer;
+  FramerSetEnableCompressionHelper(&framer, true);
+  scoped_ptr<SpdySynStreamControlFrame>
+      frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true,
+                                    &headers));
+  scoped_ptr<SpdySynStreamControlFrame>
+      frame2(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, true,
+                                    &headers));
+
+  // Expect the second frame to be more compact than the first.
+  EXPECT_LE(frame2->length(), frame1->length());
+
+  // Decompress the first frame
+  scoped_ptr<SpdyFrame> frame3(framer.DecompressFrame(*frame1.get()));
+
+  // Decompress the second frame
+  scoped_ptr<SpdyFrame> frame4(framer.DecompressFrame(*frame2.get()));
+
+  // Expect frames 3 & 4 to be the same.
+  EXPECT_EQ(0,
+      memcmp(frame3->data(), frame4->data(),
+      SpdyFrame::size() + frame3->length()));
+}
+
+TEST_F(SpdyFramerTest, DecompressUncompressedFrame) {
+  SpdyHeaderBlock headers;
+  headers["server"] = "SpdyServer 1.0";
+  headers["date"] = "Mon 12 Jan 2009 12:12:12 PST";
+  headers["status"] = "200";
+  headers["version"] = "HTTP/1.1";
+  headers["content-type"] = "text/html";
+  headers["content-length"] = "12";
+
+  SpdyFramer framer;
+  FramerSetEnableCompressionHelper(&framer, true);
+  scoped_ptr<SpdySynStreamControlFrame>
+      frame1(framer.CreateSynStream(1, 0, 1, CONTROL_FLAG_NONE, false,
+                                    &headers));
+
+  // Decompress the frame
+  scoped_ptr<SpdyFrame> frame2(framer.DecompressFrame(*frame1.get()));
+
+  EXPECT_EQ(NULL, frame2.get());
+}
+
+TEST_F(SpdyFramerTest, Basic) {
+  const unsigned char input[] = {
+    0x80, 0x01, 0x00, 0x01,   // SYN Stream #1
+    0x00, 0x00, 0x00, 0x14,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x02, 'h', 'h',
+    0x00, 0x02, 'v', 'v',
+
+    0x00, 0x00, 0x00, 0x01,   // DATA on Stream #1
+    0x00, 0x00, 0x00, 0x0c,
+      0xde, 0xad, 0xbe, 0xef,
+      0xde, 0xad, 0xbe, 0xef,
+      0xde, 0xad, 0xbe, 0xef,
+
+    0x80, 0x01, 0x00, 0x01,   // SYN Stream #3
+    0x00, 0x00, 0x00, 0x0c,
+    0x00, 0x00, 0x00, 0x03,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00,
+
+    0x00, 0x00, 0x00, 0x03,   // DATA on Stream #3
+    0x00, 0x00, 0x00, 0x08,
+      0xde, 0xad, 0xbe, 0xef,
+      0xde, 0xad, 0xbe, 0xef,
+
+    0x00, 0x00, 0x00, 0x01,   // DATA on Stream #1
+    0x00, 0x00, 0x00, 0x04,
+      0xde, 0xad, 0xbe, 0xef,
+
+    0x80, 0x01, 0x00, 0x03,   // RST_STREAM on Stream #1
+    0x00, 0x00, 0x00, 0x08,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00,
+
+    0x00, 0x00, 0x00, 0x03,   // DATA on Stream #3
+    0x00, 0x00, 0x00, 0x00,
+
+    0x80, 0x01, 0x00, 0x03,   // RST_STREAM on Stream #3
+    0x00, 0x00, 0x00, 0x08,
+    0x00, 0x00, 0x00, 0x03,
+    0x00, 0x00, 0x00, 0x00,
+  };
+
+  TestSpdyVisitor visitor;
+  visitor.SimulateInFramer(input, sizeof(input));
+
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(2, visitor.syn_frame_count_);
+  EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+  EXPECT_EQ(24, visitor.data_bytes_);
+  EXPECT_EQ(2, visitor.fin_frame_count_);
+  EXPECT_EQ(0, visitor.fin_flag_count_);
+  EXPECT_EQ(0, visitor.zero_length_data_frame_count_);
+}
+
+// Test that the FIN flag on a data frame signifies EOF.
+TEST_F(SpdyFramerTest, FinOnDataFrame) {
+  const unsigned char input[] = {
+    0x80, 0x01, 0x00, 0x01,   // SYN Stream #1
+    0x00, 0x00, 0x00, 0x14,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x02, 'h', 'h',
+    0x00, 0x02, 'v', 'v',
+
+    0x80, 0x01, 0x00, 0x02,   // SYN REPLY Stream #1
+    0x00, 0x00, 0x00, 0x10,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x02, 'a', 'a',
+    0x00, 0x02, 'b', 'b',
+
+    0x00, 0x00, 0x00, 0x01,   // DATA on Stream #1
+    0x00, 0x00, 0x00, 0x0c,
+      0xde, 0xad, 0xbe, 0xef,
+      0xde, 0xad, 0xbe, 0xef,
+      0xde, 0xad, 0xbe, 0xef,
+
+    0x00, 0x00, 0x00, 0x01,   // DATA on Stream #1, with EOF
+    0x01, 0x00, 0x00, 0x04,
+      0xde, 0xad, 0xbe, 0xef,
+  };
+
+  TestSpdyVisitor visitor;
+  visitor.SimulateInFramer(input, sizeof(input));
+
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(1, visitor.syn_frame_count_);
+  EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+  EXPECT_EQ(16, visitor.data_bytes_);
+  EXPECT_EQ(0, visitor.fin_frame_count_);
+  EXPECT_EQ(0, visitor.fin_flag_count_);
+  EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+}
+
+// Test that the FIN flag on a SYN reply frame signifies EOF.
+TEST_F(SpdyFramerTest, FinOnSynReplyFrame) {
+  const unsigned char input[] = {
+    0x80, 0x01, 0x00, 0x01,   // SYN Stream #1
+    0x00, 0x00, 0x00, 0x14,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x02, 'h', 'h',
+    0x00, 0x02, 'v', 'v',
+
+    0x80, 0x01, 0x00, 0x02,   // SYN REPLY Stream #1
+    0x01, 0x00, 0x00, 0x10,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x00, 0x01,
+    0x00, 0x02, 'a', 'a',
+    0x00, 0x02, 'b', 'b',
+  };
+
+  TestSpdyVisitor visitor;
+  visitor.SimulateInFramer(input, sizeof(input));
+
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(1, visitor.syn_frame_count_);
+  EXPECT_EQ(1, visitor.syn_reply_frame_count_);
+  EXPECT_EQ(0, visitor.data_bytes_);
+  EXPECT_EQ(0, visitor.fin_frame_count_);
+  EXPECT_EQ(1, visitor.fin_flag_count_);
+  EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+}
+
+// Basic compression & decompression
+TEST_F(SpdyFramerTest, DataCompression) {
+  SpdyFramer send_framer;
+  SpdyFramer recv_framer;
+
+  FramerSetEnableCompressionHelper(&send_framer, true);
+  FramerSetEnableCompressionHelper(&recv_framer, true);
+
+  // Mix up some SYNs and DATA frames since they use different compressors.
+  const char kHeader1[] = "header1";
+  const char kHeader2[] = "header2";
+  const char kHeader3[] = "header3";
+  const char kValue1[] = "value1";
+  const char kValue2[] = "value2";
+  const char kValue3[] = "value3";
+
+  // SYN_STREAM #1
+  SpdyHeaderBlock block;
+  block[kHeader1] = kValue1;
+  block[kHeader2] = kValue2;
+  SpdyControlFlags flags(CONTROL_FLAG_NONE);
+  scoped_ptr<spdy::SpdyFrame> syn_frame_1(
+      send_framer.CreateSynStream(1, 0, 0, flags, true, &block));
+  EXPECT_TRUE(syn_frame_1.get() != NULL);
+
+  // DATA #1
+  const char bytes[] = "this is a test test test test test!";
+  scoped_ptr<SpdyFrame> data_frame_1(
+      send_framer.CreateDataFrame(1, bytes, arraysize(bytes),
+                                  DATA_FLAG_COMPRESSED));
+  EXPECT_TRUE(data_frame_1.get() != NULL);
+
+  // SYN_STREAM #2
+  block[kHeader3] = kValue3;
+  scoped_ptr<SpdyFrame> syn_frame_2(
+      send_framer.CreateSynStream(3, 0, 0, flags, true, &block));
+  EXPECT_TRUE(syn_frame_2.get() != NULL);
+
+  // DATA #2
+  scoped_ptr<SpdyFrame> data_frame_2(
+      send_framer.CreateDataFrame(3, bytes, arraysize(bytes),
+                                  DATA_FLAG_COMPRESSED));
+  EXPECT_TRUE(data_frame_2.get() != NULL);
+
+  // Now start decompressing
+  scoped_ptr<SpdyFrame> decompressed;
+  SpdyControlFrame* control_frame;
+  SpdyDataFrame* data_frame;
+  SpdyHeaderBlock decompressed_headers;
+
+  decompressed.reset(recv_framer.DuplicateFrame(*syn_frame_1.get()));
+  EXPECT_TRUE(decompressed.get() != NULL);
+  EXPECT_TRUE(decompressed->is_control_frame());
+  control_frame = reinterpret_cast<SpdyControlFrame*>(decompressed.get());
+  EXPECT_EQ(SYN_STREAM, control_frame->type());
+  EXPECT_TRUE(recv_framer.ParseHeaderBlock(
+      control_frame, &decompressed_headers));
+  EXPECT_EQ(2u, decompressed_headers.size());
+  EXPECT_EQ(SYN_STREAM, control_frame->type());
+  EXPECT_EQ(kValue1, decompressed_headers[kHeader1]);
+  EXPECT_EQ(kValue2, decompressed_headers[kHeader2]);
+
+  decompressed.reset(recv_framer.DecompressFrame(*data_frame_1.get()));
+  EXPECT_TRUE(decompressed.get() != NULL);
+  EXPECT_FALSE(decompressed->is_control_frame());
+  data_frame = reinterpret_cast<SpdyDataFrame*>(decompressed.get());
+  EXPECT_EQ(arraysize(bytes), data_frame->length());
+  EXPECT_EQ(0, memcmp(data_frame->payload(), bytes, data_frame->length()));
+
+  decompressed.reset(recv_framer.DuplicateFrame(*syn_frame_2.get()));
+  EXPECT_TRUE(decompressed.get() != NULL);
+  EXPECT_TRUE(decompressed->is_control_frame());
+  control_frame = reinterpret_cast<SpdyControlFrame*>(decompressed.get());
+  EXPECT_EQ(control_frame->type(), SYN_STREAM);
+  decompressed_headers.clear();
+  EXPECT_TRUE(recv_framer.ParseHeaderBlock(
+      control_frame, &decompressed_headers));
+  EXPECT_EQ(3u, decompressed_headers.size());
+  EXPECT_EQ(SYN_STREAM, control_frame->type());
+  EXPECT_EQ(kValue1, decompressed_headers[kHeader1]);
+  EXPECT_EQ(kValue2, decompressed_headers[kHeader2]);
+  EXPECT_EQ(kValue3, decompressed_headers[kHeader3]);
+
+  decompressed.reset(recv_framer.DecompressFrame(*data_frame_2.get()));
+  EXPECT_TRUE(decompressed.get() != NULL);
+  EXPECT_FALSE(decompressed->is_control_frame());
+  data_frame = reinterpret_cast<SpdyDataFrame*>(decompressed.get());
+  EXPECT_EQ(arraysize(bytes), data_frame->length());
+  EXPECT_EQ(0, memcmp(data_frame->payload(), bytes, data_frame->length()));
+
+  // We didn't close these streams, so the compressors should be active.
+  EXPECT_EQ(2, send_framer.num_stream_compressors());
+  EXPECT_EQ(0, send_framer.num_stream_decompressors());
+  EXPECT_EQ(0, recv_framer.num_stream_compressors());
+  EXPECT_EQ(2, recv_framer.num_stream_decompressors());
+}
+
+// Verify we don't leak when we leave streams unclosed
+TEST_F(SpdyFramerTest, UnclosedStreamDataCompressors) {
+  SpdyFramer send_framer;
+
+  FramerSetEnableCompressionHelper(&send_framer, false);
+
+  const char kHeader1[] = "header1";
+  const char kHeader2[] = "header2";
+  const char kValue1[] = "value1";
+  const char kValue2[] = "value2";
+
+  SpdyHeaderBlock block;
+  block[kHeader1] = kValue1;
+  block[kHeader2] = kValue2;
+  SpdyControlFlags flags(CONTROL_FLAG_NONE);
+  scoped_ptr<spdy::SpdyFrame> syn_frame(
+      send_framer.CreateSynStream(1, 0, 0, flags, true, &block));
+  EXPECT_TRUE(syn_frame.get() != NULL);
+
+  const char bytes[] = "this is a test test test test test!";
+  scoped_ptr<SpdyFrame> send_frame(
+      send_framer.CreateDataFrame(1, bytes, arraysize(bytes),
+                                  DATA_FLAG_FIN));
+  EXPECT_TRUE(send_frame.get() != NULL);
+
+  // Run the inputs through the framer.
+  TestSpdyVisitor visitor;
+  const unsigned char* data;
+  data = reinterpret_cast<const unsigned char*>(syn_frame->data());
+  visitor.SimulateInFramer(data, syn_frame->length() + SpdyFrame::size());
+  data = reinterpret_cast<const unsigned char*>(send_frame->data());
+  visitor.SimulateInFramer(data, send_frame->length() + SpdyFrame::size());
+
+  EXPECT_EQ(0, visitor.error_count_);
+  EXPECT_EQ(1, visitor.syn_frame_count_);
+  EXPECT_EQ(0, visitor.syn_reply_frame_count_);
+  EXPECT_EQ(arraysize(bytes), static_cast<unsigned>(visitor.data_bytes_));
+  EXPECT_EQ(0, visitor.fin_frame_count_);
+  EXPECT_EQ(0, visitor.fin_flag_count_);
+  EXPECT_EQ(1, visitor.zero_length_data_frame_count_);
+
+  // We closed the streams, so all compressors should be down.
+  EXPECT_EQ(0, visitor.framer_.num_stream_compressors());
+  EXPECT_EQ(0, visitor.framer_.num_stream_decompressors());
+  EXPECT_EQ(0, send_framer.num_stream_compressors());
+  EXPECT_EQ(0, send_framer.num_stream_decompressors());
+}
+
+TEST_F(SpdyFramerTest, CreateDataFrame) {
+  SpdyFramer framer;
+
+  {
+    const char kDescription[] = "'hello' data frame, no FIN";
+    const unsigned char kFrameData[] = {
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x05,
+      'h', 'e', 'l', 'l',
+      'o'
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+        1, "hello", 5, DATA_FLAG_NONE));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] = "Data frame with negative data byte, no FIN";
+    const unsigned char kFrameData[] = {
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x01,
+      0xff
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+        1, "\xff", 1, DATA_FLAG_NONE));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] = "'hello' data frame, with FIN";
+    const unsigned char kFrameData[] = {
+      0x00, 0x00, 0x00, 0x01,
+      0x01, 0x00, 0x00, 0x05,
+      'h', 'e', 'l', 'l',
+      'o'
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+        1, "hello", 5, DATA_FLAG_FIN));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] = "Empty data frame";
+    const unsigned char kFrameData[] = {
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x00,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+        1, "", 0, DATA_FLAG_NONE));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] = "Data frame with max stream ID";
+    const unsigned char kFrameData[] = {
+      0x7f, 0xff, 0xff, 0xff,
+      0x01, 0x00, 0x00, 0x05,
+      'h', 'e', 'l', 'l',
+      'o'
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateDataFrame(
+        0x7fffffff, "hello", 5, DATA_FLAG_FIN));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+}
+
+TEST_F(SpdyFramerTest, CreateSynStreamUncompressed) {
+  SpdyFramer framer;
+  FramerSetEnableCompressionHelper(&framer, false);
+
+  {
+    const char kDescription[] = "SYN_STREAM frame, lowest pri, no FIN";
+
+    SpdyHeaderBlock headers;
+    headers["bar"] = "foo";
+    headers["foo"] = "bar";
+
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x20,
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x00,
+      0xC0, 0x00, 0x00, 0x02,
+      0x00, 0x03, 'b',  'a',
+      'r',  0x00, 0x03, 'f',
+      'o',  'o',  0x00, 0x03,
+      'f',  'o',  'o',  0x00,
+      0x03, 'b',  'a',  'r'
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
+        1, 0, SPDY_PRIORITY_LOWEST, CONTROL_FLAG_NONE,
+        false, &headers));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] =
+        "SYN_STREAM frame with a 0-length header name, highest pri, FIN, "
+        "max stream ID";
+
+    SpdyHeaderBlock headers;
+    headers[""] = "foo";
+    headers["foo"] = "bar";
+
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x01,
+      0x01, 0x00, 0x00, 0x1D,
+      0x7f, 0xff, 0xff, 0xff,
+      0x7f, 0xff, 0xff, 0xff,
+      0x00, 0x00, 0x00, 0x02,
+      0x00, 0x00, 0x00, 0x03,
+      'f',  'o',  'o',  0x00,
+      0x03, 'f',  'o',  'o',
+      0x00, 0x03, 'b',  'a',
+      'r'
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
+        0x7fffffff, 0x7fffffff, SPDY_PRIORITY_HIGHEST, CONTROL_FLAG_FIN,
+        false, &headers));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] =
+        "SYN_STREAM frame with a 0-length header val, highest pri, FIN, "
+        "max stream ID";
+
+    SpdyHeaderBlock headers;
+    headers["bar"] = "foo";
+    headers["foo"] = "";
+
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x01,
+      0x01, 0x00, 0x00, 0x1D,
+      0x7f, 0xff, 0xff, 0xff,
+      0x7f, 0xff, 0xff, 0xff,
+      0x00, 0x00, 0x00, 0x02,
+      0x00, 0x03, 'b',  'a',
+      'r',  0x00, 0x03, 'f',
+      'o',  'o',  0x00, 0x03,
+      'f',  'o',  'o',  0x00,
+      0x00
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
+        0x7fffffff, 0x7fffffff, SPDY_PRIORITY_HIGHEST, CONTROL_FLAG_FIN,
+        false, &headers));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+}
+
+TEST_F(SpdyFramerTest, CreateSynStreamCompressed) {
+  SpdyFramer framer;
+  FramerSetEnableCompressionHelper(&framer, true);
+
+  {
+    const char kDescription[] =
+        "SYN_STREAM frame, lowest pri, no FIN";
+
+    SpdyHeaderBlock headers;
+    headers["bar"] = "foo";
+    headers["foo"] = "bar";
+
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x25,
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x00,
+      0xC0, 0x00, 0x38, 0xea,
+      0xdf, 0xa2, 0x51, 0xb2,
+      0x62, 0x60, 0x62, 0x60,
+      0x4e, 0x4a, 0x2c, 0x62,
+      0x60, 0x4e, 0xcb, 0xcf,
+      0x87, 0x12, 0x40, 0x2e,
+      0x00, 0x00, 0x00, 0xff,
+      0xff
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateSynStream(
+        1, 0, SPDY_PRIORITY_LOWEST, CONTROL_FLAG_NONE,
+        true, &headers));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+}
+
+TEST_F(SpdyFramerTest, CreateSynReplyUncompressed) {
+  SpdyFramer framer;
+  FramerSetEnableCompressionHelper(&framer, false);
+
+  {
+    const char kDescription[] = "SYN_REPLY frame, no FIN";
+
+    SpdyHeaderBlock headers;
+    headers["bar"] = "foo";
+    headers["foo"] = "bar";
+
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x02,
+      0x00, 0x00, 0x00, 0x1C,
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x02,
+      0x00, 0x03, 'b',  'a',
+      'r',  0x00, 0x03, 'f',
+      'o',  'o',  0x00, 0x03,
+      'f',  'o',  'o',  0x00,
+      0x03, 'b',  'a',  'r'
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
+        1, CONTROL_FLAG_NONE, false, &headers));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] =
+        "SYN_REPLY frame with a 0-length header name, FIN, max stream ID";
+
+    SpdyHeaderBlock headers;
+    headers[""] = "foo";
+    headers["foo"] = "bar";
+
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x02,
+      0x01, 0x00, 0x00, 0x19,
+      0x7f, 0xff, 0xff, 0xff,
+      0x00, 0x00, 0x00, 0x02,
+      0x00, 0x00, 0x00, 0x03,
+      'f',  'o',  'o',  0x00,
+      0x03, 'f',  'o',  'o',
+      0x00, 0x03, 'b',  'a',
+      'r'
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
+        0x7fffffff, CONTROL_FLAG_FIN, false, &headers));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] =
+        "SYN_REPLY frame with a 0-length header val, FIN, max stream ID";
+
+    SpdyHeaderBlock headers;
+    headers["bar"] = "foo";
+    headers["foo"] = "";
+
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x02,
+      0x01, 0x00, 0x00, 0x19,
+      0x7f, 0xff, 0xff, 0xff,
+      0x00, 0x00, 0x00, 0x02,
+      0x00, 0x03, 'b',  'a',
+      'r',  0x00, 0x03, 'f',
+      'o',  'o',  0x00, 0x03,
+      'f',  'o',  'o',  0x00,
+      0x00
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
+        0x7fffffff, CONTROL_FLAG_FIN, false, &headers));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+}
+
+TEST_F(SpdyFramerTest, CreateSynReplyCompressed) {
+  SpdyFramer framer;
+  FramerSetEnableCompressionHelper(&framer, true);
+
+  {
+    const char kDescription[] = "SYN_REPLY frame, no FIN";
+
+    SpdyHeaderBlock headers;
+    headers["bar"] = "foo";
+    headers["foo"] = "bar";
+
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x02,
+      0x00, 0x00, 0x00, 0x21,
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x38, 0xea,
+      0xdf, 0xa2, 0x51, 0xb2,
+      0x62, 0x60, 0x62, 0x60,
+      0x4e, 0x4a, 0x2c, 0x62,
+      0x60, 0x4e, 0xcb, 0xcf,
+      0x87, 0x12, 0x40, 0x2e,
+      0x00, 0x00, 0x00, 0xff,
+      0xff
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateSynReply(
+        1, CONTROL_FLAG_NONE, true, &headers));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+}
+
+TEST_F(SpdyFramerTest, CreateRstStream) {
+  SpdyFramer framer;
+
+  {
+    const char kDescription[] = "RST_STREAM frame";
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x03,
+      0x00, 0x00, 0x00, 0x08,
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x01,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(1, PROTOCOL_ERROR));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] = "RST_STREAM frame with max stream ID";
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x03,
+      0x00, 0x00, 0x00, 0x08,
+      0x7f, 0xff, 0xff, 0xff,
+      0x00, 0x00, 0x00, 0x01,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(0x7FFFFFFF,
+                                                       PROTOCOL_ERROR));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] = "RST_STREAM frame with max status code";
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x03,
+      0x00, 0x00, 0x00, 0x08,
+      0x7f, 0xff, 0xff, 0xff,
+      0x00, 0x00, 0x00, 0x06,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateRstStream(0x7FFFFFFF,
+                                                       INTERNAL_ERROR));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+}
+
+TEST_F(SpdyFramerTest, CreateSettings) {
+  SpdyFramer framer;
+
+  {
+    const char kDescription[] = "Basic SETTINGS frame";
+
+    SpdySettings settings;
+    settings.push_back(SpdySetting(0x00000000, 0x00000000));
+    settings.push_back(SpdySetting(0xffffffff, 0x00000001));
+    settings.push_back(SpdySetting(0xff000001, 0x00000002));
+
+    // Duplicates allowed
+    settings.push_back(SpdySetting(0x01000002, 0x00000003));
+    settings.push_back(SpdySetting(0x01000002, 0x00000003));
+
+    settings.push_back(SpdySetting(0x01000003, 0x000000ff));
+    settings.push_back(SpdySetting(0x01000004, 0xff000001));
+    settings.push_back(SpdySetting(0x01000004, 0xffffffff));
+
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x04,
+      0x00, 0x00, 0x00, 0x44,
+      0x00, 0x00, 0x00, 0x08,
+      0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00,
+      0xff, 0xff, 0xff, 0xff,
+      0x00, 0x00, 0x00, 0x01,
+      0xff, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x02,
+      0x01, 0x00, 0x00, 0x02,
+      0x00, 0x00, 0x00, 0x03,
+      0x01, 0x00, 0x00, 0x02,
+      0x00, 0x00, 0x00, 0x03,
+      0x01, 0x00, 0x00, 0x03,
+      0x00, 0x00, 0x00, 0xff,
+      0x01, 0x00, 0x00, 0x04,
+      0xff, 0x00, 0x00, 0x01,
+      0x01, 0x00, 0x00, 0x04,
+      0xff, 0xff, 0xff, 0xff,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] = "Empty SETTINGS frame";
+
+    SpdySettings settings;
+
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x04,
+      0x00, 0x00, 0x00, 0x04,
+      0x00, 0x00, 0x00, 0x00,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateSettings(settings));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+}
+
+TEST_F(SpdyFramerTest, CreateNopFrame) {
+  SpdyFramer framer;
+
+  {
+    const char kDescription[] = "NOOP frame";
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x05,
+      0x00, 0x00, 0x00, 0x00,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateNopFrame());
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+}
+
+TEST_F(SpdyFramerTest, CreateGoAway) {
+  SpdyFramer framer;
+
+  {
+    const char kDescription[] = "GOAWAY frame";
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x07,
+      0x00, 0x00, 0x00, 0x04,
+      0x00, 0x00, 0x00, 0x00,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] = "GOAWAY frame with max stream ID";
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x07,
+      0x00, 0x00, 0x00, 0x04,
+      0x7f, 0xff, 0xff, 0xff,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateGoAway(0x7FFFFFFF));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+}
+
+TEST_F(SpdyFramerTest, CreateWindowUpdate) {
+  SpdyFramer framer;
+
+  {
+    const char kDescription[] = "WINDOW_UPDATE frame";
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x09,
+      0x00, 0x00, 0x00, 0x08,
+      0x00, 0x00, 0x00, 0x01,
+      0x00, 0x00, 0x00, 0x01,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 1));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] = "WINDOW_UPDATE frame with max stream ID";
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x09,
+      0x00, 0x00, 0x00, 0x08,
+      0x7f, 0xff, 0xff, 0xff,
+      0x00, 0x00, 0x00, 0x01,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(0x7FFFFFFF, 1));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+
+  {
+    const char kDescription[] = "WINDOW_UPDATE frame with max window delta";
+    const unsigned char kFrameData[] = {
+      0x80, 0x01, 0x00, 0x09,
+      0x00, 0x00, 0x00, 0x08,
+      0x00, 0x00, 0x00, 0x01,
+      0x7f, 0xff, 0xff, 0xff,
+    };
+    scoped_ptr<SpdyFrame> frame(framer.CreateWindowUpdate(1, 0x7FFFFFFF));
+    CompareFrame(kDescription, *frame, kFrameData, arraysize(kFrameData));
+  }
+}
+
+}  // namespace
diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc
new file mode 100644
index 0000000..581cb27
--- /dev/null
+++ b/net/spdy/spdy_http_stream.cc
@@ -0,0 +1,462 @@
+// Copyright (c) 2010 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/spdy/spdy_http_stream.h"
+
+#include <algorithm>
+#include <list>
+#include <string>
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
+#include "net/spdy/spdy_session.h"
+
+namespace {
+
+// Convert a SpdyHeaderBlock into an HttpResponseInfo.
+// |headers| input parameter with the SpdyHeaderBlock.
+// |info| output parameter for the HttpResponseInfo.
+// Returns true if successfully converted.  False if there was a failure
+// or if the SpdyHeaderBlock was invalid.
+bool SpdyHeadersToHttpResponse(const spdy::SpdyHeaderBlock& headers,
+                               net::HttpResponseInfo* response) {
+  std::string version;
+  std::string status;
+
+  // The "status" and "version" headers are required.
+  spdy::SpdyHeaderBlock::const_iterator it;
+  it = headers.find("status");
+  if (it == headers.end()) {
+    LOG(ERROR) << "SpdyHeaderBlock without status header.";
+    return false;
+  }
+  status = it->second;
+
+  // Grab the version.  If not provided by the server,
+  it = headers.find("version");
+  if (it == headers.end()) {
+    LOG(ERROR) << "SpdyHeaderBlock without version header.";
+    return false;
+  }
+  version = it->second;
+
+  response->response_time = base::Time::Now();
+
+  std::string raw_headers(version);
+  raw_headers.push_back(' ');
+  raw_headers.append(status);
+  raw_headers.push_back('\0');
+  for (it = headers.begin(); it != headers.end(); ++it) {
+    // For each value, if the server sends a NUL-separated
+    // list of values, we separate that back out into
+    // individual headers for each value in the list.
+    // e.g.
+    //    Set-Cookie "foo\0bar"
+    // becomes
+    //    Set-Cookie: foo\0
+    //    Set-Cookie: bar\0
+    std::string value = it->second;
+    size_t start = 0;
+    size_t end = 0;
+    do {
+      end = value.find('\0', start);
+      std::string tval;
+      if (end != value.npos)
+        tval = value.substr(start, (end - start));
+      else
+        tval = value.substr(start);
+      raw_headers.append(it->first);
+      raw_headers.push_back(':');
+      raw_headers.append(tval);
+      raw_headers.push_back('\0');
+      start = end + 1;
+    } while (end != value.npos);
+  }
+
+  response->headers = new net::HttpResponseHeaders(raw_headers);
+  response->was_fetched_via_spdy = true;
+  return true;
+}
+
+// Create a SpdyHeaderBlock for a Spdy SYN_STREAM Frame from
+// a HttpRequestInfo block.
+void CreateSpdyHeadersFromHttpRequest(
+    const net::HttpRequestInfo& info, spdy::SpdyHeaderBlock* headers) {
+  // TODO(willchan): It's not really necessary to convert from
+  // HttpRequestHeaders to spdy::SpdyHeaderBlock.
+
+  static const char kHttpProtocolVersion[] = "HTTP/1.1";
+
+  net::HttpRequestHeaders::Iterator it(info.extra_headers);
+
+  while (it.GetNext()) {
+    std::string name = StringToLowerASCII(it.name());
+    if (headers->find(name) == headers->end()) {
+      (*headers)[name] = it.value();
+    } else {
+      std::string new_value = (*headers)[name];
+      new_value.append(1, '\0');  // +=() doesn't append 0's
+      new_value += it.value();
+      (*headers)[name] = new_value;
+    }
+  }
+
+  // TODO(mbelshe): Add Proxy headers here. (See http_network_transaction.cc)
+  // TODO(mbelshe): Add authentication headers here.
+
+  (*headers)["method"] = info.method;
+  (*headers)["url"] = info.url.spec();
+  (*headers)["version"] = kHttpProtocolVersion;
+  if (!info.referrer.is_empty())
+    (*headers)["referer"] = info.referrer.spec();
+
+  // Honor load flags that impact proxy caches.
+  if (info.load_flags & net::LOAD_BYPASS_CACHE) {
+    (*headers)["pragma"] = "no-cache";
+    (*headers)["cache-control"] = "no-cache";
+  } else if (info.load_flags & net::LOAD_VALIDATE_CACHE) {
+    (*headers)["cache-control"] = "max-age=0";
+  }
+}
+
+}  // anonymous namespace
+
+namespace net {
+
+SpdyHttpStream::SpdyHttpStream()
+    : ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_factory_(this)),
+      stream_(NULL),
+      spdy_session_(NULL),
+      response_info_(NULL),
+      download_finished_(false),
+      user_callback_(NULL),
+      user_buffer_len_(0),
+      buffered_read_callback_pending_(false),
+      more_read_data_pending_(false) { }
+
+SpdyHttpStream::~SpdyHttpStream() {
+  if (stream_)
+    stream_->DetachDelegate();
+}
+
+int SpdyHttpStream::InitializeStream(
+    SpdySession* spdy_session,
+    const HttpRequestInfo& request_info,
+    const BoundNetLog& stream_net_log,
+    CompletionCallback* callback) {
+  spdy_session_ = spdy_session;
+  request_info_ = request_info;
+  if (request_info_.method == "GET") {
+    int error = spdy_session_->GetPushStream(request_info.url, &stream_,
+                                             stream_net_log);
+    if (error != OK)
+      return error;
+  }
+
+  if (stream_.get())
+    return OK;
+  else
+    return spdy_session_->CreateStream(request_info_.url,
+                                       request_info_.priority, &stream_,
+                                       stream_net_log, callback);
+}
+
+void SpdyHttpStream::InitializeRequest(
+    base::Time request_time,
+    UploadDataStream* upload_data) {
+  CHECK(stream_.get());
+  stream_->SetDelegate(this);
+  linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
+  CreateSpdyHeadersFromHttpRequest(request_info_, headers.get());
+  stream_->set_spdy_headers(headers);
+
+  stream_->SetRequestTime(request_time);
+  // This should only get called in the case of a request occuring
+  // during server push that has already begun but hasn't finished,
+  // so we set the response's request time to be the actual one
+  if (response_info_)
+    response_info_->request_time = request_time;
+
+  CHECK(!request_body_stream_.get());
+  if (upload_data) {
+    if (upload_data->size())
+      request_body_stream_.reset(upload_data);
+    else
+      delete upload_data;
+  }
+}
+
+const HttpResponseInfo* SpdyHttpStream::GetResponseInfo() const {
+  return response_info_;
+}
+
+uint64 SpdyHttpStream::GetUploadProgress() const {
+  if (!request_body_stream_.get())
+    return 0;
+
+  return request_body_stream_->position();
+}
+
+int SpdyHttpStream::ReadResponseHeaders(CompletionCallback* callback) {
+  DCHECK(stream_->is_idle());
+  // Note: The SpdyStream may have already received the response headers, so
+  //       this call may complete synchronously.
+  CHECK(callback);
+
+  if (stream_->response_complete())
+    return stream_->response_status();
+
+  int result = stream_->DoReadResponseHeaders();
+  if (result == ERR_IO_PENDING) {
+    CHECK(!user_callback_);
+    user_callback_ = callback;
+  }
+  return result;
+}
+
+int SpdyHttpStream::ReadResponseBody(
+    IOBuffer* buf, int buf_len, CompletionCallback* callback) {
+  CHECK(buf);
+  CHECK(buf_len);
+  CHECK(callback);
+  DCHECK(stream_->is_idle());
+
+  // If we have data buffered, complete the IO immediately.
+  if (!response_body_.empty()) {
+    int bytes_read = 0;
+    while (!response_body_.empty() && buf_len > 0) {
+      scoped_refptr<IOBufferWithSize> data = response_body_.front();
+      const int bytes_to_copy = std::min(buf_len, data->size());
+      memcpy(&(buf->data()[bytes_read]), data->data(), bytes_to_copy);
+      buf_len -= bytes_to_copy;
+      if (bytes_to_copy == data->size()) {
+        response_body_.pop_front();
+      } else {
+        const int bytes_remaining = data->size() - bytes_to_copy;
+        IOBufferWithSize* new_buffer = new IOBufferWithSize(bytes_remaining);
+        memcpy(new_buffer->data(), &(data->data()[bytes_to_copy]),
+               bytes_remaining);
+        response_body_.pop_front();
+        response_body_.push_front(new_buffer);
+      }
+      bytes_read += bytes_to_copy;
+    }
+    return bytes_read;
+  } else if (stream_->response_complete()) {
+    return stream_->response_status();
+  }
+
+  CHECK(!user_callback_);
+  CHECK(!user_buffer_);
+  CHECK_EQ(0, user_buffer_len_);
+
+  user_callback_ = callback;
+  user_buffer_ = buf;
+  user_buffer_len_ = buf_len;
+  return ERR_IO_PENDING;
+}
+
+int SpdyHttpStream::SendRequest(HttpResponseInfo* response,
+                                CompletionCallback* callback) {
+  CHECK(callback);
+  CHECK(!stream_->cancelled());
+  CHECK(response);
+
+  if (stream_->response_complete()) {
+    if (stream_->response_status() == OK)
+      return ERR_FAILED;
+    else
+      return stream_->response_status();
+  }
+
+  // SendRequest can be called in two cases.
+  //
+  // a) A client initiated request. In this case, |response_info_| should be
+  //    NULL to start with.
+  // b) A client request which matches a response that the server has already
+  //    pushed.  In this case, the value of |*push_response_info_| is copied
+  //    over to the new response object |*response|.  |push_response_info_| is
+  //    deleted, and |response_info_| is reset |response|.
+  if (push_response_info_.get()) {
+    *response = *push_response_info_;
+    push_response_info_.reset();
+    response_info_ = NULL;
+  }
+
+  DCHECK_EQ(static_cast<HttpResponseInfo*>(NULL), response_info_);
+  response_info_ = response;
+
+  bool has_upload_data = request_body_stream_.get() != NULL;
+  int result = stream_->DoSendRequest(has_upload_data);
+  if (result == ERR_IO_PENDING) {
+    CHECK(!user_callback_);
+    user_callback_ = callback;
+  }
+  return result;
+}
+
+void SpdyHttpStream::Cancel() {
+  if (spdy_session_)
+    spdy_session_->CancelPendingCreateStreams(&stream_);
+  user_callback_ = NULL;
+  if (stream_)
+    stream_->Cancel();
+}
+
+bool SpdyHttpStream::OnSendHeadersComplete(int status) {
+  return request_body_stream_.get() == NULL;
+}
+
+int SpdyHttpStream::OnSendBody() {
+  CHECK(request_body_stream_.get());
+  int buf_len = static_cast<int>(request_body_stream_->buf_len());
+  if (!buf_len)
+    return OK;
+  return stream_->WriteStreamData(request_body_stream_->buf(), buf_len,
+                                  spdy::DATA_FLAG_FIN);
+}
+
+bool SpdyHttpStream::OnSendBodyComplete(int status) {
+  CHECK(request_body_stream_.get());
+  request_body_stream_->DidConsume(status);
+  return request_body_stream_->eof();
+}
+
+int SpdyHttpStream::OnResponseReceived(const spdy::SpdyHeaderBlock& response,
+                                       base::Time response_time,
+                                       int status) {
+  if (!response_info_) {
+    DCHECK(stream_->pushed());
+    push_response_info_.reset(new HttpResponseInfo);
+    response_info_ = push_response_info_.get();
+  }
+
+  if (!SpdyHeadersToHttpResponse(response, response_info_)) {
+    status = ERR_INVALID_RESPONSE;
+  } else {
+    stream_->GetSSLInfo(&response_info_->ssl_info,
+                        &response_info_->was_npn_negotiated);
+    response_info_->request_time = stream_->GetRequestTime();
+    response_info_->vary_data.Init(request_info_, *response_info_->headers);
+    // TODO(ahendrickson): This is recorded after the entire SYN_STREAM control
+    // frame has been received and processed.  Move to framer?
+    response_info_->response_time = response_time;
+  }
+
+  if (user_callback_)
+    DoCallback(status);
+  return status;
+}
+
+void SpdyHttpStream::OnDataReceived(const char* data, int length) {
+  // Note that data may be received for a SpdyStream prior to the user calling
+  // ReadResponseBody(), therefore user_buffer_ may be NULL.  This may often
+  // happen for server initiated streams.
+  if (length > 0 && !stream_->response_complete()) {
+    // Save the received data.
+    IOBufferWithSize* io_buffer = new IOBufferWithSize(length);
+    memcpy(io_buffer->data(), data, length);
+    response_body_.push_back(io_buffer);
+
+    if (user_buffer_) {
+      // Handing small chunks of data to the caller creates measurable overhead.
+      // We buffer data in short time-spans and send a single read notification.
+      ScheduleBufferedReadCallback();
+    }
+  }
+}
+
+void SpdyHttpStream::OnDataSent(int length) {
+  // For HTTP streams, no data is sent from the client while in the OPEN state,
+  // so it is never called.
+  NOTREACHED();
+}
+
+void SpdyHttpStream::OnClose(int status) {
+  bool invoked_callback = false;
+  if (status == net::OK) {
+    // We need to complete any pending buffered read now.
+    invoked_callback = DoBufferedReadCallback();
+  }
+  if (!invoked_callback && user_callback_)
+    DoCallback(status);
+}
+
+void SpdyHttpStream::ScheduleBufferedReadCallback() {
+  // If there is already a scheduled DoBufferedReadCallback, don't issue
+  // another one.  Mark that we have received more data and return.
+  if (buffered_read_callback_pending_) {
+    more_read_data_pending_ = true;
+    return;
+  }
+
+  more_read_data_pending_ = false;
+  buffered_read_callback_pending_ = true;
+  const int kBufferTimeMs = 1;
+  MessageLoop::current()->PostDelayedTask(FROM_HERE, read_callback_factory_.
+      NewRunnableMethod(&SpdyHttpStream::DoBufferedReadCallback),
+      kBufferTimeMs);
+}
+
+// Checks to see if we should wait for more buffered data before notifying
+// the caller.  Returns true if we should wait, false otherwise.
+bool SpdyHttpStream::ShouldWaitForMoreBufferedData() const {
+  // If the response is complete, there is no point in waiting.
+  if (stream_->response_complete())
+    return false;
+
+  int bytes_buffered = 0;
+  std::list<scoped_refptr<IOBufferWithSize> >::const_iterator it;
+  for (it = response_body_.begin();
+       it != response_body_.end() && bytes_buffered < user_buffer_len_;
+       ++it)
+    bytes_buffered += (*it)->size();
+
+  return bytes_buffered < user_buffer_len_;
+}
+
+bool SpdyHttpStream::DoBufferedReadCallback() {
+  read_callback_factory_.RevokeAll();
+  buffered_read_callback_pending_ = false;
+
+  // If the transaction is cancelled or errored out, we don't need to complete
+  // the read.
+  if (!stream_ || stream_->response_status() != OK || stream_->cancelled())
+    return false;
+
+  // When more_read_data_pending_ is true, it means that more data has
+  // arrived since we started waiting.  Wait a little longer and continue
+  // to buffer.
+  if (more_read_data_pending_ && ShouldWaitForMoreBufferedData()) {
+    ScheduleBufferedReadCallback();
+    return false;
+  }
+
+  int rv = 0;
+  if (user_buffer_) {
+    rv = ReadResponseBody(user_buffer_, user_buffer_len_, user_callback_);
+    CHECK_NE(rv, ERR_IO_PENDING);
+    user_buffer_ = NULL;
+    user_buffer_len_ = 0;
+    DoCallback(rv);
+    return true;
+  }
+  return false;
+}
+
+void SpdyHttpStream::DoCallback(int rv) {
+  CHECK_NE(rv, ERR_IO_PENDING);
+  CHECK(user_callback_);
+
+  // Since Run may result in being called back, clear user_callback_ in advance.
+  CompletionCallback* c = user_callback_;
+  user_callback_ = NULL;
+  c->Run(rv);
+}
+
+}  // namespace net
diff --git a/net/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h
new file mode 100644
index 0000000..3926501
--- /dev/null
+++ b/net/spdy/spdy_http_stream.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2010 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_SPDY_SPDY_HTTP_STREAM_H_
+#define NET_SPDY_SPDY_HTTP_STREAM_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/task.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_log.h"
+#include "net/http/http_request_info.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_stream.h"
+
+namespace net {
+
+class HttpResponseInfo;
+class IOBuffer;
+class SpdySession;
+class UploadData;
+class UploadDataStream;
+
+// The SpdyHttpStream is a HTTP-specific type of stream known to a SpdySession.
+class SpdyHttpStream : public SpdyStream::Delegate {
+ public:
+  // SpdyHttpStream constructor
+  SpdyHttpStream();
+  virtual ~SpdyHttpStream();
+
+  SpdyStream* stream() { return stream_.get(); }
+
+  // Initialize stream.  Must be called before calling InitializeRequest().
+  int InitializeStream(SpdySession* spdy_session,
+                       const HttpRequestInfo& request_info,
+                       const BoundNetLog& stream_net_log,
+                       CompletionCallback* callback);
+
+  // Initialize request.  Must be called before calling SendRequest().
+  // SpdyHttpStream takes ownership of |upload_data|. |upload_data| may be NULL.
+  void InitializeRequest(base::Time request_time,
+                         UploadDataStream* upload_data);
+
+  const HttpResponseInfo* GetResponseInfo() const;
+
+  // ===================================================
+  // Interface for [Http|Spdy]NetworkTransaction to use.
+
+  // Sends the request.
+  // |callback| is used when this completes asynchronously.
+  // The actual SYN_STREAM packet will be sent if the stream is non-pushed.
+  int SendRequest(HttpResponseInfo* response,
+                  CompletionCallback* callback);
+
+  // Reads the response headers.  Returns a net error code.
+  int ReadResponseHeaders(CompletionCallback* callback);
+
+  // Reads the response body.  Returns a net error code or the number of bytes
+  // read.
+  int ReadResponseBody(
+      IOBuffer* buf, int buf_len, CompletionCallback* callback);
+
+  // Cancels any callbacks from being invoked and deletes the stream.
+  void Cancel();
+
+  // Returns the number of bytes uploaded.
+  uint64 GetUploadProgress() const;
+
+  // ===================================================
+  // SpdyStream::Delegate.
+
+  virtual bool OnSendHeadersComplete(int status);
+  virtual int OnSendBody();
+  virtual bool OnSendBodyComplete(int status);
+
+  // Called by the SpdySession when a response (e.g. a SYN_REPLY) has been
+  // received for this stream.
+  // SpdyHttpSession calls back |callback| set by SendRequest or
+  // ReadResponseHeaders.
+  virtual int OnResponseReceived(const spdy::SpdyHeaderBlock& response,
+                                 base::Time response_time,
+                                 int status);
+
+  // Called by the SpdySession when response data has been received for this
+  // stream.  This callback may be called multiple times as data arrives
+  // from the network, and will never be called prior to OnResponseReceived.
+  // SpdyHttpSession schedule to call back |callback| set by ReadResponseBody.
+  virtual void OnDataReceived(const char* buffer, int bytes);
+
+  // For HTTP streams, no data is sent from the client while in the OPEN state,
+  // so OnDataSent is never called.
+  virtual void OnDataSent(int length);
+
+  // Called by the SpdySession when the request is finished.  This callback
+  // will always be called at the end of the request and signals to the
+  // stream that the stream has no more network events.  No further callbacks
+  // to the stream will be made after this call.
+  // SpdyHttpSession call back |callback| set by SendRequest,
+  // ReadResponseHeaders or ReadResponseBody.
+  virtual void OnClose(int status);
+
+ private:
+  // Call the user callback.
+  void DoCallback(int rv);
+
+  void ScheduleBufferedReadCallback();
+
+  // Returns true if the callback is invoked.
+  bool DoBufferedReadCallback();
+  bool ShouldWaitForMoreBufferedData() const;
+
+  ScopedRunnableMethodFactory<SpdyHttpStream> read_callback_factory_;
+  scoped_refptr<SpdyStream> stream_;
+  scoped_refptr<SpdySession> spdy_session_;
+
+  // The request to send.
+  HttpRequestInfo request_info_;
+
+  scoped_ptr<UploadDataStream> request_body_stream_;
+
+  // |response_info_| is the HTTP response data object which is filled in
+  // when a SYN_REPLY comes in for the stream.
+  // It is not owned by this stream object, or point to |push_response_info_|.
+  HttpResponseInfo* response_info_;
+
+  scoped_ptr<HttpResponseInfo> push_response_info_;
+
+  bool download_finished_;
+
+  // We buffer the response body as it arrives asynchronously from the stream.
+  // TODO(mbelshe):  is this infinite buffering?
+  std::list<scoped_refptr<IOBufferWithSize> > response_body_;
+
+  CompletionCallback* user_callback_;
+
+  // User provided buffer for the ReadResponseBody() response.
+  scoped_refptr<IOBuffer> user_buffer_;
+  int user_buffer_len_;
+
+  // Is there a scheduled read callback pending.
+  bool buffered_read_callback_pending_;
+  // Has more data been received from the network during the wait for the
+  // scheduled read callback.
+  bool more_read_data_pending_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpdyHttpStream);
+};
+
+}  // namespace net
+
+#endif  // NET_SPDY_SPDY_HTTP_STREAM_H_
diff --git a/net/spdy/spdy_http_stream_unittest.cc b/net/spdy/spdy_http_stream_unittest.cc
new file mode 100644
index 0000000..65a8068
--- /dev/null
+++ b/net/spdy/spdy_http_stream_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2010 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/spdy/spdy_http_stream.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+class SpdyHttpStreamTest : public testing::Test {
+ protected:
+  SpdyHttpStreamTest(){}
+
+  void EnableCompression(bool enabled) {
+    spdy::SpdyFramer::set_enable_compression_default(enabled);
+  }
+
+  virtual void TearDown() {
+    MessageLoop::current()->RunAllPending();
+  }
+};
+
+TEST_F(SpdyHttpStreamTest, SendRequest) {
+  EnableCompression(false);
+  SpdySession::SetSSLMode(false);
+
+  SpdySessionDependencies session_deps;
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = {
+    CreateMockWrite(*req.get(), 1),
+  };
+  MockRead reads[] = {
+    MockRead(false, 0, 2)  // EOF
+  };
+  scoped_refptr<OrderedSocketData> data(
+      new OrderedSocketData(reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  session_deps.socket_factory.AddSocketDataProvider(data.get());
+
+  scoped_refptr<HttpNetworkSession> http_session(
+      SpdySessionDependencies::SpdyCreateSession(&session_deps));
+  scoped_refptr<SpdySessionPool> spdy_session_pool(
+      http_session->spdy_session_pool());
+  HostPortPair host_port_pair("www.google.com", 80);
+  scoped_refptr<SpdySession> session =
+      spdy_session_pool->Get(
+          host_port_pair, http_session.get(), BoundNetLog());
+  scoped_refptr<TCPSocketParams> tcp_params =
+      new TCPSocketParams(host_port_pair.host, host_port_pair.port,
+                          MEDIUM, GURL(), false);
+  int rv = session->Connect(host_port_pair.host, tcp_params, MEDIUM);
+  ASSERT_EQ(OK, rv);
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("http://www.google.com/");
+  TestCompletionCallback callback;
+  HttpResponseInfo response;
+  scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream());
+  ASSERT_EQ(
+      OK,
+      http_stream->InitializeStream(session, request, BoundNetLog(), NULL));
+  http_stream->InitializeRequest(base::Time::Now(), NULL);
+  EXPECT_EQ(ERR_IO_PENDING,
+            http_stream->SendRequest(&response, &callback));
+  MessageLoop::current()->RunAllPending();
+  EXPECT_TRUE(spdy_session_pool->HasSession(host_port_pair));
+  spdy_session_pool->Remove(session);
+}
+
+// TODO(willchan): Write a longer test for SpdyStream that exercises all
+// methods.
+
+}  // namespace net
diff --git a/net/spdy/spdy_io_buffer.cc b/net/spdy/spdy_io_buffer.cc
new file mode 100644
index 0000000..f7120c8
--- /dev/null
+++ b/net/spdy/spdy_io_buffer.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 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/spdy/spdy_io_buffer.h"
+#include "net/spdy/spdy_stream.h"
+
+namespace net {
+
+// static
+uint64 SpdyIOBuffer::order_ = 0;
+
+SpdyIOBuffer::SpdyIOBuffer(
+    IOBuffer* buffer, int size, int priority, SpdyStream* stream)
+  : buffer_(new DrainableIOBuffer(buffer, size)),
+    priority_(priority),
+    position_(++order_),
+    stream_(stream) {}
+
+SpdyIOBuffer::SpdyIOBuffer() : priority_(0), position_(0), stream_(NULL) {}
+
+SpdyIOBuffer::~SpdyIOBuffer() {}
+
+void SpdyIOBuffer::release() {
+  buffer_ = NULL;
+  stream_ = NULL;
+}
+
+}  // namespace net
diff --git a/net/spdy/spdy_io_buffer.h b/net/spdy/spdy_io_buffer.h
new file mode 100644
index 0000000..e34c7fe
--- /dev/null
+++ b/net/spdy/spdy_io_buffer.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2009 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_SPDY_SPDY_IO_BUFFER_H_
+#define NET_SPDY_SPDY_IO_BUFFER_H_
+
+#include "base/ref_counted.h"
+#include "net/base/io_buffer.h"
+
+namespace net {
+
+class SpdyStream;
+
+// A class for managing SPDY IO buffers.  These buffers need to be prioritized
+// so that the SpdySession sends them in the right order.  Further, they need
+// to track the SpdyStream which they are associated with so that incremental
+// completion of the IO can notify the appropriate stream of completion.
+class SpdyIOBuffer {
+ public:
+  // Constructor
+  // |buffer| is the actual data buffer.
+  // |size| is the size of the data buffer.
+  // |priority| is the priority of this buffer.  Lower numbers are higher
+  //            priority.
+  // |stream| is a pointer to the stream which is managing this buffer.
+  SpdyIOBuffer(IOBuffer* buffer, int size, int priority, SpdyStream* stream);
+  SpdyIOBuffer();
+  ~SpdyIOBuffer();
+
+  // Accessors.
+  DrainableIOBuffer* buffer() const { return buffer_; }
+  size_t size() const { return buffer_->size(); }
+  void release();
+  int priority() const { return priority_; }
+  const scoped_refptr<SpdyStream>& stream() const { return stream_; }
+
+  // Comparison operator to support sorting.
+  bool operator<(const SpdyIOBuffer& other) const {
+    if (priority_ != other.priority_)
+      return priority_ > other.priority_;
+    return position_ > other.position_;
+  }
+
+ private:
+  scoped_refptr<DrainableIOBuffer> buffer_;
+  int priority_;
+  uint64 position_;
+  scoped_refptr<SpdyStream> stream_;
+  static uint64 order_;  // Maintains a FIFO order for equal priorities.
+};
+
+}  // namespace net
+
+#endif  // NET_SPDY_SPDY_IO_BUFFER_H_
diff --git a/net/spdy/spdy_network_transaction.cc b/net/spdy/spdy_network_transaction.cc
new file mode 100644
index 0000000..7814553
--- /dev/null
+++ b/net/spdy/spdy_network_transaction.cc
@@ -0,0 +1,335 @@
+// Copyright (c) 2009 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/spdy/spdy_network_transaction.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/stats_counters.h"
+#include "net/base/host_resolver.h"
+#include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/base/upload_data_stream.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
+#include "net/socket/tcp_client_socket_pool.h"
+#include "net/spdy/spdy_http_stream.h"
+
+using base::Time;
+
+namespace net {
+
+//-----------------------------------------------------------------------------
+
+SpdyNetworkTransaction::SpdyNetworkTransaction(HttpNetworkSession* session)
+    : ALLOW_THIS_IN_INITIALIZER_LIST(
+        io_callback_(this, &SpdyNetworkTransaction::OnIOComplete)),
+      user_callback_(NULL),
+      user_buffer_len_(0),
+      session_(session),
+      request_(NULL),
+      next_state_(STATE_NONE),
+      stream_(NULL) {
+}
+
+SpdyNetworkTransaction::~SpdyNetworkTransaction() {
+  LOG(INFO) << "SpdyNetworkTransaction dead. " << this;
+  if (stream_.get())
+    stream_->Cancel();
+}
+
+int SpdyNetworkTransaction::Start(const HttpRequestInfo* request_info,
+                                  CompletionCallback* callback,
+                                  const BoundNetLog& net_log) {
+  CHECK(request_info);
+  CHECK(callback);
+
+  SIMPLE_STATS_COUNTER("SpdyNetworkTransaction.Count");
+
+  net_log_ = net_log;
+  request_ = request_info;
+  start_time_ = base::TimeTicks::Now();
+
+  next_state_ = STATE_INIT_CONNECTION;
+  int rv = DoLoop(OK);
+  if (rv == ERR_IO_PENDING)
+    user_callback_ = callback;
+  return rv;
+}
+
+int SpdyNetworkTransaction::RestartIgnoringLastError(
+    CompletionCallback* callback) {
+  // TODO(mbelshe): implement me.
+  NOTIMPLEMENTED();
+  return ERR_NOT_IMPLEMENTED;
+}
+
+int SpdyNetworkTransaction::RestartWithCertificate(
+    X509Certificate* client_cert, CompletionCallback* callback) {
+  // TODO(mbelshe): implement me.
+  NOTIMPLEMENTED();
+  return ERR_NOT_IMPLEMENTED;
+}
+
+int SpdyNetworkTransaction::RestartWithAuth(
+    const std::wstring& username,
+    const std::wstring& password,
+    CompletionCallback* callback) {
+  // TODO(mbelshe): implement me.
+  NOTIMPLEMENTED();
+  return 0;
+}
+
+int SpdyNetworkTransaction::Read(IOBuffer* buf, int buf_len,
+                                 CompletionCallback* callback) {
+  DCHECK(buf);
+  DCHECK_GT(buf_len, 0);
+  DCHECK(callback);
+
+  user_buffer_ = buf;
+  user_buffer_len_ = buf_len;
+
+  next_state_ = STATE_READ_BODY;
+  int rv = DoLoop(OK);
+  if (rv == ERR_IO_PENDING)
+    user_callback_ = callback;
+  return rv;
+}
+
+const HttpResponseInfo* SpdyNetworkTransaction::GetResponseInfo() const {
+  return (response_.headers || response_.ssl_info.cert) ? &response_ : NULL;
+}
+
+LoadState SpdyNetworkTransaction::GetLoadState() const {
+  switch (next_state_) {
+    case STATE_INIT_CONNECTION_COMPLETE:
+      if (spdy_.get())
+        return spdy_->GetLoadState();
+      return LOAD_STATE_CONNECTING;
+    case STATE_GET_STREAM_COMPLETE:
+    case STATE_SEND_REQUEST_COMPLETE:
+      return LOAD_STATE_SENDING_REQUEST;
+    case STATE_READ_HEADERS_COMPLETE:
+      return LOAD_STATE_WAITING_FOR_RESPONSE;
+    case STATE_READ_BODY_COMPLETE:
+      return LOAD_STATE_READING_RESPONSE;
+    default:
+      return LOAD_STATE_IDLE;
+  }
+}
+
+uint64 SpdyNetworkTransaction::GetUploadProgress() const {
+  if (!stream_.get())
+    return 0;
+
+  return stream_->GetUploadProgress();
+}
+
+void SpdyNetworkTransaction::DoCallback(int rv) {
+  CHECK_NE(rv, ERR_IO_PENDING);
+  CHECK(user_callback_);
+
+  // Since Run may result in Read being called, clear user_callback_ up front.
+  CompletionCallback* c = user_callback_;
+  user_callback_ = NULL;
+  c->Run(rv);
+}
+
+void SpdyNetworkTransaction::OnIOComplete(int result) {
+  int rv = DoLoop(result);
+  if (rv != ERR_IO_PENDING)
+    DoCallback(rv);
+}
+
+int SpdyNetworkTransaction::DoLoop(int result) {
+  DCHECK(next_state_ != STATE_NONE);
+  DCHECK(request_);
+
+  if (!request_)
+    return 0;
+
+  int rv = result;
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_INIT_CONNECTION:
+        DCHECK_EQ(OK, rv);
+        net_log_.BeginEvent(NetLog::TYPE_SPDY_TRANSACTION_INIT_CONNECTION,
+                            NULL);
+        rv = DoInitConnection();
+        break;
+      case STATE_INIT_CONNECTION_COMPLETE:
+        net_log_.EndEvent(NetLog::TYPE_SPDY_TRANSACTION_INIT_CONNECTION, NULL);
+        rv = DoInitConnectionComplete(rv);
+        break;
+      case STATE_GET_STREAM:
+        DCHECK_EQ(OK, rv);
+        rv = DoGetStream();
+        break;
+      case STATE_GET_STREAM_COMPLETE:
+        rv = DoGetStreamComplete(rv);
+      case STATE_SEND_REQUEST:
+        DCHECK_EQ(OK, rv);
+        net_log_.BeginEvent(NetLog::TYPE_SPDY_TRANSACTION_SEND_REQUEST, NULL);
+        rv = DoSendRequest();
+        break;
+      case STATE_SEND_REQUEST_COMPLETE:
+        net_log_.EndEvent(NetLog::TYPE_SPDY_TRANSACTION_SEND_REQUEST, NULL);
+        rv = DoSendRequestComplete(rv);
+        break;
+      case STATE_READ_HEADERS:
+        DCHECK_EQ(OK, rv);
+        net_log_.BeginEvent(NetLog::TYPE_SPDY_TRANSACTION_READ_HEADERS, NULL);
+        rv = DoReadHeaders();
+        break;
+      case STATE_READ_HEADERS_COMPLETE:
+        net_log_.EndEvent(NetLog::TYPE_SPDY_TRANSACTION_READ_HEADERS, NULL);
+        rv = DoReadHeadersComplete(rv);
+        break;
+      case STATE_READ_BODY:
+        DCHECK_EQ(OK, rv);
+        net_log_.BeginEvent(NetLog::TYPE_SPDY_TRANSACTION_READ_BODY, NULL);
+        rv = DoReadBody();
+        break;
+      case STATE_READ_BODY_COMPLETE:
+        net_log_.EndEvent(NetLog::TYPE_SPDY_TRANSACTION_READ_BODY, NULL);
+        rv = DoReadBodyComplete(rv);
+        break;
+      case STATE_NONE:
+        rv = ERR_FAILED;
+        break;
+      default:
+        NOTREACHED() << "bad state";
+        rv = ERR_FAILED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+
+  return rv;
+}
+
+int SpdyNetworkTransaction::DoInitConnection() {
+  next_state_ = STATE_INIT_CONNECTION_COMPLETE;
+
+  std::string host = request_->url.HostNoBrackets();
+  int port = request_->url.EffectiveIntPort();
+
+  // Use the fixed testing ports if they've been provided.  This is useful for
+  // debugging.
+  if (SpdySession::SSLMode()) {
+    if (session_->fixed_https_port() != 0)
+      port = session_->fixed_https_port();
+  } else if (session_->fixed_http_port() != 0) {
+    port = session_->fixed_http_port();
+  }
+
+  std::string connection_group = "spdy.";
+  connection_group.append(host);
+
+  HostPortPair host_port_pair(host, port);
+  scoped_refptr<TCPSocketParams> tcp_params =
+      new TCPSocketParams(host_port_pair, request_->priority,
+                          request_->referrer, false);
+
+  spdy_ = session_->spdy_session_pool()->Get(
+      host_port_pair, session_, net_log_);
+  DCHECK(spdy_);
+
+  return spdy_->Connect(
+      connection_group, tcp_params, request_->priority);
+}
+
+int SpdyNetworkTransaction::DoInitConnectionComplete(int result) {
+  if (result < 0)
+    return result;
+
+  next_state_ = STATE_GET_STREAM;
+  return OK;
+}
+
+int SpdyNetworkTransaction::DoGetStream() {
+  next_state_ = STATE_GET_STREAM_COMPLETE;
+
+  // It is possible that the spdy session was shut down while it was
+  // asynchronously waiting to connect.
+  if (spdy_->IsClosed())
+    return ERR_CONNECTION_CLOSED;
+
+  CHECK(!stream_.get());
+
+  stream_.reset(new SpdyHttpStream());
+  return stream_->InitializeStream(spdy_, *request_,
+                                   net_log_, &io_callback_);
+}
+
+int SpdyNetworkTransaction::DoGetStreamComplete(int result) {
+  if (result < 0) {
+    return result;
+  }
+
+  next_state_ = STATE_SEND_REQUEST;
+  return OK;
+}
+
+int SpdyNetworkTransaction::DoSendRequest() {
+  next_state_ = STATE_SEND_REQUEST_COMPLETE;
+
+  UploadDataStream* upload_data_stream = NULL;
+  if (request_->upload_data) {
+    int error_code;
+    upload_data_stream = UploadDataStream::Create(request_->upload_data,
+                                                  &error_code);
+    if (!upload_data_stream)
+      return error_code;
+  }
+  stream_->InitializeRequest(base::Time::Now(), upload_data_stream);
+  spdy_ = NULL;
+
+  return stream_->SendRequest(&response_, &io_callback_);
+}
+
+int SpdyNetworkTransaction::DoSendRequestComplete(int result) {
+  if (result < 0) {
+    stream_.reset();
+    return result;
+  }
+
+  next_state_ = STATE_READ_HEADERS;
+  return OK;
+}
+
+int SpdyNetworkTransaction::DoReadHeaders() {
+  next_state_ = STATE_READ_HEADERS_COMPLETE;
+  return stream_->ReadResponseHeaders(&io_callback_);
+}
+
+int SpdyNetworkTransaction::DoReadHeadersComplete(int result) {
+  // TODO(willchan): Flesh out the support for HTTP authentication here.
+  return result;
+}
+
+int SpdyNetworkTransaction::DoReadBody() {
+  next_state_ = STATE_READ_BODY_COMPLETE;
+
+  return stream_->ReadResponseBody(
+      user_buffer_, user_buffer_len_, &io_callback_);
+}
+
+int SpdyNetworkTransaction::DoReadBodyComplete(int result) {
+  user_buffer_ = NULL;
+  user_buffer_len_ = 0;
+
+  if (result <= 0)
+    stream_.reset();
+
+  return result;
+}
+
+}  // namespace net
diff --git a/net/spdy/spdy_network_transaction.h b/net/spdy/spdy_network_transaction.h
new file mode 100644
index 0000000..4611cda
--- /dev/null
+++ b/net/spdy/spdy_network_transaction.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2009 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_SPDY_NETWORK_TRANSACTION_H_
+#define NET_SPDY_NETWORK_TRANSACTION_H_
+
+#include <string>
+#include <deque>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "net/base/completion_callback.h"
+#include "net/base/load_states.h"
+#include "net/base/net_log.h"
+#include "net/http/http_response_info.h"
+#include "net/http/http_transaction.h"
+#include "net/spdy/spdy_session.h"
+
+namespace net {
+
+class SpdySession;
+class SpdyHttpStream;
+class HttpNetworkSession;
+class HttpResponseInfo;
+class IOBuffer;
+class UploadDataStream;
+
+// A SpdyNetworkTransaction can be used to fetch HTTP conent.
+// The SpdyDelegate is the consumer of events from the SpdySession.
+class SpdyNetworkTransaction : public HttpTransaction {
+ public:
+  explicit SpdyNetworkTransaction(HttpNetworkSession* session);
+  virtual ~SpdyNetworkTransaction();
+
+  // HttpTransaction methods:
+  virtual int Start(const HttpRequestInfo* request_info,
+                    CompletionCallback* callback,
+                    const BoundNetLog& net_log);
+  virtual int RestartIgnoringLastError(CompletionCallback* callback);
+  virtual int RestartWithCertificate(X509Certificate* client_cert,
+                                     CompletionCallback* callback);
+  virtual int RestartWithAuth(const std::wstring& username,
+                              const std::wstring& password,
+                              CompletionCallback* callback);
+  virtual bool IsReadyToRestartForAuth() { return false; }
+  virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
+  virtual void StopCaching() {}
+  virtual const HttpResponseInfo* GetResponseInfo() const;
+  virtual LoadState GetLoadState() const;
+  virtual uint64 GetUploadProgress() const;
+
+ private:
+  FRIEND_TEST(SpdyNetworkTransactionTest, WindowUpdate);
+  FRIEND_TEST(SpdyNetworkTransactionTest, WindowUpdateOverflow);
+
+  enum State {
+    STATE_INIT_CONNECTION,
+    STATE_INIT_CONNECTION_COMPLETE,
+    STATE_GET_STREAM,
+    STATE_GET_STREAM_COMPLETE,
+    STATE_SEND_REQUEST,
+    STATE_SEND_REQUEST_COMPLETE,
+    STATE_READ_HEADERS,
+    STATE_READ_HEADERS_COMPLETE,
+    STATE_READ_BODY,
+    STATE_READ_BODY_COMPLETE,
+    STATE_NONE
+  };
+
+  void DoCallback(int result);
+  void OnIOComplete(int result);
+
+  // Runs the state transition loop.
+  int DoLoop(int result);
+
+  // Each of these methods corresponds to a State value.  Those with an input
+  // argument receive the result from the previous state.  If a method returns
+  // ERR_IO_PENDING, then the result from OnIOComplete will be passed to the
+  // next state method as the result arg.
+  int DoInitConnection();
+  int DoInitConnectionComplete(int result);
+  int DoGetStream();
+  int DoGetStreamComplete(int result);
+  int DoSendRequest();
+  int DoSendRequestComplete(int result);
+  int DoReadHeaders();
+  int DoReadHeadersComplete(int result);
+  int DoReadBody();
+  int DoReadBodyComplete(int result);
+
+  BoundNetLog net_log_;
+
+  scoped_refptr<SpdySession> spdy_;
+
+  CompletionCallbackImpl<SpdyNetworkTransaction> io_callback_;
+  CompletionCallback* user_callback_;
+
+  // Used to pass onto the SpdyStream
+  scoped_refptr<IOBuffer> user_buffer_;
+  int user_buffer_len_;
+
+  scoped_refptr<HttpNetworkSession> session_;
+
+  const HttpRequestInfo* request_;
+  HttpResponseInfo response_;
+
+  // The time the Start method was called.
+  base::TimeTicks start_time_;
+
+  // The next state in the state machine.
+  State next_state_;
+
+  scoped_ptr<SpdyHttpStream> stream_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpdyNetworkTransaction);
+};
+
+}  // namespace net
+
+#endif  // NET_SPDY_NETWORK_TRANSACTION_H_
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
new file mode 100644
index 0000000..7dbc12c
--- /dev/null
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -0,0 +1,2534 @@
+// Copyright (c) 2010 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/base/net_log_unittest.h"
+#include "net/http/http_transaction_unittest.h"
+#include "net/spdy/spdy_http_stream.h"
+#include "net/spdy/spdy_network_transaction.h"
+#include "net/spdy/spdy_test_util.h"
+#include "testing/platform_test.h"
+
+//-----------------------------------------------------------------------------
+
+namespace net {
+
+class SpdyNetworkTransactionTest : public PlatformTest {
+ protected:
+  virtual void SetUp() {
+    // By default, all tests turn off compression.
+    EnableCompression(false);
+    google_get_request_initialized_ = false;
+    HttpNetworkTransaction::SetUseAlternateProtocols(true);
+    HttpNetworkTransaction::SetNextProtos(
+        "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy");
+  }
+
+  virtual void TearDown() {
+    // Empty the current queue.
+    MessageLoop::current()->RunAllPending();
+    PlatformTest::TearDown();
+  }
+
+  void KeepAliveConnectionResendRequestTest(const MockRead& read_failure);
+
+  struct TransactionHelperResult {
+    int rv;
+    std::string status_line;
+    std::string response_data;
+    HttpResponseInfo response_info;
+  };
+
+  void EnableCompression(bool enabled) {
+    spdy::SpdyFramer::set_enable_compression_default(enabled);
+  }
+
+  class StartTransactionCallback;
+  class DeleteSessionCallback;
+
+  // A helper class that handles all the initial npn/ssl setup.
+  class NormalSpdyTransactionHelper {
+   public:
+    NormalSpdyTransactionHelper(const HttpRequestInfo& request,
+                                const BoundNetLog& log)
+        : request_(request),
+          session_(SpdySessionDependencies::SpdyCreateSession(&session_deps_)),
+          log_(log), add_data_allowed_(true) {}
+
+    void RunPreTestSetup() {
+      // Disallow future calls to AddData
+      add_data_allowed_ = false;
+
+      // Set up http data.
+      MockRead data_reads[] = {
+        MockRead("HTTP/1.1 200 OK\r\n"),
+        MockRead("Alternate-Protocol: 443:npn-spdy/1\r\n\r\n"),
+        MockRead("hello world"),
+        MockRead(true, OK),
+      };
+      first_transaction_.reset(
+          new StaticSocketDataProvider(data_reads, arraysize(data_reads),
+                                       NULL, 0));
+      session_deps_.socket_factory.AddSocketDataProvider(
+          first_transaction_.get());
+
+      // Set up actual test data. Also add one SSLSocketDataProvider per
+      // DataProvider.
+      for(DataVector::iterator it = data_vector_.begin();
+          it != data_vector_.end(); ++it) {
+        linked_ptr<SSLSocketDataProvider> ssl_(
+            new SSLSocketDataProvider(true, OK));
+        ssl_->next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+        ssl_->next_proto = "spdy/1";
+        ssl_->was_npn_negotiated = true;
+        ssl_vector_.push_back(ssl_);
+        session_deps_.socket_factory.AddSSLSocketDataProvider(ssl_.get());
+        session_deps_.socket_factory.AddSocketDataProvider(*it);
+      }
+
+      // We first send an http request. The Alternate-Protocol header switches
+      // the HttpNetworkTransaction into SSL/SPDY mode.
+      trans_http_.reset(new HttpNetworkTransaction(session_));
+      int rv = trans_http_->Start(&request_, &callback, log_);
+      EXPECT_EQ(ERR_IO_PENDING, rv);
+      EXPECT_EQ(OK, callback.WaitForResult());
+      const HttpResponseInfo* response = trans_http_->GetResponseInfo();
+      EXPECT_TRUE(response != NULL);
+      EXPECT_TRUE(response->headers != NULL);
+      EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+      std::string response_data;
+      EXPECT_EQ(OK, ReadTransaction(trans_http_.get(), &response_data));
+      EXPECT_EQ("hello world", response_data);
+
+      // We're now ready to use SSL-npn SPDY.
+      trans_.reset(new HttpNetworkTransaction(session_));
+    }
+
+    // Start the transaction, read some data, finish.
+    void RunDefaultTest() {
+      output_.rv = trans_->Start(&request_, &callback, log_);
+
+      // We expect an IO Pending or some sort of error.
+      EXPECT_LT(output_.rv, 0);
+      if (output_.rv != ERR_IO_PENDING)
+        return;
+
+      output_.rv = callback.WaitForResult();
+      if (output_.rv != OK) {
+        session_->spdy_session_pool()->ClearSessions();
+        return;
+      }
+
+      // Verify responses.
+      const HttpResponseInfo* response = trans_->GetResponseInfo();
+      ASSERT_TRUE(response != NULL);
+      ASSERT_TRUE(response->headers != NULL);
+      EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+      EXPECT_TRUE(response->was_fetched_via_spdy);
+      EXPECT_TRUE(response->was_npn_negotiated);
+      EXPECT_TRUE(response->was_alternate_protocol_available);
+      output_.status_line = response->headers->GetStatusLine();
+      output_.response_info = *response;  // Make a copy so we can verify.
+      output_.rv = ReadTransaction(trans_.get(), &output_.response_data);
+      EXPECT_EQ(OK, output_.rv);
+      return;
+    }
+
+    // Most tests will want to call this function. In particular, the MockReads
+    // should end with an empty read, and that read needs to be processed to
+    // ensure proper deletion of the spdy_session_pool.
+    void VerifyDataConsumed() {
+      for(DataVector::iterator it = data_vector_.begin();
+          it != data_vector_.end(); ++it) {
+        EXPECT_TRUE((*it)->at_read_eof()) << "Read count: "
+                                          << (*it)->read_count()
+                                          << " Read index: "
+                                          << (*it)->read_index();
+        EXPECT_TRUE((*it)->at_write_eof()) << "Write count: "
+                                           << (*it)->write_count()
+                                           << " Write index: "
+                                           << (*it)->write_index();
+      }
+    }
+
+    // Occasionally a test will expect to error out before certain reads are
+    // processed. In that case we want to explicitly ensure that the reads were
+    // not processed.
+    void VerifyDataNotConsumed() {
+      for(DataVector::iterator it = data_vector_.begin();
+          it != data_vector_.end(); ++it) {
+        EXPECT_TRUE(!(*it)->at_read_eof()) << "Read count: "
+                                           << (*it)->read_count()
+                                           << " Read index: "
+                                           << (*it)->read_index();
+        EXPECT_TRUE(!(*it)->at_write_eof()) << "Write count: "
+                                            << (*it)->write_count()
+                                            << " Write index: "
+                                            << (*it)->write_index();
+
+      }
+    }
+
+    void RunToCompletion(StaticSocketDataProvider* data) {
+      AddData(data);
+      RunPreTestSetup();
+      RunDefaultTest();
+      VerifyDataConsumed();
+    }
+
+    // Only call AddData before calling RunPreTestSetup!! When RunPreTestSetup
+    // is run, it will add this Data Provider, and a corresponding SSL data
+    // provider.
+    void AddData(StaticSocketDataProvider* data) {
+      EXPECT_TRUE(add_data_allowed_);
+      data_vector_.push_back(data);
+    }
+
+    // This can only be called after RunPreTestSetup. It adds a Data Provider,
+    // but not a corresponding SSL data provider
+    void AddDataNoSSL(StaticSocketDataProvider* data) {
+      session_deps_.socket_factory.AddSocketDataProvider(data);
+    }
+
+    void SetSession(scoped_refptr<HttpNetworkSession>& session) {
+      session_ = session;
+    }
+    HttpNetworkTransaction* trans() { return trans_.get(); }
+    void ResetTrans() { trans_.reset(); }
+    TransactionHelperResult& output() { return output_; }
+    HttpRequestInfo& request() { return request_; }
+    scoped_refptr<HttpNetworkSession>& session() { return session_; }
+
+   private:
+    typedef std::vector<StaticSocketDataProvider*> DataVector;
+    typedef std::vector<linked_ptr<SSLSocketDataProvider> > SSLVector;
+    HttpRequestInfo request_;
+    SpdySessionDependencies session_deps_;
+    scoped_refptr<HttpNetworkSession> session_;
+    TransactionHelperResult output_;
+    scoped_ptr<StaticSocketDataProvider> first_transaction_;
+    SSLVector ssl_vector_;
+    TestCompletionCallback callback;
+    scoped_ptr<HttpNetworkTransaction> trans_;
+    scoped_ptr<HttpNetworkTransaction> trans_http_;
+    DataVector data_vector_;
+    const BoundNetLog& log_;
+    bool add_data_allowed_;
+  };
+
+  void ConnectStatusHelperWithExpectedStatus(const MockRead& status,
+                                             int expected_status);
+
+  void ConnectStatusHelper(const MockRead& status);
+
+  const HttpRequestInfo& CreateGetRequest() {
+    if (!google_get_request_initialized_) {
+      google_get_request_.method = "GET";
+      google_get_request_.url = GURL("http://www.google.com/");
+      google_get_request_.load_flags = 0;
+      google_get_request_initialized_ = true;
+    }
+    return google_get_request_;
+  }
+
+ private:
+  bool google_get_request_initialized_;
+  HttpRequestInfo google_get_request_;
+};
+
+//-----------------------------------------------------------------------------
+
+// Verify HttpNetworkTransaction constructor.
+TEST_F(SpdyNetworkTransactionTest, Constructor) {
+  SpdySessionDependencies session_deps;
+  scoped_refptr<HttpNetworkSession> session =
+      SpdySessionDependencies::SpdyCreateSession(&session_deps);
+  scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
+}
+
+TEST_F(SpdyNetworkTransactionTest, Get) {
+  // Construct the request.
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+// Start three gets simultaniously; making sure that multiplexed
+// streams work properly.
+
+// This can't use the TransactionHelper method, since it only
+// handles a single transaction, and finishes them as soon
+// as it launches them.
+
+// TODO(gavinp): create a working generalized TransactionHelper that
+// can allow multiple streams in flight.
+
+TEST_F(SpdyNetworkTransactionTest, ThreeGets) {
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, false));
+  scoped_ptr<spdy::SpdyFrame> fbody(ConstructSpdyBodyFrame(1, true));
+
+  scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3));
+  scoped_ptr<spdy::SpdyFrame> body2(ConstructSpdyBodyFrame(3, false));
+  scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true));
+
+  scoped_ptr<spdy::SpdyFrame> req3(ConstructSpdyGet(NULL, 0, false, 5, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp3(ConstructSpdyGetSynReply(NULL, 0, 5));
+  scoped_ptr<spdy::SpdyFrame> body3(ConstructSpdyBodyFrame(5, false));
+  scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(5, true));
+
+  MockWrite writes[] = { CreateMockWrite(*req),
+                         CreateMockWrite(*req2),
+                         CreateMockWrite(*req3),
+  };
+  MockRead reads[] = {
+    CreateMockRead(*resp, 1),
+    CreateMockRead(*body),
+    CreateMockRead(*resp2, 4),
+    CreateMockRead(*body2),
+    CreateMockRead(*resp3, 7),
+    CreateMockRead(*body3),
+
+    CreateMockRead(*fbody),
+    CreateMockRead(*fbody2),
+    CreateMockRead(*fbody3),
+
+    MockRead(true, 0, 0),  // EOF
+  };
+
+  scoped_refptr<OrderedSocketData> data(
+      new OrderedSocketData(reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  BoundNetLog log;
+  TransactionHelperResult out;
+  {
+    SpdySessionDependencies session_deps;
+    HttpNetworkSession* session =
+        SpdySessionDependencies::SpdyCreateSession(&session_deps);
+    SpdySession::SetSSLMode(false);
+    scoped_ptr<SpdyNetworkTransaction> trans1(
+        new SpdyNetworkTransaction(session));
+    scoped_ptr<SpdyNetworkTransaction> trans2(
+        new SpdyNetworkTransaction(session));
+    scoped_ptr<SpdyNetworkTransaction> trans3(
+        new SpdyNetworkTransaction(session));
+
+    session_deps.socket_factory.AddSocketDataProvider(data);
+
+    TestCompletionCallback callback1;
+    TestCompletionCallback callback2;
+    TestCompletionCallback callback3;
+
+    HttpRequestInfo httpreq1 = CreateGetRequest();
+    HttpRequestInfo httpreq2 = CreateGetRequest();
+    HttpRequestInfo httpreq3 = CreateGetRequest();
+
+    out.rv = trans1->Start(&httpreq1, &callback1, log);
+    ASSERT_EQ(ERR_IO_PENDING, out.rv);
+    out.rv = trans2->Start(&httpreq2, &callback2, log);
+    ASSERT_EQ(ERR_IO_PENDING, out.rv);
+    out.rv = trans3->Start(&httpreq3, &callback3, log);
+    ASSERT_EQ(ERR_IO_PENDING, out.rv);
+
+    out.rv = callback1.WaitForResult();
+    ASSERT_EQ(OK, out.rv);
+    out.rv = callback3.WaitForResult();
+    ASSERT_EQ(OK, out.rv);
+
+    const HttpResponseInfo* response1 = trans1->GetResponseInfo();
+    EXPECT_TRUE(response1->headers != NULL);
+    EXPECT_TRUE(response1->was_fetched_via_spdy);
+    out.status_line = response1->headers->GetStatusLine();
+    out.response_info = *response1;
+
+    trans2->GetResponseInfo();
+
+    out.rv = ReadTransaction(trans1.get(), &out.response_data);
+  }
+  EXPECT_EQ(OK, out.rv);
+
+  EXPECT_TRUE(data->at_read_eof());
+  EXPECT_TRUE(data->at_write_eof());
+
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("hello!hello!", out.response_data);
+}
+
+
+// Similar to ThreeGets above, however this test adds a SETTINGS
+// frame.  The SETTINGS frame is read during the IO loop waiting on
+// the first transaction completion, and sets a maximum concurrent
+// stream limit of 1.  This means that our IO loop exists after the
+// second transaction completes, so we can assert on read_index().
+TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) {
+  // Construct the request.
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, false));
+  scoped_ptr<spdy::SpdyFrame> fbody(ConstructSpdyBodyFrame(1, true));
+
+  scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3));
+  scoped_ptr<spdy::SpdyFrame> body2(ConstructSpdyBodyFrame(3, false));
+  scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true));
+
+  scoped_ptr<spdy::SpdyFrame> req3(ConstructSpdyGet(NULL, 0, false, 5, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp3(ConstructSpdyGetSynReply(NULL, 0, 5));
+  scoped_ptr<spdy::SpdyFrame> body3(ConstructSpdyBodyFrame(5, false));
+  scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(5, true));
+
+  spdy::SpdySettings settings;
+  spdy::SettingsFlagsAndId id(0);
+  id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+  const size_t max_concurrent_streams = 1;
+
+  settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
+  scoped_ptr<spdy::SpdyFrame> settings_frame(ConstructSpdySettings(settings));
+
+  MockWrite writes[] = { CreateMockWrite(*req),
+                         CreateMockWrite(*req2),
+                         CreateMockWrite(*req3),
+  };
+  MockRead reads[] = {
+    CreateMockRead(*settings_frame, 0),
+    CreateMockRead(*resp),
+    CreateMockRead(*body),
+    CreateMockRead(*fbody),
+    CreateMockRead(*resp2, 6),
+    CreateMockRead(*body2),
+    CreateMockRead(*fbody2),
+    CreateMockRead(*resp3, 11),
+    CreateMockRead(*body3),
+    CreateMockRead(*fbody3),
+
+    MockRead(true, 0, 0),  // EOF
+  };
+
+  scoped_refptr<OrderedSocketData> data(
+      new OrderedSocketData(reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  BoundNetLog log;
+  TransactionHelperResult out;
+  {
+    SpdySessionDependencies session_deps;
+    HttpNetworkSession* session =
+        SpdySessionDependencies::SpdyCreateSession(&session_deps);
+    SpdySession::SetSSLMode(false);
+    scoped_ptr<SpdyNetworkTransaction> trans1(
+        new SpdyNetworkTransaction(session));
+    scoped_ptr<SpdyNetworkTransaction> trans2(
+        new SpdyNetworkTransaction(session));
+    scoped_ptr<SpdyNetworkTransaction> trans3(
+        new SpdyNetworkTransaction(session));
+
+    session_deps.socket_factory.AddSocketDataProvider(data);
+
+    TestCompletionCallback callback1;
+    TestCompletionCallback callback2;
+    TestCompletionCallback callback3;
+
+    HttpRequestInfo httpreq1 = CreateGetRequest();
+    HttpRequestInfo httpreq2 = CreateGetRequest();
+    HttpRequestInfo httpreq3 = CreateGetRequest();
+
+    out.rv = trans1->Start(&httpreq1, &callback1, log);
+    ASSERT_EQ(out.rv, ERR_IO_PENDING);
+    // run transaction 1 through quickly to force a read of our SETTINGS
+    // frame
+    out.rv = callback1.WaitForResult();
+
+    out.rv = trans2->Start(&httpreq2, &callback2, log);
+    ASSERT_EQ(out.rv, ERR_IO_PENDING);
+    out.rv = trans3->Start(&httpreq3, &callback3, log);
+    ASSERT_EQ(out.rv, ERR_IO_PENDING);
+    out.rv = callback2.WaitForResult();
+    ASSERT_EQ(OK, out.rv);
+    EXPECT_EQ(7U, data->read_index());  // i.e. the third trans was queued
+
+    out.rv = callback3.WaitForResult();
+    ASSERT_EQ(OK, out.rv);
+
+    const HttpResponseInfo* response1 = trans1->GetResponseInfo();
+    EXPECT_TRUE(response1->headers != NULL);
+    EXPECT_TRUE(response1->was_fetched_via_spdy);
+    out.status_line = response1->headers->GetStatusLine();
+    out.response_info = *response1;
+    out.rv = ReadTransaction(trans1.get(), &out.response_data);
+    EXPECT_EQ(OK, out.rv);
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+    EXPECT_EQ("hello!hello!", out.response_data);
+
+    const HttpResponseInfo* response2 = trans2->GetResponseInfo();
+    out.status_line = response2->headers->GetStatusLine();
+    out.response_info = *response2;
+    out.rv = ReadTransaction(trans2.get(), &out.response_data);
+    EXPECT_EQ(OK, out.rv);
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+    EXPECT_EQ("hello!hello!", out.response_data);
+
+    const HttpResponseInfo* response3 = trans3->GetResponseInfo();
+    out.status_line = response3->headers->GetStatusLine();
+    out.response_info = *response3;
+    out.rv = ReadTransaction(trans3.get(), &out.response_data);
+    EXPECT_EQ(OK, out.rv);
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+    EXPECT_EQ("hello!hello!", out.response_data);
+  }
+  EXPECT_EQ(OK, out.rv);
+
+  EXPECT_TRUE(data->at_read_eof());
+  EXPECT_TRUE(data->at_write_eof());
+}
+
+// Similar to ThreeGetsWithMaxConcurrent above, however this test adds
+// a fourth transaction.  The third and fourth transactions have
+// different data ("hello!" vs "hello!hello!") and because of the
+// user specified priority, we expect to see them inverted in
+// the response from the server.
+TEST_F(SpdyNetworkTransactionTest, FourGetsWithMaxConcurrentPriority) {
+  // Construct the request.
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, false));
+  scoped_ptr<spdy::SpdyFrame> fbody(ConstructSpdyBodyFrame(1, true));
+
+  scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3));
+  scoped_ptr<spdy::SpdyFrame> body2(ConstructSpdyBodyFrame(3, false));
+  scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true));
+
+  scoped_ptr<spdy::SpdyFrame> req4(
+      ConstructSpdyGet(NULL, 0, false, 5, HIGHEST));
+  scoped_ptr<spdy::SpdyFrame> resp4(ConstructSpdyGetSynReply(NULL, 0, 5));
+  scoped_ptr<spdy::SpdyFrame> fbody4(ConstructSpdyBodyFrame(5, true));
+
+  scoped_ptr<spdy::SpdyFrame> req3(ConstructSpdyGet(NULL, 0, false, 7, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp3(ConstructSpdyGetSynReply(NULL, 0, 7));
+  scoped_ptr<spdy::SpdyFrame> body3(ConstructSpdyBodyFrame(7, false));
+  scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(7, true));
+
+
+  spdy::SpdySettings settings;
+  spdy::SettingsFlagsAndId id(0);
+  id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+  const size_t max_concurrent_streams = 1;
+
+  settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
+  scoped_ptr<spdy::SpdyFrame> settings_frame(ConstructSpdySettings(settings));
+
+  MockWrite writes[] = { CreateMockWrite(*req),
+                         CreateMockWrite(*req2),
+                         CreateMockWrite(*req4),
+                         CreateMockWrite(*req3),
+  };
+  MockRead reads[] = {
+    CreateMockRead(*settings_frame, 0),
+    CreateMockRead(*resp),
+    CreateMockRead(*body),
+    CreateMockRead(*fbody),
+    CreateMockRead(*resp2, 6),
+    CreateMockRead(*body2),
+    CreateMockRead(*fbody2),
+    CreateMockRead(*resp4, 12),
+    CreateMockRead(*fbody4),
+    CreateMockRead(*resp3, 15),
+    CreateMockRead(*body3),
+    CreateMockRead(*fbody3),
+
+    MockRead(true, 0, 0),  // EOF
+  };
+
+  scoped_refptr<OrderedSocketData> data(
+      new OrderedSocketData(reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  BoundNetLog log;
+  TransactionHelperResult out;
+  {
+    SpdySessionDependencies session_deps;
+    HttpNetworkSession* session =
+        SpdySessionDependencies::SpdyCreateSession(&session_deps);
+    SpdySession::SetSSLMode(false);
+    scoped_ptr<SpdyNetworkTransaction> trans1(
+        new SpdyNetworkTransaction(session));
+    scoped_ptr<SpdyNetworkTransaction> trans2(
+        new SpdyNetworkTransaction(session));
+    scoped_ptr<SpdyNetworkTransaction> trans3(
+        new SpdyNetworkTransaction(session));
+    scoped_ptr<SpdyNetworkTransaction> trans4(
+        new SpdyNetworkTransaction(session));
+
+    session_deps.socket_factory.AddSocketDataProvider(data);
+
+    TestCompletionCallback callback1;
+    TestCompletionCallback callback2;
+    TestCompletionCallback callback3;
+    TestCompletionCallback callback4;
+
+    HttpRequestInfo httpreq1 = CreateGetRequest();
+    HttpRequestInfo httpreq2 = CreateGetRequest();
+    HttpRequestInfo httpreq3 = CreateGetRequest();
+    HttpRequestInfo httpreq4 = CreateGetRequest();
+    httpreq4.priority = HIGHEST;
+
+    out.rv = trans1->Start(&httpreq1, &callback1, log);
+    ASSERT_EQ(ERR_IO_PENDING, out.rv);
+    // run transaction 1 through quickly to force a read of our SETTINGS
+    // frame
+    out.rv = callback1.WaitForResult();
+    ASSERT_EQ(OK, out.rv);
+
+    out.rv = trans2->Start(&httpreq2, &callback2, log);
+    ASSERT_EQ(ERR_IO_PENDING, out.rv);
+    out.rv = trans3->Start(&httpreq3, &callback3, log);
+    ASSERT_EQ(ERR_IO_PENDING, out.rv);
+    out.rv = trans4->Start(&httpreq4, &callback4, log);
+    ASSERT_EQ(ERR_IO_PENDING, out.rv);
+
+    out.rv = callback2.WaitForResult();
+    ASSERT_EQ(OK, out.rv);
+    EXPECT_EQ(data->read_index(), 7U);  // i.e. the third & fourth trans queued
+
+    out.rv = callback3.WaitForResult();
+    ASSERT_EQ(OK, out.rv);
+
+    const HttpResponseInfo* response1 = trans1->GetResponseInfo();
+    EXPECT_TRUE(response1->headers != NULL);
+    EXPECT_TRUE(response1->was_fetched_via_spdy);
+    out.status_line = response1->headers->GetStatusLine();
+    out.response_info = *response1;
+    out.rv = ReadTransaction(trans1.get(), &out.response_data);
+    EXPECT_EQ(OK, out.rv);
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+    EXPECT_EQ("hello!hello!", out.response_data);
+
+    const HttpResponseInfo* response2 = trans2->GetResponseInfo();
+    out.status_line = response2->headers->GetStatusLine();
+    out.response_info = *response2;
+    out.rv = ReadTransaction(trans2.get(), &out.response_data);
+    EXPECT_EQ(OK, out.rv);
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+    EXPECT_EQ("hello!hello!", out.response_data);
+
+    // notice: response3 gets two hellos, response4 gets one
+    // hello, so we know dequeuing priority was respected.
+    const HttpResponseInfo* response3 = trans3->GetResponseInfo();
+    out.status_line = response3->headers->GetStatusLine();
+    out.response_info = *response3;
+    out.rv = ReadTransaction(trans3.get(), &out.response_data);
+    EXPECT_EQ(OK, out.rv);
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+    EXPECT_EQ("hello!hello!", out.response_data);
+
+    out.rv = callback4.WaitForResult();
+    EXPECT_EQ(OK, out.rv);
+    const HttpResponseInfo* response4 = trans4->GetResponseInfo();
+    out.status_line = response4->headers->GetStatusLine();
+    out.response_info = *response4;
+    out.rv = ReadTransaction(trans4.get(), &out.response_data);
+    EXPECT_EQ(OK, out.rv);
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+    EXPECT_EQ("hello!", out.response_data);
+  }
+  EXPECT_EQ(OK, out.rv);
+
+  EXPECT_TRUE(data->at_read_eof());
+  EXPECT_TRUE(data->at_write_eof());
+}
+
+// Similar to ThreeGetsMaxConcurrrent above, however, this test
+// deletes a session in the middle of the transaction to insure
+// that we properly remove pendingcreatestream objects from
+// the spdy_session
+TEST_F(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrentDelete) {
+  // Construct the request.
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, false));
+  scoped_ptr<spdy::SpdyFrame> fbody(ConstructSpdyBodyFrame(1, true));
+
+  scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3));
+  scoped_ptr<spdy::SpdyFrame> body2(ConstructSpdyBodyFrame(3, false));
+  scoped_ptr<spdy::SpdyFrame> fbody2(ConstructSpdyBodyFrame(3, true));
+
+  scoped_ptr<spdy::SpdyFrame> req3(ConstructSpdyGet(NULL, 0, false, 5, LOWEST));
+  scoped_ptr<spdy::SpdyFrame> resp3(ConstructSpdyGetSynReply(NULL, 0, 5));
+  scoped_ptr<spdy::SpdyFrame> body3(ConstructSpdyBodyFrame(5, false));
+  scoped_ptr<spdy::SpdyFrame> fbody3(ConstructSpdyBodyFrame(5, true));
+
+  spdy::SpdySettings settings;
+  spdy::SettingsFlagsAndId id(0);
+  id.set_id(spdy::SETTINGS_MAX_CONCURRENT_STREAMS);
+  const size_t max_concurrent_streams = 1;
+
+  settings.push_back(spdy::SpdySetting(id, max_concurrent_streams));
+  scoped_ptr<spdy::SpdyFrame> settings_frame(ConstructSpdySettings(settings));
+
+  MockWrite writes[] = { CreateMockWrite(*req),
+                         CreateMockWrite(*req2),
+  };
+  MockRead reads[] = {
+    CreateMockRead(*settings_frame, 0),
+    CreateMockRead(*resp),
+    CreateMockRead(*body),
+    CreateMockRead(*fbody),
+    CreateMockRead(*resp2, 6),
+    CreateMockRead(*body2),
+    CreateMockRead(*fbody2),
+    MockRead(true, 0, 0),  // EOF
+  };
+
+  scoped_refptr<OrderedSocketData> data(
+      new OrderedSocketData(reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  BoundNetLog log;
+  TransactionHelperResult out;
+  {
+    SpdySessionDependencies session_deps;
+    HttpNetworkSession* session =
+        SpdySessionDependencies::SpdyCreateSession(&session_deps);
+    SpdySession::SetSSLMode(false);
+    scoped_ptr<SpdyNetworkTransaction> trans1(
+        new SpdyNetworkTransaction(session));
+    scoped_ptr<SpdyNetworkTransaction> trans2(
+        new SpdyNetworkTransaction(session));
+    scoped_ptr<SpdyNetworkTransaction> trans3(
+        new SpdyNetworkTransaction(session));
+
+    session_deps.socket_factory.AddSocketDataProvider(data);
+
+    TestCompletionCallback callback1;
+    TestCompletionCallback callback2;
+    TestCompletionCallback callback3;
+
+    HttpRequestInfo httpreq1 = CreateGetRequest();
+    HttpRequestInfo httpreq2 = CreateGetRequest();
+    HttpRequestInfo httpreq3 = CreateGetRequest();
+
+    out.rv = trans1->Start(&httpreq1, &callback1, log);
+    ASSERT_EQ(out.rv, ERR_IO_PENDING);
+    // run transaction 1 through quickly to force a read of our SETTINGS
+    // frame
+    out.rv = callback1.WaitForResult();
+
+    out.rv = trans2->Start(&httpreq2, &callback2, log);
+    ASSERT_EQ(out.rv, ERR_IO_PENDING);
+    out.rv = trans3->Start(&httpreq3, &callback3, log);
+    delete trans3.release();
+    ASSERT_EQ(out.rv, ERR_IO_PENDING);
+    out.rv = callback2.WaitForResult();
+
+    EXPECT_EQ(8U, data->read_index());
+
+    const HttpResponseInfo* response1 = trans1->GetResponseInfo();
+    EXPECT_TRUE(response1->headers != NULL);
+    EXPECT_TRUE(response1->was_fetched_via_spdy);
+    out.status_line = response1->headers->GetStatusLine();
+    out.response_info = *response1;
+    out.rv = ReadTransaction(trans1.get(), &out.response_data);
+    EXPECT_EQ(OK, out.rv);
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+    EXPECT_EQ("hello!hello!", out.response_data);
+
+    const HttpResponseInfo* response2 = trans2->GetResponseInfo();
+    out.status_line = response2->headers->GetStatusLine();
+    out.response_info = *response2;
+    out.rv = ReadTransaction(trans2.get(), &out.response_data);
+    EXPECT_EQ(OK, out.rv);
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+    EXPECT_EQ("hello!hello!", out.response_data);
+  }
+  EXPECT_EQ(OK, out.rv);
+
+  EXPECT_TRUE(data->at_read_eof());
+  EXPECT_TRUE(data->at_write_eof());
+}
+
+// Test that a simple POST works.
+TEST_F(SpdyNetworkTransactionTest, Post) {
+  static const char upload[] = { "hello!" };
+
+  // Setup the request
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("http://www.google.com/");
+  request.upload_data = new UploadData();
+  request.upload_data->AppendBytes(upload, strlen(upload));
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost(NULL, 0));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockWrite writes[] = {
+    CreateMockWrite(*req),
+    CreateMockWrite(*body),  // POST upload frame
+  };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPostSynReply(NULL, 0));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(2, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  NormalSpdyTransactionHelper helper(request,
+                                     BoundNetLog());
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+// Test that a simple POST works.
+TEST_F(SpdyNetworkTransactionTest, EmptyPost) {
+  // Setup the request
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("http://www.google.com/");
+  // Create an empty UploadData.
+  request.upload_data = new UploadData();
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost(NULL, 0));
+  // Set the FIN bit since there will be no body.
+  req->set_flags(spdy::CONTROL_FLAG_FIN);
+  MockWrite writes[] = {
+    CreateMockWrite(*req),
+  };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPostSynReply(NULL, 0));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  NormalSpdyTransactionHelper helper(request,
+                                     BoundNetLog());
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+// While we're doing a post, the server sends back a SYN_REPLY.
+TEST_F(SpdyNetworkTransactionTest, PostWithEarlySynReply) {
+  static const char upload[] = { "hello!" };
+
+  // Setup the request
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("http://www.google.com/");
+  request.upload_data = new UploadData();
+  request.upload_data->AppendBytes(upload, sizeof(upload));
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost(NULL, 0));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+    MockWrite writes[] = {
+    CreateMockWrite(*req.get(), 2),
+    CreateMockWrite(*body.get(), 3),  // POST upload frame
+  };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyPostSynReply(NULL, 0));
+  MockRead reads[] = {
+    CreateMockRead(*resp.get(), 2),
+    CreateMockRead(*body.get(), 3),
+    MockRead(false, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(0, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  NormalSpdyTransactionHelper helper(request,
+                                     BoundNetLog());
+  helper.AddData(data.get());
+  helper.RunPreTestSetup();
+  helper.RunDefaultTest();
+  helper.VerifyDataNotConsumed();
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, out.rv);
+}
+
+// Test that the transaction doesn't crash when we don't have a reply.
+TEST_F(SpdyNetworkTransactionTest, ResponseWithoutSynReply) {
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockRead reads[] = {
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads), NULL, 0));
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(ERR_SYN_REPLY_NOT_RECEIVED, out.rv);
+}
+
+// Test that the transaction doesn't crash when we get two replies on the same
+// stream ID. See http://crbug.com/45639.
+TEST_F(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) {
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    CreateMockRead(*resp),
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.AddData(data.get());
+  helper.RunPreTestSetup();
+
+  HttpNetworkTransaction* trans = helper.trans();
+
+  TestCompletionCallback callback;
+  int rv = trans->Start(&helper.request(), &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  rv = callback.WaitForResult();
+  EXPECT_EQ(OK, rv);
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  std::string response_data;
+  rv = ReadTransaction(trans, &response_data);
+  EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, rv);
+
+  helper.VerifyDataConsumed();
+}
+
+// Test that WINDOW_UPDATE frames change window_size correctly.
+TEST_F(SpdyNetworkTransactionTest, WindowUpdate) {
+  SpdySessionDependencies session_deps;
+  scoped_refptr<HttpNetworkSession> session =
+      SpdySessionDependencies::SpdyCreateSession(&session_deps);
+
+  // We disable SSL for this test.
+  SpdySession::SetSSLMode(false);
+
+  // Setup the request
+  static const char upload[] = { "hello!" };
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("http://www.google.com/");
+  request.upload_data = new UploadData();
+  request.upload_data->AppendBytes(upload, strlen(upload));
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost(NULL, 0));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockWrite writes[] = {
+    CreateMockWrite(*req),
+    CreateMockWrite(*body),
+  };
+
+  // Response frames, send WINDOW_UPDATE first
+  static const int kDeltaWindowSize = 0xff;
+  scoped_ptr<spdy::SpdyFrame> window_update(
+      ConstructSpdyWindowUpdate(1, kDeltaWindowSize));
+  scoped_ptr<spdy::SpdyFrame> reply(ConstructSpdyPostSynReply(NULL, 0));
+  MockRead reads[] = {
+    CreateMockRead(*window_update),
+    CreateMockRead(*reply),
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(2, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  session_deps.socket_factory.AddSocketDataProvider(data.get());
+
+  scoped_ptr<SpdyNetworkTransaction> trans(
+      new SpdyNetworkTransaction(session));
+
+  TestCompletionCallback callback;
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+
+  ASSERT_TRUE(trans->stream_ != NULL);
+  ASSERT_TRUE(trans->stream_->stream() != NULL);
+  EXPECT_EQ(spdy::kInitialWindowSize, trans->stream_->stream()->window_size());
+
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  rv = callback.WaitForResult();
+  EXPECT_EQ(OK, rv);
+
+  ASSERT_TRUE(trans->stream_ != NULL);
+  ASSERT_TRUE(trans->stream_->stream() != NULL);
+  EXPECT_EQ(spdy::kInitialWindowSize + kDeltaWindowSize,
+            trans->stream_->stream()->window_size());
+  EXPECT_TRUE(data->at_read_eof());
+  EXPECT_TRUE(data->at_write_eof());
+}
+
+// Test that WINDOW_UPDATE frame causing overflow is handled correctly.
+TEST_F(SpdyNetworkTransactionTest, WindowUpdateOverflow) {
+  SpdySessionDependencies session_deps;
+  scoped_refptr<HttpNetworkSession> session =
+      SpdySessionDependencies::SpdyCreateSession(&session_deps);
+
+  // We disable SSL for this test.
+  SpdySession::SetSSLMode(false);
+
+  // Setup the request
+  static const char upload[] = { "hello!" };
+  HttpRequestInfo request;
+  request.method = "POST";
+  request.url = GURL("http://www.google.com/");
+  request.upload_data = new UploadData();
+  request.upload_data->AppendBytes(upload, strlen(upload));
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyPost(NULL, 0));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  scoped_ptr<spdy::SpdyFrame> rst(
+      ConstructSpdyRstStream(1, spdy::FLOW_CONTROL_ERROR));
+  MockWrite writes[] = {
+    CreateMockWrite(*req),
+    CreateMockWrite(*body),
+    CreateMockWrite(*rst),
+  };
+
+  // Response frames, send WINDOW_UPDATE first
+  static const int kDeltaWindowSize = 0x7fffffff; // cause an overflow
+  scoped_ptr<spdy::SpdyFrame> window_update(
+      ConstructSpdyWindowUpdate(1, kDeltaWindowSize));
+  scoped_ptr<spdy::SpdyFrame> reply(ConstructSpdyPostSynReply(NULL, 0));
+  MockRead reads[] = {
+    CreateMockRead(*window_update),
+    CreateMockRead(*reply),
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(2, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  session_deps.socket_factory.AddSocketDataProvider(data.get());
+
+  scoped_ptr<SpdyNetworkTransaction> trans(
+      new SpdyNetworkTransaction(session));
+
+  TestCompletionCallback callback;
+  int rv = trans->Start(&request, &callback, BoundNetLog());
+
+  ASSERT_TRUE(trans->stream_ != NULL);
+  ASSERT_TRUE(trans->stream_->stream() != NULL);
+  EXPECT_EQ(spdy::kInitialWindowSize, trans->stream_->stream()->window_size());
+
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  rv = callback.WaitForResult();
+  EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, rv);
+
+  ASSERT_TRUE(session != NULL);
+  ASSERT_TRUE(session->spdy_session_pool() != NULL);
+  session->spdy_session_pool()->ClearSessions();
+
+  EXPECT_FALSE(data->at_read_eof());
+  EXPECT_TRUE(data->at_write_eof());
+}
+
+TEST_F(SpdyNetworkTransactionTest, CancelledTransaction) {
+  // Construct the request.
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = {
+    CreateMockWrite(*req),
+  };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    // This following read isn't used by the test, except during the
+    // RunAllPending() call at the end since the SpdySession survives the
+    // HttpNetworkTransaction and still tries to continue Read()'ing.  Any
+    // MockRead will do here.
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  StaticSocketDataProvider data(reads, arraysize(reads),
+                                writes, arraysize(writes));
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.AddData(&data);
+  helper.RunPreTestSetup();
+  HttpNetworkTransaction* trans = helper.trans();
+
+  TestCompletionCallback callback;
+  int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  helper.ResetTrans();  // Cancel the transaction.
+
+  // Flush the MessageLoop while the SpdySessionDependencies (in particular, the
+  // MockClientSocketFactory) are still alive.
+  MessageLoop::current()->RunAllPending();
+  helper.VerifyDataNotConsumed();
+}
+
+class SpdyNetworkTransactionTest::StartTransactionCallback
+    : public CallbackRunner< Tuple1<int> > {
+ public:
+  explicit StartTransactionCallback(
+      scoped_refptr<HttpNetworkSession>& session,
+      NormalSpdyTransactionHelper& helper)
+      : session_(session), helper_(helper) {}
+
+  // We try to start another transaction, which should succeed.
+  virtual void RunWithParams(const Tuple1<int>& params) {
+    scoped_ptr<HttpNetworkTransaction> trans(
+        new HttpNetworkTransaction(session_));
+    TestCompletionCallback callback;
+    HttpRequestInfo request;
+    request.method = "GET";
+    request.url = GURL("http://www.google.com/");
+    request.load_flags = 0;
+    int rv = trans->Start(&request, &callback, BoundNetLog());
+    EXPECT_EQ(ERR_IO_PENDING, rv);
+    rv = callback.WaitForResult();
+  }
+
+ private:
+  scoped_refptr<HttpNetworkSession>& session_;
+  NormalSpdyTransactionHelper& helper_;
+};
+
+// Verify that the client can correctly deal with the user callback attempting
+// to start another transaction on a session that is closing down. See
+// http://crbug.com/47455
+TEST_F(SpdyNetworkTransactionTest, StartTransactionOnReadCallback) {
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+  MockWrite writes2[] = { CreateMockWrite(*req) };
+
+  // The indicated length of this packet is longer than its actual length. When
+  // the session receives an empty packet after this one, it shuts down the
+  // session, and calls the read callback with the incomplete data.
+  const uint8 kGetBodyFrame2[] = {
+    0x00, 0x00, 0x00, 0x01,
+    0x01, 0x00, 0x00, 0x07,
+    'h', 'e', 'l', 'l', 'o', '!',
+  };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  MockRead reads[] = {
+    CreateMockRead(*resp, 2),
+    MockRead(true, ERR_IO_PENDING, 3),  // Force a pause
+    MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame2),
+             arraysize(kGetBodyFrame2), 4),
+    MockRead(true, ERR_IO_PENDING, 5),  // Force a pause
+    MockRead(true, 0, 0, 6),  // EOF
+  };
+  MockRead reads2[] = {
+    CreateMockRead(*resp, 2),
+    MockRead(true, 0, 0, 3),  // EOF
+  };
+
+  scoped_refptr<OrderedSocketData> data(
+      new OrderedSocketData(reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  scoped_refptr<DelayedSocketData> data2(
+      new DelayedSocketData(0, reads2, arraysize(reads2),
+                            writes2, arraysize(writes2)));
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.AddData(data.get());
+  helper.AddData(data2.get());
+  helper.RunPreTestSetup();
+  HttpNetworkTransaction* trans = helper.trans();
+
+  // Start the transaction with basic parameters.
+  TestCompletionCallback callback;
+  int rv = trans->Start(&helper.request(), &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  rv = callback.WaitForResult();
+
+  StartTransactionCallback callback2(helper.session(), helper);
+  const int kSize = 3000;
+  scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSize);
+  rv = trans->Read(buf, kSize, &callback2);
+  // This forces an err_IO_pending, which sets the callback.
+  data->CompleteRead();
+  // This finishes the read.
+  data->CompleteRead();
+  helper.VerifyDataConsumed();
+}
+
+class SpdyNetworkTransactionTest::DeleteSessionCallback
+    : public CallbackRunner< Tuple1<int> > {
+ public:
+  explicit DeleteSessionCallback(NormalSpdyTransactionHelper& helper) :
+      helper_(helper) {}
+
+  // We kill the transaction, which deletes the session and stream.
+  virtual void RunWithParams(const Tuple1<int>& params) {
+    helper_.ResetTrans();
+  }
+
+ private:
+  NormalSpdyTransactionHelper& helper_;
+};
+
+// Verify that the client can correctly deal with the user callback deleting the
+// transaction. Failures will usually be valgrind errors. See
+// http://crbug.com/46925
+TEST_F(SpdyNetworkTransactionTest, DeleteSessionOnReadCallback) {
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockRead reads[] = {
+    CreateMockRead(*resp.get(), 2),
+    MockRead(true, ERR_IO_PENDING, 3),  // Force a pause
+    CreateMockRead(*body.get(), 4),
+    MockRead(true, 0, 0, 5),  // EOF
+  };
+
+  scoped_refptr<OrderedSocketData> data(
+      new OrderedSocketData(reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.AddData(data.get());
+  helper.RunPreTestSetup();
+  HttpNetworkTransaction* trans = helper.trans();
+
+  // Start the transaction with basic parameters.
+  TestCompletionCallback callback;
+  int rv = trans->Start(&helper.request(), &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+  rv = callback.WaitForResult();
+
+  // Setup a user callback which will delete the session, and clear out the
+  // memory holding the stream object. Note that the callback deletes trans.
+  DeleteSessionCallback callback2(helper);
+  const int kSize = 3000;
+  scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSize);
+  rv = trans->Read(buf, kSize, &callback2);
+  ASSERT_EQ(ERR_IO_PENDING, rv);
+  data->CompleteRead();
+
+  // Finish running rest of tasks.
+  MessageLoop::current()->RunAllPending();
+  helper.VerifyDataConsumed();
+}
+
+// Verify that various SynReply headers parse correctly through the
+// HTTP layer.
+TEST_F(SpdyNetworkTransactionTest, SynReplyHeaders) {
+  struct SynReplyHeadersTests {
+    int num_headers;
+    const char* extra_headers[5];
+    const char* expected_headers;
+  } test_cases[] = {
+    // This uses a multi-valued cookie header.
+    { 2,
+      { "cookie", "val1",
+        "cookie", "val2",  // will get appended separated by NULL
+        NULL
+      },
+      "cookie: val1\n"
+      "cookie: val2\n"
+      "hello: bye\n"
+      "status: 200\n"
+      "url: /index.php\n"
+      "version: HTTP/1.1\n"
+    },
+    // This is the minimalist set of headers.
+    { 0,
+      { NULL },
+      "hello: bye\n"
+      "status: 200\n"
+      "url: /index.php\n"
+      "version: HTTP/1.1\n"
+    },
+    // Headers with a comma separated list.
+    { 1,
+      { "cookie", "val1,val2",
+        NULL
+      },
+      "cookie: val1,val2\n"
+      "hello: bye\n"
+      "status: 200\n"
+      "url: /index.php\n"
+      "version: HTTP/1.1\n"
+    }
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+    scoped_ptr<spdy::SpdyFrame> req(
+        ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+    MockWrite writes[] = { CreateMockWrite(*req) };
+
+    scoped_ptr<spdy::SpdyFrame> resp(
+        ConstructSpdyGetSynReply(test_cases[i].extra_headers,
+                                 test_cases[i].num_headers,
+                                 1));
+    scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+    MockRead reads[] = {
+      CreateMockRead(*resp),
+      CreateMockRead(*body),
+      MockRead(true, 0, 0)  // EOF
+    };
+
+    scoped_refptr<DelayedSocketData> data(
+        new DelayedSocketData(1, reads, arraysize(reads),
+                              writes, arraysize(writes)));
+    NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                       BoundNetLog());
+    helper.RunToCompletion(data.get());
+    TransactionHelperResult out = helper.output();
+
+    EXPECT_EQ(OK, out.rv);
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+    EXPECT_EQ("hello!", out.response_data);
+
+    scoped_refptr<HttpResponseHeaders> headers = out.response_info.headers;
+    EXPECT_TRUE(headers.get() != NULL);
+    void* iter = NULL;
+    std::string name, value, lines;
+    while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
+      lines.append(name);
+      lines.append(": ");
+      lines.append(value);
+      lines.append("\n");
+    }
+    EXPECT_EQ(std::string(test_cases[i].expected_headers), lines);
+  }
+}
+
+// Verify that various SynReply headers parse vary fields correctly
+// through the HTTP layer, and the response matches the request.
+TEST_F(SpdyNetworkTransactionTest, SynReplyHeadersVary) {
+  static const SpdyHeaderInfo syn_reply_info = {
+    spdy::SYN_REPLY,                              // Syn Reply
+    1,                                            // Stream ID
+    0,                                            // Associated Stream ID
+    SPDY_PRIORITY_LOWEST,                         // Priority
+    spdy::CONTROL_FLAG_NONE,                      // Control Flags
+    false,                                        // Compressed
+    spdy::INVALID,                                // Status
+    NULL,                                         // Data
+    0,                                            // Data Length
+    spdy::DATA_FLAG_NONE                          // Data Flags
+  };
+  // Modify the following data to change/add test cases:
+  struct SynReplyTests {
+    const SpdyHeaderInfo* syn_reply;
+    bool vary_matches;
+    int num_headers[2];
+    const char* extra_headers[2][16];
+  } test_cases[] = {
+    // Test the case of a multi-valued cookie.  When the value is delimited
+    // with NUL characters, it needs to be unfolded into multiple headers.
+    {
+      &syn_reply_info,
+      true,
+      { 1, 4 },
+      { { "cookie",   "val1,val2",
+          NULL
+        },
+        { "vary",     "cookie",
+          "status",   "200",
+          "url",      "/index.php",
+          "version",  "HTTP/1.1",
+          NULL
+        }
+      }
+    }, {    // Multiple vary fields.
+      &syn_reply_info,
+      true,
+      { 2, 5 },
+      { { "friend",   "barney",
+          "enemy",    "snaggletooth",
+          NULL
+        },
+        { "vary",     "friend",
+          "vary",     "enemy",
+          "status",   "200",
+          "url",      "/index.php",
+          "version",  "HTTP/1.1",
+          NULL
+        }
+      }
+    }, {    // Test a '*' vary field.
+      &syn_reply_info,
+      false,
+      { 1, 4 },
+      { { "cookie",   "val1,val2",
+          NULL
+        },
+        { "vary",     "*",
+          "status",   "200",
+          "url",      "/index.php",
+          "version",  "HTTP/1.1",
+          NULL
+        }
+      }
+    }, {    // Multiple comma-separated vary fields.
+      &syn_reply_info,
+      true,
+      { 2, 4 },
+      { { "friend",   "barney",
+          "enemy",    "snaggletooth",
+          NULL
+        },
+        { "vary",     "friend,enemy",
+          "status",   "200",
+          "url",      "/index.php",
+          "version",  "HTTP/1.1",
+          NULL
+        }
+      }
+    }
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+    // Construct the request.
+    scoped_ptr<spdy::SpdyFrame> frame_req(
+      ConstructSpdyGet(test_cases[i].extra_headers[0],
+                       test_cases[i].num_headers[0],
+                       false, 1, LOWEST));
+
+    MockWrite writes[] = {
+      CreateMockWrite(*frame_req),
+    };
+
+    // Construct the reply.
+    scoped_ptr<spdy::SpdyFrame> frame_reply(
+      ConstructSpdyPacket(*test_cases[i].syn_reply,
+                          test_cases[i].extra_headers[1],
+                          test_cases[i].num_headers[1],
+                          NULL,
+                          0));
+
+    scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+    MockRead reads[] = {
+      CreateMockRead(*frame_reply),
+      CreateMockRead(*body),
+      MockRead(true, 0, 0)  // EOF
+    };
+
+    // Attach the headers to the request.
+    int header_count = test_cases[i].num_headers[0];
+
+    HttpRequestInfo request = CreateGetRequest();
+    for (int ct = 0; ct < header_count; ct++) {
+      const char* header_key = test_cases[i].extra_headers[0][ct * 2];
+      const char* header_value = test_cases[i].extra_headers[0][ct * 2 + 1];
+      request.extra_headers.SetHeader(header_key, header_value);
+    }
+
+    scoped_refptr<DelayedSocketData> data(
+        new DelayedSocketData(1, reads, arraysize(reads),
+                              writes, arraysize(writes)));
+    NormalSpdyTransactionHelper helper(request,
+                                       BoundNetLog());
+    helper.RunToCompletion(data.get());
+    TransactionHelperResult out = helper.output();
+
+    EXPECT_EQ(OK, out.rv) << i;
+    EXPECT_EQ("HTTP/1.1 200 OK", out.status_line) << i;
+    EXPECT_EQ("hello!", out.response_data) << i;
+
+    // Test the response information.
+    EXPECT_TRUE(out.response_info.response_time >
+                out.response_info.request_time) << i;
+    base::TimeDelta test_delay = out.response_info.response_time -
+                                 out.response_info.request_time;
+    base::TimeDelta min_expected_delay;
+    min_expected_delay.FromMilliseconds(10);
+    EXPECT_GT(test_delay.InMillisecondsF(),
+              min_expected_delay.InMillisecondsF()) << i;
+    EXPECT_EQ(out.response_info.vary_data.is_valid(),
+              test_cases[i].vary_matches) << i;
+
+    // Check the headers.
+    scoped_refptr<HttpResponseHeaders> headers = out.response_info.headers;
+    ASSERT_TRUE(headers.get() != NULL) << i;
+    void* iter = NULL;
+    std::string name, value, lines;
+    while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
+      lines.append(name);
+      lines.append(": ");
+      lines.append(value);
+      lines.append("\n");
+    }
+
+    // Construct the expected header reply string.
+    char reply_buffer[256] = "";
+    ConstructSpdyReplyString(test_cases[i].extra_headers[1],
+                             test_cases[i].num_headers[1],
+                             reply_buffer,
+                             256);
+
+    EXPECT_EQ(std::string(reply_buffer), lines) << i;
+  }
+}
+
+// Verify that we don't crash on invalid SynReply responses.
+TEST_F(SpdyNetworkTransactionTest, InvalidSynReply) {
+  const SpdyHeaderInfo kSynStartHeader = {
+    spdy::SYN_REPLY,              // Kind = SynReply
+    1,                            // Stream ID
+    0,                            // Associated stream ID
+    SPDY_PRIORITY_LOWEST,         // Priority
+    spdy::CONTROL_FLAG_NONE,      // Control Flags
+    false,                        // Compressed
+    spdy::INVALID,                // Status
+    NULL,                         // Data
+    0,                            // Length
+    spdy::DATA_FLAG_NONE          // Data Flags
+  };
+
+  struct InvalidSynReplyTests {
+    int num_headers;
+    const char* headers[10];
+  } test_cases[] = {
+    // SYN_REPLY missing status header
+    { 4,
+      { "cookie", "val1",
+        "cookie", "val2",
+        "url", "/index.php",
+        "version", "HTTP/1.1",
+        NULL
+      },
+    },
+    // SYN_REPLY missing version header
+    { 2,
+      { "status", "200",
+        "url", "/index.php",
+        NULL
+      },
+    },
+    // SYN_REPLY with no headers
+    { 0, { NULL }, },
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+    scoped_ptr<spdy::SpdyFrame> req(
+        ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+    MockWrite writes[] = {
+      CreateMockWrite(*req),
+    };
+
+    scoped_ptr<spdy::SpdyFrame> resp(
+        ConstructSpdyPacket(kSynStartHeader,
+                            NULL, 0,
+                            test_cases[i].headers,
+                            test_cases[i].num_headers));
+    scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+    MockRead reads[] = {
+      CreateMockRead(*resp),
+      CreateMockRead(*body),
+      MockRead(true, 0, 0)  // EOF
+    };
+
+    scoped_refptr<DelayedSocketData> data(
+        new DelayedSocketData(1, reads, arraysize(reads),
+                              writes, arraysize(writes)));
+    NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                       BoundNetLog());
+    helper.RunToCompletion(data.get());
+    TransactionHelperResult out = helper.output();
+    EXPECT_EQ(ERR_INVALID_RESPONSE, out.rv);
+  }
+}
+
+// Verify that we don't crash on some corrupt frames.
+// TODO(eroman): Renable this test, see http://crbug.com/48588
+TEST_F(SpdyNetworkTransactionTest, DISABLED_CorruptFrameSessionError) {
+  // This is the length field with a big number
+  scoped_ptr<spdy::SpdyFrame> syn_reply_massive_length(
+      ConstructSpdyGetSynReply(NULL, 0, 1));
+  syn_reply_massive_length->set_length(0x111126);
+
+  struct SynReplyTests {
+    const spdy::SpdyFrame* syn_reply;
+  } test_cases[] = {
+    { syn_reply_massive_length.get(), },
+  };
+
+  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+    scoped_ptr<spdy::SpdyFrame> req(
+        ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+    MockWrite writes[] = {
+      CreateMockWrite(*req),
+      MockWrite(true, 0, 0)  // EOF
+    };
+
+    scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+    MockRead reads[] = {
+      CreateMockRead(*test_cases[i].syn_reply),
+      CreateMockRead(*body),
+      MockRead(true, 0, 0)  // EOF
+    };
+
+    scoped_refptr<DelayedSocketData> data(
+        new DelayedSocketData(1, reads, arraysize(reads),
+                              writes, arraysize(writes)));
+    NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                       BoundNetLog());
+    helper.RunToCompletion(data.get());
+    TransactionHelperResult out = helper.output();
+    EXPECT_EQ(ERR_SPDY_PROTOCOL_ERROR, out.rv);
+  }
+}
+
+// Test that we shutdown correctly on write errors.
+TEST_F(SpdyNetworkTransactionTest, WriteError) {
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = {
+    // We'll write 10 bytes successfully
+    MockWrite(true, req->data(), 10),
+    // Followed by ERROR!
+    MockWrite(true, ERR_FAILED),
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(2, NULL, 0,
+                            writes, arraysize(writes)));
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(ERR_FAILED, out.rv);
+  data->Reset();
+}
+
+// Test that partial writes work.
+TEST_F(SpdyNetworkTransactionTest, PartialWrite) {
+  // Chop the SYN_STREAM frame into 5 chunks.
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  const int kChunks = 5;
+  scoped_array<MockWrite> writes(ChopWriteFrame(*req.get(), kChunks));
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(kChunks, reads, arraysize(reads),
+                            writes.get(), kChunks));
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+}
+
+// In this test, we enable compression, but get a uncompressed SynReply from
+// the server.  Verify that teardown is all clean.
+TEST_F(SpdyNetworkTransactionTest, DecompressFailureOnSynReply) {
+  // For this test, we turn on the normal compression.
+  EnableCompression(true);
+
+  scoped_ptr<spdy::SpdyFrame> compressed(
+      ConstructSpdyGet(NULL, 0, true, 1, LOWEST));
+  MockWrite writes[] = {
+    CreateMockWrite(*compressed),
+  };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(ERR_SYN_REPLY_NOT_RECEIVED, out.rv);
+  data->Reset();
+
+  EnableCompression(false);
+}
+
+// Test that the NetLog contains good data for a simple GET request.
+TEST_F(SpdyNetworkTransactionTest, NetLog) {
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  net::CapturingBoundNetLog log(net::CapturingNetLog::kUnbounded);
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     log.bound());
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+
+  // Check that the NetLog was filled reasonably.
+  // This test is intentionally non-specific about the exact ordering of the
+  // log; instead we just check to make sure that certain events exist, and that
+  // they are in the right order.
+  EXPECT_LT(0u, log.entries().size());
+  int pos = 0;
+  pos = net::ExpectLogContainsSomewhere(log.entries(), 0,
+      net::NetLog::TYPE_SPDY_TRANSACTION_SEND_REQUEST,
+      net::NetLog::PHASE_BEGIN);
+  pos = net::ExpectLogContainsSomewhere(log.entries(), pos + 1,
+      net::NetLog::TYPE_SPDY_TRANSACTION_SEND_REQUEST,
+      net::NetLog::PHASE_END);
+  pos = net::ExpectLogContainsSomewhere(log.entries(), pos + 1,
+      net::NetLog::TYPE_SPDY_TRANSACTION_READ_HEADERS,
+      net::NetLog::PHASE_BEGIN);
+  pos = net::ExpectLogContainsSomewhere(log.entries(), pos + 1,
+      net::NetLog::TYPE_SPDY_TRANSACTION_READ_HEADERS,
+      net::NetLog::PHASE_END);
+  pos = net::ExpectLogContainsSomewhere(log.entries(), pos + 1,
+      net::NetLog::TYPE_SPDY_TRANSACTION_READ_BODY,
+      net::NetLog::PHASE_BEGIN);
+  pos = net::ExpectLogContainsSomewhere(log.entries(), pos + 1,
+      net::NetLog::TYPE_SPDY_TRANSACTION_READ_BODY,
+      net::NetLog::PHASE_END);
+}
+
+// Since we buffer the IO from the stream to the renderer, this test verifies
+// that when we read out the maximum amount of data (e.g. we received 50 bytes
+// on the network, but issued a Read for only 5 of those bytes) that the data
+// flow still works correctly.
+TEST_F(SpdyNetworkTransactionTest, BufferFull) {
+  spdy::SpdyFramer framer;
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  // 2 data frames in a single read.
+  scoped_ptr<spdy::SpdyFrame> data_frame_1(
+      framer.CreateDataFrame(1, "goodby", 6, spdy::DATA_FLAG_NONE));
+  scoped_ptr<spdy::SpdyFrame> data_frame_2(
+      framer.CreateDataFrame(1, "e worl", 6, spdy::DATA_FLAG_NONE));
+  const spdy::SpdyFrame* data_frames[2] = {
+    data_frame_1.get(),
+    data_frame_2.get(),
+  };
+  char combined_data_frames[100];
+  int combined_data_frames_len =
+      CombineFrames(data_frames, arraysize(data_frames),
+                    combined_data_frames, arraysize(combined_data_frames));
+  scoped_ptr<spdy::SpdyFrame> last_frame(
+      framer.CreateDataFrame(1, "d", 1, spdy::DATA_FLAG_FIN));
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    MockRead(true, ERR_IO_PENDING),  // Force a pause
+    MockRead(true, combined_data_frames, combined_data_frames_len),
+    MockRead(true, ERR_IO_PENDING),  // Force a pause
+    CreateMockRead(*last_frame),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+
+  TestCompletionCallback callback;
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.AddData(data.get());
+  helper.RunPreTestSetup();
+  HttpNetworkTransaction* trans = helper.trans();
+  int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  TransactionHelperResult out = helper.output();
+  out.rv = callback.WaitForResult();
+  EXPECT_EQ(out.rv, OK);
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  out.status_line = response->headers->GetStatusLine();
+  out.response_info = *response;  // Make a copy so we can verify.
+
+  // Read Data
+  TestCompletionCallback read_callback;
+
+  std::string content;
+  do {
+    // Read small chunks at a time.
+    const int kSmallReadSize = 3;
+    scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSmallReadSize);
+    rv = trans->Read(buf, kSmallReadSize, &read_callback);
+    if (rv == net::ERR_IO_PENDING) {
+      data->CompleteRead();
+      rv = read_callback.WaitForResult();
+    }
+    if (rv > 0) {
+      content.append(buf->data(), rv);
+    } else if (rv < 0) {
+      NOTREACHED();
+    }
+  } while (rv > 0);
+
+  out.response_data.swap(content);
+
+  // Flush the MessageLoop while the SpdySessionDependencies (in particular, the
+  // MockClientSocketFactory) are still alive.
+  MessageLoop::current()->RunAllPending();
+
+  // Verify that we consumed all test data.
+  helper.VerifyDataConsumed();
+
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("goodbye world", out.response_data);
+}
+
+TEST_F(SpdyNetworkTransactionTest, ConnectFailureFallbackToHttp) {
+  MockConnect connects[]  = {
+    MockConnect(true, ERR_NAME_NOT_RESOLVED),
+    MockConnect(false, ERR_NAME_NOT_RESOLVED),
+    MockConnect(true, ERR_INTERNET_DISCONNECTED),
+    MockConnect(false, ERR_INTERNET_DISCONNECTED)
+  };
+
+  for (size_t index = 0; index < arraysize(connects); ++index) {
+    scoped_ptr<spdy::SpdyFrame> req(
+        ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+    MockWrite writes[] = {
+      CreateMockWrite(*req),
+      MockWrite(true, 0, 0)  // EOF
+    };
+
+    scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+    scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+    MockRead reads[] = {
+      CreateMockRead(*resp),
+      CreateMockRead(*body),
+      MockRead(true, 0, 0)  // EOF
+    };
+
+    scoped_refptr<DelayedSocketData> data(
+        new DelayedSocketData(connects[index], 1, reads, arraysize(reads),
+                              writes, arraysize(writes)));
+    NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                       BoundNetLog());
+    helper.AddData(data.get());
+    helper.RunPreTestSetup();
+
+    // Set up http fallback data.
+    MockRead http_fallback_data[] = {
+      MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+      MockRead("hello world!!!"),
+      MockRead(true, OK),
+    };
+
+    scoped_ptr<StaticSocketDataProvider> http_fallback(
+          new StaticSocketDataProvider(http_fallback_data,
+                                       arraysize(http_fallback_data),
+                                       NULL, 0));
+    helper.AddDataNoSSL(http_fallback.get());
+    HttpNetworkTransaction* trans = helper.trans();
+    TestCompletionCallback callback;
+
+    int rv = trans->Start(&helper.request(), &callback, BoundNetLog());
+    EXPECT_EQ(rv, ERR_IO_PENDING);
+    rv = callback.WaitForResult();
+    const HttpResponseInfo* response = trans->GetResponseInfo();
+    ASSERT_TRUE(response != NULL);
+    ASSERT_TRUE(response->headers != NULL);
+    EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+    std::string response_data;
+    rv = ReadTransaction(trans, &response_data);
+    EXPECT_EQ(OK, rv);
+
+    EXPECT_TRUE(!response->was_fetched_via_spdy);
+    EXPECT_TRUE(!response->was_npn_negotiated);
+    EXPECT_TRUE(response->was_alternate_protocol_available);
+    EXPECT_TRUE(http_fallback->at_read_eof());
+    EXPECT_EQ(0u, data->read_index());
+    EXPECT_EQ(0u, data->write_index());
+    EXPECT_EQ("hello world!!!", response_data);
+  }
+}
+
+// Verify that basic buffering works; when multiple data frames arrive
+// at the same time, ensure that we don't notify a read completion for
+// each data frame individually.
+TEST_F(SpdyNetworkTransactionTest, Buffering) {
+  spdy::SpdyFramer framer;
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  // 4 data frames in a single read.
+  scoped_ptr<spdy::SpdyFrame> data_frame(
+      framer.CreateDataFrame(1, "message", 7, spdy::DATA_FLAG_NONE));
+  scoped_ptr<spdy::SpdyFrame> data_frame_fin(
+      framer.CreateDataFrame(1, "message", 7, spdy::DATA_FLAG_FIN));
+  const spdy::SpdyFrame* data_frames[4] = {
+    data_frame.get(),
+    data_frame.get(),
+    data_frame.get(),
+    data_frame_fin.get()
+  };
+  char combined_data_frames[100];
+  int combined_data_frames_len =
+      CombineFrames(data_frames, arraysize(data_frames),
+                    combined_data_frames, arraysize(combined_data_frames));
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    MockRead(true, ERR_IO_PENDING),  // Force a pause
+    MockRead(true, combined_data_frames, combined_data_frames_len),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.AddData(data.get());
+  helper.RunPreTestSetup();
+  HttpNetworkTransaction* trans = helper.trans();
+
+  TestCompletionCallback callback;
+  int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  TransactionHelperResult out = helper.output();
+  out.rv = callback.WaitForResult();
+  EXPECT_EQ(out.rv, OK);
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  out.status_line = response->headers->GetStatusLine();
+  out.response_info = *response;  // Make a copy so we can verify.
+
+  // Read Data
+  TestCompletionCallback read_callback;
+
+  std::string content;
+  int reads_completed = 0;
+  do {
+    // Read small chunks at a time.
+    const int kSmallReadSize = 14;
+    scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSmallReadSize);
+    rv = trans->Read(buf, kSmallReadSize, &read_callback);
+    if (rv == net::ERR_IO_PENDING) {
+      data->CompleteRead();
+      rv = read_callback.WaitForResult();
+    }
+    if (rv > 0) {
+      EXPECT_EQ(kSmallReadSize, rv);
+      content.append(buf->data(), rv);
+    } else if (rv < 0) {
+      FAIL() << "Unexpected read error: " << rv;
+    }
+    reads_completed++;
+  } while (rv > 0);
+
+  EXPECT_EQ(3, reads_completed);  // Reads are: 14 bytes, 14 bytes, 0 bytes.
+
+  out.response_data.swap(content);
+
+  // Flush the MessageLoop while the SpdySessionDependencies (in particular, the
+  // MockClientSocketFactory) are still alive.
+  MessageLoop::current()->RunAllPending();
+
+  // Verify that we consumed all test data.
+  helper.VerifyDataConsumed();
+
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("messagemessagemessagemessage", out.response_data);
+}
+
+// Verify the case where we buffer data but read it after it has been buffered.
+TEST_F(SpdyNetworkTransactionTest, BufferedAll) {
+  spdy::SpdyFramer framer;
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  // 5 data frames in a single read.
+  scoped_ptr<spdy::SpdyFrame> syn_reply(
+      ConstructSpdyGetSynReply(NULL, 0, 1));
+  syn_reply->set_flags(spdy::CONTROL_FLAG_NONE);  // turn off FIN bit
+  scoped_ptr<spdy::SpdyFrame> data_frame(
+      framer.CreateDataFrame(1, "message", 7, spdy::DATA_FLAG_NONE));
+  scoped_ptr<spdy::SpdyFrame> data_frame_fin(
+      framer.CreateDataFrame(1, "message", 7, spdy::DATA_FLAG_FIN));
+  const spdy::SpdyFrame* frames[5] = {
+    syn_reply.get(),
+    data_frame.get(),
+    data_frame.get(),
+    data_frame.get(),
+    data_frame_fin.get()
+  };
+  char combined_frames[200];
+  int combined_frames_len =
+      CombineFrames(frames, arraysize(frames),
+                    combined_frames, arraysize(combined_frames));
+
+  MockRead reads[] = {
+    MockRead(true, combined_frames, combined_frames_len),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.AddData(data.get());
+  helper.RunPreTestSetup();
+  HttpNetworkTransaction* trans = helper.trans();
+
+  TestCompletionCallback callback;
+  int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  TransactionHelperResult out = helper.output();
+  out.rv = callback.WaitForResult();
+  EXPECT_EQ(out.rv, OK);
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  out.status_line = response->headers->GetStatusLine();
+  out.response_info = *response;  // Make a copy so we can verify.
+
+  // Read Data
+  TestCompletionCallback read_callback;
+
+  std::string content;
+  int reads_completed = 0;
+  do {
+    // Read small chunks at a time.
+    const int kSmallReadSize = 14;
+    scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSmallReadSize);
+    rv = trans->Read(buf, kSmallReadSize, &read_callback);
+    if (rv > 0) {
+      EXPECT_EQ(kSmallReadSize, rv);
+      content.append(buf->data(), rv);
+    } else if (rv < 0) {
+      FAIL() << "Unexpected read error: " << rv;
+    }
+    reads_completed++;
+  } while (rv > 0);
+
+  EXPECT_EQ(3, reads_completed);
+
+  out.response_data.swap(content);
+
+  // Flush the MessageLoop while the SpdySessionDependencies (in particular, the
+  // MockClientSocketFactory) are still alive.
+  MessageLoop::current()->RunAllPending();
+
+  // Verify that we consumed all test data.
+  helper.VerifyDataConsumed();
+
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("messagemessagemessagemessage", out.response_data);
+}
+
+// Verify the case where we buffer data and close the connection.
+TEST_F(SpdyNetworkTransactionTest, BufferedClosed) {
+  spdy::SpdyFramer framer;
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  // All data frames in a single read.
+  // NOTE: We don't FIN the stream.
+  scoped_ptr<spdy::SpdyFrame> data_frame(
+      framer.CreateDataFrame(1, "message", 7, spdy::DATA_FLAG_NONE));
+  const spdy::SpdyFrame* data_frames[4] = {
+    data_frame.get(),
+    data_frame.get(),
+    data_frame.get(),
+    data_frame.get()
+  };
+  char combined_data_frames[100];
+  int combined_data_frames_len =
+      CombineFrames(data_frames, arraysize(data_frames),
+                    combined_data_frames, arraysize(combined_data_frames));
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    MockRead(true, ERR_IO_PENDING),  // Force a wait
+    MockRead(true, combined_data_frames, combined_data_frames_len),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.AddData(data.get());
+  helper.RunPreTestSetup();
+  HttpNetworkTransaction* trans = helper.trans();
+
+  TestCompletionCallback callback;
+
+  int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  TransactionHelperResult out = helper.output();
+  out.rv = callback.WaitForResult();
+  EXPECT_EQ(out.rv, OK);
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  out.status_line = response->headers->GetStatusLine();
+  out.response_info = *response;  // Make a copy so we can verify.
+
+  // Read Data
+  TestCompletionCallback read_callback;
+
+  std::string content;
+  int reads_completed = 0;
+  do {
+    // Read small chunks at a time.
+    const int kSmallReadSize = 14;
+    scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kSmallReadSize);
+    rv = trans->Read(buf, kSmallReadSize, &read_callback);
+    if (rv == net::ERR_IO_PENDING) {
+      data->CompleteRead();
+      rv = read_callback.WaitForResult();
+    }
+    if (rv > 0) {
+      content.append(buf->data(), rv);
+    } else if (rv < 0) {
+      // This test intentionally closes the connection, and will get an error.
+      EXPECT_EQ(ERR_CONNECTION_CLOSED, rv);
+      break;
+    }
+    reads_completed++;
+  } while (rv > 0);
+
+  EXPECT_EQ(0, reads_completed);
+
+  out.response_data.swap(content);
+
+  // Flush the MessageLoop while the SpdySessionDependencies (in particular, the
+  // MockClientSocketFactory) are still alive.
+  MessageLoop::current()->RunAllPending();
+
+  // Verify that we consumed all test data.
+  helper.VerifyDataConsumed();
+}
+
+// Verify the case where we buffer data and cancel the transaction.
+TEST_F(SpdyNetworkTransactionTest, BufferedCancelled) {
+  spdy::SpdyFramer framer;
+
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  // NOTE: We don't FIN the stream.
+  scoped_ptr<spdy::SpdyFrame> data_frame(
+      framer.CreateDataFrame(1, "message", 7, spdy::DATA_FLAG_NONE));
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    MockRead(true, ERR_IO_PENDING),  // Force a wait
+    CreateMockRead(*data_frame),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.AddData(data.get());
+  helper.RunPreTestSetup();
+  HttpNetworkTransaction* trans = helper.trans();
+  TestCompletionCallback callback;
+
+  int rv = trans->Start(&CreateGetRequest(), &callback, BoundNetLog());
+  EXPECT_EQ(ERR_IO_PENDING, rv);
+
+  TransactionHelperResult out = helper.output();
+  out.rv = callback.WaitForResult();
+  EXPECT_EQ(out.rv, OK);
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  out.status_line = response->headers->GetStatusLine();
+  out.response_info = *response;  // Make a copy so we can verify.
+
+  // Read Data
+  TestCompletionCallback read_callback;
+
+  do {
+    const int kReadSize = 256;
+    scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(kReadSize);
+    rv = trans->Read(buf, kReadSize, &read_callback);
+    if (rv == net::ERR_IO_PENDING) {
+      // Complete the read now, which causes buffering to start.
+      data->CompleteRead();
+      // Destroy the transaction, causing the stream to get cancelled
+      // and orphaning the buffered IO task.
+      helper.ResetTrans();
+      break;
+    }
+    // We shouldn't get here in this test.
+    FAIL() << "Unexpected read: " << rv;
+  } while (rv > 0);
+
+  // Flush the MessageLoop; this will cause the buffered IO task
+  // to run for the final time.
+  MessageLoop::current()->RunAllPending();
+
+  // Verify that we consumed all test data.
+  helper.VerifyDataConsumed();
+}
+
+// Test that if the server requests persistence of settings, that we save
+// the settings in the SpdySettingsStorage.
+TEST_F(SpdyNetworkTransactionTest, SettingsSaved) {
+  static const SpdyHeaderInfo kSynReplyInfo = {
+    spdy::SYN_REPLY,                              // Syn Reply
+    1,                                            // Stream ID
+    0,                                            // Associated Stream ID
+    SPDY_PRIORITY_LOWEST,                         // Priority
+    spdy::CONTROL_FLAG_NONE,                      // Control Flags
+    false,                                        // Compressed
+    spdy::INVALID,                                // Status
+    NULL,                                         // Data
+    0,                                            // Data Length
+    spdy::DATA_FLAG_NONE                          // Data Flags
+  };
+  static const char* const kExtraHeaders[] = {
+    "status",   "200",
+    "version",  "HTTP/1.1"
+  };
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+
+  // Verify that no settings exist initially.
+  HostPortPair host_port_pair("www.google.com", 443);
+  EXPECT_TRUE(helper.session()->spdy_settings().Get(host_port_pair).empty());
+
+  // Construct the request.
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  // Construct the reply.
+  scoped_ptr<spdy::SpdyFrame> reply(
+    ConstructSpdyPacket(kSynReplyInfo,
+                        kExtraHeaders,
+                        arraysize(kExtraHeaders) / 2,
+                        NULL,
+                        0));
+
+  unsigned int kSampleId1 = 0x1;
+  unsigned int kSampleValue1 = 0x0a0a0a0a;
+  unsigned int kSampleId2 = 0x2;
+  unsigned int kSampleValue2 = 0x0b0b0b0b;
+  unsigned int kSampleId3 = 0xababab;
+  unsigned int kSampleValue3 = 0x0c0c0c0c;
+  scoped_ptr<spdy::SpdyFrame> settings_frame;
+  {
+    // Construct the SETTINGS frame.
+    spdy::SpdySettings settings;
+    spdy::SettingsFlagsAndId setting(0);
+    // First add a persisted setting
+    setting.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+    setting.set_id(kSampleId1);
+    settings.push_back(std::make_pair(setting, kSampleValue1));
+    // Next add a non-persisted setting
+    setting.set_flags(0);
+    setting.set_id(kSampleId2);
+    settings.push_back(std::make_pair(setting, kSampleValue2));
+    // Next add another persisted setting
+    setting.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+    setting.set_id(kSampleId3);
+    settings.push_back(std::make_pair(setting, kSampleValue3));
+    settings_frame.reset(ConstructSpdySettings(settings));
+  }
+
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockRead reads[] = {
+    CreateMockRead(*reply),
+    CreateMockRead(*body),
+    CreateMockRead(*settings_frame),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+
+  {
+    // Verify we had two persisted settings.
+    spdy::SpdySettings saved_settings =
+        helper.session()->spdy_settings().Get(host_port_pair);
+    ASSERT_EQ(2u, saved_settings.size());
+
+    // Verify the first persisted setting.
+    spdy::SpdySetting setting = saved_settings.front();
+    saved_settings.pop_front();
+    EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, setting.first.flags());
+    EXPECT_EQ(kSampleId1, setting.first.id());
+    EXPECT_EQ(kSampleValue1, setting.second);
+
+    // Verify the second persisted setting.
+    setting = saved_settings.front();
+    saved_settings.pop_front();
+    EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, setting.first.flags());
+    EXPECT_EQ(kSampleId3, setting.first.id());
+    EXPECT_EQ(kSampleValue3, setting.second);
+  }
+}
+
+// Test that when there are settings saved that they are sent back to the
+// server upon session establishment.
+TEST_F(SpdyNetworkTransactionTest, SettingsPlayback) {
+  static const SpdyHeaderInfo kSynReplyInfo = {
+    spdy::SYN_REPLY,                              // Syn Reply
+    1,                                            // Stream ID
+    0,                                            // Associated Stream ID
+    SPDY_PRIORITY_LOWEST,                         // Priority
+    spdy::CONTROL_FLAG_NONE,                      // Control Flags
+    false,                                        // Compressed
+    spdy::INVALID,                                // Status
+    NULL,                                         // Data
+    0,                                            // Data Length
+    spdy::DATA_FLAG_NONE                          // Data Flags
+  };
+  static const char* kExtraHeaders[] = {
+    "status",   "200",
+    "version",  "HTTP/1.1"
+  };
+
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+
+  // Verify that no settings exist initially.
+  HostPortPair host_port_pair("www.google.com", 443);
+  EXPECT_TRUE(helper.session()->spdy_settings().Get(host_port_pair).empty());
+
+  unsigned int kSampleId1 = 0x1;
+  unsigned int kSampleValue1 = 0x0a0a0a0a;
+  unsigned int kSampleId2 = 0xababab;
+  unsigned int kSampleValue2 = 0x0c0c0c0c;
+  // Manually insert settings into the SpdySettingsStorage here.
+  {
+    spdy::SpdySettings settings;
+    spdy::SettingsFlagsAndId setting(0);
+    // First add a persisted setting
+    setting.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+    setting.set_id(kSampleId1);
+    settings.push_back(std::make_pair(setting, kSampleValue1));
+    // Next add another persisted setting
+    setting.set_flags(spdy::SETTINGS_FLAG_PLEASE_PERSIST);
+    setting.set_id(kSampleId2);
+    settings.push_back(std::make_pair(setting, kSampleValue2));
+
+    helper.session()->mutable_spdy_settings()->Set(host_port_pair, settings);
+  }
+
+  EXPECT_EQ(2u, helper.session()->spdy_settings().Get(host_port_pair).size());
+
+  // Construct the SETTINGS frame.
+  const spdy::SpdySettings& settings =
+      helper.session()->spdy_settings().Get(host_port_pair);
+  scoped_ptr<spdy::SpdyFrame> settings_frame(ConstructSpdySettings(settings));
+
+  // Construct the request.
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+
+  MockWrite writes[] = {
+    CreateMockWrite(*settings_frame),
+    CreateMockWrite(*req),
+  };
+
+  // Construct the reply.
+  scoped_ptr<spdy::SpdyFrame> reply(
+    ConstructSpdyPacket(kSynReplyInfo,
+                        kExtraHeaders,
+                        arraysize(kExtraHeaders) / 2,
+                        NULL,
+                        0));
+
+  scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+  MockRead reads[] = {
+    CreateMockRead(*reply),
+    CreateMockRead(*body),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(2, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(OK, out.rv);
+  EXPECT_EQ("HTTP/1.1 200 OK", out.status_line);
+  EXPECT_EQ("hello!", out.response_data);
+
+  {
+    // Verify we had two persisted settings.
+    spdy::SpdySettings saved_settings =
+        helper.session()->spdy_settings().Get(host_port_pair);
+    ASSERT_EQ(2u, saved_settings.size());
+
+    // Verify the first persisted setting.
+    spdy::SpdySetting setting = saved_settings.front();
+    saved_settings.pop_front();
+    EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, setting.first.flags());
+    EXPECT_EQ(kSampleId1, setting.first.id());
+    EXPECT_EQ(kSampleValue1, setting.second);
+
+    // Verify the second persisted setting.
+    setting = saved_settings.front();
+    saved_settings.pop_front();
+    EXPECT_EQ(spdy::SETTINGS_FLAG_PERSISTED, setting.first.flags());
+    EXPECT_EQ(kSampleId2, setting.first.id());
+    EXPECT_EQ(kSampleValue2, setting.second);
+  }
+}
+
+TEST_F(SpdyNetworkTransactionTest, GoAwayWithActiveStream) {
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  scoped_ptr<spdy::SpdyFrame> go_away(ConstructSpdyGoAway());
+  MockRead reads[] = {
+    CreateMockRead(*go_away),
+    MockRead(true, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     BoundNetLog());
+  helper.RunToCompletion(data.get());
+  TransactionHelperResult out = helper.output();
+  EXPECT_EQ(ERR_CONNECTION_CLOSED, out.rv);
+}
+
+TEST_F(SpdyNetworkTransactionTest, CloseWithActiveStream) {
+  scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST));
+  MockWrite writes[] = { CreateMockWrite(*req) };
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    MockRead(false, 0, 0)  // EOF
+  };
+
+  scoped_refptr<DelayedSocketData> data(
+      new DelayedSocketData(1, reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  BoundNetLog log;
+  NormalSpdyTransactionHelper helper(CreateGetRequest(),
+                                     log);
+  helper.AddData(data.get());
+  helper.RunPreTestSetup();
+  HttpNetworkTransaction* trans = helper.trans();
+
+  TestCompletionCallback callback;
+  TransactionHelperResult out;
+  out.rv = trans->Start(&CreateGetRequest(), &callback, log);
+
+  EXPECT_EQ(out.rv, ERR_IO_PENDING);
+  out.rv = callback.WaitForResult();
+  EXPECT_EQ(out.rv, OK);
+
+  const HttpResponseInfo* response = trans->GetResponseInfo();
+  EXPECT_TRUE(response->headers != NULL);
+  EXPECT_TRUE(response->was_fetched_via_spdy);
+  out.rv = ReadTransaction(trans, &out.response_data);
+  EXPECT_EQ(ERR_CONNECTION_CLOSED, out.rv);
+
+  // Verify that we consumed all test data.
+  helper.VerifyDataConsumed();
+}
+
+}  // namespace net
diff --git a/net/spdy/spdy_protocol.h b/net/spdy/spdy_protocol.h
new file mode 100644
index 0000000..810d5b3
--- /dev/null
+++ b/net/spdy/spdy_protocol.h
@@ -0,0 +1,664 @@
+// Copyright (c) 2010 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.
+
+// This file contains some protocol structures for use with Spdy.
+
+#ifndef NET_SPDY_SPDY_PROTOCOL_H_
+#define NET_SPDY_SPDY_PROTOCOL_H_
+
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "net/spdy/spdy_bitmasks.h"
+
+//  Data Frame Format
+//  +----------------------------------+
+//  |0|       Stream-ID (31bits)       |
+//  +----------------------------------+
+//  | flags (8)  |  Length (24 bits)   |
+//  +----------------------------------+
+//  |               Data               |
+//  +----------------------------------+
+//
+//  Control Frame Format
+//  +----------------------------------+
+//  |1| Version(15bits) | Type(16bits) |
+//  +----------------------------------+
+//  | flags (8)  |  Length (24 bits)   |
+//  +----------------------------------+
+//  |               Data               |
+//  +----------------------------------+
+//
+//  Control Frame: SYN_STREAM
+//  +----------------------------------+
+//  |1|000000000000001|0000000000000001|
+//  +----------------------------------+
+//  | flags (8)  |  Length (24 bits)   |  >= 8
+//  +----------------------------------+
+//  |X|       Stream-ID(31bits)        |
+//  +----------------------------------+
+//  |X|Associated-To-Stream-ID (31bits)|
+//  +----------------------------------+
+//  |Pri| unused      | Length (16bits)|
+//  +----------------------------------+
+//
+//  Control Frame: SYN_REPLY
+//  +----------------------------------+
+//  |1|000000000000001|0000000000000010|
+//  +----------------------------------+
+//  | flags (8)  |  Length (24 bits)   |  >= 8
+//  +----------------------------------+
+//  |X|       Stream-ID(31bits)        |
+//  +----------------------------------+
+//  | unused (16 bits)| Length (16bits)|
+//  +----------------------------------+
+//
+//  Control Frame: RST_STREAM
+//  +----------------------------------+
+//  |1|000000000000001|0000000000000011|
+//  +----------------------------------+
+//  | flags (8)  |  Length (24 bits)   |  >= 4
+//  +----------------------------------+
+//  |X|       Stream-ID(31bits)        |
+//  +----------------------------------+
+//  |        Status code (32 bits)     |
+//  +----------------------------------+
+//
+//  Control Frame: SETTINGS
+//  +----------------------------------+
+//  |1|000000000000001|0000000000000100|
+//  +----------------------------------+
+//  | flags (8)  |  Length (24 bits)   |
+//  +----------------------------------+
+//  |        # of entries (32)         |
+//  +----------------------------------+
+//
+//  Control Frame: NOOP
+//  +----------------------------------+
+//  |1|000000000000001|0000000000000101|
+//  +----------------------------------+
+//  | flags (8)  |  Length (24 bits)   | = 0
+//  +----------------------------------+
+//
+//  Control Frame: PING
+//  +----------------------------------+
+//  |1|000000000000001|0000000000000110|
+//  +----------------------------------+
+//  | flags (8)  |  Length (24 bits)   | = 4
+//  +----------------------------------+
+//  |        Unique id (32 bits)       |
+//  +----------------------------------+
+//
+//  Control Frame: GOAWAY
+//  +----------------------------------+
+//  |1|000000000000001|0000000000000111|
+//  +----------------------------------+
+//  | flags (8)  |  Length (24 bits)   | = 4
+//  +----------------------------------+
+//  |X|  Last-accepted-stream-id       |
+//  +----------------------------------+
+//
+//  Control Frame: WINDOW_UPDATE
+//  +----------------------------------+
+//  |1|000000000000001|0000000000001001|
+//  +----------------------------------+
+//  | flags (8)  |  Length (24 bits)   | = 8
+//  +----------------------------------+
+//  |X|      Stream-ID (31 bits)       |
+//  +----------------------------------+
+//  |   Delta-Window-Size (32 bits)    |
+//  +----------------------------------+
+
+
+namespace spdy {
+
+// This implementation of Spdy is version 1.
+const int kSpdyProtocolVersion = 1;
+
+// Default initial window size.
+const int kInitialWindowSize = 64 * 1024;
+
+// Note: all protocol data structures are on-the-wire format.  That means that
+//       data is stored in network-normalized order.  Readers must use the
+//       accessors provided or call ntohX() functions.
+
+// Types of Spdy Control Frames.
+enum SpdyControlType {
+  SYN_STREAM = 1,
+  SYN_REPLY,
+  RST_STREAM,
+  SETTINGS,
+  NOOP,
+  PING,
+  GOAWAY,
+  HEADERS,
+  WINDOW_UPDATE,
+  NUM_CONTROL_FRAME_TYPES
+};
+
+// Flags on data packets.
+enum SpdyDataFlags {
+  DATA_FLAG_NONE = 0,
+  DATA_FLAG_FIN = 1,
+  DATA_FLAG_COMPRESSED = 2
+};
+
+// Flags on control packets
+enum SpdyControlFlags {
+  CONTROL_FLAG_NONE = 0,
+  CONTROL_FLAG_FIN = 1,
+  CONTROL_FLAG_UNIDIRECTIONAL = 2
+};
+
+// Flags on the SETTINGS control frame.
+enum SpdySettingsControlFlags {
+  SETTINGS_FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS = 0x1
+};
+
+// Flags for settings within a SETTINGS frame.
+enum SpdySettingsFlags {
+  SETTINGS_FLAG_PLEASE_PERSIST = 0x1,
+  SETTINGS_FLAG_PERSISTED = 0x2
+};
+
+// List of known settings.
+enum SpdySettingsIds {
+  SETTINGS_UPLOAD_BANDWIDTH = 0x1,
+  SETTINGS_DOWNLOAD_BANDWIDTH = 0x2,
+  SETTINGS_ROUND_TRIP_TIME = 0x3,
+  SETTINGS_MAX_CONCURRENT_STREAMS = 0x4,
+  SETTINGS_CURRENT_CWND = 0x5,
+  // Downstream byte retransmission rate in percentage.
+  SETTINGS_DOWNLOAD_RETRANS_RATE = 0x6
+};
+
+// Status codes, as used in control frames (primarily RST_STREAM).
+enum SpdyStatusCodes {
+  INVALID = 0,
+  PROTOCOL_ERROR = 1,
+  INVALID_STREAM = 2,
+  REFUSED_STREAM = 3,
+  UNSUPPORTED_VERSION = 4,
+  CANCEL = 5,
+  INTERNAL_ERROR = 6,
+  FLOW_CONTROL_ERROR = 7,
+  NUM_STATUS_CODES = 8
+};
+
+// A SPDY stream id is a 31 bit entity.
+typedef uint32 SpdyStreamId;
+
+// A SPDY priority is a number between 0 and 4.
+typedef uint8 SpdyPriority;
+
+// SPDY Priorities. (there are only 2 bits)
+#define SPDY_PRIORITY_LOWEST 3
+#define SPDY_PRIORITY_HIGHEST 0
+
+// -------------------------------------------------------------------------
+// These structures mirror the protocol structure definitions.
+
+// For the control data structures, we pack so that sizes match the
+// protocol over-the-wire sizes.
+#pragma pack(push)
+#pragma pack(1)
+
+// A special structure for the 8 bit flags and 24 bit length fields.
+union FlagsAndLength {
+  uint8 flags_[4];  // 8 bits
+  uint32 length_;   // 24 bits
+};
+
+// The basic SPDY Frame structure.
+struct SpdyFrameBlock {
+  union {
+    struct {
+      uint16 version_;
+      uint16 type_;
+    } control_;
+    struct {
+      SpdyStreamId stream_id_;
+    } data_;
+  };
+  FlagsAndLength flags_length_;
+};
+
+// A SYN_STREAM Control Frame structure.
+struct SpdySynStreamControlFrameBlock : SpdyFrameBlock {
+  SpdyStreamId stream_id_;
+  SpdyStreamId associated_stream_id_;
+  SpdyPriority priority_;
+  uint8 unused_;
+};
+
+// A SYN_REPLY Control Frame structure.
+struct SpdySynReplyControlFrameBlock : SpdyFrameBlock {
+  SpdyStreamId stream_id_;
+  uint16 unused_;
+};
+
+// A RST_STREAM Control Frame structure.
+struct SpdyRstStreamControlFrameBlock : SpdyFrameBlock {
+  SpdyStreamId stream_id_;
+  uint32 status_;
+};
+
+// A GOAWAY Control Frame structure.
+struct SpdyGoAwayControlFrameBlock : SpdyFrameBlock {
+  SpdyStreamId last_accepted_stream_id_;
+};
+
+// A structure for the 8 bit flags and 24 bit ID fields.
+union SettingsFlagsAndId {
+  uint8 flags_[4];  // 8 bits
+  uint32 id_;       // 24 bits
+
+  SettingsFlagsAndId(uint32 val) : id_(val) {};
+  uint8 flags() const { return flags_[0]; }
+  void set_flags(uint8 flags) { flags_[0] = flags; }
+  uint32 id() const { return (ntohl(id_) & kSettingsIdMask); };
+  void set_id(uint32 id) {
+    DCHECK_EQ(0u, (id & ~kSettingsIdMask));
+    id = htonl(id & kSettingsIdMask);
+    id_ = flags() | id;
+  }
+};
+
+// A SETTINGS Control Frame structure.
+struct SpdySettingsControlFrameBlock : SpdyFrameBlock {
+  uint32 num_entries_;
+  // Variable data here.
+};
+
+// A WINDOW_UPDATE Control Frame structure
+struct SpdyWindowUpdateControlFrameBlock : SpdyFrameBlock {
+  SpdyStreamId stream_id_;
+  uint32 delta_window_size_;
+};
+
+#pragma pack(pop)
+
+// -------------------------------------------------------------------------
+// Wrapper classes for various SPDY frames.
+
+// All Spdy Frame types derive from this SpdyFrame class.
+class SpdyFrame {
+ public:
+  // Create a SpdyFrame for a given sized buffer.
+  explicit SpdyFrame(size_t size) : frame_(NULL), owns_buffer_(true) {
+    DCHECK_GE(size, sizeof(struct SpdyFrameBlock));
+    char* buffer = new char[size];
+    memset(buffer, 0, size);
+    frame_ = reinterpret_cast<struct SpdyFrameBlock*>(buffer);
+  }
+
+  // Create a SpdyFrame using a pre-created buffer.
+  // If |owns_buffer| is true, this class takes ownership of the buffer
+  // and will delete it on cleanup.  The buffer must have been created using
+  // new char[].
+  // If |owns_buffer| is false, the caller retains ownership of the buffer and
+  // is responsible for making sure the buffer outlives this frame.  In other
+  // words, this class does NOT create a copy of the buffer.
+  SpdyFrame(char* data, bool owns_buffer)
+      : frame_(reinterpret_cast<struct SpdyFrameBlock*>(data)),
+        owns_buffer_(owns_buffer) {
+    DCHECK(frame_);
+  }
+
+  ~SpdyFrame() {
+    if (owns_buffer_) {
+      char* buffer = reinterpret_cast<char*>(frame_);
+      delete [] buffer;
+    }
+    frame_ = NULL;
+  }
+
+  // Provides access to the frame bytes, which is a buffer containing
+  // the frame packed as expected for sending over the wire.
+  char* data() const { return reinterpret_cast<char*>(frame_); }
+
+  uint8 flags() const { return frame_->flags_length_.flags_[0]; }
+  void set_flags(uint8 flags) { frame_->flags_length_.flags_[0] = flags; }
+
+  uint32 length() const {
+    return ntohl(frame_->flags_length_.length_) & kLengthMask;
+  }
+
+  void set_length(uint32 length) {
+    DCHECK_EQ(0u, (length & ~kLengthMask));
+    length = htonl(length & kLengthMask);
+    frame_->flags_length_.length_ = flags() | length;
+  }
+
+  bool is_control_frame() const {
+    return (ntohs(frame_->control_.version_) & kControlFlagMask) ==
+        kControlFlagMask;
+  }
+
+  // Returns the size of the SpdyFrameBlock structure.
+  // Every SpdyFrame* class has a static size() method for accessing
+  // the size of the data structure which will be sent over the wire.
+  // Note:  this is not the same as sizeof(SpdyFrame).
+  static size_t size() { return sizeof(struct SpdyFrameBlock); }
+
+ protected:
+  SpdyFrameBlock* frame_;
+
+ private:
+  bool owns_buffer_;
+  DISALLOW_COPY_AND_ASSIGN(SpdyFrame);
+};
+
+// A Data Frame.
+class SpdyDataFrame : public SpdyFrame {
+ public:
+  SpdyDataFrame() : SpdyFrame(size()) {}
+  SpdyDataFrame(char* data, bool owns_buffer)
+      : SpdyFrame(data, owns_buffer) {}
+
+  SpdyStreamId stream_id() const {
+    return ntohl(frame_->data_.stream_id_) & kStreamIdMask;
+  }
+
+  // Note that setting the stream id sets the control bit to false.
+  // As stream id should always be set, this means the control bit
+  // should always be set correctly.
+  void set_stream_id(SpdyStreamId id) {
+    DCHECK_EQ(0u, (id & ~kStreamIdMask));
+    frame_->data_.stream_id_ = htonl(id & kStreamIdMask);
+  }
+
+  // Returns the size of the SpdyFrameBlock structure.
+  // Note: this is not the size of the SpdyDataFrame class.
+  static size_t size() { return SpdyFrame::size(); }
+
+  const char* payload() const {
+    return reinterpret_cast<const char*>(frame_) + size();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SpdyDataFrame);
+};
+
+// A Control Frame.
+class SpdyControlFrame : public SpdyFrame {
+ public:
+  explicit SpdyControlFrame(size_t size) : SpdyFrame(size) {}
+  SpdyControlFrame(char* data, bool owns_buffer)
+      : SpdyFrame(data, owns_buffer) {}
+
+  // Callers can use this method to check if the frame appears to be a valid
+  // frame.  Does not guarantee that there are no errors.
+  bool AppearsToBeAValidControlFrame() const {
+    // Right now we only check if the frame has an out-of-bounds type.
+    uint16 type = ntohs(block()->control_.type_);
+    return (type >= SYN_STREAM && type < NUM_CONTROL_FRAME_TYPES);
+  }
+
+  uint16 version() const {
+    const int kVersionMask = 0x7fff;
+    return ntohs(block()->control_.version_) & kVersionMask;
+  }
+
+  void set_version(uint16 version) {
+    DCHECK_EQ(0u, version & kControlFlagMask);
+    mutable_block()->control_.version_ = htons(kControlFlagMask | version);
+  }
+
+  SpdyControlType type() const {
+    uint16 type = ntohs(block()->control_.type_);
+    DCHECK(type >= SYN_STREAM && type < NUM_CONTROL_FRAME_TYPES);
+    return static_cast<SpdyControlType>(type);
+  }
+
+  void set_type(SpdyControlType type) {
+    DCHECK(type >= SYN_STREAM && type < NUM_CONTROL_FRAME_TYPES);
+    mutable_block()->control_.type_ = htons(type);
+  }
+
+  // Returns the size of the SpdyFrameBlock structure.
+  // Note: this is not the size of the SpdyControlFrame class.
+  static size_t size() { return sizeof(SpdyFrameBlock); }
+
+ private:
+  const struct SpdyFrameBlock* block() const {
+    return frame_;
+  }
+  struct SpdyFrameBlock* mutable_block() {
+    return frame_;
+  }
+  DISALLOW_COPY_AND_ASSIGN(SpdyControlFrame);
+};
+
+// A SYN_STREAM frame.
+class SpdySynStreamControlFrame : public SpdyControlFrame {
+ public:
+  SpdySynStreamControlFrame() : SpdyControlFrame(size()) {}
+  SpdySynStreamControlFrame(char* data, bool owns_buffer)
+      : SpdyControlFrame(data, owns_buffer) {}
+
+  SpdyStreamId stream_id() const {
+    return ntohl(block()->stream_id_) & kStreamIdMask;
+  }
+
+  void set_stream_id(SpdyStreamId id) {
+    mutable_block()->stream_id_ = htonl(id & kStreamIdMask);
+  }
+
+  SpdyStreamId associated_stream_id() const {
+    return ntohl(block()->associated_stream_id_) & kStreamIdMask;
+  }
+
+  void set_associated_stream_id(SpdyStreamId id) {
+    mutable_block()->associated_stream_id_ = htonl(id & kStreamIdMask);
+  }
+
+  SpdyPriority priority() const {
+    return (block()->priority_ & kPriorityMask) >> 6;
+  }
+
+  // The number of bytes in the header block beyond the frame header length.
+  int header_block_len() const {
+    return length() - (size() - SpdyFrame::size());
+  }
+
+  const char* header_block() const {
+    return reinterpret_cast<const char*>(block()) + size();
+  }
+
+  // Returns the size of the SpdySynStreamControlFrameBlock structure.
+  // Note: this is not the size of the SpdySynStreamControlFrame class.
+  static size_t size() { return sizeof(SpdySynStreamControlFrameBlock); }
+
+ private:
+  const struct SpdySynStreamControlFrameBlock* block() const {
+    return static_cast<SpdySynStreamControlFrameBlock*>(frame_);
+  }
+  struct SpdySynStreamControlFrameBlock* mutable_block() {
+    return static_cast<SpdySynStreamControlFrameBlock*>(frame_);
+  }
+  DISALLOW_COPY_AND_ASSIGN(SpdySynStreamControlFrame);
+};
+
+// A SYN_REPLY frame.
+class SpdySynReplyControlFrame : public SpdyControlFrame {
+ public:
+  SpdySynReplyControlFrame() : SpdyControlFrame(size()) {}
+  SpdySynReplyControlFrame(char* data, bool owns_buffer)
+      : SpdyControlFrame(data, owns_buffer) {}
+
+  SpdyStreamId stream_id() const {
+    return ntohl(block()->stream_id_) & kStreamIdMask;
+  }
+
+  void set_stream_id(SpdyStreamId id) {
+    mutable_block()->stream_id_ = htonl(id & kStreamIdMask);
+  }
+
+  int header_block_len() const {
+    return length() - (size() - SpdyFrame::size());
+  }
+
+  const char* header_block() const {
+    return reinterpret_cast<const char*>(block()) + size();
+  }
+
+  // Returns the size of the SpdySynReplyControlFrameBlock structure.
+  // Note: this is not the size of the SpdySynReplyControlFrame class.
+  static size_t size() { return sizeof(SpdySynReplyControlFrameBlock); }
+
+ private:
+  const struct SpdySynReplyControlFrameBlock* block() const {
+    return static_cast<SpdySynReplyControlFrameBlock*>(frame_);
+  }
+  struct SpdySynReplyControlFrameBlock* mutable_block() {
+    return static_cast<SpdySynReplyControlFrameBlock*>(frame_);
+  }
+  DISALLOW_COPY_AND_ASSIGN(SpdySynReplyControlFrame);
+};
+
+// A RST_STREAM frame.
+class SpdyRstStreamControlFrame : public SpdyControlFrame {
+ public:
+  SpdyRstStreamControlFrame() : SpdyControlFrame(size()) {}
+  SpdyRstStreamControlFrame(char* data, bool owns_buffer)
+      : SpdyControlFrame(data, owns_buffer) {}
+
+  SpdyStreamId stream_id() const {
+    return ntohl(block()->stream_id_) & kStreamIdMask;
+  }
+
+  void set_stream_id(SpdyStreamId id) {
+    mutable_block()->stream_id_ = htonl(id & kStreamIdMask);
+  }
+
+  SpdyStatusCodes status() const {
+    return static_cast<SpdyStatusCodes>(ntohl(block()->status_));
+  }
+  void set_status(SpdyStatusCodes status) {
+    mutable_block()->status_ = htonl(static_cast<uint32>(status));
+  }
+
+  // Returns the size of the SpdyRstStreamControlFrameBlock structure.
+  // Note: this is not the size of the SpdyRstStreamControlFrame class.
+  static size_t size() { return sizeof(SpdyRstStreamControlFrameBlock); }
+
+ private:
+  const struct SpdyRstStreamControlFrameBlock* block() const {
+    return static_cast<SpdyRstStreamControlFrameBlock*>(frame_);
+  }
+  struct SpdyRstStreamControlFrameBlock* mutable_block() {
+    return static_cast<SpdyRstStreamControlFrameBlock*>(frame_);
+  }
+  DISALLOW_COPY_AND_ASSIGN(SpdyRstStreamControlFrame);
+};
+
+class SpdyGoAwayControlFrame : public SpdyControlFrame {
+ public:
+  SpdyGoAwayControlFrame() : SpdyControlFrame(size()) {}
+  SpdyGoAwayControlFrame(char* data, bool owns_buffer)
+      : SpdyControlFrame(data, owns_buffer) {}
+
+  SpdyStreamId last_accepted_stream_id() const {
+    return ntohl(block()->last_accepted_stream_id_) & kStreamIdMask;
+  }
+
+  void set_last_accepted_stream_id(SpdyStreamId id) {
+    mutable_block()->last_accepted_stream_id_ = htonl(id & kStreamIdMask);
+  }
+
+  static size_t size() { return sizeof(SpdyGoAwayControlFrameBlock); }
+
+ private:
+  const struct SpdyGoAwayControlFrameBlock* block() const {
+    return static_cast<SpdyGoAwayControlFrameBlock*>(frame_);
+  }
+  struct SpdyGoAwayControlFrameBlock* mutable_block() {
+    return static_cast<SpdyGoAwayControlFrameBlock*>(frame_);
+  }
+  DISALLOW_COPY_AND_ASSIGN(SpdyGoAwayControlFrame);
+};
+
+class SpdySettingsControlFrame : public SpdyControlFrame {
+ public:
+  SpdySettingsControlFrame() : SpdyControlFrame(size()) {}
+  SpdySettingsControlFrame(char* data, bool owns_buffer)
+      : SpdyControlFrame(data, owns_buffer) {}
+
+  uint32 num_entries() const {
+    return ntohl(block()->num_entries_);
+  }
+
+  void set_num_entries(int val) {
+    mutable_block()->num_entries_ = htonl(val);
+  }
+
+  int header_block_len() const {
+    return length() - (size() - SpdyFrame::size());
+  }
+
+  const char* header_block() const {
+    return reinterpret_cast<const char*>(block()) + size();
+  }
+
+  // Returns the size of the SpdySettingsControlFrameBlock structure.
+  // Note: this is not the size of the SpdySettingsControlFrameBlock class.
+  static size_t size() { return sizeof(SpdySettingsControlFrameBlock); }
+
+ private:
+  const struct SpdySettingsControlFrameBlock* block() const {
+    return static_cast<SpdySettingsControlFrameBlock*>(frame_);
+  }
+  struct SpdySettingsControlFrameBlock* mutable_block() {
+    return static_cast<SpdySettingsControlFrameBlock*>(frame_);
+  }
+  DISALLOW_COPY_AND_ASSIGN(SpdySettingsControlFrame);
+};
+
+// A WINDOW_UPDATE frame.
+class SpdyWindowUpdateControlFrame : public SpdyControlFrame {
+ public:
+  SpdyWindowUpdateControlFrame() : SpdyControlFrame(size()) {}
+  SpdyWindowUpdateControlFrame(char* data, bool owns_buffer)
+      : SpdyControlFrame(data, owns_buffer) {}
+
+  SpdyStreamId stream_id() const {
+    return ntohl(block()->stream_id_) & kStreamIdMask;
+  }
+
+  void set_stream_id(SpdyStreamId id) {
+    mutable_block()->stream_id_ = htonl(id & kStreamIdMask);
+  }
+
+  uint32 delta_window_size() const {
+    return ntohl(block()->delta_window_size_);
+  }
+
+  void set_delta_window_size(uint32 delta_window_size) {
+    mutable_block()->delta_window_size_ = htonl(delta_window_size);
+  }
+
+  // Returns the size of the SpdyWindowUpdateControlFrameBlock structure.
+  // Note: this is not the size of the SpdyWindowUpdateControlFrame class.
+  static size_t size() { return sizeof(SpdyWindowUpdateControlFrameBlock); }
+
+ private:
+  const struct SpdyWindowUpdateControlFrameBlock* block() const {
+    return static_cast<SpdyWindowUpdateControlFrameBlock*>(frame_);
+  }
+  struct SpdyWindowUpdateControlFrameBlock* mutable_block() {
+    return static_cast<SpdyWindowUpdateControlFrameBlock*>(frame_);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(SpdyWindowUpdateControlFrame);
+};
+
+}  // namespace spdy
+
+#endif  // NET_SPDY_SPDY_PROTOCOL_H_
diff --git a/net/spdy/spdy_protocol_test.cc b/net/spdy/spdy_protocol_test.cc
new file mode 100644
index 0000000..e0b0591
--- /dev/null
+++ b/net/spdy/spdy_protocol_test.cc
@@ -0,0 +1,308 @@
+// Copyright (c) 2010 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/spdy/spdy_protocol.h"
+
+#include "base/scoped_ptr.h"
+#include "net/spdy/spdy_bitmasks.h"
+#include "net/spdy/spdy_framer.h"
+#include "testing/platform_test.h"
+
+using spdy::CONTROL_FLAG_FIN;
+using spdy::CONTROL_FLAG_NONE;
+using spdy::GOAWAY;
+using spdy::HEADERS;
+using spdy::NOOP;
+using spdy::PING;
+using spdy::RST_STREAM;
+using spdy::SETTINGS;
+using spdy::SYN_REPLY;
+using spdy::SYN_STREAM;
+using spdy::WINDOW_UPDATE;
+using spdy::FlagsAndLength;
+using spdy::SpdyControlFrame;
+using spdy::SpdyControlType;
+using spdy::SpdyDataFrame;
+using spdy::SpdyFrame;
+using spdy::SpdyFramer;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdyGoAwayControlFrame;
+using spdy::SpdyRstStreamControlFrame;
+using spdy::SpdySettings;
+using spdy::SpdySettingsControlFrame;
+using spdy::SpdyStatusCodes;
+using spdy::SpdySynReplyControlFrame;
+using spdy::SpdySynStreamControlFrame;
+using spdy::SpdyWindowUpdateControlFrame;
+using spdy::SettingsFlagsAndId;
+using spdy::kLengthMask;
+using spdy::kSpdyProtocolVersion;
+using spdy::kStreamIdMask;
+
+namespace {
+
+// Test our protocol constants
+TEST(SpdyProtocolTest, ProtocolConstants) {
+  EXPECT_EQ(8u, SpdyFrame::size());
+  EXPECT_EQ(8u, SpdyDataFrame::size());
+  EXPECT_EQ(8u, SpdyControlFrame::size());
+  EXPECT_EQ(18u, SpdySynStreamControlFrame::size());
+  EXPECT_EQ(14u, SpdySynReplyControlFrame::size());
+  EXPECT_EQ(16u, SpdyRstStreamControlFrame::size());
+  EXPECT_EQ(12u, SpdyGoAwayControlFrame::size());
+  EXPECT_EQ(12u, SpdySettingsControlFrame::size());
+  EXPECT_EQ(16u, SpdyWindowUpdateControlFrame::size());
+  EXPECT_EQ(4u, sizeof(FlagsAndLength));
+  EXPECT_EQ(1, SYN_STREAM);
+  EXPECT_EQ(2, SYN_REPLY);
+  EXPECT_EQ(3, RST_STREAM);
+  EXPECT_EQ(4, SETTINGS);
+  EXPECT_EQ(5, NOOP);
+  EXPECT_EQ(6, PING);
+  EXPECT_EQ(7, GOAWAY);
+  EXPECT_EQ(8, HEADERS);
+  EXPECT_EQ(9, WINDOW_UPDATE);
+}
+
+// Test some of the protocol helper functions
+TEST(SpdyProtocolTest, FrameStructs) {
+  SpdyFrame frame(SpdyFrame::size());
+  frame.set_length(12345);
+  frame.set_flags(10);
+  EXPECT_EQ(12345u, frame.length());
+  EXPECT_EQ(10u, frame.flags());
+  EXPECT_EQ(false, frame.is_control_frame());
+
+  frame.set_length(0);
+  frame.set_flags(10);
+  EXPECT_EQ(0u, frame.length());
+  EXPECT_EQ(10u, frame.flags());
+  EXPECT_EQ(false, frame.is_control_frame());
+}
+
+TEST(SpdyProtocolTest, DataFrameStructs) {
+  SpdyDataFrame data_frame;
+  data_frame.set_stream_id(12345);
+  EXPECT_EQ(12345u, data_frame.stream_id());
+}
+
+TEST(SpdyProtocolTest, ControlFrameStructs) {
+  SpdyFramer framer;
+  SpdyHeaderBlock headers;
+
+  scoped_ptr<SpdySynStreamControlFrame> syn_frame(
+      framer.CreateSynStream(123, 456, 2, CONTROL_FLAG_FIN, false, &headers));
+  EXPECT_EQ(kSpdyProtocolVersion, syn_frame->version());
+  EXPECT_TRUE(syn_frame->is_control_frame());
+  EXPECT_EQ(SYN_STREAM, syn_frame->type());
+  EXPECT_EQ(123u, syn_frame->stream_id());
+  EXPECT_EQ(456u, syn_frame->associated_stream_id());
+  EXPECT_EQ(2u, syn_frame->priority());
+  EXPECT_EQ(2, syn_frame->header_block_len());
+  EXPECT_EQ(1u, syn_frame->flags());
+  syn_frame->set_associated_stream_id(999u);
+  EXPECT_EQ(123u, syn_frame->stream_id());
+  EXPECT_EQ(999u, syn_frame->associated_stream_id());
+
+  scoped_ptr<SpdySynReplyControlFrame> syn_reply(
+      framer.CreateSynReply(123, CONTROL_FLAG_NONE, false, &headers));
+  EXPECT_EQ(kSpdyProtocolVersion, syn_reply->version());
+  EXPECT_TRUE(syn_reply->is_control_frame());
+  EXPECT_EQ(SYN_REPLY, syn_reply->type());
+  EXPECT_EQ(123u, syn_reply->stream_id());
+  EXPECT_EQ(2, syn_reply->header_block_len());
+  EXPECT_EQ(0, syn_reply->flags());
+
+  scoped_ptr<SpdyRstStreamControlFrame> rst_frame(
+      framer.CreateRstStream(123, spdy::PROTOCOL_ERROR));
+  EXPECT_EQ(kSpdyProtocolVersion, rst_frame->version());
+  EXPECT_TRUE(rst_frame->is_control_frame());
+  EXPECT_EQ(RST_STREAM, rst_frame->type());
+  EXPECT_EQ(123u, rst_frame->stream_id());
+  EXPECT_EQ(spdy::PROTOCOL_ERROR, rst_frame->status());
+  rst_frame->set_status(spdy::INVALID_STREAM);
+  EXPECT_EQ(spdy::INVALID_STREAM, rst_frame->status());
+  EXPECT_EQ(0, rst_frame->flags());
+
+  scoped_ptr<SpdyGoAwayControlFrame> goaway_frame(
+      framer.CreateGoAway(123));
+  EXPECT_EQ(kSpdyProtocolVersion, goaway_frame->version());
+  EXPECT_TRUE(goaway_frame->is_control_frame());
+  EXPECT_EQ(GOAWAY, goaway_frame->type());
+  EXPECT_EQ(123u, goaway_frame->last_accepted_stream_id());
+
+  scoped_ptr<SpdyWindowUpdateControlFrame> window_update_frame(
+      framer.CreateWindowUpdate(123, 456));
+  EXPECT_EQ(kSpdyProtocolVersion, window_update_frame->version());
+  EXPECT_TRUE(window_update_frame->is_control_frame());
+  EXPECT_EQ(WINDOW_UPDATE, window_update_frame->type());
+  EXPECT_EQ(123u, window_update_frame->stream_id());
+  EXPECT_EQ(456u, window_update_frame->delta_window_size());
+}
+
+TEST(SpdyProtocolTest, TestDataFrame) {
+  SpdyDataFrame frame;
+
+  // Set the stream ID to various values.
+  frame.set_stream_id(0);
+  EXPECT_EQ(0u, frame.stream_id());
+  EXPECT_FALSE(frame.is_control_frame());
+  frame.set_stream_id(~0 & kStreamIdMask);
+  EXPECT_EQ(~0 & kStreamIdMask, frame.stream_id());
+  EXPECT_FALSE(frame.is_control_frame());
+
+  // Set length to various values.  Make sure that when you set_length(x),
+  // length() == x.  Also make sure the flags are unaltered.
+  memset(frame.data(), '1', SpdyDataFrame::size());
+  int8 flags = frame.flags();
+  frame.set_length(0);
+  EXPECT_EQ(0u, frame.length());
+  EXPECT_EQ(flags, frame.flags());
+  frame.set_length(kLengthMask);
+  EXPECT_EQ(kLengthMask, frame.length());
+  EXPECT_EQ(flags, frame.flags());
+  frame.set_length(5u);
+  EXPECT_EQ(5u, frame.length());
+  EXPECT_EQ(flags, frame.flags());
+
+  // Set flags to various values.  Make sure that when you set_flags(x),
+  // flags() == x.  Also make sure the length is unaltered.
+  memset(frame.data(), '1', SpdyDataFrame::size());
+  uint32 length = frame.length();
+  frame.set_flags(0u);
+  EXPECT_EQ(0u, frame.flags());
+  EXPECT_EQ(length, frame.length());
+  int8 all_flags = ~0;
+  frame.set_flags(all_flags);
+  flags = frame.flags();
+  EXPECT_EQ(all_flags, flags);
+  EXPECT_EQ(length, frame.length());
+  frame.set_flags(5u);
+  EXPECT_EQ(5u, frame.flags());
+  EXPECT_EQ(length, frame.length());
+}
+
+// Test various types of SETTINGS frames.
+TEST(SpdyProtocolTest, TestSpdySettingsFrame) {
+  SpdyFramer framer;
+
+  // Create a settings frame with no settings.
+  SpdySettings settings;
+  scoped_ptr<SpdySettingsControlFrame> settings_frame(
+      framer.CreateSettings(settings));
+  EXPECT_EQ(kSpdyProtocolVersion, settings_frame->version());
+  EXPECT_TRUE(settings_frame->is_control_frame());
+  EXPECT_EQ(SETTINGS, settings_frame->type());
+  EXPECT_EQ(0u, settings_frame->num_entries());
+
+  // We'll add several different ID/Flag combinations and then verify
+  // that they encode and decode properly.
+  SettingsFlagsAndId ids[] = {
+    0x00000000,
+    0xffffffff,
+    0xff000001,
+    0x01000002,
+  };
+
+  for (size_t index = 0; index < arraysize(ids); ++index) {
+    settings.insert(settings.end(), std::make_pair(ids[index], index));
+    settings_frame.reset(framer.CreateSettings(settings));
+    EXPECT_EQ(kSpdyProtocolVersion, settings_frame->version());
+    EXPECT_TRUE(settings_frame->is_control_frame());
+    EXPECT_EQ(SETTINGS, settings_frame->type());
+    EXPECT_EQ(index + 1, settings_frame->num_entries());
+
+    SpdySettings parsed_settings;
+    EXPECT_TRUE(framer.ParseSettings(settings_frame.get(), &parsed_settings));
+    EXPECT_EQ(parsed_settings.size(), settings.size());
+    SpdySettings::const_iterator it = parsed_settings.begin();
+    int pos = 0;
+    while (it != parsed_settings.end()) {
+      SettingsFlagsAndId parsed = it->first;
+      uint32 value = it->second;
+      EXPECT_EQ(parsed.flags(), ids[pos].flags());
+      EXPECT_EQ(parsed.id(), ids[pos].id());
+      EXPECT_EQ(value, static_cast<uint32>(pos));
+      ++it;
+      ++pos;
+    }
+  }
+}
+
+// Make sure that overflows both die in debug mode, and do not cause problems
+// in opt mode.  Note:  The EXPECT_DEBUG_DEATH call does not work on Win32 yet,
+// so we comment it out.
+TEST(SpdyProtocolDeathTest, TestDataFrame) {
+  SpdyDataFrame frame;
+
+  frame.set_stream_id(0);
+  // TODO(mbelshe):  implement EXPECT_DEBUG_DEATH on windows.
+#ifndef WIN32
+  EXPECT_DEBUG_DEATH(frame.set_stream_id(~0), "");
+#endif
+  EXPECT_FALSE(frame.is_control_frame());
+
+  frame.set_flags(0);
+#ifndef WIN32
+  EXPECT_DEBUG_DEATH(frame.set_length(~0), "");
+#endif
+  EXPECT_EQ(0, frame.flags());
+}
+
+TEST(SpdyProtocolDeathTest, TestSpdyControlFrameStreamId) {
+  SpdyControlFrame frame_store(SpdySynStreamControlFrame::size());
+  memset(frame_store.data(), '1', SpdyControlFrame::size());
+  SpdySynStreamControlFrame* frame =
+      reinterpret_cast<SpdySynStreamControlFrame*>(&frame_store);
+
+  // Set the stream ID to various values.
+  frame->set_stream_id(0);
+  EXPECT_EQ(0u, frame->stream_id());
+  EXPECT_FALSE(frame->is_control_frame());
+  frame->set_stream_id(kStreamIdMask);
+  EXPECT_EQ(kStreamIdMask, frame->stream_id());
+  EXPECT_FALSE(frame->is_control_frame());
+}
+
+TEST(SpdyProtocolDeathTest, TestSpdyControlFrameVersion) {
+  const unsigned int kVersionMask = 0x7fff;
+  SpdyControlFrame frame(SpdySynStreamControlFrame::size());
+  memset(frame.data(), '1', SpdyControlFrame::size());
+
+  // Set the version to various values, and make sure it does not affect the
+  // type.
+  frame.set_type(SYN_STREAM);
+  frame.set_version(0);
+  EXPECT_EQ(0, frame.version());
+  EXPECT_TRUE(frame.is_control_frame());
+  EXPECT_EQ(SYN_STREAM, frame.type());
+
+  SpdySynStreamControlFrame* syn_stream =
+      reinterpret_cast<SpdySynStreamControlFrame*>(&frame);
+  syn_stream->set_stream_id(~0 & kVersionMask);
+  EXPECT_EQ(~0 & kVersionMask, syn_stream->stream_id());
+  EXPECT_TRUE(frame.is_control_frame());
+  EXPECT_EQ(SYN_STREAM, frame.type());
+}
+
+TEST(SpdyProtocolDeathTest, TestSpdyControlFrameType) {
+  SpdyControlFrame frame(SpdyControlFrame::size());
+  memset(frame.data(), 255, SpdyControlFrame::size());
+
+  // type() should be out of bounds.
+  EXPECT_FALSE(frame.AppearsToBeAValidControlFrame());
+
+  uint16 version = frame.version();
+
+  for (int i = SYN_STREAM; i <= spdy::NOOP; ++i) {
+    frame.set_type(static_cast<SpdyControlType>(i));
+    EXPECT_EQ(i, static_cast<int>(frame.type()));
+    EXPECT_TRUE(frame.AppearsToBeAValidControlFrame());
+    // Make sure setting type does not alter the version block.
+    EXPECT_EQ(version, frame.version());
+    EXPECT_TRUE(frame.is_control_frame());
+  }
+}
+
+}  // namespace
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
new file mode 100644
index 0000000..06ba7d5
--- /dev/null
+++ b/net/spdy/spdy_session.cc
@@ -0,0 +1,1324 @@
+// Copyright (c) 2010 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/spdy/spdy_session.h"
+
+#include "base/basictypes.h"
+#include "base/linked_ptr.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/rand_util.h"
+#include "base/stats_counters.h"
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/values.h"
+#include "net/base/connection_type_histograms.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/http/http_network_session.h"
+#include "net/socket/client_socket.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_settings_storage.h"
+#include "net/spdy/spdy_stream.h"
+
+namespace {
+
+// Diagnostics function to dump the headers of a request.
+// TODO(mbelshe): Remove this function.
+void DumpSpdyHeaders(const spdy::SpdyHeaderBlock& headers) {
+  // Because this function gets called on every request,
+  // take extra care to optimize it away if logging is turned off.
+  if (logging::LOG_INFO < logging::GetMinLogLevel())
+    return;
+
+  spdy::SpdyHeaderBlock::const_iterator it = headers.begin();
+  while (it != headers.end()) {
+    std::string val = (*it).second;
+    std::string::size_type pos = 0;
+    while ((pos = val.find('\0', pos)) != val.npos)
+      val[pos] = '\n';
+    LOG(INFO) << (*it).first << "==" << val;
+    ++it;
+  }
+}
+
+}  // namespace
+
+namespace net {
+
+namespace {
+
+#ifdef WIN32
+// We use an artificially small buffer size on windows because the async IO
+// system will artifiially delay IO completions when we use large buffers.
+const int kReadBufferSize = 2 * 1024;
+#else
+const int kReadBufferSize = 8 * 1024;
+#endif
+
+void AdjustSocketBufferSizes(ClientSocket* socket) {
+  // Adjust socket buffer sizes.
+  // SPDY uses one socket, and we want a really big buffer.
+  // This greatly helps on links with packet loss - we can even
+  // outperform Vista's dynamic window sizing algorithm.
+  // TODO(mbelshe): more study.
+  const int kSocketBufferSize = 512 * 1024;
+  socket->SetReceiveBufferSize(kSocketBufferSize);
+  socket->SetSendBufferSize(kSocketBufferSize);
+}
+
+class NetLogSpdySynParameter : public NetLog::EventParameters {
+ public:
+  NetLogSpdySynParameter(const linked_ptr<spdy::SpdyHeaderBlock>& headers,
+                         spdy::SpdyControlFlags flags,
+                         spdy::SpdyStreamId id)
+      : headers_(headers), flags_(flags), id_(id) {}
+
+  Value* ToValue() const {
+    DictionaryValue* dict = new DictionaryValue();
+    DictionaryValue* headers_dict = new DictionaryValue();
+    for (spdy::SpdyHeaderBlock::const_iterator it = headers_->begin();
+         it != headers_->end(); ++it) {
+      headers_dict->SetString(ASCIIToWide(it->first), it->second);
+    }
+    dict->SetInteger(L"flags", flags_);
+    dict->Set(L"headers", headers_dict);
+    dict->SetInteger(L"id", id_);
+    return dict;
+  }
+
+ private:
+  ~NetLogSpdySynParameter() {}
+
+  const linked_ptr<spdy::SpdyHeaderBlock> headers_;
+  spdy::SpdyControlFlags flags_;
+  spdy::SpdyStreamId id_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetLogSpdySynParameter);
+};
+
+class NetLogSpdySettingsParameter : public NetLog::EventParameters {
+ public:
+  explicit NetLogSpdySettingsParameter(const spdy::SpdySettings& settings)
+      : settings_(settings) {}
+
+  Value* ToValue() const {
+    DictionaryValue* dict = new DictionaryValue();
+    ListValue* settings = new ListValue();
+    for (spdy::SpdySettings::const_iterator it = settings_.begin();
+         it != settings_.end(); ++it) {
+      settings->Append(new StringValue(
+          StringPrintf("[%u:%u]", it->first.id(), it->second)));
+    }
+    dict->Set(L"settings", settings);
+    return dict;
+  }
+
+ private:
+  ~NetLogSpdySettingsParameter() {}
+
+  const spdy::SpdySettings settings_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetLogSpdySettingsParameter);
+};
+
+}  // namespace
+
+// static
+bool SpdySession::use_ssl_ = true;
+
+SpdySession::SpdySession(const HostPortPair& host_port_pair,
+                         HttpNetworkSession* session,
+                         NetLog* net_log)
+    : ALLOW_THIS_IN_INITIALIZER_LIST(
+          connect_callback_(this, &SpdySession::OnTCPConnect)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          ssl_connect_callback_(this, &SpdySession::OnSSLConnect)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          read_callback_(this, &SpdySession::OnReadComplete)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          write_callback_(this, &SpdySession::OnWriteComplete)),
+      host_port_pair_(host_port_pair),
+      session_(session),
+      connection_(new ClientSocketHandle),
+      read_buffer_(new IOBuffer(kReadBufferSize)),
+      read_pending_(false),
+      stream_hi_water_mark_(1),  // Always start at 1 for the first stream id.
+      write_pending_(false),
+      delayed_write_pending_(false),
+      is_secure_(false),
+      certificate_error_code_(OK),
+      error_(OK),
+      state_(IDLE),
+      max_concurrent_streams_(kDefaultMaxConcurrentStreams),
+      streams_initiated_count_(0),
+      streams_pushed_count_(0),
+      streams_pushed_and_claimed_count_(0),
+      streams_abandoned_count_(0),
+      sent_settings_(false),
+      received_settings_(false),
+      in_session_pool_(true),
+      initial_window_size_(spdy::kInitialWindowSize),
+      net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_SPDY_SESSION)) {
+  net_log_.BeginEvent(
+      NetLog::TYPE_SPDY_SESSION,
+      new NetLogStringParameter("host_port", host_port_pair_.ToString()));
+
+  // TODO(mbelshe): consider randomization of the stream_hi_water_mark.
+
+  spdy_framer_.set_visitor(this);
+
+  session_->ssl_config_service()->GetSSLConfig(&ssl_config_);
+
+  SendSettings();
+}
+
+SpdySession::~SpdySession() {
+  state_ = CLOSED;
+
+  // Cleanup all the streams.
+  CloseAllStreams(net::ERR_ABORTED);
+
+  if (connection_->is_initialized()) {
+    // With Spdy we can't recycle sockets.
+    connection_->socket()->Disconnect();
+  }
+
+  RecordHistograms();
+
+  net_log_.EndEvent(NetLog::TYPE_SPDY_SESSION, NULL);
+}
+
+net::Error SpdySession::InitializeWithSSLSocket(
+    ClientSocketHandle* connection,
+    int certificate_error_code) {
+  static StatsCounter spdy_sessions("spdy.sessions");
+  spdy_sessions.Increment();
+
+  AdjustSocketBufferSizes(connection->socket());
+
+  state_ = CONNECTED;
+  connection_.reset(connection);
+  is_secure_ = true;  // |connection| contains an SSLClientSocket.
+  certificate_error_code_ = certificate_error_code;
+
+  // This is a newly initialized session that no client should have a handle to
+  // yet, so there's no need to start writing data as in OnTCPConnect(), but we
+  // should start reading data.
+  net::Error error = ReadSocket();
+  if (error == ERR_IO_PENDING)
+    return OK;
+  return error;
+}
+
+net::Error SpdySession::Connect(
+    const std::string& group_name,
+    const scoped_refptr<TCPSocketParams>& destination,
+    RequestPriority priority) {
+  DCHECK(priority >= SPDY_PRIORITY_HIGHEST && priority <= SPDY_PRIORITY_LOWEST);
+
+  // If the connect process is started, let the caller continue.
+  if (state_ > IDLE)
+    return net::OK;
+
+  state_ = CONNECTING;
+
+  static StatsCounter spdy_sessions("spdy.sessions");
+  spdy_sessions.Increment();
+
+  int rv = connection_->Init(group_name, destination, priority,
+                             &connect_callback_, session_->tcp_socket_pool(),
+                             net_log_);
+  DCHECK(rv <= 0);
+
+  // If the connect is pending, we still return ok.  The APIs enqueue
+  // work until after the connect completes asynchronously later.
+  if (rv == net::ERR_IO_PENDING)
+    return net::OK;
+  OnTCPConnect(rv);
+  return static_cast<net::Error>(rv);
+}
+
+int SpdySession::GetPushStream(
+    const GURL& url,
+    scoped_refptr<SpdyStream>* stream,
+    const BoundNetLog& stream_net_log) {
+  CHECK_NE(state_, CLOSED);
+
+  *stream = NULL;
+
+  // Don't allow access to secure push streams over an unauthenticated, but
+  // encrypted SSL socket.
+  if (is_secure_ && certificate_error_code_ != OK &&
+      (url.SchemeIs("https") || url.SchemeIs("wss"))) {
+    LOG(DFATAL) << "Tried to get pushed spdy stream for secure content over an "
+                << "unauthenticated session.";
+    return certificate_error_code_;
+  }
+
+  const std::string& path = url.PathForRequest();
+
+  *stream = GetActivePushStream(path);
+  if (stream->get()) {
+    DCHECK(streams_pushed_and_claimed_count_ < streams_pushed_count_);
+    streams_pushed_and_claimed_count_++;
+    return OK;
+  }
+
+  // Check if we have a pending push stream for this url.
+  // Note that we shouldn't have a pushed stream for non-GET method.
+  PendingStreamMap::iterator it;
+  it = pending_streams_.find(path);
+  if (it != pending_streams_.end()) {
+    // Server has advertised a stream, but not yet sent it.
+    DCHECK(!it->second);
+    // Server will assign a stream id when the push stream arrives.  Use 0 for
+    // now.
+    net_log_.AddEvent(NetLog::TYPE_SPDY_STREAM_ADOPTED_PUSH_STREAM, NULL);
+    *stream = new SpdyStream(this, 0, true);
+    (*stream)->set_path(path);
+    (*stream)->set_net_log(stream_net_log);
+    it->second = *stream;
+    return OK;
+  }
+  return OK;
+}
+
+int SpdySession::CreateStream(
+    const GURL& url,
+    RequestPriority priority,
+    scoped_refptr<SpdyStream>* spdy_stream,
+    const BoundNetLog& stream_net_log,
+    CompletionCallback* callback) {
+  if (!max_concurrent_streams_ ||
+      active_streams_.size() < max_concurrent_streams_) {
+    return CreateStreamImpl(url, priority, spdy_stream, stream_net_log);
+  }
+
+  create_stream_queues_[priority].push(
+      PendingCreateStream(url, priority, spdy_stream,
+                          stream_net_log, callback));
+  return ERR_IO_PENDING;
+}
+
+void SpdySession::ProcessPendingCreateStreams() {
+  while (!max_concurrent_streams_ ||
+         active_streams_.size() < max_concurrent_streams_) {
+    bool no_pending_create_streams = true;
+    for (int i = 0;i < NUM_PRIORITIES;++i) {
+      if (!create_stream_queues_[i].empty()) {
+        PendingCreateStream& pending_create = create_stream_queues_[i].front();
+        no_pending_create_streams = false;
+        int error = CreateStreamImpl(*pending_create.url,
+                                     pending_create.priority,
+                                     pending_create.spdy_stream,
+                                     *pending_create.stream_net_log);
+        pending_create.callback->Run(error);
+        create_stream_queues_[i].pop();
+        break;
+      }
+    }
+    if (no_pending_create_streams)
+      return;  // there were no streams in any queue
+  }
+}
+
+void SpdySession::CancelPendingCreateStreams(
+    const scoped_refptr<SpdyStream>* spdy_stream) {
+  for (int i = 0;i < NUM_PRIORITIES;++i) {
+    PendingCreateStreamQueue tmp;
+    // Make a copy removing this trans
+    while (!create_stream_queues_[i].empty()) {
+      PendingCreateStream& pending_create = create_stream_queues_[i].front();
+      if (pending_create.spdy_stream != spdy_stream)
+        tmp.push(pending_create);
+      create_stream_queues_[i].pop();
+    }
+    // Now copy it back
+    while (!tmp.empty()) {
+      create_stream_queues_[i].push(tmp.front());
+      tmp.pop();
+    }
+  }
+}
+
+int SpdySession::CreateStreamImpl(
+    const GURL& url,
+    RequestPriority priority,
+    scoped_refptr<SpdyStream>* spdy_stream,
+    const BoundNetLog& stream_net_log) {
+  // Make sure that we don't try to send https/wss over an unauthenticated, but
+  // encrypted SSL socket.
+  if (is_secure_ && certificate_error_code_ != OK &&
+      (url.SchemeIs("https") || url.SchemeIs("wss"))) {
+    LOG(DFATAL) << "Tried to create spdy stream for secure content over an "
+                << "unauthenticated session.";
+    return certificate_error_code_;
+  }
+
+  const std::string& path = url.PathForRequest();
+
+  const spdy::SpdyStreamId stream_id = GetNewStreamId();
+
+  *spdy_stream = new SpdyStream(this, stream_id, false);
+  const scoped_refptr<SpdyStream>& stream = *spdy_stream;
+
+  stream->set_priority(priority);
+  stream->set_path(path);
+  stream->set_net_log(stream_net_log);
+  stream->set_window_size(initial_window_size_);
+  ActivateStream(stream);
+
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyPriorityCount",
+      static_cast<int>(priority), 0, 10, 11);
+
+  LOG(INFO) << "SpdyStream: Creating stream " << stream_id << " for " << url;
+  // TODO(mbelshe): Optimize memory allocations
+  DCHECK(priority >= SPDY_PRIORITY_HIGHEST &&
+         priority <= SPDY_PRIORITY_LOWEST);
+
+  DCHECK_EQ(active_streams_[stream_id].get(), stream.get());
+  return OK;
+}
+
+int SpdySession::WriteSynStream(
+    spdy::SpdyStreamId stream_id,
+    RequestPriority priority,
+    spdy::SpdyControlFlags flags,
+    const linked_ptr<spdy::SpdyHeaderBlock>& headers) {
+  // Find our stream
+  if (!IsStreamActive(stream_id))
+    return ERR_INVALID_SPDY_STREAM;
+  const scoped_refptr<SpdyStream>& stream = active_streams_[stream_id];
+  CHECK_EQ(stream->stream_id(), stream_id);
+
+  scoped_ptr<spdy::SpdySynStreamControlFrame> syn_frame(
+      spdy_framer_.CreateSynStream(stream_id, 0, priority, flags, false,
+                                   headers.get()));
+  QueueFrame(syn_frame.get(), priority, stream);
+
+  static StatsCounter spdy_requests("spdy.requests");
+  spdy_requests.Increment();
+  streams_initiated_count_++;
+
+  LOG(INFO) << "SPDY SYN_STREAM HEADERS ----------------------------------";
+  DumpSpdyHeaders(*headers);
+
+  const BoundNetLog& log = stream->net_log();
+  if (log.HasListener()) {
+    log.AddEvent(
+        NetLog::TYPE_SPDY_STREAM_SYN_STREAM,
+        new NetLogSpdySynParameter(headers, flags, stream_id));
+  }
+
+  return ERR_IO_PENDING;
+}
+
+int SpdySession::WriteStreamData(spdy::SpdyStreamId stream_id,
+                                 net::IOBuffer* data, int len,
+                                 spdy::SpdyDataFlags flags) {
+  LOG(INFO) << "Writing Stream Data for stream " << stream_id << " (" << len
+            << " bytes)";
+  const int kMss = 1430;  // This is somewhat arbitrary and not really fixed,
+                          // but it will always work reasonably with ethernet.
+  // Chop the world into 2-packet chunks.  This is somewhat arbitrary, but
+  // is reasonably small and ensures that we elicit ACKs quickly from TCP
+  // (because TCP tries to only ACK every other packet).
+  const int kMaxSpdyFrameChunkSize = (2 * kMss) - spdy::SpdyFrame::size();
+
+  // Find our stream
+  DCHECK(IsStreamActive(stream_id));
+  scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
+  CHECK_EQ(stream->stream_id(), stream_id);
+  if (!stream)
+    return ERR_INVALID_SPDY_STREAM;
+
+  if (len > kMaxSpdyFrameChunkSize) {
+    len = kMaxSpdyFrameChunkSize;
+    flags = spdy::DATA_FLAG_NONE;
+  }
+
+  // TODO(mbelshe): reduce memory copies here.
+  scoped_ptr<spdy::SpdyDataFrame> frame(
+      spdy_framer_.CreateDataFrame(stream_id, data->data(), len, flags));
+  QueueFrame(frame.get(), stream->priority(), stream);
+  return ERR_IO_PENDING;
+}
+
+void SpdySession::CloseStream(spdy::SpdyStreamId stream_id, int status) {
+  LOG(INFO) << "Closing stream " << stream_id << " with status " << status;
+  // TODO(mbelshe): We should send a RST_STREAM control frame here
+  //                so that the server can cancel a large send.
+
+  DeleteStream(stream_id, status);
+}
+
+void SpdySession::ResetStream(
+    spdy::SpdyStreamId stream_id, spdy::SpdyStatusCodes status) {
+  DCHECK(IsStreamActive(stream_id));
+  scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
+  CHECK_EQ(stream->stream_id(), stream_id);
+
+  LOG(INFO) << "Sending a RST_STREAM frame for stream " << stream_id
+            << " with status " << status;
+
+  scoped_ptr<spdy::SpdyRstStreamControlFrame> rst_frame(
+      spdy_framer_.CreateRstStream(stream_id, status));
+  QueueFrame(rst_frame.get(), stream->priority(), stream);
+
+  DeleteStream(stream_id, ERR_SPDY_PROTOCOL_ERROR);
+}
+
+bool SpdySession::IsStreamActive(spdy::SpdyStreamId stream_id) const {
+  return ContainsKey(active_streams_, stream_id);
+}
+
+LoadState SpdySession::GetLoadState() const {
+  // NOTE: The application only queries the LoadState via the
+  //       SpdyNetworkTransaction, and details are only needed when
+  //       we're in the process of connecting.
+
+  // If we're connecting, defer to the connection to give us the actual
+  // LoadState.
+  if (state_ == CONNECTING)
+    return connection_->GetLoadState();
+
+  // Just report that we're idle since the session could be doing
+  // many things concurrently.
+  return LOAD_STATE_IDLE;
+}
+
+void SpdySession::OnTCPConnect(int result) {
+  LOG(INFO) << "Spdy socket connected (result=" << result << ")";
+
+  // We shouldn't be coming through this path if we didn't just open a fresh
+  // socket (or have an error trying to do so).
+  DCHECK(!connection_->socket() || !connection_->is_reused());
+
+  if (result != net::OK) {
+    DCHECK_LT(result, 0);
+    CloseSessionOnError(static_cast<net::Error>(result));
+    return;
+  } else {
+    UpdateConnectionTypeHistograms(CONNECTION_SPDY);
+  }
+
+  AdjustSocketBufferSizes(connection_->socket());
+
+  if (use_ssl_) {
+    // Add a SSL socket on top of our existing transport socket.
+    ClientSocket* socket = connection_->release_socket();
+    // TODO(mbelshe): Fix the hostname.  This is BROKEN without having
+    //                a real hostname.
+    socket = session_->socket_factory()->CreateSSLClientSocket(
+        socket, "" /* request_->url.HostNoBrackets() */ , ssl_config_);
+    connection_->set_socket(socket);
+    is_secure_ = true;
+    int status = connection_->socket()->Connect(&ssl_connect_callback_);
+    if (status != ERR_IO_PENDING)
+      OnSSLConnect(status);
+  } else {
+    DCHECK_EQ(state_, CONNECTING);
+    state_ = CONNECTED;
+
+    // Make sure we get any pending data sent.
+    WriteSocketLater();
+    // Start reading
+    ReadSocket();
+  }
+}
+
+void SpdySession::OnSSLConnect(int result) {
+  // TODO(mbelshe): We need to replicate the functionality of
+  //   HttpNetworkTransaction::DoSSLConnectComplete here, where it calls
+  //   HandleCertificateError() and such.
+  if (IsCertificateError(result))
+    result = OK;   // TODO(mbelshe): pretend we're happy anyway.
+
+  if (result == OK) {
+    DCHECK_EQ(state_, CONNECTING);
+    state_ = CONNECTED;
+
+    // After we've connected, send any data to the server, and then issue
+    // our read.
+    WriteSocketLater();
+    ReadSocket();
+  } else {
+    DCHECK_LT(result, 0);  // It should be an error, not a byte count.
+    CloseSessionOnError(static_cast<net::Error>(result));
+  }
+}
+
+void SpdySession::OnReadComplete(int bytes_read) {
+  // Parse a frame.  For now this code requires that the frame fit into our
+  // buffer (32KB).
+  // TODO(mbelshe): support arbitrarily large frames!
+
+  LOG(INFO) << "Spdy socket read: " << bytes_read << " bytes";
+
+  read_pending_ = false;
+
+  if (bytes_read <= 0) {
+    // Session is tearing down.
+    net::Error error = static_cast<net::Error>(bytes_read);
+    if (bytes_read == 0) {
+      LOG(INFO) << "Spdy socket closed by server[" <<
+          host_port_pair().ToString() << "].";
+      error = ERR_CONNECTION_CLOSED;
+    }
+    CloseSessionOnError(error);
+    return;
+  }
+
+  // The SpdyFramer will use callbacks onto |this| as it parses frames.
+  // When errors occur, those callbacks can lead to teardown of all references
+  // to |this|, so maintain a reference to self during this call for safe
+  // cleanup.
+  scoped_refptr<SpdySession> self(this);
+
+  char *data = read_buffer_->data();
+  while (bytes_read &&
+         spdy_framer_.error_code() == spdy::SpdyFramer::SPDY_NO_ERROR) {
+    uint32 bytes_processed = spdy_framer_.ProcessInput(data, bytes_read);
+    bytes_read -= bytes_processed;
+    data += bytes_processed;
+    if (spdy_framer_.state() == spdy::SpdyFramer::SPDY_DONE)
+      spdy_framer_.Reset();
+  }
+
+  if (state_ != CLOSED)
+    ReadSocket();
+}
+
+void SpdySession::OnWriteComplete(int result) {
+  DCHECK(write_pending_);
+  DCHECK(in_flight_write_.size());
+  DCHECK_NE(result, 0);  // This shouldn't happen for write.
+
+  write_pending_ = false;
+
+  scoped_refptr<SpdyStream> stream = in_flight_write_.stream();
+
+  LOG(INFO) << "Spdy write complete (result=" << result << ")"
+            << (stream ? std::string(" for stream ") +
+                IntToString(stream->stream_id()) : "");
+
+  if (result >= 0) {
+    // It should not be possible to have written more bytes than our
+    // in_flight_write_.
+    DCHECK_LE(result, in_flight_write_.buffer()->BytesRemaining());
+
+    in_flight_write_.buffer()->DidConsume(result);
+
+    // We only notify the stream when we've fully written the pending frame.
+    if (!in_flight_write_.buffer()->BytesRemaining()) {
+      if (stream) {
+        // Report the number of bytes written to the caller, but exclude the
+        // frame size overhead.  NOTE: if this frame was compressed the
+        // reported bytes written is the compressed size, not the original
+        // size.
+        if (result > 0) {
+          result = in_flight_write_.buffer()->size();
+          DCHECK_GT(result, static_cast<int>(spdy::SpdyFrame::size()));
+          result -= static_cast<int>(spdy::SpdyFrame::size());
+        }
+
+        // It is possible that the stream was cancelled while we were writing
+        // to the socket.
+        if (!stream->cancelled())
+          stream->OnWriteComplete(result);
+      }
+
+      // Cleanup the write which just completed.
+      in_flight_write_.release();
+    }
+
+    // Write more data.  We're already in a continuation, so we can
+    // go ahead and write it immediately (without going back to the
+    // message loop).
+    WriteSocketLater();
+  } else {
+    in_flight_write_.release();
+
+    // The stream is now errored.  Close it down.
+    CloseSessionOnError(static_cast<net::Error>(result));
+  }
+}
+
+net::Error SpdySession::ReadSocket() {
+  if (read_pending_)
+    return OK;
+
+  if (state_ == CLOSED) {
+    NOTREACHED();
+    return ERR_UNEXPECTED;
+  }
+
+  CHECK(connection_.get());
+  CHECK(connection_->socket());
+  int bytes_read = connection_->socket()->Read(read_buffer_.get(),
+                                               kReadBufferSize,
+                                               &read_callback_);
+  switch (bytes_read) {
+    case 0:
+      // Socket is closed!
+      CloseSessionOnError(ERR_CONNECTION_CLOSED);
+      return ERR_CONNECTION_CLOSED;
+    case net::ERR_IO_PENDING:
+      // Waiting for data.  Nothing to do now.
+      read_pending_ = true;
+      return ERR_IO_PENDING;
+    default:
+      // Data was read, process it.
+      // Schedule the work through the message loop to avoid recursive
+      // callbacks.
+      read_pending_ = true;
+      MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+          this, &SpdySession::OnReadComplete, bytes_read));
+      break;
+  }
+  return OK;
+}
+
+void SpdySession::WriteSocketLater() {
+  if (delayed_write_pending_)
+    return;
+
+  if (state_ < CONNECTED)
+    return;
+
+  delayed_write_pending_ = true;
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      this, &SpdySession::WriteSocket));
+}
+
+void SpdySession::WriteSocket() {
+  // This function should only be called via WriteSocketLater.
+  DCHECK(delayed_write_pending_);
+  delayed_write_pending_ = false;
+
+  // If the socket isn't connected yet, just wait; we'll get called
+  // again when the socket connection completes.  If the socket is
+  // closed, just return.
+  if (state_ < CONNECTED || state_ == CLOSED)
+    return;
+
+  if (write_pending_)   // Another write is in progress still.
+    return;
+
+  // Loop sending frames until we've sent everything or until the write
+  // returns error (or ERR_IO_PENDING).
+  while (in_flight_write_.buffer() || !queue_.empty()) {
+    if (!in_flight_write_.buffer()) {
+      // Grab the next SpdyFrame to send.
+      SpdyIOBuffer next_buffer = queue_.top();
+      queue_.pop();
+
+      // We've deferred compression until just before we write it to the socket,
+      // which is now.  At this time, we don't compress our data frames.
+      spdy::SpdyFrame uncompressed_frame(next_buffer.buffer()->data(), false);
+      size_t size;
+      if (spdy_framer_.IsCompressible(uncompressed_frame)) {
+        scoped_ptr<spdy::SpdyFrame> compressed_frame(
+            spdy_framer_.CompressFrame(uncompressed_frame));
+        if (!compressed_frame.get()) {
+          LOG(ERROR) << "SPDY Compression failure";
+          CloseSessionOnError(net::ERR_SPDY_PROTOCOL_ERROR);
+          return;
+        }
+
+        size = compressed_frame->length() + spdy::SpdyFrame::size();
+
+        DCHECK_GT(size, 0u);
+
+        // TODO(mbelshe): We have too much copying of data here.
+        IOBufferWithSize* buffer = new IOBufferWithSize(size);
+        memcpy(buffer->data(), compressed_frame->data(), size);
+
+        // Attempt to send the frame.
+        in_flight_write_ = SpdyIOBuffer(buffer, size, 0, next_buffer.stream());
+      } else {
+        size = uncompressed_frame.length() + spdy::SpdyFrame::size();
+        in_flight_write_ = next_buffer;
+      }
+    } else {
+      DCHECK(in_flight_write_.buffer()->BytesRemaining());
+    }
+
+    write_pending_ = true;
+    int rv = connection_->socket()->Write(in_flight_write_.buffer(),
+        in_flight_write_.buffer()->BytesRemaining(), &write_callback_);
+    if (rv == net::ERR_IO_PENDING)
+      break;
+
+    // We sent the frame successfully.
+    OnWriteComplete(rv);
+
+    // TODO(mbelshe):  Test this error case.  Maybe we should mark the socket
+    //                 as in an error state.
+    if (rv < 0)
+      break;
+  }
+}
+
+void SpdySession::CloseAllStreams(net::Error status) {
+  LOG(INFO) << "Closing all SPDY Streams for " << host_port_pair().ToString();
+
+  static StatsCounter abandoned_streams("spdy.abandoned_streams");
+  static StatsCounter abandoned_push_streams("spdy.abandoned_push_streams");
+
+  if (!active_streams_.empty())
+    abandoned_streams.Add(active_streams_.size());
+  if (!pushed_streams_.empty()) {
+    streams_abandoned_count_ += pushed_streams_.size();
+    abandoned_push_streams.Add(pushed_streams_.size());
+  }
+
+  for (int i = 0;i < NUM_PRIORITIES;++i) {
+    while (!create_stream_queues_[i].empty()) {
+      PendingCreateStream& pending_create = create_stream_queues_[i].front();
+      pending_create.callback->Run(ERR_ABORTED);
+      create_stream_queues_[i].pop();
+    }
+  }
+
+  while (!active_streams_.empty()) {
+    ActiveStreamMap::iterator it = active_streams_.begin();
+    const scoped_refptr<SpdyStream>& stream = it->second;
+    DCHECK(stream);
+    LOG(ERROR) << "ABANDONED (stream_id=" << stream->stream_id()
+      << "): " << stream->path();
+    DeleteStream(stream->stream_id(), status);
+  }
+
+  // TODO(erikchen): ideally stream->OnClose() is only ever called by
+  // DeleteStream, but pending streams fall into their own category for now.
+  PendingStreamMap::iterator it;
+  for (it = pending_streams_.begin(); it != pending_streams_.end(); ++it) {
+    const scoped_refptr<SpdyStream>& stream = it->second;
+    if (stream)
+      stream->OnClose(ERR_ABORTED);
+  }
+  pending_streams_.clear();
+
+  // We also need to drain the queue.
+  while (queue_.size())
+    queue_.pop();
+}
+
+int SpdySession::GetNewStreamId() {
+  int id = stream_hi_water_mark_;
+  stream_hi_water_mark_ += 2;
+  if (stream_hi_water_mark_ > 0x7fff)
+    stream_hi_water_mark_ = 1;
+  return id;
+}
+
+void SpdySession::QueueFrame(spdy::SpdyFrame* frame,
+                             spdy::SpdyPriority priority,
+                             SpdyStream* stream) {
+  int length = spdy::SpdyFrame::size() + frame->length();
+  IOBuffer* buffer = new IOBuffer(length);
+  memcpy(buffer->data(), frame->data(), length);
+  queue_.push(SpdyIOBuffer(buffer, length, priority, stream));
+
+  WriteSocketLater();
+}
+
+void SpdySession::CloseSessionOnError(net::Error err) {
+  // Closing all streams can have a side-effect of dropping the last reference
+  // to |this|.  Hold a reference through this function.
+  scoped_refptr<SpdySession> self(this);
+
+  DCHECK_LT(err, OK);
+  LOG(INFO) << "spdy::CloseSessionOnError(" << err << ") for " <<
+      host_port_pair().ToString();
+
+  // Don't close twice.  This can occur because we can have both
+  // a read and a write outstanding, and each can complete with
+  // an error.
+  if (state_ != CLOSED) {
+    state_ = CLOSED;
+    error_ = err;
+    RemoveFromPool();
+    CloseAllStreams(err);
+  }
+}
+
+void SpdySession::ActivateStream(SpdyStream* stream) {
+  const spdy::SpdyStreamId id = stream->stream_id();
+  DCHECK(!IsStreamActive(id));
+
+  active_streams_[id] = stream;
+}
+
+void SpdySession::DeleteStream(spdy::SpdyStreamId id, int status) {
+  // Remove the stream from pushed_streams_ and active_streams_.
+  ActivePushedStreamList::iterator it;
+  for (it = pushed_streams_.begin(); it != pushed_streams_.end(); ++it) {
+    scoped_refptr<SpdyStream> curr = *it;
+    if (id == curr->stream_id()) {
+      pushed_streams_.erase(it);
+      break;
+    }
+  }
+
+  // The stream might have been deleted.
+  ActiveStreamMap::iterator it2 = active_streams_.find(id);
+  if (it2 == active_streams_.end())
+    return;
+
+  // If this is an active stream, call the callback.
+  const scoped_refptr<SpdyStream> stream(it2->second);
+  active_streams_.erase(it2);
+  if (stream)
+    stream->OnClose(status);
+  ProcessPendingCreateStreams();
+}
+
+void SpdySession::RemoveFromPool() {
+  if (in_session_pool_) {
+    session_->spdy_session_pool()->Remove(this);
+    in_session_pool_ = false;
+  }
+}
+
+scoped_refptr<SpdyStream> SpdySession::GetActivePushStream(
+    const std::string& path) {
+  static StatsCounter used_push_streams("spdy.claimed_push_streams");
+
+  LOG(INFO) << "Looking for push stream: " << path;
+
+  scoped_refptr<SpdyStream> stream;
+
+  // We just walk a linear list here.
+  ActivePushedStreamList::iterator it;
+  for (it = pushed_streams_.begin(); it != pushed_streams_.end(); ++it) {
+    stream = *it;
+    if (path == stream->path()) {
+      CHECK(stream->pushed());
+      pushed_streams_.erase(it);
+      used_push_streams.Increment();
+      LOG(INFO) << "Push Stream Claim for: " << path;
+      return stream;
+    }
+  }
+
+  return NULL;
+}
+
+bool SpdySession::GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated) {
+  if (is_secure_) {
+    SSLClientSocket* ssl_socket =
+        reinterpret_cast<SSLClientSocket*>(connection_->socket());
+    ssl_socket->GetSSLInfo(ssl_info);
+    *was_npn_negotiated = ssl_socket->wasNpnNegotiated();
+    return true;
+  }
+  return false;
+}
+
+void SpdySession::OnError(spdy::SpdyFramer* framer) {
+  LOG(ERROR) << "SpdySession error: " << framer->error_code();
+  CloseSessionOnError(net::ERR_SPDY_PROTOCOL_ERROR);
+}
+
+void SpdySession::OnStreamFrameData(spdy::SpdyStreamId stream_id,
+                                    const char* data,
+                                    size_t len) {
+  LOG(INFO) << "Spdy data for stream " << stream_id << ", " << len << " bytes";
+
+  if (!IsStreamActive(stream_id)) {
+    // NOTE:  it may just be that the stream was cancelled.
+    LOG(WARNING) << "Received data frame for invalid stream " << stream_id;
+    return;
+  }
+
+  scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
+  stream->OnDataReceived(data, len);
+}
+
+bool SpdySession::Respond(const spdy::SpdyHeaderBlock& headers,
+                          const scoped_refptr<SpdyStream> stream) {
+  int rv = OK;
+
+  rv = stream->OnResponseReceived(headers);
+  if (rv < 0) {
+    DCHECK_NE(rv, ERR_IO_PENDING);
+    const spdy::SpdyStreamId stream_id = stream->stream_id();
+    DeleteStream(stream_id, rv);
+    return false;
+  }
+  return true;
+}
+
+void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame& frame,
+                        const linked_ptr<spdy::SpdyHeaderBlock>& headers) {
+  spdy::SpdyStreamId stream_id = frame.stream_id();
+
+  LOG(INFO) << "Spdy SynStream for stream " << stream_id;
+
+  // Server-initiated streams should have even sequence numbers.
+  if ((stream_id & 0x1) != 0) {
+    LOG(ERROR) << "Received invalid OnSyn stream id " << stream_id;
+    return;
+  }
+
+  if (IsStreamActive(stream_id)) {
+    LOG(ERROR) << "Received OnSyn for active stream " << stream_id;
+    return;
+  }
+
+  streams_pushed_count_++;
+
+  LOG(INFO) << "SpdySession: Syn received for stream: " << stream_id;
+
+  LOG(INFO) << "SPDY SYN RESPONSE HEADERS -----------------------";
+  DumpSpdyHeaders(*headers);
+
+  // TODO(mbelshe): DCHECK that this is a GET method?
+
+  const std::string& path = ContainsKey(*headers, "path") ?
+      headers->find("path")->second : "";
+
+  // Verify that the response had a URL for us.
+  DCHECK(!path.empty());
+  if (path.empty()) {
+    LOG(WARNING) << "Pushed stream did not contain a path.";
+    return;
+  }
+
+  // Only HTTP push a stream.
+  scoped_refptr<SpdyStream> stream;
+
+  // Check if we already have a delegate awaiting this stream.
+  PendingStreamMap::iterator it;
+  it = pending_streams_.find(path);
+  if (it != pending_streams_.end()) {
+    stream = it->second;
+    pending_streams_.erase(it);
+  }
+
+  if (stream) {
+    CHECK(stream->pushed());
+    CHECK_EQ(0u, stream->stream_id());
+    stream->set_stream_id(stream_id);
+    const BoundNetLog& log = stream->net_log();
+    if (log.HasListener()) {
+      log.AddEvent(
+          NetLog::TYPE_SPDY_STREAM_PUSHED_SYN_STREAM,
+          new NetLogSpdySynParameter(
+              headers, static_cast<spdy::SpdyControlFlags>(frame.flags()),
+              stream_id));
+    }
+  } else {
+    stream = new SpdyStream(this, stream_id, true);
+
+    if (net_log_.HasListener()) {
+      net_log_.AddEvent(
+          NetLog::TYPE_SPDY_SESSION_PUSHED_SYN_STREAM,
+          new NetLogSpdySynParameter(
+              headers, static_cast<spdy::SpdyControlFlags>(frame.flags()),
+              stream_id));
+    }
+  }
+
+  pushed_streams_.push_back(stream);
+
+  // Activate a stream and parse the headers.
+  ActivateStream(stream);
+
+  stream->set_path(path);
+
+  if (!Respond(*headers, stream))
+    return;
+
+  LOG(INFO) << "Got pushed stream for " << stream->path();
+
+  static StatsCounter push_requests("spdy.pushed_streams");
+  push_requests.Increment();
+}
+
+void SpdySession::OnSynReply(const spdy::SpdySynReplyControlFrame& frame,
+                             const linked_ptr<spdy::SpdyHeaderBlock>& headers) {
+  spdy::SpdyStreamId stream_id = frame.stream_id();
+  LOG(INFO) << "Spdy SynReply for stream " << stream_id;
+
+  bool valid_stream = IsStreamActive(stream_id);
+  if (!valid_stream) {
+    // NOTE:  it may just be that the stream was cancelled.
+    LOG(WARNING) << "Received SYN_REPLY for invalid stream " << stream_id;
+    return;
+  }
+
+  LOG(INFO) << "SPDY SYN_REPLY RESPONSE HEADERS for stream: " << stream_id;
+  DumpSpdyHeaders(*headers);
+
+  scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
+  CHECK_EQ(stream->stream_id(), stream_id);
+  CHECK(!stream->cancelled());
+
+  if (stream->syn_reply_received()) {
+    LOG(WARNING) << "Received duplicate SYN_REPLY for stream " << stream_id;
+    CloseStream(stream->stream_id(), ERR_SPDY_PROTOCOL_ERROR);
+    return;
+  }
+  stream->set_syn_reply_received();
+
+  // We record content declared as being pushed so that we don't
+  // request a duplicate stream which is already scheduled to be
+  // sent to us.
+  spdy::SpdyHeaderBlock::const_iterator it;
+  it = headers->find("x-associated-content");
+  if (it != headers->end()) {
+    const std::string& content = it->second;
+    std::string::size_type start = 0;
+    std::string::size_type end = 0;
+    do {
+      end = content.find("||", start);
+      if (end == std::string::npos)
+        end = content.length();
+      std::string url = content.substr(start, end - start);
+      std::string::size_type pos = url.find("??");
+      if (pos == std::string::npos)
+        break;
+      url = url.substr(pos + 2);
+      GURL gurl(url);
+      std::string path = gurl.PathForRequest();
+      if (path.length())
+        pending_streams_[path] = NULL;
+      else
+        LOG(INFO) << "Invalid X-Associated-Content path: " << url;
+      start = end + 2;
+    } while (start < content.length());
+  }
+
+  const BoundNetLog& log = stream->net_log();
+  if (log.HasListener()) {
+    log.AddEvent(
+        NetLog::TYPE_SPDY_STREAM_SYN_REPLY,
+        new NetLogSpdySynParameter(
+            headers, static_cast<spdy::SpdyControlFlags>(frame.flags()),
+            stream_id));
+  }
+
+  Respond(*headers, stream);
+}
+
+void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) {
+  const linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
+  uint32 type = frame->type();
+  if (type == spdy::SYN_STREAM || type == spdy::SYN_REPLY) {
+    if (!spdy_framer_.ParseHeaderBlock(frame, headers.get())) {
+      LOG(WARNING) << "Could not parse Spdy Control Frame Header";
+      // TODO(mbelshe):  Error the session?
+      return;
+    }
+  }
+
+  switch (type) {
+    case spdy::GOAWAY:
+      OnGoAway(*reinterpret_cast<const spdy::SpdyGoAwayControlFrame*>(frame));
+      break;
+    case spdy::SETTINGS:
+      OnSettings(
+          *reinterpret_cast<const spdy::SpdySettingsControlFrame*>(frame));
+      break;
+    case spdy::RST_STREAM:
+      OnFin(*reinterpret_cast<const spdy::SpdyRstStreamControlFrame*>(frame));
+      break;
+    case spdy::SYN_STREAM:
+      OnSyn(*reinterpret_cast<const spdy::SpdySynStreamControlFrame*>(frame),
+            headers);
+      break;
+    case spdy::SYN_REPLY:
+      OnSynReply(
+          *reinterpret_cast<const spdy::SpdySynReplyControlFrame*>(frame),
+          headers);
+      break;
+    case spdy::WINDOW_UPDATE:
+      OnWindowUpdate(
+          *reinterpret_cast<const spdy::SpdyWindowUpdateControlFrame*>(frame));
+      break;
+    default:
+      DCHECK(false);  // Error!
+  }
+}
+
+void SpdySession::OnFin(const spdy::SpdyRstStreamControlFrame& frame) {
+  spdy::SpdyStreamId stream_id = frame.stream_id();
+  LOG(INFO) << "Spdy Fin for stream " << stream_id;
+
+  bool valid_stream = IsStreamActive(stream_id);
+  if (!valid_stream) {
+    // NOTE:  it may just be that the stream was cancelled.
+    LOG(WARNING) << "Received FIN for invalid stream" << stream_id;
+    return;
+  }
+  scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
+  CHECK_EQ(stream->stream_id(), stream_id);
+  CHECK(!stream->cancelled());
+
+  const BoundNetLog& log = stream->net_log();
+  log.AddEvent(
+      NetLog::TYPE_SPDY_STREAM_RST_STREAM,
+      new NetLogIntegerParameter("status", frame.status()));
+
+  if (frame.status() == 0) {
+    stream->OnDataReceived(NULL, 0);
+  } else {
+    LOG(ERROR) << "Spdy stream closed: " << frame.status();
+    // TODO(mbelshe): Map from Spdy-protocol errors to something sensical.
+    //                For now, it doesn't matter much - it is a protocol error.
+    DeleteStream(stream_id, ERR_SPDY_PROTOCOL_ERROR);
+  }
+}
+
+void SpdySession::OnGoAway(const spdy::SpdyGoAwayControlFrame& frame) {
+  LOG(INFO) << "Spdy GOAWAY for session[" << this << "] for " <<
+      host_port_pair().ToString();
+
+  net_log_.AddEvent(
+      NetLog::TYPE_SPDY_SESSION_GOAWAY,
+      new NetLogIntegerParameter(
+          "last_accepted_stream_id",
+          frame.last_accepted_stream_id()));
+
+  RemoveFromPool();
+
+  // TODO(willchan): Cancel any streams that are past the GoAway frame's
+  // |last_accepted_stream_id|.
+
+  // Don't bother killing any streams that are still reading.  They'll either
+  // complete successfully or get an ERR_CONNECTION_CLOSED when the socket is
+  // closed.
+}
+
+void SpdySession::OnSettings(const spdy::SpdySettingsControlFrame& frame) {
+  spdy::SpdySettings settings;
+  if (spdy_framer_.ParseSettings(&frame, &settings)) {
+    HandleSettings(settings);
+    SpdySettingsStorage* settings_storage = session_->mutable_spdy_settings();
+    settings_storage->Set(host_port_pair_, settings);
+  }
+
+  // TODO(agayev): Implement initial and per stream window size update.
+
+  received_settings_ = true;
+
+  net_log_.AddEvent(
+      NetLog::TYPE_SPDY_SESSION_RECV_SETTINGS,
+      new NetLogSpdySettingsParameter(settings));
+}
+
+void SpdySession::OnWindowUpdate(
+    const spdy::SpdyWindowUpdateControlFrame& frame) {
+  spdy::SpdyStreamId stream_id = frame.stream_id();
+  LOG(INFO) << "Spdy WINDOW_UPDATE for stream " << stream_id;
+
+  if (!IsStreamActive(stream_id)) {
+    LOG(WARNING) << "Received WINDOW_UPDATE for invalid stream " << stream_id;
+    return;
+  }
+
+  int delta_window_size = static_cast<int>(frame.delta_window_size());
+  if (delta_window_size < 1) {
+    LOG(WARNING) << "Received WINDOW_UPDATE with an invalid delta_window_size "
+                 << delta_window_size;
+    ResetStream(stream_id, spdy::FLOW_CONTROL_ERROR);
+    return;
+  }
+
+  scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
+  CHECK_EQ(stream->stream_id(), stream_id);
+  CHECK(!stream->cancelled());
+
+  stream->UpdateWindowSize(delta_window_size);
+}
+
+void SpdySession::SendSettings() {
+  const SpdySettingsStorage& settings_storage = session_->spdy_settings();
+  const spdy::SpdySettings& settings = settings_storage.Get(host_port_pair_);
+  if (settings.empty())
+    return;
+  HandleSettings(settings);
+
+  net_log_.AddEvent(
+      NetLog::TYPE_SPDY_SESSION_SEND_SETTINGS,
+      new NetLogSpdySettingsParameter(settings));
+
+  // Create the SETTINGS frame and send it.
+  scoped_ptr<spdy::SpdySettingsControlFrame> settings_frame(
+      spdy_framer_.CreateSettings(settings));
+  sent_settings_ = true;
+  QueueFrame(settings_frame.get(), 0, NULL);
+}
+
+void SpdySession::HandleSettings(const spdy::SpdySettings& settings) {
+  for (spdy::SpdySettings::const_iterator i = settings.begin(),
+           end = settings.end(); i != end; ++i) {
+    const uint32 id = i->first.id();
+    const uint32 val = i->second;
+    switch (id) {
+      case spdy::SETTINGS_MAX_CONCURRENT_STREAMS:
+        max_concurrent_streams_ = val;
+        ProcessPendingCreateStreams();
+        break;
+    }
+  }
+}
+
+void SpdySession::RecordHistograms() {
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPerSession",
+                              streams_initiated_count_,
+                              0, 300, 50);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedPerSession",
+                              streams_pushed_count_,
+                              0, 300, 50);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedAndClaimedPerSession",
+                              streams_pushed_and_claimed_count_,
+                              0, 300, 50);
+  UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsAbandonedPerSession",
+                              streams_abandoned_count_,
+                              0, 300, 50);
+  UMA_HISTOGRAM_ENUMERATION("Net.SpdySettingsSent",
+                            sent_settings_ ? 1 : 0, 2);
+  UMA_HISTOGRAM_ENUMERATION("Net.SpdySettingsReceived",
+                            received_settings_ ? 1 : 0, 2);
+
+  if (received_settings_) {
+    // Enumerate the saved settings, and set histograms for it.
+    const SpdySettingsStorage& settings_storage = session_->spdy_settings();
+    const spdy::SpdySettings& settings = settings_storage.Get(host_port_pair_);
+
+    spdy::SpdySettings::const_iterator it;
+    for (it = settings.begin(); it != settings.end(); ++it) {
+      const spdy::SpdySetting setting = *it;
+      switch (setting.first.id()) {
+        case spdy::SETTINGS_CURRENT_CWND:
+          UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsCwnd",
+                                      setting.second,
+                                      1, 200, 100);
+          break;
+        case spdy::SETTINGS_ROUND_TRIP_TIME:
+          UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsRTT",
+                                      setting.second,
+                                      1, 1200, 100);
+          break;
+        case spdy::SETTINGS_DOWNLOAD_RETRANS_RATE:
+          UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdySettingsRetransRate",
+                                      setting.second,
+                                      1, 100, 50);
+          break;
+      }
+    }
+  }
+}
+
+}  // namespace net
diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h
new file mode 100644
index 0000000..3add9b0
--- /dev/null
+++ b/net/spdy/spdy_session.h
@@ -0,0 +1,342 @@
+// Copyright (c) 2010 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_SPDY_SPDY_SESSION_H_
+#define NET_SPDY_SPDY_SESSION_H_
+
+#include <deque>
+#include <list>
+#include <map>
+#include <queue>
+#include <string>
+
+#include "base/linked_ptr.h"
+#include "base/ref_counted.h"
+#include "net/base/io_buffer.h"
+#include "net/base/load_states.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/base/request_priority.h"
+#include "net/base/ssl_config_service.h"
+#include "net/base/upload_data_stream.h"
+#include "net/socket/client_socket.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/tcp_client_socket_pool.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_io_buffer.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/spdy/spdy_session_pool.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"  // For FRIEND_TEST
+
+namespace net {
+
+class SpdyStream;
+class HttpNetworkSession;
+class BoundNetLog;
+class SSLInfo;
+
+class SpdySession : public base::RefCounted<SpdySession>,
+                    public spdy::SpdyFramerVisitorInterface {
+ public:
+  // Create a new SpdySession.
+  // |host_port_pair| is the host/port that this session connects to.
+  // |session| is the HttpNetworkSession.  |net_log| is the NetLog that we log
+  // network events to.
+  SpdySession(const HostPortPair& host_port_pair, HttpNetworkSession* session,
+              NetLog* net_log);
+
+  const HostPortPair& host_port_pair() const { return host_port_pair_; }
+
+  // Connect the Spdy Socket.
+  // Returns net::Error::OK on success.
+  // Note that this call does not wait for the connect to complete. Callers can
+  // immediately start using the SpdySession while it connects.
+  net::Error Connect(const std::string& group_name,
+                     const scoped_refptr<TCPSocketParams>& destination,
+                     RequestPriority priority);
+
+  // Get a pushed stream for a given |url|.
+  // If the server initiates a stream, it might already exist for a given path.
+  // The server might also not have initiated the stream yet, but indicated it
+  // will via X-Associated-Content.  Writes the stream out to |spdy_stream|.
+  // Returns a net error code.
+  int GetPushStream(
+      const GURL& url,
+      scoped_refptr<SpdyStream>* spdy_stream,
+      const BoundNetLog& stream_net_log);
+
+  // Create a new stream for a given |url|.  Writes it out to |spdy_stream|.
+  // Returns a net error code, possibly ERR_IO_PENDING.
+  int CreateStream(
+      const GURL& url,
+      RequestPriority priority,
+      scoped_refptr<SpdyStream>* spdy_stream,
+      const BoundNetLog& stream_net_log,
+      CompletionCallback* callback);
+
+  // Remove PendingCreateStream objects on transaction deletion
+  void CancelPendingCreateStreams(const scoped_refptr<SpdyStream>* spdy_stream);
+
+  // Used by SpdySessionPool to initialize with a pre-existing SSL socket.
+  // Returns OK on success, or an error on failure.
+  net::Error InitializeWithSSLSocket(ClientSocketHandle* connection,
+                                     int certificate_error_code);
+
+  // Send the SYN frame for |stream_id|.
+  int WriteSynStream(
+      spdy::SpdyStreamId stream_id,
+      RequestPriority priority,
+      spdy::SpdyControlFlags flags,
+      const linked_ptr<spdy::SpdyHeaderBlock>& headers);
+
+  // Write a data frame to the stream.
+  // Used to create and queue a data frame for the given stream.
+  int WriteStreamData(spdy::SpdyStreamId stream_id, net::IOBuffer* data,
+                      int len,
+                      spdy::SpdyDataFlags flags);
+
+  // Close a stream.
+  void CloseStream(spdy::SpdyStreamId stream_id, int status);
+
+  // Reset a stream by sending a RST_STREAM frame with given status code.
+  // Also closes the stream.  Was not piggybacked to CloseStream since not
+  // all of the calls to CloseStream necessitate sending a RST_STREAM.
+  void ResetStream(spdy::SpdyStreamId stream_id, spdy::SpdyStatusCodes status);
+
+  // Check if a stream is active.
+  bool IsStreamActive(spdy::SpdyStreamId stream_id) const;
+
+  // The LoadState is used for informing the user of the current network
+  // status, such as "resolving host", "connecting", etc.
+  LoadState GetLoadState() const;
+
+  // Fills SSL info in |ssl_info| and returns true when SSL is in use.
+  bool GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated);
+
+  // Enable or disable SSL.
+  static void SetSSLMode(bool enable) { use_ssl_ = enable; }
+  static bool SSLMode() { return use_ssl_; }
+
+  // If session is closed, no new streams/transactions should be created.
+  bool IsClosed() const { return state_ == CLOSED; }
+
+  // Closes this session.  This will close all active streams and mark
+  // the session as permanently closed.
+  // |err| should not be OK; this function is intended to be called on
+  // error.
+  void CloseSessionOnError(net::Error err);
+
+ private:
+  friend class base::RefCounted<SpdySession>;
+  FRIEND_TEST(SpdySessionTest, GetActivePushStream);
+
+  enum State {
+    IDLE,
+    CONNECTING,
+    CONNECTED,
+    CLOSED
+  };
+
+  enum { kDefaultMaxConcurrentStreams = 100 };  // TODO(mbelshe) remove this
+
+  struct PendingCreateStream {
+    const GURL* url;
+    RequestPriority priority;
+    scoped_refptr<SpdyStream>* spdy_stream;
+    const BoundNetLog* stream_net_log;
+    CompletionCallback* callback;
+
+    PendingCreateStream(const GURL& url, RequestPriority priority,
+                        scoped_refptr<SpdyStream>* spdy_stream,
+                        const BoundNetLog& stream_net_log,
+                        CompletionCallback* callback)
+        : url(&url), priority(priority), spdy_stream(spdy_stream),
+          stream_net_log(&stream_net_log), callback(callback) { }
+  };
+  typedef std::queue<PendingCreateStream, std::list< PendingCreateStream> >
+      PendingCreateStreamQueue;
+  typedef std::map<int, scoped_refptr<SpdyStream> > ActiveStreamMap;
+  // Only HTTP push a stream.
+  typedef std::list<scoped_refptr<SpdyStream> > ActivePushedStreamList;
+  typedef std::map<std::string, scoped_refptr<SpdyStream> > PendingStreamMap;
+  typedef std::priority_queue<SpdyIOBuffer> OutputQueue;
+
+  virtual ~SpdySession();
+
+  void ProcessPendingCreateStreams();
+  int CreateStreamImpl(
+      const GURL& url,
+      RequestPriority priority,
+      scoped_refptr<SpdyStream>* spdy_stream,
+      const BoundNetLog& stream_net_log);
+
+  // SpdyFramerVisitorInterface
+  virtual void OnError(spdy::SpdyFramer*);
+  virtual void OnStreamFrameData(spdy::SpdyStreamId stream_id,
+                                 const char* data,
+                                 size_t len);
+  virtual void OnControl(const spdy::SpdyControlFrame* frame);
+
+  // Control frame handlers.
+  void OnSyn(const spdy::SpdySynStreamControlFrame& frame,
+             const linked_ptr<spdy::SpdyHeaderBlock>& headers);
+  void OnSynReply(const spdy::SpdySynReplyControlFrame& frame,
+                  const linked_ptr<spdy::SpdyHeaderBlock>& headers);
+  void OnFin(const spdy::SpdyRstStreamControlFrame& frame);
+  void OnGoAway(const spdy::SpdyGoAwayControlFrame& frame);
+  void OnSettings(const spdy::SpdySettingsControlFrame& frame);
+  void OnWindowUpdate(const spdy::SpdyWindowUpdateControlFrame& frame);
+
+  // IO Callbacks
+  void OnTCPConnect(int result);
+  void OnSSLConnect(int result);
+  void OnReadComplete(int result);
+  void OnWriteComplete(int result);
+
+  // Send relevant SETTINGS.  This is generally called on connection setup.
+  void SendSettings();
+
+  // Handle SETTINGS.  Either when we send settings, or when we receive a
+  // SETTINGS ontrol frame, update our SpdySession accordingly.
+  void HandleSettings(const spdy::SpdySettings& settings);
+
+  // Start reading from the socket.
+  // Returns OK on success, or an error on failure.
+  net::Error ReadSocket();
+
+  // Write current data to the socket.
+  void WriteSocketLater();
+  void WriteSocket();
+
+  // Get a new stream id.
+  int GetNewStreamId();
+
+  // Queue a frame for sending.
+  // |frame| is the frame to send.
+  // |priority| is the priority for insertion into the queue.
+  // |stream| is the stream which this IO is associated with (or NULL).
+  void QueueFrame(spdy::SpdyFrame* frame, spdy::SpdyPriority priority,
+                  SpdyStream* stream);
+
+  // Track active streams in the active stream list.
+  void ActivateStream(SpdyStream* stream);
+  void DeleteStream(spdy::SpdyStreamId id, int status);
+
+  // Removes this session from the session pool.
+  void RemoveFromPool();
+
+  // Check if we have a pending pushed-stream for this url
+  // Returns the stream if found (and returns it from the pending
+  // list), returns NULL otherwise.
+  scoped_refptr<SpdyStream> GetActivePushStream(const std::string& url);
+
+  // Calls OnResponseReceived().
+  // Returns true if successful.
+  bool Respond(const spdy::SpdyHeaderBlock& headers,
+               const scoped_refptr<SpdyStream> stream);
+
+  void RecordHistograms();
+
+  // Closes all streams.  Used as part of shutdown.
+  void CloseAllStreams(net::Error status);
+
+  // Callbacks for the Spdy session.
+  CompletionCallbackImpl<SpdySession> connect_callback_;
+  CompletionCallbackImpl<SpdySession> ssl_connect_callback_;
+  CompletionCallbackImpl<SpdySession> read_callback_;
+  CompletionCallbackImpl<SpdySession> write_callback_;
+
+  // The domain this session is connected to.
+  const HostPortPair host_port_pair_;
+
+  SSLConfig ssl_config_;
+
+  scoped_refptr<HttpNetworkSession> session_;
+
+  // The socket handle for this session.
+  scoped_ptr<ClientSocketHandle> connection_;
+
+  // The read buffer used to read data from the socket.
+  scoped_refptr<IOBuffer> read_buffer_;
+  bool read_pending_;
+
+  int stream_hi_water_mark_;  // The next stream id to use.
+
+  // Queue, for each priority, of pending Create Streams that have not
+  // yet been satisfied
+  PendingCreateStreamQueue create_stream_queues_[NUM_PRIORITIES];
+
+  // TODO(mbelshe): We need to track these stream lists better.
+  //                I suspect it is possible to remove a stream from
+  //                one list, but not the other.
+
+  // Map from stream id to all active streams.  Streams are active in the sense
+  // that they have a consumer (typically SpdyNetworkTransaction and regardless
+  // of whether or not there is currently any ongoing IO [might be waiting for
+  // the server to start pushing the stream]) or there are still network events
+  // incoming even though the consumer has already gone away (cancellation).
+  // TODO(willchan): Perhaps we should separate out cancelled streams and move
+  // them into a separate ActiveStreamMap, and not deliver network events to
+  // them?
+  ActiveStreamMap active_streams_;
+  // List of all the streams that have already started to be pushed by the
+  // server, but do not have consumers yet.
+  ActivePushedStreamList pushed_streams_;
+  // List of streams declared in X-Associated-Content headers, but do not have
+  // consumers yet.
+  // The key is a string representing the path of the URI being pushed.
+  PendingStreamMap pending_streams_;
+
+  // As we gather data to be sent, we put it into the output queue.
+  OutputQueue queue_;
+
+  // The packet we are currently sending.
+  bool write_pending_;            // Will be true when a write is in progress.
+  SpdyIOBuffer in_flight_write_;  // This is the write buffer in progress.
+
+  // Flag if we have a pending message scheduled for WriteSocket.
+  bool delayed_write_pending_;
+
+  // Flag if we're using an SSL connection for this SpdySession.
+  bool is_secure_;
+
+  // Certificate error code when using a secure connection.
+  int certificate_error_code_;
+
+  // Spdy Frame state.
+  spdy::SpdyFramer spdy_framer_;
+
+  // If an error has occurred on the session, the session is effectively
+  // dead.  Record this error here.  When no error has occurred, |error_| will
+  // be OK.
+  net::Error error_;
+  State state_;
+
+  // Limits
+  size_t max_concurrent_streams_;  // 0 if no limit
+
+  // Some statistics counters for the session.
+  int streams_initiated_count_;
+  int streams_pushed_count_;
+  int streams_pushed_and_claimed_count_;
+  int streams_abandoned_count_;
+  bool sent_settings_;      // Did this session send settings when it started.
+  bool received_settings_;  // Did this session receive at least one settings
+                            // frame.
+
+  bool in_session_pool_;  // True if the session is currently in the pool.
+
+  int initial_window_size_; // Initial window size for the session; can be
+                            // changed by an arriving SETTINGS frame; newly
+                            // created streams use this value for the initial
+                            // window size.
+
+  BoundNetLog net_log_;
+
+  static bool use_ssl_;
+};
+
+}  // namespace net
+
+#endif  // NET_SPDY_SPDY_SESSION_H_
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc
new file mode 100644
index 0000000..8caa040
--- /dev/null
+++ b/net/spdy/spdy_session_pool.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2010 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/spdy/spdy_session_pool.h"
+
+#include "base/logging.h"
+#include "net/spdy/spdy_session.h"
+
+namespace net {
+
+// The maximum number of sessions to open to a single domain.
+static const size_t kMaxSessionsPerDomain = 1;
+
+int SpdySessionPool::g_max_sessions_per_domain = kMaxSessionsPerDomain;
+
+SpdySessionPool::SpdySessionPool() {
+  NetworkChangeNotifier::AddObserver(this);
+}
+
+SpdySessionPool::~SpdySessionPool() {
+  CloseAllSessions();
+
+  NetworkChangeNotifier::RemoveObserver(this);
+}
+
+scoped_refptr<SpdySession> SpdySessionPool::Get(
+    const HostPortPair& host_port_pair, HttpNetworkSession* session,
+    const BoundNetLog& net_log) {
+  scoped_refptr<SpdySession> spdy_session;
+  SpdySessionList* list = GetSessionList(host_port_pair);
+  if (list) {
+    if (list->size() >= static_cast<unsigned int>(g_max_sessions_per_domain)) {
+      spdy_session = list->front();
+      list->pop_front();
+    }
+  } else {
+    list = AddSessionList(host_port_pair);
+  }
+
+  DCHECK(list);
+  if (!spdy_session)
+    spdy_session = new SpdySession(host_port_pair, session, net_log.net_log());
+
+  DCHECK(spdy_session);
+  list->push_back(spdy_session);
+  DCHECK_LE(list->size(), static_cast<unsigned int>(g_max_sessions_per_domain));
+  return spdy_session;
+}
+
+net::Error SpdySessionPool::GetSpdySessionFromSSLSocket(
+    const HostPortPair& host_port_pair,
+    HttpNetworkSession* session,
+    ClientSocketHandle* connection,
+    const BoundNetLog& net_log,
+    int certificate_error_code,
+    scoped_refptr<SpdySession>* spdy_session) {
+  // Create the SPDY session and add it to the pool.
+  *spdy_session = new SpdySession(host_port_pair, session, net_log.net_log());
+  SpdySessionList* list = GetSessionList(host_port_pair);
+  if (!list)
+    list = AddSessionList(host_port_pair);
+  DCHECK(list->empty());
+  list->push_back(*spdy_session);
+
+  // Now we can initialize the session with the SSL socket.
+  return (*spdy_session)->InitializeWithSSLSocket(connection,
+                                                  certificate_error_code);
+}
+
+bool SpdySessionPool::HasSession(const HostPortPair& host_port_pair) const {
+  if (GetSessionList(host_port_pair))
+    return true;
+  return false;
+}
+
+void SpdySessionPool::Remove(const scoped_refptr<SpdySession>& session) {
+  SpdySessionList* list = GetSessionList(session->host_port_pair());
+  DCHECK(list);  // We really shouldn't remove if we've already been removed.
+  if (!list)
+    return;
+  list->remove(session);
+  if (list->empty())
+    RemoveSessionList(session->host_port_pair());
+}
+
+void SpdySessionPool::OnIPAddressChanged() {
+  ClearSessions();
+}
+
+SpdySessionPool::SpdySessionList*
+    SpdySessionPool::AddSessionList(const HostPortPair& host_port_pair) {
+  DCHECK(sessions_.find(host_port_pair) == sessions_.end());
+  SpdySessionPool::SpdySessionList* list = new SpdySessionList();
+  sessions_[host_port_pair] = list;
+  return list;
+}
+
+SpdySessionPool::SpdySessionList*
+    SpdySessionPool::GetSessionList(const HostPortPair& host_port_pair) {
+  SpdySessionsMap::iterator it = sessions_.find(host_port_pair);
+  if (it == sessions_.end())
+    return NULL;
+  return it->second;
+}
+
+const SpdySessionPool::SpdySessionList*
+    SpdySessionPool::GetSessionList(const HostPortPair& host_port_pair) const {
+  SpdySessionsMap::const_iterator it = sessions_.find(host_port_pair);
+  if (it == sessions_.end())
+    return NULL;
+  return it->second;
+}
+
+void SpdySessionPool::RemoveSessionList(const HostPortPair& host_port_pair) {
+  SpdySessionList* list = GetSessionList(host_port_pair);
+  if (list) {
+    delete list;
+    sessions_.erase(host_port_pair);
+  } else {
+    DCHECK(false) << "removing orphaned session list";
+  }
+}
+
+void SpdySessionPool::ClearSessions() {
+  while (!sessions_.empty()) {
+    SpdySessionList* list = sessions_.begin()->second;
+    DCHECK(list);
+    sessions_.erase(sessions_.begin()->first);
+    while (list->size()) {
+      list->pop_front();
+    }
+    delete list;
+  }
+}
+
+void SpdySessionPool::CloseAllSessions() {
+  while (!sessions_.empty()) {
+    SpdySessionList* list = sessions_.begin()->second;
+    DCHECK(list);
+    const scoped_refptr<SpdySession>& session = list->front();
+    DCHECK(session);
+    session->CloseSessionOnError(net::ERR_ABORTED);
+  }
+}
+
+}  // namespace net
diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h
new file mode 100644
index 0000000..1004169
--- /dev/null
+++ b/net/spdy/spdy_session_pool.h
@@ -0,0 +1,113 @@
+// Copyright (c) 2010 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_SPDY_SPDY_SESSION_POOL_H_
+#define NET_SPDY_SPDY_SESSION_POOL_H_
+
+#include <map>
+#include <list>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_errors.h"
+#include "net/base/network_change_notifier.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"  // For FRIEND_TEST
+
+namespace net {
+
+class BoundNetLog;
+class ClientSocketHandle;
+class HttpNetworkSession;
+class SpdySession;
+
+// This is a very simple pool for open SpdySessions.
+// TODO(mbelshe): Make this production ready.
+class SpdySessionPool
+    : public base::RefCounted<SpdySessionPool>,
+      public NetworkChangeNotifier::Observer {
+ public:
+  SpdySessionPool();
+
+  // Either returns an existing SpdySession or creates a new SpdySession for
+  // use.
+  scoped_refptr<SpdySession> Get(
+      const HostPortPair& host_port_pair, HttpNetworkSession* session,
+      const BoundNetLog& net_log);
+
+  // Set the maximum concurrent sessions per domain.
+  static void set_max_sessions_per_domain(int max) {
+    if (max >= 1)
+      g_max_sessions_per_domain = max;
+  }
+
+  // Builds a SpdySession from an existing SSL socket.  Users should try
+  // calling Get() first to use an existing SpdySession so we don't get
+  // multiple SpdySessions per domain.  Note that ownership of |connection| is
+  // transferred from the caller to the SpdySession.
+  // |certificate_error_code| is used to indicate the certificate error
+  // encountered when connecting the SSL socket.  OK means there was no error.
+  // Returns OK on success, and the |spdy_session| will be provided.
+  // Returns an error on failure, and |spdy_session| will be NULL.
+  net::Error GetSpdySessionFromSSLSocket(
+      const HostPortPair& host_port_pair,
+      HttpNetworkSession* session,
+      ClientSocketHandle* connection,
+      const BoundNetLog& net_log,
+      int certificate_error_code,
+      scoped_refptr<SpdySession>* spdy_session);
+
+  // TODO(willchan): Consider renaming to HasReusableSession, since perhaps we
+  // should be creating a new session.
+  bool HasSession(const HostPortPair& host_port_pair)const;
+
+  // Close all Spdy Sessions; used for debugging.
+  void CloseAllSessions();
+
+  // Removes a SpdySession from the SpdySessionPool.
+  void Remove(const scoped_refptr<SpdySession>& session);
+
+  // NetworkChangeNotifier::Observer methods:
+
+  // We flush all idle sessions and release references to the active ones so
+  // they won't get re-used.  The active ones will either complete successfully
+  // or error out due to the IP address change.
+  virtual void OnIPAddressChanged();
+
+ private:
+  friend class base::RefCounted<SpdySessionPool>;
+  friend class SpdySessionPoolPeer;  // For testing.
+  friend class SpdyNetworkTransactionTest;  // For testing.
+  FRIEND_TEST(SpdyNetworkTransactionTest, WindowUpdateOverflow);
+
+  typedef std::list<scoped_refptr<SpdySession> > SpdySessionList;
+  typedef std::map<HostPortPair, SpdySessionList*> SpdySessionsMap;
+
+  virtual ~SpdySessionPool();
+
+  // Helper functions for manipulating the lists.
+  SpdySessionList* AddSessionList(const HostPortPair& host_port_pair);
+  SpdySessionList* GetSessionList(const HostPortPair& host_port_pair);
+  const SpdySessionList* GetSessionList(
+      const HostPortPair& host_port_pair) const;
+  void RemoveSessionList(const HostPortPair& host_port_pair);
+  // Releases the SpdySessionPool reference to all sessions.  Will result in all
+  // idle sessions being deleted, and the active sessions from being reused, so
+  // they will be deleted once all active streams belonging to that session go
+  // away.
+  void ClearSessions();
+
+  // This is our weak session pool - one session per domain.
+  SpdySessionsMap sessions_;
+
+  static int g_max_sessions_per_domain;
+
+  DISALLOW_COPY_AND_ASSIGN(SpdySessionPool);
+};
+
+}  // namespace net
+
+#endif  // NET_SPDY_SPDY_SESSION_POOL_H_
diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc
new file mode 100644
index 0000000..c012347
--- /dev/null
+++ b/net/spdy/spdy_session_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2010 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/spdy/spdy_io_buffer.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_stream.h"
+#include "net/spdy/spdy_test_util.h"
+#include "testing/platform_test.h"
+
+namespace net {
+
+// TODO(cbentzel): Expose compression setter/getter in public SpdySession
+//                 interface rather than going through all these contortions.
+class SpdySessionTest : public PlatformTest {
+ public:
+  static void TurnOffCompression() {
+    spdy::SpdyFramer::set_enable_compression_default(false);
+  }
+};
+
+namespace {
+// Test the SpdyIOBuffer class.
+TEST_F(SpdySessionTest, SpdyIOBuffer) {
+  std::priority_queue<SpdyIOBuffer> queue_;
+  const size_t kQueueSize = 100;
+
+  // Insert 100 items; pri 100 to 1.
+  for (size_t index = 0; index < kQueueSize; ++index) {
+    SpdyIOBuffer buffer(new IOBuffer(), 0, kQueueSize - index, NULL);
+    queue_.push(buffer);
+  }
+
+  // Insert several priority 0 items last.
+  const size_t kNumDuplicates = 12;
+  IOBufferWithSize* buffers[kNumDuplicates];
+  for (size_t index = 0; index < kNumDuplicates; ++index) {
+    buffers[index] = new IOBufferWithSize(index+1);
+    queue_.push(SpdyIOBuffer(buffers[index], buffers[index]->size(), 0, NULL));
+  }
+
+  EXPECT_EQ(kQueueSize + kNumDuplicates, queue_.size());
+
+  // Verify the P0 items come out in FIFO order.
+  for (size_t index = 0; index < kNumDuplicates; ++index) {
+    SpdyIOBuffer buffer = queue_.top();
+    EXPECT_EQ(0, buffer.priority());
+    EXPECT_EQ(index + 1, buffer.size());
+    queue_.pop();
+  }
+
+  int priority = 1;
+  while (queue_.size()) {
+    SpdyIOBuffer buffer = queue_.top();
+    EXPECT_EQ(priority++, buffer.priority());
+    queue_.pop();
+  }
+}
+
+TEST_F(SpdySessionTest, GoAway) {
+  SpdySessionDependencies session_deps;
+  session_deps.host_resolver->set_synchronous_mode(true);
+
+  MockConnect connect_data(false, OK);
+  scoped_ptr<spdy::SpdyFrame> goaway(ConstructSpdyGoAway());
+  MockRead reads[] = {
+    CreateMockRead(*goaway),
+    MockRead(false, 0, 0)  // EOF
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
+  data.set_connect_data(connect_data);
+  session_deps.socket_factory.AddSocketDataProvider(&data);
+
+  SSLSocketDataProvider ssl(false, OK);
+  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+  scoped_refptr<HttpNetworkSession> http_session(
+      SpdySessionDependencies::SpdyCreateSession(&session_deps));
+
+  const std::string kTestHost("www.foo.com");
+  const int kTestPort = 80;
+  HostPortPair test_host_port_pair;
+  test_host_port_pair.host = kTestHost;
+  test_host_port_pair.port = kTestPort;
+
+  scoped_refptr<SpdySessionPool> spdy_session_pool(
+      http_session->spdy_session_pool());
+  EXPECT_FALSE(spdy_session_pool->HasSession(test_host_port_pair));
+  scoped_refptr<SpdySession> session =
+      spdy_session_pool->Get(
+          test_host_port_pair, http_session.get(), BoundNetLog());
+  EXPECT_TRUE(spdy_session_pool->HasSession(test_host_port_pair));
+
+  scoped_refptr<TCPSocketParams> tcp_params =
+      new TCPSocketParams(kTestHost, kTestPort, MEDIUM, GURL(), false);
+  int rv = session->Connect(kTestHost, tcp_params, MEDIUM);
+  ASSERT_EQ(OK, rv);
+
+  // Flush the SpdySession::OnReadComplete() task.
+  MessageLoop::current()->RunAllPending();
+
+  EXPECT_FALSE(spdy_session_pool->HasSession(test_host_port_pair));
+
+  scoped_refptr<SpdySession> session2 =
+      spdy_session_pool->Get(
+          test_host_port_pair, http_session.get(), BoundNetLog());
+
+  // Delete the first session.
+  session = NULL;
+
+  // Delete the second session.
+  spdy_session_pool->Remove(session2);
+  session2 = NULL;
+}
+
+}  // namespace
+
+TEST_F(SpdySessionTest, GetActivePushStream) {
+  spdy::SpdyFramer framer;
+  SpdySessionTest::TurnOffCompression();
+
+  SpdySessionDependencies session_deps;
+  session_deps.host_resolver->set_synchronous_mode(true);
+
+  MockConnect connect_data(false, OK);
+  spdy::SpdyHeaderBlock headers;
+  headers["path"] = "/foo.js";
+  headers["status"] = "200";
+  headers["version"] = "HTTP/1.1";
+  scoped_ptr<spdy::SpdyFrame> push_syn(framer.CreateSynStream(
+      2, 1, 0, spdy::CONTROL_FLAG_NONE, false, &headers));
+  MockRead reads[] = {
+    CreateMockRead(*push_syn),
+    MockRead(true, ERR_IO_PENDING, 0)  // EOF
+  };
+  StaticSocketDataProvider data(reads, arraysize(reads), NULL, 0);
+  data.set_connect_data(connect_data);
+  session_deps.socket_factory.AddSocketDataProvider(&data);
+
+  SSLSocketDataProvider ssl(false, OK);
+  session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+  scoped_refptr<HttpNetworkSession> http_session(
+      SpdySessionDependencies::SpdyCreateSession(&session_deps));
+
+  const std::string kTestHost("www.foo.com");
+  const int kTestPort = 80;
+  HostPortPair test_host_port_pair;
+  test_host_port_pair.host = kTestHost;
+  test_host_port_pair.port = kTestPort;
+
+  scoped_refptr<SpdySessionPool> spdy_session_pool(
+      http_session->spdy_session_pool());
+  EXPECT_FALSE(spdy_session_pool->HasSession(test_host_port_pair));
+  scoped_refptr<SpdySession> session =
+      spdy_session_pool->Get(
+          test_host_port_pair, http_session.get(), BoundNetLog());
+  EXPECT_TRUE(spdy_session_pool->HasSession(test_host_port_pair));
+
+  // No push streams should exist in the beginning.
+  std::string test_push_path = "/foo.js";
+  scoped_refptr<SpdyStream> first_stream = session->GetActivePushStream(
+      test_push_path);
+  EXPECT_EQ(static_cast<SpdyStream*>(NULL), first_stream.get());
+
+  // Read in the data which contains a server-issued SYN_STREAM.
+  scoped_refptr<TCPSocketParams> tcp_params =
+      new TCPSocketParams(test_host_port_pair, MEDIUM, GURL(), false);
+  int rv = session->Connect(kTestHost, tcp_params, MEDIUM);
+  ASSERT_EQ(OK, rv);
+  MessageLoop::current()->RunAllPending();
+
+  // An unpushed path should not work.
+  scoped_refptr<SpdyStream> unpushed_stream = session->GetActivePushStream(
+      "/unpushed_path");
+  EXPECT_EQ(static_cast<SpdyStream*>(NULL), unpushed_stream.get());
+
+  // The pushed path should be found.
+  scoped_refptr<SpdyStream> second_stream = session->GetActivePushStream(
+      test_push_path);
+  ASSERT_NE(static_cast<SpdyStream*>(NULL), second_stream.get());
+  EXPECT_EQ(test_push_path, second_stream->path());
+  EXPECT_EQ(2U, second_stream->stream_id());
+  EXPECT_EQ(0, second_stream->priority());
+
+  // Clean up
+  second_stream = NULL;
+  session = NULL;
+  spdy_session_pool->CloseAllSessions();
+
+  // RunAllPending needs to be called here because the
+  // ClientSocketPoolBase posts a task to clean up and destroy the
+  // underlying socket.
+  MessageLoop::current()->RunAllPending();
+}
+
+}  // namespace net
diff --git a/net/spdy/spdy_settings_storage.cc b/net/spdy/spdy_settings_storage.cc
new file mode 100644
index 0000000..32069b7
--- /dev/null
+++ b/net/spdy/spdy_settings_storage.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2010 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/spdy/spdy_settings_storage.h"
+
+#include <utility>
+
+namespace net {
+
+SpdySettingsStorage::SpdySettingsStorage() {
+}
+
+const spdy::SpdySettings& SpdySettingsStorage::Get(
+    const HostPortPair& host_port_pair) const {
+  SettingsMap::const_iterator it = settings_map_.find(host_port_pair);
+  if (it == settings_map_.end()) {
+    static const spdy::SpdySettings kEmpty;
+    return kEmpty;
+  }
+  return it->second;
+}
+
+void SpdySettingsStorage::Set(const HostPortPair& host_port_pair,
+                              const spdy::SpdySettings& settings) {
+  spdy::SpdySettings persistent_settings;
+
+  // Iterate through the list, and only copy those settings which are marked
+  // for persistence.
+  spdy::SpdySettings::const_iterator it;
+  for (it = settings.begin(); it != settings.end(); ++it) {
+    spdy::SettingsFlagsAndId id = it->first;
+    if (id.flags() & spdy::SETTINGS_FLAG_PLEASE_PERSIST) {
+      id.set_flags(spdy::SETTINGS_FLAG_PERSISTED);
+      persistent_settings.push_back(std::make_pair(id, it->second));
+    }
+  }
+
+  // If we didn't persist anything, then we are done.
+  if (persistent_settings.empty())
+    return;
+
+  settings_map_[host_port_pair] = persistent_settings;
+}
+
+}  // namespace net
+
diff --git a/net/spdy/spdy_settings_storage.h b/net/spdy/spdy_settings_storage.h
new file mode 100644
index 0000000..93b44dc
--- /dev/null
+++ b/net/spdy/spdy_settings_storage.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 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_SPDY_SPDY_SETTING_STORAGE_H_
+#define NET_SPDY_SPDY_SETTING_STORAGE_H_
+
+#include <map>
+#include "base/basictypes.h"
+#include "net/base/host_port_pair.h"
+#include "net/spdy/spdy_framer.h"
+
+namespace net {
+
+// SpdySettingsStorage stores SpdySettings which have been transmitted between
+// endpoints for the SPDY SETTINGS frame.
+class SpdySettingsStorage {
+ public:
+  SpdySettingsStorage();
+
+  // Get a copy of the SpdySettings stored for a host.
+  // If no settings are stored, returns an empty set of settings.
+  const spdy::SpdySettings& Get(const HostPortPair& host_port_pair) const;
+
+  // Save settings for a host.
+  void Set(const HostPortPair& host_port_pair,
+           const spdy::SpdySettings& settings);
+
+ private:
+  typedef std::map<HostPortPair, spdy::SpdySettings> SettingsMap;
+
+  SettingsMap settings_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpdySettingsStorage);
+};
+
+}  // namespace net
+
+#endif  // NET_SPDY_SPDY_SETTING_STORAGE_H_
+
diff --git a/net/spdy/spdy_stream.cc b/net/spdy/spdy_stream.cc
new file mode 100644
index 0000000..a5f9d1f
--- /dev/null
+++ b/net/spdy/spdy_stream.cc
@@ -0,0 +1,437 @@
+// Copyright (c) 2010 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/spdy/spdy_stream.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/singleton.h"
+#include "net/spdy/spdy_session.h"
+
+namespace net {
+
+SpdyStream::SpdyStream(
+    SpdySession* session, spdy::SpdyStreamId stream_id, bool pushed)
+    : stream_id_(stream_id),
+      priority_(0),
+      pushed_(pushed),
+      metrics_(Singleton<BandwidthMetrics>::get()),
+      syn_reply_received_(false),
+      session_(session),
+      delegate_(NULL),
+      request_time_(base::Time::Now()),
+      response_(new spdy::SpdyHeaderBlock),
+      response_complete_(false),
+      io_state_(STATE_NONE),
+      response_status_(OK),
+      cancelled_(false),
+      send_bytes_(0),
+      recv_bytes_(0),
+      histograms_recorded_(false) {}
+
+SpdyStream::~SpdyStream() {
+  DLOG(INFO) << "Deleting SpdyStream for stream " << stream_id_;
+
+  // When the stream_id_ is 0, we expect that it is because
+  // we've cancelled or closed the stream and set the stream_id to 0.
+  if (!stream_id_)
+    DCHECK(response_complete_);
+}
+
+void SpdyStream::SetDelegate(Delegate* delegate) {
+  CHECK(delegate);
+  delegate_ = delegate;
+
+  if (!response_->empty()) {
+    // The stream already got response.
+    delegate_->OnResponseReceived(*response_, response_time_, OK);
+  }
+
+  std::vector<scoped_refptr<IOBufferWithSize> > buffers;
+  buffers.swap(pending_buffers_);
+  for (size_t i = 0; i < buffers.size(); ++i) {
+    if (delegate_)
+      delegate_->OnDataReceived(buffers[i]->data(), buffers[i]->size());
+  }
+}
+
+void SpdyStream::DetachDelegate() {
+  delegate_ = NULL;
+  if (!cancelled())
+    Cancel();
+}
+
+const linked_ptr<spdy::SpdyHeaderBlock>& SpdyStream::spdy_headers() const {
+  return request_;
+}
+
+void SpdyStream::set_spdy_headers(
+    const linked_ptr<spdy::SpdyHeaderBlock>& headers) {
+  request_ = headers;
+}
+
+void SpdyStream::UpdateWindowSize(int delta_window_size) {
+  DCHECK_GE(delta_window_size, 1);
+  int new_window_size = window_size_ + delta_window_size;
+
+  // it's valid for window_size_ to become negative (via an incoming
+  // SETTINGS frame which is handled in SpdySession::OnSettings), in which
+  // case incoming WINDOW_UPDATEs will eventually make it positive;
+  // however, if window_size_ is positive and incoming WINDOW_UPDATE makes
+  // it negative, we have an overflow.
+  if (window_size_ > 0 && new_window_size < 0) {
+    LOG(WARNING) << "Received WINDOW_UPDATE [delta:" << delta_window_size
+                 << "] for stream " << stream_id_
+                 << " overflows window size [current:" << window_size_ << "]";
+    session_->ResetStream(stream_id_, spdy::FLOW_CONTROL_ERROR);
+    return;
+  }
+  window_size_ = new_window_size;
+}
+
+base::Time SpdyStream::GetRequestTime() const {
+  return request_time_;
+}
+
+void SpdyStream::SetRequestTime(base::Time t) {
+  request_time_ = t;
+}
+
+int SpdyStream::OnResponseReceived(const spdy::SpdyHeaderBlock& response) {
+  int rv = OK;
+  LOG(INFO) << "OnResponseReceived";
+  DCHECK_NE(io_state_, STATE_OPEN);
+
+  metrics_.StartStream();
+
+  DCHECK(response_->empty());
+  *response_ = response;  // TODO(ukai): avoid copy.
+
+  recv_first_byte_time_ = base::TimeTicks::Now();
+  response_time_ = base::Time::Now();
+
+  if (io_state_ == STATE_NONE) {
+    CHECK(pushed_);
+    io_state_ = STATE_READ_HEADERS;
+  } else if (io_state_ == STATE_READ_HEADERS_COMPLETE) {
+    // This SpdyStream could be in this state in both true and false pushed_
+    // conditions.
+    // The false pushed_ condition (client request) will always go through
+    // this state.
+    // The true pushed_condition (server push) can be in this state when the
+    // client requests an X-Associated-Content piece of content prior
+    // to when the server push happens.
+  } else {
+    // We're not expecting a response while in this state.  Error!
+    rv = ERR_SPDY_PROTOCOL_ERROR;
+  }
+
+  rv = DoLoop(rv);
+  if (delegate_)
+    rv = delegate_->OnResponseReceived(*response_, response_time_, rv);
+  // if delegate_ is not yet attached, we'll return response when delegate
+  // gets attached to the stream.
+
+  return rv;
+}
+
+void SpdyStream::OnDataReceived(const char* data, int length) {
+  DCHECK_GE(length, 0);
+  LOG(INFO) << "SpdyStream: Data (" << length << " bytes) received for "
+            << stream_id_;
+
+  CHECK(!response_complete_);
+
+  // If we don't have a response, then the SYN_REPLY did not come through.
+  // We cannot pass data up to the caller unless the reply headers have been
+  // received.
+  if (response_->empty()) {
+    session_->CloseStream(stream_id_, ERR_SYN_REPLY_NOT_RECEIVED);
+    return;
+  }
+
+  // A zero-length read means that the stream is being closed.
+  if (!length) {
+    metrics_.StopStream();
+    scoped_refptr<SpdyStream> self(this);
+    session_->CloseStream(stream_id_, net::OK);
+    UpdateHistograms();
+    return;
+  }
+
+  // Track our bandwidth.
+  metrics_.RecordBytes(length);
+  recv_bytes_ += length;
+  recv_last_byte_time_ = base::TimeTicks::Now();
+
+  if (!delegate_) {
+    // It should be valid for this to happen in the server push case.
+    // We'll return received data when delegate gets attached to the stream.
+    IOBufferWithSize* buf = new IOBufferWithSize(length);
+    memcpy(buf->data(), data, length);
+    pending_buffers_.push_back(buf);
+    return;
+  }
+
+  delegate_->OnDataReceived(data, length);
+}
+
+void SpdyStream::OnWriteComplete(int status) {
+  // TODO(mbelshe): Check for cancellation here.  If we're cancelled, we
+  // should discontinue the DoLoop.
+
+  // It is possible that this stream was closed while we had a write pending.
+  if (response_complete_)
+    return;
+
+  if (status > 0)
+    send_bytes_ += status;
+
+  DoLoop(status);
+}
+
+void SpdyStream::OnClose(int status) {
+  response_complete_ = true;
+  response_status_ = status;
+  stream_id_ = 0;
+  Delegate* delegate = delegate_;
+  delegate_ = NULL;
+  if (delegate)
+    delegate->OnClose(status);
+}
+
+void SpdyStream::Cancel() {
+  cancelled_ = true;
+  session_->CloseStream(stream_id_, ERR_ABORTED);
+}
+
+int SpdyStream::DoSendRequest(bool has_upload_data) {
+  CHECK(!cancelled_);
+
+  if (!pushed_) {
+    spdy::SpdyControlFlags flags = spdy::CONTROL_FLAG_NONE;
+    if (!has_upload_data)
+      flags = spdy::CONTROL_FLAG_FIN;
+
+    CHECK(request_.get());
+    int result = session_->WriteSynStream(
+        stream_id_, static_cast<RequestPriority>(priority_), flags,
+        request_);
+    if (result != ERR_IO_PENDING)
+      return result;
+  }
+
+  send_time_ = base::TimeTicks::Now();
+
+  int result = OK;
+  if (!pushed_) {
+    DCHECK_EQ(io_state_, STATE_NONE);
+    io_state_ = STATE_SEND_HEADERS;
+  } else {
+    DCHECK(!has_upload_data);
+    if (!response_->empty()) {
+      // We already have response headers, so we don't need to read the header.
+      // Pushed stream should not have upload data.
+      // We don't need to call DoLoop() in this state.
+      DCHECK_EQ(io_state_, STATE_OPEN);
+      return OK;
+    } else {
+      io_state_ = STATE_READ_HEADERS;
+    }
+  }
+  return DoLoop(result);
+}
+
+int SpdyStream::DoReadResponseHeaders() {
+  CHECK(!cancelled_);
+
+  // The SYN_REPLY has already been received.
+  if (!response_->empty()) {
+    CHECK_EQ(STATE_OPEN, io_state_);
+    return OK;
+  } else {
+    CHECK_EQ(STATE_NONE, io_state_);
+  }
+
+
+  io_state_ = STATE_READ_HEADERS;
+  // Q: do we need to run DoLoop here?
+  return ERR_IO_PENDING;
+}
+
+int SpdyStream::WriteStreamData(IOBuffer* data, int length,
+                                spdy::SpdyDataFlags flags) {
+  return session_->WriteStreamData(stream_id_, data, length, flags);
+}
+
+bool SpdyStream::GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated) {
+  return session_->GetSSLInfo(ssl_info, was_npn_negotiated);
+}
+
+int SpdyStream::DoLoop(int result) {
+  do {
+    State state = io_state_;
+    io_state_ = STATE_NONE;
+    switch (state) {
+      // State machine 1: Send headers and wait for response headers.
+      case STATE_SEND_HEADERS:
+        CHECK_EQ(OK, result);
+        net_log_.BeginEvent(NetLog::TYPE_SPDY_STREAM_SEND_HEADERS, NULL);
+        result = DoSendHeaders();
+        break;
+      case STATE_SEND_HEADERS_COMPLETE:
+        net_log_.EndEvent(NetLog::TYPE_SPDY_STREAM_SEND_HEADERS, NULL);
+        result = DoSendHeadersComplete(result);
+        break;
+      case STATE_SEND_BODY:
+        CHECK_EQ(OK, result);
+        net_log_.BeginEvent(NetLog::TYPE_SPDY_STREAM_SEND_BODY, NULL);
+        result = DoSendBody();
+        break;
+      case STATE_SEND_BODY_COMPLETE:
+        net_log_.EndEvent(NetLog::TYPE_SPDY_STREAM_SEND_BODY, NULL);
+        result = DoSendBodyComplete(result);
+        break;
+      case STATE_READ_HEADERS:
+        CHECK_EQ(OK, result);
+        net_log_.BeginEvent(NetLog::TYPE_SPDY_STREAM_READ_HEADERS, NULL);
+        result = DoReadHeaders();
+        break;
+      case STATE_READ_HEADERS_COMPLETE:
+        net_log_.EndEvent(NetLog::TYPE_SPDY_STREAM_READ_HEADERS, NULL);
+        result = DoReadHeadersComplete(result);
+        break;
+
+      // State machine 2: connection is established.
+      // In STATE_OPEN, OnResponseReceived has already been called.
+      // OnDataReceived, OnClose and OnWriteCompelte can be called.
+      // Only OnWriteCompletee calls DoLoop(().
+      //
+      // For HTTP streams, no data is sent from the client while in the OPEN
+      // state, so OnWriteComplete is never called here.  The HTTP body is
+      // handled in the OnDataReceived callback, which does not call into
+      // DoLoop.
+      //
+      // For WebSocket streams, which are bi-directional, we'll send and
+      // receive data once the connection is established.  Received data is
+      // handled in OnDataReceived.  Sent data is handled in OnWriteComplete,
+      // which calls DoOpen().
+      case STATE_OPEN:
+        result = DoOpen(result);
+        break;
+
+      case STATE_DONE:
+        DCHECK(result != ERR_IO_PENDING);
+        break;
+      default:
+        NOTREACHED() << io_state_;
+        break;
+    }
+  } while (result != ERR_IO_PENDING && io_state_ != STATE_NONE &&
+           io_state_ != STATE_OPEN);
+
+  return result;
+}
+
+int SpdyStream::DoSendHeaders() {
+  // The SpdySession will always call us back when the send is complete.
+  // TODO(willchan): This code makes the assumption that for the non-push stream
+  // case, the client code calls SendRequest() after creating the stream and
+  // before yielding back to the MessageLoop.  This is true in the current code,
+  // but is not obvious from the headers.  We should make the code handle
+  // SendRequest() being called after the SYN_REPLY has been received.
+  io_state_ = STATE_SEND_HEADERS_COMPLETE;
+  return ERR_IO_PENDING;
+}
+
+int SpdyStream::DoSendHeadersComplete(int result) {
+  if (result < 0)
+    return result;
+
+  CHECK_GT(result, 0);
+
+  if (!delegate_)
+    return ERR_UNEXPECTED;
+
+  // There is no body, skip that state.
+  if (delegate_->OnSendHeadersComplete(result)) {
+    io_state_ = STATE_READ_HEADERS;
+    return OK;
+  }
+
+  io_state_ = STATE_SEND_BODY;
+  return OK;
+}
+
+// DoSendBody is called to send the optional body for the request.  This call
+// will also be called as each write of a chunk of the body completes.
+int SpdyStream::DoSendBody() {
+  // If we're already in the STATE_SENDING_BODY state, then we've already
+  // sent a portion of the body.  In that case, we need to first consume
+  // the bytes written in the body stream.  Note that the bytes written is
+  // the number of bytes in the frame that were written, only consume the
+  // data portion, of course.
+  io_state_ = STATE_SEND_BODY_COMPLETE;
+  if (!delegate_)
+    return ERR_UNEXPECTED;
+  return delegate_->OnSendBody();
+}
+
+int SpdyStream::DoSendBodyComplete(int result) {
+  if (result < 0)
+    return result;
+
+  CHECK_NE(result, 0);
+
+  if (!delegate_)
+    return ERR_UNEXPECTED;
+
+  if (!delegate_->OnSendBodyComplete(result))
+    io_state_ = STATE_SEND_BODY;
+  else
+    io_state_ = STATE_READ_HEADERS;
+
+  return OK;
+}
+
+int SpdyStream::DoReadHeaders() {
+  io_state_ = STATE_READ_HEADERS_COMPLETE;
+  return !response_->empty() ? OK : ERR_IO_PENDING;
+}
+
+int SpdyStream::DoReadHeadersComplete(int result) {
+  io_state_ = STATE_OPEN;
+  return result;
+}
+
+int SpdyStream::DoOpen(int result) {
+  if (delegate_)
+    delegate_->OnDataSent(result);
+  io_state_ = STATE_OPEN;
+  return result;
+}
+
+void SpdyStream::UpdateHistograms() {
+  if (histograms_recorded_)
+    return;
+
+  histograms_recorded_ = true;
+
+  // We need all timers to be filled in, otherwise metrics can be bogus.
+  if (send_time_.is_null() || recv_first_byte_time_.is_null() ||
+      recv_last_byte_time_.is_null())
+    return;
+
+  UMA_HISTOGRAM_TIMES("Net.SpdyStreamTimeToFirstByte",
+      recv_first_byte_time_ - send_time_);
+  UMA_HISTOGRAM_TIMES("Net.SpdyStreamDownloadTime",
+      recv_last_byte_time_ - recv_first_byte_time_);
+  UMA_HISTOGRAM_TIMES("Net.SpdyStreamTime",
+      recv_last_byte_time_ - send_time_);
+
+  UMA_HISTOGRAM_COUNTS("Net.SpdySendBytes", send_bytes_);
+  UMA_HISTOGRAM_COUNTS("Net.SpdyRecvBytes", recv_bytes_);
+}
+
+}  // namespace net
diff --git a/net/spdy/spdy_stream.h b/net/spdy/spdy_stream.h
new file mode 100644
index 0000000..13a6644
--- /dev/null
+++ b/net/spdy/spdy_stream.h
@@ -0,0 +1,256 @@
+// Copyright (c) 2010 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_SPDY_SPDY_STREAM_H_
+#define NET_SPDY_SPDY_STREAM_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/linked_ptr.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "net/base/bandwidth_metrics.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_log.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+
+namespace net {
+
+class SpdySession;
+class SSLInfo;
+
+// The SpdyStream is used by the SpdySession to represent each stream known
+// on the SpdySession.  This class provides interfaces for SpdySession to use.
+// Streams can be created either by the client or by the server.  When they
+// are initiated by the client, both the SpdySession and client object (such as
+// a SpdyNetworkTransaction) will maintain a reference to the stream.  When
+// initiated by the server, only the SpdySession will maintain any reference,
+// until such a time as a client object requests a stream for the path.
+class SpdyStream : public base::RefCounted<SpdyStream> {
+ public:
+  // Delegate handles protocol specific behavior of spdy stream.
+  class Delegate {
+   public:
+    Delegate() {}
+
+    // Called when SYN frame has been sent.
+    // Returns true if no more data to be sent after SYN frame.
+    virtual bool OnSendHeadersComplete(int status) = 0;
+
+    // Called when stream is ready to send data.
+    // Returns network error code. OK when it successfully sent data.
+    virtual int OnSendBody() = 0;
+
+    // Called when data has been sent. |status| indicates network error
+    // or number of bytes has been sent.
+    // Returns true if no more data to be sent.
+    virtual bool OnSendBodyComplete(int status) = 0;
+
+    // Called when SYN_REPLY received. |status| indicates network error.
+    // Returns network error code.
+    virtual int OnResponseReceived(const spdy::SpdyHeaderBlock& response,
+                                   base::Time response_time,
+                                   int status) = 0;
+
+    // Called when data is received.
+    virtual void OnDataReceived(const char* data, int length) = 0;
+
+    // Called when data is sent.
+    virtual void OnDataSent(int length) = 0;
+
+    // Called when SpdyStream is closed.
+    virtual void OnClose(int status) = 0;
+
+   protected:
+    friend class base::RefCounted<Delegate>;
+    virtual ~Delegate() {}
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Delegate);
+  };
+
+  // SpdyStream constructor
+  SpdyStream(SpdySession* session, spdy::SpdyStreamId stream_id, bool pushed);
+
+  // Set new |delegate|. |delegate| must not be NULL.
+  // If it already received SYN_REPLY or data, OnResponseReceived() or
+  // OnDataReceived() will be called.
+  void SetDelegate(Delegate* delegate);
+  Delegate* GetDelegate() { return delegate_; }
+
+  // Detach delegate from the stream. It will cancel the stream if it was not
+  // cancelled yet.  It is safe to call multiple times.
+  void DetachDelegate();
+
+  // Is this stream a pushed stream from the server.
+  bool pushed() const { return pushed_; }
+
+  spdy::SpdyStreamId stream_id() const { return stream_id_; }
+  void set_stream_id(spdy::SpdyStreamId stream_id) { stream_id_ = stream_id; }
+
+  bool syn_reply_received() const { return syn_reply_received_; }
+  void set_syn_reply_received() { syn_reply_received_ = true; }
+
+  // For pushed streams, we track a path to identify them.
+  const std::string& path() const { return path_; }
+  void set_path(const std::string& path) { path_ = path; }
+
+  int priority() const { return priority_; }
+  void set_priority(int priority) { priority_ = priority; }
+
+  int window_size() const { return window_size_; }
+  void set_window_size(int window_size) { window_size_ = window_size; }
+
+  // Updates |window_size_| with delta extracted from a WINDOW_UPDATE
+  // frame; sends a RST_STREAM if delta overflows |window_size_| and
+  // removes the stream from the session.
+  void UpdateWindowSize(int delta_window_size);
+
+  const BoundNetLog& net_log() const { return net_log_; }
+  void set_net_log(const BoundNetLog& log) { net_log_ = log; }
+
+  const linked_ptr<spdy::SpdyHeaderBlock>& spdy_headers() const;
+  void set_spdy_headers(const linked_ptr<spdy::SpdyHeaderBlock>& headers);
+  base::Time GetRequestTime() const;
+  void SetRequestTime(base::Time t);
+
+  // Called by the SpdySession when a response (e.g. a SYN_REPLY) has been
+  // received for this stream.  |path| is the path of the URL for a server
+  // initiated stream, otherwise is empty.
+  // Returns a status code.
+  int OnResponseReceived(const spdy::SpdyHeaderBlock& response);
+
+  // Called by the SpdySession when response data has been received for this
+  // stream.  This callback may be called multiple times as data arrives
+  // from the network, and will never be called prior to OnResponseReceived.
+  // |buffer| contains the data received.  The stream must copy any data
+  //          from this buffer before returning from this callback.
+  // |length| is the number of bytes received or an error.
+  //         A zero-length count does not indicate end-of-stream.
+  void OnDataReceived(const char* buffer, int bytes);
+
+  // Called by the SpdySession when a write has completed.  This callback
+  // will be called multiple times for each write which completes.  Writes
+  // include the SYN_STREAM write and also DATA frame writes.
+  // |result| is the number of bytes written or a net error code.
+  void OnWriteComplete(int status);
+
+  // Called by the SpdySession when the request is finished.  This callback
+  // will always be called at the end of the request and signals to the
+  // stream that the stream has no more network events.  No further callbacks
+  // to the stream will be made after this call.
+  // |status| is an error code or OK.
+  void OnClose(int status);
+
+  void Cancel();
+  bool cancelled() const { return cancelled_; }
+
+  // Interface for Spdy[Http|WebSocket]Stream to use.
+
+  // Sends the request.
+  // For non push stream, it will send SYN_STREAM frame.
+  int DoSendRequest(bool has_upload_data);
+
+  // Reads response headers. If the SpdyStream have already received
+  // the response headers, return OK and response headers filled in
+  // |response| given in SendRequest.
+  // Otherwise, return ERR_IO_PENDING and OnResponseReceived() will be called.
+  int DoReadResponseHeaders();
+
+  // Sends DATA frame.
+  int WriteStreamData(IOBuffer* data, int length,
+                      spdy::SpdyDataFlags flags);
+
+  bool GetSSLInfo(SSLInfo* ssl_info, bool* was_npn_negotiated);
+
+  bool is_idle() const {
+    return io_state_ == STATE_NONE || io_state_ == STATE_OPEN;
+  }
+  bool response_complete() const { return response_complete_; }
+  int response_status() const { return response_status_; }
+
+ private:
+  enum State {
+    STATE_NONE,
+    STATE_SEND_HEADERS,
+    STATE_SEND_HEADERS_COMPLETE,
+    STATE_SEND_BODY,
+    STATE_SEND_BODY_COMPLETE,
+    STATE_READ_HEADERS,
+    STATE_READ_HEADERS_COMPLETE,
+    STATE_OPEN,
+    STATE_DONE
+  };
+
+  friend class base::RefCounted<SpdyStream>;
+  virtual ~SpdyStream();
+
+  // Try to make progress sending/receiving the request/response.
+  int DoLoop(int result);
+
+  // The implementations of each state of the state machine.
+  int DoSendHeaders();
+  int DoSendHeadersComplete(int result);
+  int DoSendBody();
+  int DoSendBodyComplete(int result);
+  int DoReadHeaders();
+  int DoReadHeadersComplete(int result);
+  int DoOpen(int result);
+
+  // Update the histograms.  Can safely be called repeatedly, but should only
+  // be called after the stream has completed.
+  void UpdateHistograms();
+
+  spdy::SpdyStreamId stream_id_;
+  std::string path_;
+  int priority_;
+  int window_size_;
+  const bool pushed_;
+  ScopedBandwidthMetrics metrics_;
+  bool syn_reply_received_;
+
+  scoped_refptr<SpdySession> session_;
+
+  // The transaction should own the delegate.
+  SpdyStream::Delegate* delegate_;
+
+  // The request to send.
+  linked_ptr<spdy::SpdyHeaderBlock> request_;
+
+  // The time at which the request was made that resulted in this response.
+  // For cached responses, this time could be "far" in the past.
+  base::Time request_time_;
+
+  linked_ptr<spdy::SpdyHeaderBlock> response_;
+  base::Time response_time_;
+
+  bool response_complete_;  // TODO(mbelshe): fold this into the io_state.
+  State io_state_;
+
+  // Since we buffer the response, we also buffer the response status.
+  // Not valid until response_complete_ is true.
+  int response_status_;
+
+  bool cancelled_;
+
+  BoundNetLog net_log_;
+
+  base::TimeTicks send_time_;
+  base::TimeTicks recv_first_byte_time_;
+  base::TimeTicks recv_last_byte_time_;
+  int send_bytes_;
+  int recv_bytes_;
+  bool histograms_recorded_;
+  // Data received before delegate is attached.
+  std::vector<scoped_refptr<IOBufferWithSize> > pending_buffers_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpdyStream);
+};
+
+}  // namespace net
+
+#endif  // NET_SPDY_SPDY_STREAM_H_
diff --git a/net/spdy/spdy_stream_unittest.cc b/net/spdy/spdy_stream_unittest.cc
new file mode 100644
index 0000000..4daad96
--- /dev/null
+++ b/net/spdy/spdy_stream_unittest.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2010 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/spdy/spdy_stream.h"
+#include "base/ref_counted.h"
+#include "net/spdy/spdy_session.h"
+#include "net/spdy/spdy_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+// TODO(ukai): factor out common part with spdy_http_stream_unittest.cc
+class SpdySessionPoolPeer {
+ public:
+  explicit SpdySessionPoolPeer(const scoped_refptr<SpdySessionPool>& pool)
+      : pool_(pool) {}
+
+  void RemoveSpdySession(const scoped_refptr<SpdySession>& session) {
+    pool_->Remove(session);
+  }
+
+ private:
+  const scoped_refptr<SpdySessionPool> pool_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpdySessionPoolPeer);
+};
+
+namespace {
+
+// Create a proxy service which fails on all requests (falls back to direct).
+ProxyService* CreateNullProxyService() {
+  return ProxyService::CreateNull();
+}
+
+class TestSpdyStreamDelegate : public SpdyStream::Delegate {
+ public:
+  TestSpdyStreamDelegate(SpdyStream* stream,
+                         IOBufferWithSize* buf,
+                         CompletionCallback* callback)
+      : stream_(stream),
+        buf_(buf),
+        callback_(callback),
+        send_headers_completed_(false),
+        response_(new spdy::SpdyHeaderBlock),
+        data_sent_(0),
+        closed_(false) {}
+  virtual ~TestSpdyStreamDelegate() {}
+
+  virtual bool OnSendHeadersComplete(int status) {
+    send_headers_completed_ = true;
+    return true;
+  }
+  virtual int OnSendBody() {
+    ADD_FAILURE() << "OnSendBody should not be called";
+    return ERR_UNEXPECTED;
+  }
+  virtual bool OnSendBodyComplete(int status) {
+    ADD_FAILURE() << "OnSendBodyComplete should not be called";
+    return true;
+  }
+  virtual int OnResponseReceived(const spdy::SpdyHeaderBlock& response,
+                                 base::Time response_time,
+                                 int status) {
+    EXPECT_TRUE(send_headers_completed_);
+    *response_ = response;
+    if (buf_) {
+      EXPECT_EQ(ERR_IO_PENDING,
+                stream_->WriteStreamData(buf_.get(), buf_->size(),
+                                         spdy::DATA_FLAG_NONE));
+    }
+    return status;
+  }
+  virtual void OnDataReceived(const char* buffer, int bytes) {
+    received_data_ += std::string(buffer, bytes);
+  }
+  virtual void OnDataSent(int length) {
+    data_sent_ += length;
+  }
+  virtual void OnClose(int status) {
+    closed_ = true;
+    CompletionCallback* callback = callback_;
+    callback_ = NULL;
+    callback->Run(OK);
+  }
+
+  bool send_headers_completed() const { return send_headers_completed_; }
+  const linked_ptr<spdy::SpdyHeaderBlock>& response() const {
+    return response_;
+  }
+  const std::string& received_data() const { return received_data_; }
+  int data_sent() const { return data_sent_; }
+  bool closed() const {  return closed_; }
+
+ private:
+  SpdyStream* stream_;
+  scoped_refptr<IOBufferWithSize> buf_;
+  CompletionCallback* callback_;
+  bool send_headers_completed_;
+  linked_ptr<spdy::SpdyHeaderBlock> response_;
+  std::string received_data_;
+  int data_sent_;
+  bool closed_;
+};
+
+spdy::SpdyFrame* ConstructSpdyBodyFrame(const char* data, int length) {
+  spdy::SpdyFramer framer;
+  return framer.CreateDataFrame(1, data, length, spdy::DATA_FLAG_NONE);
+}
+
+}  // anonymous namespace
+
+class SpdyStreamTest : public testing::Test {
+ protected:
+  SpdyStreamTest() {
+  }
+
+  scoped_refptr<SpdySession> CreateSpdySession() {
+    spdy::SpdyFramer::set_enable_compression_default(false);
+    HostPortPair host_port_pair("www.google.com", 80);
+    scoped_refptr<SpdySession> session(
+        session_->spdy_session_pool()->Get(
+            host_port_pair, session_, BoundNetLog()));
+    return session;
+  }
+
+  virtual void TearDown() {
+    MessageLoop::current()->RunAllPending();
+  }
+
+  scoped_refptr<HttpNetworkSession> session_;
+};
+
+TEST_F(SpdyStreamTest, SendDataAfterOpen) {
+  SpdySessionDependencies session_deps;
+
+  session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps);
+  SpdySessionPoolPeer pool_peer_(session_->spdy_session_pool());
+
+  const SpdyHeaderInfo kSynStartHeader = {
+    spdy::SYN_STREAM,
+    1,
+    0,
+    SPDY_PRIORITY_LOWEST,
+    spdy::CONTROL_FLAG_NONE,
+    false,
+    spdy::INVALID,
+    NULL,
+    0,
+    spdy::DATA_FLAG_NONE
+  };
+  static const char* const kGetHeaders[] = {
+    "method",
+    "GET",
+    "url",
+    "http://www.google.com/",
+    "version",
+    "HTTP/1.1",
+  };
+  scoped_ptr<spdy::SpdyFrame> req(
+      ConstructSpdyPacket(
+          kSynStartHeader, NULL, 0, kGetHeaders, arraysize(kGetHeaders) / 2));
+  scoped_ptr<spdy::SpdyFrame> msg(
+      ConstructSpdyBodyFrame("\0hello!\xff", 8));
+  MockWrite writes[] = {
+    CreateMockWrite(*req),
+    CreateMockWrite(*msg),
+  };
+  writes[0].sequence_number = 0;
+  writes[1].sequence_number = 2;
+
+  scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
+  scoped_ptr<spdy::SpdyFrame> echo(
+      ConstructSpdyBodyFrame("\0hello!\xff", 8));
+  MockRead reads[] = {
+    CreateMockRead(*resp),
+    CreateMockRead(*echo),
+    MockRead(true, 0, 0), // EOF
+  };
+  reads[0].sequence_number = 1;
+  reads[1].sequence_number = 3;
+  reads[2].sequence_number = 4;
+
+  scoped_refptr<OrderedSocketData> data(
+      new OrderedSocketData(reads, arraysize(reads),
+                            writes, arraysize(writes)));
+  MockConnect connect_data(false, OK);
+  data->set_connect_data(connect_data);
+
+  session_deps.socket_factory.AddSocketDataProvider(data.get());
+  SpdySession::SetSSLMode(false);
+
+  scoped_refptr<SpdySession> session(CreateSpdySession());
+  GURL url("http://www.google.com/");
+
+  HostPortPair host_port_pair("www.google.com", 80);
+  scoped_refptr<TCPSocketParams> tcp_params =
+      new TCPSocketParams(host_port_pair, LOWEST, GURL(), false);
+  EXPECT_EQ(OK, session->Connect("spdy.www.google.com", tcp_params,
+                                 LOWEST));
+
+  scoped_refptr<SpdyStream> stream;
+  ASSERT_EQ(
+      OK,
+      session->CreateStream(url, LOWEST, &stream, BoundNetLog(), NULL));
+  scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(8));
+  memcpy(buf->data(), "\0hello!\xff", 8);
+  TestCompletionCallback callback;
+
+  scoped_ptr<TestSpdyStreamDelegate> delegate(
+      new TestSpdyStreamDelegate(stream.get(), buf.get(), &callback));
+  stream->SetDelegate(delegate.get());
+
+  linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock);
+  (*headers)["method"] = "GET";
+  (*headers)["url"] = "http://www.google.com/";
+  (*headers)["version"] = "HTTP/1.1";
+  stream->set_spdy_headers(headers);
+
+  EXPECT_EQ(ERR_IO_PENDING, stream->DoSendRequest(true));
+
+  EXPECT_EQ(OK, callback.WaitForResult());
+
+  EXPECT_TRUE(delegate->send_headers_completed());
+  EXPECT_EQ("200", (*delegate->response())["status"]);
+  EXPECT_EQ("HTTP/1.1", (*delegate->response())["version"]);
+  EXPECT_EQ(std::string("\0hello!\xff", 8), delegate->received_data());
+  EXPECT_EQ(8, delegate->data_sent());
+  EXPECT_TRUE(delegate->closed());
+}
+
+}  // namespace net
diff --git a/net/spdy/spdy_test_util.cc b/net/spdy/spdy_test_util.cc
new file mode 100644
index 0000000..99af4f8
--- /dev/null
+++ b/net/spdy/spdy_test_util.cc
@@ -0,0 +1,523 @@
+// Copyright (c) 2010 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/spdy/spdy_test_util.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+
+namespace net {
+
+// Chop a frame into an array of MockWrites.
+// |data| is the frame to chop.
+// |length| is the length of the frame to chop.
+// |num_chunks| is the number of chunks to create.
+MockWrite* ChopWriteFrame(const char* data, int length, int num_chunks) {
+  MockWrite* chunks = new MockWrite[num_chunks];
+  int chunk_size = length / num_chunks;
+  for (int index = 0; index < num_chunks; index++) {
+    const char* ptr = data + (index * chunk_size);
+    if (index == num_chunks - 1)
+      chunk_size += length % chunk_size;  // The last chunk takes the remainder.
+    chunks[index] = MockWrite(true, ptr, chunk_size);
+  }
+  return chunks;
+}
+
+// Chop a SpdyFrame into an array of MockWrites.
+// |frame| is the frame to chop.
+// |num_chunks| is the number of chunks to create.
+MockWrite* ChopWriteFrame(const spdy::SpdyFrame& frame, int num_chunks) {
+  return ChopWriteFrame(frame.data(),
+                        frame.length() + spdy::SpdyFrame::size(),
+                        num_chunks);
+}
+
+// Chop a frame into an array of MockReads.
+// |data| is the frame to chop.
+// |length| is the length of the frame to chop.
+// |num_chunks| is the number of chunks to create.
+MockRead* ChopReadFrame(const char* data, int length, int num_chunks) {
+  MockRead* chunks = new MockRead[num_chunks];
+  int chunk_size = length / num_chunks;
+  for (int index = 0; index < num_chunks; index++) {
+    const char* ptr = data + (index * chunk_size);
+    if (index == num_chunks - 1)
+      chunk_size += length % chunk_size;  // The last chunk takes the remainder.
+    chunks[index] = MockRead(true, ptr, chunk_size);
+  }
+  return chunks;
+}
+
+// Chop a SpdyFrame into an array of MockReads.
+// |frame| is the frame to chop.
+// |num_chunks| is the number of chunks to create.
+MockRead* ChopReadFrame(const spdy::SpdyFrame& frame, int num_chunks) {
+  return ChopReadFrame(frame.data(),
+                       frame.length() + spdy::SpdyFrame::size(),
+                       num_chunks);
+}
+
+// Adds headers and values to a map.
+// |extra_headers| is an array of { name, value } pairs, arranged as strings
+// where the even entries are the header names, and the odd entries are the
+// header values.
+// |headers| gets filled in from |extra_headers|.
+void AppendHeadersToSpdyFrame(const char* const extra_headers[],
+                              int extra_header_count,
+                              spdy::SpdyHeaderBlock* headers) {
+  std::string this_header;
+  std::string this_value;
+
+  if (!extra_header_count)
+    return;
+
+  // Sanity check: Non-NULL header list.
+  DCHECK(NULL != extra_headers) << "NULL header value pair list";
+  // Sanity check: Non-NULL header map.
+  DCHECK(NULL != headers) << "NULL header map";
+  // Copy in the headers.
+  for (int i = 0; i < extra_header_count; i++) {
+    // Sanity check: Non-empty header.
+    DCHECK_NE('\0', *extra_headers[i * 2]) << "Empty header value pair";
+    this_header = extra_headers[i * 2];
+    std::string::size_type header_len = this_header.length();
+    if (!header_len)
+      continue;
+    this_value = extra_headers[1 + (i * 2)];
+    std::string new_value;
+    if (headers->find(this_header) != headers->end()) {
+      // More than one entry in the header.
+      // Don't add the header again, just the append to the value,
+      // separated by a NULL character.
+
+      // Adjust the value.
+      new_value = (*headers)[this_header];
+      // Put in a NULL separator.
+      new_value.append(1, '\0');
+      // Append the new value.
+      new_value += this_value;
+    } else {
+      // Not a duplicate, just write the value.
+      new_value = this_value;
+    }
+    (*headers)[this_header] = new_value;
+  }
+}
+
+// Writes |val| to a location of size |len|, in big-endian format.
+// in the buffer pointed to by |buffer_handle|.
+// Updates the |*buffer_handle| pointer by |len|
+// Returns the number of bytes written
+int AppendToBuffer(int val,
+                   int len,
+                   unsigned char** buffer_handle,
+                   int* buffer_len_remaining) {
+  if (len <= 0)
+    return 0;
+  DCHECK((size_t) len <= sizeof(len)) << "Data length too long for data type";
+  DCHECK(NULL != buffer_handle) << "NULL buffer handle";
+  DCHECK(NULL != *buffer_handle) << "NULL pointer";
+  DCHECK(NULL != buffer_len_remaining)
+      << "NULL buffer remainder length pointer";
+  DCHECK_GE(*buffer_len_remaining, len) << "Insufficient buffer size";
+  for (int i = 0; i < len; i++) {
+    int shift = (8 * (len - (i + 1)));
+    unsigned char val_chunk = (val >> shift) & 0x0FF;
+    *(*buffer_handle)++ = val_chunk;
+    *buffer_len_remaining += 1;
+  }
+  return len;
+}
+
+// Construct a SPDY packet.
+// |head| is the start of the packet, up to but not including
+// the header value pairs.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// |tail| is any (relatively constant) header-value pairs to add.
+// |buffer| is the buffer we're filling in.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdyPacket(const SpdyHeaderInfo& header_info,
+                                     const char* const extra_headers[],
+                                     int extra_header_count,
+                                     const char* const tail[],
+                                     int tail_header_count) {
+  spdy::SpdyFramer framer;
+  spdy::SpdyHeaderBlock headers;
+  // Copy in the extra headers to our map.
+  AppendHeadersToSpdyFrame(extra_headers, extra_header_count, &headers);
+  // Copy in the tail headers to our map.
+  if (tail && tail_header_count)
+    AppendHeadersToSpdyFrame(tail, tail_header_count, &headers);
+  spdy::SpdyFrame* frame = NULL;
+  switch (header_info.kind) {
+    case spdy::SYN_STREAM:
+      frame = framer.CreateSynStream(header_info.id, header_info.assoc_id,
+                                     header_info.priority,
+                                     header_info.control_flags,
+                                     header_info.compressed, &headers);
+      break;
+    case spdy::SYN_REPLY:
+      frame = framer.CreateSynReply(header_info.id, header_info.control_flags,
+                                    header_info.compressed, &headers);
+      break;
+    case spdy::RST_STREAM:
+      frame = framer.CreateRstStream(header_info.id, header_info.status);
+      break;
+    default:
+      frame = framer.CreateDataFrame(header_info.id, header_info.data,
+                                     header_info.data_length,
+                                     header_info.data_flags);
+      break;
+  }
+  return frame;
+}
+
+// Construct an expected SPDY SETTINGS frame.
+// |settings| are the settings to set.
+// Returns the constructed frame.  The caller takes ownership of the frame.
+spdy::SpdyFrame* ConstructSpdySettings(spdy::SpdySettings settings) {
+  spdy::SpdyFramer framer;
+  return framer.CreateSettings(settings);
+}
+
+// Construct a SPDY GOAWAY frame.
+// Returns the constructed frame.  The caller takes ownership of the frame.
+spdy::SpdyFrame* ConstructSpdyGoAway() {
+  spdy::SpdyFramer framer;
+  return framer.CreateGoAway(0);
+}
+
+// Construct a SPDY WINDOW_UPDATE frame.
+// Returns the constructed frame.  The caller takes ownership of the frame.
+spdy::SpdyFrame* ConstructSpdyWindowUpdate(
+    const spdy::SpdyStreamId stream_id, uint32 delta_window_size) {
+  spdy::SpdyFramer framer;
+  return framer.CreateWindowUpdate(stream_id, delta_window_size);
+}
+
+// Construct a SPDY RST_STREAM frame.
+// Returns the constructed frame.  The caller takes ownership of the frame.
+spdy::SpdyFrame* ConstructSpdyRstStream(spdy::SpdyStreamId stream_id,
+                                        spdy::SpdyStatusCodes status) {
+  spdy::SpdyFramer framer;
+  return framer.CreateRstStream(stream_id, status);
+}
+
+// Construct a single SPDY header entry, for validation.
+// |extra_headers| are the extra header-value pairs.
+// |buffer| is the buffer we're filling in.
+// |index| is the index of the header we want.
+// Returns the number of bytes written into |buffer|.
+int ConstructSpdyHeader(const char* const extra_headers[],
+                        int extra_header_count,
+                        char* buffer,
+                        int buffer_length,
+                        int index) {
+  const char* this_header = NULL;
+  const char* this_value = NULL;
+  if (!buffer || !buffer_length)
+    return 0;
+  *buffer = '\0';
+  // Sanity check: Non-empty header list.
+  DCHECK(NULL != extra_headers) << "NULL extra headers pointer";
+  // Sanity check: Index out of range.
+  DCHECK((index >= 0) && (index < extra_header_count))
+      << "Index " << index
+      << " out of range [0, " << extra_header_count << ")";
+  this_header = extra_headers[index * 2];
+  // Sanity check: Non-empty header.
+  if (!*this_header)
+    return 0;
+  std::string::size_type header_len = strlen(this_header);
+  if (!header_len)
+    return 0;
+  this_value = extra_headers[1 + (index * 2)];
+  // Sanity check: Non-empty value.
+  if (!*this_value)
+    this_value = "";
+  int n = base::snprintf(buffer,
+                         buffer_length,
+                         "%s: %s\r\n",
+                         this_header,
+                         this_value);
+  return n;
+}
+
+// Constructs a standard SPDY GET SYN packet, optionally compressed.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[],
+                                  int extra_header_count,
+                                  bool compressed,
+                                  int stream_id,
+                                  RequestPriority request_priority) {
+  const SpdyHeaderInfo kSynStartHeader = {
+    spdy::SYN_STREAM,             // Kind = Syn
+    stream_id,                    // Stream ID
+    0,                            // Associated stream ID
+    request_priority,             // Priority
+    spdy::CONTROL_FLAG_FIN,       // Control Flags
+    compressed,                   // Compressed
+    spdy::INVALID,                // Status
+    NULL,                         // Data
+    0,                            // Length
+    spdy::DATA_FLAG_NONE          // Data Flags
+  };
+  static const char* const kStandardGetHeaders[] = {
+    "method",
+    "GET",
+    "url",
+    "http://www.google.com/",
+    "version",
+    "HTTP/1.1"
+  };
+  return ConstructSpdyPacket(
+      kSynStartHeader,
+      extra_headers,
+      extra_header_count,
+      kStandardGetHeaders,
+      arraysize(kStandardGetHeaders) / 2);
+}
+
+// Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[],
+                                          int extra_header_count,
+                                          int stream_id) {
+  const SpdyHeaderInfo kSynStartHeader = {
+    spdy::SYN_REPLY,              // Kind = SynReply
+    stream_id,                    // Stream ID
+    0,                            // Associated stream ID
+    SPDY_PRIORITY_LOWEST,         // Priority
+    spdy::CONTROL_FLAG_NONE,      // Control Flags
+    false,                        // Compressed
+    spdy::INVALID,                // Status
+    NULL,                         // Data
+    0,                            // Length
+    spdy::DATA_FLAG_NONE          // Data Flags
+  };
+  static const char* const kStandardGetHeaders[] = {
+    "hello",
+    "bye",
+    "status",
+    "200",
+    "url",
+    "/index.php",
+    "version",
+    "HTTP/1.1"
+  };
+  return ConstructSpdyPacket(
+      kSynStartHeader,
+      extra_headers,
+      extra_header_count,
+      kStandardGetHeaders,
+      arraysize(kStandardGetHeaders) / 2);
+}
+
+// Constructs a standard SPDY POST SYN packet.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdyPost(const char* const extra_headers[],
+                                   int extra_header_count) {
+  const SpdyHeaderInfo kSynStartHeader = {
+    spdy::SYN_STREAM,             // Kind = Syn
+    1,                            // Stream ID
+    0,                            // Associated stream ID
+    SPDY_PRIORITY_LOWEST,         // Priority
+    spdy::CONTROL_FLAG_NONE,      // Control Flags
+    false,                        // Compressed
+    spdy::INVALID,                // Status
+    NULL,                         // Data
+    0,                            // Length
+    spdy::DATA_FLAG_NONE          // Data Flags
+  };
+  static const char* const kStandardGetHeaders[] = {
+    "method",
+    "POST",
+    "url",
+    "http://www.google.com/",
+    "version",
+    "HTTP/1.1"
+  };
+  return ConstructSpdyPacket(
+      kSynStartHeader,
+      extra_headers,
+      extra_header_count,
+      kStandardGetHeaders,
+      arraysize(kStandardGetHeaders) / 2);
+}
+
+// Constructs a standard SPDY SYN_REPLY packet to match the SPDY POST.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdyPostSynReply(const char* const extra_headers[],
+                                           int extra_header_count) {
+  const SpdyHeaderInfo kSynStartHeader = {
+    spdy::SYN_REPLY,              // Kind = SynReply
+    1,                            // Stream ID
+    0,                            // Associated stream ID
+    SPDY_PRIORITY_LOWEST,         // Priority
+    spdy::CONTROL_FLAG_NONE,      // Control Flags
+    false,                        // Compressed
+    spdy::INVALID,                // Status
+    NULL,                         // Data
+    0,                            // Length
+    spdy::DATA_FLAG_NONE          // Data Flags
+  };
+  static const char* const kStandardGetHeaders[] = {
+    "hello",
+    "bye",
+    "status",
+    "200",
+    "url",
+    "/index.php",
+    "version",
+    "HTTP/1.1"
+  };
+  return ConstructSpdyPacket(
+      kSynStartHeader,
+      extra_headers,
+      extra_header_count,
+      kStandardGetHeaders,
+      arraysize(kStandardGetHeaders) / 2);
+}
+
+// Constructs a single SPDY data frame with the contents "hello!"
+spdy::SpdyFrame* ConstructSpdyBodyFrame(int stream_id, bool fin) {
+  spdy::SpdyFramer framer;
+  return
+      framer.CreateDataFrame(stream_id, "hello!", 6,
+                             fin ? spdy::DATA_FLAG_FIN : spdy::DATA_FLAG_NONE);
+}
+
+// Construct an expected SPDY reply string.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// |buffer| is the buffer we're filling in.
+// Returns the number of bytes written into |buffer|.
+int ConstructSpdyReplyString(const char* const extra_headers[],
+                             int extra_header_count,
+                             char* buffer,
+                             int buffer_length) {
+  int packet_size = 0;
+  int header_count = 0;
+  char* buffer_write = buffer;
+  int buffer_left = buffer_length;
+  spdy::SpdyHeaderBlock headers;
+  if (!buffer || !buffer_length)
+    return 0;
+  // Copy in the extra headers.
+  AppendHeadersToSpdyFrame(extra_headers, extra_header_count, &headers);
+  header_count = headers.size();
+  // The iterator gets us the list of header/value pairs in sorted order.
+  spdy::SpdyHeaderBlock::iterator next = headers.begin();
+  spdy::SpdyHeaderBlock::iterator last = headers.end();
+  for ( ; next != last; ++next) {
+    // Write the header.
+    int value_len, current_len, offset;
+    const char* header_string = next->first.c_str();
+    packet_size += AppendToBuffer(header_string,
+                                  next->first.length(),
+                                  &buffer_write,
+                                  &buffer_left);
+    packet_size += AppendToBuffer(": ",
+                                  strlen(": "),
+                                  &buffer_write,
+                                  &buffer_left);
+    // Write the value(s).
+    const char* value_string = next->second.c_str();
+    // Check if it's split among two or more values.
+    value_len = next->second.length();
+    current_len = strlen(value_string);
+    offset = 0;
+    // Handle the first N-1 values.
+    while (current_len < value_len) {
+      // Finish this line -- write the current value.
+      packet_size += AppendToBuffer(value_string + offset,
+                                    current_len - offset,
+                                    &buffer_write,
+                                    &buffer_left);
+      packet_size += AppendToBuffer("\n",
+                                    strlen("\n"),
+                                    &buffer_write,
+                                    &buffer_left);
+      // Advance to next value.
+      offset = current_len + 1;
+      current_len += 1 + strlen(value_string + offset);
+      // Start another line -- add the header again.
+      packet_size += AppendToBuffer(header_string,
+                                    next->first.length(),
+                                    &buffer_write,
+                                    &buffer_left);
+      packet_size += AppendToBuffer(": ",
+                                    strlen(": "),
+                                    &buffer_write,
+                                    &buffer_left);
+    }
+    EXPECT_EQ(value_len, current_len);
+    // Copy the last (or only) value.
+    packet_size += AppendToBuffer(value_string + offset,
+                                  value_len - offset,
+                                  &buffer_write,
+                                  &buffer_left);
+    packet_size += AppendToBuffer("\n",
+                                  strlen("\n"),
+                                  &buffer_write,
+                                  &buffer_left);
+  }
+  return packet_size;
+}
+
+// Create a MockWrite from the given SpdyFrame.
+MockWrite CreateMockWrite(const spdy::SpdyFrame& req) {
+  return MockWrite(
+      true, req.data(), req.length() + spdy::SpdyFrame::size());
+}
+
+// Create a MockWrite from the given SpdyFrame and sequence number.
+MockWrite CreateMockWrite(const spdy::SpdyFrame& req, int seq) {
+  return MockWrite(
+      true, req.data(), req.length() + spdy::SpdyFrame::size(), seq);
+}
+
+// Create a MockRead from the given SpdyFrame.
+MockRead CreateMockRead(const spdy::SpdyFrame& resp) {
+  return MockRead(
+      true, resp.data(), resp.length() + spdy::SpdyFrame::size());
+}
+
+// Create a MockRead from the given SpdyFrame and sequence number.
+MockRead CreateMockRead(const spdy::SpdyFrame& resp, int seq) {
+  return MockRead(
+      true, resp.data(), resp.length() + spdy::SpdyFrame::size(), seq);
+}
+
+// Combines the given SpdyFrames into the given char array and returns
+// the total length.
+int CombineFrames(const spdy::SpdyFrame** frames, int num_frames,
+                  char* buff, int buff_len) {
+  int total_len = 0;
+  for (int i = 0; i < num_frames; ++i) {
+    total_len += frames[i]->length() + spdy::SpdyFrame::size();
+  }
+  DCHECK_LE(total_len, buff_len);
+  char* ptr = buff;
+  for (int i = 0; i < num_frames; ++i) {
+    int len = frames[i]->length() + spdy::SpdyFrame::size();
+    memcpy(ptr, frames[i]->data(), len);
+    ptr += len;
+  }
+  return total_len;
+}
+
+}  // namespace net
diff --git a/net/spdy/spdy_test_util.h b/net/spdy/spdy_test_util.h
new file mode 100644
index 0000000..6e7a8d0
--- /dev/null
+++ b/net/spdy/spdy_test_util.h
@@ -0,0 +1,255 @@
+// Copyright (c) 2010 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_SPDY_SPDY_TEST_UTIL_H_
+#define NET_SPDY_SPDY_TEST_UTIL_H_
+
+#include "base/basictypes.h"
+#include "net/base/mock_host_resolver.h"
+#include "net/base/request_priority.h"
+#include "net/base/ssl_config_service_defaults.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/http_network_session.h"
+#include "net/proxy/proxy_service.h"
+#include "net/socket/socket_test_util.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_session_pool.h"
+
+namespace net {
+
+// NOTE: In GCC, on a Mac, this can't be in an anonymous namespace!
+// This struct holds information used to construct spdy control and data frames.
+struct SpdyHeaderInfo {
+  spdy::SpdyControlType kind;
+  spdy::SpdyStreamId id;
+  spdy::SpdyStreamId assoc_id;
+  spdy::SpdyPriority priority;
+  spdy::SpdyControlFlags control_flags;
+  bool compressed;
+  spdy::SpdyStatusCodes status;
+  const char* data;
+  uint32 data_length;
+  spdy::SpdyDataFlags data_flags;
+};
+
+// Chop a frame into an array of MockWrites.
+// |data| is the frame to chop.
+// |length| is the length of the frame to chop.
+// |num_chunks| is the number of chunks to create.
+MockWrite* ChopWriteFrame(const char* data, int length, int num_chunks);
+
+// Chop a SpdyFrame into an array of MockWrites.
+// |frame| is the frame to chop.
+// |num_chunks| is the number of chunks to create.
+MockWrite* ChopWriteFrame(const spdy::SpdyFrame& frame, int num_chunks);
+
+// Chop a frame into an array of MockReads.
+// |data| is the frame to chop.
+// |length| is the length of the frame to chop.
+// |num_chunks| is the number of chunks to create.
+MockRead* ChopReadFrame(const char* data, int length, int num_chunks);
+
+// Chop a SpdyFrame into an array of MockReads.
+// |frame| is the frame to chop.
+// |num_chunks| is the number of chunks to create.
+MockRead* ChopReadFrame(const spdy::SpdyFrame& frame, int num_chunks);
+
+// Adds headers and values to a map.
+// |extra_headers| is an array of { name, value } pairs, arranged as strings
+// where the even entries are the header names, and the odd entries are the
+// header values.
+// |headers| gets filled in from |extra_headers|.
+void AppendHeadersToSpdyFrame(const char* const extra_headers[],
+                              int extra_header_count,
+                              spdy::SpdyHeaderBlock* headers);
+
+// Writes |str| of the given |len| to the buffer pointed to by |buffer_handle|.
+// Uses a template so buffer_handle can be a char* or an unsigned char*.
+// Updates the |*buffer_handle| pointer by |len|
+// Returns the number of bytes written into *|buffer_handle|
+template<class T>
+int AppendToBuffer(const char* str,
+                   int len,
+                   T** buffer_handle,
+                   int* buffer_len_remaining) {
+  DCHECK_GT(len, 0);
+  DCHECK(NULL != buffer_handle) << "NULL buffer handle";
+  DCHECK(NULL != *buffer_handle) << "NULL pointer";
+  DCHECK(NULL != buffer_len_remaining)
+      << "NULL buffer remainder length pointer";
+  DCHECK_GE(*buffer_len_remaining, len) << "Insufficient buffer size";
+  memcpy(*buffer_handle, str, len);
+  *buffer_handle += len;
+  *buffer_len_remaining -= len;
+  return len;
+}
+
+// Writes |val| to a location of size |len|, in big-endian format.
+// in the buffer pointed to by |buffer_handle|.
+// Updates the |*buffer_handle| pointer by |len|
+// Returns the number of bytes written
+int AppendToBuffer(int val,
+                   int len,
+                   unsigned char** buffer_handle,
+                   int* buffer_len_remaining);
+
+// Construct a SPDY packet.
+// |head| is the start of the packet, up to but not including
+// the header value pairs.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// |tail| is any (relatively constant) header-value pairs to add.
+// |buffer| is the buffer we're filling in.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdyPacket(const SpdyHeaderInfo& header_info,
+                                     const char* const extra_headers[],
+                                     int extra_header_count,
+                                     const char* const tail[],
+                                     int tail_header_count);
+
+// Construct an expected SPDY reply string.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// |buffer| is the buffer we're filling in.
+// Returns the number of bytes written into |buffer|.
+int ConstructSpdyReplyString(const char* const extra_headers[],
+                             int extra_header_count,
+                             char* buffer,
+                             int buffer_length);
+
+// Construct an expected SPDY SETTINGS frame.
+// |settings| are the settings to set.
+// Returns the constructed frame.  The caller takes ownership of the frame.
+spdy::SpdyFrame* ConstructSpdySettings(spdy::SpdySettings settings);
+
+// Construct a SPDY GOAWAY frame.
+// Returns the constructed frame.  The caller takes ownership of the frame.
+spdy::SpdyFrame* ConstructSpdyGoAway();
+
+// Construct a SPDY WINDOW_UPDATE frame.
+// Returns the constructed frame.  The caller takes ownership of the frame.
+spdy::SpdyFrame* ConstructSpdyWindowUpdate(spdy::SpdyStreamId,
+                                           uint32 delta_window_size);
+
+// Construct a SPDY RST_STREAM frame.
+// Returns the constructed frame.  The caller takes ownership of the frame.
+spdy::SpdyFrame* ConstructSpdyRstStream(spdy::SpdyStreamId stream_id,
+                                        spdy::SpdyStatusCodes status);
+
+// Construct a single SPDY header entry, for validation.
+// |extra_headers| are the extra header-value pairs.
+// |buffer| is the buffer we're filling in.
+// |index| is the index of the header we want.
+// Returns the number of bytes written into |buffer|.
+int ConstructSpdyHeader(const char* const extra_headers[],
+                        int extra_header_count,
+                        char* buffer,
+                        int buffer_length,
+                        int index);
+
+// Constructs a standard SPDY GET SYN packet, optionally compressed.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[],
+                                  int extra_header_count,
+                                  bool compressed,
+                                  int stream_id,
+                                  RequestPriority request_priority);
+
+// Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[],
+                                          int extra_header_count,
+                                          int stream_id);
+
+// Constructs a standard SPDY POST SYN packet.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdyPost(const char* const extra_headers[],
+                                   int extra_header_count);
+
+// Constructs a standard SPDY SYN_REPLY packet to match the SPDY POST.
+// |extra_headers| are the extra header-value pairs, which typically
+// will vary the most between calls.
+// Returns a SpdyFrame.
+spdy::SpdyFrame* ConstructSpdyPostSynReply(const char* const extra_headers[],
+                                           int extra_header_count);
+
+// Constructs a single SPDY data frame with the contents "hello!"
+spdy::SpdyFrame* ConstructSpdyBodyFrame(int stream_id,
+                                        bool fin);
+
+// Create an async MockWrite from the given SpdyFrame.
+MockWrite CreateMockWrite(const spdy::SpdyFrame& req);
+
+// Create an async MockWrite from the given SpdyFrame and sequence number.
+MockWrite CreateMockWrite(const spdy::SpdyFrame& req, int seq);
+
+// Create a MockRead from the given SpdyFrame.
+MockRead CreateMockRead(const spdy::SpdyFrame& resp);
+
+// Create a MockRead from the given SpdyFrame and sequence number.
+MockRead CreateMockRead(const spdy::SpdyFrame& resp, int seq);
+
+// Combines the given SpdyFrames into the given char array and returns
+// the total length.
+int CombineFrames(const spdy::SpdyFrame** frames, int num_frames,
+                  char* buff, int buff_len);
+
+// Helper to manage the lifetimes of the dependencies for a
+// HttpNetworkTransaction.
+class SpdySessionDependencies {
+ public:
+  // Default set of dependencies -- "null" proxy service.
+  SpdySessionDependencies()
+      : host_resolver(new MockHostResolver),
+        proxy_service(ProxyService::CreateNull()),
+        ssl_config_service(new SSLConfigServiceDefaults),
+        http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault()),
+        spdy_session_pool(new SpdySessionPool()) {
+          // Note: The CancelledTransaction test does cleanup by running all
+          // tasks in the message loop (RunAllPending).  Unfortunately, that
+          // doesn't clean up tasks on the host resolver thread; and
+          // TCPConnectJob is currently not cancellable.  Using synchronous
+          // lookups allows the test to shutdown cleanly.  Until we have
+          // cancellable TCPConnectJobs, use synchronous lookups.
+          host_resolver->set_synchronous_mode(true);
+        }
+
+  // Custom proxy service dependency.
+  explicit SpdySessionDependencies(ProxyService* proxy_service)
+      : host_resolver(new MockHostResolver),
+        proxy_service(proxy_service),
+        ssl_config_service(new SSLConfigServiceDefaults),
+        http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault()),
+        spdy_session_pool(new SpdySessionPool()) {}
+
+  scoped_refptr<MockHostResolverBase> host_resolver;
+  scoped_refptr<ProxyService> proxy_service;
+  scoped_refptr<SSLConfigService> ssl_config_service;
+  MockClientSocketFactory socket_factory;
+  scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory;
+  scoped_refptr<SpdySessionPool> spdy_session_pool;
+
+  static HttpNetworkSession* SpdyCreateSession(
+      SpdySessionDependencies* session_deps) {
+    return new HttpNetworkSession(session_deps->host_resolver,
+                                  session_deps->proxy_service,
+                                  &session_deps->socket_factory,
+                                  session_deps->ssl_config_service,
+                                  session_deps->spdy_session_pool,
+                                  session_deps->http_auth_handler_factory.get(),
+                                  NULL,
+                                  NULL);
+}
+};
+
+
+}  // namespace net
+
+#endif  // NET_SPDY_SPDY_TEST_UTIL_H_
diff --git a/net/spdy/spdy_transaction_factory.h b/net/spdy/spdy_transaction_factory.h
new file mode 100644
index 0000000..bb626f9
--- /dev/null
+++ b/net/spdy/spdy_transaction_factory.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2009 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_SPDY_SPDY_TRANSACTION_FACTORY_H__
+#define NET_SPDY_SPDY_TRANSACTION_FACTORY_H__
+
+#include "net/http/http_transaction_factory.h"
+#include "net/spdy/spdy_network_transaction.h"
+
+namespace net {
+
+class SpdyTransactionFactory : public HttpTransactionFactory {
+ public:
+  explicit SpdyTransactionFactory(HttpNetworkSession* session)
+     : session_(session) {
+  }
+  virtual ~SpdyTransactionFactory() {}
+
+  // HttpTransactionFactory Interface.
+  virtual HttpTransaction* CreateTransaction() {
+    return new SpdyNetworkTransaction(session_);
+  }
+  virtual HttpCache* GetCache() {
+    return NULL;
+  }
+  virtual void Suspend(bool suspend) {
+  }
+
+ private:
+  scoped_refptr<HttpNetworkSession> session_;
+};
+
+}  // namespace net
+
+#endif  // NET_SPDY_SPDY_TRANSACTION_FACTORY_H__
diff --git a/net/stress_cache.target.mk b/net/stress_cache.target.mk
new file mode 100644
index 0000000..1bd44eb
--- /dev/null
+++ b/net/stress_cache.target.mk
@@ -0,0 +1,183 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := stress_cache
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I.
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I.
+
+OBJS := $(obj).target/$(TARGET)/net/disk_cache/stress_cache.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/net/libnet.a $(obj).target/net/libnet_test_support.a $(obj).target/base/libbase.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a $(obj).target/testing/libgtest.a
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-Wl,--gc-sections
+
+LIBS := -lrt \
+	-ldl \
+	-lgtk-x11-2.0 \
+	-lgdk-x11-2.0 \
+	-latk-1.0 \
+	-lgio-2.0 \
+	-lpangoft2-1.0 \
+	-lgdk_pixbuf-2.0 \
+	-lm \
+	-lpangocairo-1.0 \
+	-lcairo \
+	-lpango-1.0 \
+	-lfreetype \
+	-lfontconfig \
+	-lgobject-2.0 \
+	-lgmodule-2.0 \
+	-lgthread-2.0 \
+	-lglib-2.0 \
+	-lnss3 \
+	-lnssutil3 \
+	-lsmime3 \
+	-lplds4 \
+	-lplc4 \
+	-lnspr4 \
+	-lpthread \
+	-lz \
+	-lgconf-2
+
+$(builddir)/stress_cache: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(builddir)/stress_cache: LIBS := $(LIBS)
+$(builddir)/stress_cache: TOOLSET := $(TOOLSET)
+$(builddir)/stress_cache: $(OBJS) $(obj).target/net/libnet.a $(obj).target/net/libnet_test_support.a $(obj).target/base/libbase.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/base/libbase_i18n.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/sdch/libsdch.a $(obj).target/net/libnet_base.a $(obj).target/v8/tools/gyp/libv8_snapshot.a $(obj).target/v8/tools/gyp/libv8_base.a $(obj).target/testing/libgtest.a FORCE_DO_CMD
+	$(call do_cmd,link)
+
+all_deps += $(builddir)/stress_cache
+# Add target alias
+.PHONY: stress_cache
+stress_cache: $(builddir)/stress_cache
+
+# Add executable to "all" target.
+.PHONY: all
+all: $(builddir)/stress_cache
+
diff --git a/net/test/test_server.cc b/net/test/test_server.cc
new file mode 100644
index 0000000..4abed54
--- /dev/null
+++ b/net/test/test_server.cc
@@ -0,0 +1,414 @@
+// Copyright (c) 2010 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/test/test_server.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <wincrypt.h>
+#elif defined(OS_MACOSX)
+#include "net/base/x509_certificate.h"
+#endif
+
+#include "base/file_util.h"
+#include "base/leak_annotations.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/cert_test_util.h"
+#include "net/base/host_resolver.h"
+#include "net/base/net_test_constants.h"
+#include "net/base/test_completion_callback.h"
+#include "net/socket/tcp_client_socket.h"
+#include "net/socket/tcp_pinger.h"
+#include "testing/platform_test.h"
+
+#if defined(OS_WIN)
+#pragma comment(lib, "crypt32.lib")
+#endif
+
+namespace net {
+
+#if defined(OS_MACOSX)
+void SetMacTestCertificate(X509Certificate* cert);
+#endif
+
+// static
+const char TestServerLauncher::kHostName[] = "127.0.0.1";
+const char TestServerLauncher::kMismatchedHostName[] = "localhost";
+const int TestServerLauncher::kOKHTTPSPort = 9443;
+const int TestServerLauncher::kBadHTTPSPort = 9666;
+
+// The issuer name of the cert that should be trusted for the test to work.
+const wchar_t TestServerLauncher::kCertIssuerName[] = L"Test CA";
+
+TestServerLauncher::TestServerLauncher() : process_handle_(
+    base::kNullProcessHandle),
+    forking_(false),
+    connection_attempts_(kDefaultTestConnectionAttempts),
+    connection_timeout_(kDefaultTestConnectionTimeout)
+{
+  InitCertPath();
+}
+
+TestServerLauncher::TestServerLauncher(int connection_attempts,
+                                       int connection_timeout)
+                        : process_handle_(base::kNullProcessHandle),
+                          forking_(false),
+                          connection_attempts_(connection_attempts),
+                          connection_timeout_(connection_timeout)
+{
+  InitCertPath();
+}
+
+void TestServerLauncher::InitCertPath() {
+  PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_);
+  cert_dir_ = cert_dir_.Append(FILE_PATH_LITERAL("net"))
+                       .Append(FILE_PATH_LITERAL("data"))
+                       .Append(FILE_PATH_LITERAL("ssl"))
+                       .Append(FILE_PATH_LITERAL("certificates"));
+}
+
+namespace {
+
+void AppendToPythonPath(const FilePath& dir) {
+  // Do nothing if dir already on path.
+
+#if defined(OS_WIN)
+  const wchar_t kPythonPath[] = L"PYTHONPATH";
+  // TODO(dkegel): handle longer PYTHONPATH variables
+  wchar_t oldpath[4096];
+  if (GetEnvironmentVariable(kPythonPath, oldpath, arraysize(oldpath)) == 0) {
+    SetEnvironmentVariableW(kPythonPath, dir.value().c_str());
+  } else if (!wcsstr(oldpath, dir.value().c_str())) {
+    std::wstring newpath(oldpath);
+    newpath.append(L";");
+    newpath.append(dir.value());
+    SetEnvironmentVariableW(kPythonPath, newpath.c_str());
+  }
+#elif defined(OS_POSIX)
+  const char kPythonPath[] = "PYTHONPATH";
+  const char* oldpath = getenv(kPythonPath);
+  // setenv() leaks memory intentionally on Mac
+  if (!oldpath) {
+    setenv(kPythonPath, dir.value().c_str(), 1);
+  } else if (!strstr(oldpath, dir.value().c_str())) {
+    std::string newpath(oldpath);
+    newpath.append(":");
+    newpath.append(dir.value());
+    setenv(kPythonPath, newpath.c_str(), 1);
+  }
+#endif
+}
+
+}  // end namespace
+
+void TestServerLauncher::SetPythonPath() {
+  FilePath third_party_dir;
+  CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir));
+  third_party_dir = third_party_dir.Append(FILE_PATH_LITERAL("third_party"));
+
+  AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("tlslite")));
+  AppendToPythonPath(third_party_dir.Append(FILE_PATH_LITERAL("pyftpdlib")));
+
+  // Locate the Python code generated by the protocol buffers compiler.
+  FilePath generated_code_dir;
+  CHECK(PathService::Get(base::DIR_EXE, &generated_code_dir));
+  generated_code_dir = generated_code_dir.Append(FILE_PATH_LITERAL("pyproto"));
+  AppendToPythonPath(generated_code_dir);
+  AppendToPythonPath(generated_code_dir.Append(FILE_PATH_LITERAL("sync_pb")));
+}
+
+bool TestServerLauncher::Start(Protocol protocol,
+                               const std::string& host_name, int port,
+                               const FilePath& document_root,
+                               const FilePath& cert_path,
+                               const std::wstring& file_root_url) {
+  if (!cert_path.value().empty()) {
+    if (!LoadTestRootCert())
+      return false;
+    if (!CheckCATrusted())
+      return false;
+  }
+
+  std::string port_str = IntToString(port);
+
+  // Get path to python server script
+  FilePath testserver_path;
+  if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_path))
+    return false;
+  testserver_path = testserver_path
+      .Append(FILE_PATH_LITERAL("net"))
+      .Append(FILE_PATH_LITERAL("tools"))
+      .Append(FILE_PATH_LITERAL("testserver"))
+      .Append(FILE_PATH_LITERAL("testserver.py"));
+
+  PathService::Get(base::DIR_SOURCE_ROOT, &document_root_dir_);
+  document_root_dir_ = document_root_dir_.Append(document_root);
+
+  SetPythonPath();
+
+#if defined(OS_WIN)
+  // Get path to python interpreter
+  if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_runtime_))
+    return false;
+  python_runtime_ = python_runtime_
+      .Append(FILE_PATH_LITERAL("third_party"))
+      .Append(FILE_PATH_LITERAL("python_24"))
+      .Append(FILE_PATH_LITERAL("python.exe"));
+
+  std::wstring command_line =
+      L"\"" + python_runtime_.ToWStringHack() + L"\" " +
+      L"\"" + testserver_path.ToWStringHack() +
+      L"\" --port=" + UTF8ToWide(port_str) +
+      L" --data-dir=\"" + document_root_dir_.ToWStringHack() + L"\"";
+  if (protocol == ProtoFTP)
+    command_line.append(L" -f");
+  if (!cert_path.value().empty()) {
+    command_line.append(L" --https=\"");
+    command_line.append(cert_path.ToWStringHack());
+    command_line.append(L"\"");
+  }
+  if (!file_root_url.empty()) {
+    command_line.append(L" --file-root-url=\"");
+    command_line.append(file_root_url);
+    command_line.append(L"\"");
+  }
+  // Deliberately do not pass the --forking flag. It breaks the tests
+  // on Windows.
+
+  if (!LaunchTestServerAsJob(command_line,
+                             true,
+                             &process_handle_,
+                             &job_handle_)) {
+    LOG(ERROR) << "Failed to launch " << command_line;
+    return false;
+  }
+#elif defined(OS_POSIX)
+  std::vector<std::string> command_line;
+  command_line.push_back("python");
+  command_line.push_back(testserver_path.value());
+  command_line.push_back("--port=" + port_str);
+  command_line.push_back("--data-dir=" + document_root_dir_.value());
+  if (protocol == ProtoFTP)
+    command_line.push_back("-f");
+  if (!cert_path.value().empty())
+    command_line.push_back("--https=" + cert_path.value());
+  if (forking_)
+    command_line.push_back("--forking");
+
+  base::file_handle_mapping_vector no_mappings;
+  LOG(INFO) << "Trying to launch " << command_line[0] << " ...";
+  if (!base::LaunchApp(command_line, no_mappings, false, &process_handle_)) {
+    LOG(ERROR) << "Failed to launch " << command_line[0] << " ...";
+    return false;
+  }
+#endif
+
+  // Let the server start, then verify that it's up.
+  // Our server is Python, and takes about 500ms to start
+  // up the first time, and about 200ms after that.
+  if (!WaitToStart(host_name, port)) {
+    LOG(ERROR) << "Failed to connect to server";
+    Stop();
+    return false;
+  }
+
+  LOG(INFO) << "Started on port " << port_str;
+  return true;
+}
+
+bool TestServerLauncher::WaitToStart(const std::string& host_name, int port) {
+  // Verify that the webserver is actually started.
+  // Otherwise tests can fail if they run faster than Python can start.
+  net::AddressList addr;
+  scoped_refptr<net::HostResolver> resolver(
+      net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism));
+  net::HostResolver::RequestInfo info(host_name, port);
+  int rv = resolver->Resolve(info, &addr, NULL, NULL, BoundNetLog());
+  if (rv != net::OK)
+    return false;
+
+  net::TCPPinger pinger(addr);
+  rv = pinger.Ping(base::TimeDelta::FromMilliseconds(connection_timeout_),
+                   connection_attempts_);
+  return rv == net::OK;
+}
+
+bool TestServerLauncher::WaitToFinish(int timeout_ms) {
+  if (!process_handle_)
+    return true;
+
+  bool ret = base::WaitForSingleProcess(process_handle_, timeout_ms);
+  if (ret) {
+    base::CloseProcessHandle(process_handle_);
+    process_handle_ = base::kNullProcessHandle;
+    LOG(INFO) << "Finished.";
+  } else {
+    LOG(INFO) << "Timed out.";
+  }
+  return ret;
+}
+
+bool TestServerLauncher::Stop() {
+  if (!process_handle_)
+    return true;
+
+  // First check if the process has already terminated.
+  bool ret = base::WaitForSingleProcess(process_handle_, 0);
+  if (!ret)
+    ret = base::KillProcess(process_handle_, 1, true);
+
+  if (ret) {
+    base::CloseProcessHandle(process_handle_);
+    process_handle_ = base::kNullProcessHandle;
+    LOG(INFO) << "Stopped.";
+  } else {
+    LOG(INFO) << "Kill failed?";
+  }
+
+  return ret;
+}
+
+TestServerLauncher::~TestServerLauncher() {
+#if defined(OS_MACOSX)
+  SetMacTestCertificate(NULL);
+#endif
+  Stop();
+}
+
+FilePath TestServerLauncher::GetRootCertPath() {
+  FilePath path(cert_dir_);
+  path = path.AppendASCII("root_ca_cert.crt");
+  return path;
+}
+
+FilePath TestServerLauncher::GetOKCertPath() {
+  FilePath path(cert_dir_);
+  path = path.AppendASCII("ok_cert.pem");
+  return path;
+}
+
+FilePath TestServerLauncher::GetExpiredCertPath() {
+  FilePath path(cert_dir_);
+  path = path.AppendASCII("expired_cert.pem");
+  return path;
+}
+
+bool TestServerLauncher::LoadTestRootCert() {
+#if defined(USE_NSS)
+  if (cert_)
+    return true;
+
+  // TODO(dkegel): figure out how to get this to only happen once?
+
+  // This currently leaks a little memory.
+  // TODO(dkegel): fix the leak and remove the entry in
+  // tools/valgrind/memcheck/suppressions.txt
+  ANNOTATE_SCOPED_MEMORY_LEAK;  // Tell heap checker about the leak.
+  cert_ = LoadTemporaryRootCert(GetRootCertPath());
+  DCHECK(cert_);
+  return (cert_ != NULL);
+#elif defined(OS_MACOSX)
+  X509Certificate* cert = LoadTemporaryRootCert(GetRootCertPath());
+  if (!cert)
+    return false;
+  SetMacTestCertificate(cert);
+  return true;
+#else
+  return true;
+#endif
+}
+
+bool TestServerLauncher::CheckCATrusted() {
+#if defined(OS_WIN)
+  HCERTSTORE cert_store = CertOpenSystemStore(NULL, L"ROOT");
+  if (!cert_store) {
+    LOG(ERROR) << " could not open trusted root CA store";
+    return false;
+  }
+  PCCERT_CONTEXT cert =
+      CertFindCertificateInStore(cert_store,
+                                 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+                                 0,
+                                 CERT_FIND_ISSUER_STR,
+                                 kCertIssuerName,
+                                 NULL);
+  if (cert)
+    CertFreeCertificateContext(cert);
+  CertCloseStore(cert_store, 0);
+
+  if (!cert) {
+    LOG(ERROR) << " TEST CONFIGURATION ERROR: you need to import the test ca "
+                  "certificate to your trusted roots for this test to work. "
+                  "For more info visit:\n"
+                  "http://dev.chromium.org/developers/testing\n";
+    return false;
+  }
+#endif
+  return true;
+}
+
+#if defined(OS_WIN)
+bool LaunchTestServerAsJob(const std::wstring& cmdline,
+                           bool start_hidden,
+                           base::ProcessHandle* process_handle,
+                           ScopedHandle* job_handle) {
+  // Launch test server process.
+  STARTUPINFO startup_info = {0};
+  startup_info.cb = sizeof(startup_info);
+  startup_info.dwFlags = STARTF_USESHOWWINDOW;
+  startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW;
+  PROCESS_INFORMATION process_info;
+
+  // If this code is run under a debugger, the test server process is
+  // automatically associated with a job object created by the debugger.
+  // The CREATE_BREAKAWAY_FROM_JOB flag is used to prevent this.
+  if (!CreateProcess(NULL,
+                     const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
+                     FALSE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL,
+                     &startup_info, &process_info)) {
+    LOG(ERROR) << "Could not create process.";
+    return false;
+  }
+  CloseHandle(process_info.hThread);
+
+  // If the caller wants the process handle, we won't close it.
+  if (process_handle) {
+    *process_handle = process_info.hProcess;
+  } else {
+    CloseHandle(process_info.hProcess);
+  }
+
+  // Create a JobObject and associate the test server process with it.
+  job_handle->Set(CreateJobObject(NULL, NULL));
+  if (!job_handle->IsValid()) {
+    LOG(ERROR) << "Could not create JobObject.";
+    return false;
+  } else {
+    JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
+    limit_info.BasicLimitInformation.LimitFlags =
+        JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+    if (0 == SetInformationJobObject(job_handle->Get(),
+      JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info))) {
+      LOG(ERROR) << "Could not SetInformationJobObject.";
+      return false;
+    }
+    if (0 == AssignProcessToJobObject(job_handle->Get(),
+                                      process_info.hProcess)) {
+      LOG(ERROR) << "Could not AssignProcessToObject.";
+      return false;
+    }
+  }
+  return true;
+}
+#endif
+
+}  // namespace net
diff --git a/net/test/test_server.h b/net/test/test_server.h
new file mode 100644
index 0000000..25f2c8b
--- /dev/null
+++ b/net/test/test_server.h
@@ -0,0 +1,149 @@
+// Copyright (c) 2010 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_TEST_TEST_SERVER_H_
+#define NET_TEST_TEST_SERVER_H_
+
+#include "build/build_config.h"
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/process_util.h"
+
+#if defined(OS_WIN)
+#include "base/scoped_handle_win.h"
+#endif
+
+#if defined(USE_NSS)
+#include "base/ref_counted.h"
+#include "net/base/x509_certificate.h"
+#endif
+
+namespace net {
+
+// This object bounds the lifetime of an external python-based HTTP/HTTPS/FTP
+// server that can provide various responses useful for testing.
+// A few basic convenience methods are provided, but no
+// URL handling methods (those belong at a higher layer, e.g. in
+// url_request_unittest.h).
+
+class TestServerLauncher {
+ public:
+  TestServerLauncher();
+  TestServerLauncher(int connection_attempts, int connection_timeout);
+
+  virtual ~TestServerLauncher();
+
+  enum Protocol {
+    ProtoHTTP, ProtoFTP
+  };
+
+  // Load the test root cert, if it hasn't been loaded yet.
+  bool LoadTestRootCert();
+
+  // Tells the server to enable/disable servicing each request
+  // in a separate process. Takes effect only if called before Start.
+  void set_forking(bool forking) { forking_ = forking; }
+
+  // Start src/net/tools/testserver/testserver.py and
+  // ask it to serve the given protocol.
+  // If protocol is HTTP, and cert_path is not empty, serves HTTPS.
+  // file_root_url specifies the root url on the server that documents will be
+  // served out of. This is /files/ by default.
+  // Returns true on success, false if files not found or root cert
+  // not trusted.
+  bool Start(net::TestServerLauncher::Protocol protocol,
+             const std::string& host_name, int port,
+             const FilePath& document_root,
+             const FilePath& cert_path,
+             const std::wstring& file_root_url);
+
+  // Stop the server started by Start().
+  bool Stop();
+
+  // If you access the server's Kill url, it will exit by itself
+  // without a call to Stop().
+  // WaitToFinish is handy in that case.
+  // It returns true if the server exited cleanly.
+  bool WaitToFinish(int milliseconds);
+
+  // Paths to a good, an expired, and an invalid server certificate
+  // (use as arguments to Start()).
+  FilePath GetOKCertPath();
+  FilePath GetExpiredCertPath();
+
+  FilePath GetDocumentRootPath() { return document_root_dir_; }
+
+  // Issuer name of the root cert that should be trusted for the test to work.
+  static const wchar_t kCertIssuerName[];
+
+  // Hostname to use for test server
+  static const char kHostName[];
+
+  // Different hostname to use for test server (that still resolves to same IP)
+  static const char kMismatchedHostName[];
+
+  // Port to use for test server
+  static const int kOKHTTPSPort;
+
+  // Port to use for bad test server
+  static const int kBadHTTPSPort;
+
+ private:
+  // Wait a while for the server to start, return whether
+  // we were able to make a connection to it.
+  bool WaitToStart(const std::string& host_name, int port);
+
+  // Append to PYTHONPATH so Python can find pyftpdlib and tlslite.
+  void SetPythonPath();
+
+  // Path to our test root certificate.
+  FilePath GetRootCertPath();
+
+  // Returns false if our test root certificate is not trusted.
+  bool CheckCATrusted();
+
+  // Initilize the certificate path.
+  void InitCertPath();
+
+  FilePath document_root_dir_;
+
+  FilePath cert_dir_;
+
+  FilePath python_runtime_;
+
+  base::ProcessHandle process_handle_;
+
+#if defined(OS_WIN)
+  // JobObject used to clean up orphaned child processes.
+  ScopedHandle job_handle_;
+#endif
+
+  // True if the server should handle each request in a separate process.
+  bool forking_;
+
+  // Number of tries and timeout for each try used for WaitToStart.
+  int connection_attempts_;
+  int connection_timeout_;
+
+#if defined(USE_NSS)
+  scoped_refptr<X509Certificate> cert_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(TestServerLauncher);
+};
+
+#if defined(OS_WIN)
+// Launch test server as a job so that it is not orphaned if the test case is
+// abnormally terminated.
+bool LaunchTestServerAsJob(const std::wstring& cmdline,
+                           bool start_hidden,
+                           base::ProcessHandle* process_handle,
+                           ScopedHandle* job_handle);
+#endif
+
+}  // namespace net
+
+#endif  // NET_TEST_TEST_SERVER_H_
diff --git a/net/third_party/gssapi/LICENSE b/net/third_party/gssapi/LICENSE
new file mode 100644
index 0000000..cac53b2
--- /dev/null
+++ b/net/third_party/gssapi/LICENSE
@@ -0,0 +1,19 @@
+Copyright 1993 by OpenVision Technologies, Inc.
+
+Permission to use, copy, modify, distribute, and sell this software
+and its documentation for any purpose is hereby granted without fee,
+provided that the above copyright notice appears in all copies and
+that both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of OpenVision not be used
+in advertising or publicity pertaining to distribution of the software
+without specific, written prior permission. OpenVision makes no
+representations about the suitability of this software for any
+purpose.  It is provided "as is" without express or implied warranty.
+
+OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/net/third_party/gssapi/README.chromium b/net/third_party/gssapi/README.chromium
new file mode 100644
index 0000000..5261c72
--- /dev/null
+++ b/net/third_party/gssapi/README.chromium
@@ -0,0 +1,18 @@
+Name: gssapi
+URL: https://hg.mozilla.org/mozilla-central/file/05f3c68e73c9/extensions/auth/gssapi.h
+InfoURL: http://web.mit.edu/Kerberos/krb5-1.7/krb5-1.7.1/doc/krb5-install.html
+Version: krb5-1.8.1
+License: OpenVision Technologies
+
+Description:
+This is Chromium's copy of the Mozilla gssapi header.
+
+Originally obtained from Mozilla's Mercurial repository
+on 11 May 2010.
+
+The LICENSE from the header has been copied here.
+
+Local Modifications:
+There are no local changes to the code itself.
+
+A gssapi.gyp file has been added for building with Chromium.
diff --git a/net/third_party/gssapi/gssapi.gyp b/net/third_party/gssapi/gssapi.gyp
new file mode 100644
index 0000000..0b8ae8c
--- /dev/null
+++ b/net/third_party/gssapi/gssapi.gyp
@@ -0,0 +1,21 @@
+# Copyright (c) 2010 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'gssapi',
+      'type': 'none',
+      'sources': [
+        'gssapi.h',
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/net/third_party/gssapi/gssapi.h b/net/third_party/gssapi/gssapi.h
new file mode 100644
index 0000000..45e5020
--- /dev/null
+++ b/net/third_party/gssapi/gssapi.h
@@ -0,0 +1,844 @@
+/*
+ * Copied from Firefox source extensions/auth/gssapi.h
+ */
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Copyright 1993 by OpenVision Technologies, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OpenVision not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. OpenVision makes no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ ****** END LICENSE BLOCK ***** */
+
+#ifndef GSSAPI_H_
+#define GSSAPI_H_
+
+/*
+ * Also define _GSSAPI_H_ as that is what the Kerberos 5 code defines and
+ * what header files on some systems look for.
+ */
+#define _GSSAPI_H_
+
+/*
+ * On Mac OS X, Kerberos/Kerberos.h is used to gain access to certain
+ * system-specific Kerberos functions, but on 10.4, that file also brings
+ * in other headers that conflict with this one.
+ */
+#define _GSSAPI_GENERIC_H_
+#define _GSSAPI_KRB5_H_
+
+/*
+ * Define windows specific needed parameters.
+ */
+
+#ifndef GSS_CALLCONV
+#if defined(_WIN32)
+#define GSS_CALLCONV __stdcall
+#define GSS_CALLCONV_C __cdecl
+#else
+#define GSS_CALLCONV
+#define GSS_CALLCONV_C
+#endif
+#endif /* GSS_CALLCONV */
+
+#ifdef GSS_USE_FUNCTION_POINTERS
+#ifdef _WIN32
+#undef GSS_CALLCONV
+#define GSS_CALLCONV
+#define GSS_FUNC(f) (__stdcall *f##_type)
+#else
+#define GSS_FUNC(f) (*f##_type)
+#endif
+#define GSS_MAKE_TYPEDEF typedef
+#else
+#define GSS_FUNC(f) f
+#define GSS_MAKE_TYPEDEF
+#endif
+
+/*
+ * First, include stddef.h to get size_t defined.
+ */
+#include <stddef.h>
+
+/*
+ * Configure set the following
+ */
+
+#ifndef SIZEOF_LONG
+#undef SIZEOF_LONG
+#endif
+#ifndef SIZEOF_SHORT
+#undef SIZEOF_SHORT
+#endif
+
+#ifndef EXTERN_C_BEGIN
+#ifdef __cplusplus
+#define EXTERN_C_BEGIN extern "C" {
+#define EXTERN_C_END }
+#else
+#define EXTERN_C_BEGIN
+#define EXTERN_C_END
+#endif
+#endif
+
+EXTERN_C_BEGIN
+
+/*
+ * If the platform supports the xom.h header file, it should be
+ * included here.
+ */
+/* #include <xom.h> */
+
+
+/*
+ * Now define the three implementation-dependent types.
+ */
+
+typedef void * gss_name_t ;
+typedef void * gss_ctx_id_t ;
+typedef void * gss_cred_id_t ;
+
+
+/*
+ * The following type must be defined as the smallest natural
+ * unsigned integer supported by the platform that has at least
+ * 32 bits of precision.
+ */
+
+#if SIZEOF_LONG == 4
+typedef unsigned long gss_uint32;
+#elif SIZEOF_SHORT == 4
+typedef unsigned short gss_uint32;
+#else
+typedef unsigned int gss_uint32;
+#endif
+
+#ifdef OM_STRING
+
+/*
+ * We have included the xom.h header file.  Verify that OM_uint32
+ * is defined correctly.
+ */
+
+#if sizeof(gss_uint32) != sizeof(OM_uint32)
+#error Incompatible definition of OM_uint32 from xom.h
+#endif
+
+typedef OM_object_identifier gss_OID_desc, *gss_OID;
+
+#else /* !OM_STRING */
+
+/*
+ * We can't use X/Open definitions, so roll our own.
+ */
+typedef gss_uint32 OM_uint32;
+typedef struct gss_OID_desc_struct {
+  OM_uint32 length;
+  void *elements;
+} gss_OID_desc, *gss_OID;
+
+#endif /* !OM_STRING */
+
+typedef struct gss_OID_set_desc_struct  {
+  size_t     count;
+  gss_OID    elements;
+} gss_OID_set_desc, *gss_OID_set;
+
+
+/*
+ * For now, define a QOP-type as an OM_uint32
+ */
+typedef OM_uint32 gss_qop_t;
+
+typedef int gss_cred_usage_t;
+
+
+typedef struct gss_buffer_desc_struct {
+  size_t length;
+  void *value;
+} gss_buffer_desc, *gss_buffer_t;
+
+typedef struct gss_channel_bindings_struct {
+  OM_uint32 initiator_addrtype;
+  gss_buffer_desc initiator_address;
+  OM_uint32 acceptor_addrtype;
+  gss_buffer_desc acceptor_address;
+  gss_buffer_desc application_data;
+} *gss_channel_bindings_t;
+
+
+/*
+ * Flag bits for context-level services.
+ */
+#define GSS_C_DELEG_FLAG 1
+#define GSS_C_MUTUAL_FLAG 2
+#define GSS_C_REPLAY_FLAG 4
+#define GSS_C_SEQUENCE_FLAG 8
+#define GSS_C_CONF_FLAG 16
+#define GSS_C_INTEG_FLAG 32
+#define GSS_C_ANON_FLAG 64
+#define GSS_C_PROT_READY_FLAG 128
+#define GSS_C_TRANS_FLAG 256
+
+/*
+ * Credential usage options
+ */
+#define GSS_C_BOTH 0
+#define GSS_C_INITIATE 1
+#define GSS_C_ACCEPT 2
+
+/*
+ * Status code types for gss_display_status
+ */
+#define GSS_C_GSS_CODE 1
+#define GSS_C_MECH_CODE 2
+
+/*
+ * The constant definitions for channel-bindings address families
+ */
+#define GSS_C_AF_UNSPEC     0
+#define GSS_C_AF_LOCAL      1
+#define GSS_C_AF_INET       2
+#define GSS_C_AF_IMPLINK    3
+#define GSS_C_AF_PUP        4
+#define GSS_C_AF_CHAOS      5
+#define GSS_C_AF_NS         6
+#define GSS_C_AF_NBS        7
+#define GSS_C_AF_ECMA       8
+#define GSS_C_AF_DATAKIT    9
+#define GSS_C_AF_CCITT      10
+#define GSS_C_AF_SNA        11
+#define GSS_C_AF_DECnet     12
+#define GSS_C_AF_DLI        13
+#define GSS_C_AF_LAT        14
+#define GSS_C_AF_HYLINK     15
+#define GSS_C_AF_APPLETALK  16
+#define GSS_C_AF_BSC        17
+#define GSS_C_AF_DSS        18
+#define GSS_C_AF_OSI        19
+#define GSS_C_AF_X25        21
+
+#define GSS_C_AF_NULLADDR   255
+
+/*
+ * Various Null values
+ */
+#define GSS_C_NO_NAME ((gss_name_t) 0)
+#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
+#define GSS_C_NO_OID ((gss_OID) 0)
+#define GSS_C_NO_OID_SET ((gss_OID_set) 0)
+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
+#define GSS_C_EMPTY_BUFFER {0, NULL}
+
+/*
+ * Some alternate names for a couple of the above
+ * values.  These are defined for V1 compatibility.
+ */
+#define GSS_C_NULL_OID GSS_C_NO_OID
+#define GSS_C_NULL_OID_SET GSS_C_NO_OID_SET
+
+/*
+ * Define the default Quality of Protection for per-message
+ * services.  Note that an implementation that offers multiple
+ * levels of QOP may define GSS_C_QOP_DEFAULT to be either zero
+ * (as done here) to mean "default protection", or to a specific
+ * explicit QOP value.  However, a value of 0 should always be
+ * interpreted by a GSSAPI implementation as a request for the
+ * default protection level.
+ */
+#define GSS_C_QOP_DEFAULT 0
+
+/*
+ * Expiration time of 2^32-1 seconds means infinite lifetime for a
+ * credential or security context
+ */
+#define GSS_C_INDEFINITE 0xfffffffful
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ *              "\x01\x02\x01\x01"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ *  infosys(1) gssapi(2) generic(1) user_name(1)}.  The constant
+ * GSS_C_NT_USER_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_USER_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ *              "\x01\x02\x01\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ *  infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
+ * The constant GSS_C_NT_MACHINE_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_MACHINE_UID_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ *              "\x01\x02\x01\x03"},
+ * corresponding to an object-identifier value of
+ * {iso(1) member-body(2) United States(840) mit(113554)
+ *  infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
+ * The constant GSS_C_NT_STRING_UID_NAME should be
+ * initialized to point to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_STRING_UID_NAME;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
+ * corresponding to an object-identifier value of
+ * {iso(1) org(3) dod(6) internet(1) security(5)
+ * nametypes(6) gss-host-based-services(2)).  The constant
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
+ * to that gss_OID_desc.  This is a deprecated OID value, and
+ * implementations wishing to support hostbased-service names
+ * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
+ * defined below, to identify such names;
+ * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
+ * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
+ * parameter, but should not be emitted by GSSAPI
+ * implementations
+ */
+extern gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {10, (void *)"\x2a\x86\x48\x86\xf7\x12"
+ *              "\x01\x02\x01\x04"}, corresponding to an
+ * object-identifier value of {iso(1) member-body(2)
+ * Unites States(840) mit(113554) infosys(1) gssapi(2)
+ * generic(1) service_name(4)}.  The constant
+ * GSS_C_NT_HOSTBASED_SERVICE should be initialized
+ * to point to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_HOSTBASED_SERVICE;
+
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\01\x05\x06\x03"},
+ * corresponding to an object identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 3(gss-anonymous-name)}.  The constant
+ * and GSS_C_NT_ANONYMOUS should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_ANONYMOUS;
+
+/*
+ * The implementation must reserve static storage for a
+ * gss_OID_desc object containing the value
+ * {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
+ * corresponding to an object-identifier value of
+ * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+ * 6(nametypes), 4(gss-api-exported-name)}.  The constant
+ * GSS_C_NT_EXPORT_NAME should be initialized to point
+ * to that gss_OID_desc.
+ */
+extern gss_OID GSS_C_NT_EXPORT_NAME;
+
+/* Major status codes */
+
+#define GSS_S_COMPLETE 0
+
+/*
+ * Some "helper" definitions to make the status code macros obvious.
+ */
+#define GSS_C_CALLING_ERROR_OFFSET 24
+#define GSS_C_ROUTINE_ERROR_OFFSET 16
+#define GSS_C_SUPPLEMENTARY_OFFSET 0
+#define GSS_C_CALLING_ERROR_MASK 0377ul
+#define GSS_C_ROUTINE_ERROR_MASK 0377ul
+#define GSS_C_SUPPLEMENTARY_MASK 0177777ul
+
+/*
+ * The macros that test status codes for error conditions.
+ * Note that the GSS_ERROR() macro has changed slightly from
+ * the V1 GSSAPI so that it now evaluates its argument
+ * only once.
+ */
+#define GSS_CALLING_ERROR(x) \
+(x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
+#define GSS_ROUTINE_ERROR(x) \
+     (x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
+#define GSS_SUPPLEMENTARY_INFO(x) \
+     (x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
+#define GSS_ERROR(x) \
+     (x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
+           (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
+
+/*
+ * Now the actual status code definitions
+ */
+
+/*
+ * Calling errors:
+ */
+#define GSS_S_CALL_INACCESSIBLE_READ \
+     (1ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_INACCESSIBLE_WRITE \
+     (2ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_BAD_STRUCTURE \
+     (3ul << GSS_C_CALLING_ERROR_OFFSET)
+
+/*
+ * Routine errors:
+ */
+#define GSS_S_BAD_MECH (1ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAME (2ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAMETYPE (3ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_BINDINGS (4ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_STATUS (5ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_SIG (6ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_MIC GSS_S_BAD_SIG
+#define GSS_S_NO_CRED (7ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NO_CONTEXT (8ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_TOKEN (9ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_CREDENTIAL (10ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CREDENTIALS_EXPIRED (11ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CONTEXT_EXPIRED (12ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_FAILURE (13ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_QOP (14ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAUTHORIZED (15ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAVAILABLE (16ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DUPLICATE_ELEMENT (17ul << GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NAME_NOT_MN (18ul << GSS_C_ROUTINE_ERROR_OFFSET)
+
+/*
+ * Supplementary info bits:
+ */
+#define GSS_S_CONTINUE_NEEDED (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
+#define GSS_S_DUPLICATE_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
+#define GSS_S_OLD_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
+#define GSS_S_UNSEQ_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
+#define GSS_S_GAP_TOKEN (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
+
+/*
+ * Finally, function prototypes for the GSS-API routines.
+ */
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_acquire_cred)
+(OM_uint32 *,             /*  minor_status */
+ const gss_name_t,        /* desired_name */
+ OM_uint32,               /* time_req */
+ const gss_OID_set,       /* desired_mechs */
+ gss_cred_usage_t,        /* cred_usage */
+ gss_cred_id_t *,         /* output_cred_handle */
+ gss_OID_set *,           /* actual_mechs */
+ OM_uint32 *              /* time_rec */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_release_cred)
+(OM_uint32 *,             /* minor_status */
+ gss_cred_id_t *          /* cred_handle */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_init_sec_context)
+(OM_uint32 *,             /* minor_status */
+ const gss_cred_id_t,     /* initiator_cred_handle */
+ gss_ctx_id_t *,          /* context_handle */
+ const gss_name_t,        /* target_name */
+ const gss_OID,           /* mech_type */
+ OM_uint32,               /* req_flags */
+ OM_uint32,               /* time_req */
+ const gss_channel_bindings_t, /* input_chan_bindings */
+ const gss_buffer_t,      /* input_token */
+ gss_OID *,               /* actual_mech_type */
+ gss_buffer_t,            /* output_token */
+ OM_uint32 *,             /* ret_flags */
+ OM_uint32 *              /* time_rec */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_accept_sec_context)
+(OM_uint32 *,             /* minor_status */
+ gss_ctx_id_t *,          /* context_handle */
+ const gss_cred_id_t,     /* acceptor_cred_handle */
+ const gss_buffer_t,      /* input_token_buffer */
+ const gss_channel_bindings_t, /* input_chan_bindings */
+ gss_name_t *,            /* src_name */
+ gss_OID *,               /* mech_type */
+ gss_buffer_t,            /* output_token */
+ OM_uint32 *,             /* ret_flags */
+ OM_uint32 *,             /* time_rec */
+ gss_cred_id_t *          /* delegated_cred_handle */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_process_context_token)
+(OM_uint32 *,             /* minor_status */
+ const gss_ctx_id_t,      /* context_handle */
+ const gss_buffer_t       /* token_buffer */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_delete_sec_context)
+(OM_uint32 *,             /* minor_status */
+ gss_ctx_id_t *,          /* context_handle */
+ gss_buffer_t             /* output_token */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_context_time)
+(OM_uint32 *,             /* minor_status */
+ const gss_ctx_id_t,      /* context_handle */
+ OM_uint32 *              /* time_rec */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_get_mic)
+(OM_uint32 *,             /* minor_status */
+ const gss_ctx_id_t,      /* context_handle */
+ gss_qop_t,               /* qop_req */
+ const gss_buffer_t,      /* message_buffer */
+ gss_buffer_t             /* message_token */
+ );
+
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_verify_mic)
+(OM_uint32 *,             /* minor_status */
+ const gss_ctx_id_t,      /* context_handle */
+ const gss_buffer_t,      /* message_buffer */
+ const gss_buffer_t,      /* token_buffer */
+ gss_qop_t *              /* qop_state */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_wrap)
+(OM_uint32 *,             /* minor_status */
+ const gss_ctx_id_t,      /* context_handle */
+ int,                     /* conf_req_flag */
+ gss_qop_t,               /* qop_req */
+ const gss_buffer_t,      /* input_message_buffer */
+ int *,                   /* conf_state */
+ gss_buffer_t             /* output_message_buffer */
+ );
+
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_unwrap)
+(OM_uint32 *,             /* minor_status */
+ const gss_ctx_id_t,      /* context_handle */
+ const gss_buffer_t,      /* input_message_buffer */
+ gss_buffer_t,            /* output_message_buffer */
+ int *,                   /* conf_state */
+ gss_qop_t *              /* qop_state */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_display_status)
+(OM_uint32 *,             /* minor_status */
+ OM_uint32,               /* status_value */
+ int,                     /* status_type */
+ const gss_OID,           /* mech_type */
+ OM_uint32 *,             /* message_context */
+ gss_buffer_t             /* status_string */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_indicate_mechs)
+(OM_uint32 *,             /* minor_status */
+ gss_OID_set *            /* mech_set */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_compare_name)
+(OM_uint32 *,             /* minor_status */
+ const gss_name_t,        /* name1 */
+ const gss_name_t,        /* name2 */
+ int *                    /* name_equal */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_display_name)
+(OM_uint32 *,             /* minor_status */
+ const gss_name_t,        /* input_name */
+ gss_buffer_t,            /* output_name_buffer */
+ gss_OID *                /* output_name_type */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_import_name)
+(OM_uint32 *,             /* minor_status */
+ const gss_buffer_t,      /* input_name_buffer */
+ const gss_OID,           /* input_name_type */
+ gss_name_t *             /* output_name */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_export_name)
+(OM_uint32  *,            /* minor_status */
+ const gss_name_t,        /* input_name */
+ gss_buffer_t             /* exported_name */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_release_name)
+(OM_uint32 *,             /* minor_status */
+ gss_name_t *             /* input_name */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_release_buffer)
+(OM_uint32 *,             /* minor_status */
+ gss_buffer_t             /* buffer */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_release_oid_set)
+(OM_uint32 *,             /* minor_status */
+ gss_OID_set *            /* set */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_inquire_cred)
+(OM_uint32 *,             /* minor_status */
+ const gss_cred_id_t,     /* cred_handle */
+ gss_name_t *,            /* name */
+ OM_uint32 *,             /* lifetime */
+ gss_cred_usage_t *,      /* cred_usage */
+ gss_OID_set *            /* mechanisms */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_inquire_context)
+(OM_uint32 *,             /* minor_status */
+ const gss_ctx_id_t,      /* context_handle */
+ gss_name_t *,            /* src_name */
+ gss_name_t *,            /* targ_name */
+ OM_uint32 *,             /* lifetime_rec */
+ gss_OID *,               /* mech_type */
+ OM_uint32 *,             /* ctx_flags */
+ int *,                   /* locally_initiated */
+ int *                    /* open */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_wrap_size_limit)
+(OM_uint32 *,             /* minor_status */
+ const gss_ctx_id_t,      /* context_handle */
+ int,                     /* conf_req_flag */
+ gss_qop_t,               /* qop_req */
+ OM_uint32,               /* req_output_size */
+ OM_uint32 *              /* max_input_size */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_add_cred)
+(OM_uint32 *,             /* minor_status */
+ const gss_cred_id_t,     /* input_cred_handle */
+ const gss_name_t,        /* desired_name */
+ const gss_OID,           /* desired_mech */
+ gss_cred_usage_t,        /* cred_usage */
+ OM_uint32,               /* initiator_time_req */
+ OM_uint32,               /* acceptor_time_req */
+ gss_cred_id_t *,         /* output_cred_handle */
+ gss_OID_set *,           /* actual_mechs */
+ OM_uint32 *,             /* initiator_time_rec */
+ OM_uint32 *              /* acceptor_time_rec */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_inquire_cred_by_mech)
+(OM_uint32 *,             /* minor_status */
+ const gss_cred_id_t,     /* cred_handle */
+ const gss_OID,           /* mech_type */
+ gss_name_t *,            /* name */
+ OM_uint32 *,             /* initiator_lifetime */
+ OM_uint32 *,             /* acceptor_lifetime */
+ gss_cred_usage_t *       /* cred_usage */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_export_sec_context)
+(OM_uint32 *,             /* minor_status */
+ gss_ctx_id_t *,          /* context_handle */
+ gss_buffer_t             /* interprocess_token */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_import_sec_context)
+(OM_uint32 *,             /* minor_status */
+ const gss_buffer_t,      /* interprocess_token */
+ gss_ctx_id_t *           /* context_handle */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_create_empty_oid_set)
+(OM_uint32 *,             /* minor_status */
+ gss_OID_set *            /* oid_set */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_add_oid_set_member)
+(OM_uint32 *,             /* minor_status */
+ const gss_OID,           /* member_oid */
+ gss_OID_set *            /* oid_set */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_test_oid_set_member)
+(OM_uint32 *,             /* minor_status */
+ const gss_OID,           /* member */
+ const gss_OID_set,       /* set */
+ int *                    /* present */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_inquire_names_for_mech)
+(OM_uint32 *,             /* minor_status */
+ const gss_OID,           /* mechanism */
+ gss_OID_set *            /* name_types */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_inquire_mechs_for_name)
+(OM_uint32 *,             /* minor_status */
+ const gss_name_t,        /* input_name */
+ gss_OID_set *            /* mech_types */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_canonicalize_name)
+(OM_uint32 *,             /* minor_status */
+ const gss_name_t,        /* input_name */
+ const gss_OID,           /* mech_type */
+ gss_name_t *             /* output_name */
+ );
+
+GSS_MAKE_TYPEDEF
+OM_uint32
+GSS_CALLCONV GSS_FUNC(gss_duplicate_name)
+(OM_uint32 *,             /* minor_status */
+ const gss_name_t,        /* src_name */
+ gss_name_t *             /* dest_name */
+ );
+
+   /*
+    * The following routines are obsolete variants of gss_get_mic,
+    * gss_verify_mic, gss_wrap and gss_unwrap.  They should be
+    * provided by GSSAPI V2 implementations for backwards
+    * compatibility with V1 applications.  Distinct entrypoints
+    * (as opposed to #defines) should be provided, both to allow
+    * GSSAPI V1 applications to link against GSSAPI V2 implementations,
+    * and to retain the slight parameter type differences between the
+    * obsolete versions of these routines and their current forms.
+    */
+
+   GSS_MAKE_TYPEDEF
+   OM_uint32
+   GSS_CALLCONV GSS_FUNC(gss_sign)
+              (OM_uint32 *,        /* minor_status */
+               gss_ctx_id_t,       /* context_handle */
+               int,                /* qop_req */
+               gss_buffer_t,       /* message_buffer */
+               gss_buffer_t        /* message_token */
+              );
+
+
+   GSS_MAKE_TYPEDEF
+   OM_uint32
+   GSS_CALLCONV GSS_FUNC(gss_verify)
+              (OM_uint32 *,        /* minor_status */
+               gss_ctx_id_t,       /* context_handle */
+               gss_buffer_t,       /* message_buffer */
+               gss_buffer_t,       /* token_buffer */
+               int *               /* qop_state */
+              );
+
+   GSS_MAKE_TYPEDEF
+   OM_uint32
+   GSS_CALLCONV GSS_FUNC(gss_seal)
+              (OM_uint32 *,        /* minor_status */
+               gss_ctx_id_t,       /* context_handle */
+               int,                /* conf_req_flag */
+               int,                /* qop_req */
+               gss_buffer_t,       /* input_message_buffer */
+               int *,              /* conf_state */
+               gss_buffer_t        /* output_message_buffer */
+              );
+
+
+   GSS_MAKE_TYPEDEF
+   OM_uint32
+   GSS_CALLCONV GSS_FUNC(gss_unseal)
+              (OM_uint32 *,        /* minor_status */
+               gss_ctx_id_t,       /* context_handle */
+               gss_buffer_t,       /* input_message_buffer */
+               gss_buffer_t,       /* output_message_buffer */
+               int *,              /* conf_state */
+               int *               /* qop_state */
+              );
+
+
+
+EXTERN_C_END
+
+#endif /* GSSAPI_H_ */
+
diff --git a/net/third_party/mozilla_security_manager/LICENSE b/net/third_party/mozilla_security_manager/LICENSE
new file mode 100644
index 0000000..17de8fb
--- /dev/null
+++ b/net/third_party/mozilla_security_manager/LICENSE
@@ -0,0 +1,35 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
diff --git a/net/third_party/mozilla_security_manager/README.chromium b/net/third_party/mozilla_security_manager/README.chromium
new file mode 100644
index 0000000..d030d65
--- /dev/null
+++ b/net/third_party/mozilla_security_manager/README.chromium
@@ -0,0 +1,12 @@
+Name: Mozilla Personal Security Manager
+URL: http://mxr.mozilla.org/mozilla1.9.2/source/security/manager/
+InfoURL: http://www.mozilla.org/
+Version: Mozilla 1.9.2
+
+Description:
+This is selected code bits from Mozilla's Personal Security Manager.
+
+Local Modifications:
+Files are forked from Mozilla's because of the heavy adaptations necessary.
+Differences are using Chromium localization and other libraries instead of
+Mozilla's, matching the Chromium style, etc.
diff --git a/net/third_party/mozilla_security_manager/nsKeygenHandler.cpp b/net/third_party/mozilla_security_manager/nsKeygenHandler.cpp
new file mode 100644
index 0000000..ffef66d
--- /dev/null
+++ b/net/third_party/mozilla_security_manager/nsKeygenHandler.cpp
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vipul Gupta <vipul.gupta@sun.com>
+ *   Douglas Stebila <douglas@stebila.ca>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "net/third_party/mozilla_security_manager/nsKeygenHandler.h"
+
+#include <pk11pub.h>
+#include <prerror.h>   // PR_GetError()
+#include <secmod.h>
+#include <secder.h>    // DER_Encode()
+#include <cryptohi.h>  // SEC_DerSignData()
+#include <keyhi.h>     // SECKEY_CreateSubjectPublicKeyInfo()
+
+#include "base/base64.h"
+#include "base/nss_util_internal.h"
+#include "base/nss_util.h"
+#include "base/logging.h"
+
+namespace {
+
+// Template for creating the signed public key structure to be sent to the CA.
+DERTemplate SECAlgorithmIDTemplate[] = {
+  { DER_SEQUENCE,
+    0, NULL, sizeof(SECAlgorithmID) },
+  { DER_OBJECT_ID,
+    offsetof(SECAlgorithmID, algorithm), },
+  { DER_OPTIONAL | DER_ANY,
+    offsetof(SECAlgorithmID, parameters), },
+  { 0, }
+};
+
+DERTemplate CERTSubjectPublicKeyInfoTemplate[] = {
+  { DER_SEQUENCE,
+    0, NULL, sizeof(CERTSubjectPublicKeyInfo) },
+  { DER_INLINE,
+    offsetof(CERTSubjectPublicKeyInfo, algorithm),
+    SECAlgorithmIDTemplate, },
+  { DER_BIT_STRING,
+    offsetof(CERTSubjectPublicKeyInfo, subjectPublicKey), },
+  { 0, }
+};
+
+DERTemplate CERTPublicKeyAndChallengeTemplate[] = {
+  { DER_SEQUENCE,
+    0, NULL, sizeof(CERTPublicKeyAndChallenge) },
+  { DER_ANY,
+    offsetof(CERTPublicKeyAndChallenge, spki), },
+  { DER_IA5_STRING,
+    offsetof(CERTPublicKeyAndChallenge, challenge), },
+  { 0, }
+};
+
+}  // namespace
+
+namespace mozilla_security_manager {
+
+// This function is based on the nsKeygenFormProcessor::GetPublicKey function
+// in mozilla/security/manager/ssl/src/nsKeygenHandler.cpp.
+std::string GenKeyAndSignChallenge(int key_size_in_bits,
+                                   const std::string& challenge,
+                                   bool stores_key) {
+  // Key pair generation mechanism - only RSA is supported at present.
+  PRUint32 keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;  // from nss/pkcs11t.h
+
+  // Temporary structures used for generating the result
+  // in the right format.
+  PK11SlotInfo *slot = NULL;
+  PK11RSAGenParams rsaKeyGenParams;  // Keygen parameters.
+  SECOidTag algTag;  // used by SEC_DerSignData().
+  SECKEYPrivateKey *privateKey = NULL;
+  SECKEYPublicKey *publicKey = NULL;
+  CERTSubjectPublicKeyInfo *spkInfo = NULL;
+  PRArenaPool *arena = NULL;
+  SECStatus sec_rv =SECFailure;
+  SECItem spkiItem;
+  SECItem pkacItem;
+  SECItem signedItem;
+  CERTPublicKeyAndChallenge pkac;
+  void *keyGenParams;
+  bool isSuccess = true;  // Set to false as soon as a step fails.
+
+  std::string result_blob;  // the result.
+
+  // Ensure NSS is initialized.
+  base::EnsureNSSInit();
+
+  slot = base::GetDefaultNSSKeySlot();
+  if (!slot) {
+    LOG(ERROR) << "Couldn't get Internal key slot!";
+    isSuccess = false;
+    goto failure;
+  }
+
+  switch (keyGenMechanism) {
+    case CKM_RSA_PKCS_KEY_PAIR_GEN:
+      rsaKeyGenParams.keySizeInBits = key_size_in_bits;
+      rsaKeyGenParams.pe = DEFAULT_RSA_KEYGEN_PE;
+      keyGenParams = &rsaKeyGenParams;
+
+      algTag = DEFAULT_RSA_KEYGEN_ALG;
+      break;
+    default:
+      // TODO(gauravsh): If we ever support other mechanisms,
+      // this can be changed.
+      LOG(ERROR) << "Only RSA keygen mechanism is supported";
+      isSuccess = false;
+      goto failure;
+  }
+
+  // Need to make sure that the token was initialized.
+  // Assume a null password.
+  sec_rv = PK11_Authenticate(slot, PR_TRUE, NULL);
+  if (SECSuccess != sec_rv) {
+    LOG(ERROR) << "Couldn't initialze PK11 token!";
+    isSuccess = false;
+    goto failure;
+  }
+
+  LOG(INFO) << "Creating key pair...";
+  {
+    base::AutoNSSWriteLock lock;
+    privateKey = PK11_GenerateKeyPair(slot,
+                                      keyGenMechanism,
+                                      keyGenParams,
+                                      &publicKey,
+                                      PR_TRUE,  // isPermanent?
+                                      PR_TRUE,  // isSensitive?
+                                      NULL);
+  }
+  LOG(INFO) << "done.";
+
+  if (!privateKey) {
+    LOG(INFO) << "Generation of Keypair failed!";
+    isSuccess = false;
+    goto failure;
+  }
+
+  // The CA expects the signed public key in a specific format
+  // Let's create that now.
+
+  // Create a subject public key info from the public key.
+  spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey);
+  if (!spkInfo) {
+    LOG(ERROR) << "Couldn't create SubjectPublicKeyInfo from public key";
+    isSuccess = false;
+    goto failure;
+  }
+
+  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+  if (!arena) {
+    LOG(ERROR) << "PORT_NewArena: Couldn't allocate memory";
+    isSuccess = false;
+    goto failure;
+  }
+
+  // DER encode the whole subjectPublicKeyInfo.
+  sec_rv = DER_Encode(arena, &spkiItem, CERTSubjectPublicKeyInfoTemplate,
+                      spkInfo);
+  if (SECSuccess != sec_rv) {
+    LOG(ERROR) << "Couldn't DER Encode subjectPublicKeyInfo";
+    isSuccess = false;
+    goto failure;
+  }
+
+  // Set up the PublicKeyAndChallenge data structure, then DER encode it.
+  pkac.spki = spkiItem;
+  pkac.challenge.type = siBuffer;
+  pkac.challenge.len = challenge.length();
+  pkac.challenge.data = (unsigned char *)challenge.data();
+  sec_rv = DER_Encode(arena, &pkacItem, CERTPublicKeyAndChallengeTemplate,
+                      &pkac);
+  if (SECSuccess != sec_rv) {
+    LOG(ERROR) << "Couldn't DER Encode PublicKeyAndChallenge";
+    isSuccess = false;
+    goto failure;
+  }
+
+  // Sign the DER encoded PublicKeyAndChallenge.
+  sec_rv = SEC_DerSignData(arena, &signedItem, pkacItem.data, pkacItem.len,
+                           privateKey, algTag);
+  if (SECSuccess != sec_rv) {
+    LOG(ERROR) << "Couldn't sign the DER encoded PublicKeyandChallenge";
+    isSuccess = false;
+    goto failure;
+  }
+
+  // Convert the signed public key and challenge into base64/ascii.
+  if (!base::Base64Encode(std::string(reinterpret_cast<char*>(signedItem.data),
+                                      signedItem.len),
+                          &result_blob)) {
+    LOG(ERROR) << "Couldn't convert signed public key into base64";
+    isSuccess = false;
+    goto failure;
+  }
+
+ failure:
+  if (!isSuccess) {
+    LOG(ERROR) << "SSL Keygen failed! (NSS error code " << PR_GetError() << ")";
+  } else {
+    LOG(INFO) << "SSL Keygen succeeded!";
+  }
+
+  // Do cleanups
+  if (privateKey) {
+    // On successful keygen we need to keep the private key, of course,
+    // or we won't be able to use the client certificate.
+    if (!isSuccess || !stores_key) {
+      base::AutoNSSWriteLock lock;
+      PK11_DestroyTokenObject(privateKey->pkcs11Slot, privateKey->pkcs11ID);
+    }
+    SECKEY_DestroyPrivateKey(privateKey);
+  }
+
+  if (publicKey) {
+    if (!isSuccess || !stores_key) {
+      base::AutoNSSWriteLock lock;
+      PK11_DestroyTokenObject(publicKey->pkcs11Slot, publicKey->pkcs11ID);
+    }
+    SECKEY_DestroyPublicKey(publicKey);
+  }
+  if (spkInfo) {
+    SECKEY_DestroySubjectPublicKeyInfo(spkInfo);
+  }
+  if (arena) {
+    PORT_FreeArena(arena, PR_TRUE);
+  }
+  if (slot != NULL) {
+    PK11_FreeSlot(slot);
+  }
+
+  return (isSuccess ? result_blob : std::string());
+}
+
+}  // namespace mozilla_security_manager
diff --git a/net/third_party/mozilla_security_manager/nsKeygenHandler.h b/net/third_party/mozilla_security_manager/nsKeygenHandler.h
new file mode 100644
index 0000000..75703bb
--- /dev/null
+++ b/net/third_party/mozilla_security_manager/nsKeygenHandler.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   David Drinan. (ddrinan@netscape.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _NSKEYGENHANDLER_H_
+#define _NSKEYGENHANDLER_H_
+
+#include <string>
+
+namespace mozilla_security_manager {
+
+#define DEFAULT_RSA_KEYGEN_PE 65537L
+#define DEFAULT_RSA_KEYGEN_ALG SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION
+
+// Generates the key pair and the cert request (SPKAC), and returns a
+// base64-encoded string suitable for use as the form value of <keygen>.
+// Parameters:
+//   key_size_in_bits: key size in bits (usually 2048)
+//   challenge: challenge string sent by server
+//   stores_key: should the generated key pair be stored persistently?
+std::string GenKeyAndSignChallenge(int key_size_in_bits,
+                                   const std::string& challenge,
+                                   bool stores_key);
+
+}  // namespace mozilla_security_manager
+
+#endif //_NSKEYGENHANDLER_H_
diff --git a/net/third_party/nss/LICENSE b/net/third_party/nss/LICENSE
new file mode 100644
index 0000000..0367164
--- /dev/null
+++ b/net/third_party/nss/LICENSE
@@ -0,0 +1,35 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
diff --git a/net/third_party/nss/README.chromium b/net/third_party/nss/README.chromium
new file mode 100644
index 0000000..b1141fe
--- /dev/null
+++ b/net/third_party/nss/README.chromium
@@ -0,0 +1,32 @@
+Name: Network Security Services (NSS)
+URL: http://www.mozilla.org/projects/security/pki/nss/
+
+This directory includes a copy of NSS's libssl from the CVS repo at:
+  :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot
+
+The snapshot was updated to the CVS tag: NSS_3_12_6_RC0
+
+Patches:
+
+  * Next protocol negotiation support.
+    patches/nextproto.patch
+    http://codereview.chromium.org/415005
+
+  * False start support
+    patches/falsestart.patch
+
+  * Commenting out a couple of functions because they need NSS symbols
+    which may not exist in the system NSS library.
+    patches/versionskew.patch
+
+  * Send empty renegotiation info extension instead of SCSV unless TLS is
+    disabled.
+    patches/renegoscsv.patch
+    https://bugzilla.mozilla.org/show_bug.cgi?id=549042
+
+  * Cache the peer's intermediate CA certificates in session ID, so that
+    they're available when we resume a session.
+    patches/cachecerts.patch
+
+The ssl/bodge directory contains files taken from the NSS repo that we required
+for building libssl outside of its usual build environment.
diff --git a/net/third_party/nss/SConstruct b/net/third_party/nss/SConstruct
new file mode 100644
index 0000000..8b7134d
--- /dev/null
+++ b/net/third_party/nss/SConstruct
@@ -0,0 +1,2 @@
+# This file is generated; do not edit.
+SConscript('nss_main.scons')
diff --git a/net/third_party/nss/nss_main.scons b/net/third_party/nss/nss_main.scons
new file mode 100644
index 0000000..b310129
--- /dev/null
+++ b/net/third_party/nss/nss_main.scons
@@ -0,0 +1,326 @@
+# This file is generated; do not edit.
+
+__doc__ = '''
+Wrapper configuration for building this entire "solution,"
+including all the specific targets in various *.scons files.
+'''
+
+import os
+import sys
+
+import SCons.Environment
+import SCons.Util
+
+def GetProcessorCount():
+  '''
+  Detects the number of CPUs on the system. Adapted form:
+  http://codeliberates.blogspot.com/2008/05/detecting-cpuscores-in-python.html
+  '''
+  # Linux, Unix and Mac OS X:
+  if hasattr(os, 'sysconf'):
+    if os.sysconf_names.has_key('SC_NPROCESSORS_ONLN'):
+      # Linux and Unix or Mac OS X with python >= 2.5:
+      return os.sysconf('SC_NPROCESSORS_ONLN')
+    else:  # Mac OS X with Python < 2.5:
+      return int(os.popen2("sysctl -n hw.ncpu")[1].read())
+  # Windows:
+  if os.environ.has_key('NUMBER_OF_PROCESSORS'):
+    return max(int(os.environ.get('NUMBER_OF_PROCESSORS', '1')), 1)
+  return 1  # Default
+
+# Support PROGRESS= to show progress in different ways.
+p = ARGUMENTS.get('PROGRESS')
+if p == 'spinner':
+  Progress(['/\r', '|\r', '\\\r', '-\r'],
+           interval=5,
+           file=open('/dev/tty', 'w'))
+elif p == 'name':
+  Progress('$TARGET\r', overwrite=True, file=open('/dev/tty', 'w'))
+
+# Set the default -j value based on the number of processors.
+SetOption('num_jobs', GetProcessorCount() + 1)
+
+# Have SCons use its cached dependency information.
+SetOption('implicit_cache', 1)
+
+# Only re-calculate MD5 checksums if a timestamp has changed.
+Decider('MD5-timestamp')
+
+# Since we set the -j value by default, suppress SCons warnings about being
+# unable to support parallel build on versions of Python with no threading.
+default_warnings = ['no-no-parallel-support']
+SetOption('warn', default_warnings + GetOption('warn'))
+
+AddOption('--mode', nargs=1, dest='conf_list', default=[],
+          action='append', help='Configuration to build.')
+
+AddOption('--verbose', dest='verbose', default=False,
+          action='store_true', help='Verbose command-line output.')
+
+
+#
+sconscript_file_map = dict(
+    ssl = 'ssl.scons',
+)
+
+class LoadTarget:
+  '''
+  Class for deciding if a given target sconscript is to be included
+  based on a list of included target names, optionally prefixed with '-'
+  to exclude a target name.
+  '''
+  def __init__(self, load):
+    '''
+    Initialize a class with a list of names for possible loading.
+
+    Arguments:
+      load:  list of elements in the LOAD= specification
+    '''
+    self.included = set([c for c in load if not c.startswith('-')])
+    self.excluded = set([c[1:] for c in load if c.startswith('-')])
+
+    if not self.included:
+      self.included = set(['all'])
+
+  def __call__(self, target):
+    '''
+    Returns True if the specified target's sconscript file should be
+    loaded, based on the initialized included and excluded lists.
+    '''
+    return (target in self.included or
+            ('all' in self.included and not target in self.excluded))
+
+if 'LOAD' in ARGUMENTS:
+  load = ARGUMENTS['LOAD'].split(',')
+else:
+  load = []
+load_target = LoadTarget(load)
+
+sconscript_files = []
+for target, sconscript in sconscript_file_map.iteritems():
+  if load_target(target):
+    sconscript_files.append(sconscript)
+
+
+target_alias_list= []
+
+conf_list = GetOption('conf_list')
+if conf_list:
+    # In case the same --mode= value was specified multiple times.
+    conf_list = list(set(conf_list))
+else:
+    conf_list = ['Debug']
+
+sconsbuild_dir = Dir('../../../sconsbuild')
+
+
+def FilterOut(self, **kw):
+  kw = SCons.Environment.copy_non_reserved_keywords(kw)
+  for key, val in kw.items():
+    envval = self.get(key, None)
+    if envval is None:
+      # No existing variable in the environment, so nothing to delete.
+      continue
+
+    for vremove in val:
+      # Use while not if, so we can handle duplicates.
+      while vremove in envval:
+        envval.remove(vremove)
+
+    self[key] = envval
+
+    # TODO(sgk): SCons.Environment.Append() has much more logic to deal
+    # with various types of values.  We should handle all those cases in here
+    # too.  (If variable is a dict, etc.)
+
+
+non_compilable_suffixes = {
+    'LINUX' : set([
+        '.bdic',
+        '.css',
+        '.dat',
+        '.fragment',
+        '.gperf',
+        '.h',
+        '.hh',
+        '.hpp',
+        '.html',
+        '.hxx',
+        '.idl',
+        '.in',
+        '.in0',
+        '.in1',
+        '.js',
+        '.mk',
+        '.rc',
+        '.sigs',
+        '',
+    ]),
+    'WINDOWS' : set([
+        '.h',
+        '.hh',
+        '.hpp',
+        '.dat',
+        '.idl',
+        '.in',
+        '.in0',
+        '.in1',
+    ]),
+}
+
+def compilable(env, file):
+  base, ext = os.path.splitext(str(file))
+  if ext in non_compilable_suffixes[env['TARGET_PLATFORM']]:
+    return False
+  return True
+
+def compilable_files(env, sources):
+  return [x for x in sources if compilable(env, x)]
+
+def GypProgram(env, target, source, *args, **kw):
+  source = compilable_files(env, source)
+  result = env.Program(target, source, *args, **kw)
+  if env.get('INCREMENTAL'):
+    env.Precious(result)
+  return result
+
+def GypTestProgram(env, target, source, *args, **kw):
+  source = compilable_files(env, source)
+  result = env.Program(target, source, *args, **kw)
+  if env.get('INCREMENTAL'):
+    env.Precious(*result)
+  return result
+
+def GypLibrary(env, target, source, *args, **kw):
+  source = compilable_files(env, source)
+  result = env.Library(target, source, *args, **kw)
+  return result
+
+def GypLoadableModule(env, target, source, *args, **kw):
+  source = compilable_files(env, source)
+  result = env.LoadableModule(target, source, *args, **kw)
+  return result
+
+def GypStaticLibrary(env, target, source, *args, **kw):
+  source = compilable_files(env, source)
+  result = env.StaticLibrary(target, source, *args, **kw)
+  return result
+
+def GypSharedLibrary(env, target, source, *args, **kw):
+  source = compilable_files(env, source)
+  result = env.SharedLibrary(target, source, *args, **kw)
+  if env.get('INCREMENTAL'):
+    env.Precious(result)
+  return result
+
+def add_gyp_methods(env):
+  env.AddMethod(GypProgram)
+  env.AddMethod(GypTestProgram)
+  env.AddMethod(GypLibrary)
+  env.AddMethod(GypLoadableModule)
+  env.AddMethod(GypStaticLibrary)
+  env.AddMethod(GypSharedLibrary)
+
+  env.AddMethod(FilterOut)
+
+  env.AddMethod(compilable)
+
+
+base_env = Environment(
+    tools = ['ar', 'as', 'gcc', 'g++', 'gnulink', 'chromium_builders'],
+    INTERMEDIATE_DIR='$OBJ_DIR/${COMPONENT_NAME}/_${TARGET_NAME}_intermediate',
+    LIB_DIR='$TOP_BUILDDIR/lib',
+    OBJ_DIR='$TOP_BUILDDIR/obj',
+    SCONSBUILD_DIR=sconsbuild_dir.abspath,
+    SHARED_INTERMEDIATE_DIR='$OBJ_DIR/_global_intermediate',
+    SRC_DIR=Dir('../../..'),
+    TARGET_PLATFORM='LINUX',
+    TOP_BUILDDIR='$SCONSBUILD_DIR/$CONFIG_NAME',
+    LIBPATH=['$LIB_DIR'],
+)
+
+if not GetOption('verbose'):
+  base_env.SetDefault(
+      ARCOMSTR='Creating library $TARGET',
+      ASCOMSTR='Assembling $TARGET',
+      CCCOMSTR='Compiling $TARGET',
+      CONCATSOURCECOMSTR='ConcatSource $TARGET',
+      CXXCOMSTR='Compiling $TARGET',
+      LDMODULECOMSTR='Building loadable module $TARGET',
+      LINKCOMSTR='Linking $TARGET',
+      MANIFESTCOMSTR='Updating manifest for $TARGET',
+      MIDLCOMSTR='Compiling IDL $TARGET',
+      PCHCOMSTR='Precompiling $TARGET',
+      RANLIBCOMSTR='Indexing $TARGET',
+      RCCOMSTR='Compiling resource $TARGET',
+      SHCCCOMSTR='Compiling $TARGET',
+      SHCXXCOMSTR='Compiling $TARGET',
+      SHLINKCOMSTR='Linking $TARGET',
+      SHMANIFESTCOMSTR='Updating manifest for $TARGET',
+  )
+
+add_gyp_methods(base_env)
+
+for conf in conf_list:
+  env = base_env.Clone(CONFIG_NAME=conf)
+  SConsignFile(env.File('$TOP_BUILDDIR/.sconsign').abspath)
+  for sconscript in sconscript_files:
+    target_alias = env.SConscript(sconscript, exports=['env'])
+    if target_alias:
+      target_alias_list.extend(target_alias)
+
+Default(Alias('all', target_alias_list))
+
+help_fmt = '''
+Usage: hammer [SCONS_OPTIONS] [VARIABLES] [TARGET] ...
+
+Local command-line build options:
+  --mode=CONFIG             Configuration to build:
+                              --mode=Debug [default]
+                              --mode=Release
+  --verbose                 Print actual executed command lines.
+
+Supported command-line build variables:
+  LOAD=[module,...]         Comma-separated list of components to load in the
+                              dependency graph ('-' prefix excludes)
+  PROGRESS=type             Display a progress indicator:
+                              name:  print each evaluated target name
+                              spinner:  print a spinner every 5 targets
+
+The following TARGET names can also be used as LOAD= module names:
+
+%s
+'''
+
+if GetOption('help'):
+  def columnar_text(items, width=78, indent=2, sep=2):
+    result = []
+    colwidth = max(map(len, items)) + sep
+    cols = (width - indent) / colwidth
+    if cols < 1:
+      cols = 1
+    rows = (len(items) + cols - 1) / cols
+    indent = '%*s' % (indent, '')
+    sep = indent
+    for row in xrange(0, rows):
+      result.append(sep)
+      for i in xrange(row, len(items), rows):
+        result.append('%-*s' % (colwidth, items[i]))
+      sep = '\n' + indent
+    result.append('\n')
+    return ''.join(result)
+
+  load_list = set(sconscript_file_map.keys())
+  target_aliases = set(map(str, target_alias_list))
+
+  common = load_list and target_aliases
+  load_only = load_list - common
+  target_only = target_aliases - common
+  help_text = [help_fmt % columnar_text(sorted(list(common)))]
+  if target_only:
+    fmt = "The following are additional TARGET names:\n\n%s\n"
+    help_text.append(fmt % columnar_text(sorted(list(target_only))))
+  if load_only:
+    fmt = "The following are additional LOAD= module names:\n\n%s\n"
+    help_text.append(fmt % columnar_text(sorted(list(load_only))))
+  Help(''.join(help_text))
diff --git a/net/third_party/nss/patches/cachecerts.patch b/net/third_party/nss/patches/cachecerts.patch
new file mode 100644
index 0000000..c91ad60
--- /dev/null
+++ b/net/third_party/nss/patches/cachecerts.patch
@@ -0,0 +1,124 @@
+diff --git a/mozilla/security/nss/lib/ssl/ssl3con.c b/mozilla/security/nss/lib/ssl/ssl3con.c
+index 45bf853..e3f9a9a 100644
+--- a/mozilla/security/nss/lib/ssl/ssl3con.c
++++ b/mozilla/security/nss/lib/ssl/ssl3con.c
+@@ -72,6 +72,7 @@
+ #endif
+ 
+ static void      ssl3_CleanupPeerCerts(sslSocket *ss);
++static void      ssl3_CopyPeerCertsFromSID(sslSocket *ss, sslSessionID *sid);
+ static PK11SymKey *ssl3_GenerateRSAPMS(sslSocket *ss, ssl3CipherSpec *spec,
+                                        PK11SlotInfo * serverKeySlot);
+ static SECStatus ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms);
+@@ -5136,6 +5137,7 @@ ssl3_HandleServerHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+ 	/* copy the peer cert from the SID */
+ 	if (sid->peerCert != NULL) {
+ 	    ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
++	    ssl3_CopyPeerCertsFromSID(ss, sid);
+ 	}
+ 
+ 
+@@ -6378,6 +6380,7 @@ compression_found:
+ 	ss->sec.ci.sid = sid;
+ 	if (sid->peerCert != NULL) {
+ 	    ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
++	    ssl3_CopyPeerCertsFromSID(ss, sid);
+ 	}
+ 
+ 	/*
+@@ -7746,6 +7749,38 @@ ssl3_CleanupPeerCerts(sslSocket *ss)
+     ss->ssl3.peerCertChain = NULL;
+ }
+ 
++static void
++ssl3_CopyPeerCertsFromSID(sslSocket *ss, sslSessionID *sid)
++{
++    PRArenaPool *arena;
++    ssl3CertNode *certs = NULL;
++    int i;
++
++    if (!sid->peerCertChain[0])
++	return;
++    PORT_Assert(!ss->ssl3.peerCertArena);
++    PORT_Assert(!ss->ssl3.peerCertChain);
++    ss->ssl3.peerCertArena = arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
++    for (i = 0; i < MAX_PEER_CERT_CHAIN_SIZE && sid->peerCertChain[i]; i++) {
++	ssl3CertNode *c = PORT_ArenaNew(arena, ssl3CertNode);
++	c->cert = CERT_DupCertificate(sid->peerCertChain[i]);
++	c->next = certs;
++	certs = c;
++    }
++    ss->ssl3.peerCertChain = certs;
++}
++
++static void
++ssl3_CopyPeerCertsToSID(ssl3CertNode *certs, sslSessionID *sid)
++{
++    int i = 0;
++    ssl3CertNode *c = certs;
++    for (; i < MAX_PEER_CERT_CHAIN_SIZE && c; i++, c = c->next) {
++	PORT_Assert(!sid->peerCertChain[i]);
++	sid->peerCertChain[i] = CERT_DupCertificate(c->cert);
++    }
++}
++
+ /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
+  * ssl3 Certificate message.
+  * Caller must hold Handshake and RecvBuf locks.
+@@ -7932,6 +7967,7 @@ ssl3_HandleCertificate(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
+     }
+ 
+     ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
++    ssl3_CopyPeerCertsToSID(certs, ss->sec.ci.sid);
+ 
+     if (!ss->sec.isServer) {
+ 	/* set the server authentication and key exchange types and sizes
+@@ -8103,6 +8139,8 @@ ssl3_RestartHandshakeAfterServerCert(sslSocket *ss)
+     if (ss->handshake != NULL) {
+ 	ss->handshake = ssl_GatherRecord1stHandshake;
+ 	ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
++	ssl3_CopyPeerCertsToSID((ssl3CertNode *)ss->ssl3.peerCertChain,
++				ss->sec.ci.sid);
+ 
+ 	ssl_GetRecvBufLock(ss);
+ 	if (ss->ssl3.hs.msgState.buf != NULL) {
+diff --git a/mozilla/security/nss/lib/ssl/sslimpl.h b/mozilla/security/nss/lib/ssl/sslimpl.h
+index a800d56..fe7ac7a 100644
+--- a/mozilla/security/nss/lib/ssl/sslimpl.h
++++ b/mozilla/security/nss/lib/ssl/sslimpl.h
+@@ -569,10 +569,13 @@ typedef enum {	never_cached,
+ 		invalid_cache		/* no longer in any cache. */
+ } Cached;
+ 
++#define MAX_PEER_CERT_CHAIN_SIZE 8
++
+ struct sslSessionIDStr {
+     sslSessionID *        next;   /* chain used for client sockets, only */
+ 
+     CERTCertificate *     peerCert;
++    CERTCertificate *     peerCertChain[MAX_PEER_CERT_CHAIN_SIZE];
+     const char *          peerID;     /* client only */
+     const char *          urlSvrName; /* client only */
+     CERTCertificate *     localCert;
+diff --git a/mozilla/security/nss/lib/ssl/sslnonce.c b/mozilla/security/nss/lib/ssl/sslnonce.c
+index 63dc5a2..64adc1f 100644
+--- a/mozilla/security/nss/lib/ssl/sslnonce.c
++++ b/mozilla/security/nss/lib/ssl/sslnonce.c
+@@ -197,6 +197,7 @@ lock_cache(void)
+ static void
+ ssl_DestroySID(sslSessionID *sid)
+ {
++    int i;
+     SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached));
+     PORT_Assert((sid->references == 0));
+ 
+@@ -216,6 +217,9 @@ ssl_DestroySID(sslSessionID *sid)
+     if ( sid->peerCert ) {
+ 	CERT_DestroyCertificate(sid->peerCert);
+     }
++    for (i = 0; i < MAX_PEER_CERT_CHAIN_SIZE && sid->peerCertChain[i]; i++) {
++	CERT_DestroyCertificate(sid->peerCertChain[i]);
++    }
+     if ( sid->localCert ) {
+ 	CERT_DestroyCertificate(sid->localCert);
+     }
diff --git a/net/third_party/nss/patches/falsestart.patch b/net/third_party/nss/patches/falsestart.patch
new file mode 100644
index 0000000..6a71159
--- /dev/null
+++ b/net/third_party/nss/patches/falsestart.patch
@@ -0,0 +1,355 @@
+Index: mozilla/security/nss/cmd/strsclnt/strsclnt.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/cmd/strsclnt/strsclnt.c,v
+retrieving revision 1.66
+diff -u -p -r1.66 strsclnt.c
+--- mozilla/security/nss/cmd/strsclnt/strsclnt.c	10 Feb 2010 18:07:20 -0000	1.66
++++ mozilla/security/nss/cmd/strsclnt/strsclnt.c	16 Mar 2010 01:25:41 -0000
+@@ -162,6 +162,7 @@ static PRBool disableLocking  = PR_FALSE
+ static PRBool ignoreErrors    = PR_FALSE;
+ static PRBool enableSessionTickets = PR_FALSE;
+ static PRBool enableCompression    = PR_FALSE;
++static PRBool enableFalseStart     = PR_FALSE;
+ 
+ PRIntervalTime maxInterval    = PR_INTERVAL_NO_TIMEOUT;
+ 
+@@ -197,7 +198,8 @@ Usage(const char *progName)
+         "       -U means enable throttling up threads\n"
+ 	"       -B bypasses the PKCS11 layer for SSL encryption and MACing\n"
+ 	"       -u enable TLS Session Ticket extension\n"
+-	"       -z enable compression\n",
++	"       -z enable compression\n"
++	"       -g enable false start\n",
+ 	progName);
+     exit(1);
+ }
+@@ -1244,6 +1246,12 @@ client_main(
+ 	    errExit("SSL_OptionSet SSL_ENABLE_DEFLATE");
+     }
+ 
++    if (enableFalseStart) {
++	rv = SSL_OptionSet(model_sock, SSL_ENABLE_FALSE_START, PR_TRUE);
++	if (rv != SECSuccess)
++	    errExit("SSL_OptionSet SSL_ENABLE_FALSE_START");
++    }
++
+     SSL_SetURL(model_sock, hostName);
+ 
+     SSL_AuthCertificateHook(model_sock, mySSLAuthCertificate, 
+@@ -1354,7 +1362,7 @@ main(int argc, char **argv)
+  
+ 
+     optstate = PL_CreateOptState(argc, argv,
+-                                 "23BC:DNP:TUW:a:c:d:f:in:op:qst:uvw:z");
++                                 "23BC:DNP:TUW:a:c:d:f:gin:op:qst:uvw:z");
+     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
+ 	switch(optstate->option) {
+ 
+@@ -1384,6 +1392,8 @@ main(int argc, char **argv)
+ 
+ 	case 'f': fileName = optstate->value; break;
+ 
++	case 'g': enableFalseStart = PR_TRUE; break;
++
+ 	case 'i': ignoreErrors = PR_TRUE; break;
+ 
+         case 'n': nickName = PL_strdup(optstate->value); break;
+Index: mozilla/security/nss/cmd/tstclnt/tstclnt.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/cmd/tstclnt/tstclnt.c,v
+retrieving revision 1.62
+diff -u -p -r1.62 tstclnt.c
+--- mozilla/security/nss/cmd/tstclnt/tstclnt.c	10 Feb 2010 18:07:21 -0000	1.62
++++ mozilla/security/nss/cmd/tstclnt/tstclnt.c	16 Mar 2010 01:25:41 -0000
+@@ -225,6 +225,7 @@ static void Usage(const char *progName)
+     fprintf(stderr, "%-20s Renegotiate N times (resuming session if N>1).\n", "-r N");
+     fprintf(stderr, "%-20s Enable the session ticket extension.\n", "-u");
+     fprintf(stderr, "%-20s Enable compression.\n", "-z");
++    fprintf(stderr, "%-20s Enable false start.\n", "-g");
+     fprintf(stderr, "%-20s Letter(s) chosen from the following list\n", 
+                     "-c ciphers");
+     fprintf(stderr, 
+@@ -521,6 +522,7 @@ int main(int argc, char **argv)
+     int                useExportPolicy = 0;
+     int                enableSessionTickets = 0;
+     int                enableCompression = 0;
++    int                enableFalseStart = 0;
+     PRSocketOptionData opt;
+     PRNetAddr          addr;
+     PRPollDesc         pollset[2];
+@@ -551,7 +553,7 @@ int main(int argc, char **argv)
+     }
+ 
+     optstate = PL_CreateOptState(argc, argv,
+-                                 "23BSTW:a:c:d:fh:m:n:op:qr:suvw:xz");
++                                 "23BSTW:a:c:d:fgh:m:n:op:qr:suvw:xz");
+     while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
+ 	switch (optstate->option) {
+ 	  case '?':
+@@ -578,6 +580,8 @@ int main(int argc, char **argv)
+ 
+           case 'c': cipherString = PORT_Strdup(optstate->value); break;
+ 
++          case 'g': enableFalseStart = 1; 		break;
++
+           case 'd': certDir = PORT_Strdup(optstate->value);   break;
+ 
+           case 'f': clientSpeaksFirst = PR_TRUE;        break;
+@@ -863,7 +867,14 @@ int main(int argc, char **argv)
+ 	SECU_PrintError(progName, "error enabling compression");
+ 	return 1;
+     }
+-               
++
++    /* enable false start. */
++    rv = SSL_OptionSet(s, SSL_ENABLE_FALSE_START, enableFalseStart);
++    if (rv != SECSuccess) {
++	SECU_PrintError(progName, "error enabling false start");
++	return 1;
++    }
++
+     SSL_SetPKCS11PinArg(s, &pwdata);
+ 
+     SSL_AuthCertificateHook(s, SSL_AuthCertificate, (void *)handle);
+Index: mozilla/security/nss/lib/ssl/ssl.h
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl.h,v
+retrieving revision 1.38
+diff -u -p -r1.38 ssl.h
+--- mozilla/security/nss/lib/ssl/ssl.h	17 Feb 2010 02:29:07 -0000	1.38
++++ mozilla/security/nss/lib/ssl/ssl.h	16 Mar 2010 01:25:41 -0000
+@@ -128,6 +128,17 @@ SSL_IMPORT PRFileDesc *SSL_ImportFD(PRFi
+                                           /* Renegotiation  Info (RI)       */
+ 					  /* extension in ALL handshakes.   */
+                                           /* default: off                   */
++#define SSL_ENABLE_FALSE_START	       22 /* Enable SSL false start (off by */
++					  /* default, applies only to       */
++					  /* clients). False start is a     */
++/* mode where an SSL client will start sending application data before      */
++/* verifying the server's Finished message. This means that we could end up */
++/* sending data to an imposter. However, the data will be encrypted and     */
++/* only the true server can derive the session key. Thus, so long as the    */
++/* cipher isn't broken this is safe. Because of this, False Start will only */
++/* occur on RSA or DH ciphersuites where the cipher's key length is >= 80   */
++/* bits. The advantage of False Start is that it saves a round trip for     */
++/* client-speaks-first protocols when performing a full handshake.          */
+ 
+ #ifdef SSL_DEPRECATED_FUNCTION 
+ /* Old deprecated function names */
+Index: mozilla/security/nss/lib/ssl/ssl3con.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl3con.c,v
+retrieving revision 1.136
+diff -u -p -r1.136 ssl3con.c
+--- mozilla/security/nss/lib/ssl/ssl3con.c	17 Feb 2010 02:29:07 -0000	1.136
++++ mozilla/security/nss/lib/ssl/ssl3con.c	16 Mar 2010 01:25:41 -0000
+@@ -5656,7 +5656,17 @@ ssl3_RestartHandshakeAfterCertReq(sslSoc
+     return rv;
+ }
+ 
+-
++PRBool
++ssl3_CanFalseStart(sslSocket *ss) {
++    return ss->opt.enableFalseStart &&
++	   !ss->sec.isServer &&
++	   !ss->ssl3.hs.isResuming &&
++	   ss->ssl3.cwSpec &&
++	   ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
++	   (ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_rsa ||
++	    ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_dh  ||
++	    ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_ecdh);
++}
+ 
+ /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
+  * ssl3 Server Hello Done message.
+@@ -5728,6 +5738,12 @@ ssl3_HandleServerHelloDone(sslSocket *ss
+ 	ss->ssl3.hs.ws = wait_new_session_ticket;
+     else
+ 	ss->ssl3.hs.ws = wait_change_cipher;
++
++    /* Do the handshake callback for sslv3 here. */
++    if (ss->handshakeCallback != NULL && ssl3_CanFalseStart(ss)) {
++	(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
++    }
++
+     return SECSuccess;
+ 
+ loser:
+@@ -8468,7 +8484,7 @@ xmit_loser:
+     ss->ssl3.hs.ws = idle_handshake;
+ 
+     /* Do the handshake callback for sslv3 here. */
+-    if (ss->handshakeCallback != NULL) {
++    if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) {
+ 	(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+     }
+ 
+Index: mozilla/security/nss/lib/ssl/ssl3gthr.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl3gthr.c,v
+retrieving revision 1.9
+diff -u -p -r1.9 ssl3gthr.c
+--- mozilla/security/nss/lib/ssl/ssl3gthr.c	20 Nov 2008 07:37:25 -0000	1.9
++++ mozilla/security/nss/lib/ssl/ssl3gthr.c	16 Mar 2010 01:25:41 -0000
+@@ -188,6 +188,7 @@ ssl3_GatherCompleteHandshake(sslSocket *
+ {
+     SSL3Ciphertext cText;
+     int            rv;
++    PRBool         canFalseStart = PR_FALSE;
+ 
+     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
+     do {
+@@ -207,7 +208,20 @@ ssl3_GatherCompleteHandshake(sslSocket *
+ 	if (rv < 0) {
+ 	    return ss->recvdCloseNotify ? 0 : rv;
+ 	}
+-    } while (ss->ssl3.hs.ws != idle_handshake && ss->gs.buf.len == 0);
++
++	/* If we kicked off a false start in ssl3_HandleServerHelloDone, break
++	 * out of this loop early without finishing the handshake.
++	 */
++	if (ss->opt.enableFalseStart) {
++	    ssl_GetSSL3HandshakeLock(ss);
++	    canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher ||
++			     ss->ssl3.hs.ws == wait_new_session_ticket) &&
++		            ssl3_CanFalseStart(ss);
++	    ssl_ReleaseSSL3HandshakeLock(ss);
++	}
++    } while (ss->ssl3.hs.ws != idle_handshake &&
++             !canFalseStart &&
++             ss->gs.buf.len == 0);
+ 
+     ss->gs.readOffset = 0;
+     ss->gs.writeOffset = ss->gs.buf.len;
+Index: mozilla/security/nss/lib/ssl/sslimpl.h
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/sslimpl.h,v
+retrieving revision 1.77
+diff -u -p -r1.77 sslimpl.h
+--- mozilla/security/nss/lib/ssl/sslimpl.h	10 Feb 2010 00:33:50 -0000	1.77
++++ mozilla/security/nss/lib/ssl/sslimpl.h	16 Mar 2010 01:25:41 -0000
+@@ -333,6 +333,7 @@ typedef struct sslOptionsStr {
+     unsigned int enableDeflate          : 1;  /* 19 */
+     unsigned int enableRenegotiation    : 2;  /* 20-21 */
+     unsigned int requireSafeNegotiation : 1;  /* 22 */
++    unsigned int enableFalseStart       : 1;  /* 23 */
+ } sslOptions;
+ 
+ typedef enum { sslHandshakingUndetermined = 0,
+@@ -1250,6 +1251,8 @@ extern void      ssl_SetAlwaysBlock(sslS
+ 
+ extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled);
+ 
++extern PRBool    ssl3_CanFalseStart(sslSocket *ss);
++
+ #define SSL_LOCK_READER(ss)		if (ss->recvLock) PZ_Lock(ss->recvLock)
+ #define SSL_UNLOCK_READER(ss)		if (ss->recvLock) PZ_Unlock(ss->recvLock)
+ #define SSL_LOCK_WRITER(ss)		if (ss->sendLock) PZ_Lock(ss->sendLock)
+Index: mozilla/security/nss/lib/ssl/sslsecur.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/sslsecur.c,v
+retrieving revision 1.43
+diff -u -p -r1.43 sslsecur.c
+--- mozilla/security/nss/lib/ssl/sslsecur.c	14 Jan 2010 22:15:25 -0000	1.43
++++ mozilla/security/nss/lib/ssl/sslsecur.c	16 Mar 2010 01:25:41 -0000
+@@ -1199,8 +1199,17 @@ ssl_SecureSend(sslSocket *ss, const unsi
+     	ss->writerThread = PR_GetCurrentThread();
+     /* If any of these is non-zero, the initial handshake is not done. */
+     if (!ss->firstHsDone) {
++	PRBool canFalseStart = PR_FALSE;
+ 	ssl_Get1stHandshakeLock(ss);
+-	if (ss->handshake || ss->nextHandshake || ss->securityHandshake) {
++	if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
++	    (ss->ssl3.hs.ws == wait_change_cipher ||
++	     ss->ssl3.hs.ws == wait_finished ||
++	     ss->ssl3.hs.ws == wait_new_session_ticket) &&
++	    ssl3_CanFalseStart(ss)) {
++	    canFalseStart = PR_TRUE;
++	}
++	if (!canFalseStart &&
++	    (ss->handshake || ss->nextHandshake || ss->securityHandshake)) {
+ 	    rv = ssl_Do1stHandshake(ss);
+ 	}
+ 	ssl_Release1stHandshakeLock(ss);
+Index: mozilla/security/nss/lib/ssl/sslsock.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/sslsock.c,v
+retrieving revision 1.66
+diff -u -p -r1.66 sslsock.c
+--- mozilla/security/nss/lib/ssl/sslsock.c	26 Feb 2010 20:44:54 -0000	1.66
++++ mozilla/security/nss/lib/ssl/sslsock.c	16 Mar 2010 01:25:41 -0000
+@@ -183,6 +183,7 @@ static sslOptions ssl_defaults = {
+     PR_FALSE,   /* enableDeflate      */
+     2,          /* enableRenegotiation (default: requires extension) */
+     PR_FALSE,   /* requireSafeNegotiation */
++    PR_FALSE,   /* enableFalseStart   */
+ };
+ 
+ sslSessionIDLookupFunc  ssl_sid_lookup;
+@@ -728,6 +729,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh
+ 	ss->opt.requireSafeNegotiation = on;
+ 	break;
+ 
++      case SSL_ENABLE_FALSE_START:
++	ss->opt.enableFalseStart = on;
++	break;
++
+       default:
+ 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ 	rv = SECFailure;
+@@ -791,6 +796,7 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 wh
+                                   on = ss->opt.enableRenegotiation; break;
+     case SSL_REQUIRE_SAFE_NEGOTIATION: 
+                                   on = ss->opt.requireSafeNegotiation; break;
++    case SSL_ENABLE_FALSE_START:  on = ss->opt.enableFalseStart;   break;
+ 
+     default:
+ 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+@@ -841,6 +847,7 @@ SSL_OptionGetDefault(PRInt32 which, PRBo
+     case SSL_REQUIRE_SAFE_NEGOTIATION: 
+                                   on = ssl_defaults.requireSafeNegotiation; 
+ 				  break;
++    case SSL_ENABLE_FALSE_START:  on = ssl_defaults.enableFalseStart;   break;
+ 
+     default:
+ 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+@@ -984,6 +991,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBo
+ 	ssl_defaults.requireSafeNegotiation = on;
+ 	break;
+ 
++      case SSL_ENABLE_FALSE_START:
++	ssl_defaults.enableFalseStart = on;
++	break;
++
+       default:
+ 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ 	return SECFailure;
+Index: mozilla/security/nss/tests/ssl/sslstress.txt
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/tests/ssl/sslstress.txt,v
+retrieving revision 1.18
+diff -u -p -r1.18 sslstress.txt
+--- mozilla/security/nss/tests/ssl/sslstress.txt	3 Feb 2010 02:25:36 -0000	1.18
++++ mozilla/security/nss/tests/ssl/sslstress.txt	16 Mar 2010 01:25:41 -0000
+@@ -42,9 +42,11 @@
+   noECC     0      _         -c_1000_-C_A                  Stress SSL2 RC4 128 with MD5
+   noECC     0      _         -c_1000_-C_c_-T               Stress SSL3 RC4 128 with MD5
+   noECC     0      _         -c_1000_-C_c                  Stress TLS  RC4 128 with MD5
++  noECC     0      _         -c_1000_-C_c_-h               Stress TLS  RC4 128 with MD5 (false start)
+   noECC     0      -u        -2_-c_1000_-C_c_-u            Stress TLS  RC4 128 with MD5 (session ticket)
+   noECC     0      -z        -2_-c_1000_-C_c_-z            Stress TLS  RC4 128 with MD5 (compression)
+   noECC     0      -u_-z     -2_-c_1000_-C_c_-u_-z         Stress TLS  RC4 128 with MD5 (session ticket, compression)
++  noECC     0      -u_-z     -2_-c_1000_-C_c_-u_-z_-h      Stress TLS  RC4 128 with MD5 (session ticket, compression, false start)
+   SNI       0      -u_-a_Host-sni.Dom -2_-3_-c_1000_-C_c_-u Stress TLS RC4 128 with MD5 (session ticket, SNI)
+ 
+ #
+@@ -55,7 +57,9 @@
+   noECC     0      -r_-r     -c_100_-C_c_-N_-n_TestUser    Stress TLS RC4 128 with MD5 (no reuse, client auth)
+   noECC     0      -r_-r_-u  -2_-c_100_-C_c_-n_TestUser_-u Stress TLS RC4 128 with MD5 (session ticket, client auth)
+   noECC     0      -r_-r_-z  -2_-c_100_-C_c_-n_TestUser_-z Stress TLS RC4 128 with MD5 (compression, client auth)
++  noECC     0      -r_-r_-z  -2_-c_100_-C_c_-n_TestUser_-z_-h Stress TLS RC4 128 with MD5 (compression, client auth, false start)
+   noECC     0   -r_-r_-u_-z  -2_-c_100_-C_c_-n_TestUser_-u_-z Stress TLS RC4 128 with MD5 (session ticket, compression, client auth)
++  noECC     0   -r_-r_-u_-z  -2_-c_100_-C_c_-n_TestUser_-u_-z_-h Stress TLS RC4 128 with MD5 (session ticket, compression, client auth, false start)
+   SNI       0   -r_-r_-u_-a_Host-sni.Dom -2_-3_-c_1000_-C_c_-u Stress TLS RC4 128 with MD5 (session ticket, SNI, client auth, default virt host)
+   SNI       0   -r_-r_-u_-a_Host-sni.Dom_-k_Host-sni.Dom -2_-3_-c_1000_-C_c_-u_-a_Host-sni.Dom Stress TLS RC4 128 with MD5 (session ticket, SNI, client auth, change virt host)
+ 
diff --git a/net/third_party/nss/patches/nextproto.patch b/net/third_party/nss/patches/nextproto.patch
new file mode 100644
index 0000000..837295e
--- /dev/null
+++ b/net/third_party/nss/patches/nextproto.patch
@@ -0,0 +1,498 @@
+diff --git a/mozilla/security/nss/cmd/tstclnt/tstclnt.c b/mozilla/security/nss/cmd/tstclnt/tstclnt.c
+index c15a0ad..b6210bf 100644
+--- a/mozilla/security/nss/cmd/tstclnt/tstclnt.c
++++ b/mozilla/security/nss/cmd/tstclnt/tstclnt.c
+@@ -863,7 +863,13 @@ int main(int argc, char **argv)
+ 	SECU_PrintError(progName, "error enabling compression");
+ 	return 1;
+     }
+-               
++
++    rv = SSL_SetNextProtoNego(s, "\004flip\004http1.1", 10);
++    if (rv != SECSuccess) {
++	SECU_PrintError(progName, "error enabling next protocol negotiation");
++	return 1;
++    }
++
+     SSL_SetPKCS11PinArg(s, &pwdata);
+ 
+     SSL_AuthCertificateHook(s, SSL_AuthCertificate, (void *)handle);
+diff --git a/mozilla/security/nss/lib/ssl/ssl.def b/mozilla/security/nss/lib/ssl/ssl.def
+index d3f455c..a1f4b51 100644
+--- a/mozilla/security/nss/lib/ssl/ssl.def
++++ b/mozilla/security/nss/lib/ssl/ssl.def
+@@ -152,3 +152,10 @@ SSL_SNISocketConfigHook;
+ ;+    local:
+ ;+*;
+ ;+};
++;+NSS_CHROMIUM {
++;+    global:
++SSL_GetNextProto;
++SSL_SetNextProtoNego;
++;+    local:
++;+*;
++;+};
+diff --git a/mozilla/security/nss/lib/ssl/ssl.h b/mozilla/security/nss/lib/ssl/ssl.h
+index d60a73c..00c250b 100644
+--- a/mozilla/security/nss/lib/ssl/ssl.h
++++ b/mozilla/security/nss/lib/ssl/ssl.h
+@@ -142,6 +142,18 @@ SSL_IMPORT SECStatus SSL_OptionSetDefault(PRInt32 option, PRBool on);
+ SSL_IMPORT SECStatus SSL_OptionGetDefault(PRInt32 option, PRBool *on);
+ SSL_IMPORT SECStatus SSL_CertDBHandleSet(PRFileDesc *fd, CERTCertDBHandle *dbHandle);
+ 
++SSL_IMPORT SECStatus SSL_SetNextProtoNego(PRFileDesc *fd,
++					  const unsigned char *data,
++					  unsigned short length);
++SSL_IMPORT SECStatus SSL_GetNextProto(PRFileDesc *fd,
++				      int *state,
++				      unsigned char *buf,
++				      unsigned *length,
++				      unsigned buf_len);
++#define SSL_NEXT_PROTO_NO_SUPPORT	0 /* No peer support                */
++#define SSL_NEXT_PROTO_NEGOTIATED	1 /* Mutual agreement               */
++#define SSL_NEXT_PROTO_NO_OVERLAP	2 /* No protocol overlap found      */
++
+ /*
+ ** Control ciphers that SSL uses. If on is non-zero then the named cipher
+ ** is enabled, otherwise it is disabled. 
+diff --git a/mozilla/security/nss/lib/ssl/ssl3con.c b/mozilla/security/nss/lib/ssl/ssl3con.c
+index 083248d..5c14672 100644
+--- a/mozilla/security/nss/lib/ssl/ssl3con.c
++++ b/mozilla/security/nss/lib/ssl/ssl3con.c
+@@ -81,6 +81,7 @@ static SECStatus ssl3_InitState(             sslSocket *ss);
+ static SECStatus ssl3_SendCertificate(       sslSocket *ss);
+ static SECStatus ssl3_SendEmptyCertificate(  sslSocket *ss);
+ static SECStatus ssl3_SendCertificateRequest(sslSocket *ss);
++static SECStatus ssl3_SendNextProto(         sslSocket *ss);
+ static SECStatus ssl3_SendFinished(          sslSocket *ss, PRInt32 flags);
+ static SECStatus ssl3_SendServerHello(       sslSocket *ss);
+ static SECStatus ssl3_SendServerHelloDone(   sslSocket *ss);
+@@ -5717,6 +5718,12 @@ ssl3_HandleServerHelloDone(sslSocket *ss)
+     if (rv != SECSuccess) {
+ 	goto loser;	/* err code was set. */
+     }
++
++    rv = ssl3_SendNextProto(ss);
++    if (rv != SECSuccess) {
++	goto loser;	/* err code was set. */
++    }
++
+     rv = ssl3_SendFinished(ss, 0);
+     if (rv != SECSuccess) {
+ 	goto loser;	/* err code was set. */
+@@ -8138,6 +8145,40 @@ ssl3_ComputeTLSFinished(ssl3CipherSpec *spec,
+ }
+ 
+ /* called from ssl3_HandleServerHelloDone
++ */
++static SECStatus
++ssl3_SendNextProto(sslSocket *ss)
++{
++    SECStatus rv;
++    int padding_len;
++    static const unsigned char padding[32] = {0};
++
++    if (ss->ssl3.nextProtoState == SSL_NEXT_PROTO_NO_SUPPORT)
++	return SECSuccess;
++
++    PORT_Assert( ss->opt.noLocks || ssl_HaveXmitBufLock(ss));
++    PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
++
++    padding_len = 32 - ((ss->ssl3.nextProto.len + 2) % 32);
++
++    rv = ssl3_AppendHandshakeHeader(ss, next_proto, ss->ssl3.nextProto.len +
++						    2 + padding_len);
++    if (rv != SECSuccess) {
++	return rv;	/* error code set by AppendHandshakeHeader */
++    }
++    rv = ssl3_AppendHandshakeVariable(ss, ss->ssl3.nextProto.data,
++				      ss->ssl3.nextProto.len, 1);
++    if (rv != SECSuccess) {
++	return rv;	/* error code set by AppendHandshake */
++    }
++    rv = ssl3_AppendHandshakeVariable(ss, padding, padding_len, 1);
++    if (rv != SECSuccess) {
++	return rv;	/* error code set by AppendHandshake */
++    }
++    return rv;
++}
++
++/* called from ssl3_HandleServerHelloDone
+  *             ssl3_HandleClientHello
+  *             ssl3_HandleFinished
+  */
+@@ -8390,6 +8431,14 @@ ssl3_HandleFinished(sslSocket *ss, SSL3Opaque *b, PRUint32 length,
+ 	if (doStepUp || ss->writerThread == PR_GetCurrentThread()) {
+ 	    flags = ssl_SEND_FLAG_FORCE_INTO_BUFFER;
+ 	}
++
++	if (!isServer) {
++	    rv = ssl3_SendNextProto(ss);
++	    if (rv != SECSuccess) {
++		goto xmit_loser;	/* err code was set. */
++	    }
++	}
++
+ 	rv = ssl3_SendFinished(ss, flags);
+ 	if (rv != SECSuccess) {
+ 	    goto xmit_loser;	/* err is set. */
+@@ -9455,6 +9504,11 @@ ssl3_DestroySSL3Info(sslSocket *ss)
+     ssl3_DestroyCipherSpec(&ss->ssl3.specs[1], PR_TRUE/*freeSrvName*/);
+ 
+     ss->ssl3.initialized = PR_FALSE;
++
++    if (ss->ssl3.nextProto.data) {
++	PORT_Free(ss->ssl3.nextProto.data);
++	ss->ssl3.nextProto.data = NULL;
++    }
+ }
+ 
+ /* End of ssl3con.c */
+diff --git a/mozilla/security/nss/lib/ssl/ssl3ext.c b/mozilla/security/nss/lib/ssl/ssl3ext.c
+index ac2b067..04f45a4 100644
+--- a/mozilla/security/nss/lib/ssl/ssl3ext.c
++++ b/mozilla/security/nss/lib/ssl/ssl3ext.c
+@@ -235,6 +235,7 @@ static const ssl3HelloExtensionHandler clientHelloHandlers[] = {
+ #endif
+     { ssl_session_ticket_xtn,     &ssl3_ServerHandleSessionTicketXtn },
+     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
++    { ssl_next_proto_neg_xtn,     &ssl3_ServerHandleNextProtoNegoXtn },
+     { -1, NULL }
+ };
+ 
+@@ -245,6 +246,7 @@ static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = {
+     /* TODO: add a handler for ssl_ec_point_formats_xtn */
+     { ssl_session_ticket_xtn,     &ssl3_ClientHandleSessionTicketXtn },
+     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
++    { ssl_next_proto_neg_xtn,     &ssl3_ClientHandleNextProtoNegoXtn },
+     { -1, NULL }
+ };
+ 
+@@ -267,7 +269,8 @@ ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = {
+     { ssl_elliptic_curves_xtn,    &ssl3_SendSupportedCurvesXtn },
+     { ssl_ec_point_formats_xtn,   &ssl3_SendSupportedPointFormatsXtn },
+ #endif
+-    { ssl_session_ticket_xtn,     &ssl3_SendSessionTicketXtn }
++    { ssl_session_ticket_xtn,     &ssl3_SendSessionTicketXtn },
++    { ssl_next_proto_neg_xtn,     &ssl3_ClientSendNextProtoNegoXtn }
+     /* any extra entries will appear as { 0, NULL }    */
+ };
+ 
+@@ -532,6 +535,123 @@ ssl3_SendSessionTicketXtn(
+     return -1;
+ }
+ 
++/* handle an incoming Next Protocol Negotiation extension. */
++SECStatus
++ssl3_ServerHandleNextProtoNegoXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
++{
++    if (data->len != 0) {
++	/* Clients MUST send an empty NPN extension, if any. */
++	return SECFailure;
++    }
++
++    ss->ssl3.hs.nextProtoNego = PR_TRUE;
++    return SECSuccess;
++}
++
++/* ssl3_ValidateNextProtoNego checks that the given block of data is valid: none
++ * of the length may be 0 and the sum of the lengths must equal the length of
++ * the block. */
++SECStatus
++ssl3_ValidateNextProtoNego(const unsigned char* data, unsigned short length)
++{
++    unsigned int offset = 0;
++
++    while (offset < length) {
++	if (data[offset] == 0) {
++	    return SECFailure;
++	}
++	offset += (unsigned int)data[offset] + 1;
++    }
++
++    if (offset > length)
++	return SECFailure;
++
++    return SECSuccess;
++}
++
++SECStatus
++ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss, PRUint16 ex_type,
++                                 SECItem *data)
++{
++    unsigned int i, j;
++    SECStatus rv;
++    unsigned char *result;
++
++    if (data->len == 0) {
++	/* The server supports the extension, but doesn't have any
++	 * protocols configured. In this case we request our favoured
++	 * protocol. */
++	goto pick_first;
++    }
++
++    rv = ssl3_ValidateNextProtoNego(data->data, data->len);
++    if (rv != SECSuccess)
++	return rv;
++
++    /* For each protocol in server preference order, see if we support it. */
++    for (i = 0; i < data->len; ) {
++	for (j = 0; j < ss->opt.nextProtoNego.len; ) {
++	    if (data->data[i] == ss->opt.nextProtoNego.data[j] &&
++		memcmp(&data->data[i+1], &ss->opt.nextProtoNego.data[j+1],
++		       data->data[i]) == 0) {
++		/* We found a match */
++		ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NEGOTIATED;
++		result = &data->data[i];
++		goto found;
++	    }
++	    j += (unsigned int)ss->opt.nextProtoNego.data[j] + 1;
++	}
++
++	i += (unsigned int)data->data[i] + 1;
++    }
++
++  pick_first:
++    ss->ssl3.nextProtoState = SSL_NEXT_PROTO_NO_OVERLAP;
++    result = ss->opt.nextProtoNego.data;
++
++  found:
++    if (ss->ssl3.nextProto.data)
++	PORT_Free(ss->ssl3.nextProto.data);
++    ss->ssl3.nextProto.data = PORT_Alloc(result[0]);
++    PORT_Memcpy(ss->ssl3.nextProto.data, result + 1, result[0]);
++    ss->ssl3.nextProto.len = result[0];
++    return SECSuccess;
++}
++
++PRInt32
++ssl3_ClientSendNextProtoNegoXtn(sslSocket * ss,
++			       PRBool      append,
++			       PRUint32    maxBytes)
++{
++    PRInt32 extension_length;
++
++    /* Renegotiations do not send this extension. */
++    if (ss->opt.nextProtoNego.len == 0 || ss->firstHsDone) {
++	return 0;
++    }
++
++    extension_length = 4;
++
++    if (append && maxBytes >= extension_length) {
++	SECStatus rv;
++	rv = ssl3_AppendHandshakeNumber(ss, ssl_next_proto_neg_xtn, 2);
++	if (rv != SECSuccess)
++	    goto loser;
++	rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
++	if (rv != SECSuccess)
++	    goto loser;
++	ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
++		ssl_next_proto_neg_xtn;
++    } else if (maxBytes < extension_length) {
++	return 0;
++    }
++
++    return extension_length;
++
++ loser:
++    return -1;
++}
++
+ /*
+  * NewSessionTicket
+  * Called from ssl3_HandleFinished
+diff --git a/mozilla/security/nss/lib/ssl/ssl3prot.h b/mozilla/security/nss/lib/ssl/ssl3prot.h
+index 0fc1675..c82c891 100644
+--- a/mozilla/security/nss/lib/ssl/ssl3prot.h
++++ b/mozilla/security/nss/lib/ssl/ssl3prot.h
+@@ -157,7 +157,8 @@ typedef enum {
+     server_hello_done	= 14,
+     certificate_verify	= 15, 
+     client_key_exchange	= 16, 
+-    finished		= 20
++    finished		= 20,
++    next_proto		= 67
+ } SSL3HandshakeType;
+ 
+ typedef struct {
+diff --git a/mozilla/security/nss/lib/ssl/sslimpl.h b/mozilla/security/nss/lib/ssl/sslimpl.h
+index 7581b98..0658d2c 100644
+--- a/mozilla/security/nss/lib/ssl/sslimpl.h
++++ b/mozilla/security/nss/lib/ssl/sslimpl.h
+@@ -313,6 +313,11 @@ typedef struct {
+ #endif /* NSS_ENABLE_ECC */
+ 
+ typedef struct sslOptionsStr {
++    /* For clients, this is a validated list of protocols in preference order
++     * and wire format. For servers, this is the list of support protocols,
++     * also in wire format. */
++    SECItem      nextProtoNego;
++
+     unsigned int useSecurity		: 1;  /*  1 */
+     unsigned int useSocks		: 1;  /*  2 */
+     unsigned int requestCertificate	: 1;  /*  3 */
+@@ -785,6 +790,7 @@ const ssl3CipherSuiteDef *suite_def;
+ #ifdef NSS_ENABLE_ECC
+     PRUint32              negotiatedECCurves; /* bit mask */
+ #endif /* NSS_ENABLE_ECC */
++    PRBool                nextProtoNego;/* Our peer has sent this extension */
+ } SSL3HandshakeState;
+ 
+ 
+@@ -826,6 +832,16 @@ struct ssl3StateStr {
+     PRBool               initialized;
+     SSL3HandshakeState   hs;
+     ssl3CipherSpec       specs[2];	/* one is current, one is pending. */
++
++    /* In a client: if the server supports Next Protocol Negotiation, then
++     * this is the protocol that was requested.
++     * In a server: this is the protocol that the client requested via Next
++     * Protocol Negotiation.
++     *
++     * In either case, if the data pointer is non-NULL, then it is malloced
++     * data.  */
++    SECItem		nextProto;
++    int			nextProtoState;	/* See SSL_NEXT_PROTO_* defines */
+ };
+ 
+ typedef struct {
+@@ -1491,8 +1507,12 @@ extern SECStatus ssl3_HandleSupportedPointFormatsXtn(sslSocket * ss,
+ 			PRUint16 ex_type, SECItem *data);
+ extern SECStatus ssl3_ClientHandleSessionTicketXtn(sslSocket *ss,
+ 			PRUint16 ex_type, SECItem *data);
++extern SECStatus ssl3_ClientHandleNextProtoNegoXtn(sslSocket *ss,
++			PRUint16 ex_type, SECItem *data);
+ extern SECStatus ssl3_ServerHandleSessionTicketXtn(sslSocket *ss,
+ 			PRUint16 ex_type, SECItem *data);
++extern SECStatus ssl3_ServerHandleNextProtoNegoXtn(sslSocket *ss,
++			PRUint16 ex_type, SECItem *data);
+ 
+ /* ClientHello and ServerHello extension senders.
+  * Note that not all extension senders are exposed here; only those that
+@@ -1523,6 +1543,10 @@ extern PRInt32 ssl3_SendSupportedCurvesXtn(sslSocket *ss,
+ extern PRInt32 ssl3_SendSupportedPointFormatsXtn(sslSocket *ss,
+ 			PRBool append, PRUint32 maxBytes);
+ #endif
++extern PRInt32 ssl3_ClientSendNextProtoNegoXtn(sslSocket *ss, PRBool append,
++					       PRUint32 maxBytes);
++extern SECStatus ssl3_ValidateNextProtoNego(const unsigned char* data,
++					    unsigned short length);
+ 
+ /* call the registered extension handlers. */
+ extern SECStatus ssl3_HandleHelloExtensions(sslSocket *ss, 
+diff --git a/mozilla/security/nss/lib/ssl/sslsock.c b/mozilla/security/nss/lib/ssl/sslsock.c
+index f1d1921..6536354 100644
+--- a/mozilla/security/nss/lib/ssl/sslsock.c
++++ b/mozilla/security/nss/lib/ssl/sslsock.c
+@@ -163,6 +163,7 @@ static const sslSocketOps ssl_secure_ops = {	/* SSL. */
+ ** default settings for socket enables
+ */
+ static sslOptions ssl_defaults = {
++    { siBuffer, NULL, 0 },	/* nextProtoNego */
+     PR_TRUE, 	/* useSecurity        */
+     PR_FALSE,	/* useSocks           */
+     PR_FALSE,	/* requestCertificate */
+@@ -437,6 +438,10 @@ ssl_DestroySocketContents(sslSocket *ss)
+ 	ssl3_FreeKeyPair(ss->ephemeralECDHKeyPair);
+ 	ss->ephemeralECDHKeyPair = NULL;
+     }
++    if (ss->opt.nextProtoNego.data) {
++	PORT_Free(ss->opt.nextProtoNego.data);
++	ss->opt.nextProtoNego.data = NULL;
++    }
+     PORT_Assert(!ss->xtnData.sniNameArr);
+     if (ss->xtnData.sniNameArr) {
+         PORT_Free(ss->xtnData.sniNameArr);
+@@ -1255,6 +1260,75 @@ SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd)
+     return fd;
+ }
+ 
++/* SSL_SetNextProtoNego sets the list of supported protocols for the given
++ * socket. The list is a series of 8-bit, length prefixed strings. */
++SECStatus
++SSL_SetNextProtoNego(PRFileDesc *fd, const unsigned char *data,
++		     unsigned short length)
++{
++    sslSocket *ss = ssl_FindSocket(fd);
++
++    if (!ss) {
++	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetNextProtoNego", SSL_GETPID(),
++		fd));
++	return SECFailure;
++    }
++
++    if (ssl3_ValidateNextProtoNego(data, length) != SECSuccess)
++	return SECFailure;
++
++    ssl_GetSSL3HandshakeLock(ss);
++    if (ss->opt.nextProtoNego.data)
++	PORT_Free(ss->opt.nextProtoNego.data);
++    ss->opt.nextProtoNego.data = PORT_Alloc(length);
++    if (!ss->opt.nextProtoNego.data) {
++	ssl_ReleaseSSL3HandshakeLock(ss);
++	return SECFailure;
++    }
++    memcpy(ss->opt.nextProtoNego.data, data, length);
++    ss->opt.nextProtoNego.len = length;
++    ss->opt.nextProtoNego.type = siBuffer;
++    ssl_ReleaseSSL3HandshakeLock(ss);
++
++    return SECSuccess;
++}
++
++/* SSL_GetNextProto reads the resulting Next Protocol Negotiation result for
++ * the given socket. It's only valid to call this once the handshake has
++ * completed.
++ *
++ * state is set to one of the SSL_NEXT_PROTO_* constants. The negotiated
++ * protocol, if any, is written into buf, which must be at least buf_len
++ * bytes long. If the negotiated protocol is longer than this, it is truncated.
++ * The number of bytes copied is written into length.
++ */
++SECStatus
++SSL_GetNextProto(PRFileDesc *fd, int *state, unsigned char *buf,
++		 unsigned int *length, unsigned int buf_len)
++{
++    sslSocket *ss = ssl_FindSocket(fd);
++
++    if (!ss) {
++	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetNextProto", SSL_GETPID(),
++		fd));
++	return SECFailure;
++    }
++
++    *state = ss->ssl3.nextProtoState;
++
++    if (ss->ssl3.nextProtoState != SSL_NEXT_PROTO_NO_SUPPORT &&
++	ss->ssl3.nextProto.data) {
++	*length = ss->ssl3.nextProto.len;
++	if (*length > buf_len)
++	    *length = buf_len;
++	PORT_Memcpy(buf, ss->ssl3.nextProto.data, *length);
++    } else {
++	*length = 0;
++    }
++
++    return SECSuccess;
++}
++
+ PRFileDesc *
+ SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
+ {
+diff --git a/mozilla/security/nss/lib/ssl/sslt.h b/mozilla/security/nss/lib/ssl/sslt.h
+index c7d4553..f6e0b62 100644
+--- a/mozilla/security/nss/lib/ssl/sslt.h
++++ b/mozilla/security/nss/lib/ssl/sslt.h
+@@ -203,9 +203,10 @@ typedef enum {
+     ssl_ec_point_formats_xtn         = 11,
+ #endif
+     ssl_session_ticket_xtn           = 35,
++    ssl_next_proto_neg_xtn           = 13172,
+     ssl_renegotiation_info_xtn       = 0xff01	/* experimental number */
+ } SSLExtensionType;
+ 
+-#define SSL_MAX_EXTENSIONS             5
++#define SSL_MAX_EXTENSIONS             6
+ 
+ #endif /* __sslt_h_ */
diff --git a/net/third_party/nss/patches/renegoscsv.patch b/net/third_party/nss/patches/renegoscsv.patch
new file mode 100644
index 0000000..a9c188c
--- /dev/null
+++ b/net/third_party/nss/patches/renegoscsv.patch
@@ -0,0 +1,29 @@
+Index: mozilla/security/nss/lib/ssl/ssl3con.c
+===================================================================
+RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl3con.c,v
+retrieving revision 1.136
+diff -u -p -u -8 -r1.136 ssl3con.c
+--- mozilla/security/nss/lib/ssl/ssl3con.c	17 Feb 2010 02:29:07 -0000	1.136
++++ mozilla/security/nss/lib/ssl/ssl3con.c	27 Feb 2010 02:55:21 -0000
+@@ -3863,19 +3863,19 @@ ssl3_SendClientHello(sslSocket *ss)
+     }
+ 
+     /* how many suites does our PKCS11 support (regardless of policy)? */
+     num_suites = ssl3_config_match_init(ss);
+     if (!num_suites)
+     	return SECFailure;	/* ssl3_config_match_init has set error code. */
+ 
+     /* HACK for SCSV in SSL 3.0.  On initial handshake, prepend SCSV,
+-     * only if we're willing to complete an SSL 3.0 handshake.
++     * only if TLS is disabled.
+      */
+-    if (!ss->firstHsDone && ss->opt.enableSSL3) {
++    if (!ss->firstHsDone && !isTLS) {
+ 	/* Must set this before calling Hello Extension Senders, 
+ 	 * to suppress sending of empty RI extension.
+ 	 */
+ 	ss->ssl3.hs.sendingSCSV = PR_TRUE;
+     }
+ 
+     if (isTLS || (ss->firstHsDone && ss->peerRequestedProtection)) {
+ 	PRUint32 maxBytes = 65535; /* 2^16 - 1 */
diff --git a/net/third_party/nss/patches/versionskew.patch b/net/third_party/nss/patches/versionskew.patch
new file mode 100644
index 0000000..1b96983
--- /dev/null
+++ b/net/third_party/nss/patches/versionskew.patch
@@ -0,0 +1,47 @@
+diff --git a/mozilla/security/nss/lib/ssl/sslsecur.c b/mozilla/security/nss/lib/ssl/sslsecur.c
+index 8f79135..80c2ba6 100644
+--- a/mozilla/security/nss/lib/ssl/sslsecur.c
++++ b/mozilla/security/nss/lib/ssl/sslsecur.c
+@@ -1307,6 +1307,10 @@ SSL_SetURL(PRFileDesc *fd, const char *url)
+ SECStatus
+ SSL_SetTrustAnchors(PRFileDesc *fd, CERTCertList *certList)
+ {
++    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
++    PR_NOT_REACHED("not implemented");
++    return SECFailure;
++#if 0
+     sslSocket *   ss = ssl_FindSocket(fd);
+     CERTDistNames *names = NULL;
+ 
+@@ -1334,6 +1338,7 @@ SSL_SetTrustAnchors(PRFileDesc *fd, CERTCertList *certList)
+     ssl_Release1stHandshakeLock(ss);
+ 
+     return SECSuccess;
++#endif
+ }
+ 
+ /*
+diff --git a/mozilla/security/nss/lib/ssl/sslsock.c b/mozilla/security/nss/lib/ssl/sslsock.c
+index aab48d6..01ef3bd 100644
+--- a/mozilla/security/nss/lib/ssl/sslsock.c
++++ b/mozilla/security/nss/lib/ssl/sslsock.c
+@@ -1258,6 +1258,11 @@ SSL_ImportFD(PRFileDesc *model, PRFileDesc *fd)
+ PRFileDesc *
+ SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
+ {
++    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
++    PR_NOT_REACHED("not implemented");
++    return NULL;
++
++#if 0
+     sslSocket * sm = NULL, *ss = NULL;
+     int i;
+     sslServerCerts * mc = sm->serverCerts;
+@@ -1360,6 +1365,7 @@ SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
+     return fd;
+ loser:
+     return NULL;
++#endif
+ }
+ 
+ /************************************************************************/
diff --git a/net/third_party/nss/ssl.Makefile b/net/third_party/nss/ssl.Makefile
new file mode 100644
index 0000000..4f52412
--- /dev/null
+++ b/net/third_party/nss/ssl.Makefile
@@ -0,0 +1,6 @@
+# This file is generated by gyp; do not edit.
+
+export builddir_name ?= /usr/local/google/src/chromium-merge/src/net/third_party/nss/out
+.PHONY: all
+all:
+	$(MAKE) -C ../../.. ssl
diff --git a/net/third_party/nss/ssl.gyp b/net/third_party/nss/ssl.gyp
new file mode 100644
index 0000000..3166be8
--- /dev/null
+++ b/net/third_party/nss/ssl.gyp
@@ -0,0 +1,149 @@
+# Copyright (c) 2009 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.
+
+{
+  'conditions': [
+    [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd"', {
+      'conditions': [
+        ['sysroot!=""', {
+          'variables': {
+            'pkg-config': '../../../build/linux/pkg-config-wrapper "<(sysroot)"',
+          },
+        }, {
+          'variables': {
+            'pkg-config': 'pkg-config'
+          },
+        }],
+      ],
+    }],
+  ],
+
+  'targets': [
+    {
+      'target_name': 'ssl',
+      'product_name': 'ssl',
+      'type': '<(library)',
+      'sources': [
+        'ssl/authcert.c',
+        'ssl/cmpcert.c',
+        'ssl/derive.c',
+        'ssl/nsskea.c',
+        'ssl/os2_err.c',
+        'ssl/os2_err.h',
+        'ssl/preenc.h',
+        'ssl/prelib.c',
+        'ssl/ssl.h',
+        'ssl/ssl3con.c',
+        'ssl/ssl3ecc.c',
+        'ssl/ssl3ext.c',
+        'ssl/ssl3gthr.c',
+        'ssl/ssl3prot.h',
+        'ssl/sslauth.c',
+        'ssl/sslcon.c',
+        'ssl/ssldef.c',
+        'ssl/sslenum.c',
+        'ssl/sslerr.c',
+        'ssl/sslerr.h',
+        'ssl/sslgathr.c',
+        'ssl/sslimpl.h',
+        'ssl/sslinfo.c',
+        'ssl/sslmutex.c',
+        'ssl/sslmutex.h',
+        'ssl/sslnonce.c',
+        'ssl/sslproto.h',
+        'ssl/sslreveal.c',
+        'ssl/sslsecur.c',
+        'ssl/sslsnce.c',
+        'ssl/sslsock.c',
+        'ssl/sslt.h',
+        'ssl/ssltrace.c',
+        'ssl/sslver.c',
+        'ssl/unix_err.c',
+        'ssl/unix_err.h',
+        'ssl/win32err.c',
+        'ssl/win32err.h',
+        'ssl/bodge/loader.c',
+        'ssl/bodge/loader.h',
+        'ssl/bodge/secure_memcmp.c',
+      ],
+      'sources!': [
+        'ssl/os2_err.c',
+        'ssl/os2_err.h',
+      ],
+      'defines': [
+        'NSS_ENABLE_ECC',
+        'NSS_ENABLE_ZLIB',
+        'USE_UTIL_DIRECTLY',
+      ],
+      'defines!': [
+        # Regrettably, NSS can't be compiled with NO_NSPR_10_SUPPORT yet.
+        'NO_NSPR_10_SUPPORT',
+      ],
+      'conditions': [
+        [ 'OS == "win"', {
+            'sources!': [
+              'ssl/unix_err.c',
+              'ssl/unix_err.h',
+            ],
+          },
+          {  # else: OS != "win"
+            'sources!': [
+              'ssl/win32err.c',
+              'ssl/win32err.h',
+            ],
+          },
+        ],
+        [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd"', {
+          'defines': [
+            # These macros are needed only for compiling the files in
+            # ssl/bodge.
+            'SHLIB_PREFIX="lib"',
+            'SHLIB_SUFFIX="so"',
+            'SHLIB_VERSION="3"',
+            'SOFTOKEN_SHLIB_VERSION="3"',
+          ],
+          'include_dirs': [
+            'ssl/bodge',
+          ],
+          'cflags': [
+            '<!@(<(pkg-config) --cflags nss)',
+          ],
+          'ldflags': [
+            '<!@(<(pkg-config) --libs-only-L --libs-only-other nss)',
+          ],
+          'libraries': [
+            '<!@(<(pkg-config) --libs-only-l nss | sed -e "s/-lssl3//")',
+          ],
+        }],
+        [ 'OS == "mac" or OS == "win"', {
+          'sources/': [
+            ['exclude', 'ssl/bodge/'],
+          ],
+          'dependencies': [
+            '../../../third_party/zlib/zlib.gyp:zlib',
+            '../../../third_party/nss/nss.gyp:nss',
+          ],
+          'direct_dependent_settings': {
+            'include_dirs': [
+              'ssl',
+            ],
+          },
+        }],
+      ],
+      'configurations': {
+        'Debug_Base': {
+          'defines': [
+            'DEBUG',
+          ],
+        },
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/net/third_party/nss/ssl.scons b/net/third_party/nss/ssl.scons
new file mode 100644
index 0000000..c1b52c4
--- /dev/null
+++ b/net/third_party/nss/ssl.scons
@@ -0,0 +1,351 @@
+# This file is generated; do not edit.
+
+import os
+
+Import("env")
+
+env = env.Clone(COMPONENT_NAME='nss',
+                TARGET_NAME='ssl')
+
+configurations = {
+    'Release' : {
+        'Append' : dict(
+            CCFLAGS = [
+                '-pthread',
+                '-fno-exceptions',
+                '-D_FILE_OFFSET_BITS=64',
+                '-fvisibility=hidden',
+                '-fno-strict-aliasing',
+                '-I/usr/include/nss',
+                '-I/usr/include/nspr',
+                '-O2',
+                '-fno-ident',
+                '-fdata-sections',
+                '-ffunction-sections',
+                '-fno-asynchronous-unwind-tables'
+            ],
+            CPPDEFINES = [
+                'CHROMIUM_BUILD',
+                'ENABLE_GPU=1',
+                'NSS_ENABLE_ECC',
+                'NSS_ENABLE_ZLIB',
+                'USE_UTIL_DIRECTLY',
+                'SHLIB_PREFIX=\\"lib\\"',
+                'SHLIB_SUFFIX=\\"so\\"',
+                'SHLIB_VERSION=\\"3\\"',
+                'SOFTOKEN_SHLIB_VERSION=\\"3\\"',
+                'NDEBUG',
+                'NVALGRIND'
+            ],
+            CPPPATH = [
+                env.Dir('$SRC_DIR/net/third_party/nss/ssl/bodge')
+            ],
+            CXXFLAGS = [
+                '-fno-rtti',
+                '-fno-threadsafe-statics',
+                '-fvisibility-inlines-hidden'
+            ],
+            LINKFLAGS = [
+                '-pthread',
+                '-Wl,--gc-sections'
+            ],
+            LIBS = [
+                '-lnss3',
+                '-lnssutil3',
+                '-lsmime3',
+                '-lplds4',
+                '-lplc4',
+                '-lnspr4',
+                '-lpthread',
+                '-ldl'
+            ],
+        ),
+        'FilterOut' : dict(
+        ),
+        'Replace' : dict(
+             FLOCK_LDMODULE = ['flock', '$TOP_BUILDDIR/linker.lock', '$LDMODULE'],
+             FLOCK_LINK = ['flock', '$TOP_BUILDDIR/linker.lock', '$LINK'],
+             FLOCK_SHLINK = ['flock', '$TOP_BUILDDIR/linker.lock', '$SHLINK'],
+             IMPLICIT_COMMAND_DEPENDENCIES = '0',
+             LDMODULECOM = [['$FLOCK_LDMODULE',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$LDMODULEFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+             LIBPATH = ['$LIB_DIR'],
+             LINKCOM = [['$FLOCK_LINK',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$LINKFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+             SHLINKCOM = [['$FLOCK_SHLINK',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$SHLINKFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+        ),
+        'ImportExternal' : [
+             'AS',
+             'CC',
+             'CXX',
+             'LINK',
+        ],
+        'PropagateExternal' : [
+             'AS',
+             'CC',
+             'CCACHE_DIR',
+             'CXX',
+             'DISTCC_DIR',
+             'DISTCC_HOSTS',
+             'HOME',
+             'INCLUDE_SERVER_ARGS',
+             'INCLUDE_SERVER_PORT',
+             'LINK',
+             'CHROME_BUILD_TYPE',
+             'CHROMIUM_BUILD',
+             'OFFICIAL_BUILD',
+        ],
+    },
+    'Debug' : {
+        'Append' : dict(
+            CCFLAGS = [
+                '-pthread',
+                '-fno-exceptions',
+                '-D_FILE_OFFSET_BITS=64',
+                '-fvisibility=hidden',
+                '-fno-strict-aliasing',
+                '-I/usr/include/nss',
+                '-I/usr/include/nspr',
+                '-O0',
+                '-g'
+            ],
+            CPPDEFINES = [
+                'CHROMIUM_BUILD',
+                'ENABLE_GPU=1',
+                'NSS_ENABLE_ECC',
+                'NSS_ENABLE_ZLIB',
+                'USE_UTIL_DIRECTLY',
+                'SHLIB_PREFIX=\\"lib\\"',
+                'SHLIB_SUFFIX=\\"so\\"',
+                'SHLIB_VERSION=\\"3\\"',
+                'SOFTOKEN_SHLIB_VERSION=\\"3\\"',
+                '_DEBUG'
+            ],
+            CPPPATH = [
+                env.Dir('$SRC_DIR/net/third_party/nss/ssl/bodge')
+            ],
+            CXXFLAGS = [
+                '-fno-rtti',
+                '-fno-threadsafe-statics',
+                '-fvisibility-inlines-hidden'
+            ],
+            LINKFLAGS = [
+                '-pthread',
+                '-rdynamic'
+            ],
+            LIBS = [
+                '-lnss3',
+                '-lnssutil3',
+                '-lsmime3',
+                '-lplds4',
+                '-lplc4',
+                '-lnspr4',
+                '-lpthread',
+                '-ldl'
+            ],
+        ),
+        'FilterOut' : dict(
+        ),
+        'Replace' : dict(
+             FLOCK_LDMODULE = ['flock', '$TOP_BUILDDIR/linker.lock', '$LDMODULE'],
+             FLOCK_LINK = ['flock', '$TOP_BUILDDIR/linker.lock', '$LINK'],
+             FLOCK_SHLINK = ['flock', '$TOP_BUILDDIR/linker.lock', '$SHLINK'],
+             IMPLICIT_COMMAND_DEPENDENCIES = '0',
+             LDMODULECOM = [['$FLOCK_LDMODULE',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$LDMODULEFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+             LIBPATH = ['$LIB_DIR'],
+             LINKCOM = [['$FLOCK_LINK',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$LINKFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+             SHLINKCOM = [['$FLOCK_SHLINK',
+  '-o',
+  '$TARGET',
+  '$_LIBDIRFLAGS',
+  '$SHLINKFLAGS',
+  '$SOURCES',
+  '-Wl,--start-group',
+  '$_LIBFLAGS',
+  '-Wl,--end-group']],
+        ),
+        'ImportExternal' : [
+             'AS',
+             'CC',
+             'CXX',
+             'LINK',
+        ],
+        'PropagateExternal' : [
+             'AS',
+             'CC',
+             'CCACHE_DIR',
+             'CXX',
+             'DISTCC_DIR',
+             'DISTCC_HOSTS',
+             'HOME',
+             'INCLUDE_SERVER_ARGS',
+             'INCLUDE_SERVER_PORT',
+             'LINK',
+             'CHROME_BUILD_TYPE',
+             'CHROMIUM_BUILD',
+             'OFFICIAL_BUILD',
+        ],
+    },
+}
+
+config = configurations[env['CONFIG_NAME']]
+env.Append(**config['Append'])
+env.FilterOut(**config['FilterOut'])
+env.Replace(**config['Replace'])
+
+# Scons forces -fPIC for SHCCFLAGS on some platforms.
+# Disable that so we can control it from cflags in gyp.
+# Note that Scons itself is inconsistent with its -fPIC
+# setting. SHCCFLAGS forces -fPIC, and SHCFLAGS does not.
+# This will make SHCCFLAGS consistent with SHCFLAGS.
+env['SHCCFLAGS'] = ['$CCFLAGS']
+
+for _var in config['ImportExternal']:
+  if _var in ARGUMENTS:
+    env[_var] = ARGUMENTS[_var]
+  elif _var in os.environ:
+    env[_var] = os.environ[_var]
+for _var in config['PropagateExternal']:
+  if _var in ARGUMENTS:
+    env[_var] = ARGUMENTS[_var]
+  elif _var in os.environ:
+    env['ENV'][_var] = os.environ[_var]
+
+env['ENV']['LD_LIBRARY_PATH'] = env.subst('$LIB_DIR')
+
+if ARGUMENTS.get('COVERAGE') not in (None, '0'):
+  env.AppendUnique(
+            CCFLAGS = [
+                '-fprofile-arcs',
+                '-ftest-coverage'
+            ],
+            LINKFLAGS = [
+                '-fprofile-arcs'
+            ],
+  )
+
+if ARGUMENTS.get('PROFILE') not in (None, '0'):
+  env.AppendUnique(
+            CCFLAGS = [
+                '-pg',
+                '-g'
+            ],
+            LINKFLAGS = [
+                '-pg'
+            ],
+  )
+
+if ARGUMENTS.get('SYMBOLS') not in (None, '0'):
+  env.AppendUnique(
+            CCFLAGS = [
+                '-g'
+            ],
+  )
+
+input_files = [
+    'ssl/authcert.c',
+    'ssl/cmpcert.c',
+    'ssl/derive.c',
+    'ssl/nsskea.c',
+    'ssl/preenc.h',
+    'ssl/prelib.c',
+    'ssl/ssl.h',
+    'ssl/ssl3con.c',
+    'ssl/ssl3ecc.c',
+    'ssl/ssl3ext.c',
+    'ssl/ssl3gthr.c',
+    'ssl/ssl3prot.h',
+    'ssl/sslauth.c',
+    'ssl/sslcon.c',
+    'ssl/ssldef.c',
+    'ssl/sslenum.c',
+    'ssl/sslerr.c',
+    'ssl/sslerr.h',
+    'ssl/sslgathr.c',
+    'ssl/sslimpl.h',
+    'ssl/sslinfo.c',
+    'ssl/sslmutex.c',
+    'ssl/sslmutex.h',
+    'ssl/sslnonce.c',
+    'ssl/sslproto.h',
+    'ssl/sslreveal.c',
+    'ssl/sslsecur.c',
+    'ssl/sslsnce.c',
+    'ssl/sslsock.c',
+    'ssl/sslt.h',
+    'ssl/ssltrace.c',
+    'ssl/sslver.c',
+    'ssl/unix_err.c',
+    'ssl/unix_err.h',
+    'ssl/bodge/loader.c',
+    'ssl/bodge/loader.h',
+    'ssl/bodge/secure_memcmp.c',
+]
+
+target_files = []
+prerequisites = []
+
+_result = []
+for infile in input_files:
+  if env.compilable(infile):
+    if (type(infile) == type('')
+        and (infile.startswith('$SRC_DIR/net/third_party/nss/')
+             or not os.path.isabs(env.subst(infile)))):
+      # Force files below the build directory by replacing all '..'
+      # elements in the path with '__':
+      base, ext = os.path.splitext(os.path.normpath(infile))
+      base = [d == '..' and '__' or d for d in base.split('/')]
+      base = os.path.join(*base)
+      object = '${OBJ_DIR}/${COMPONENT_NAME}/${TARGET_NAME}/' + base
+      if not infile.startswith('$SRC_DIR/net/third_party/nss/'):
+        infile = '$SRC_DIR/net/third_party/nss/' + infile
+      infile = env.StaticObject(object, infile)[0]
+    else:
+      infile = env.StaticObject(infile)[0]
+  _result.append(infile)
+input_files = _result
+
+_outputs = env.GypStaticLibrary(env.File('${LIB_DIR}/${LIBPREFIX}ssl${LIBSUFFIX}'), input_files)
+target_files.extend(_outputs)
+
+gyp_target = env.Alias('ssl', target_files)
+env.Requires(gyp_target, prerequisites)
+Return("gyp_target")
diff --git a/net/third_party/nss/ssl.target.mk b/net/third_party/nss/ssl.target.mk
new file mode 100644
index 0000000..235d092
--- /dev/null
+++ b/net/third_party/nss/ssl.target.mk
@@ -0,0 +1,166 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := ssl
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DNSS_ENABLE_ECC' \
+	'-DNSS_ENABLE_ZLIB' \
+	'-DUSE_UTIL_DIRECTLY' \
+	'-DSHLIB_PREFIX="lib"' \
+	'-DSHLIB_SUFFIX="so"' \
+	'-DSHLIB_VERSION="3"' \
+	'-DSOFTOKEN_SHLIB_VERSION="3"' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG' \
+	'-DDEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -pthread \
+	-fno-exceptions \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-I/usr/include/nss \
+	-I/usr/include/nspr \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -Inet/third_party/nss/ssl/bodge
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-DNSS_ENABLE_ECC' \
+	'-DNSS_ENABLE_ZLIB' \
+	'-DUSE_UTIL_DIRECTLY' \
+	'-DSHLIB_PREFIX="lib"' \
+	'-DSHLIB_SUFFIX="so"' \
+	'-DSHLIB_VERSION="3"' \
+	'-DSOFTOKEN_SHLIB_VERSION="3"' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -pthread \
+	-fno-exceptions \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-I/usr/include/nss \
+	-I/usr/include/nspr \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -Inet/third_party/nss/ssl/bodge
+
+OBJS := $(obj).target/$(TARGET)/net/third_party/nss/ssl/authcert.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/cmpcert.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/derive.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/nsskea.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/prelib.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/ssl3con.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/ssl3ecc.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/ssl3ext.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/ssl3gthr.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslauth.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslcon.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/ssldef.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslenum.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslerr.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslgathr.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslinfo.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslmutex.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslnonce.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslreveal.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslsecur.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslsnce.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslsock.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/ssltrace.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/sslver.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/unix_err.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/bodge/loader.o \
+	$(obj).target/$(TARGET)/net/third_party/nss/ssl/bodge/secure_memcmp.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD
+	@$(call do_cmd,cc,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD
+	@$(call do_cmd,cc,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD
+	@$(call do_cmd,cc,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,--gc-sections
+
+LIBS := -lnss3 \
+	-lnssutil3 \
+	-lsmime3 \
+	-lplds4 \
+	-lplc4 \
+	-lnspr4 \
+	-lpthread \
+	-ldl
+
+$(obj).target/net/third_party/nss/libssl.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(obj).target/net/third_party/nss/libssl.a: LIBS := $(LIBS)
+$(obj).target/net/third_party/nss/libssl.a: TOOLSET := $(TOOLSET)
+$(obj).target/net/third_party/nss/libssl.a: $(OBJS) FORCE_DO_CMD
+	$(call do_cmd,alink)
+
+all_deps += $(obj).target/net/third_party/nss/libssl.a
+# Add target alias
+.PHONY: ssl
+ssl: $(obj).target/net/third_party/nss/libssl.a
+
+# Add target alias to "all" target.
+.PHONY: all
+all: ssl
+
diff --git a/net/third_party/nss/ssl/ssl.def b/net/third_party/nss/ssl/ssl.def
index 6a11804..a1f4b51 100644
--- a/net/third_party/nss/ssl/ssl.def
+++ b/net/third_party/nss/ssl/ssl.def
@@ -139,7 +139,20 @@
 ;+    local:
 ;+*;
 ;+};
-;+NSS_CHROMIUM {      # Chromium experimental
+;+NSS_3.12.6 {      # NSS 3.12.6 release
+;+    global:
+SSL_ConfigServerSessionIDCacheWithOpt;
+SSL_GetImplementedCiphers;
+SSL_GetNegotiatedHostInfo;
+SSL_GetNumImplementedCiphers;
+SSL_HandshakeNegotiatedExtension;
+SSL_ReconfigFD;
+SSL_SetTrustAnchors;
+SSL_SNISocketConfigHook;
+;+    local:
+;+*;
+;+};
+;+NSS_CHROMIUM {
 ;+    global:
 SSL_GetNextProto;
 SSL_SetNextProtoNego;
diff --git a/net/third_party/nss/ssl/ssl.h b/net/third_party/nss/ssl/ssl.h
index c17f7b1..0bc02f8 100644
--- a/net/third_party/nss/ssl/ssl.h
+++ b/net/third_party/nss/ssl/ssl.h
@@ -36,7 +36,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: ssl.h,v 1.31 2009/11/25 05:24:25 wtc%google.com Exp $ */
+/* $Id: ssl.h,v 1.36 2010/02/10 18:07:21 wtc%google.com Exp $ */
 
 #ifndef __ssl_h_
 #define __ssl_h_
@@ -61,9 +61,15 @@
 /* constant table enumerating all implemented SSL 2 and 3 cipher suites. */
 SSL_IMPORT const PRUint16 SSL_ImplementedCiphers[];
 
+/* the same as the above, but is a function */
+SSL_IMPORT const PRUint16 *SSL_GetImplementedCiphers(void);
+
 /* number of entries in the above table. */
 SSL_IMPORT const PRUint16 SSL_NumImplementedCiphers;
 
+/* the same as the above, but is a function */
+SSL_IMPORT PRUint16 SSL_GetNumImplementedCiphers(void);
+
 /* Macro to tell which ciphers in table are SSL2 vs SSL3/TLS. */
 #define SSL_IS_SSL2_CIPHER(which) (((which) & 0xfff0) == 0xff00)
 
@@ -117,10 +123,22 @@
 #define SSL_ENABLE_DEFLATE             19 /* Enable TLS compression with    */
                                           /* DEFLATE (off by default)       */
 #define SSL_ENABLE_RENEGOTIATION       20 /* Values below (default: never)  */
-#define SSL_REQUIRE_SAFE_NEGOTIATION   21 /* Peer must use renegotiation    */
-                                          /* extension in ALL handshakes.   */
+#define SSL_REQUIRE_SAFE_NEGOTIATION   21 /* Peer must send Signalling      */
+					  /* Cipher Suite Value (SCSV) or   */
+                                          /* Renegotiation  Info (RI)       */
+					  /* extension in ALL handshakes.   */
                                           /* default: off                   */
-					  /* NOT YET IMPLEMENTED in 3.12.5  */
+#define SSL_ENABLE_FALSE_START	       22 /* Enable SSL false start (off by */
+					  /* default, applies only to       */
+					  /* clients). False start is a     */
+/* mode where an SSL client will start sending application data before      */
+/* verifying the server's Finished message. This means that we could end up */
+/* sending data to an imposter. However, the data will be encrypted and     */
+/* only the true server can derive the session key. Thus, so long as the    */
+/* cipher isn't broken this is safe. Because of this, False Start will only */
+/* occur on RSA or DH ciphersuites where the cipher's key length is >= 80   */
+/* bits. The advantage of False Start is that it saves a round trip for     */
+/* client-speaks-first protocols when performing a full handshake.          */
 
 #ifdef SSL_DEPRECATED_FUNCTION 
 /* Old deprecated function names */
@@ -184,11 +202,14 @@
 /* Never renegotiate at all.                                               */
 #define SSL_RENEGOTIATE_NEVER        ((PRBool)0)
 /* Renegotiate without restriction, whether or not the peer's client hello */
-/* bears the renegotiation info extension (like we always did in the past).*/
+/* bears the renegotiation info extension.  Vulnerable, as in the past.    */
 #define SSL_RENEGOTIATE_UNRESTRICTED ((PRBool)1)
-/*  Only renegotiate if the peer's hello bears the TLS renegotiation_info  */
-/*  extension.  Cannot renegotiate in SSL 3.0 sessions.                    */
-#define SSL_RENEGOTIATE_REQUIRES_XTN ((PRBool)2) /*  (NOT YET IMPLEMENTED) */
+/* Only renegotiate if the peer's hello bears the TLS renegotiation_info   */
+/* extension. This is safe renegotiation.                                  */
+#define SSL_RENEGOTIATE_REQUIRES_XTN ((PRBool)2) 
+/* Disallow all renegotiation in server sockets only, but allow clients    */
+/* to continue to renegotiate with vulnerable servers.                     */
+#define SSL_RENEGOTIATE_CLIENT_ONLY  ((PRBool)3)
 
 /*
 ** Reset the handshake state for fd. This will make the complete SSL
@@ -282,6 +303,61 @@
 
 
 /*
+** SNI extension processing callback function.
+** It is called when SSL socket receives SNI extension in ClientHello message.
+** Upon this callback invocation, application is responsible to reconfigure the
+** socket with the data for a particular server name.
+** There are three potential outcomes of this function invocation:
+**    * application does not recognize the name or the type and wants the
+**    "unrecognized_name" alert be sent to the client. In this case the callback
+**    function must return SSL_SNI_SEND_ALERT status.
+**    * application does not recognize  the name, but wants to continue with
+**    the handshake using the current socket configuration. In this case,
+**    no socket reconfiguration is needed and the function should return
+**    SSL_SNI_CURRENT_CONFIG_IS_USED.
+**    * application recognizes the name and reconfigures the socket with
+**    appropriate certs, key, etc. There are many ways to reconfigure. NSS
+**    provides SSL_ReconfigFD function that can be used to update the socket
+**    data from model socket. To continue with the rest of the handshake, the
+**    implementation function should return an index of a name it has chosen.
+** LibSSL will ignore any SNI extension received in a ClientHello message
+** if application does not register a SSLSNISocketConfig callback.
+** Each type field of SECItem indicates the name type.
+** NOTE: currently RFC3546 defines only one name type: sni_host_name.
+** Client is allowed to send only one name per known type. LibSSL will
+** send an "unrecognized_name" alert if SNI extension name list contains more
+** then one name of a type.
+*/
+typedef PRInt32 (PR_CALLBACK *SSLSNISocketConfig)(PRFileDesc *fd,
+                                            const SECItem *srvNameArr,
+                                                  PRUint32 srvNameArrSize,
+                                                  void *arg);
+
+/*
+** SSLSNISocketConfig should return an index within 0 and srvNameArrSize-1
+** when it has reconfigured the socket fd to use certs and keys, etc
+** for a specific name. There are two other allowed return values. One
+** tells libSSL to use the default cert and key.  The other tells libSSL
+** to send the "unrecognized_name" alert.  These values are:
+**/
+#define SSL_SNI_CURRENT_CONFIG_IS_USED           -1
+#define SSL_SNI_SEND_ALERT                       -2
+
+/*
+** Set application implemented SNISocketConfig callback.
+*/
+SSL_IMPORT SECStatus SSL_SNISocketConfigHook(PRFileDesc *fd, 
+                                             SSLSNISocketConfig f,
+                                             void *arg);
+
+/*
+** Reconfigure fd SSL socket with model socket parameters. Sets
+** server certs and keys, list of trust anchor, socket options
+** and all SSL socket call backs and parameters.
+*/
+SSL_IMPORT PRFileDesc *SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd);
+
+/*
  * Set the client side argument for SSL to retrieve PKCS #11 pin.
  *	fd - the file descriptor for the connection in question
  *	a - pkcs11 application specific data
@@ -298,7 +374,7 @@
 				     void *arg);
 
 /*
-** Configure ssl for running a secure server. Needs the
+** Configure SSL socket for running a secure server. Needs the
 ** certificate for the server and the servers private key. The arguments
 ** are copied.
 */
@@ -307,7 +383,7 @@
 				SECKEYPrivateKey *key, SSLKEAType kea);
 
 /*
-** Configure a secure servers session-id cache. Define the maximum number
+** Configure a secure server's session-id cache. Define the maximum number
 ** of entries in the cache, the longevity of the entires, and the directory
 ** where the cache files will be placed.  These values can be zero, and 
 ** if so, the implementation will choose defaults.
@@ -318,6 +394,18 @@
 					            PRUint32 timeout,
 					            PRUint32 ssl3_timeout,
 				              const char *   directory);
+
+/* Configure a secure server's session-id cache. Depends on value of
+ * enableMPCache, configures malti-proc or single proc cache. */
+SSL_IMPORT SECStatus SSL_ConfigServerSessionIDCacheWithOpt(
+                                                           PRUint32 timeout,
+                                                       PRUint32 ssl3_timeout,
+                                                     const char *   directory,
+                                                          int maxCacheEntries,
+                                                      int maxCertCacheEntries,
+                                                    int maxSrvNameCacheEntries,
+                                                           PRBool enableMPCache);
+
 /*
 ** Like SSL_ConfigServerSessionIDCache, with one important difference.
 ** If the application will run multiple processes (as opposed to, or in 
@@ -393,11 +481,17 @@
 #endif
 
 /*
- * Allow the application to pass a URL or hostname into the SSL library
+ * Allow the application to pass a URL or hostname into the SSL library.
  */
 SSL_IMPORT SECStatus SSL_SetURL(PRFileDesc *fd, const char *url);
 
 /*
+ * Allow an application to define a set of trust anchors for peer
+ * cert validation.
+ */
+SSL_IMPORT SECStatus SSL_SetTrustAnchors(PRFileDesc *fd, CERTCertList *list);
+
+/*
 ** Return the number of bytes that SSL has waiting in internal buffers.
 ** Return 0 if security is not enabled.
 */
@@ -436,7 +530,6 @@
 SSL_IMPORT void * SSL_RevealPinArg(PRFileDesc * socket);
 SSL_IMPORT char * SSL_RevealURL(PRFileDesc * socket);
 
-
 /* This callback may be passed to the SSL library via a call to
  * SSL_GetClientAuthDataHook() for each SSL client socket.
  * It will be invoked when SSL needs to know what certificate and private key
@@ -499,6 +592,9 @@
 SSL_IMPORT SECStatus SSL_GetCipherSuiteInfo(PRUint16 cipherSuite, 
                                         SSLCipherSuiteInfo *info, PRUintn len);
 
+/* Returnes negotiated through SNI host info. */
+SSL_IMPORT SECItem *SSL_GetNegotiatedHostInfo(PRFileDesc *fd);
+
 /*
 ** Return a new reference to the certificate that was most recently sent
 ** to the peer on this SSL/TLS connection, or NULL if none has been sent.
@@ -536,6 +632,14 @@
 				   PRUint16 *ciphers, int nciphers,
                                    PRBool *pcanbypass, void *pwArg);
 
+/*
+** Did the handshake with the peer negotiate the given extension?
+** Output parameter valid only if function returns SECSuccess
+*/
+SSL_IMPORT SECStatus SSL_HandshakeNegotiatedExtension(PRFileDesc * socket,
+                                                      SSLExtensionType extId,
+                                                      PRBool *yes);
+
 SEC_END_PROTOS
 
 #endif /* __ssl_h_ */
diff --git a/net/third_party/nss/ssl/ssl3con.c b/net/third_party/nss/ssl/ssl3con.c
index 0018859..9b671e7 100644
--- a/net/third_party/nss/ssl/ssl3con.c
+++ b/net/third_party/nss/ssl/ssl3con.c
@@ -39,7 +39,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: ssl3con.c,v 1.126 2009/12/01 17:59:46 wtc%google.com Exp $ */
+/* $Id: ssl3con.c,v 1.134 2010/02/03 03:44:29 wtc%google.com Exp $ */
 
 #include "cert.h"
 #include "ssl.h"
@@ -72,6 +72,7 @@
 #endif
 
 static void      ssl3_CleanupPeerCerts(sslSocket *ss);
+static void      ssl3_CopyPeerCertsFromSID(sslSocket *ss, sslSessionID *sid);
 static PK11SymKey *ssl3_GenerateRSAPMS(sslSocket *ss, ssl3CipherSpec *spec,
                                        PK11SlotInfo * serverKeySlot);
 static SECStatus ssl3_DeriveMasterSecret(sslSocket *ss, PK11SymKey *pms);
@@ -1169,7 +1170,7 @@
 ** Caller must hold SpecWriteLock.
 */
 static void
-ssl3_DestroyCipherSpec(ssl3CipherSpec *spec)
+ssl3_DestroyCipherSpec(ssl3CipherSpec *spec, PRBool freeSrvName)
 {
     PRBool freeit = (PRBool)(!spec->bypassCiphers);
 /*  PORT_Assert( ss->opt.noLocks || ssl_HaveSpecWriteLock(ss)); Don't have ss! */
@@ -1187,6 +1188,9 @@
 	spec->destroyDecompressContext(spec->decompressContext, 1);
 	spec->decompressContext = NULL;
     }
+    if (freeSrvName && spec->srvVirtName.data) {
+        SECITEM_FreeItem(&spec->srvVirtName, PR_FALSE);
+    }
     if (spec->master_secret != NULL) {
 	PK11_FreeSymKey(spec->master_secret);
 	spec->master_secret = NULL;
@@ -1445,8 +1449,8 @@
       SSLCompressionMethod compression_method;
       SECStatus          rv;
 
-    PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-
+    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
+    PORT_Assert(ss->opt.noLocks || ssl_HaveSpecWriteLock(ss));
     PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
 
     pwSpec        = ss->ssl3.pwSpec;
@@ -1558,6 +1562,14 @@
 	* is decrypting, and vice versa.
 	*/
         optArg1 = !optArg1;
+        break;
+    /* kill warnings. */
+    case ssl_calg_null:
+    case ssl_calg_rc4:
+    case ssl_calg_rc2:
+    case ssl_calg_idea:
+    case ssl_calg_fortezza:
+        break;
     }
 
     rv = (*initFn)(clientContext,
@@ -1626,7 +1638,7 @@
       SSLCipherAlgorithm calg;
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
-
+    PORT_Assert( ss->opt.noLocks || ssl_HaveSpecWriteLock(ss));
     PORT_Assert(ss->ssl3.prSpec == ss->ssl3.pwSpec);
 
     pwSpec        = ss->ssl3.pwSpec;
@@ -2723,7 +2735,7 @@
      * (Both the read and write sides have changed) destroy it.
      */
     if (ss->ssl3.prSpec == ss->ssl3.pwSpec) {
-    	ssl3_DestroyCipherSpec(ss->ssl3.pwSpec);
+    	ssl3_DestroyCipherSpec(ss->ssl3.pwSpec, PR_FALSE/*freeSrvName*/);
     }
     ssl_ReleaseSpecWriteLock(ss); /**************************************/
 
@@ -2785,7 +2797,7 @@
      * (Both the read and write sides have changed) destroy it.
      */
     if (ss->ssl3.prSpec == ss->ssl3.pwSpec) {
-    	ssl3_DestroyCipherSpec(ss->ssl3.prSpec);
+    	ssl3_DestroyCipherSpec(ss->ssl3.prSpec, PR_FALSE/*freeSrvName*/);
     }
     ssl_ReleaseSpecWriteLock(ss);   /*************************************/
     return SECSuccess;
@@ -3206,6 +3218,8 @@
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) ); /* protects sendBuf. */
 
+    if (!bytes)
+    	return SECSuccess;
     if (ss->sec.ci.sendBuf.space < MAX_SEND_BUF_LENGTH && room < bytes) {
 	rv = sslBuffer_Grow(&ss->sec.ci.sendBuf, PR_MAX(MIN_SEND_BUF_LENGTH,
 		 PR_MIN(MAX_SEND_BUF_LENGTH, ss->sec.ci.sendBuf.len + bytes)));
@@ -3715,6 +3729,7 @@
     int              length;
     int              num_suites;
     int              actual_count = 0;
+    PRBool           isTLS = PR_FALSE;
     PRInt32          total_exten_len = 0;
     unsigned         numCompressionMethods;
 
@@ -3728,6 +3743,7 @@
     if (rv != SECSuccess) {
 	return rv;		/* ssl3_InitState has set the error code. */
     }
+    ss->ssl3.hs.sendingSCSV = PR_FALSE; /* Must be reset every handshake */
 
     /* We might be starting a session renegotiation in which case we should
      * clear previous state.
@@ -3825,6 +3841,7 @@
         }
     }
 
+    isTLS = (ss->version > SSL_LIBRARY_VERSION_3_0);
     ssl_GetSpecWriteLock(ss);
     cwSpec = ss->ssl3.cwSpec;
     if (cwSpec->mac_def->mac == mac_null) {
@@ -3852,7 +3869,17 @@
     if (!num_suites)
     	return SECFailure;	/* ssl3_config_match_init has set error code. */
 
-    if (ss->opt.enableTLS && ss->version > SSL_LIBRARY_VERSION_3_0) {
+    /* HACK for SCSV in SSL 3.0.  On initial handshake, prepend SCSV,
+     * only if TLS is disabled.
+     */
+    if (!ss->firstHsDone && !isTLS) {
+	/* Must set this before calling Hello Extension Senders, 
+	 * to suppress sending of empty RI extension.
+	 */
+	ss->ssl3.hs.sendingSCSV = PR_TRUE;
+    }
+
+    if (isTLS || (ss->firstHsDone && ss->peerRequestedProtection)) {
 	PRUint32 maxBytes = 65535; /* 2^16 - 1 */
 	PRInt32  extLen;
 
@@ -3866,8 +3893,10 @@
 	if (total_exten_len > 0)
 	    total_exten_len += 2;
     }
+
 #if defined(NSS_ENABLE_ECC) && !defined(NSS_ECC_MORE_THAN_SUITE_B)
-    else { /* SSL3 only */
+    if (!total_exten_len || !isTLS) {
+	/* not sending the elliptic_curves and ec_point_formats extensions */
     	ssl3_DisableECCSuites(ss, NULL); /* disable all ECC suites */
     }
 #endif
@@ -3876,6 +3905,9 @@
     num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
     if (!num_suites)
     	return SECFailure;	/* count_cipher_suites has set error code. */
+    if (ss->ssl3.hs.sendingSCSV) {
+	++num_suites;   /* make room for SCSV */
+    }
 
     /* count compression methods */
     numCompressionMethods = 0;
@@ -3923,7 +3955,15 @@
 	return rv;	/* err set by ssl3_AppendHandshake* */
     }
 
-
+    if (ss->ssl3.hs.sendingSCSV) {
+	/* Add the actual SCSV */
+	rv = ssl3_AppendHandshakeNumber(ss, TLS_RENEGO_PROTECTION_REQUEST,
+					sizeof(ssl3CipherSuite));
+	if (rv != SECSuccess) {
+	    return rv;	/* err set by ssl3_AppendHandshake* */
+	}
+	actual_count++;
+    }
     for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
 	ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
 	if (config_match(suite, ss->ssl3.policy, PR_TRUE)) {
@@ -3978,9 +4018,14 @@
 	}
 	maxBytes -= extLen;
 	PORT_Assert(!maxBytes);
+    } 
+    if (ss->ssl3.hs.sendingSCSV) {
+	/* Since we sent the SCSV, pretend we sent empty RI extension. */
+	TLSExtensionData *xtnData = &ss->xtnData;
+	xtnData->advertised[xtnData->numAdvertised++] = 
+	    ssl_renegotiation_info_xtn;
     }
 
-
     rv = ssl3_FlushHandshake(ss, 0);
     if (rv != SECSuccess) {
 	return rv;	/* error code set by ssl3_FlushHandshake */
@@ -4491,19 +4536,6 @@
 	goto loser;
     }
 
-#if defined(TRACE)
-    if (ssl_trace >= 100) {
-	SECStatus extractRV = PK11_ExtractKeyValue(pms);
-	if (extractRV == SECSuccess) {
-	    SECItem * keyData = PK11_GetKeyData(pms);
-	    if (keyData && keyData->data && keyData->len) {
-		ssl_PrintBuf(ss, "Pre-Master Secret", 
-			     keyData->data, keyData->len);
-	    }
-    	}
-    }
-#endif
-
     /* Get the wrapped (encrypted) pre-master secret, enc_pms */
     enc_pms.len  = SECKEY_PublicKeyStrength(svrPubKey);
     enc_pms.data = (unsigned char*)PORT_Alloc(enc_pms.len);
@@ -4518,6 +4550,48 @@
 	goto loser;
     }
 
+#if defined(TRACE)
+    if (ssl_trace >= 100 || ssl_keylog_iob) {
+	SECStatus extractRV = PK11_ExtractKeyValue(pms);
+	if (extractRV == SECSuccess) {
+	    SECItem * keyData = PK11_GetKeyData(pms);
+	    if (keyData && keyData->data && keyData->len) {
+		if (ssl_trace >= 100) {
+		    ssl_PrintBuf(ss, "Pre-Master Secret",
+				 keyData->data, keyData->len);
+		}
+		if (ssl_keylog_iob && enc_pms.len >= 8 && keyData->len == 48) {
+		    /* https://developer.mozilla.org/en/NSS_Key_Log_Format */
+
+		    /* There could be multiple, concurrent writers to the
+		     * keylog, so we have to do everything in a single call to
+		     * fwrite. */
+		    char buf[4 + 8*2 + 1 + 48*2 + 1];
+		    static const char hextable[16] = "0123456789abcdef";
+		    unsigned int i;
+
+		    strcpy(buf, "RSA ");
+
+		    for (i = 0; i < 8; i++) {
+			buf[4 + i*2] = hextable[enc_pms.data[i] >> 4];
+			buf[4 + i*2 + 1] = hextable[enc_pms.data[i] & 15];
+		    }
+		    buf[20] = ' ';
+
+		    for (i = 0; i < 48; i++) {
+			buf[21 + i*2] = hextable[keyData->data[i] >> 4];
+			buf[21 + i*2 + 1] = hextable[keyData->data[i] & 15];
+		    }
+		    buf[sizeof(buf) - 1] = '\n';
+
+		    fwrite(buf, sizeof(buf), 1, ssl_keylog_iob);
+		    fflush(ssl_keylog_iob);
+		}
+	    }
+	}
+    }
+#endif
+
     rv = ssl3_InitPendingCipherSpec(ss,  pms);
     PK11_FreeSymKey(pms); pms = NULL;
 
@@ -4909,20 +4983,36 @@
     }
     ss->ssl3.hs.compression = (SSLCompressionMethod)temp;
 
-    /* Note that if !isTLS && length != 0, we do NOT goto alert_loser.
+    /* Note that if !isTLS and the extra stuff is not extensions, we
+     * do NOT goto alert_loser.
      * There are some old SSL 3.0 implementations that do send stuff
      * after the end of the server hello, and we deliberately ignore
      * such stuff in the interest of maximal interoperability (being
      * "generous in what you accept").
+     * Update: Starting in NSS 3.12.6, we handle the renegotiation_info
+     * extension in SSL 3.0.
      */
-    if (isTLS && length != 0) {
+    if (length != 0) {
 	SECItem extensions;
 	rv = ssl3_ConsumeHandshakeVariable(ss, &extensions, 2, &b, &length);
-	if (rv != SECSuccess || length != 0)
-	    goto alert_loser;
-	rv = ssl3_HandleHelloExtensions(ss, &extensions.data, &extensions.len);
-	if (rv != SECSuccess)
-	    goto alert_loser;
+	if (rv != SECSuccess || length != 0) {
+	    if (isTLS)
+		goto alert_loser;
+	} else {
+	    rv = ssl3_HandleHelloExtensions(ss, &extensions.data,
+					    &extensions.len);
+	    if (rv != SECSuccess)
+		goto alert_loser;
+	}
+    }
+    if ((ss->opt.requireSafeNegotiation || 
+         (ss->firstHsDone && (ss->peerRequestedProtection ||
+	 ss->opt.enableRenegotiation == SSL_RENEGOTIATE_REQUIRES_XTN))) &&
+	!ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
+	desc = handshake_failure;
+	errCode = ss->firstHsDone ? SSL_ERROR_RENEGOTIATION_NOT_ALLOWED
+	                          : SSL_ERROR_UNSAFE_NEGOTIATION;
+	goto alert_loser;
     }
 
     /* Any errors after this point are not "malformed" errors. */
@@ -5037,7 +5127,7 @@
 	    sid->u.ssl3.sessionTicket.ticket.data != NULL)
 	    SSL_AtomicIncrementLong(& ssl3stats.hsh_sid_stateless_resumes );
 
-	if (ssl3_ExtensionNegotiated(ss, session_ticket_xtn))
+	if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
 	    ss->ssl3.hs.ws = wait_new_session_ticket;
 	else
 	    ss->ssl3.hs.ws = wait_change_cipher;
@@ -5047,6 +5137,7 @@
 	/* copy the peer cert from the SID */
 	if (sid->peerCert != NULL) {
 	    ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
+	    ssl3_CopyPeerCertsFromSID(ss, sid);
 	}
 
 
@@ -5568,7 +5659,17 @@
     return rv;
 }
 
-
+PRBool
+ssl3_CanFalseStart(sslSocket *ss) {
+    return ss->opt.enableFalseStart &&
+	   !ss->sec.isServer &&
+	   !ss->ssl3.hs.isResuming &&
+	   ss->ssl3.cwSpec &&
+	   ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
+	   (ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_rsa ||
+	    ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_dh  ||
+	    ss->ssl3.hs.kea_def->exchKeyType == ssl_kea_ecdh);
+}
 
 /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
  * ssl3 Server Hello Done message.
@@ -5642,10 +5743,16 @@
 
     ssl_ReleaseXmitBufLock(ss);		/*******************************/
 
-    if (ssl3_ExtensionNegotiated(ss, session_ticket_xtn))
+    if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn))
 	ss->ssl3.hs.ws = wait_new_session_ticket;
     else
 	ss->ssl3.hs.ws = wait_change_cipher;
+
+    /* Do the handshake callback for sslv3 here. */
+    if (ss->handshakeCallback != NULL && ssl3_CanFalseStart(ss)) {
+	(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
+    }
+
     return SECSuccess;
 
 loser:
@@ -5679,6 +5786,25 @@
     return SECSuccess;
 }
 
+/*
+ * Called from:
+ *	ssl3_HandleClientHello()
+ */
+static SECComparison
+ssl3_ServerNameCompare(const SECItem *name1, const SECItem *name2)
+{
+    if (!name1 != !name2) {
+        return SECLessThan;
+    }
+    if (!name1) {
+        return SECEqual;
+    }
+    if (name1->type != name2->type) {
+        return SECLessThan;
+    }
+    return SECITEM_CompareItem(name1, name2);
+}
+
 /* Sets memory error when returning NULL.
  * Called from:
  *	ssl3_SendClientHello()
@@ -5695,6 +5821,21 @@
     if (sid == NULL)
     	return sid;
 
+    if (is_server) {
+        const SECItem *  srvName;
+        SECStatus        rv = SECSuccess;
+
+        ssl_GetSpecReadLock(ss);	/********************************/
+        srvName = &ss->ssl3.prSpec->srvVirtName;
+        if (srvName->len && srvName->data) {
+            rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.srvName, srvName);
+        }
+        ssl_ReleaseSpecReadLock(ss); /************************************/
+        if (rv != SECSuccess) {
+            PORT_Free(sid);
+            return NULL;
+        }
+    }
     sid->peerID		= (ss->peerID == NULL) ? NULL : PORT_Strdup(ss->peerID);
     sid->urlSvrName	= (ss->url    == NULL) ? NULL : PORT_Strdup(ss->url);
     sid->addr           = ss->sec.ci.peer;
@@ -5802,6 +5943,9 @@
     return SECSuccess;
 }
 
+/* An empty TLS Renegotiation Info (RI) extension */
+static const PRUint8 emptyRIext[5] = {0xff, 0x01, 0x00, 0x01, 0x00};
+
 /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
  * ssl3 Client Hello message.
  * Caller must hold Handshake and RecvBuf locks.
@@ -5854,7 +5998,8 @@
 	goto alert_loser;
     }
     if (ss->ssl3.hs.ws == idle_handshake  &&
-    	ss->opt.enableRenegotiation == SSL_RENEGOTIATE_NEVER) {
+        (ss->opt.enableRenegotiation == SSL_RENEGOTIATE_NEVER ||
+         ss->opt.enableRenegotiation == SSL_RENEGOTIATE_CLIENT_ONLY)) {
 	desc    = no_renegotiation;
 	level   = alert_warning;
 	errCode = SSL_ERROR_RENEGOTIATION_NOT_ALLOWED;
@@ -5923,13 +6068,43 @@
 	    goto loser;		/* malformed */
 	}
     }
+    if (!ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
+    	/* If we didn't receive an RI extension, look for the SCSV,
+	 * and if found, treat it just like an empty RI extension
+	 * by processing a local copy of an empty RI extension.
+	 */
+	for (i = 0; i + 1 < suites.len; i += 2) {
+	    PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
+	    if (suite_i == TLS_RENEGO_PROTECTION_REQUEST) {
+		SSL3Opaque * b2 = (SSL3Opaque *)emptyRIext;
+		PRUint32     L2 = sizeof emptyRIext;
+		(void)ssl3_HandleHelloExtensions(ss, &b2, &L2);
+	    	break;
+	    }
+	}
+    }
+    if (ss->firstHsDone &&
+        ss->opt.enableRenegotiation == SSL_RENEGOTIATE_REQUIRES_XTN && 
+	!ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
+	desc    = no_renegotiation;
+	level   = alert_warning;
+	errCode = SSL_ERROR_RENEGOTIATION_NOT_ALLOWED;
+	goto alert_loser;
+    }
+    if ((ss->opt.requireSafeNegotiation || 
+         (ss->firstHsDone && ss->peerRequestedProtection)) &&
+	!ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
+	desc = handshake_failure;
+	errCode = SSL_ERROR_UNSAFE_NEGOTIATION;
+    	goto alert_loser;
+    }
 
     /* We do stateful resumes only if either of the following
      * conditions are satisfied: (1) the client does not support the
      * session ticket extension, or (2) the client support the session
      * ticket extension, but sent an empty ticket.
      */
-    if (!ssl3_ExtensionNegotiated(ss, session_ticket_xtn) ||
+    if (!ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn) ||
 	ss->xtnData.emptySessionTicket) {
 	if (sidBytes.len > 0 && !ss->opt.noCache) {
 	    SSL_TRC(7, ("%d: SSL3[%d]: server, lookup client session-id for 0x%08x%08x%08x%08x",
@@ -5973,9 +6148,9 @@
      * but OpenSSL-0.9.8g does not accept session tickets while
      * resuming.)
      */
-    if (ssl3_ExtensionNegotiated(ss, session_ticket_xtn) && sid == NULL) {
+    if (ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn) && sid == NULL) {
 	ssl3_RegisterServerHelloExtensionSender(ss,
-	    session_ticket_xtn, ssl3_SendSessionTicketXtn);
+	    ssl_session_ticket_xtn, ssl3_SendSessionTicketXtn);
     }
 
     if (sid != NULL) {
@@ -6053,10 +6228,9 @@
 	    break;
 #endif
 	/* Double check that the cached cipher suite is in the client's list */
-	for (i = 0; i < suites.len; i += 2) {
-	    if ((suites.data[i]     == MSB(suite->cipher_suite)) &&
-	        (suites.data[i + 1] == LSB(suite->cipher_suite))) {
-
+	for (i = 0; i + 1 < suites.len; i += 2) {
+	    PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
+	    if (suite_i == suite->cipher_suite) {
 		ss->ssl3.hs.cipher_suite = suite->cipher_suite;
 		ss->ssl3.hs.suite_def =
 		    ssl_LookupCipherSuiteDef(ss->ssl3.hs.cipher_suite);
@@ -6087,10 +6261,9 @@
 	ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
 	if (!config_match(suite, ss->ssl3.policy, PR_TRUE))
 	    continue;
-	for (i = 0; i < suites.len; i += 2) {
-	    if ((suites.data[i]     == MSB(suite->cipher_suite)) &&
-	        (suites.data[i + 1] == LSB(suite->cipher_suite))) {
-
+	for (i = 0; i + 1 < suites.len; i += 2) {
+	    PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1];
+	    if (suite_i == suite->cipher_suite) {
 		ss->ssl3.hs.cipher_suite = suite->cipher_suite;
 		ss->ssl3.hs.suite_def =
 		    ssl_LookupCipherSuiteDef(ss->ssl3.hs.cipher_suite);
@@ -6207,6 +6380,7 @@
 	ss->sec.ci.sid = sid;
 	if (sid->peerCert != NULL) {
 	    ss->sec.peerCert = CERT_DupCertificate(sid->peerCert);
+	    ssl3_CopyPeerCertsFromSID(ss, sid);
 	}
 
 	/*
@@ -6232,6 +6406,32 @@
 	ss->sec.localCert     = 
 		CERT_DupCertificate(ss->serverCerts[sid->keaType].serverCert);
 
+        /* Copy cached name in to pending spec */
+        if (sid != NULL &&
+            sid->version > SSL_LIBRARY_VERSION_3_0 &&
+            sid->u.ssl3.srvName.len && sid->u.ssl3.srvName.data) {
+            /* Set server name from sid */
+            SECItem *sidName = &sid->u.ssl3.srvName;
+            SECItem *pwsName = &ss->ssl3.pwSpec->srvVirtName;
+            if (pwsName->data) {
+                SECITEM_FreeItem(pwsName, PR_FALSE);
+            }
+            rv = SECITEM_CopyItem(NULL, pwsName, sidName);
+            if (rv != SECSuccess) {
+                errCode = PORT_GetError();
+                desc = internal_error;
+                goto alert_loser;
+            }
+        }
+
+        /* Clean up sni name array */
+        if (ssl3_ExtensionNegotiated(ss, ssl_server_name_xtn) &&
+            ss->xtnData.sniNameArr) {
+            PORT_Free(ss->xtnData.sniNameArr);
+            ss->xtnData.sniNameArr = NULL;
+            ss->xtnData.sniNameArrSize = 0;
+        }
+
 	ssl_GetXmitBufLock(ss); haveXmitBufLock = PR_TRUE;
 
 	rv = ssl3_SendServerHello(ss);
@@ -6285,6 +6485,146 @@
     }
     SSL_AtomicIncrementLong(& ssl3stats.hch_sid_cache_misses );
 
+    if (ssl3_ExtensionNegotiated(ss, ssl_server_name_xtn)) {
+        int ret = 0;
+        if (ss->sniSocketConfig) do { /* not a loop */
+            ret = SSL_SNI_SEND_ALERT;
+            /* If extension is negotiated, the len of names should > 0. */
+            if (ss->xtnData.sniNameArrSize) {
+                /* Calling client callback to reconfigure the socket. */
+                ret = (SECStatus)(*ss->sniSocketConfig)(ss->fd,
+                                         ss->xtnData.sniNameArr,
+                                      ss->xtnData.sniNameArrSize,
+                                          ss->sniSocketConfigArg);
+            }
+            if (ret <= SSL_SNI_SEND_ALERT) {
+                /* Application does not know the name or was not able to
+                 * properly reconfigure the socket. */
+                errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+                desc = unrecognized_name;
+                break;
+            } else if (ret == SSL_SNI_CURRENT_CONFIG_IS_USED) {
+                SECStatus       rv = SECSuccess;
+                SECItem *       cwsName, *pwsName;
+
+                ssl_GetSpecWriteLock(ss);  /*******************************/
+                pwsName = &ss->ssl3.pwSpec->srvVirtName;
+                cwsName = &ss->ssl3.cwSpec->srvVirtName;
+#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
+                /* not allow name change on the 2d HS */
+                if (ss->firstHsDone) {
+                    if (ssl3_ServerNameCompare(pwsName, cwsName)) {
+                        ssl_ReleaseSpecWriteLock(ss);  /******************/
+                        errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+                        desc = handshake_failure;
+                        ret = SSL_SNI_SEND_ALERT;
+                        break;
+                    }
+                }
+#endif
+                if (pwsName->data) {
+                    SECITEM_FreeItem(pwsName, PR_FALSE);
+                }
+                if (cwsName->data) {
+                    rv = SECITEM_CopyItem(NULL, pwsName, cwsName);
+                }
+                ssl_ReleaseSpecWriteLock(ss);  /**************************/
+                if (rv != SECSuccess) {
+                    errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
+                    desc = internal_error;
+                    ret = SSL_SNI_SEND_ALERT;
+                    break;
+                }
+            } else if (ret < ss->xtnData.sniNameArrSize) {
+                /* Application has configured new socket info. Lets check it
+                 * and save the name. */
+                SECStatus       rv;
+                SECItem *       name = &ss->xtnData.sniNameArr[ret];
+                int             configedCiphers;
+                SECItem *       pwsName;
+
+                /* get rid of the old name and save the newly picked. */
+                /* This code is protected by ssl3HandshakeLock. */
+                ssl_GetSpecWriteLock(ss);  /*******************************/
+#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
+                /* not allow name change on the 2d HS */
+                if (ss->firstHsDone) {
+                    SECItem *cwsName = &ss->ssl3.cwSpec->srvVirtName;
+                    if (ssl3_ServerNameCompare(name, cwsName)) {
+                        ssl_ReleaseSpecWriteLock(ss);  /******************/
+                        errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+                        desc = handshake_failure;
+                        ret = SSL_SNI_SEND_ALERT;
+                        break;
+                    }
+                }
+#endif
+                pwsName = &ss->ssl3.pwSpec->srvVirtName;
+                if (pwsName->data) {
+                    SECITEM_FreeItem(pwsName, PR_FALSE);
+                }
+                rv = SECITEM_CopyItem(NULL, pwsName, name);
+                ssl_ReleaseSpecWriteLock(ss);  /***************************/
+                if (rv != SECSuccess) {
+                    errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
+                    desc = internal_error;
+                    ret = SSL_SNI_SEND_ALERT;
+                    break;
+                }
+                configedCiphers = ssl3_config_match_init(ss);
+                if (configedCiphers <= 0) {
+                    /* no ciphers are working/supported */
+                    errCode = PORT_GetError();
+                    desc = handshake_failure;
+                    ret = SSL_SNI_SEND_ALERT;
+                    break;
+                }
+                /* Need to tell the client that application has picked
+                 * the name from the offered list and reconfigured the socket.
+                 */
+                ssl3_RegisterServerHelloExtensionSender(ss, ssl_server_name_xtn,
+                                                        ssl3_SendServerNameXtn);
+            } else {
+                /* Callback returned index outside of the boundary. */
+                PORT_Assert(ret < ss->xtnData.sniNameArrSize);
+                errCode = SSL_ERROR_INTERNAL_ERROR_ALERT;
+                desc = internal_error;
+                ret = SSL_SNI_SEND_ALERT;
+                break;
+            }
+        } while (0);
+        /* Free sniNameArr. The data that each SECItem in the array
+         * points into is the data from the input buffer "b". It will
+         * not be available outside the scope of this or it's child
+         * functions.*/
+        if (ss->xtnData.sniNameArr) {
+            PORT_Free(ss->xtnData.sniNameArr);
+            ss->xtnData.sniNameArr = NULL;
+            ss->xtnData.sniNameArrSize = 0;
+        }
+        if (ret <= SSL_SNI_SEND_ALERT) {
+            /* desc and errCode should be set. */
+            goto alert_loser;
+        }
+    }
+#ifndef SSL_SNI_ALLOW_NAME_CHANGE_2HS
+    else if (ss->firstHsDone) {
+        /* Check that we don't have the name is current spec
+         * if this extension was not negotiated on the 2d hs. */
+        PRBool passed = PR_TRUE;
+        ssl_GetSpecReadLock(ss);  /*******************************/
+        if (ss->ssl3.cwSpec->srvVirtName.data) {
+            passed = PR_FALSE;
+        }
+        ssl_ReleaseSpecReadLock(ss);  /***************************/
+        if (!passed) {
+            errCode = SSL_ERROR_UNRECOGNIZED_NAME_ALERT;
+            desc = handshake_failure;
+            goto alert_loser;
+        }
+    }
+#endif
+
     sid = ssl3_NewSessionID(ss, PR_TRUE);
     if (sid == NULL) {
 	errCode = PORT_GetError();
@@ -6430,11 +6770,9 @@
 	ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
 	if (!config_match(suite, ss->ssl3.policy, PR_TRUE))
 	    continue;
-	for (i = 0; i < suite_length; i += 3) {
-	    if ((suites[i]   == 0) &&
-		(suites[i+1] == MSB(suite->cipher_suite)) &&
-		(suites[i+2] == LSB(suite->cipher_suite))) {
-
+	for (i = 0; i+2 < suite_length; i += 3) {
+	    PRUint32 suite_i = (suites[i] << 16)|(suites[i+1] << 8)|suites[i+2];
+	    if (suite_i == suite->cipher_suite) {
 		ss->ssl3.hs.cipher_suite = suite->cipher_suite;
 		ss->ssl3.hs.suite_def =
 		    ssl_LookupCipherSuiteDef(ss->ssl3.hs.cipher_suite);
@@ -6447,6 +6785,26 @@
 
 suite_found:
 
+    /* Look for the SCSV, and if found, treat it just like an empty RI 
+     * extension by processing a local copy of an empty RI extension.
+     */
+    for (i = 0; i+2 < suite_length; i += 3) {
+	PRUint32 suite_i = (suites[i] << 16) | (suites[i+1] << 8) | suites[i+2];
+	if (suite_i == TLS_RENEGO_PROTECTION_REQUEST) {
+	    SSL3Opaque * b2 = (SSL3Opaque *)emptyRIext;
+	    PRUint32     L2 = sizeof emptyRIext;
+	    (void)ssl3_HandleHelloExtensions(ss, &b2, &L2);
+	    break;
+	}
+    }
+
+    if (ss->opt.requireSafeNegotiation &&
+	!ssl3_ExtensionNegotiated(ss, ssl_renegotiation_info_xtn)) {
+	desc = handshake_failure;
+	errCode = SSL_ERROR_UNSAFE_NEGOTIATION;
+    	goto alert_loser;
+    }
+
     ss->ssl3.hs.compression = ssl_compression_null;
     ss->sec.send            = ssl3_SendApplicationData;
 
@@ -7391,6 +7749,38 @@
     ss->ssl3.peerCertChain = NULL;
 }
 
+static void
+ssl3_CopyPeerCertsFromSID(sslSocket *ss, sslSessionID *sid)
+{
+    PRArenaPool *arena;
+    ssl3CertNode *certs = NULL;
+    int i;
+
+    if (!sid->peerCertChain[0])
+	return;
+    PORT_Assert(!ss->ssl3.peerCertArena);
+    PORT_Assert(!ss->ssl3.peerCertChain);
+    ss->ssl3.peerCertArena = arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    for (i = 0; i < MAX_PEER_CERT_CHAIN_SIZE && sid->peerCertChain[i]; i++) {
+	ssl3CertNode *c = PORT_ArenaNew(arena, ssl3CertNode);
+	c->cert = CERT_DupCertificate(sid->peerCertChain[i]);
+	c->next = certs;
+	certs = c;
+    }
+    ss->ssl3.peerCertChain = certs;
+}
+
+static void
+ssl3_CopyPeerCertsToSID(ssl3CertNode *certs, sslSessionID *sid)
+{
+    int i = 0;
+    ssl3CertNode *c = certs;
+    for (; i < MAX_PEER_CERT_CHAIN_SIZE && c; i++, c = c->next) {
+	PORT_Assert(!sid->peerCertChain[i]);
+	sid->peerCertChain[i] = CERT_DupCertificate(c->cert);
+    }
+}
+
 /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete
  * ssl3 Certificate message.
  * Caller must hold Handshake and RecvBuf locks.
@@ -7577,6 +7967,7 @@
     }
 
     ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
+    ssl3_CopyPeerCertsToSID(certs, ss->sec.ci.sid);
 
     if (!ss->sec.isServer) {
 	/* set the server authentication and key exchange types and sizes
@@ -7748,6 +8139,8 @@
     if (ss->handshake != NULL) {
 	ss->handshake = ssl_GatherRecord1stHandshake;
 	ss->sec.ci.sid->peerCert = CERT_DupCertificate(ss->sec.peerCert);
+	ssl3_CopyPeerCertsToSID((ssl3CertNode *)ss->ssl3.peerCertChain,
+				ss->sec.ci.sid);
 
 	ssl_GetRecvBufLock(ss);
 	if (ss->ssl3.hs.msgState.buf != NULL) {
@@ -7872,6 +8265,11 @@
     }
 
     if (isTLS) {
+	if (isServer)
+	    ss->ssl3.hs.finishedMsgs.tFinished[1] = tlsFinished;
+	else
+	    ss->ssl3.hs.finishedMsgs.tFinished[0] = tlsFinished;
+	ss->ssl3.hs.finishedBytes = sizeof tlsFinished;
 	rv = ssl3_AppendHandshakeHeader(ss, finished, sizeof tlsFinished);
 	if (rv != SECSuccess) 
 	    goto fail; 		/* err set by AppendHandshake. */
@@ -7879,6 +8277,11 @@
 	if (rv != SECSuccess) 
 	    goto fail; 		/* err set by AppendHandshake. */
     } else {
+	if (isServer)
+	    ss->ssl3.hs.finishedMsgs.sFinished[1] = hashes;
+	else
+	    ss->ssl3.hs.finishedMsgs.sFinished[0] = hashes;
+	ss->ssl3.hs.finishedBytes = sizeof hashes;
 	rv = ssl3_AppendHandshakeHeader(ss, finished, sizeof hashes);
 	if (rv != SECSuccess) 
 	    goto fail; 		/* err set by AppendHandshake. */
@@ -8017,6 +8420,11 @@
 	}
 	rv = ssl3_ComputeTLSFinished(ss->ssl3.crSpec, !isServer, 
 	                             hashes, &tlsFinished);
+	if (!isServer)
+	    ss->ssl3.hs.finishedMsgs.tFinished[1] = tlsFinished;
+	else
+	    ss->ssl3.hs.finishedMsgs.tFinished[0] = tlsFinished;
+	ss->ssl3.hs.finishedBytes = sizeof tlsFinished;
 	if (rv != SECSuccess ||
 	    0 != NSS_SecureMemcmp(&tlsFinished, b, length)) {
 	    (void)SSL3_SendAlert(ss, alert_fatal, decrypt_error);
@@ -8030,6 +8438,11 @@
 	    return SECFailure;
 	}
 
+	if (!isServer)
+	    ss->ssl3.hs.finishedMsgs.sFinished[1] = *hashes;
+	else
+	    ss->ssl3.hs.finishedMsgs.sFinished[0] = *hashes;
+	ss->ssl3.hs.finishedBytes = sizeof *hashes;
 	if (0 != NSS_SecureMemcmp(hashes, b, length)) {
 	    (void)ssl3_HandshakeFailure(ss);
 	    PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
@@ -8052,7 +8465,7 @@
 	 * ServerHello message.)
 	 */
 	if (isServer && !ss->ssl3.hs.isResuming &&
-	    ssl3_ExtensionNegotiated(ss, session_ticket_xtn)) {
+	    ssl3_ExtensionNegotiated(ss, ssl_session_ticket_xtn)) {
 	    rv = ssl3_SendNewSessionTicket(ss);
 	    if (rv != SECSuccess) {
 		goto xmit_loser;
@@ -8072,6 +8485,14 @@
 	if (doStepUp || ss->writerThread == PR_GetCurrentThread()) {
 	    flags = ssl_SEND_FLAG_FORCE_INTO_BUFFER;
 	}
+
+	if (!isServer) {
+	    rv = ssl3_SendNextProto(ss);
+	    if (rv != SECSuccess) {
+		goto xmit_loser;	/* err code was set. */
+	    }
+	}
+
 	rv = ssl3_SendFinished(ss, flags);
 	if (rv != SECSuccess) {
 	    goto xmit_loser;	/* err is set. */
@@ -8150,7 +8571,7 @@
     ss->ssl3.hs.ws = idle_handshake;
 
     /* Do the handshake callback for sslv3 here. */
-    if (ss->handshakeCallback != NULL) {
+    if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) {
 	(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
     }
 
@@ -8643,11 +9064,33 @@
 				  databuf->space,
 				  plaintext->buf,
 				  plaintext->len);
+
 	if (rv != SECSuccess) {
 	    int err = ssl_MapLowLevelError(SSL_ERROR_DECOMPRESSION_FAILURE);
-	    PORT_Free(plaintext->buf);
 	    SSL3_SendAlert(ss, alert_fatal,
 			   isTLS ? decompression_failure : bad_record_mac);
+
+	    /* There appears to be a bug with (at least) Apache + OpenSSL where
+	     * resumed SSLv3 connections don't actually use compression. See
+	     * comments 93-95 of
+	     * https://bugzilla.mozilla.org/show_bug.cgi?id=275744
+	     *
+	     * So, if we get a decompression error, and the record appears to
+	     * be already uncompressed, then we return a more specific error
+	     * code to hopefully save somebody some debugging time in the
+	     * future.
+	     */
+	    if (plaintext->len >= 4) {
+		unsigned int len = ((unsigned int) plaintext->buf[1] << 16) |
+		                   ((unsigned int) plaintext->buf[2] << 8) |
+		                   (unsigned int) plaintext->buf[3];
+		if (len == plaintext->len - 4) {
+		    /* This appears to be uncompressed already */
+		    err = SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD;
+		}
+	    }
+
+	    PORT_Free(plaintext->buf);
 	    PORT_SetError(err);
 	    return SECFailure;
 	}
@@ -8782,6 +9225,7 @@
     ss->ssl3.crSpec = ss->ssl3.cwSpec = &ss->ssl3.specs[0];
     ss->ssl3.prSpec = ss->ssl3.pwSpec = &ss->ssl3.specs[1];
     ss->ssl3.hs.rehandshake = PR_FALSE;
+    ss->ssl3.hs.sendingSCSV = PR_FALSE;
     ssl3_InitCipherSpec(ss, ss->ssl3.crSpec);
     ssl3_InitCipherSpec(ss, ss->ssl3.prSpec);
 
@@ -9049,7 +9493,9 @@
 	PORT_SetError(SSL_ERROR_HANDSHAKE_NOT_COMPLETED);
 	return SECFailure;
     }
-    if (ss->opt.enableRenegotiation == SSL_RENEGOTIATE_NEVER) {
+    if (ss->opt.enableRenegotiation == SSL_RENEGOTIATE_NEVER ||
+       (ss->opt.enableRenegotiation == SSL_RENEGOTIATE_CLIENT_ONLY &&
+        ss->sec.isServer)) {
 	PORT_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED);
 	return SECFailure;
     }
@@ -9110,8 +9556,8 @@
     PORT_Free(ss->ssl3.hs.msg_body.buf);
 
     /* free up the CipherSpecs */
-    ssl3_DestroyCipherSpec(&ss->ssl3.specs[0]);
-    ssl3_DestroyCipherSpec(&ss->ssl3.specs[1]);
+    ssl3_DestroyCipherSpec(&ss->ssl3.specs[0], PR_TRUE/*freeSrvName*/);
+    ssl3_DestroyCipherSpec(&ss->ssl3.specs[1], PR_TRUE/*freeSrvName*/);
 
     ss->ssl3.initialized = PR_FALSE;
 
diff --git a/net/third_party/nss/ssl/ssl3ecc.c b/net/third_party/nss/ssl/ssl3ecc.c
index fafecfa..42720e5 100644
--- a/net/third_party/nss/ssl/ssl3ecc.c
+++ b/net/third_party/nss/ssl/ssl3ecc.c
@@ -40,7 +40,7 @@
  * ***** END LICENSE BLOCK ***** */
 
 /* ECC code moved here from ssl3con.c */
-/* $Id: ssl3ecc.c,v 1.22 2008/03/10 00:01:28 wtc%google.com Exp $ */
+/* $Id: ssl3ecc.c,v 1.23 2010/01/28 16:14:25 kaie%kuix.de Exp $ */
 
 #include "nss.h"
 #include "cert.h"
@@ -1059,7 +1059,7 @@
 	if (!ss->sec.isServer) {
 	    TLSExtensionData *xtnData = &ss->xtnData;
 	    xtnData->advertised[xtnData->numAdvertised++] =
-		elliptic_curves_xtn;
+		ssl_elliptic_curves_xtn;
 	}
     }
     return (sizeof EClist);
@@ -1083,7 +1083,7 @@
 	if (!ss->sec.isServer) {
 	    TLSExtensionData *xtnData = &ss->xtnData;
 	    xtnData->advertised[xtnData->numAdvertised++] =
-		ec_point_formats_xtn;
+		ssl_ec_point_formats_xtn;
 	}
     }
     return (sizeof ECPtFmt);
diff --git a/net/third_party/nss/ssl/ssl3ext.c b/net/third_party/nss/ssl/ssl3ext.c
index 1eaf47c..ead0cfd 100644
--- a/net/third_party/nss/ssl/ssl3ext.c
+++ b/net/third_party/nss/ssl/ssl3ext.c
@@ -41,11 +41,12 @@
  * ***** END LICENSE BLOCK ***** */
 
 /* TLS extension code moved here from ssl3ecc.c */
-/* $Id: ssl3ext.c,v 1.5 2009/11/07 18:23:06 wtc%google.com Exp $ */
+/* $Id: ssl3ext.c,v 1.11 2010/02/03 02:38:20 wtc%google.com Exp $ */
 
 #include "nssrenam.h"
 #include "nss.h"
 #include "ssl.h"
+#include "sslproto.h"
 #include "sslimpl.h"
 #include "pk11pub.h"
 #include "blapi.h"
@@ -61,8 +62,7 @@
 static PRBool         session_ticket_keys_initialized = PR_FALSE;
 static PRCallOnceType generate_session_keys_once;
 
-static PRInt32 ssl3_SendServerNameXtn(sslSocket * ss,
-    PRBool append, PRUint32 maxBytes);
+/* forward static function declarations */
 static SECStatus ssl3_ParseEncryptedSessionTicket(sslSocket *ss,
     SECItem *data, EncryptedSessionTicket *enc_session_ticket);
 static SECStatus ssl3_AppendToItem(SECItem *item, const unsigned char *buf,
@@ -74,6 +74,10 @@
 static SECStatus ssl3_GetSessionTicketKeys(const unsigned char **aes_key,
     PRUint32 *aes_key_length, const unsigned char **mac_key,
     PRUint32 *mac_key_length);
+static PRInt32 ssl3_SendRenegotiationInfoXtn(sslSocket * ss,
+    PRBool append, PRUint32 maxBytes);
+static SECStatus ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, 
+    PRUint16 ex_type, SECItem *data);
 
 /*
  * Write bytes.  Using this function means the SECItem structure
@@ -222,42 +226,58 @@
  * In the second generation, this table will be dynamic, and functions
  * will be registered here.
  */
+/* This table is used by the server, to handle client hello extensions. */
 static const ssl3HelloExtensionHandler clientHelloHandlers[] = {
-    { server_name_xtn, &ssl3_HandleServerNameXtn },
+    { ssl_server_name_xtn,        &ssl3_HandleServerNameXtn },
 #ifdef NSS_ENABLE_ECC
-    { elliptic_curves_xtn, &ssl3_HandleSupportedCurvesXtn },
-    { ec_point_formats_xtn, &ssl3_HandleSupportedPointFormatsXtn },
+    { ssl_elliptic_curves_xtn,    &ssl3_HandleSupportedCurvesXtn },
+    { ssl_ec_point_formats_xtn,   &ssl3_HandleSupportedPointFormatsXtn },
 #endif
-    { session_ticket_xtn, &ssl3_ServerHandleSessionTicketXtn },
-    { next_proto_neg_xtn, &ssl3_ServerHandleNextProtoNegoXtn },
+    { ssl_session_ticket_xtn,     &ssl3_ServerHandleSessionTicketXtn },
+    { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
+    { ssl_next_proto_neg_xtn,     &ssl3_ServerHandleNextProtoNegoXtn },
     { -1, NULL }
 };
 
-static const ssl3HelloExtensionHandler serverHelloHandlers[] = {
-    { server_name_xtn, &ssl3_HandleServerNameXtn },
-    /* TODO: add a handler for ec_point_formats_xtn */
-    { session_ticket_xtn, &ssl3_ClientHandleSessionTicketXtn },
-    { next_proto_neg_xtn, &ssl3_ClientHandleNextProtoNegoXtn },
+/* These two tables are used by the client, to handle server hello
+ * extensions. */
+static const ssl3HelloExtensionHandler serverHelloHandlersTLS[] = {
+    { ssl_server_name_xtn,        &ssl3_HandleServerNameXtn },
+    /* TODO: add a handler for ssl_ec_point_formats_xtn */
+    { ssl_session_ticket_xtn,     &ssl3_ClientHandleSessionTicketXtn },
+    { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
+    { ssl_next_proto_neg_xtn,     &ssl3_ClientHandleNextProtoNegoXtn },
     { -1, NULL }
 };
 
-/* Table of functions to format TLS hello extensions, one per extension.
- * This static table is for the formatting of client hello extensions.
+static const ssl3HelloExtensionHandler serverHelloHandlersSSL3[] = {
+    { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
+    { -1, NULL }
+};
+
+/* Tables of functions to format TLS hello extensions, one function per
+ * extension.
+ * These static tables are for the formatting of client hello extensions.
  * The server's table of hello senders is dynamic, in the socket struct,
  * and sender functions are registered there.
  */
 static const 
-ssl3HelloExtensionSender clientHelloSenders[MAX_EXTENSIONS] = {
-    { server_name_xtn, &ssl3_SendServerNameXtn },
+ssl3HelloExtensionSender clientHelloSendersTLS[SSL_MAX_EXTENSIONS] = {
+    { ssl_server_name_xtn,        &ssl3_SendServerNameXtn        },
+    { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn },
 #ifdef NSS_ENABLE_ECC
-    { elliptic_curves_xtn, &ssl3_SendSupportedCurvesXtn },
-    { ec_point_formats_xtn, &ssl3_SendSupportedPointFormatsXtn },
-#else
-    { -1, NULL },
-    { -1, NULL },
+    { ssl_elliptic_curves_xtn,    &ssl3_SendSupportedCurvesXtn },
+    { ssl_ec_point_formats_xtn,   &ssl3_SendSupportedPointFormatsXtn },
 #endif
-    { session_ticket_xtn, ssl3_SendSessionTicketXtn },
-    { next_proto_neg_xtn, ssl3_ClientSendNextProtoNegoXtn }
+    { ssl_session_ticket_xtn,     &ssl3_SendSessionTicketXtn },
+    { ssl_next_proto_neg_xtn,     &ssl3_ClientSendNextProtoNegoXtn }
+    /* any extra entries will appear as { 0, NULL }    */
+};
+
+static const 
+ssl3HelloExtensionSender clientHelloSendersSSL3[SSL_MAX_EXTENSIONS] = {
+    { ssl_renegotiation_info_xtn, &ssl3_SendRenegotiationInfoXtn }
+    /* any extra entries will appear as { 0, NULL }    */
 };
 
 static PRBool
@@ -287,60 +307,159 @@
 
 /* Format an SNI extension, using the name from the socket's URL,
  * unless that name is a dotted decimal string.
+ * Used by client and server.
  */
-static PRInt32 
-ssl3_SendServerNameXtn(
-			sslSocket * ss,
-			PRBool      append,
-			PRUint32    maxBytes)
+PRInt32
+ssl3_SendServerNameXtn(sslSocket * ss, PRBool append,
+                       PRUint32 maxBytes)
 {
-    PRUint32 len;
-    PRNetAddr netAddr;
-
-    /* must have a hostname */
-    if (!ss || !ss->url || !ss->url[0])
-    	return 0;
-    /* must not be an IPv4 or IPv6 address */
-    if (PR_SUCCESS == PR_StringToNetAddr(ss->url, &netAddr)) {
-        /* is an IP address (v4 or v6) */
-        return 0;
+    SECStatus rv;
+    if (!ss->sec.isServer) {
+        PRUint32 len;
+        PRNetAddr netAddr;
+        
+        /* must have a hostname */
+        if (!ss || !ss->url || !ss->url[0])
+            return 0;
+        /* must not be an IPv4 or IPv6 address */
+        if (PR_SUCCESS == PR_StringToNetAddr(ss->url, &netAddr)) {
+            /* is an IP address (v4 or v6) */
+            return 0;
+        }
+        len  = PORT_Strlen(ss->url);
+        if (append && maxBytes >= len + 9) {
+            /* extension_type */
+            rv = ssl3_AppendHandshakeNumber(ss, ssl_server_name_xtn, 2); 
+            if (rv != SECSuccess) return -1;
+            /* length of extension_data */
+            rv = ssl3_AppendHandshakeNumber(ss, len + 5, 2); 
+            if (rv != SECSuccess) return -1;
+            /* length of server_name_list */
+            rv = ssl3_AppendHandshakeNumber(ss, len + 3, 2);
+            if (rv != SECSuccess) return -1;
+            /* Name Type (sni_host_name) */
+            rv = ssl3_AppendHandshake(ss,       "\0",    1);
+            if (rv != SECSuccess) return -1;
+            /* HostName (length and value) */
+            rv = ssl3_AppendHandshakeVariable(ss, (PRUint8 *)ss->url, len, 2);
+            if (rv != SECSuccess) return -1;
+            if (!ss->sec.isServer) {
+                TLSExtensionData *xtnData = &ss->xtnData;
+                xtnData->advertised[xtnData->numAdvertised++] = 
+		    ssl_server_name_xtn;
+            }
+        }
+        return len + 9;
     }
-    len  = PORT_Strlen(ss->url);
-    if (append && maxBytes >= len + 9) {
-	SECStatus rv;
-	/* extension_type */
-	rv = ssl3_AppendHandshakeNumber(ss, server_name_xtn, 2); 
-	if (rv != SECSuccess) return -1;
-	/* length of extension_data */
-	rv = ssl3_AppendHandshakeNumber(ss, len + 5, 2); 
-	if (rv != SECSuccess) return -1;
-	/* length of server_name_list */
-	rv = ssl3_AppendHandshakeNumber(ss, len + 3, 2);
-	if (rv != SECSuccess) return -1;
-	/* Name Type (host_name) */
-	rv = ssl3_AppendHandshake(ss,       "\0",    1);
-	if (rv != SECSuccess) return -1;
-	/* HostName (length and value) */
-	rv = ssl3_AppendHandshakeVariable(ss, (unsigned char *)ss->url, len, 2);
-	if (rv != SECSuccess) return -1;
-	if (!ss->sec.isServer) {
-	    TLSExtensionData *xtnData = &ss->xtnData;
-	    xtnData->advertised[xtnData->numAdvertised++] = server_name_xtn;
-	}
+    /* Server side */
+    if (append && maxBytes >= 4) {
+        rv = ssl3_AppendHandshakeNumber(ss, ssl_server_name_xtn, 2);
+        if (rv != SECSuccess)  return -1;
+        /* length of extension_data */
+        rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
+        if (rv != SECSuccess) return -1;
     }
-    return len + 9;
+    return 4;
 }
 
 /* handle an incoming SNI extension, by ignoring it. */
 SECStatus
 ssl3_HandleServerNameXtn(sslSocket * ss, PRUint16 ex_type, SECItem *data)
 {
-    /* TODO: if client, should verify extension_data is empty. */
-    /* TODO: if server, should send empty extension_data. */
-    /* For now, we ignore this, as if we didn't understand it. :-)  */
-    return SECSuccess;
-}
+    SECItem *names = NULL;
+    PRUint32 listCount = 0, namesPos = 0, i;
+    TLSExtensionData *xtnData = &ss->xtnData;
+    SECItem  ldata;
+    PRInt32  listLenBytes = 0;
 
+    if (!ss->sec.isServer) {
+        /* Verify extension_data is empty. */
+        if (data->data || data->len ||
+            !ssl3_ExtensionNegotiated(ss, ssl_server_name_xtn)) {
+            /* malformed or was not initiated by the client.*/
+            return SECFailure;
+        }
+        return SECSuccess;
+    }
+
+    /* Server side - consume client data and register server sender. */
+    /* do not parse the data if don't have user extension handling function. */
+    if (!ss->sniSocketConfig) {
+        return SECSuccess;
+    }
+    /* length of server_name_list */
+    listLenBytes = ssl3_ConsumeHandshakeNumber(ss, 2, &data->data, &data->len); 
+    if (listLenBytes == 0 || listLenBytes != data->len) {
+        return SECFailure;
+    }
+    ldata = *data;
+    /* Calculate the size of the array.*/
+    while (listLenBytes > 0) {
+        SECItem litem;
+        SECStatus rv;
+        PRInt32  type;
+        /* Name Type (sni_host_name) */
+        type = ssl3_ConsumeHandshakeNumber(ss, 1, &ldata.data, &ldata.len); 
+        if (!ldata.len) {
+            return SECFailure;
+        }
+        rv = ssl3_ConsumeHandshakeVariable(ss, &litem, 2, &ldata.data, &ldata.len);
+        if (rv != SECSuccess) {
+            return SECFailure;
+        }
+        /* Adjust total length for cunsumed item, item len and type.*/
+        listLenBytes -= litem.len + 3;
+        if (listLenBytes > 0 && !ldata.len) {
+            return SECFailure;
+        }
+        listCount += 1;
+    }
+    if (!listCount) {
+        return SECFailure;
+    }
+    names = PORT_ZNewArray(SECItem, listCount);
+    if (!names) {
+        return SECFailure;
+    }
+    for (i = 0;i < listCount;i++) {
+        int j;
+        PRInt32  type;
+        SECStatus rv;
+        PRBool nametypePresent = PR_FALSE;
+        /* Name Type (sni_host_name) */
+        type = ssl3_ConsumeHandshakeNumber(ss, 1, &data->data, &data->len); 
+        /* Check if we have such type in the list */
+        for (j = 0;j < listCount && names[j].data;j++) {
+            if (names[j].type == type) {
+                nametypePresent = PR_TRUE;
+                break;
+            }
+        }
+        /* HostName (length and value) */
+        rv = ssl3_ConsumeHandshakeVariable(ss, &names[namesPos], 2,
+                                           &data->data, &data->len);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
+        if (nametypePresent == PR_FALSE) {
+            namesPos += 1;
+        }
+    }
+    /* Free old and set the new data. */
+    if (xtnData->sniNameArr) {
+        PORT_Free(ss->xtnData.sniNameArr);
+    }
+    xtnData->sniNameArr = names;
+    xtnData->sniNameArrSize = namesPos;
+    xtnData->negotiated[xtnData->numNegotiated++] = ssl_server_name_xtn;
+
+    return SECSuccess;
+
+loser:
+    PORT_Free(names);
+    return SECFailure;
+}
+        
 /* Called by both clients and servers.
  * Clients sends a filled in session ticket if one is available, and otherwise
  * sends an empty ticket.  Servers always send empty tickets.
@@ -386,7 +505,7 @@
     if (append && maxBytes >= extension_length) {
 	SECStatus rv;
 	/* extension_type */
-        rv = ssl3_AppendHandshakeNumber(ss, session_ticket_xtn, 2);
+        rv = ssl3_AppendHandshakeNumber(ss, ssl_session_ticket_xtn, 2);
         if (rv != SECSuccess)
 	    goto loser;
 	if (session_ticket && session_ticket->ticket.data &&
@@ -402,7 +521,8 @@
 
 	if (!ss->sec.isServer) {
 	    TLSExtensionData *xtnData = &ss->xtnData;
-	    xtnData->advertised[xtnData->numAdvertised++] = session_ticket_xtn;
+	    xtnData->advertised[xtnData->numAdvertised++] = 
+		ssl_session_ticket_xtn;
 	}
     } else if (maxBytes < extension_length) {
 	PORT_Assert(0);
@@ -514,15 +634,14 @@
 
     if (append && maxBytes >= extension_length) {
 	SECStatus rv;
-	TLSExtensionData *xtnData;
-	rv = ssl3_AppendHandshakeNumber(ss, next_proto_neg_xtn, 2);
+	rv = ssl3_AppendHandshakeNumber(ss, ssl_next_proto_neg_xtn, 2);
 	if (rv != SECSuccess)
 	    goto loser;
 	rv = ssl3_AppendHandshakeNumber(ss, 0, 2);
 	if (rv != SECSuccess)
 	    goto loser;
-	xtnData = &ss->xtnData;
-	xtnData->advertised[xtnData->numAdvertised++] = next_proto_neg_xtn;
+	ss->xtnData.advertised[ss->xtnData.numAdvertised++] =
+		ssl_next_proto_neg_xtn;
     } else if (maxBytes < extension_length) {
 	return 0;
     }
@@ -575,6 +694,8 @@
     unsigned int         computed_mac_length;
     unsigned char        iv[AES_BLOCK_SIZE];
     SECItem              ivItem;
+    SECItem             *srvName = NULL;
+    PRUint32             srvNameLen = 0;
     CK_MECHANISM_TYPE    msWrapMech = 0; /* dummy default value,
                                           * must be >= 0 */
 
@@ -635,6 +756,11 @@
 	}
 	ms_is_wrapped = PR_TRUE;
     }
+    /* Prep to send negotiated name */
+    srvName = &ss->ssl3.pwSpec->srvVirtName;
+    if (srvName->data && srvName->len) {
+        srvNameLen = 2 + srvName->len; /* len bytes + name len */
+    }
 
     ciphertext_length = 
 	sizeof(PRUint16)                     /* ticket_version */
@@ -649,6 +775,8 @@
 	+ ms_item.len                        /* master_secret */
 	+ 1                                  /* client_auth_type */
 	+ cert_length                        /* cert */
+        + 1                                  /* server name type */
+        + srvNameLen                         /* name len + length field */
 	+ sizeof(ticket.ticket_lifetime_hint);
     padding_length =  AES_BLOCK_SIZE -
 	(ciphertext_length % AES_BLOCK_SIZE);
@@ -731,6 +859,22 @@
 	sizeof(ticket.ticket_lifetime_hint));
     if (rv != SECSuccess) goto loser;
 
+    if (srvNameLen) {
+        /* Name Type (sni_host_name) */
+        rv = ssl3_AppendNumberToItem(&plaintext, srvName->type, 1);
+        if (rv != SECSuccess) goto loser;
+        /* HostName (length and value) */
+        rv = ssl3_AppendNumberToItem(&plaintext, srvName->len, 2);
+        if (rv != SECSuccess) goto loser;
+        rv = ssl3_AppendToItem(&plaintext, srvName->data, srvName->len);
+        if (rv != SECSuccess) goto loser;
+    } else {
+        /* No Name */
+        rv = ssl3_AppendNumberToItem(&plaintext, (char)TLS_STE_NO_SERVER_NAME,
+                                     1);
+        if (rv != SECSuccess) goto loser;
+    }
+
     PORT_Assert(plaintext.len == padding_length);
     for (i = 0; i < padding_length; i++)
 	plaintext.data[i] = (unsigned char)padding_length;
@@ -903,6 +1047,7 @@
 	unsigned int           buffer_len;
 	PRInt32                temp;
 	SECItem                cert_item;
+        PRInt8                 nameType = TLS_STE_NO_SERVER_NAME;
 
 	/* Turn off stateless session resumption if the client sends a
 	 * SessionTicket extension, even if the extension turns out to be
@@ -1155,6 +1300,20 @@
 	    goto no_ticket;
 	parsed_session_ticket->timestamp = (PRUint32)temp;
 
+        /* Read server name */
+        nameType =
+                ssl3_ConsumeHandshakeNumber(ss, 1, &buffer, &buffer_len); 
+        if (nameType != TLS_STE_NO_SERVER_NAME) {
+            SECItem name_item;
+            rv = ssl3_ConsumeHandshakeVariable(ss, &name_item, 2, &buffer,
+                                               &buffer_len);
+            if (rv != SECSuccess) goto no_ticket;
+            rv = SECITEM_CopyItem(NULL, &parsed_session_ticket->srvName,
+                                  &name_item);
+            if (rv != SECSuccess) goto no_ticket;
+            parsed_session_ticket->srvName.type = nameType;
+        }
+
 	/* Done parsing.  Check that all bytes have been consumed. */
 	if (buffer_len != padding_length)
 	    goto no_ticket;
@@ -1211,6 +1370,9 @@
 		    goto loser;
 		}
 	    }
+	    if (parsed_session_ticket->srvName.data != NULL) {
+                sid->u.ssl3.srvName = parsed_session_ticket->srvName;
+            }
 	    ss->statelessResume = PR_TRUE;
 	    ss->sec.ci.sid = sid;
 	}
@@ -1293,8 +1455,15 @@
 SECStatus 
 ssl3_HandleHelloExtensions(sslSocket *ss, SSL3Opaque **b, PRUint32 *length)
 {
-    const ssl3HelloExtensionHandler * handlers =
-	ss->sec.isServer ? clientHelloHandlers : serverHelloHandlers;
+    const ssl3HelloExtensionHandler * handlers;
+
+    if (ss->sec.isServer) {
+        handlers = clientHelloHandlers;
+    } else if (ss->version > SSL_LIBRARY_VERSION_3_0) {
+        handlers = serverHelloHandlersTLS;
+    } else {
+        handlers = serverHelloHandlersSSL3;
+    }
 
     while (*length) {
 	const ssl3HelloExtensionHandler * handler;
@@ -1347,7 +1516,7 @@
     int i;
     ssl3HelloExtensionSender *sender = &ss->xtnData.serverSenders[0];
 
-    for (i = 0; i < MAX_EXTENSIONS; ++i, ++sender) {
+    for (i = 0; i < SSL_MAX_EXTENSIONS; ++i, ++sender) {
         if (!sender->ex_sender) {
 	    sender->ex_type   = ex_type;
 	    sender->ex_sender = cb;
@@ -1360,7 +1529,7 @@
 	    break;
 	}
     }
-    PORT_Assert(i < MAX_EXTENSIONS); /* table needs to grow */
+    PORT_Assert(i < SSL_MAX_EXTENSIONS); /* table needs to grow */
     PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
     return SECFailure;
 }
@@ -1373,10 +1542,12 @@
     PRInt32 total_exten_len = 0;
     int i;
 
-    if (!sender)
-    	sender = &clientHelloSenders[0];
+    if (!sender) {
+    	sender = ss->version > SSL_LIBRARY_VERSION_3_0 ?
+                 &clientHelloSendersTLS[0] : &clientHelloSendersSSL3[0];
+    }
 
-    for (i = 0; i < MAX_EXTENSIONS; ++i, ++sender) {
+    for (i = 0; i < SSL_MAX_EXTENSIONS; ++i, ++sender) {
 	if (sender->ex_sender) {
 	    PRInt32 extLen = (*sender->ex_sender)(ss, append, maxBytes);
 	    if (extLen < 0)
@@ -1387,3 +1558,82 @@
     }
     return total_exten_len;
 }
+
+
+/* Extension format:
+ * Extension number:   2 bytes
+ * Extension length:   2 bytes
+ * Verify Data Length: 1 byte
+ * Verify Data (TLS): 12 bytes (client) or 24 bytes (server)
+ * Verify Data (SSL): 36 bytes (client) or 72 bytes (server)
+ */
+static PRInt32 
+ssl3_SendRenegotiationInfoXtn(
+			sslSocket * ss,
+			PRBool      append,
+			PRUint32    maxBytes)
+{
+    PRInt32 len, needed;
+
+    /* In draft-ietf-tls-renegotiation-03, it is NOT RECOMMENDED to send
+     * both the SCSV and the empty RI, so when we send SCSV in 
+     * the initial handshake, we don't also send RI.
+     */
+    if (!ss || ss->ssl3.hs.sendingSCSV)
+    	return 0;
+    len = !ss->firstHsDone ? 0 : 
+	   (ss->sec.isServer ? ss->ssl3.hs.finishedBytes * 2 
+			     : ss->ssl3.hs.finishedBytes);
+    needed = 5 + len;
+    if (append && maxBytes >= needed) {
+	SECStatus rv;
+	/* extension_type */
+	rv = ssl3_AppendHandshakeNumber(ss, ssl_renegotiation_info_xtn, 2); 
+	if (rv != SECSuccess) return -1;
+	/* length of extension_data */
+	rv = ssl3_AppendHandshakeNumber(ss, len + 1, 2); 
+	if (rv != SECSuccess) return -1;
+	/* verify_Data from previous Finished message(s) */
+	rv = ssl3_AppendHandshakeVariable(ss, 
+		  ss->ssl3.hs.finishedMsgs.data, len, 1);
+	if (rv != SECSuccess) return -1;
+	if (!ss->sec.isServer) {
+	    TLSExtensionData *xtnData = &ss->xtnData;
+	    xtnData->advertised[xtnData->numAdvertised++] = 
+	                                           ssl_renegotiation_info_xtn;
+	}
+    }
+    return needed;
+}
+
+/* This function runs in both the client and server.  */
+static SECStatus
+ssl3_HandleRenegotiationInfoXtn(sslSocket *ss, PRUint16 ex_type, SECItem *data)
+{
+    SECStatus rv = SECSuccess;
+    PRUint32 len = 0;
+
+    if (ss->firstHsDone) {
+	len = ss->sec.isServer ? ss->ssl3.hs.finishedBytes 
+	                       : ss->ssl3.hs.finishedBytes * 2;
+    }
+    if (data->len != 1 + len  ||
+	data->data[0] != len  || (len && 
+	NSS_SecureMemcmp(ss->ssl3.hs.finishedMsgs.data,
+	                 data->data + 1, len))) {
+	/* Can we do this here? Or, must we arrange for the caller to do it? */
+	(void)SSL3_SendAlert(ss, alert_fatal, handshake_failure);
+	PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
+	return SECFailure;
+    }
+    /* remember that we got this extension and it was correct. */
+    ss->peerRequestedProtection = 1;
+    ss->xtnData.negotiated[ss->xtnData.numNegotiated++] = ex_type;
+    if (ss->sec.isServer) {
+	/* prepare to send back the appropriate response */
+	rv = ssl3_RegisterServerHelloExtensionSender(ss, ex_type,
+					     ssl3_SendRenegotiationInfoXtn);
+    }
+    return rv;
+}
+
diff --git a/net/third_party/nss/ssl/ssl3gthr.c b/net/third_party/nss/ssl/ssl3gthr.c
index bdd2958..6712370 100644
--- a/net/third_party/nss/ssl/ssl3gthr.c
+++ b/net/third_party/nss/ssl/ssl3gthr.c
@@ -188,6 +188,7 @@
 {
     SSL3Ciphertext cText;
     int            rv;
+    PRBool         canFalseStart = PR_FALSE;
 
     PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
     do {
@@ -207,7 +208,20 @@
 	if (rv < 0) {
 	    return ss->recvdCloseNotify ? 0 : rv;
 	}
-    } while (ss->ssl3.hs.ws != idle_handshake && ss->gs.buf.len == 0);
+
+	/* If we kicked off a false start in ssl3_HandleServerHelloDone, break
+	 * out of this loop early without finishing the handshake.
+	 */
+	if (ss->opt.enableFalseStart) {
+	    ssl_GetSSL3HandshakeLock(ss);
+	    canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher ||
+			     ss->ssl3.hs.ws == wait_new_session_ticket) &&
+		            ssl3_CanFalseStart(ss);
+	    ssl_ReleaseSSL3HandshakeLock(ss);
+	}
+    } while (ss->ssl3.hs.ws != idle_handshake &&
+             !canFalseStart &&
+             ss->gs.buf.len == 0);
 
     ss->gs.readOffset = 0;
     ss->gs.writeOffset = ss->gs.buf.len;
diff --git a/net/third_party/nss/ssl/ssl3prot.h b/net/third_party/nss/ssl/ssl3prot.h
index 84d73a9..c82c891 100644
--- a/net/third_party/nss/ssl/ssl3prot.h
+++ b/net/third_party/nss/ssl/ssl3prot.h
@@ -38,7 +38,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: ssl3prot.h,v 1.15 2009/11/07 18:23:06 wtc%google.com Exp $ */
+/* $Id: ssl3prot.h,v 1.18 2010/02/03 02:25:35 alexei.volkov.bugs%sun.com Exp $ */
 
 #ifndef __ssl3proto_h_
 #define __ssl3proto_h_
@@ -344,20 +344,8 @@
     unsigned char *mac;
 } EncryptedSessionTicket;
 
-/* Supported extensions. */
-/* Update MAX_EXTENSIONS whenever a new extension type is added. */
-typedef enum {
-    server_name_xtn              = 0,
-#ifdef NSS_ENABLE_ECC
-    elliptic_curves_xtn          = 10,
-    ec_point_formats_xtn         = 11,
-#endif
-    session_ticket_xtn           = 35,
-    next_proto_neg_xtn           = 13172
-} ExtensionType;
-
-#define MAX_EXTENSIONS             5
-
 #define TLS_EX_SESS_TICKET_MAC_LENGTH       32
 
+#define TLS_STE_NO_SERVER_NAME        -1
+
 #endif /* __ssl3proto_h_ */
diff --git a/net/third_party/nss/ssl/sslcon.c b/net/third_party/nss/ssl/sslcon.c
index 500b787..c02b315 100644
--- a/net/third_party/nss/ssl/sslcon.c
+++ b/net/third_party/nss/ssl/sslcon.c
@@ -37,7 +37,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslcon.c,v 1.37 2009/10/16 17:45:35 wtc%google.com Exp $ */
+/* $Id: sslcon.c,v 1.39 2010/02/04 03:08:44 wtc%google.com Exp $ */
 
 #include "nssrenam.h"
 #include "cert.h"
@@ -3007,6 +3007,7 @@
     unsigned int      i;
     int               sendLen, sidLen = 0;
     SECStatus         rv;
+    TLSExtensionData  *xtnData;
 
     PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
 
@@ -3151,7 +3152,8 @@
     localCipherSpecs = ss->cipherSpecs;
     localCipherSize  = ss->sizeCipherSpecs;
 
-    sendLen = SSL_HL_CLIENT_HELLO_HBYTES + localCipherSize + sidLen +
+    /* Add 3 for SCSV */
+    sendLen = SSL_HL_CLIENT_HELLO_HBYTES + localCipherSize + 3 + sidLen +
 	SSL_CHALLENGE_BYTES;
 
     /* Generate challenge bytes for server */
@@ -3176,8 +3178,9 @@
     
     msg[1] = MSB(ss->clientHelloVersion);
     msg[2] = LSB(ss->clientHelloVersion);
-    msg[3] = MSB(localCipherSize);
-    msg[4] = LSB(localCipherSize);
+    /* Add 3 for SCSV */
+    msg[3] = MSB(localCipherSize + 3);
+    msg[4] = LSB(localCipherSize + 3);
     msg[5] = MSB(sidLen);
     msg[6] = LSB(sidLen);
     msg[7] = MSB(SSL_CHALLENGE_BYTES);
@@ -3185,6 +3188,16 @@
     cp += SSL_HL_CLIENT_HELLO_HBYTES;
     PORT_Memcpy(cp, localCipherSpecs, localCipherSize);
     cp += localCipherSize;
+    /*
+     * Add SCSV.  SSL 2.0 cipher suites are listed before SSL 3.0 cipher
+     * suites in localCipherSpecs for compatibility with SSL 2.0 servers.
+     * Since SCSV looks like an SSL 3.0 cipher suite, we can't add it at
+     * the beginning.
+     */
+    cp[0] = 0x00;
+    cp[1] = 0x00;
+    cp[2] = 0xff;
+    cp += 3;
     if (sidLen) {
 	PORT_Memcpy(cp, sid->u.ssl2.sessionID, sidLen);
 	cp += sidLen;
@@ -3207,6 +3220,14 @@
 	goto loser;
     }
 
+    /*
+     * Since we sent the SCSV, pretend we sent empty RI extension.  We need
+     * to record the extension has been advertised after ssl3_InitState has
+     * been called, which ssl3_StartHandshakeHash took care for us above.
+     */
+    xtnData = &ss->xtnData;
+    xtnData->advertised[xtnData->numAdvertised++] = ssl_renegotiation_info_xtn;
+
     /* Setup to receive servers hello message */
     ssl_GetRecvBufLock(ss);
     ss->gs.recordLen = 0;
diff --git a/net/third_party/nss/ssl/sslenum.c b/net/third_party/nss/ssl/sslenum.c
index fa834f9..b8aa8cc 100644
--- a/net/third_party/nss/ssl/sslenum.c
+++ b/net/third_party/nss/ssl/sslenum.c
@@ -39,7 +39,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslenum.c,v 1.16 2008/12/17 06:09:19 nelson%bolyard.com Exp $ */
+/* $Id: sslenum.c,v 1.17 2010/02/10 18:07:21 wtc%google.com Exp $ */
 
 #include "ssl.h"
 #include "sslproto.h"
@@ -54,6 +54,9 @@
  * such as AES and RC4 to allow servers that prefer Camellia to negotiate
  * Camellia without having to disable AES and RC4, which are needed for
  * interoperability with clients that don't yet implement Camellia.
+ *
+ * If new ECC cipher suites are added, also update the ssl3CipherSuite arrays
+ * in ssl3ecc.c.
  */
 const PRUint16 SSL_ImplementedCiphers[] = {
     /* 256-bit */
@@ -149,3 +152,14 @@
 const PRUint16 SSL_NumImplementedCiphers = 
     (sizeof SSL_ImplementedCiphers) / (sizeof SSL_ImplementedCiphers[0]) - 1;
 
+const PRUint16 *
+SSL_GetImplementedCiphers(void)
+{
+    return SSL_ImplementedCiphers;
+}
+
+PRUint16
+SSL_GetNumImplementedCiphers(void)
+{
+    return SSL_NumImplementedCiphers;
+}
diff --git a/net/third_party/nss/ssl/sslerr.h b/net/third_party/nss/ssl/sslerr.h
index c132ab9..61b721c 100644
--- a/net/third_party/nss/ssl/sslerr.h
+++ b/net/third_party/nss/ssl/sslerr.h
@@ -36,7 +36,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslerr.h,v 1.8 2009/11/06 20:11:28 nelson%bolyard.com Exp $ */
+/* $Id: sslerr.h,v 1.10 2010/02/03 03:44:29 wtc%google.com Exp $ */
 #ifndef __SSL_ERR_H_
 #define __SSL_ERR_H_
 
@@ -197,6 +197,9 @@
 
 SSL_ERROR_DECOMPRESSION_FAILURE		= (SSL_ERROR_BASE + 111),
 SSL_ERROR_RENEGOTIATION_NOT_ALLOWED     = (SSL_ERROR_BASE + 112),
+SSL_ERROR_UNSAFE_NEGOTIATION            = (SSL_ERROR_BASE + 113),
+
+SSL_ERROR_RX_UNEXPECTED_UNCOMPRESSED_RECORD	= (SSL_ERROR_BASE + 114),
 
 SSL_ERROR_END_OF_LIST	/* let the c compiler determine the value of this. */
 } SSLErrorCodes;
diff --git a/net/third_party/nss/ssl/sslimpl.h b/net/third_party/nss/ssl/sslimpl.h
index 6f2316a..fe7ac7a 100644
--- a/net/third_party/nss/ssl/sslimpl.h
+++ b/net/third_party/nss/ssl/sslimpl.h
@@ -39,7 +39,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslimpl.h,v 1.70 2009/11/21 03:40:49 wtc%google.com Exp $ */
+/* $Id: sslimpl.h,v 1.77 2010/02/10 00:33:50 wtc%google.com Exp $ */
 
 #ifndef __sslimpl_h_
 #define __sslimpl_h_
@@ -130,14 +130,10 @@
 #define SSL_DBG(b)
 #endif
 
-#ifdef macintosh
-#include "pprthred.h"
-#else
 #include "private/pprthred.h"	/* for PR_InMonitor() */
-#endif
 #define ssl_InMonitor(m) PZ_InMonitor(m)
 
-#define LSB(x) ((unsigned char) (x & 0xff))
+#define LSB(x) ((unsigned char) ((x) & 0xff))
 #define MSB(x) ((unsigned char) (((unsigned)(x)) >> 8))
 
 /************************************************************************/
@@ -342,6 +338,7 @@
     unsigned int enableDeflate          : 1;  /* 19 */
     unsigned int enableRenegotiation    : 2;  /* 20-21 */
     unsigned int requireSafeNegotiation : 1;  /* 22 */
+    unsigned int enableFalseStart       : 1;  /* 23 */
 } sslOptions;
 
 typedef enum { sslHandshakingUndetermined = 0,
@@ -561,6 +558,9 @@
     SECItem            msItem;
     unsigned char      key_block[NUM_MIXERS * MD5_LENGTH];
     unsigned char      raw_master_secret[56];
+    SECItem            srvVirtName;    /* for server: name that was negotiated
+                                        * with a client. For client - is
+                                        * always set to NULL.*/
 } ssl3CipherSpec;
 
 typedef enum {	never_cached, 
@@ -569,10 +569,13 @@
 		invalid_cache		/* no longer in any cache. */
 } Cached;
 
+#define MAX_PEER_CERT_CHAIN_SIZE 8
+
 struct sslSessionIDStr {
     sslSessionID *        next;   /* chain used for client sockets, only */
 
     CERTCertificate *     peerCert;
+    CERTCertificate *     peerCertChain[MAX_PEER_CERT_CHAIN_SIZE];
     const char *          peerID;     /* client only */
     const char *          urlSvrName; /* client only */
     CERTCertificate *     localCert;
@@ -656,6 +659,7 @@
 	     * ClientHello message.  This field is used by clients.
 	     */
 	    NewSessionTicket  sessionTicket;
+            SECItem           srvName;
 	} ssl3;
     } u;
 };
@@ -730,16 +734,23 @@
 
 struct TLSExtensionDataStr {
     /* registered callbacks that send server hello extensions */
-    ssl3HelloExtensionSender serverSenders[MAX_EXTENSIONS];
+    ssl3HelloExtensionSender serverSenders[SSL_MAX_EXTENSIONS];
     /* Keep track of the extensions that are negotiated. */
     PRUint16 numAdvertised;
     PRUint16 numNegotiated;
-    PRUint16 advertised[MAX_EXTENSIONS];
-    PRUint16 negotiated[MAX_EXTENSIONS];
+    PRUint16 advertised[SSL_MAX_EXTENSIONS];
+    PRUint16 negotiated[SSL_MAX_EXTENSIONS];
 
     /* SessionTicket Extension related data. */
     PRBool ticketTimestampVerified;
     PRBool emptySessionTicket;
+
+    /* SNI Extension related data
+     * Names data is not coppied from the input buffer. It can not be
+     * used outside the scope where input buffer is defined and that
+     * is beyond ssl3_HandleClientHello function. */
+    SECItem *sniNameArr;
+    PRUint32 sniNameArrSize;
 };
 
 /*
@@ -770,9 +781,16 @@
     PRBool                rehandshake; /* immediately start another handshake 
                                         * when this one finishes */
     PRBool                usedStepDownKey;  /* we did a server key exchange. */
+    PRBool                sendingSCSV; /* instead of empty RI */
     sslBuffer             msgState;    /* current state for handshake messages*/
                                        /* protected by recvBufLock */
     sslBuffer             messages;    /* Accumulated handshake messages */
+    PRUint16              finishedBytes; /* size of single finished below */
+    union {
+	TLSFinished       tFinished[2]; /* client, then server */
+	SSL3Hashes        sFinished[2];
+	SSL3Opaque        data[72];
+    }                     finishedMsgs;
 #ifdef NSS_ENABLE_ECC
     PRUint32              negotiatedECCurves; /* bit mask */
 #endif /* NSS_ENABLE_ECC */
@@ -877,6 +895,7 @@
     ClientIdentity        client_identity;
     SECItem               peer_cert;
     uint32                timestamp;
+    SECItem               srvName; /* negotiated server name */
 }  SessionTicket;
 
 /*
@@ -1022,6 +1041,7 @@
     unsigned long    recvdCloseNotify;    /* received SSL EOF. */
     unsigned long    TCPconnected;       
     unsigned long    appDataBuffered;
+    unsigned long    peerRequestedProtection; /* from old renegotiation */
 
     /* version of the protocol to use */
     SSL3ProtocolVersion version;
@@ -1050,6 +1070,8 @@
     void                     *authCertificateArg;
     SSLGetClientAuthData      getClientAuthData;
     void                     *getClientAuthDataArg;
+    SSLSNISocketConfig        sniSocketConfig;
+    void                     *sniSocketConfigArg;
     SSLBadCertHandler         handleBadCert;
     void                     *badCertArg;
     SSLHandshakeCallback      handshakeCallback;
@@ -1130,6 +1152,7 @@
 extern char                    ssl_debug;
 extern char                    ssl_trace;
 extern FILE *                  ssl_trace_iob;
+extern FILE *                  ssl_keylog_iob;
 extern CERTDistNames *         ssl3_server_ca_list;
 extern PRUint32                ssl_sid_timeout;
 extern PRUint32                ssl3_sid_timeout;
@@ -1247,6 +1270,8 @@
 
 extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled);
 
+extern PRBool    ssl3_CanFalseStart(sslSocket *ss);
+
 #define SSL_LOCK_READER(ss)		if (ss->recvLock) PZ_Lock(ss->recvLock)
 #define SSL_UNLOCK_READER(ss)		if (ss->recvLock) PZ_Unlock(ss->recvLock)
 #define SSL_LOCK_WRITER(ss)		if (ss->sendLock) PZ_Lock(ss->sendLock)
@@ -1501,6 +1526,23 @@
  */
 extern PRInt32 ssl3_SendSessionTicketXtn(sslSocket *ss, PRBool append,
 			PRUint32 maxBytes);
+
+/* ClientHello and ServerHello extension senders.
+ * The code is in ssl3ext.c.
+ */
+extern PRInt32 ssl3_SendServerNameXtn(sslSocket *ss, PRBool append,
+                     PRUint32 maxBytes);
+
+/* Assigns new cert, cert chain and keys to ss->serverCerts
+ * struct. If certChain is NULL, tries to find one. Aborts if
+ * fails to do so. If cert and keyPair are NULL - unconfigures
+ * sslSocket of kea type.*/
+extern SECStatus ssl_ConfigSecureServer(sslSocket *ss, CERTCertificate *cert,
+                                        CERTCertificateList *certChain,
+                                        ssl3KeyPair *keyPair, SSLKEAType kea);
+/* Return key type for the cert */
+extern SSLKEAType ssl_FindCertKEAType(CERTCertificate * cert);
+
 #ifdef NSS_ENABLE_ECC
 extern PRInt32 ssl3_SendSupportedCurvesXtn(sslSocket *ss,
 			PRBool append, PRUint32 maxBytes);
diff --git a/net/third_party/nss/ssl/sslinfo.c b/net/third_party/nss/ssl/sslinfo.c
index baa1ab3..e4ee35f 100644
--- a/net/third_party/nss/ssl/sslinfo.c
+++ b/net/third_party/nss/ssl/sslinfo.c
@@ -34,7 +34,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslinfo.c,v 1.21 2009/11/09 22:00:18 wtc%google.com Exp $ */
+/* $Id: sslinfo.c,v 1.23 2010/01/15 01:49:33 alexei.volkov.bugs%sun.com Exp $ */
 #include "ssl.h"
 #include "sslimpl.h"
 #include "sslproto.h"
@@ -307,3 +307,43 @@
     }
     return PR_FALSE;
 }
+
+SECItem*
+SSL_GetNegotiatedHostInfo(PRFileDesc *fd)
+{
+    SECItem *sniName = NULL;
+    sslSocket *ss;
+    char *name = NULL;
+
+    ss = ssl_FindSocket(fd);
+    if (!ss) {
+	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetNegotiatedHostInfo",
+		 SSL_GETPID(), fd));
+	return NULL;
+    }
+
+    if (ss->sec.isServer) {
+        if (ss->version > SSL_LIBRARY_VERSION_3_0 &&
+            ss->ssl3.initialized) { /* TLS */
+            SECItem *crsName;
+            ssl_GetSpecReadLock(ss); /*********************************/
+            crsName = &ss->ssl3.crSpec->srvVirtName;
+            if (crsName->data) {
+                sniName = SECITEM_DupItem(crsName);
+            }
+            ssl_ReleaseSpecReadLock(ss); /*----------------------------*/
+        }
+        return sniName;
+    } 
+    name = SSL_RevealURL(fd);
+    if (name) {
+        sniName = PORT_ZNew(SECItem);
+        if (!sniName) {
+            PORT_Free(name);
+            return NULL;
+        }
+        sniName->data = (void*)name;
+        sniName->len  = PORT_Strlen(name);
+    }
+    return sniName;
+}
diff --git a/net/third_party/nss/ssl/sslnonce.c b/net/third_party/nss/ssl/sslnonce.c
index 63dc5a2..64adc1f 100644
--- a/net/third_party/nss/ssl/sslnonce.c
+++ b/net/third_party/nss/ssl/sslnonce.c
@@ -197,6 +197,7 @@
 static void
 ssl_DestroySID(sslSessionID *sid)
 {
+    int i;
     SSL_TRC(8, ("SSL: destroy sid: sid=0x%x cached=%d", sid, sid->cached));
     PORT_Assert((sid->references == 0));
 
@@ -216,6 +217,9 @@
     if ( sid->peerCert ) {
 	CERT_DestroyCertificate(sid->peerCert);
     }
+    for (i = 0; i < MAX_PEER_CERT_CHAIN_SIZE && sid->peerCertChain[i]; i++) {
+	CERT_DestroyCertificate(sid->peerCertChain[i]);
+    }
     if ( sid->localCert ) {
 	CERT_DestroyCertificate(sid->localCert);
     }
diff --git a/net/third_party/nss/ssl/sslproto.h b/net/third_party/nss/ssl/sslproto.h
index b0b466f..bf7b71b 100644
--- a/net/third_party/nss/ssl/sslproto.h
+++ b/net/third_party/nss/ssl/sslproto.h
@@ -39,7 +39,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslproto.h,v 1.13 2008/12/17 06:09:19 nelson%bolyard.com Exp $ */
+/* $Id: sslproto.h,v 1.14 2010/01/28 06:19:12 nelson%bolyard.com Exp $ */
 
 #ifndef __sslproto_h_
 #define __sslproto_h_
@@ -181,6 +181,15 @@
 
 #define TLS_RSA_WITH_SEED_CBC_SHA		0x0096
 
+/* TLS "Signalling Cipher Suite Value" (SCSV). May be requested by client.
+ * Must NEVER be chosen by server.  SSL 3.0 server acknowledges by sending
+ * back an empty Renegotiation Info (RI) server hello extension.
+ */
+#define TLS_RENEGO_PROTECTION_REQUEST		0x00FF
+
+/* Cipher Suite Values starting with 0xC000 are defined in informational
+ * RFCs.
+ */
 #define TLS_ECDH_ECDSA_WITH_NULL_SHA            0xC001
 #define TLS_ECDH_ECDSA_WITH_RC4_128_SHA         0xC002
 #define TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA    0xC003
diff --git a/net/third_party/nss/ssl/sslreveal.c b/net/third_party/nss/ssl/sslreveal.c
index a981dee..74f8814 100644
--- a/net/third_party/nss/ssl/sslreveal.c
+++ b/net/third_party/nss/ssl/sslreveal.c
@@ -36,7 +36,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslreveal.c,v 1.4 2004/04/27 23:04:39 gerv%gerv.net Exp $ */
+/* $Id: sslreveal.c,v 1.7 2010/02/04 03:21:11 wtc%google.com Exp $ */
 
 #include "cert.h"
 #include "ssl.h"
@@ -82,7 +82,7 @@
 
 
 /* given PRFileDesc, returns a pointer to the URL associated with the socket
- * the caller should free url when done  
+ * the caller should free url when done
  */
 char * 
 SSL_RevealURL(PRFileDesc * fd)
@@ -98,3 +98,41 @@
   return url;
 }
 
+
+/* given PRFileDesc, returns status information related to extensions 
+ * negotiated with peer during the handshake.
+ */
+
+SECStatus
+SSL_HandshakeNegotiatedExtension(PRFileDesc * socket, 
+                                 SSLExtensionType extId,
+                                 PRBool *pYes)
+{
+  /* some decisions derived from SSL_GetChannelInfo */
+  sslSocket * sslsocket = NULL;
+  SECStatus rv = SECFailure;
+
+  if (!pYes)
+    return rv;
+
+  sslsocket = ssl_FindSocket(socket);
+
+  /* according to public API SSL_GetChannelInfo, this doesn't need a lock */
+  if (sslsocket && sslsocket->opt.useSecurity && sslsocket->firstHsDone) {
+    if (sslsocket->ssl3.initialized) { /* SSL3 and TLS */
+      /* now we know this socket went through ssl3_InitState() and
+       * ss->xtnData got initialized, which is the only member accessed by
+       * ssl3_ExtensionNegotiated();
+       * Member xtnData appears to get accessed in functions that handle
+       * the handshake (hello messages and extension sending),
+       * therefore the handshake lock should be sufficient.
+       */
+      ssl_GetSSL3HandshakeLock(sslsocket);
+      *pYes = ssl3_ExtensionNegotiated(sslsocket, extId);
+      ssl_ReleaseSSL3HandshakeLock(sslsocket);
+      rv = SECSuccess;
+    }
+  }
+
+  return rv;
+}
diff --git a/net/third_party/nss/ssl/sslsecur.c b/net/third_party/nss/ssl/sslsecur.c
index c13100a..49a81bc 100644
--- a/net/third_party/nss/ssl/sslsecur.c
+++ b/net/third_party/nss/ssl/sslsecur.c
@@ -37,7 +37,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslsecur.c,v 1.42 2008/10/03 19:20:20 wtc%google.com Exp $ */
+/* $Id: sslsecur.c,v 1.43 2010/01/14 22:15:25 alexei.volkov.bugs%sun.com Exp $ */
 #include "cert.h"
 #include "secitem.h"
 #include "keyhi.h"
@@ -672,6 +672,79 @@
     return PR_FAILURE;
 }
 
+SECStatus
+ssl_ConfigSecureServer(sslSocket *ss, CERTCertificate *cert,
+                       CERTCertificateList *certChain,
+                       ssl3KeyPair *keyPair, SSLKEAType kea)
+{
+    CERTCertificateList *localCertChain = NULL;
+    sslServerCerts  *sc = ss->serverCerts + kea;
+
+    /* load the server certificate */
+    if (sc->serverCert != NULL) {
+	CERT_DestroyCertificate(sc->serverCert);
+    	sc->serverCert = NULL;
+        sc->serverKeyBits = 0;
+    }
+    /* load the server cert chain */
+    if (sc->serverCertChain != NULL) {
+	CERT_DestroyCertificateList(sc->serverCertChain);
+    	sc->serverCertChain = NULL;
+    }
+    if (cert) {
+        sc->serverCert = CERT_DupCertificate(cert);
+        /* get the size of the cert's public key, and remember it */
+        sc->serverKeyBits = SECKEY_PublicKeyStrengthInBits(keyPair->pubKey);
+        if (!certChain) {
+            localCertChain =
+                CERT_CertChainFromCert(sc->serverCert, certUsageSSLServer,
+                                       PR_TRUE);
+            if (!localCertChain)
+                goto loser;
+        }
+        sc->serverCertChain = (certChain) ? CERT_DupCertList(certChain) :
+                                            localCertChain;
+        if (!sc->serverCertChain) {
+            goto loser;
+        }
+        localCertChain = NULL;      /* consumed */
+    }
+
+    /* get keyPair */
+    if (sc->serverKeyPair != NULL) {
+        ssl3_FreeKeyPair(sc->serverKeyPair);
+        sc->serverKeyPair = NULL;
+    }
+    if (keyPair) {
+        SECKEY_CacheStaticFlags(keyPair->privKey);
+        sc->serverKeyPair = ssl3_GetKeyPairRef(keyPair);
+    }
+    if (kea == kt_rsa && cert && sc->serverKeyBits > 512 &&
+        !ss->opt.noStepDown && !ss->stepDownKeyPair) { 
+        if (ssl3_CreateRSAStepDownKeys(ss) != SECSuccess) {
+            goto loser;
+        }
+    }
+    return SECSuccess;
+
+loser:
+    if (localCertChain) {
+        CERT_DestroyCertificateList(localCertChain);
+    }
+    if (sc->serverCert != NULL) {
+	CERT_DestroyCertificate(sc->serverCert);
+	sc->serverCert = NULL;
+    }
+    if (sc->serverCertChain != NULL) {
+	CERT_DestroyCertificateList(sc->serverCertChain);
+	sc->serverCertChain = NULL;
+    }
+    if (sc->serverKeyPair != NULL) {
+	ssl3_FreeKeyPair(sc->serverKeyPair);
+	sc->serverKeyPair = NULL;
+    }
+    return SECFailure;
+}
 
 /* XXX need to protect the data that gets changed here.!! */
 
@@ -679,10 +752,10 @@
 SSL_ConfigSecureServer(PRFileDesc *fd, CERTCertificate *cert,
 		       SECKEYPrivateKey *key, SSL3KEAType kea)
 {
-    SECStatus rv;
     sslSocket *ss;
-    sslServerCerts  *sc;
-    SECKEYPublicKey * pubKey = NULL;
+    SECKEYPublicKey *pubKey = NULL;
+    ssl3KeyPair *keyPair = NULL;
+    SECStatus rv = SECFailure;
 
     ss = ssl_FindSocket(fd);
     if (!ss) {
@@ -708,41 +781,13 @@
 	return SECFailure;
     }
 
-    sc = ss->serverCerts + kea;
-    /* load the server certificate */
-    if (sc->serverCert != NULL) {
-	CERT_DestroyCertificate(sc->serverCert);
-    	sc->serverCert = NULL;
-    }
     if (cert) {
-	sc->serverCert = CERT_DupCertificate(cert);
-	if (!sc->serverCert)
-	    goto loser;
     	/* get the size of the cert's public key, and remember it */
 	pubKey = CERT_ExtractPublicKey(cert);
 	if (!pubKey) 
-	    goto loser;
-	sc->serverKeyBits = SECKEY_PublicKeyStrengthInBits(pubKey);
+            return SECFailure;
     }
 
-
-    /* load the server cert chain */
-    if (sc->serverCertChain != NULL) {
-	CERT_DestroyCertificateList(sc->serverCertChain);
-    	sc->serverCertChain = NULL;
-    }
-    if (cert) {
-	sc->serverCertChain = CERT_CertChainFromCert(
-			    sc->serverCert, certUsageSSLServer, PR_TRUE);
-	if (sc->serverCertChain == NULL)
-	     goto loser;
-    }
-
-    /* load the private key */
-    if (sc->serverKeyPair != NULL) {
-        ssl3_FreeKeyPair(sc->serverKeyPair);
-        sc->serverKeyPair = NULL;
-    }
     if (key) {
 	SECKEYPrivateKey * keyCopy	= NULL;
 	CK_MECHANISM_TYPE  keyMech	= CKM_INVALID_MECHANISM;
@@ -770,51 +815,34 @@
 	    keyCopy = SECKEY_CopyPrivateKey(key);
 	if (keyCopy == NULL)
 	    goto loser;
-	SECKEY_CacheStaticFlags(keyCopy);
-        sc->serverKeyPair = ssl3_NewKeyPair(keyCopy, pubKey);
-        if (sc->serverKeyPair == NULL) {
+        keyPair = ssl3_NewKeyPair(keyCopy, pubKey);
+        if (keyPair == NULL) {
             SECKEY_DestroyPrivateKey(keyCopy);
             goto loser;
         }
 	pubKey = NULL; /* adopted by serverKeyPair */
     }
-
-    if (kea == kt_rsa && cert && sc->serverKeyBits > 512) {
-	if (ss->opt.noStepDown) {
-	    /* disable all export ciphersuites */
-	} else {
-	    rv = ssl3_CreateRSAStepDownKeys(ss);
-	    if (rv != SECSuccess) {
-		return SECFailure; /* err set by ssl3_CreateRSAStepDownKeys */
-	    }
-	}
+    if (ssl_ConfigSecureServer(ss, cert, NULL,
+                               keyPair, kea) == SECFailure) {
+        goto loser;
     }
 
     /* Only do this once because it's global. */
     if (PR_SUCCESS == PR_CallOnceWithArg(&setupServerCAListOnce, 
                                          &serverCAListSetup,
                                          (void *)(ss->dbHandle))) {
-	return SECSuccess;
+        rv = SECSuccess;
     }
 
 loser:
+    if (keyPair) {
+        ssl3_FreeKeyPair(keyPair);
+    }
     if (pubKey) {
 	SECKEY_DestroyPublicKey(pubKey); 
 	pubKey = NULL;
     }
-    if (sc->serverCert != NULL) {
-	CERT_DestroyCertificate(sc->serverCert);
-	sc->serverCert = NULL;
-    }
-    if (sc->serverCertChain != NULL) {
-	CERT_DestroyCertificateList(sc->serverCertChain);
-	sc->serverCertChain = NULL;
-    }
-    if (sc->serverKeyPair != NULL) {
-	ssl3_FreeKeyPair(sc->serverKeyPair);
-	sc->serverKeyPair = NULL;
-    }
-    return SECFailure;
+    return rv;
 }
 
 /************************************************************************/
@@ -1171,8 +1199,17 @@
     	ss->writerThread = PR_GetCurrentThread();
     /* If any of these is non-zero, the initial handshake is not done. */
     if (!ss->firstHsDone) {
+	PRBool canFalseStart = PR_FALSE;
 	ssl_Get1stHandshakeLock(ss);
-	if (ss->handshake || ss->nextHandshake || ss->securityHandshake) {
+	if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
+	    (ss->ssl3.hs.ws == wait_change_cipher ||
+	     ss->ssl3.hs.ws == wait_finished ||
+	     ss->ssl3.hs.ws == wait_new_session_ticket) &&
+	    ssl3_CanFalseStart(ss)) {
+	    canFalseStart = PR_TRUE;
+	}
+	if (!canFalseStart &&
+	    (ss->handshake || ss->nextHandshake || ss->securityHandshake)) {
 	    rv = ssl_Do1stHandshake(ss);
 	}
 	ssl_Release1stHandshakeLock(ss);
@@ -1241,7 +1278,8 @@
 
 /*
  * Allow the application to pass the url or hostname into the SSL library
- * so that we can do some checking on it.
+ * so that we can do some checking on it. It will be used for the value in
+ * SNI extension of client hello message.
  */
 SECStatus
 SSL_SetURL(PRFileDesc *fd, const char *url)
@@ -1273,6 +1311,46 @@
 }
 
 /*
+ * Allow the application to pass the set of trust anchors
+ */
+SECStatus
+SSL_SetTrustAnchors(PRFileDesc *fd, CERTCertList *certList)
+{
+    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+    PR_NOT_REACHED("not implemented");
+    return SECFailure;
+#if 0
+    sslSocket *   ss = ssl_FindSocket(fd);
+    CERTDistNames *names = NULL;
+
+    if (!certList) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+    if (!ss) {
+	SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetTrustAnchors",
+		 SSL_GETPID(), fd));
+	return SECFailure;
+    }
+
+    names = CERT_DistNamesFromCertList(certList);
+    if (names == NULL) {
+        return SECFailure;
+    }
+    ssl_Get1stHandshakeLock(ss);
+    ssl_GetSSL3HandshakeLock(ss);
+    if (ss->ssl3.ca_list) {
+        CERT_FreeDistNames(ss->ssl3.ca_list);
+    }
+    ss->ssl3.ca_list = names;
+    ssl_ReleaseSSL3HandshakeLock(ss);
+    ssl_Release1stHandshakeLock(ss);
+
+    return SECSuccess;
+#endif
+}
+
+/*
 ** Returns Negative number on error, zero or greater on success.
 ** Returns the amount of data immediately available to be read.
 */
@@ -1440,3 +1518,22 @@
     ssl_Release1stHandshakeLock(ss);
     return rv;
 }
+
+/* For more info see ssl.h */
+SECStatus 
+SSL_SNISocketConfigHook(PRFileDesc *fd, SSLSNISocketConfig func,
+                        void *arg)
+{
+    sslSocket *ss;
+
+    ss = ssl_FindSocket(fd);
+    if (!ss) {
+	SSL_DBG(("%d: SSL[%d]: bad socket in SNISocketConfigHook",
+		 SSL_GETPID(), fd));
+	return SECFailure;
+    }
+
+    ss->sniSocketConfig = func;
+    ss->sniSocketConfigArg = arg;
+    return SECSuccess;
+}
diff --git a/net/third_party/nss/ssl/sslsnce.c b/net/third_party/nss/ssl/sslsnce.c
index 115766c..5658dc2 100644
--- a/net/third_party/nss/ssl/sslsnce.c
+++ b/net/third_party/nss/ssl/sslsnce.c
@@ -36,7 +36,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslsnce.c,v 1.51 2009/11/07 18:23:06 wtc%google.com Exp $ */
+/* $Id: sslsnce.c,v 1.52 2010/01/14 22:15:25 alexei.volkov.bugs%sun.com Exp $ */
 
 /* Note: ssl_FreeSID() in sslnonce.c gets used for both client and server 
  * cache sids!
@@ -71,6 +71,8 @@
  *     encKeyCacheEntry         ticketEncKey; // Wrapped in non-bypass mode
  *     encKeyCacheEntry         ticketMacKey; // Wrapped in non-bypass mode
  *     PRBool                   ticketKeysValid;
+ *     sidCacheLock             srvNameCacheLock;
+ *     srvNameCacheEntry        srvNameData[ numSrvNameCacheEntries ];
  * } cacheMemCacheData;
  */
 #include "seccomon.h"
@@ -84,6 +86,7 @@
 #include "pk11func.h"
 #include "base64.h"
 #include "keyhi.h"
+#include "blapi.h"
 
 #include <stdio.h>
 
@@ -150,10 +153,12 @@
 /*  4 */    PRUint32    masterWrapMech; 
 /*  4 */    SSL3KEAType exchKeyType;
 /*  4 */    PRInt32     certIndex;
-/*116 */} ssl3;
+/*  4 */    PRInt32     srvNameIndex;
+/* 32 */    PRUint8     srvNameHash[SHA256_LENGTH]; /* SHA256 name hash */
+/*152 */} ssl3;
 /* force sizeof(sidCacheEntry) to be a multiple of cache line size */
         struct {
-/*120 */    PRUint8     filler[120]; /* 72+120==196, a multiple of 16 */
+/*152 */    PRUint8     filler[120]; /* 72+152==224, a multiple of 16 */
 	} forceSize;
     } u;
 };
@@ -186,6 +191,18 @@
 };
 typedef struct encKeyCacheEntryStr encKeyCacheEntry;
 
+#define SSL_MAX_DNS_HOST_NAME  1024
+
+struct srvNameCacheEntryStr {
+    PRUint16    type;                                   /*    2 */
+    PRUint16    nameLen;                                /*    2 */
+    PRUint8	name[SSL_MAX_DNS_HOST_NAME + 12];       /* 1034 */
+    PRUint8 	nameHash[SHA256_LENGTH];                /*   32 */
+                                                        /* 1072 */
+};
+typedef struct srvNameCacheEntryStr srvNameCacheEntry;
+
+
 struct cacheDescStr {
 
     PRUint32            cacheMemSize;
@@ -203,6 +220,9 @@
     PRUint32            numKeyCacheEntries;
     PRUint32            keyCacheSize;
 
+    PRUint32            numSrvNameCacheEntries;
+    PRUint32            srvNameCacheSize;
+
     PRUint32		ssl2Timeout;
     PRUint32		ssl3Timeout;
 
@@ -218,6 +238,7 @@
     sidCacheLock    *          sidCacheLocks;
     sidCacheLock    *          keyCacheLock;
     sidCacheLock    *          certCacheLock;
+    sidCacheLock    *          srvNameCacheLock;
     sidCacheSet     *          sidCacheSets;
     sidCacheEntry   *          sidCacheData;
     certCacheEntry  *          certCacheData;
@@ -226,6 +247,7 @@
     encKeyCacheEntry         * ticketEncKey;
     encKeyCacheEntry         * ticketMacKey;
     PRUint32        *          ticketKeysValid;
+    srvNameCacheEntry *        srvNameCacheData;
 
     /* Only the private copies of these pointers are valid */
     char *                     cacheMem;
@@ -248,6 +270,7 @@
 #define DEF_CERT_CACHE_ENTRIES 250
 #define MIN_CERT_CACHE_ENTRIES 125 /* the effective size in old releases. */
 #define DEF_KEY_CACHE_ENTRIES  250
+#define DEF_NAME_CACHE_ENTRIES  1000
 
 #define SID_CACHE_ENTRIES_PER_SET  128
 #define SID_ALIGNMENT          16
@@ -394,6 +417,59 @@
 
 }
 
+/* Server configuration hash tables need to account the SECITEM.type
+ * field as well. These functions accomplish that. */
+static PLHashNumber
+Get32BitNameHash(const SECItem *name)
+{
+    PLHashNumber rv = SECITEM_Hash(name);
+    
+    PRUint8 *rvc = (PRUint8 *)&rv;
+    rvc[ name->len % sizeof(rv) ] ^= name->type;
+
+    return rv;
+}
+
+/* Put a name in the cache.  Update the cert index in the sce.
+*/
+static PRUint32
+CacheSrvName(cacheDesc * cache, SECItem *name, sidCacheEntry *sce)
+{
+    PRUint32           now;
+    PRUint32           ndx;
+    srvNameCacheEntry  snce;
+
+    if (!name || name->len <= 0 ||
+        name->len > SSL_MAX_DNS_HOST_NAME) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return 0;
+    }
+
+    snce.type = name->type;
+    snce.nameLen = name->len;
+    PORT_Memcpy(snce.name, name->data, snce.nameLen);
+    SHA256_HashBuf(snce.nameHash, (unsigned char*)name->data,
+                   name->len);
+    /* get index of the next name */
+    ndx = Get32BitNameHash(name);
+    /* get lock on cert cache */
+    now = LockSidCacheLock(cache->srvNameCacheLock, 0);
+    if (now) {
+        if (cache->numSrvNameCacheEntries > 0) {
+            /* Fit the index into array */
+            ndx %= cache->numSrvNameCacheEntries;
+            /* write the entry */
+            cache->srvNameCacheData[ndx] = snce;
+            /* remember where we put it. */
+            sce->u.ssl3.srvNameIndex = ndx;
+            /* Copy hash into sid hash */
+            PORT_Memcpy(sce->u.ssl3.srvNameHash, snce.nameHash, SHA256_LENGTH);
+        }
+	UnlockSidCacheLock(cache->srvNameCacheLock);
+    }
+    return now;
+}
+
 /*
 ** Convert local SID to shared memory one
 */
@@ -454,6 +530,7 @@
 	to->u.ssl3.exchKeyType      = from->u.ssl3.exchKeyType;
 	to->sessionIDLength         = from->u.ssl3.sessionIDLength;
 	to->u.ssl3.certIndex        = -1;
+	to->u.ssl3.srvNameIndex     = -1;
 
 	PORT_Memcpy(to->sessionID, from->u.ssl3.sessionID,
 		    to->sessionIDLength);
@@ -472,7 +549,9 @@
 ** Caller must hold cache lock when calling this.
 */
 static sslSessionID *
-ConvertToSID(sidCacheEntry *from, certCacheEntry *pcce, 
+ConvertToSID(sidCacheEntry *    from,
+             certCacheEntry *   pcce,
+             srvNameCacheEntry *psnce,
              CERTCertDBHandle * dbHandle)
 {
     sslSessionID *to;
@@ -526,6 +605,17 @@
 	to->u.ssl3.keys             = from->u.ssl3.keys;
 	to->u.ssl3.masterWrapMech   = from->u.ssl3.masterWrapMech;
 	to->u.ssl3.exchKeyType      = from->u.ssl3.exchKeyType;
+	if (from->u.ssl3.srvNameIndex != -1 && psnce) {
+            SECItem name;
+            SECStatus rv;
+            name.type                   = psnce->type;
+            name.len                    = psnce->nameLen;
+            name.data                   = psnce->name;
+            rv = SECITEM_CopyItem(NULL, &to->u.ssl3.srvName, &name);
+            if (rv != SECSuccess) {
+                goto loser;
+            }
+        }
 
 	PORT_Memcpy(to->u.ssl3.sessionID, from->sessionID, from->sessionIDLength);
 
@@ -582,7 +672,9 @@
 		PORT_Free(to->u.ssl2.masterKey.data);
 	    if (to->u.ssl2.cipherArg.data)
 		PORT_Free(to->u.ssl2.cipherArg.data);
-	}
+	} else {
+            SECITEM_FreeItem(&to->u.ssl3.srvName, PR_FALSE);
+        }
 	PORT_Free(to);
     }
     return NULL;
@@ -682,12 +774,14 @@
     sslSessionID *  sid      = 0;
     sidCacheEntry * psce;
     certCacheEntry *pcce     = 0;
+    srvNameCacheEntry *psnce = 0;
     cacheDesc *     cache    = &globalCache;
     PRUint32        now;
     PRUint32        set;
     PRInt32         cndx;
     sidCacheEntry   sce;
     certCacheEntry  cce;
+    srvNameCacheEntry snce;
 
     set = SIDindex(cache, addr, sessionID, sessionIDLength);
     now = LockSet(cache, set, 0);
@@ -696,36 +790,65 @@
 
     psce = FindSID(cache, set, now, addr, sessionID, sessionIDLength);
     if (psce) {
-	if (psce->version >= SSL_LIBRARY_VERSION_3_0 && 
-	    (cndx = psce->u.ssl3.certIndex) != -1) {
-
-	    PRUint32 gotLock = LockSidCacheLock(cache->certCacheLock, now);
-	    if (gotLock) {
-		pcce = &cache->certCacheData[cndx];
-
-		/* See if the cert's session ID matches the sce cache. */
-		if ((pcce->sessionIDLength == psce->sessionIDLength) &&
-		    !PORT_Memcmp(pcce->sessionID, psce->sessionID, 
-		                 pcce->sessionIDLength)) {
-		    cce = *pcce;
-		} else {
-		    /* The cert doesen't match the SID cache entry, 
-		    ** so invalidate the SID cache entry. 
-		    */
-		    psce->valid = 0;
-		    psce = 0;
-		    pcce = 0;
-		}
-		UnlockSidCacheLock(cache->certCacheLock);
-	    } else {
-		/* what the ??.  Didn't get the cert cache lock.
-		** Don't invalidate the SID cache entry, but don't find it.
-		*/
-		PORT_Assert(!("Didn't get cert Cache Lock!"));
-		psce = 0;
-		pcce = 0;
-	    }
-	}
+	if (psce->version >= SSL_LIBRARY_VERSION_3_0) {
+	    if ((cndx = psce->u.ssl3.certIndex) != -1) {
+                
+                PRUint32 gotLock = LockSidCacheLock(cache->certCacheLock, now);
+                if (gotLock) {
+                    pcce = &cache->certCacheData[cndx];
+                    
+                    /* See if the cert's session ID matches the sce cache. */
+                    if ((pcce->sessionIDLength == psce->sessionIDLength) &&
+                        !PORT_Memcmp(pcce->sessionID, psce->sessionID, 
+                                     pcce->sessionIDLength)) {
+                        cce = *pcce;
+                    } else {
+                        /* The cert doesen't match the SID cache entry, 
+                        ** so invalidate the SID cache entry. 
+                        */
+                        psce->valid = 0;
+                        psce = 0;
+                        pcce = 0;
+                    }
+                    UnlockSidCacheLock(cache->certCacheLock);
+                } else {
+                    /* what the ??.  Didn't get the cert cache lock.
+                    ** Don't invalidate the SID cache entry, but don't find it.
+                    */
+                    PORT_Assert(!("Didn't get cert Cache Lock!"));
+                    psce = 0;
+                    pcce = 0;
+                }
+            }
+            if ((cndx = psce->u.ssl3.srvNameIndex) != -1) {
+                PRUint32 gotLock = LockSidCacheLock(cache->srvNameCacheLock,
+                                                    now);
+                if (gotLock) {
+                    psnce = &cache->srvNameCacheData[cndx];
+                    
+                    if (!PORT_Memcmp(psnce->nameHash, psce->u.ssl3.srvNameHash, 
+                                     SHA256_LENGTH)) {
+                        snce = *psnce;
+                    } else {
+                        /* The name doesen't match the SID cache entry, 
+                        ** so invalidate the SID cache entry. 
+                        */
+                        psce->valid = 0;
+                        psce = 0;
+                        psnce = 0;
+                    }
+                    UnlockSidCacheLock(cache->srvNameCacheLock);
+                } else {
+                    /* what the ??.  Didn't get the cert cache lock.
+                    ** Don't invalidate the SID cache entry, but don't find it.
+                    */
+                    PORT_Assert(!("Didn't get name Cache Lock!"));
+                    psce = 0;
+                    psnce = 0;
+                }
+                
+            }
+        }
 	if (psce) {
 	    psce->lastAccessTime = now;
 	    sce = *psce;	/* grab a copy while holding the lock */
@@ -736,7 +859,7 @@
 	/* sce conains a copy of the cache entry.
 	** Convert shared memory format to local format 
 	*/
-	sid = ConvertToSID(&sce, pcce ? &cce : 0, dbHandle);
+	sid = ConvertToSID(&sce, pcce ? &cce : 0, psnce ? &snce : 0, dbHandle);
     }
     return sid;
 }
@@ -796,9 +919,14 @@
 
 	ConvertFromSID(&sce, sid);
 
-	if ((version >= SSL_LIBRARY_VERSION_3_0) && 
-	    (sid->peerCert != NULL)) {
-	    now = CacheCert(cache, sid->peerCert, &sce);
+	if (version >= SSL_LIBRARY_VERSION_3_0) {
+            SECItem *name = &sid->u.ssl3.srvName;
+            if (name->len && name->data) {
+                now = CacheSrvName(cache, name, &sce);
+            }
+            if (sid->peerCert != NULL) {
+                now = CacheCert(cache, sid->peerCert, &sce);
+            }
 	}
 
 	set = SIDindex(cache, &sce.addr, sce.sessionID, sce.sessionIDLength);
@@ -924,7 +1052,8 @@
 }
 
 static SECStatus
-InitCache(cacheDesc *cache, int maxCacheEntries, PRUint32 ssl2_timeout, 
+InitCache(cacheDesc *cache, int maxCacheEntries, int maxCertCacheEntries,
+          int maxSrvNameCacheEntries, PRUint32 ssl2_timeout, 
           PRUint32 ssl3_timeout, const char *directory, PRBool shared)
 {
     ptrdiff_t     ptr;
@@ -973,6 +1102,11 @@
     cache->numSIDCacheSetsPerLock = 
     	SID_HOWMANY(cache->numSIDCacheSets, cache->numSIDCacheLocks);
 
+    cache->numCertCacheEntries = (maxCertCacheEntries > 0) ?
+                                             maxCertCacheEntries : 0;
+    cache->numSrvNameCacheEntries = (maxSrvNameCacheEntries > 0) ?
+                                             maxSrvNameCacheEntries : 0;
+
     /* compute size of shared memory, and offsets of all pointers */
     ptr = 0;
     cache->cacheMem     = (char *)ptr;
@@ -981,7 +1115,8 @@
     cache->sidCacheLocks = (sidCacheLock *)ptr;
     cache->keyCacheLock  = cache->sidCacheLocks + cache->numSIDCacheLocks;
     cache->certCacheLock = cache->keyCacheLock  + 1;
-    ptr = (ptrdiff_t)(cache->certCacheLock + 1);
+    cache->srvNameCacheLock = cache->certCacheLock  + 1;
+    ptr = (ptrdiff_t)(cache->srvNameCacheLock + 1);
     ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
 
     cache->sidCacheSets  = (sidCacheSet *)ptr;
@@ -996,10 +1131,12 @@
     cache->sidCacheSize  = 
     	(char *)cache->certCacheData - (char *)cache->sidCacheData;
 
-    /* This is really a poor way to computer this! */
-    cache->numCertCacheEntries = cache->sidCacheSize / sizeof(certCacheEntry);
-    if (cache->numCertCacheEntries < MIN_CERT_CACHE_ENTRIES)
+    if (cache->numCertCacheEntries < MIN_CERT_CACHE_ENTRIES) {
+        /* This is really a poor way to computer this! */
+        cache->numCertCacheEntries = cache->sidCacheSize / sizeof(certCacheEntry);
+        if (cache->numCertCacheEntries < MIN_CERT_CACHE_ENTRIES)
     	cache->numCertCacheEntries = MIN_CERT_CACHE_ENTRIES;
+    }
     ptr = (ptrdiff_t)(cache->certCacheData + cache->numCertCacheEntries);
     ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
 
@@ -1030,6 +1167,15 @@
     ptr = (ptrdiff_t)(cache->ticketKeysValid + 1);
     ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
 
+    cache->srvNameCacheData = (srvNameCacheEntry *)ptr;
+    if (cache->numSrvNameCacheEntries < 0) {
+        cache->numSrvNameCacheEntries = DEF_NAME_CACHE_ENTRIES;
+    }
+    cache->srvNameCacheSize =
+        cache->numSrvNameCacheEntries * sizeof(srvNameCacheEntry);
+    ptr = (ptrdiff_t)(cache->srvNameCacheData + cache->numSrvNameCacheEntries);
+    ptr = SID_ROUNDUP(ptr, SID_ALIGNMENT);
+
     cache->cacheMemSize = ptr;
 
     if (ssl2_timeout) {   
@@ -1113,6 +1259,7 @@
     *(ptrdiff_t *)(&cache->sidCacheLocks) += ptr;
     *(ptrdiff_t *)(&cache->keyCacheLock ) += ptr;
     *(ptrdiff_t *)(&cache->certCacheLock) += ptr;
+    *(ptrdiff_t *)(&cache->srvNameCacheLock) += ptr;
     *(ptrdiff_t *)(&cache->sidCacheSets ) += ptr;
     *(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
     *(ptrdiff_t *)(&cache->certCacheData) += ptr;
@@ -1121,11 +1268,12 @@
     *(ptrdiff_t *)(&cache->ticketEncKey ) += ptr;
     *(ptrdiff_t *)(&cache->ticketMacKey ) += ptr;
     *(ptrdiff_t *)(&cache->ticketKeysValid) += ptr;
+    *(ptrdiff_t *)(&cache->srvNameCacheData) += ptr;
 
     /* initialize the locks */
     init_time = ssl_Time();
     pLock = cache->sidCacheLocks;
-    for (locks_to_initialize = cache->numSIDCacheLocks + 2;
+    for (locks_to_initialize = cache->numSIDCacheLocks + 3;
          locks_initialized < locks_to_initialize; 
 	 ++locks_initialized, ++pLock ) {
 
@@ -1170,23 +1318,28 @@
     return SECSuccess;
 }
 
-SECStatus
-SSL_ConfigServerSessionIDCacheInstance(	cacheDesc *cache,
-                                int      maxCacheEntries, 
-				PRUint32 ssl2_timeout,
-			       	PRUint32 ssl3_timeout, 
-			  const char *   directory, PRBool shared)
+static SECStatus
+ssl_ConfigServerSessionIDCacheInstanceWithOpt(cacheDesc *cache,
+                                              PRUint32 ssl2_timeout,
+                                              PRUint32 ssl3_timeout, 
+                                              const char *   directory,
+                                              PRBool shared,
+                                              int      maxCacheEntries, 
+                                              int      maxCertCacheEntries,
+                                              int      maxSrvNameCacheEntries)
 {
     SECStatus rv;
 
-    PORT_Assert(sizeof(sidCacheEntry) == 192);
+    PORT_Assert(sizeof(sidCacheEntry) == 224);
     PORT_Assert(sizeof(certCacheEntry) == 4096);
+    PORT_Assert(sizeof(srvNameCacheEntry) == 1072);
 
     myPid = SSL_GETPID();
     if (!directory) {
 	directory = DEFAULT_CACHE_DIRECTORY;
     }
-    rv = InitCache(cache, maxCacheEntries, ssl2_timeout, ssl3_timeout, 
+    rv = InitCache(cache, maxCacheEntries, maxCertCacheEntries,
+                   maxSrvNameCacheEntries, ssl2_timeout, ssl3_timeout, 
                    directory, shared);
     if (rv) {
 	SET_ERROR_CODE
@@ -1200,6 +1353,22 @@
 }
 
 SECStatus
+SSL_ConfigServerSessionIDCacheInstance(	cacheDesc *cache,
+                                int      maxCacheEntries, 
+                                PRUint32 ssl2_timeout,
+                                PRUint32 ssl3_timeout, 
+                                const char *   directory, PRBool shared)
+{
+    return ssl_ConfigServerSessionIDCacheInstanceWithOpt(cache,
+                                                         ssl2_timeout,
+                                                         ssl3_timeout,
+                                                         directory,
+                                                         shared,
+                                                         maxCacheEntries, 
+                                                         -1, -1);
+}
+
+SECStatus
 SSL_ConfigServerSessionIDCache(	int      maxCacheEntries, 
 				PRUint32 ssl2_timeout,
 			       	PRUint32 ssl3_timeout, 
@@ -1231,11 +1400,13 @@
 /* Use this function, instead of SSL_ConfigServerSessionIDCache,
  * if the cache will be shared by multiple processes.
  */
-SECStatus
-SSL_ConfigMPServerSIDCache(	int      maxCacheEntries, 
-				PRUint32 ssl2_timeout,
-			       	PRUint32 ssl3_timeout, 
-		          const char *   directory)
+static SECStatus
+ssl_ConfigMPServerSIDCacheWithOpt(      PRUint32 ssl2_timeout,
+                                        PRUint32 ssl3_timeout, 
+                                        const char *   directory,
+                                        int maxCacheEntries,
+                                        int maxCertCacheEntries,
+                                        int maxSrvNameCacheEntries)
 {
     char *	envValue;
     char *	inhValue;
@@ -1248,8 +1419,9 @@
     char        fmString[PR_FILEMAP_STRING_BUFSIZE];
 
     isMultiProcess = PR_TRUE;
-    result = SSL_ConfigServerSessionIDCacheInstance(cache, maxCacheEntries, 
-			ssl2_timeout, ssl3_timeout, directory, PR_TRUE);
+    result = ssl_ConfigServerSessionIDCacheInstanceWithOpt(cache,
+                  ssl2_timeout, ssl3_timeout, directory, PR_TRUE,
+        maxCacheEntries, maxCacheEntries, maxSrvNameCacheEntries);
     if (result != SECSuccess) 
         return result;
 
@@ -1289,6 +1461,44 @@
     return result;
 }
 
+/* Use this function, instead of SSL_ConfigServerSessionIDCache,
+ * if the cache will be shared by multiple processes.
+ */
+SECStatus
+SSL_ConfigMPServerSIDCache(	int      maxCacheEntries, 
+				PRUint32 ssl2_timeout,
+			       	PRUint32 ssl3_timeout, 
+		          const char *   directory)
+{
+    return ssl_ConfigMPServerSIDCacheWithOpt(ssl2_timeout,
+                                             ssl3_timeout,
+                                             directory,
+                                             maxCacheEntries,
+                                             -1, -1);
+}
+
+SECStatus
+SSL_ConfigServerSessionIDCacheWithOpt(
+				PRUint32 ssl2_timeout,
+			       	PRUint32 ssl3_timeout, 
+                                const char *   directory,
+                                int maxCacheEntries,
+                                int maxCertCacheEntries,
+                                int maxSrvNameCacheEntries,
+                                PRBool enableMPCache)
+{
+    if (!enableMPCache) {
+        ssl_InitSessionCacheLocks(PR_FALSE);
+        return ssl_ConfigServerSessionIDCacheInstanceWithOpt(&globalCache, 
+           ssl2_timeout, ssl3_timeout, directory, PR_FALSE,
+           maxCacheEntries, maxCertCacheEntries, maxSrvNameCacheEntries);
+    } else {
+        return ssl_ConfigMPServerSIDCacheWithOpt(ssl2_timeout, ssl3_timeout,
+                directory, maxCacheEntries, maxCertCacheEntries,
+                                                 maxSrvNameCacheEntries);
+    }
+}
+
 SECStatus
 SSL_InheritMPServerSIDCacheInstance(cacheDesc *cache, const char * envString)
 {
@@ -1391,6 +1601,7 @@
     *(ptrdiff_t *)(&cache->sidCacheLocks) += ptr;
     *(ptrdiff_t *)(&cache->keyCacheLock ) += ptr;
     *(ptrdiff_t *)(&cache->certCacheLock) += ptr;
+    *(ptrdiff_t *)(&cache->srvNameCacheLock) += ptr;
     *(ptrdiff_t *)(&cache->sidCacheSets ) += ptr;
     *(ptrdiff_t *)(&cache->sidCacheData ) += ptr;
     *(ptrdiff_t *)(&cache->certCacheData) += ptr;
@@ -1399,6 +1610,7 @@
     *(ptrdiff_t *)(&cache->ticketEncKey ) += ptr;
     *(ptrdiff_t *)(&cache->ticketMacKey ) += ptr;
     *(ptrdiff_t *)(&cache->ticketKeysValid) += ptr;
+    *(ptrdiff_t *)(&cache->srvNameCacheData) += ptr;
 
     cache->cacheMemMap = my.cacheMemMap;
     cache->cacheMem    = my.cacheMem;
@@ -1420,7 +1632,7 @@
     /* note from jpierre : this should be free'd in child processes when
     ** a function is added to delete the SSL session cache in the future. 
     */
-    locks_to_initialize = cache->numSIDCacheLocks + 2;
+    locks_to_initialize = cache->numSIDCacheLocks + 3;
     newLocks = PORT_NewArray(sidCacheLock, locks_to_initialize);
     if (!newLocks)
     	goto loser;
@@ -1443,6 +1655,7 @@
     /* also fix the key and cert cache which use the last 2 lock entries */
     cache->keyCacheLock  = cache->sidCacheLocks + cache->numSIDCacheLocks;
     cache->certCacheLock = cache->keyCacheLock  + 1;
+    cache->srvNameCacheLock = cache->certCacheLock  + 1;
 #endif
 
     PORT_Free(myEnvString);
diff --git a/net/third_party/nss/ssl/sslsock.c b/net/third_party/nss/ssl/sslsock.c
index 2275800..c4611a0 100644
--- a/net/third_party/nss/ssl/sslsock.c
+++ b/net/third_party/nss/ssl/sslsock.c
@@ -40,7 +40,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslsock.c,v 1.60 2009/11/25 05:24:25 wtc%google.com Exp $ */
+/* $Id: sslsock.c,v 1.64 2010/01/28 06:19:13 nelson%bolyard.com Exp $ */
 #include "seccomon.h"
 #include "cert.h"
 #include "keyhi.h"
@@ -182,8 +182,9 @@
     PR_FALSE,   /* noLocks            */
     PR_FALSE,   /* enableSessionTickets */
     PR_FALSE,   /* enableDeflate      */
-    0,          /* enableRenegotiation (default: never) */
+    2,          /* enableRenegotiation (default: requires extension) */
     PR_FALSE,   /* requireSafeNegotiation */
+    PR_FALSE,   /* enableFalseStart   */
 };
 
 sslSessionIDLookupFunc  ssl_sid_lookup;
@@ -199,6 +200,7 @@
 char                    ssl_debug;
 char                    ssl_trace;
 FILE *                  ssl_trace_iob;
+FILE *                  ssl_keylog_iob;
 char lockStatus[] = "Locks are ENABLED.  ";
 #define LOCKSTATUS_OFFSET 10 /* offset of ENABLED */
 
@@ -305,7 +307,7 @@
 	    int i;
 	    sslServerCerts * oc = os->serverCerts;
 	    sslServerCerts * sc = ss->serverCerts;
-
+            
 	    for (i=kt_null; i < kt_kea_size; i++, oc++, sc++) {
 		if (oc->serverCert && oc->serverCertChain) {
 		    sc->serverCert      = CERT_DupCertificate(oc->serverCert);
@@ -334,6 +336,8 @@
 	    ss->authCertificateArg    = os->authCertificateArg;
 	    ss->getClientAuthData     = os->getClientAuthData;
 	    ss->getClientAuthDataArg  = os->getClientAuthDataArg;
+            ss->sniSocketConfig       = os->sniSocketConfig;
+            ss->sniSocketConfigArg    = os->sniSocketConfigArg;
 	    ss->handleBadCert         = os->handleBadCert;
 	    ss->badCertArg            = os->badCertArg;
 	    ss->handshakeCallback     = os->handshakeCallback;
@@ -439,6 +443,11 @@
 	PORT_Free(ss->opt.nextProtoNego.data);
 	ss->opt.nextProtoNego.data = NULL;
     }
+    PORT_Assert(!ss->xtnData.sniNameArr);
+    if (ss->xtnData.sniNameArr) {
+        PORT_Free(ss->xtnData.sniNameArr);
+        ss->xtnData.sniNameArr = NULL;
+    }
 }
 
 /*
@@ -725,6 +734,10 @@
 	ss->opt.requireSafeNegotiation = on;
 	break;
 
+      case SSL_ENABLE_FALSE_START:
+	ss->opt.enableFalseStart = on;
+	break;
+
       default:
 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
 	rv = SECFailure;
@@ -788,6 +801,7 @@
                                   on = ss->opt.enableRenegotiation; break;
     case SSL_REQUIRE_SAFE_NEGOTIATION: 
                                   on = ss->opt.requireSafeNegotiation; break;
+    case SSL_ENABLE_FALSE_START:  on = ss->opt.enableFalseStart;   break;
 
     default:
 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -838,6 +852,7 @@
     case SSL_REQUIRE_SAFE_NEGOTIATION: 
                                   on = ssl_defaults.requireSafeNegotiation; 
 				  break;
+    case SSL_ENABLE_FALSE_START:  on = ssl_defaults.enableFalseStart;   break;
 
     default:
 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
@@ -981,6 +996,10 @@
 	ssl_defaults.requireSafeNegotiation = on;
 	break;
 
+      case SSL_ENABLE_FALSE_START:
+	ssl_defaults.enableFalseStart = on;
+	break;
+
       default:
 	PORT_SetError(SEC_ERROR_INVALID_ARGS);
 	return SECFailure;
@@ -1321,6 +1340,119 @@
     return SECSuccess;
 }
 
+PRFileDesc *
+SSL_ReconfigFD(PRFileDesc *model, PRFileDesc *fd)
+{
+    PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+    PR_NOT_REACHED("not implemented");
+    return NULL;
+
+#if 0
+    sslSocket * sm = NULL, *ss = NULL;
+    int i;
+    sslServerCerts * mc = sm->serverCerts;
+    sslServerCerts * sc = ss->serverCerts;
+
+    if (model == NULL) {
+        PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+        return NULL;
+    }
+    sm = ssl_FindSocket(model);
+    if (sm == NULL) {
+        SSL_DBG(("%d: SSL[%d]: bad model socket in ssl_ReconfigFD", 
+                 SSL_GETPID(), model));
+        return NULL;
+    }
+    ss = ssl_FindSocket(fd);
+    PORT_Assert(ss);
+    if (ss == NULL) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return NULL;
+    }
+    
+    ss->opt  = sm->opt;
+    PORT_Memcpy(ss->cipherSuites, sm->cipherSuites, sizeof sm->cipherSuites);
+
+    if (!ss->opt.useSecurity) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return NULL;
+    }
+    /* This int should be SSLKEAType, but CC on Irix complains,
+     * during the for loop.
+     */
+    for (i=kt_null; i < kt_kea_size; i++, mc++, sc++) {
+        if (mc->serverCert && mc->serverCertChain) {
+            if (sc->serverCert) {
+                CERT_DestroyCertificate(sc->serverCert);
+            }
+            sc->serverCert      = CERT_DupCertificate(mc->serverCert);
+            if (sc->serverCertChain) {
+                CERT_DestroyCertificateList(sc->serverCertChain);
+            }
+            sc->serverCertChain = CERT_DupCertList(mc->serverCertChain);
+            if (!sc->serverCertChain)
+                goto loser;
+        }
+        if (mc->serverKeyPair) {
+            if (sc->serverKeyPair) {
+                ssl3_FreeKeyPair(sc->serverKeyPair);
+            }
+            sc->serverKeyPair = ssl3_GetKeyPairRef(mc->serverKeyPair);
+            sc->serverKeyBits = mc->serverKeyBits;
+        }
+    }
+    if (sm->stepDownKeyPair) {
+        if (ss->stepDownKeyPair) {
+            ssl3_FreeKeyPair(ss->stepDownKeyPair);
+        }
+        ss->stepDownKeyPair = ssl3_GetKeyPairRef(sm->stepDownKeyPair);
+    }
+    if (sm->ephemeralECDHKeyPair) {
+        if (ss->ephemeralECDHKeyPair) {
+            ssl3_FreeKeyPair(ss->ephemeralECDHKeyPair);
+        }
+        ss->ephemeralECDHKeyPair =
+            ssl3_GetKeyPairRef(sm->ephemeralECDHKeyPair);
+    }
+    /* copy trust anchor names */
+    if (sm->ssl3.ca_list) {
+        if (ss->ssl3.ca_list) {
+            CERT_FreeDistNames(ss->ssl3.ca_list);
+        }
+        ss->ssl3.ca_list = CERT_DupDistNames(sm->ssl3.ca_list);
+        if (!ss->ssl3.ca_list) {
+            goto loser;
+        }
+    }
+    
+    if (sm->authCertificate)
+        ss->authCertificate       = sm->authCertificate;
+    if (sm->authCertificateArg)
+        ss->authCertificateArg    = sm->authCertificateArg;
+    if (sm->getClientAuthData)
+        ss->getClientAuthData     = sm->getClientAuthData;
+    if (sm->getClientAuthDataArg)
+        ss->getClientAuthDataArg  = sm->getClientAuthDataArg;
+    if (sm->sniSocketConfig)
+        ss->sniSocketConfig       = sm->sniSocketConfig;
+    if (sm->sniSocketConfigArg)
+        ss->sniSocketConfigArg    = sm->sniSocketConfigArg;
+    if (sm->handleBadCert)
+        ss->handleBadCert         = sm->handleBadCert;
+    if (sm->badCertArg)
+        ss->badCertArg            = sm->badCertArg;
+    if (sm->handshakeCallback)
+        ss->handshakeCallback     = sm->handshakeCallback;
+    if (sm->handshakeCallbackData)
+        ss->handshakeCallbackData = sm->handshakeCallbackData;
+    if (sm->pkcs11PinArg)
+        ss->pkcs11PinArg          = sm->pkcs11PinArg;
+    return fd;
+loser:
+    return NULL;
+#endif
+}
+
 /************************************************************************/
 /* The following functions are the TOP LEVEL SSL functions.
 ** They all get called through the NSPRIOMethods table below.
@@ -2223,6 +2355,15 @@
 	    ssl_trace = atoi(ev);
 	    SSL_TRACE(("SSL: tracing set to %d", ssl_trace));
 	}
+	ev = getenv("SSLKEYLOGFILE");
+	if (ev && ev[0]) {
+	    ssl_keylog_iob = fopen(ev, "a");
+	    if (ftell(ssl_keylog_iob) == 0) {
+		fputs("# pre-master secret log file, generated by NSS\n",
+		      ssl_keylog_iob);
+	    }
+	    SSL_TRACE(("SSL: logging pre-master secrets to %s", ev));
+	}
 #endif /* TRACE */
 	ev = getenv("SSLDEBUG");
 	if (ev && ev[0]) {
@@ -2247,19 +2388,12 @@
 	if (ev) {
 	    if (ev[0] == '1' || LOWER(ev[0]) == 'u')
 	    	ssl_defaults.enableRenegotiation = SSL_RENEGOTIATE_UNRESTRICTED;
-#ifdef LATER
-	    /* When SSL_RENEGOTIATE_REQUIRES_XTN is implemented, it will be 
-	     * the default.  Until then, NEVER will be the default. 
-	     */
 	    else if (ev[0] == '0' || LOWER(ev[0]) == 'n')
 	    	ssl_defaults.enableRenegotiation = SSL_RENEGOTIATE_NEVER;
+	    else if (ev[0] == '3' || LOWER(ev[0]) == 'c')
+	    	ssl_defaults.enableRenegotiation = SSL_RENEGOTIATE_CLIENT_ONLY;
 	    else
 	    	ssl_defaults.enableRenegotiation = SSL_RENEGOTIATE_REQUIRES_XTN;
-#else
-	    else
-	    	ssl_defaults.enableRenegotiation = SSL_RENEGOTIATE_NEVER;
-#endif
-
 	    SSL_TRACE(("SSL: enableRenegotiation set to %d", 
 	               ssl_defaults.enableRenegotiation));
 	}
@@ -2309,6 +2443,8 @@
 	/* Provide default implementation of hooks */
 	ss->authCertificate    = SSL_AuthCertificate;
 	ss->authCertificateArg = (void *)ss->dbHandle;
+        ss->sniSocketConfig    = NULL;
+        ss->sniSocketConfigArg = NULL;
 	ss->getClientAuthData  = NULL;
 	ss->handleBadCert      = NULL;
 	ss->badCertArg         = NULL;
diff --git a/net/third_party/nss/ssl/sslt.h b/net/third_party/nss/ssl/sslt.h
index 80f1dc2..f6e0b62 100644
--- a/net/third_party/nss/ssl/sslt.h
+++ b/net/third_party/nss/ssl/sslt.h
@@ -37,7 +37,7 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
-/* $Id: sslt.h,v 1.13 2009/11/07 18:23:06 wtc%google.com Exp $ */
+/* $Id: sslt.h,v 1.16 2010/02/04 03:21:11 wtc%google.com Exp $ */
 
 #ifndef __sslt_h_
 #define __sslt_h_
@@ -189,4 +189,24 @@
 
 } SSLCipherSuiteInfo;
 
+typedef enum {
+    SSL_sni_host_name                    = 0,
+    SSL_sni_type_total
+} SSLSniNameType;
+
+/* Supported extensions. */
+/* Update SSL_MAX_EXTENSIONS whenever a new extension type is added. */
+typedef enum {
+    ssl_server_name_xtn              = 0,
+#ifdef NSS_ENABLE_ECC
+    ssl_elliptic_curves_xtn          = 10,
+    ssl_ec_point_formats_xtn         = 11,
+#endif
+    ssl_session_ticket_xtn           = 35,
+    ssl_next_proto_neg_xtn           = 13172,
+    ssl_renegotiation_info_xtn       = 0xff01	/* experimental number */
+} SSLExtensionType;
+
+#define SSL_MAX_EXTENSIONS             6
+
 #endif /* __sslt_h_ */
diff --git a/net/tld_cleanup.target.mk b/net/tld_cleanup.target.mk
new file mode 100644
index 0000000..982ef8a
--- /dev/null
+++ b/net/tld_cleanup.target.mk
@@ -0,0 +1,182 @@
+# This file is generated by gyp; do not edit.
+
+TOOLSET := target
+TARGET := tld_cleanup
+DEFS_Debug := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+	'-D_DEBUG'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Debug := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O0 \
+	-g
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Debug := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Debug := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Debug := -I.
+
+DEFS_Release := '-DNO_HEAPCHECKER' \
+	'-DCHROMIUM_BUILD' \
+	'-DENABLE_REMOTING=1' \
+	'-DENABLE_GPU=1' \
+	'-D__STDC_FORMAT_MACROS' \
+	'-DNDEBUG' \
+	'-DNVALGRIND' \
+	'-DDYNAMIC_ANNOTATIONS_ENABLED=0'
+
+# Flags passed to both C and C++ files.
+CFLAGS_Release := -Werror \
+	-pthread \
+	-fno-exceptions \
+	-Wall \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-D_FILE_OFFSET_BITS=64 \
+	-fvisibility=hidden \
+	-fno-strict-aliasing \
+	-pthread \
+	-D_REENTRANT \
+	-I/usr/include/gtk-2.0 \
+	-I/usr/lib/gtk-2.0/include \
+	-I/usr/include/atk-1.0 \
+	-I/usr/include/cairo \
+	-I/usr/include/pango-1.0 \
+	-I/usr/include/gio-unix-2.0/ \
+	-I/usr/include/glib-2.0 \
+	-I/usr/lib/glib-2.0/include \
+	-I/usr/include/pixman-1 \
+	-I/usr/include/freetype2 \
+	-I/usr/include/directfb \
+	-I/usr/include/libpng12 \
+	-O2 \
+	-fno-ident \
+	-fdata-sections \
+	-ffunction-sections
+
+# Flags passed to only C (and not C++) files.
+CFLAGS_C_Release := 
+
+# Flags passed to only C++ (and not C) files.
+CFLAGS_CC_Release := -fno-rtti \
+	-fno-threadsafe-statics \
+	-fvisibility-inlines-hidden
+
+INCS_Release := -I.
+
+OBJS := $(obj).target/$(TARGET)/net/tools/tld_cleanup/tld_cleanup.o
+
+# Add to the list of files we specially track dependencies for.
+all_deps += $(OBJS)
+
+# Make sure our dependencies are built before any of us.
+$(OBJS): | $(obj).target/base/libbase.a $(obj).target/base/libbase_i18n.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a
+
+# CFLAGS et al overrides must be target-local.
+# See "Target-specific Variable Values" in the GNU Make manual.
+$(OBJS): TOOLSET := $(TOOLSET)
+$(OBJS): GYP_CFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+$(OBJS): GYP_CXXFLAGS := $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE))
+
+# Suffix rules, putting all outputs into $(obj).
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# Try building from generated source, too.
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
+	@$(call do_cmd,cxx,1)
+
+# End of this set of suffix rules
+### Rules for final target.
+LDFLAGS_Debug := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-rdynamic
+
+LDFLAGS_Release := -pthread \
+	-Wl,-z,noexecstack \
+	-Wl,-uIsHeapProfilerRunning,-uProfilerStart \
+	-Wl,-u_Z21InitialMallocHook_NewPKvj,-u_Z22InitialMallocHook_MMapPKvS0_jiiix,-u_Z22InitialMallocHook_SbrkPKvi \
+	-Wl,-u_Z21InitialMallocHook_NewPKvm,-u_Z22InitialMallocHook_MMapPKvS0_miiil,-u_Z22InitialMallocHook_SbrkPKvl \
+	-Wl,--gc-sections
+
+LIBS := -lrt \
+	-ldl \
+	-lgtk-x11-2.0 \
+	-lgdk-x11-2.0 \
+	-latk-1.0 \
+	-lgio-2.0 \
+	-lpangoft2-1.0 \
+	-lgdk_pixbuf-2.0 \
+	-lm \
+	-lpangocairo-1.0 \
+	-lcairo \
+	-lpango-1.0 \
+	-lfreetype \
+	-lfontconfig \
+	-lgobject-2.0 \
+	-lgmodule-2.0 \
+	-lgthread-2.0 \
+	-lglib-2.0 \
+	-lnss3 \
+	-lnssutil3 \
+	-lsmime3 \
+	-lplds4 \
+	-lplc4 \
+	-lnspr4 \
+	-lpthread \
+	-lz
+
+$(builddir)/tld_cleanup: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
+$(builddir)/tld_cleanup: LIBS := $(LIBS)
+$(builddir)/tld_cleanup: TOOLSET := $(TOOLSET)
+$(builddir)/tld_cleanup: $(OBJS) $(obj).target/base/libbase.a $(obj).target/base/libbase_i18n.a $(obj).target/build/temp_gyp/libgoogleurl.a $(obj).target/third_party/modp_b64/libmodp_b64.a $(obj).target/base/third_party/dynamic_annotations/libdynamic_annotations.a $(obj).target/base/libsymbolize.a $(obj).target/net/third_party/nss/libssl.a $(obj).target/third_party/zlib/libzlib.a $(obj).target/base/libxdg_mime.a $(obj).target/base/allocator/liballocator.a $(obj).target/third_party/libevent/libevent.a $(obj).target/third_party/icu/libicui18n.a $(obj).target/third_party/icu/libicuuc.a $(obj).target/third_party/icu/libicudata.a FORCE_DO_CMD
+	$(call do_cmd,link)
+
+all_deps += $(builddir)/tld_cleanup
+# Add target alias
+.PHONY: tld_cleanup
+tld_cleanup: $(builddir)/tld_cleanup
+
+# Add executable to "all" target.
+.PHONY: all
+all: $(builddir)/tld_cleanup
+
diff --git a/net/tools/crash_cache/crash_cache.cc b/net/tools/crash_cache/crash_cache.cc
index 4686d13..1d11013 100644
--- a/net/tools/crash_cache/crash_cache.cc
+++ b/net/tools/crash_cache/crash_cache.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -17,7 +17,9 @@
 #include "base/path_service.h"
 #include "base/process_util.h"
 #include "base/string_util.h"
-
+#include "base/thread.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
 #include "net/disk_cache/backend_impl.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/disk_cache/disk_cache_test_util.h"
@@ -116,11 +118,23 @@
   return file_util::CreateDirectory(*full_path);
 }
 
+// Makes sure that any pending task is processed.
+void FlushQueue(disk_cache::Backend* cache) {
+  TestCompletionCallback cb;
+  int rv =
+      reinterpret_cast<disk_cache::BackendImpl*>(cache)->FlushQueueForTest(&cb);
+  cb.GetResult(rv);  // Ignore the result;
+}
+
 // Generates the files for an empty and one item cache.
-int SimpleInsert(const FilePath& path, RankCrashes action) {
-  disk_cache::Backend* cache = disk_cache::CreateCacheBackend(path, false, 0,
-                                                              net::DISK_CACHE);
-  if (!cache || cache->GetEntryCount())
+int SimpleInsert(const FilePath& path, RankCrashes action,
+                 base::Thread* cache_thread) {
+  TestCompletionCallback cb;
+  disk_cache::Backend* cache;
+  int rv = disk_cache::CreateCacheBackend(net::DISK_CACHE, path, 0, false,
+                                          cache_thread->message_loop_proxy(),
+                                          &cache, &cb);
+  if (cb.GetResult(rv) != net::OK || cache->GetEntryCount())
     return GENERIC;
 
   const char* test_name = "some other key";
@@ -131,92 +145,122 @@
   }
 
   disk_cache::Entry* entry;
-  if (!cache->CreateEntry(test_name, &entry))
+  rv = cache->CreateEntry(test_name, &entry, &cb);
+  if (cb.GetResult(rv) != net::OK)
     return GENERIC;
 
   entry->Close();
+  FlushQueue(cache);
 
   DCHECK(action <= disk_cache::INSERT_ONE_3);
   g_rankings_crash = action;
   test_name = kCrashEntryName;
 
-  if (!cache->CreateEntry(test_name, &entry))
+  rv = cache->CreateEntry(test_name, &entry, &cb);
+  if (cb.GetResult(rv) != net::OK)
     return GENERIC;
 
   return NOT_REACHED;
 }
 
 // Generates the files for a one item cache, and removing the head.
-int SimpleRemove(const FilePath& path, RankCrashes action) {
+int SimpleRemove(const FilePath& path, RankCrashes action,
+                 base::Thread* cache_thread) {
   DCHECK(action >= disk_cache::REMOVE_ONE_1);
   DCHECK(action <= disk_cache::REMOVE_TAIL_3);
 
-  disk_cache::Backend* cache = disk_cache::CreateCacheBackend(path, false, 0,
-                                                              net::DISK_CACHE);
-  if (!cache || cache->GetEntryCount())
+  TestCompletionCallback cb;
+  disk_cache::Backend* cache;
+  // Use a simple LRU for eviction.
+  int rv = disk_cache::CreateCacheBackend(net::MEDIA_CACHE, path, 0, false,
+                                          cache_thread->message_loop_proxy(),
+                                          &cache, &cb);
+  if (cb.GetResult(rv) != net::OK || cache->GetEntryCount())
     return GENERIC;
 
   disk_cache::Entry* entry;
-  if (!cache->CreateEntry(kCrashEntryName, &entry))
+  rv = cache->CreateEntry(kCrashEntryName, &entry, &cb);
+  if (cb.GetResult(rv) != net::OK)
     return GENERIC;
 
   entry->Close();
+  FlushQueue(cache);
 
   if (action >= disk_cache::REMOVE_TAIL_1) {
-    if (!cache->CreateEntry("some other key", &entry))
+    rv = cache->CreateEntry("some other key", &entry, &cb);
+    if (cb.GetResult(rv) != net::OK)
       return GENERIC;
 
     entry->Close();
+    FlushQueue(cache);
   }
 
-  if (!cache->OpenEntry(kCrashEntryName, &entry))
+  rv = cache->OpenEntry(kCrashEntryName, &entry, &cb);
+  if (cb.GetResult(rv) != net::OK)
     return GENERIC;
 
   g_rankings_crash = action;
   entry->Doom();
   entry->Close();
+  FlushQueue(cache);
 
   return NOT_REACHED;
 }
 
-int HeadRemove(const FilePath& path, RankCrashes action) {
+int HeadRemove(const FilePath& path, RankCrashes action,
+               base::Thread* cache_thread) {
   DCHECK(action >= disk_cache::REMOVE_HEAD_1);
   DCHECK(action <= disk_cache::REMOVE_HEAD_4);
 
-  disk_cache::Backend* cache = disk_cache::CreateCacheBackend(path, false, 0,
-                                                              net::DISK_CACHE);
-  if (!cache || cache->GetEntryCount())
+  TestCompletionCallback cb;
+  disk_cache::Backend* cache;
+  // Use a simple LRU for eviction.
+  int rv = disk_cache::CreateCacheBackend(net::MEDIA_CACHE, path, 0, false,
+                                          cache_thread->message_loop_proxy(),
+                                          &cache, &cb);
+  if (cb.GetResult(rv) != net::OK || cache->GetEntryCount())
     return GENERIC;
 
   disk_cache::Entry* entry;
-  if (!cache->CreateEntry("some other key", &entry))
+  rv = cache->CreateEntry("some other key", &entry, &cb);
+  if (cb.GetResult(rv) != net::OK)
     return GENERIC;
 
   entry->Close();
-  if (!cache->CreateEntry(kCrashEntryName, &entry))
+  FlushQueue(cache);
+  rv = cache->CreateEntry(kCrashEntryName, &entry, &cb);
+  if (cb.GetResult(rv) != net::OK)
     return GENERIC;
 
   entry->Close();
+  FlushQueue(cache);
 
-  if (!cache->OpenEntry(kCrashEntryName, &entry))
+  rv = cache->OpenEntry(kCrashEntryName, &entry, &cb);
+  if (cb.GetResult(rv) != net::OK)
     return GENERIC;
 
   g_rankings_crash = action;
   entry->Doom();
   entry->Close();
+  FlushQueue(cache);
 
   return NOT_REACHED;
 }
 
 // Generates the files for insertion and removals on heavy loaded caches.
-int LoadOperations(const FilePath& path, RankCrashes action) {
+int LoadOperations(const FilePath& path, RankCrashes action,
+                   base::Thread* cache_thread) {
   DCHECK(action >= disk_cache::INSERT_LOAD_1);
 
-  // Work with a tiny index table (16 entries)
-  disk_cache::BackendImpl* cache =
-      new disk_cache::BackendImpl(path, 0xf);
-  if (!cache || !cache->SetMaxSize(0x100000) || !cache->Init() ||
-      cache->GetEntryCount())
+  // Work with a tiny index table (16 entries).
+  disk_cache::BackendImpl* cache = new disk_cache::BackendImpl(
+      path, 0xf, cache_thread->message_loop_proxy());
+  if (!cache || !cache->SetMaxSize(0x100000))
+    return GENERIC;
+
+  TestCompletionCallback cb;
+  int rv = cache->Init(&cb);
+  if (cb.GetResult(rv) != net::OK || cache->GetEntryCount())
     return GENERIC;
 
   int seed = static_cast<int>(Time::Now().ToInternalValue());
@@ -225,37 +269,44 @@
   disk_cache::Entry* entry;
   for (int i = 0; i < 100; i++) {
     std::string key = GenerateKey(true);
-    if (!cache->CreateEntry(key, &entry))
+    rv = cache->CreateEntry(key, &entry, &cb);
+    if (cb.GetResult(rv) != net::OK)
       return GENERIC;
     entry->Close();
+    FlushQueue(cache);
     if (50 == i && action >= disk_cache::REMOVE_LOAD_1) {
-      if (!cache->CreateEntry(kCrashEntryName, &entry))
+      rv = cache->CreateEntry(kCrashEntryName, &entry, &cb);
+      if (cb.GetResult(rv) != net::OK)
         return GENERIC;
       entry->Close();
+      FlushQueue(cache);
     }
   }
 
   if (action <= disk_cache::INSERT_LOAD_2) {
     g_rankings_crash = action;
 
-    if (!cache->CreateEntry(kCrashEntryName, &entry))
+    rv = cache->CreateEntry(kCrashEntryName, &entry, &cb);
+    if (cb.GetResult(rv) != net::OK)
       return GENERIC;
   }
 
-  if (!cache->OpenEntry(kCrashEntryName, &entry))
+  rv = cache->OpenEntry(kCrashEntryName, &entry, &cb);
+  if (cb.GetResult(rv) != net::OK)
     return GENERIC;
 
   g_rankings_crash = action;
 
   entry->Doom();
   entry->Close();
+  FlushQueue(cache);
 
   return NOT_REACHED;
 }
 
 // Main function on the child process.
 int SlaveCode(const FilePath& path, RankCrashes action) {
-  MessageLoop message_loop;
+  MessageLoopForIO message_loop;
 
   FilePath full_path;
   if (!CreateTargetFolder(path, action, &full_path)) {
@@ -263,23 +314,28 @@
     return CRASH_OVERWRITE;
   }
 
+  base::Thread cache_thread("CacheThread");
+  if (!cache_thread.StartWithOptions(
+          base::Thread::Options(MessageLoop::TYPE_IO, 0)))
+    return GENERIC;
+
   if (action <= disk_cache::INSERT_ONE_3)
-    return SimpleInsert(full_path, action);
+    return SimpleInsert(full_path, action, &cache_thread);
 
   if (action <= disk_cache::INSERT_LOAD_2)
-    return LoadOperations(full_path, action);
+    return LoadOperations(full_path, action, &cache_thread);
 
   if (action <= disk_cache::REMOVE_ONE_4)
-    return SimpleRemove(full_path, action);
+    return SimpleRemove(full_path, action, &cache_thread);
 
   if (action <= disk_cache::REMOVE_HEAD_4)
-    return HeadRemove(full_path, action);
+    return HeadRemove(full_path, action, &cache_thread);
 
   if (action <= disk_cache::REMOVE_TAIL_3)
-    return SimpleRemove(full_path, action);
+    return SimpleRemove(full_path, action, &cache_thread);
 
   if (action <= disk_cache::REMOVE_LOAD_3)
-    return LoadOperations(full_path, action);
+    return LoadOperations(full_path, action, &cache_thread);
 
   return NOT_REACHED;
 }
diff --git a/net/tools/dump_cache/cache_dumper.cc b/net/tools/dump_cache/cache_dumper.cc
index 1120817..f573b2a 100644
--- a/net/tools/dump_cache/cache_dumper.cc
+++ b/net/tools/dump_cache/cache_dumper.cc
@@ -1,25 +1,27 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/dump_cache/cache_dumper.h"
 
 #include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
 #include "net/disk_cache/entry_impl.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
 #include "net/tools/dump_cache/url_to_filename_encoder.h"
 
-bool CacheDumper::CreateEntry(const std::string& key,
-                              disk_cache::Entry** entry) {
-  return cache_->CreateEntry(key, entry);
+int CacheDumper::CreateEntry(const std::string& key,
+                             disk_cache::Entry** entry,
+                             net::CompletionCallback* callback) {
+  return cache_->CreateEntry(key, entry, callback);
 }
 
-bool CacheDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset,
-                             net::IOBuffer* buf, int buf_len) {
-  int written = entry->WriteData(index, offset, buf, buf_len, NULL, false);
-  return written == buf_len;
+int CacheDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset,
+                            net::IOBuffer* buf, int buf_len,
+                            net::CompletionCallback* callback) {
+  return entry->WriteData(index, offset, buf, buf_len, callback, false);
 }
 
 void CacheDumper::CloseEntry(disk_cache::Entry* entry, base::Time last_used,
@@ -48,7 +50,7 @@
     pos = 4;
 
   // Create the subdirectories individually
-  while((pos = path.find(backslash, pos)) != std::wstring::npos) {
+  while ((pos = path.find(backslash, pos)) != std::wstring::npos) {
     std::wstring subdir = path.substr(0, pos);
     CreateDirectoryW(subdir.c_str(), NULL);
     // we keep going even if directory creation failed.
@@ -61,10 +63,17 @@
 #endif
 }
 
-bool DiskDumper::CreateEntry(const std::string& key,
-                             disk_cache::Entry** entry) {
+int DiskDumper::CreateEntry(const std::string& key,
+                            disk_cache::Entry** entry,
+                            net::CompletionCallback* callback) {
   FilePath path(path_);
-  entry_path_ = net::UrlToFilenameEncoder::Encode(key, path);
+  // The URL may not start with a valid protocol; search for it.
+  int urlpos = key.find("http");
+  std::string url = urlpos > 0 ? key.substr(urlpos) : key;
+  std::string base_path = WideToASCII(path_);
+  std::string new_path =
+      net::UrlToFilenameEncoder::Encode(url, base_path, false);
+  entry_path_ = FilePath(ASCIIToWide(new_path));
 
 #ifdef WIN32_LARGE_FILENAME_SUPPORT
   // In order for long filenames to work, we'll need to prepend
@@ -86,10 +95,12 @@
 #ifdef WIN32_LARGE_FILENAME_SUPPORT
   entry_ = CreateFileW(file.c_str(), GENERIC_WRITE|GENERIC_READ, 0, 0,
                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
-  return entry_ != INVALID_HANDLE_VALUE;
+  if (entry_ == INVALID_HANDLE_VALUE)
+    wprintf(L"CreateFileW (%s) failed: %d\n", file.c_str(), GetLastError());
+  return (entry_ != INVALID_HANDLE_VALUE) ? net::OK : net::ERR_FAILED;
 #else
   entry_ = file_util::OpenFile(entry_path_, "w+");
-  return entry_ != NULL;
+  return (entry_ != NULL) ? net::OK : net::ERR_FAILED;
 #endif
 }
 
@@ -133,24 +144,25 @@
   output->append("\r\n");
 }
 
-bool DiskDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset,
-                            net::IOBuffer* buf, int buf_len) {
+int DiskDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset,
+                           net::IOBuffer* buf, int buf_len,
+                           net::CompletionCallback* callback) {
   if (!entry_)
-    return false;
+    return 0;
 
   std::string headers;
   const char *data;
-  int len;
+  size_t len;
   if (index == 0) {  // Stream 0 is the headers.
     net::HttpResponseInfo response_info;
     bool truncated;
     if (!net::HttpCache::ParseResponseInfo(buf->data(), buf_len,
                                            &response_info, &truncated))
-      return false;
+      return 0;
 
     // Skip this entry if it was truncated (results in an empty file).
     if (truncated)
-      return true;
+      return buf_len;
 
     // Remove the size headers.
     response_info.headers->RemoveHeader("transfer-encoding");
@@ -180,11 +192,12 @@
   }
 #ifdef WIN32_LARGE_FILENAME_SUPPORT
   DWORD bytes;
-  DWORD rv = WriteFile(entry_, data, len, &bytes, 0);
-  return rv == TRUE && bytes == len;
+  if (!WriteFile(entry_, data, len, &bytes, 0))
+    return 0;
+
+  return bytes;
 #else
-  int bytes = fwrite(data, 1, len, entry_);
-  return bytes == len;
+  return fwrite(data, 1, len, entry_);
 #endif
 }
 
diff --git a/net/tools/dump_cache/cache_dumper.h b/net/tools/dump_cache/cache_dumper.h
index 916b1b4..d2e6034 100644
--- a/net/tools/dump_cache/cache_dumper.h
+++ b/net/tools/dump_cache/cache_dumper.h
@@ -1,9 +1,9 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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_DUMP_CACHE_DUMPER_H_
-#define NET_TOOLS_DUMP_CACHE_DUMPER_H_
+#ifndef NET_TOOLS_DUMP_CACHE_CACHE_DUMPER_H_
+#define NET_TOOLS_DUMP_CACHE_CACHE_DUMPER_H_
 
 #include <string>
 #include "base/file_path.h"
@@ -22,16 +22,20 @@
 // An abstract class for writing cache dump data.
 class CacheDumpWriter {
  public:
+  virtual ~CacheDumpWriter() {}
+
   // Creates an entry to be written.
   // On success, populates the |entry|.
-  // Returns true on success, false otherwise.
-  virtual bool CreateEntry(const std::string& key,
-                           disk_cache::Entry** entry) = 0;
+  // Returns a net error code.
+  virtual int CreateEntry(const std::string& key,
+                          disk_cache::Entry** entry,
+                          net::CompletionCallback* callback) = 0;
 
   // Write to the current entry.
-  // Returns true on success, false otherwise.
-  virtual bool WriteEntry(disk_cache::Entry* entry, int stream, int offset,
-                          net::IOBuffer* buf, int buf_len) = 0;
+  // Returns a net error code.
+  virtual int WriteEntry(disk_cache::Entry* entry, int stream, int offset,
+                         net::IOBuffer* buf, int buf_len,
+                         net::CompletionCallback* callback) = 0;
 
   // Close the current entry.
   virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used,
@@ -41,27 +45,31 @@
 // Writes data to a cache.
 class CacheDumper : public CacheDumpWriter {
  public:
-  CacheDumper(disk_cache::BackendImpl* cache) : cache_(cache) {};
+  explicit CacheDumper(disk_cache::Backend* cache) : cache_(cache) {}
 
-  virtual bool CreateEntry(const std::string& key, disk_cache::Entry** entry);
-  virtual bool WriteEntry(disk_cache::Entry* entry, int stream, int offset,
-                          net::IOBuffer* buf, int buf_len);
+  virtual int CreateEntry(const std::string& key, disk_cache::Entry** entry,
+                          net::CompletionCallback* callback);
+  virtual int WriteEntry(disk_cache::Entry* entry, int stream, int offset,
+                         net::IOBuffer* buf, int buf_len,
+                         net::CompletionCallback* callback);
   virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used,
                           base::Time last_modified);
 
  private:
-  disk_cache::BackendImpl* cache_;
+  disk_cache::Backend* cache_;
 };
 
 // Writes data to a disk.
 class DiskDumper : public CacheDumpWriter {
  public:
-  DiskDumper(const std::wstring& path) : path_(path), entry_(NULL) {
+  explicit DiskDumper(const std::wstring& path) : path_(path), entry_(NULL) {
     file_util::CreateDirectory(FilePath(path));
-  };
-  virtual bool CreateEntry(const std::string& key, disk_cache::Entry** entry);
-  virtual bool WriteEntry(disk_cache::Entry* entry, int stream, int offset,
-                          net::IOBuffer* buf, int buf_len);
+  }
+  virtual int CreateEntry(const std::string& key, disk_cache::Entry** entry,
+                          net::CompletionCallback* callback);
+  virtual int WriteEntry(disk_cache::Entry* entry, int stream, int offset,
+                         net::IOBuffer* buf, int buf_len,
+                         net::CompletionCallback* callback);
   virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used,
                           base::Time last_modified);
 
@@ -79,4 +87,4 @@
 #endif
 };
 
-#endif  // NET_TOOLS_DUMP_CACHE_DUMPER_H_
+#endif  // NET_TOOLS_DUMP_CACHE_CACHE_DUMPER_H_
diff --git a/net/tools/dump_cache/upgrade.cc b/net/tools/dump_cache/upgrade.cc
index 566eb0b..60099b2 100644
--- a/net/tools/dump_cache/upgrade.cc
+++ b/net/tools/dump_cache/upgrade.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -7,8 +7,10 @@
 #include "base/message_loop.h"
 #include "base/scoped_ptr.h"
 #include "base/string_util.h"
+#include "base/thread.h"
 #include "googleurl/src/gurl.h"
 #include "net/base/io_buffer.h"
+#include "net/base/test_completion_callback.h"
 #include "net/disk_cache/backend_impl.h"
 #include "net/disk_cache/entry_impl.h"
 #include "net/http/http_cache.h"
@@ -102,14 +104,15 @@
   RESULT_OK = 0,
   RESULT_UNKNOWN_COMMAND,
   RESULT_INVALID_PARAMETER,
-  RESULT_NAME_OVERFLOW
+  RESULT_NAME_OVERFLOW,
+  RESULT_PENDING  // This error code is NOT expected by the master process.
 };
 
 // -----------------------------------------------------------------------
 
 class BaseSM : public MessageLoopForIO::IOHandler {
  public:
-  BaseSM(HANDLE channel);
+  explicit BaseSM(HANDLE channel);
   virtual ~BaseSM();
 
  protected:
@@ -128,11 +131,14 @@
   scoped_array<char> out_buffer_;
   IoBuffer* input_;
   IoBuffer* output_;
+  base::Thread cache_thread_;
+
   DISALLOW_COPY_AND_ASSIGN(BaseSM);
 };
 
 BaseSM::BaseSM(HANDLE channel)
-      : entry_(NULL), channel_(channel), state_(0), pending_count_(0) {
+      : entry_(NULL), channel_(channel), state_(0), pending_count_(0),
+        cache_thread_("cache") {
   in_buffer_.reset(new char[kChannelSize]);
   out_buffer_.reset(new char[kChannelSize]);
   input_ = reinterpret_cast<IoBuffer*>(in_buffer_.get());
@@ -143,6 +149,8 @@
   in_context_.handler = this;
   out_context_.handler = this;
   MessageLoopForIO::current()->RegisterIOHandler(channel_, this);
+  CHECK(cache_thread_.StartWithOptions(
+            base::Thread::Options(MessageLoop::TYPE_IO, 0)));
 }
 
 BaseSM::~BaseSM() {
@@ -200,8 +208,12 @@
 
 class MasterSM : public BaseSM {
  public:
-   MasterSM(const std::wstring& path, HANDLE channel, bool dump_to_disk)
-      : BaseSM(channel), path_(path), dump_to_disk_(dump_to_disk) {
+  MasterSM(const std::wstring& path, HANDLE channel, bool dump_to_disk)
+      : BaseSM(channel), path_(path), dump_to_disk_(dump_to_disk),
+        ALLOW_THIS_IN_INITIALIZER_LIST(
+            create_callback_(this, &MasterSM::DoCreateEntryComplete)),
+        ALLOW_THIS_IN_INITIALIZER_LIST(
+            write_callback_(this, &MasterSM::DoReadDataComplete)) {
   }
   virtual ~MasterSM() {
     delete writer_;
@@ -227,12 +239,14 @@
   void SendGetPrevEntry();
   void DoGetEntry();
   void DoGetKey(int bytes_read);
+  void DoCreateEntryComplete(int result);
   void DoGetUseTimes();
   void SendGetDataSize();
   void DoGetDataSize();
   void CloseEntry();
   void SendReadData();
   void DoReadData(int bytes_read);
+  void DoReadDataComplete(int ret);
   void SendQuit();
   void DoEnd();
   void Fail();
@@ -244,10 +258,13 @@
   int bytes_remaining_;
   int offset_;
   int copied_entries_;
-  scoped_ptr<disk_cache::BackendImpl> cache_;
+  int read_size_;
+  scoped_ptr<disk_cache::Backend> cache_;
   CacheDumpWriter* writer_;
   const std::wstring& path_;
   bool dump_to_disk_;
+  net::CompletionCallbackImpl<MasterSM> create_callback_;
+  net::CompletionCallbackImpl<MasterSM> write_callback_;
 };
 
 void MasterSM::OnIOCompleted(MessageLoopForIO::IOContext* context,
@@ -302,11 +319,18 @@
   if (dump_to_disk_) {
     writer_ = new DiskDumper(path_);
   } else {
-    cache_.reset(new disk_cache::BackendImpl(FilePath::FromWStringHack(path_)));
-    if (!cache_->Init()) {
+    disk_cache::Backend* cache;
+    TestCompletionCallback cb;
+    int rv = disk_cache::CreateCacheBackend(net::DISK_CACHE,
+                                            FilePath::FromWStringHack(path_), 0,
+                                            false,
+                                            cache_thread_.message_loop_proxy(),
+                                            &cache, &cb);
+    if (cb.GetResult(rv) != net::OK) {
       printf("Unable to initialize new files\n");
       return false;
     }
+    cache_.reset(cache);
     writer_ = new CacheDumper(cache_.get());
   }
   if (!writer_)
@@ -367,11 +391,20 @@
     return Fail();
 
   std::string key(input_->buffer);
-  DCHECK(key.size() == input_->msg.buffer_bytes - 1);
+  DCHECK(key.size() == static_cast<size_t>(input_->msg.buffer_bytes - 1));
 
-  if (!writer_->CreateEntry(key,
-                            reinterpret_cast<disk_cache::Entry**>(&entry_))) {
-    printf("Skipping entry \"%s\" (name conflict!)\n", key.c_str());
+  int rv = writer_->CreateEntry(key,
+                                reinterpret_cast<disk_cache::Entry**>(&entry_),
+                                &create_callback_);
+
+  if (rv != net::ERR_IO_PENDING)
+    DoCreateEntryComplete(rv);
+}
+
+void MasterSM::DoCreateEntryComplete(int result) {
+  std::string key(input_->buffer);
+  if (result != net::OK) {
+    printf("Skipping entry \"%s\": %d\n", key.c_str(), GetLastError());
     return SendGetPrevEntry();
   }
 
@@ -474,7 +507,15 @@
 
   scoped_refptr<net::WrappedIOBuffer> buf =
       new net::WrappedIOBuffer(input_->buffer);
-  if (!writer_->WriteEntry(entry_, stream_, offset_, buf, read_size))
+  int rv = writer_->WriteEntry(entry_, stream_, offset_, buf, read_size,
+                               &write_callback_);
+  if (rv == net::ERR_IO_PENDING) {
+    // We'll continue in DoReadDataComplete.
+    read_size_ = read_size;
+    return;
+  }
+
+  if (rv <= 0)
     return Fail();
 
   offset_ += read_size;
@@ -483,6 +524,16 @@
   SendReadData();
 }
 
+void MasterSM::DoReadDataComplete(int ret) {
+  if (ret != read_size_)
+    return Fail();
+
+  offset_ += ret;
+  bytes_remaining_ -= ret;
+  // Read some more.
+  SendReadData();
+}
+
 void MasterSM::SendQuit() {
   DEBUGMSG("Master SendQuit\n");
   state_ = MASTER_END;
@@ -508,15 +559,7 @@
 
 class SlaveSM : public BaseSM {
  public:
-  SlaveSM(const std::wstring& path, HANDLE channel)
-      : BaseSM(channel), iterator_(NULL) {
-    cache_.reset(new disk_cache::BackendImpl(FilePath::FromWStringHack(path)));
-    if (!cache_->Init()) {
-      printf("Unable to open cache files\n");
-      return;
-    }
-    cache_->SetUpgradeMode();
-  }
+  SlaveSM(const std::wstring& path, HANDLE channel);
   virtual ~SlaveSM();
 
   bool DoInit();
@@ -533,19 +576,45 @@
   void DoGetNextEntry();
   void DoGetPrevEntry();
   int32 GetEntryFromList();
+  void DoGetEntryComplete(int result);
   void DoCloseEntry();
   void DoGetKey();
   void DoGetUseTimes();
   void DoGetDataSize();
   void DoReadData();
+  void DoReadDataComplete(int ret);
   void DoEnd();
   void Fail();
 
   void* iterator_;
+  Message msg_;  // Used for DoReadDataComplete and DoGetEntryComplete.
 
+  net::CompletionCallbackImpl<SlaveSM> read_callback_;
+  net::CompletionCallbackImpl<SlaveSM> next_callback_;
   scoped_ptr<disk_cache::BackendImpl> cache_;
 };
 
+SlaveSM::SlaveSM(const std::wstring& path, HANDLE channel)
+    : BaseSM(channel), iterator_(NULL),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          read_callback_(this, &SlaveSM::DoReadDataComplete)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          next_callback_(this, &SlaveSM::DoGetEntryComplete)) {
+  disk_cache::Backend* cache;
+  TestCompletionCallback cb;
+  int rv = disk_cache::CreateCacheBackend(net::DISK_CACHE,
+                                          FilePath::FromWStringHack(path), 0,
+                                          false,
+                                          cache_thread_.message_loop_proxy(),
+                                          &cache, &cb);
+  if (cb.GetResult(rv) != net::OK) {
+    printf("Unable to open cache files\n");
+    return;
+  }
+  cache_.reset(reinterpret_cast<disk_cache::BackendImpl*>(cache));
+  cache_->SetUpgradeMode();
+}
+
 SlaveSM::~SlaveSM() {
   if (iterator_)
     cache_->EndEnumeration(&iterator_);
@@ -608,6 +677,9 @@
   DEBUGMSG("\t\t\tSlave DoInit\n");
   DCHECK(state_ == SLAVE_INITIAL);
   state_ = SLAVE_WAITING;
+  if (!cache_.get())
+    return false;
+
   return ReceiveMsg();
 }
 
@@ -636,6 +708,11 @@
     msg.result = RESULT_UNKNOWN_COMMAND;
   } else {
     msg.result = GetEntryFromList();
+    if (msg.result == RESULT_PENDING) {
+      // We are not done yet.
+      msg_ = msg;
+      return;
+    }
     msg.long_arg1 = reinterpret_cast<int64>(entry_);
   }
   SendMsg(msg);
@@ -651,23 +728,31 @@
   if (entry_)
     entry_->Close();
 
-  bool ret;
+  int rv;
   if (input_->msg.command == GET_NEXT_ENTRY) {
-    ret = cache_->OpenNextEntry(&iterator_,
-                                reinterpret_cast<disk_cache::Entry**>(&entry_));
+    rv = cache_->OpenNextEntry(&iterator_,
+                               reinterpret_cast<disk_cache::Entry**>(&entry_),
+                               &next_callback_);
   } else {
     DCHECK(input_->msg.command == GET_PREV_ENTRY);
-    ret = cache_->OpenPrevEntry(&iterator_,
-                                reinterpret_cast<disk_cache::Entry**>(&entry_));
+    rv = cache_->OpenPrevEntry(&iterator_,
+                               reinterpret_cast<disk_cache::Entry**>(&entry_),
+                               &next_callback_);
+  }
+  DCHECK_EQ(net::ERR_IO_PENDING, rv);
+  return RESULT_PENDING;
+}
+
+void SlaveSM::DoGetEntryComplete(int result) {
+  DEBUGMSG("\t\t\tSlave DoGetEntryComplete\n");
+  if (result != net::OK) {
+    entry_ = NULL;
+    DEBUGMSG("\t\t\tSlave end of list\n");
   }
 
-  if (!ret)
-    entry_ = NULL;
-
-  if (!entry_)
-    DEBUGMSG("\t\t\tSlave end of list\n");
-
-  return RESULT_OK;
+  msg_.result = RESULT_OK;
+  msg_.long_arg1 = reinterpret_cast<int64>(entry_);
+  SendMsg(msg_);
 }
 
 void SlaveSM::DoCloseEntry() {
@@ -698,7 +783,7 @@
     msg.buffer_bytes = std::min(key.size() + 1,
                                 static_cast<size_t>(kBufferSize));
     memcpy(output_->buffer, key.c_str(), msg.buffer_bytes);
-    if (msg.buffer_bytes != key.size() + 1) {
+    if (msg.buffer_bytes != static_cast<int32>(key.size() + 1)) {
       // We don't support moving this entry. Just tell the master.
       msg.result = RESULT_NAME_OVERFLOW;
     } else {
@@ -753,7 +838,13 @@
   } else {
     scoped_refptr<net::WrappedIOBuffer> buf =
         new net::WrappedIOBuffer(output_->buffer);
-    int ret = entry_->ReadData(stream, input_->msg.arg3, buf, size, NULL);
+    int ret = entry_->ReadData(stream, input_->msg.arg3, buf, size,
+                               &read_callback_);
+    if (ret == net::ERR_IO_PENDING) {
+      // Save the message so we can continue were we left.
+      msg_ = msg;
+      return;
+    }
 
     msg.buffer_bytes = (ret < 0) ? 0 : ret;
     msg.result = RESULT_OK;
@@ -761,6 +852,14 @@
   SendMsg(msg);
 }
 
+void SlaveSM::DoReadDataComplete(int ret) {
+  DEBUGMSG("\t\t\tSlave DoReadDataComplete\n");
+  DCHECK_EQ(READ_DATA, msg_.command);
+  msg_.buffer_bytes = (ret < 0) ? 0 : ret;
+  msg_.result = RESULT_OK;
+  SendMsg(msg_);
+}
+
 void SlaveSM::DoEnd() {
   DEBUGMSG("\t\t\tSlave DoEnd\n");
   MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
diff --git a/net/tools/dump_cache/url_to_filename_encoder.cc b/net/tools/dump_cache/url_to_filename_encoder.cc
new file mode 100644
index 0000000..89a1ca4
--- /dev/null
+++ b/net/tools/dump_cache/url_to_filename_encoder.cc
@@ -0,0 +1,302 @@
+// Copyright (c) 2010 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/logging.h"
+#include "base/string_util.h"
+#include "net/base/net_util.h"
+#include "net/tools/dump_cache/url_to_filename_encoder.h"
+
+using std::string;
+
+namespace {
+
+inline bool IsHexDigit(unsigned char c) {
+  return (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') ||
+          ('a' <= c && c <= 'f'));
+}
+
+// Returns 1 if buf is prefixed by "num_digits" of hex digits
+// Teturns 0 otherwise.
+// The function checks for '\0' for string termination.
+int HexDigitsPrefix(const char* buf, int num_digits) {
+  for (int i = 0; i < num_digits; i++)
+    if (!IsHexDigit(buf[i]))
+      return 0;  // This also detects end of string as '\0' is not xdigit.
+  return 1;
+}
+
+#ifdef WIN32
+#define strtoull _strtoui64
+#endif
+
+// A simple parser for long long values. Returns the parsed value if a
+// valid integer is found; else returns deflt
+// UInt64 and Int64 cannot handle decimal numbers with leading 0s.
+uint64 ParseLeadingHex64Value(const char *str, uint64 deflt) {
+  char *error = NULL;
+  const uint64 value = strtoull(str, &error, 16);
+  return (error == str) ? deflt : value;
+}
+
+}
+
+namespace net {
+
+// The escape character choice is made here -- all code and tests in this
+// directory are based off of this constant.  However, our test ata
+// has tons of dependencies on this, so it cannot be changed without
+// re-running those tests and fixing them.
+const char kTruncationChar = '-';
+const char kEscapeChar = ',';
+const size_t kMaximumSubdirectoryLength = 128;
+
+void UrlToFilenameEncoder::AppendSegment(
+    char dir_separator, string* segment, string* dest) {
+  if (segment->empty() || (*segment == ".") || (*segment == "..")) {
+    dest->append(1, kEscapeChar);
+    dest->append(*segment);
+    segment->clear();
+  } else {
+    size_t segment_size = segment->size();
+    if (segment_size > kMaximumSubdirectoryLength) {
+      // We need to inject ",-" at the end of the segment to signify that
+      // we are inserting an artificial '/'.  This means we have to chop
+      // off at least two characters to make room.
+      segment_size = kMaximumSubdirectoryLength - 2;
+
+      // But we don't want to break up an escape sequence that happens to lie at
+      // the end.  Escape sequences are at most 2 characters.
+      if ((*segment)[segment_size - 1] == kEscapeChar) {
+        segment_size -= 1;
+      } else if ((*segment)[segment_size - 2] == kEscapeChar) {
+        segment_size -= 2;
+      }
+      dest->append(segment->data(), segment_size);
+      dest->append(1, kEscapeChar);
+      dest->append(1, kTruncationChar);
+      segment->erase(0, segment_size);
+
+      // At this point, if we had segment_size=3, and segment="abcd",
+      // then after this erase, we will have written "abc,-" and set segment="d"
+    } else {
+      dest->append(*segment);
+      segment->clear();
+    }
+  }
+}
+
+void UrlToFilenameEncoder::EncodeSegment(const string& filename_prefix,
+                                         const string& filename_ending,
+                                         char dir_separator,
+                                         string* encoded_filename) {
+  char encoded[3];
+  int encoded_len;
+  string segment;
+
+  // TODO(jmarantz): This code would be a bit simpler if we disallowed
+  // Instaweb allowing filename_prefix to not end in "/".  We could
+  // then change the is routine to just take one input string.
+  size_t start_of_segment = filename_prefix.find_last_of(dir_separator);
+  if (start_of_segment == string::npos) {
+    segment = filename_prefix;
+  } else {
+    segment = filename_prefix.substr(start_of_segment + 1);
+    *encoded_filename = filename_prefix.substr(0, start_of_segment + 1);
+  }
+
+  size_t index = 0;
+  // Special case the first / to avoid adding a leading kEscapeChar.
+  if (!filename_ending.empty() && (filename_ending[0] == dir_separator)) {
+    encoded_filename->append(segment);
+    segment.clear();
+    encoded_filename->append(1, dir_separator);
+    ++index;
+  }
+
+  for (; index < filename_ending.length(); ++index) {
+    unsigned char ch = static_cast<unsigned char>(filename_ending[index]);
+
+    if (ch == dir_separator) {
+      AppendSegment(dir_separator, &segment, encoded_filename);
+      encoded_filename->append(1, dir_separator);
+      segment.clear();
+    } else {
+      // & is common in URLs and is legal filename syntax, but is also
+      // a special Unix shell character, so let's avoid making
+      // filenames with &, as well as ?.  It's probably better to
+      // blow up query-params than it is to make it hard to work with
+      // the files in shell-scripts.
+      if ((ch == 0x5F) || (ch == 0x2E) ||    // underscore period
+          (ch == 0x25) || (ch == 0x3D) ||    // percent equals
+          (ch == 0x2B) || (ch == 0x2D) ||    // plus dash
+          ((0x30 <= ch) && (ch <= 0x39)) ||  // Digits [0-9]
+          ((0x41 <= ch) && (ch <= 0x5A)) ||  // Uppercase [A-Z]
+          ((0x61 <= ch) && (ch <= 0x7A))) {  // Lowercase [a-z]
+        encoded[0] = ch;
+        encoded_len = 1;
+      } else {
+        encoded[0] = kEscapeChar;
+        encoded[1] = ch / 16;
+        encoded[1] += (encoded[1] >= 10) ? 'A' - 10 : '0';
+        encoded[2] = ch % 16;
+        encoded[2] += (encoded[2] >= 10) ? 'A' - 10 : '0';
+        encoded_len = 3;
+      }
+      segment.append(encoded, encoded_len);
+
+      // Note:  We chop paths into medium sized 'chunks'.
+      //        This is due to filename limits on Windows and Unix.
+      //        The Windows limit appears to be 128 characters, and
+      //        Unix is larger, but not as large as URLs with large
+      //        numbers of query params.
+      if (segment.size() > kMaximumSubdirectoryLength) {
+        AppendSegment(dir_separator, &segment, encoded_filename);
+        encoded_filename->append(1, dir_separator);
+      }
+    }
+  }
+
+  // Append "," to the leaf filename so the leaf can also be a branch., e.g.
+  // allow http://a/b/c and http://a/b/c/d to co-exist as files "/a/b/c," and
+  // /a/b/c/d".  So we will rename the "d" here to "d,".  If doing that pushed
+  // us over the 128 char limit, then we will need to append "/" and the
+  // remaining chars.
+  segment += kEscapeChar;
+  AppendSegment(dir_separator, &segment, encoded_filename);
+  if (!segment.empty()) {
+    // The last overflow segment is special, because we appended in
+    // kEscapeChar above.  We won't need to check it again for size
+    // or further escaping.
+    encoded_filename->append(1, dir_separator);
+    encoded_filename->append(segment);
+  }
+}
+
+// Note: this decoder is not the exact inverse of the EncodeSegment above,
+// because it does not take into account a prefix.
+bool UrlToFilenameEncoder::Decode(const string& encoded_filename,
+                                  char dir_separator,
+                                  string* decoded_url) {
+  enum State {
+    kStart,
+    kEscape,
+    kFirstDigit,
+    kTruncate,
+    kEscapeDot
+  };
+  State state = kStart;
+  int char_code = 0;
+  char hex_buffer[3];
+  hex_buffer[2] = '\0';
+  for (size_t i = 0; i < encoded_filename.size(); ++i) {
+    char ch = encoded_filename[i];
+    switch (state) {
+      case kStart:
+        if (ch == kEscapeChar) {
+          state = kEscape;
+        } else {
+          decoded_url->append(1, ch);
+        }
+        break;
+      case kEscape:
+        if (HexDigitsPrefix(&ch, 1) == 1) {
+          hex_buffer[0] = ch;
+          state = kFirstDigit;
+        } else if (ch == kTruncationChar) {
+          state = kTruncate;
+        } else if (ch == '.') {
+          decoded_url->append(1, '.');
+          state = kEscapeDot;  // Look for at most one more dot.
+        } else if (ch == dir_separator) {
+          // Consider url "//x".  This will get encoded to "/,/x,".
+          // This code is what skips the first Escape.
+          decoded_url->append(1, ch);
+          state = kStart;
+        } else {
+          return false;
+        }
+        break;
+      case kFirstDigit:
+        if (HexDigitsPrefix(&ch, 1) == 1) {
+          hex_buffer[1] = ch;
+          uint64 hex_value = ParseLeadingHex64Value(hex_buffer, 0);
+          decoded_url->append(1, static_cast<char>(hex_value));
+          char_code = 0;
+          state = kStart;
+        } else {
+          return false;
+        }
+        break;
+      case kTruncate:
+        if (ch == dir_separator) {
+          // Skip this separator, it was only put in to break up long
+          // path segments, but is not part of the URL.
+          state = kStart;
+        } else {
+          return false;
+        }
+        break;
+      case kEscapeDot:
+        decoded_url->append(1, ch);
+        state = kStart;
+        break;
+    }
+  }
+
+  // All legal encoded filenames end in kEscapeChar.
+  return (state == kEscape);
+}
+
+// Escapes the given input |path| and chop any individual components
+// of the path which are greater than kMaximumSubdirectoryLength characters
+// into two chunks.
+//
+// This legacy version has several issues with aliasing of different URLs,
+// inability to represent both /a/b/c and /a/b/c/d, and inability to decode
+// the filenames back into URLs.
+//
+// But there is a large body of slurped data which depends on this format,
+// so leave it as the default for spdy_in_mem_edsm_server.
+string UrlToFilenameEncoder::LegacyEscape(const string& path) {
+  string output;
+
+  // Note:  We also chop paths into medium sized 'chunks'.
+  //        This is due to the incompetence of the windows
+  //        filesystem, which still hasn't figured out how
+  //        to deal with long filenames.
+  int last_slash = 0;
+  for (size_t index = 0; index < path.length(); index++) {
+    char ch = path[index];
+    if (ch == 0x5C)
+      last_slash = index;
+    if ((ch == 0x2D) ||                    // hyphen
+        (ch == 0x5C) || (ch == 0x5F) ||    // backslash, underscore
+        ((0x30 <= ch) && (ch <= 0x39)) ||  // Digits [0-9]
+        ((0x41 <= ch) && (ch <= 0x5A)) ||  // Uppercase [A-Z]
+        ((0x61 <= ch) && (ch <= 0x7A))) {  // Lowercase [a-z]
+      output.append(&path[index], 1);
+    } else {
+      char encoded[3];
+      encoded[0] = 'x';
+      encoded[1] = ch / 16;
+      encoded[1] += (encoded[1] >= 10) ? 'A' - 10 : '0';
+      encoded[2] = ch % 16;
+      encoded[2] += (encoded[2] >= 10) ? 'A' - 10 : '0';
+      output.append(encoded, 3);
+    }
+    if (index - last_slash > kMaximumSubdirectoryLength) {
+#ifdef WIN32
+      char slash = '\\';
+#else
+      char slash = '/';
+#endif
+      output.append(&slash, 1);
+      last_slash = index;
+    }
+  }
+  return output;
+}
+
+}  // namespace net
+
diff --git a/net/tools/dump_cache/url_to_filename_encoder.h b/net/tools/dump_cache/url_to_filename_encoder.h
index 4b9e6c5..b5cac37 100644
--- a/net/tools/dump_cache/url_to_filename_encoder.h
+++ b/net/tools/dump_cache/url_to_filename_encoder.h
@@ -1,7 +1,77 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
+// URL filename encoder goals:
+//
+// 1. Allow URLs with arbitrary path-segment length, generating filenames
+//    with a maximum of 128 characters.
+// 2. Provide a somewhat human readable filenames, for easy debugging flow.
+// 3. Provide reverse-mapping from filenames back to URLs.
+// 4. Be able to distinguish http://x from http://x/ from http://x/index.html.
+//    Those can all be different URLs.
+// 5. Be able to represent http://a/b/c and http://a/b/c/d, a pattern seen
+//    with Facebook Connect.
+//
+// We need an escape-character for representing characters that are legal
+// in URL paths, but not in filenames, such as '?'.  Illegal characters
+// in Windows are <>:"/\|?*.  For reference, see
+//   http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+//
+// We can pick any legal character as an escape, as long as we escape it too.
+// But as we have a goal of having filenames that humans can correlate with
+// URLs, we should pick one that doesn't show up frequently in URLs. Candidates
+// are ~`!@#$%^&()-=_+{}[],. but we would prefer to avoid characters that are
+// shell escapes, and characters that occur frequently in URLs.
+//
+// .#&%-=_+ occur frequently in URLs.
+// ~`!$^&(){}[] are special to Unix shells
+//
+// @ might seem like a reasonble option, but some build tools don't appreciate
+// filenames with @ in testdata.  Perforce does not appreciate # in a filename.
+//
+// Though a web-site http://www.vias.org/linux-knowhow/lnag_05_05_09.html
+// identifies ^ as a special shell character, it did not appear to be an
+// issue to use it unquoted as a filename in bash or tcsh.
+//
+// Here are some frequencies of some special characters in a data set from Fall
+// '09.  We find only 3 occurences of "x5E" (^ is ascii 0x53):
+//   ^   3               build tools don't like ^ in testdata filenames
+//   @   10              build tools don't like @ in testdata filenames
+//   .   1676            too frequent in URLs
+//   ,   76              THE WINNER
+//   #   0               build tools doesn't like it
+//   &   487             Prefer to avoid shell escapes
+//   %   374             g4 doesn't like it
+//   =   579             very frequent in URLs -- leave unmodified
+//   -   464             very frequent in URLs -- leave unmodified
+//   _   798             very frequent in URLs -- leave unmodified
+//
+// It is interesting that there were no slurped URLs with #, but I suspect this
+// might be due to the slurping methdology.  So let's stick with the relatively
+// rare ','.
+//
+// Here's the escaping methodology:
+//
+//     URL               File
+//     /                 /,
+//     /.                /.,
+//     //                /,/,
+//     /./               /,./,
+//     /../              /,../,
+//     /,                /,2C,
+//     /,/               /,2C/,
+//     /a/b              /a/b,     (, at the end of a name indicates a leaf).
+//     /a/b/             /a/b/,
+//
+// path segments greater than 128 characters (after escape expansion) are
+// suffixed with ,- so we can know that the next "/" is not part of the URL:
+//
+//    /verylongname/    /verylong,-/name
+
+// NOTE: we avoid using some classes here (like FilePath and GURL) because we
+//       share this code with other projects externally.
+
 #ifndef NET_TOOLS_DUMP_CACHE_URL_TO_FILE_ENCODER_H_
 #define NET_TOOLS_DUMP_CACHE_URL_TO_FILE_ENCODER_H_
 
@@ -10,25 +80,31 @@
 #include "base/file_path.h"
 #include "base/file_util.h"
 #include "base/string_util.h"
-#include "googleurl/src/gurl.h"
+#include "net/tools/dump_cache/url_utilities.h"
 
 namespace net {
 
 // Helper class for converting a URL into a filename.
 class UrlToFilenameEncoder {
  public:
-  // Given a |url| and a |base_path|, returns a FilePath which represents this
+  // Given a |url| and a |base_path|, returns a string which represents this
   // |url|.
-  static FilePath Encode(const std::string& url, FilePath base_path) {
+  // |legacy_escape| indicates that this function should use the old-style
+  // of encoding.
+  // TODO(mbelshe): delete the legacy_escape code.
+  static std::string Encode(const std::string& url, std::string base_path,
+                            bool legacy_escape) {
     std::string clean_url(url);
     if (clean_url.length() && clean_url[clean_url.length()-1] == '/')
       clean_url.append("index.html");
 
-    GURL gurl(clean_url);
-    FilePath filename(base_path);
-    filename = filename.AppendASCII(gurl.host());
+    std::string host = UrlUtilities::GetUrlHost(clean_url);
+    std::string filename(base_path);
+    filename.append("\\");
+    filename = filename.append(host);
+    filename.append("\\");
 
-    std::string url_filename = gurl.PathForRequest();
+    std::string url_filename = UrlUtilities::GetUrlPath(clean_url);
     // Strip the leading '/'
     if (url_filename[0] == '/')
       url_filename = url_filename.substr(1);
@@ -40,59 +116,71 @@
     StripDoubleSlashes(&url_filename);
 
     // Save path as filesystem-safe characters
-    url_filename = Escape(url_filename);
-    filename = filename.AppendASCII(url_filename);
+    if (legacy_escape) {
+      url_filename = LegacyEscape(url_filename);
+    } else {
+      url_filename = Escape(url_filename);
+    }
+    filename = filename.append(url_filename);
+
+#ifndef WIN32
+    // Last step - convert to native slashes!
+    const std::string slash("/");
+    const std::string backslash("\\");
+    ReplaceAll(&filename, backslash, slash);
+#endif
 
     return filename;
   }
 
- private:
-  // This is the length at which we chop individual subdirectories.
-  // Technically, we shouldn't need to do this, but I found that
-  // even with long-filename support, windows had trouble creating
-  // long subdirectories, and making them shorter helps.
-  static const size_t kMaximumSubdirectoryLength = 128;
+  // Rewrite HTML in a form that the SPDY in-memory server
+  // can read.
+  // |filename_prefix| is prepended without escaping.
+  // |filename_ending| is the URL to be encoded into a filename.
+  // |dir_separator| is "/" on Unix, "\" on Windows.
+  // |encoded_filename| is the resultant filename.
+  static void EncodeSegment(
+      const std::string& filename_prefix,
+      const std::string& filename_ending,
+      char dir_separator,
+      std::string* encoded_filename);
 
-  // Escape the given input |path| and chop any individual components
+  // Decodes a filename that was encoded with EncodeSegment,
+  // yielding back the original URL.
+  static bool Decode(const std::string& encoded_filename,
+                     char dir_separator,
+                     std::string* decoded_url);
+
+ private:
+  // Appends a segment of the path, special-casing ".", "..", and "", and
+  // ensuring that the segment does not exceed the path length.  If it does,
+  // it chops the end off the segment, writes the segment with a separator of
+  // ",-/", and then rewrites segment to contain just the truncated piece so
+  // it can be used in the next iteration.
+  // |dir_separator| is "/" on Unix, "\" on Windows.
+  // |segment| is a read/write parameter containing segment to write
+  static void AppendSegment(
+      char dir_separator,
+      std::string* segment,
+      std::string* dest);
+
+  // Escapes the given input |path| and chop any individual components
   // of the path which are greater than kMaximumSubdirectoryLength characters
   // into two chunks.
   static std::string Escape(const std::string& path) {
     std::string output;
-    int last_slash = 0;
-    for (size_t index = 0; index < path.length(); index++) {
-      char ch = path[index];
-      if (ch == 0x5C)
-        last_slash = index;
-      if ((ch == 0x2D) ||                   // hyphen
-          (ch == 0x5C) || (ch == 0x5F) ||   // backslash, underscore
-          ((0x30 <= ch) && (ch <= 0x39)) || // Digits [0-9]
-          ((0x41 <= ch) && (ch <= 0x5A)) || // Uppercase [A-Z]
-          ((0x61 <= ch) && (ch <= 0x7A))) { // Lowercase [a-z]
-        output.append(&path[index],1);
-      } else {
-        char encoded[3];
-        encoded[0] = 'x';
-        encoded[1] = ch / 16;
-        encoded[1] += (encoded[1] >= 10) ? 'A' - 10 : '0';
-        encoded[2] = ch % 16;
-        encoded[2] += (encoded[2] >= 10) ? 'A' - 10 : '0';
-        output.append(encoded, 3);
-      }
-      if (index - last_slash > kMaximumSubdirectoryLength) {
-        char backslash = '\\';
-        output.append(&backslash, 1);
-        last_slash = index;
-      }
-    }
+    EncodeSegment("", path, '\\', &output);
     return output;
   }
 
+  // Allow reading of old slurped files.
+  static std::string LegacyEscape(const std::string& path);
+
   // Replace all instances of |from| within |str| as |to|.
-  static void ReplaceAll(const std::string& from,
-                         const std::string& to,
-                         std::string* str) {
+  static void ReplaceAll(std::string* str, const std::string& from,
+                  const std::string& to) {
     std::string::size_type pos(0);
-    while((pos = str->find(from, pos)) != std::string::npos) {
+    while ((pos = str->find(from, pos)) != std::string::npos) {
       str->replace(pos, from.size(), to);
       pos += from.size();
     }
@@ -100,21 +188,20 @@
 
   // Replace all instances of "/" with "\" in |path|.
   static void ConvertToSlashes(std::string* path) {
-    static const char slash[] = { '/', '\0' };
-    static const char backslash[] = { '\\', '\0' };
-    ReplaceAll(slash, backslash, path);
+    const std::string slash("/");
+    const std::string backslash("\\");
+    ReplaceAll(path, slash, backslash);
   }
 
   // Replace all instances of "\\" with "%5C%5C" in |path|.
   static void StripDoubleSlashes(std::string* path) {
-    static const char doubleslash[] = { '\\', '\\', '\0' };
-    static const char escaped_doubleslash[] =
-      { '%', '5', 'C', '%', '5', 'C','\0' };
-    ReplaceAll(doubleslash, escaped_doubleslash, path);
+    const std::string doubleslash("\\\\");
+    const std::string escaped_doubleslash("%5C%5C");
+    ReplaceAll(path, doubleslash, escaped_doubleslash);
   }
 };
 
-} // namespace net
+}  // namespace net
 
-#endif  // NET_TOOLS_DUMP_CACHE_URL_TO_FILE_ENCODER_H__
+#endif  // NET_TOOLS_DUMP_CACHE_URL_TO_FILE_ENCODER_H_
 
diff --git a/net/tools/dump_cache/url_to_filename_encoder_unittest.cc b/net/tools/dump_cache/url_to_filename_encoder_unittest.cc
new file mode 100644
index 0000000..32cef99
--- /dev/null
+++ b/net/tools/dump_cache/url_to_filename_encoder_unittest.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2010 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/dump_cache/url_to_filename_encoder.h"
+
+#include <string>
+#include <vector>
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+// The escape character choice is made here -- all code and tests in this
+// directory are based off of this constant.  However, our test ata
+// has tons of dependencies on this, so it cannot be changed without
+// re-running those tests and fixing them.
+const char kTruncationChar = '-';
+const char kEscapeChar = ',';
+const size_t kMaximumSubdirectoryLength = 128;
+
+class UrlToFilenameEncoderTest : public ::testing::Test {
+ protected:
+  UrlToFilenameEncoderTest() : escape_(1, kEscapeChar) {}
+
+  void CheckSegmentLength(const StringPiece& escaped_word) {
+    std::vector<StringPiece> components;
+    Tokenize(escaped_word, StringPiece("/"), &components);
+    for (size_t i = 0; i < components.size(); ++i) {
+      EXPECT_GE(kMaximumSubdirectoryLength,
+                components[i].size());
+    }
+  }
+
+  void CheckValidChars(const StringPiece& escaped_word) {
+    // These characters are invalid in Windows.  We will
+    // ignore / for this test, but add in ', as that's pretty
+    // inconvenient in a Unix filename.
+    //
+    // See http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+    static const char kInvalidChars[] = "<>:\"\\|?*'";
+    for (size_t i = 0; i < escaped_word.size(); ++i) {
+      char c = escaped_word[i];
+      EXPECT_EQ(NULL, strchr(kInvalidChars, c));
+      EXPECT_NE('\0', c);  // only invalid character in Posix
+      EXPECT_GT(0x7E, c);  // only English printable characters
+    }
+  }
+
+  void Validate(const string& in_word, const string& gold_word) {
+    string escaped_word, url;
+    UrlToFilenameEncoder::EncodeSegment("", in_word, '/', &escaped_word);
+    EXPECT_EQ(gold_word, escaped_word);
+    CheckSegmentLength(escaped_word);
+    CheckValidChars(escaped_word);
+    UrlToFilenameEncoder::Decode(escaped_word, '/', &url);
+    EXPECT_EQ(in_word, url);
+  }
+
+  void ValidateAllSegmentsSmall(const string& in_word) {
+    string escaped_word, url;
+    UrlToFilenameEncoder::EncodeSegment("", in_word, '/', &escaped_word);
+    CheckSegmentLength(escaped_word);
+    CheckValidChars(escaped_word);
+    UrlToFilenameEncoder::Decode(escaped_word, '/', &url);
+    EXPECT_EQ(in_word, url);
+  }
+
+  void ValidateNoChange(const string& word) {
+    // We always suffix the leaf with kEscapeChar, unless the leaf is empty.
+    Validate(word, word + escape_);
+  }
+
+  void ValidateEscaped(unsigned char ch) {
+    // We always suffix the leaf with kEscapeChar, unless the leaf is empty.
+    char escaped[100];
+    const char escape = kEscapeChar;
+    base::snprintf(escaped, sizeof(escaped), "%c%02X%c", escape, ch, escape);
+    Validate(string(1, ch), escaped);
+  }
+
+  string escape_;
+};
+
+TEST_F(UrlToFilenameEncoderTest, DoesNotEscape) {
+  ValidateNoChange("");
+  ValidateNoChange("abcdefg");
+  ValidateNoChange("abcdefghijklmnopqrstuvwxyz");
+  ValidateNoChange("ZYXWVUT");
+  ValidateNoChange("ZYXWVUTSRQPONMLKJIHGFEDCBA");
+  ValidateNoChange("01234567689");
+  ValidateNoChange("/-_");
+  ValidateNoChange("abcdefghijklmnopqrstuvwxyzZYXWVUTSRQPONMLKJIHGFEDCBA"
+                   "01234567689/-_");
+  ValidateNoChange("index.html");
+  ValidateNoChange("/");
+  ValidateNoChange("/.");
+  ValidateNoChange(".");
+  ValidateNoChange("..");
+  ValidateNoChange("%");
+  ValidateNoChange("=");
+  ValidateNoChange("+");
+  ValidateNoChange("_");
+}
+
+TEST_F(UrlToFilenameEncoderTest, Escapes) {
+  ValidateEscaped('!');
+  ValidateEscaped('"');
+  ValidateEscaped('#');
+  ValidateEscaped('$');
+  ValidateEscaped('&');
+  ValidateEscaped('(');
+  ValidateEscaped(')');
+  ValidateEscaped('*');
+  ValidateEscaped(',');
+  ValidateEscaped(':');
+  ValidateEscaped(';');
+  ValidateEscaped('<');
+  ValidateEscaped('>');
+  ValidateEscaped('@');
+  ValidateEscaped('[');
+  ValidateEscaped('\'');
+  ValidateEscaped('\\');
+  ValidateEscaped(']');
+  ValidateEscaped('^');
+  ValidateEscaped('`');
+  ValidateEscaped('{');
+  ValidateEscaped('|');
+  ValidateEscaped('}');
+  ValidateEscaped('~');
+
+  // check non-printable characters
+  ValidateEscaped('\0');
+  for (int i = 127; i < 256; ++i) {
+    ValidateEscaped(static_cast<char>(i));
+  }
+}
+
+TEST_F(UrlToFilenameEncoderTest, DoesEscapeCorrectly) {
+  Validate("mysite.com&x", "mysite.com" + escape_ + "26x" + escape_);
+  Validate("/./", "/" + escape_ + "./" + escape_);
+  Validate("/../", "/" + escape_ + "../" + escape_);
+  Validate("//", "/" + escape_ + "/" + escape_);
+  Validate("/./leaf", "/" + escape_ + "./leaf" + escape_);
+  Validate("/../leaf", "/" + escape_ + "../leaf" + escape_);
+  Validate("//leaf", "/" + escape_ + "/leaf" + escape_);
+  Validate("mysite/u?param1=x&param2=y",
+           "mysite/u" + escape_ + "3Fparam1=x" + escape_ + "26param2=y" +
+           escape_);
+  Validate("search?q=dogs&go=&form=QBLH&qs=n",  // from Latency Labs bing test.
+           "search" + escape_ + "3Fq=dogs" + escape_ + "26go=" + escape_ +
+           "26form=QBLH" + escape_ + "26qs=n" + escape_);
+  Validate("~joebob/my_neeto-website+with_stuff.asp?id=138&content=true",
+           "" + escape_ + "7Ejoebob/my_neeto-website+with_stuff.asp" + escape_ +
+           "3Fid=138" + escape_ + "26content=true" + escape_);
+}
+
+TEST_F(UrlToFilenameEncoderTest, LongTail) {
+  static char long_word[] =
+      "~joebob/briggs/12345678901234567890123456789012345678901234567890"
+      "1234567890123456789012345678901234567890123456789012345678901234567890"
+      "1234567890123456789012345678901234567890123456789012345678901234567890"
+      "1234567890123456789012345678901234567890123456789012345678901234567890"
+      "1234567890123456789012345678901234567890123456789012345678901234567890"
+      "1234567890123456789012345678901234567890123456789012345678901234567890";
+
+  // the long lines in the string below are 64 characters, so we can see
+  // the slashes every 128.
+  string gold_long_word =
+      escape_ + "7Ejoebob/briggs/"
+      "1234567890123456789012345678901234567890123456789012345678901234"
+      "56789012345678901234567890123456789012345678901234567890123456" +
+      escape_ + "-/"
+      "7890123456789012345678901234567890123456789012345678901234567890"
+      "12345678901234567890123456789012345678901234567890123456789012" +
+      escape_ + "-/"
+      "3456789012345678901234567890123456789012345678901234567890123456"
+      "78901234567890123456789012345678901234567890123456789012345678" +
+      escape_ + "-/"
+      "9012345678901234567890" + escape_;
+  EXPECT_LT(kMaximumSubdirectoryLength,
+            sizeof(long_word));
+  Validate(long_word, gold_long_word);
+}
+
+TEST_F(UrlToFilenameEncoderTest, LongTailQuestion) {
+  // Here the '?' in the last path segment expands to @3F, making
+  // it hit 128 chars before the input segment gets that big.
+  static char long_word[] =
+      "~joebob/briggs/1234567?1234567?1234567?1234567?1234567?"
+      "1234567?1234567?1234567?1234567?1234567?1234567?1234567?"
+      "1234567?1234567?1234567?1234567?1234567?1234567?1234567?"
+      "1234567?1234567?1234567?1234567?1234567?1234567?1234567?"
+      "1234567?1234567?1234567?1234567?1234567?1234567?1234567?"
+      "1234567?1234567?1234567?1234567?1234567?1234567?1234567?";
+
+  // Notice that at the end of the third segment, we avoid splitting
+  // the (escape_ + "3F") that was generated from the "?", so that segment is
+  // only 127 characters.
+  string pattern = "1234567" + escape_ + "3F";  // 10 characters
+  string gold_long_word =
+      escape_ + "7Ejoebob/briggs/" +
+      pattern + pattern + pattern + pattern + pattern + pattern + "1234"
+      "567" + escape_ + "3F" + pattern + pattern + pattern + pattern + pattern +
+       "123456" + escape_ + "-/"
+      "7" + escape_ + "3F" + pattern + pattern + pattern + pattern + pattern +
+      pattern + pattern + pattern + pattern + pattern + pattern + pattern +
+      "12" +
+      escape_ + "-/"
+      "34567" + escape_ + "3F" + pattern + pattern + pattern + pattern + pattern
+      + "1234567" + escape_ + "3F" + pattern + pattern + pattern + pattern
+      + pattern + "1234567" +
+      escape_ + "-/" +
+      escape_ + "3F" + pattern + pattern + escape_;
+  EXPECT_LT(kMaximumSubdirectoryLength,
+            sizeof(long_word));
+  Validate(long_word, gold_long_word);
+}
+
+TEST_F(UrlToFilenameEncoderTest, CornerCasesNearMaxLenNoEscape) {
+  // hit corner cases, +/- 4 characters from kMaxLen
+  for (int i = -4; i <= 4; ++i) {
+    string input;
+    input.append(i + kMaximumSubdirectoryLength, 'x');
+    ValidateAllSegmentsSmall(input);
+  }
+}
+
+TEST_F(UrlToFilenameEncoderTest, CornerCasesNearMaxLenWithEscape) {
+  // hit corner cases, +/- 4 characters from kMaxLen.  This time we
+  // leave off the last 'x' and put in a '.', which ensures that we
+  // are truncating with '/' *after* the expansion.
+  for (int i = -4; i <= 4; ++i) {
+    string input;
+    input.append(i + kMaximumSubdirectoryLength - 1, 'x');
+    input.append(1, '.');  // this will expand to 3 characters.
+    ValidateAllSegmentsSmall(input);
+  }
+}
+
+TEST_F(UrlToFilenameEncoderTest, LeafBranchAlias) {
+  Validate("/a/b/c", "/a/b/c" + escape_);        // c is leaf file "c,"
+  Validate("/a/b/c/d", "/a/b/c/d" + escape_);    // c is directory "c"
+  Validate("/a/b/c/d/", "/a/b/c/d/" + escape_);
+}
+
+
+TEST_F(UrlToFilenameEncoderTest, BackslashSeparator) {
+  string long_word;
+  string escaped_word;
+  long_word.append(kMaximumSubdirectoryLength + 1, 'x');
+  UrlToFilenameEncoder::EncodeSegment("", long_word, '\\', &escaped_word);
+
+  // check that one backslash, plus the escape ",-", and the ending , got added.
+  EXPECT_EQ(long_word.size() + 4, escaped_word.size());
+  ASSERT_LT(kMaximumSubdirectoryLength,
+            escaped_word.size());
+  // Check that the backslash got inserted at the correct spot.
+  EXPECT_EQ('\\', escaped_word[
+      kMaximumSubdirectoryLength]);
+}
+
+}  // namespace
+
diff --git a/net/tools/dump_cache/url_utilities.h b/net/tools/dump_cache/url_utilities.h
new file mode 100644
index 0000000..4de95dc
--- /dev/null
+++ b/net/tools/dump_cache/url_utilities.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2010 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_DUMP_CACHE_URL_UTILITIES_H_
+#define NET_TOOLS_DUMP_CACHE_URL_UTILITIES_H_
+
+#include <string>
+
+namespace net {
+
+namespace UrlUtilities {
+
+// Gets the host from an url, strips the port number as well if the url
+// has one.
+// For example: calling GetUrlHost(www.foo.com:8080/boo) returns www.foo.com
+static std::string GetUrlHost(const std::string& url) {
+  size_t b = url.find("//");
+  if (b == std::string::npos)
+    b = 0;
+  else
+    b += 2;
+  size_t next_slash = url.find_first_of('/', b);
+  size_t next_colon = url.find_first_of(':', b);
+  if (next_slash != std::string::npos
+      && next_colon != std::string::npos
+      && next_colon < next_slash) {
+    return std::string(url, b, next_colon - b);
+  }
+  if (next_slash == std::string::npos) {
+    if (next_colon != std::string::npos) {
+      return std::string(url, next_colon - b);
+    } else {
+      next_slash = url.size();
+    }
+  }
+  return std::string(url, b, next_slash - b);
+}
+
+// Gets the path portion of an url.
+// e.g   http://www.foo.com/path
+//       returns /path
+static std::string GetUrlPath(const std::string& url) {
+  size_t b = url.find("//");
+  if (b == std::string::npos)
+    b = 0;
+  else
+    b += 2;
+  b = url.find("/", b);
+  if (b == std::string::npos)
+    return "/";
+
+  size_t e = url.find("#", b+1);
+  if (e != std::string::npos)
+    return std::string(url, b, (e - b));
+  return std::string(url, b);
+}
+
+}  // namespace UrlUtilities
+
+}  // namespace net
+
+#endif  // NET_TOOLS_DUMP_CACHE_URL_UTILITIES_H_
+
diff --git a/net/tools/fetch/fetch_client.cc b/net/tools/fetch/fetch_client.cc
index 120b5ca..8863dd7 100644
--- a/net/tools/fetch/fetch_client.cc
+++ b/net/tools/fetch/fetch_client.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -15,6 +15,7 @@
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
 #include "net/base/ssl_config_service.h"
+#include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_network_layer.h"
 #include "net/http/http_request_info.h"
@@ -62,7 +63,7 @@
     request_info_.url = url_;
     request_info_.method = "GET";
     int state = transaction_->Start(
-        &request_info_, &connect_callback_, NULL);
+        &request_info_, &connect_callback_, net::BoundNetLog());
     DCHECK(state == net::ERR_IO_PENDING);
   };
 
@@ -132,20 +133,24 @@
   MessageLoop loop(MessageLoop::TYPE_IO);
 
   scoped_refptr<net::HostResolver> host_resolver(
-      net::CreateSystemHostResolver(NULL));
+      net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism));
 
   scoped_refptr<net::ProxyService> proxy_service(
       net::ProxyService::CreateNull());
   scoped_refptr<net::SSLConfigService> ssl_config_service(
       net::SSLConfigService::CreateSystemSSLConfigService());
   net::HttpTransactionFactory* factory = NULL;
+  scoped_ptr<net::HttpAuthHandlerFactory> http_auth_handler_factory(
+      net::HttpAuthHandlerFactory::CreateDefault());
   if (use_cache) {
-    factory = new net::HttpCache(NULL, host_resolver, proxy_service,
-                                 ssl_config_service, 0);
+    factory = new net::HttpCache(host_resolver, proxy_service,
+        ssl_config_service, http_auth_handler_factory.get(), NULL, NULL,
+        net::HttpCache::DefaultBackend::InMemory(0));
   } else {
     factory = new net::HttpNetworkLayer(
-        net::ClientSocketFactory::GetDefaultFactory(), NULL, host_resolver,
-        proxy_service, ssl_config_service);
+        net::ClientSocketFactory::GetDefaultFactory(), host_resolver,
+        proxy_service, ssl_config_service, http_auth_handler_factory.get(),
+        NULL, NULL);
   }
 
   {
diff --git a/net/tools/fetch/http_listen_socket.cc b/net/tools/fetch/http_listen_socket.cc
index 033afc4..d0d6b97 100644
--- a/net/tools/fetch/http_listen_socket.cc
+++ b/net/tools/fetch/http_listen_socket.cc
@@ -2,11 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "net/tools/fetch/http_listen_socket.h"
+
+#include <map>
+
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/message_loop.h"
 #include "base/string_util.h"
-#include "net/tools/fetch/http_listen_socket.h"
 #include "net/tools/fetch/http_server_request_info.h"
 #include "net/tools/fetch/http_server_response_info.h"
 
@@ -182,8 +185,9 @@
 }
 
 void HttpListenSocket::DidRead(ListenSocket* connection,
-                               const std::string& data) {
-  recv_data_ += data;
+                               const char* data,
+                               int len) {
+  recv_data_.append(data, len);
   while (recv_data_.length()) {
     HttpServerRequestInfo* request = ParseHeaders();
     if (!request)
diff --git a/net/tools/fetch/http_listen_socket.h b/net/tools/fetch/http_listen_socket.h
index 73f09f6..a1b77c5 100644
--- a/net/tools/fetch/http_listen_socket.h
+++ b/net/tools/fetch/http_listen_socket.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -36,7 +36,7 @@
 
   // ListenSocketDelegate
   virtual void DidAccept(ListenSocket* server, ListenSocket* connection);
-  virtual void DidRead(ListenSocket* connection, const std::string& data);
+  virtual void DidRead(ListenSocket* connection, const char* data, int len);
   virtual void DidClose(ListenSocket* sock);
 
  private:
@@ -54,7 +54,7 @@
   HttpListenSocket::Delegate* delegate_;
   std::string recv_data_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(HttpListenSocket);
+  DISALLOW_COPY_AND_ASSIGN(HttpListenSocket);
 };
 
 #endif // NET_BASE_TOOLS_HTTP_LISTEN_SOCKET_H_
diff --git a/net/tools/fetch/http_server.h b/net/tools/fetch/http_server.h
index 6b8e719..1a3d402 100644
--- a/net/tools/fetch/http_server.h
+++ b/net/tools/fetch/http_server.h
@@ -1,25 +1,25 @@
-// Copyright (c) 2009 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_BASE_TOOLS_HTTP_SERVER_H_

-#define NET_BASE_TOOLS_HTTP_SERVER_H_

-

-#include "base/basictypes.h"

-#include "net/tools/fetch/http_session.h"

-

-// Implements a simple, single-threaded HttpServer.

-// Right now, this class implements no functionality above and beyond

-// the HttpSession.

-class HttpServer {

-public:

-  HttpServer(std::string ip, int port);

-  ~HttpServer();

-

-private:

-  scoped_ptr<HttpSession> session_;

-  DISALLOW_EVIL_CONSTRUCTORS(HttpServer);

-};

-

-#endif // NET_BASE_TOOLS_HTTP_SERVER_H_

-

+// Copyright (c) 2010 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_BASE_TOOLS_HTTP_SERVER_H_
+#define NET_BASE_TOOLS_HTTP_SERVER_H_
+
+#include "base/basictypes.h"
+#include "net/tools/fetch/http_session.h"
+
+// Implements a simple, single-threaded HttpServer.
+// Right now, this class implements no functionality above and beyond
+// the HttpSession.
+class HttpServer {
+public:
+  HttpServer(std::string ip, int port);
+  ~HttpServer();
+
+private:
+  scoped_ptr<HttpSession> session_;
+  DISALLOW_COPY_AND_ASSIGN(HttpServer);
+};
+
+#endif // NET_BASE_TOOLS_HTTP_SERVER_H_
+
diff --git a/net/tools/fetch/http_server_request_info.h b/net/tools/fetch/http_server_request_info.h
index 7af161f..fb45bdb 100644
--- a/net/tools/fetch/http_server_request_info.h
+++ b/net/tools/fetch/http_server_request_info.h
@@ -1,24 +1,25 @@
-// Copyright (c) 2009 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_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_

-#define NET_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_

-

-#include <string>

-

-#include "net/http/http_request_info.h"

-

-// Meta information about an HTTP request.

-// This is geared toward servers in that it keeps a map of the headers and

-// values rather than just a list of header strings (which net::HttpRequestInfo

-// does).

-class HttpServerRequestInfo : public net::HttpRequestInfo {

- public:

-  HttpServerRequestInfo() : net::HttpRequestInfo() {}

-

-  // A map of the names -> values for HTTP headers.

-  std::map<std::string, std::string> headers;

-};

-

-#endif  // NET_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_

+// Copyright (c) 2009 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_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_
+#define NET_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_
+
+#include <map>
+#include <string>
+
+#include "net/http/http_request_info.h"
+
+// Meta information about an HTTP request.
+// This is geared toward servers in that it keeps a map of the headers and
+// values rather than just a list of header strings (which net::HttpRequestInfo
+// does).
+class HttpServerRequestInfo : public net::HttpRequestInfo {
+ public:
+  HttpServerRequestInfo() : net::HttpRequestInfo() {}
+
+  // A map of the names -> values for HTTP headers.
+  std::map<std::string, std::string> headers;
+};
+
+#endif  // NET_BASE_TOOLS_HTTP_SERVER_REQUEST_INFO_H_
diff --git a/net/tools/fetch/http_session.h b/net/tools/fetch/http_session.h
index 4d9e84b..a1d57bf 100644
--- a/net/tools/fetch/http_session.h
+++ b/net/tools/fetch/http_session.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -20,7 +20,7 @@
 
  private:
   scoped_refptr<HttpListenSocket> socket_;
-  DISALLOW_EVIL_CONSTRUCTORS(HttpSession);
+  DISALLOW_COPY_AND_ASSIGN(HttpSession);
 };
 
 #endif // NET_BASE_TOOLS_HTTP_SESSION_H_
diff --git a/net/tools/flip_server/balsa_frame.cc b/net/tools/flip_server/balsa_frame.cc
index 90631ce..9b8173e 100644
--- a/net/tools/flip_server/balsa_frame.cc
+++ b/net/tools/flip_server/balsa_frame.cc
@@ -9,7 +9,6 @@
 #include <strings.h>
 
 #include <limits>
-#include <iostream>
 #include <string>
 #include <utility>
 #include <vector>
diff --git a/net/tools/flip_server/balsa_headers.cc b/net/tools/flip_server/balsa_headers.cc
index 40e446b..b58fb03 100644
--- a/net/tools/flip_server/balsa_headers.cc
+++ b/net/tools/flip_server/balsa_headers.cc
@@ -71,6 +71,12 @@
 
 const size_t BalsaBuffer::kDefaultBlocksize;
 
+std::ostream& BalsaHeaders::iterator_base::operator<<(std::ostream& os,
+                                                      const iterator_base& it) {
+   os << "[" << it.headers_ << ", " << it.idx_ << "]";
+   return os;
+ }
+
 void BalsaHeaders::Clear() {
   balsa_buffer_.Clear();
   transfer_encoding_is_chunked_ = false;
@@ -756,4 +762,3 @@
 }
 
 }  // namespace net
-
diff --git a/net/tools/flip_server/balsa_headers.h b/net/tools/flip_server/balsa_headers.h
index 6f590c2..24e7940 100644
--- a/net/tools/flip_server/balsa_headers.h
+++ b/net/tools/flip_server/balsa_headers.h
@@ -6,7 +6,7 @@
 #define NET_TOOLS_FLIP_SERVER_BALSA_HEADERS_H_
 
 #include <algorithm>
-#include <iostream>
+#include <iosfwd>
 #include <iterator>
 #include <string>
 #include <utility>
@@ -470,10 +470,7 @@
     // operator<< work for the classes it sees.  It would be better if there
     // was an additional traits-like system for the gUnit output... but oh
     // well.
-    friend std::ostream& operator<<(std::ostream& os, const iterator_base& it) {
-      os << "[" << it.headers_ << ", " << it.idx_ << "]";
-      return os;
-    }
+    std::ostream& operator<<(std::ostream& os, const iterator_base& it);
 
    protected:
     iterator_base(const BalsaHeaders* headers, HeaderLines::size_type index) :
@@ -1117,7 +1114,7 @@
 
  protected:
   friend class BalsaFrame;
-  friend class FlipFrame;
+  friend class SpdyFrame;
   friend class HTTPMessage;
   friend class BalsaHeadersTokenUtils;
 
diff --git a/net/tools/flip_server/create_listener.cc b/net/tools/flip_server/create_listener.cc
index 7ea5efa..3538261 100644
--- a/net/tools/flip_server/create_listener.cc
+++ b/net/tools/flip_server/create_listener.cc
@@ -10,6 +10,7 @@
 #include <sys/socket.h>  // for getaddrinfo and getnameinfo
 #include <sys/types.h>   // "
 #include <unistd.h>      // for exit()
+#include <ostream>
 
 #include "net/tools/flip_server/create_listener.h"
 
diff --git a/net/tools/flip_server/create_listener.h b/net/tools/flip_server/create_listener.h
index 06979d9..32758ee 100644
--- a/net/tools/flip_server/create_listener.h
+++ b/net/tools/flip_server/create_listener.h
@@ -5,7 +5,7 @@
 #ifndef NET_TOOLS_FLIP_SERVER_CREATE_LISTENER_H__
 #define NET_TOOLS_FLIP_SERVER_CREATE_LISTENER_H__
 
-#include <iostream>
+#include <iosfwd>
 #include <string>
 
 namespace net {
diff --git a/net/tools/flip_server/flip_in_mem_edsm_server.cc b/net/tools/flip_server/flip_in_mem_edsm_server.cc
index 9ffc86f..974994d 100644
--- a/net/tools/flip_server/flip_in_mem_edsm_server.cc
+++ b/net/tools/flip_server/flip_in_mem_edsm_server.cc
@@ -7,6 +7,7 @@
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <openssl/err.h>
 #include <openssl/ssl.h>
 
 #include <deque>
@@ -19,9 +20,9 @@
 #include "base/simple_thread.h"
 #include "base/timer.h"
 #include "base/lock.h"
-#include "net/flip/flip_frame_builder.h"
-#include "net/flip/flip_framer.h"
-#include "net/flip/flip_protocol.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_enums.h"
 #include "net/tools/flip_server/balsa_frame.h"
 #include "net/tools/flip_server/balsa_headers.h"
@@ -29,7 +30,6 @@
 #include "net/tools/flip_server/buffer_interface.h"
 #include "net/tools/flip_server/create_listener.h"
 #include "net/tools/flip_server/epoll_server.h"
-#include "net/tools/flip_server/loadtime_measurement.h"
 #include "net/tools/flip_server/other_defines.h"
 #include "net/tools/flip_server/ring_buffer.h"
 #include "net/tools/flip_server/simple_buffer.h"
@@ -39,54 +39,20 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-using base::StringPiece;
-using base::SimpleThread;
-// using base::Lock;  // heh, this isn't in base namespace?!
-// using base::AutoLock;  // ditto!
-using flip::CONTROL_FLAG_NONE;
-using flip::DATA_FLAG_COMPRESSED;
-using flip::DATA_FLAG_FIN;
-using flip::FIN_STREAM;
-using flip::FlipControlFrame;
-using flip::FlipDataFlags;
-using flip::FlipDataFrame;
-using flip::FlipFinStreamControlFrame;
-using flip::FlipFrame;
-using flip::FlipFrameBuilder;
-using flip::FlipFramer;
-using flip::FlipFramerVisitorInterface;
-using flip::FlipHeaderBlock;
-using flip::FlipStreamId;
-using flip::FlipSynReplyControlFrame;
-using flip::FlipSynStreamControlFrame;
-using flip::SYN_REPLY;
-using flip::SYN_STREAM;
-using net::BalsaFrame;
-using net::BalsaFrameEnums;
-using net::BalsaHeaders;
-using net::BalsaHeadersEnums;
-using net::BalsaVisitorInterface;
-using net::EpollAlarmCallbackInterface;
-using net::EpollCallbackInterface;
-using net::EpollEvent;
-using net::EpollServer;
-using net::RingBuffer;
-using net::SimpleBuffer;
-using net::SplitStringPieceToVector;
-using net::UrlUtilities;
+using std::cerr;
 using std::deque;
+using std::list;
 using std::map;
+using std::ostream;
 using std::pair;
 using std::string;
 using std::vector;
-using std::list;
-using std::ostream;
-using std::cerr;
 
 ////////////////////////////////////////////////////////////////////////////////
 
+
 //         If set to true, then the server will act as an SSL server for both
-//          HTTP and FLIP);
+//          HTTP and SPDY);
 bool FLAGS_use_ssl = true;
 
 // The name of the cert .pem file);
@@ -108,8 +74,8 @@
 //  is completely drained and the accept() call returns an error);
 int32 FLAGS_accepts_per_wake = 0;
 
-// The port on which the flip server listens);
-int32 FLAGS_flip_port = 10040;
+// The port on which the spdy server listens);
+int32 FLAGS_spdy_port = 10040;
 
 // The port on which the http server listens);
 int32 FLAGS_port = 16002;
@@ -145,18 +111,55 @@
 // Does the server compress data frames);
 bool FLAGS_use_compression = false;
 
-// The path to the urls file which includes the urls for testing);
-string FLAGS_urls_file = "experimental/users/fenix/flip/urls.txt";
+////////////////////////////////////////////////////////////////////////////////
 
-// The path to the html that does the pageload in iframe);
-string FLAGS_pageload_html_file =
-  "experimental/users/fenix/flip/loadtime_measurement.html";
+using base::StringPiece;
+using base::SimpleThread;
+// using base::Lock;  // heh, this isn't in base namespace?!
+// using base::AutoLock;  // ditto!
+using net::BalsaFrame;
+using net::BalsaFrameEnums;
+using net::BalsaHeaders;
+using net::BalsaHeadersEnums;
+using net::BalsaVisitorInterface;
+using net::EpollAlarmCallbackInterface;
+using net::EpollCallbackInterface;
+using net::EpollEvent;
+using net::EpollServer;
+using net::RingBuffer;
+using net::SimpleBuffer;
+using net::SplitStringPieceToVector;
+using net::UrlUtilities;
+using spdy::CONTROL_FLAG_NONE;
+using spdy::DATA_FLAG_COMPRESSED;
+using spdy::DATA_FLAG_FIN;
+using spdy::RST_STREAM;
+using spdy::SYN_REPLY;
+using spdy::SYN_STREAM;
+using spdy::SpdyControlFrame;
+using spdy::SpdyDataFlags;
+using spdy::SpdyDataFrame;
+using spdy::SpdyRstStreamControlFrame;
+using spdy::SpdyFrame;
+using spdy::SpdyFrameBuilder;
+using spdy::SpdyFramer;
+using spdy::SpdyFramerVisitorInterface;
+using spdy::SpdyHeaderBlock;
+using spdy::SpdyStreamId;
+using spdy::SpdySynReplyControlFrame;
+using spdy::SpdySynStreamControlFrame;
 
-// If set to true, record requests in file named as fd used);
-bool FLAGS_record_mode = false;
 
-// The path to save the record files);
-string FLAGS_record_path = ".";
+////////////////////////////////////////////////////////////////////////////////
+
+void PrintSslError() {
+  char buf[128];  // this buffer must be at least 120 chars long.
+  int error_num = ERR_get_error();
+  while (error_num != 0) {
+    LOG(INFO)<< ERR_error_string(error_num, buf);
+    error_num = ERR_get_error();
+  }
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -189,13 +192,27 @@
     << " errno=" << errno;
 }
 
-////////////////////////////////////////////////////////////////////////////////
+// Encode the URL.
+string EncodeURL(string uri, string host, string method) {
+  if (!FLAGS_need_to_encode_url) {
+    // TODO(mbelshe): if uri is fully qualified, need to strip protocol/host.
+    return string(method + "_" + uri);
+  }
 
-LoadtimeMeasurement global_loadtime_measurement(FLAGS_urls_file,
-                                                FLAGS_pageload_html_file);
+  string filename;
+  if (uri[0] == '/') {
+    // uri is not fully qualified.
+    filename = net::UrlToFilenameEncoder::Encode(
+        "http://" + host + uri, method + "_/");
+  } else {
+    filename = net::UrlToFilenameEncoder::Encode(uri, method + "_/");
+  }
+  return filename;
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 
+
 struct GlobalSSLState {
   SSL_METHOD* ssl_method;
   SSL_CTX* ssl_ctx;
@@ -208,40 +225,53 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 // SSL stuff
-void flip_init_ssl(GlobalSSLState* state) {
+void spdy_init_ssl(GlobalSSLState* state) {
   SSL_library_init();
-  SSL_load_error_strings();
+  PrintSslError();
 
-  state->ssl_method = TLSv1_server_method();
+  SSL_load_error_strings();
+  PrintSslError();
+
+  state->ssl_method = SSLv23_method();
   state->ssl_ctx = SSL_CTX_new(state->ssl_method);
   if (!state->ssl_ctx) {
+    PrintSslError();
     LOG(FATAL) << "Unable to create SSL context";
   }
+  // Disable SSLv2 support.
+  SSL_CTX_set_options(state->ssl_ctx, SSL_OP_NO_SSLv2);
   if (SSL_CTX_use_certificate_file(state->ssl_ctx,
                                    FLAGS_ssl_cert_name.c_str(),
                                    SSL_FILETYPE_PEM) <= 0) {
+    PrintSslError();
     LOG(FATAL) << "Unable to use cert.pem as SSL cert.";
   }
   if (SSL_CTX_use_PrivateKey_file(state->ssl_ctx,
                                   FLAGS_ssl_key_name.c_str(),
                                   SSL_FILETYPE_PEM) <= 0) {
+    PrintSslError();
     LOG(FATAL) << "Unable to use key.pem as SSL key.";
   }
   if (!SSL_CTX_check_private_key(state->ssl_ctx)) {
+    PrintSslError();
     LOG(FATAL) << "The cert.pem and key.pem files don't match";
   }
 }
 
-SSL* flip_new_ssl(SSL_CTX* ssl_ctx) {
+SSL* spdy_new_ssl(SSL_CTX* ssl_ctx) {
   SSL* ssl = SSL_new(ssl_ctx);
+  PrintSslError();
+
   SSL_set_accept_state(ssl);
+  PrintSslError();
   return ssl;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-const int kInitialDataSendersThreshold =  (2 * 1460) - FlipFrame::size();
-const int kNormalSegmentSize = (2 * 1460) - FlipFrame::size();
+const int kMSS = 1460;
+const int kInitialDataSendersThreshold = (2 * kMSS) - SpdyFrame::size();
+const int kNormalSegmentSize = (2 * kMSS) - SpdyFrame::size();
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -481,7 +511,7 @@
     // versions of content.
     // TODO(mbelshe) REMOVE ME
 #if 0
-    // TODO(mbelshe): append current date.
+    // TODO(mbelshe) append current date.
     visitor.headers.RemoveAllOfHeader("date");
     if (visitor.headers.HasHeader("expires")) {
       visitor.headers.RemoveAllOfHeader("expires");
@@ -491,8 +521,7 @@
 #endif
     BalsaHeaders* headers = new BalsaHeaders;
     headers->CopyFrom(visitor.headers);
-    string filename_stripped =
-      string(filename).substr(cwd_.size() + 1);
+    string filename_stripped = string(filename).substr(cwd_.size() + 1);
 //    LOG(INFO) << "Adding file (" << visitor.body.length() << " bytes): "
 //              << filename_stripped;
     files_[filename_stripped] = FileData();
@@ -501,8 +530,7 @@
     fd.filename = string(filename_stripped,
                          filename_stripped.find_first_of('/'));
     if (headers->HasHeader("X-Associated-Content")) {
-      string content =
-        headers->GetHeader("X-Associated-Content").as_string();
+      string content = headers->GetHeader("X-Associated-Content").as_string();
       vector<StringPiece> urls_and_priorities;
       SplitStringPieceToVector(content, "||", &urls_and_priorities, true);
       VLOG(1) << "Examining X-Associated-Content header";
@@ -516,7 +544,7 @@
                                  url_and_priority[0].size());
           string filename_string(url_and_priority[1].data(),
                                  url_and_priority[1].size());
-          int priority;
+          long priority;
           char* last_eaten_char;
           priority = strtol(priority_string.c_str(), &last_eaten_char, 0);
           if (last_eaten_char ==
@@ -684,7 +712,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 class SMServerConnection;
-typedef SMInterface*(SMInterfaceFactory)(SMServerConnection* conn);
+typedef SMInterface*(SMInterfaceFactory)(SMServerConnection*);
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -698,7 +726,7 @@
  public:
   virtual ~SMServerConnectionPoolInterface() {}
   // SMServerConnections will use this:
-  virtual void SMServerConnectionDone(SMServerConnection* conn) = 0;
+  virtual void SMServerConnectionDone(SMServerConnection* connection) = 0;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -710,7 +738,6 @@
                      MemoryCache* memory_cache,
                      EpollServer* epoll_server) :
       fd_(-1),
-      record_fd_(-1),
       events_(0),
 
       registered_in_epoll_server_(false),
@@ -723,12 +750,11 @@
       memory_cache_(memory_cache),
       sm_interface_(sm_interface_factory(this)),
 
-      max_bytes_sent_per_dowrite_(128),
+      max_bytes_sent_per_dowrite_(4096),
 
       ssl_(NULL) {}
 
   int fd_;
-  int record_fd_;
   int events_;
 
   bool registered_in_epoll_server_;
@@ -750,13 +776,6 @@
   EpollServer* epoll_server() { return epoll_server_; }
   OutputList* output_list() { return &output_list_; }
   MemoryCache* memory_cache() { return memory_cache_; }
-  int record_fd() { return record_fd_; }
-  void close_record_fd() {
-    if (record_fd_ != -1) {
-      close(record_fd_);
-      record_fd_ = -1;
-    }
-  }
   void ReadyToSend() {
     epoll_server_->SetFDReady(fd_, EPOLLIN | EPOLLOUT);
   }
@@ -795,16 +814,6 @@
       close(fd_);
       fd_ = -1;
     }
-    if (FLAGS_record_mode) {
-      char record_file_name[1024];
-      snprintf(record_file_name, sizeof(record_file_name), "%s/%d_%ld",
-              FLAGS_record_path.c_str(), fd, epoll_server->NowInUsec()/1000);
-      record_fd_ = open(record_file_name, O_CREAT|O_APPEND|O_WRONLY, S_IRWXU);
-      if (record_fd_ < 0) {
-        LOG(ERROR) << "Open record file for fd " << fd << " failed";
-        record_fd_ = -1;
-      }
-    }
 
     fd_ = fd;
 
@@ -820,8 +829,9 @@
     epoll_server_->RegisterFD(fd_, this, EPOLLIN | EPOLLOUT | EPOLLET);
 
     if (global_ssl_state) {
-      ssl_ = flip_new_ssl(global_ssl_state->ssl_ctx);
+      ssl_ = spdy_new_ssl(global_ssl_state->ssl_ctx);
       SSL_set_fd(ssl_, fd_);
+      PrintSslError();
     }
     sm_interface_->PostAcceptHook();
   }
@@ -887,6 +897,7 @@
       ssize_t bytes_read = 0;
       if (ssl_) {
         bytes_read = SSL_read(ssl_, bytes, size);
+        PrintSslError();
       } else {
         bytes_read = recv(fd_, bytes, size, MSG_DONTWAIT);
       }
@@ -1004,6 +1015,7 @@
       ssize_t bytes_written = 0;
       if (ssl_) {
         bytes_written = SSL_write(ssl_, bytes, size);
+        PrintSslError();
       } else {
         bytes_written = send(fd_, bytes, size, flags);
       }
@@ -1050,7 +1062,9 @@
     VLOG(2) << "Resetting";
     if (ssl_) {
       SSL_shutdown(ssl_);
+      PrintSslError();
       SSL_free(ssl_);
+      PrintSslError();
     }
     if (registered_in_epoll_server_) {
       epoll_server_->UnregisterFD(fd_);
@@ -1177,6 +1191,19 @@
     if (ExistsInPriorityMaps(mci.stream_id))
       LOG(FATAL) << "OOps, already was inserted here?!";
 
+    double think_time_in_s = FLAGS_server_think_time_in_s;
+    string x_server_latency =
+      mci.file_data->headers->GetHeader("X-Server-Latency").as_string();
+    if (x_server_latency.size() != 0) {
+      char* endp;
+      double tmp_think_time_in_s = strtod(x_server_latency.c_str(), &endp);
+      if (endp != x_server_latency.c_str() + x_server_latency.size()) {
+        LOG(ERROR) << "Unable to understand X-Server-Latency of: "
+          << x_server_latency << " for resource: " << mci.file_data->filename;
+      } else {
+        think_time_in_s = tmp_think_time_in_s;
+      }
+    }
     StreamIdToPriorityMap::iterator sitpmi;
     sitpmi = stream_ids_.insert(
         pair<uint32, PriorityMapPointer>(mci.stream_id,
@@ -1184,8 +1211,9 @@
     PriorityMapPointer& pmp = sitpmi->second;
 
     BeginOutputtingAlarm* boa = new BeginOutputtingAlarm(this, &pmp, mci);
+    VLOG(2) << "Server think time: " << think_time_in_s;
     epoll_server_->RegisterAlarmApproximateDelta(
-        FLAGS_server_think_time_in_s * 1000000, boa);
+        think_time_in_s * 1000000, boa);
   }
 
   void SpliceToPriorityRing(PriorityRing::iterator pri) {
@@ -1249,10 +1277,10 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-class FlipSM : public FlipFramerVisitorInterface, public SMInterface {
+class SpdySM : public SpdyFramerVisitorInterface, public SMInterface {
  private:
   uint64 seq_num_;
-  FlipFramer* framer_;
+  SpdyFramer* framer_;
 
   SMServerConnection* connection_;
   OutputList* output_list_;
@@ -1260,9 +1288,9 @@
   MemoryCache* memory_cache_;
   uint32 next_outgoing_stream_id_;
  public:
-  explicit FlipSM(SMServerConnection* connection) :
+  explicit SpdySM(SMServerConnection* connection) :
       seq_num_(0),
-      framer_(new FlipFramer),
+      framer_(new SpdyFramer),
       connection_(connection),
       output_list_(connection->output_list()),
       output_ordering_(connection),
@@ -1271,97 +1299,73 @@
     framer_->set_visitor(this);
   }
  private:
-  virtual void OnError(FlipFramer* framer) {
+  virtual void OnError(SpdyFramer* framer) {
     /* do nothing with this right now */
   }
 
-  virtual void OnControl(const FlipControlFrame* frame) {
-    FlipHeaderBlock headers;
+  virtual void OnControl(const SpdyControlFrame* frame) {
+    SpdyHeaderBlock headers;
     bool parsed_headers = false;
     switch (frame->type()) {
       case SYN_STREAM:
         {
+        const SpdySynStreamControlFrame* syn_stream =
+            reinterpret_cast<const SpdySynStreamControlFrame*>(frame);
         parsed_headers = framer_->ParseHeaderBlock(frame, &headers);
-        VLOG(2) << "OnSyn(" << frame->stream_id() << ")";
+        VLOG(2) << "OnSyn(" << syn_stream->stream_id() << ")";
         VLOG(2) << "headers parsed?: " << (parsed_headers? "yes": "no");
         if (parsed_headers) {
           VLOG(2) << "# headers: " << headers.size();
         }
-        unsigned int j = 0;
-        for (FlipHeaderBlock::iterator i = headers.begin();
+        for (SpdyHeaderBlock::iterator i = headers.begin();
              i != headers.end();
              ++i) {
           VLOG(2) << i->first << ": " << i->second;
-          if (FLAGS_record_mode && connection_->record_fd() > 0) {
-            // If record mode is enabled and corresponding server connection
-            // has file opened, then save the request headers into the file.
-            // All the requests from the same connection is save in one file.
-            // This file will be used to replay and generate FLIP requests
-            // load.
-            string header = i->first + ": " + i->second + "\n";
-            ++j;
-            if (j == headers.size()) {
-              header += "\n";  // add an additional empty lime
-            }
-            int r = write(
-                connection_->record_fd(), header.c_str(), header.size());
-            if (r < 0) {
-              perror("unable to write to record file:");
-            }
-          }
         }
 
-        FlipHeaderBlock::iterator method = headers.find("method");
-        FlipHeaderBlock::iterator url = headers.find("url");
+        SpdyHeaderBlock::iterator method = headers.find("method");
+        SpdyHeaderBlock::iterator url = headers.find("url");
         if (url == headers.end() || method == headers.end()) {
           VLOG(2) << "didn't find method or url or method. Not creating stream";
           break;
         }
 
-        FlipHeaderBlock::iterator referer = headers.find("referer");
+        SpdyHeaderBlock::iterator referer = headers.find("referer");
         if (referer != headers.end() && method->second == "GET") {
           memory_cache_->UpdateHeaders(referer->second, url->second);
         }
         string uri = UrlUtilities::GetUrlPath(url->second);
         string host = UrlUtilities::GetUrlHost(url->second);
-        // requests started with /testing are loadtime measurement related
-        // urls, use LoadtimeMeasurement class to handle them.
-        if (uri.find("/testing") == 0) {
-          string output;
-          global_loadtime_measurement.ProcessRequest(uri, output);
-          SendOKResponse(frame->stream_id(), &output);
-        } else {
-          string filename;
-          if (FLAGS_need_to_encode_url) {
-            filename = net::UrlToFilenameEncoder::Encode(
-                "http://" + host + uri, method->second + "_/");
-          } else {
-            filename = string(method->second + "_" + url->second);
-          }
 
-          NewStream(frame->stream_id(),
-                    reinterpret_cast<const FlipSynStreamControlFrame*>(frame)->
-                      priority(),
-                    filename);
-          }
+        string filename = EncodeURL(uri, host, method->second);
+        NewStream(syn_stream->stream_id(),
+                  reinterpret_cast<const SpdySynStreamControlFrame*>(frame)->
+                    priority(),
+                  filename);
         }
         break;
 
       case SYN_REPLY:
         parsed_headers = framer_->ParseHeaderBlock(frame, &headers);
-        VLOG(2) << "OnSynReply(" << frame->stream_id() << ")";
+        VLOG(2) << "OnSynReply("
+                << reinterpret_cast<const SpdySynReplyControlFrame*>(
+                    frame)->stream_id() << ")";
         break;
-      case FIN_STREAM:
-        VLOG(2) << "OnFin(" << frame->stream_id() << ")";
-        output_ordering_.RemoveStreamId(frame->stream_id());
+      case RST_STREAM:
+        {
+        const SpdyRstStreamControlFrame* rst_stream =
+            reinterpret_cast<const SpdyRstStreamControlFrame*>(frame);
+        VLOG(2) << "OnRst(" << rst_stream->stream_id() << ")";
+        output_ordering_.RemoveStreamId(rst_stream ->stream_id());
+        }
+        break;
 
-        break;
       default:
         LOG(DFATAL) << "Unknown control frame type";
     }
   }
   virtual void OnStreamFrameData(
-    FlipStreamId stream_id,
+    SpdyStreamId stream_id,
     const char* data, size_t len) {
     VLOG(2) << "StreamData(" << stream_id << ", [" << len << "])";
     /* do nothing with this right now */
@@ -1371,7 +1375,7 @@
   }
 
  public:
-  ~FlipSM() {
+  ~SpdySM() {
     Reset();
   }
   size_t ProcessInput(const char* data, size_t len) {
@@ -1387,14 +1391,14 @@
   }
 
   const char* ErrorAsString() const {
-    return FlipFramer::ErrorCodeToString(framer_->error_code());
+    return SpdyFramer::ErrorCodeToString(framer_->error_code());
   }
 
   void Reset() {}
   void ResetForNewConnection() {
     // seq_num is not cleared, intentionally.
     delete framer_;
-    framer_ = new FlipFramer;
+    framer_ = new SpdyFramer;
     framer_->set_visitor(this);
     output_ordering_.Reset();
     next_outgoing_stream_id_ = 2;
@@ -1412,12 +1416,12 @@
 
     LOG(ERROR) << "Sending NOP FRAMES";
 
-    scoped_ptr<FlipControlFrame> frame(FlipFramer::CreateNopFrame());
+    scoped_ptr<SpdyControlFrame> frame(SpdyFramer::CreateNopFrame());
     for (int i = 0; i < kPkts; ++i) {
       char* bytes = frame->data();
-      size_t size = FlipFrame::size();
+      size_t size = SpdyFrame::size();
       ssize_t bytes_written = connection_->Send(bytes, size, MSG_DONTWAIT);
-      if (bytes_written > 0 && static_cast<size_t>(bytes_written) != size) {
+      if (static_cast<size_t>(bytes_written) != size) {
         LOG(ERROR) << "Trouble sending Nop packet! (" << errno << ")";
         if (errno == EAGAIN)
           break;
@@ -1485,11 +1489,11 @@
 
   void SendDataFrame(uint32 stream_id, const char* data, int64 len,
                      uint32 flags, bool compress) {
-    FlipDataFlags flip_flags = static_cast<FlipDataFlags>(flags);
-    SendDataFrameImpl(stream_id, data, len, flip_flags, compress);
+    SpdyDataFlags spdy_flags = static_cast<SpdyDataFlags>(flags);
+    SendDataFrameImpl(stream_id, data, len, spdy_flags, compress);
   }
 
-  FlipFramer* flip_framer() { return framer_; }
+  SpdyFramer* spdy_framer() { return framer_; }
 
  private:
   void SendEOFImpl(uint32 stream_id) {
@@ -1519,12 +1523,12 @@
     output_ordering_.RemoveStreamId(stream_id);
   }
 
-  void CopyHeaders(FlipHeaderBlock& dest, const BalsaHeaders& headers) {
+  void CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers) {
     for (BalsaHeaders::const_header_lines_iterator hi =
          headers.header_lines_begin();
          hi != headers.header_lines_end();
          ++hi) {
-      FlipHeaderBlock::iterator fhi = dest.find(hi->first.as_string());
+      SpdyHeaderBlock::iterator fhi = dest.find(hi->first.as_string());
       if (fhi == dest.end()) {
         dest[hi->first.as_string()] = hi->second.as_string();
       } else {
@@ -1540,7 +1544,7 @@
   }
 
   size_t SendSynStreamImpl(uint32 stream_id, const BalsaHeaders& headers) {
-    FlipHeaderBlock block;
+    SpdyHeaderBlock block;
     block["method"] = headers.request_method().as_string();
     if (!headers.HasHeader("status"))
       block["status"] = headers.response_code().as_string();
@@ -1554,10 +1558,11 @@
     }
     CopyHeaders(block, headers);
 
-    FlipSynStreamControlFrame* fsrcf =
-      framer_->CreateSynStream(stream_id, 0, CONTROL_FLAG_NONE, true, &block);
+    SpdySynStreamControlFrame* fsrcf =
+      framer_->CreateSynStream(stream_id, 0, 0, CONTROL_FLAG_NONE, true,
+                               &block);
     DataFrame df;
-    df.size = fsrcf->length() + FlipFrame::size();
+    df.size = fsrcf->length() + SpdyFrame::size();
     size_t df_size = df.size;
     df.data = fsrcf->data();
     df.delete_when_done = true;
@@ -1568,16 +1573,16 @@
   }
 
   size_t SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers) {
-    FlipHeaderBlock block;
+    SpdyHeaderBlock block;
     CopyHeaders(block, headers);
     block["status"] = headers.response_code().as_string() + " " +
                       headers.response_reason_phrase().as_string();
     block["version"] = headers.response_version().as_string();
 
-    FlipSynReplyControlFrame* fsrcf =
+    SpdySynReplyControlFrame* fsrcf =
       framer_->CreateSynReply(stream_id, CONTROL_FLAG_NONE, true, &block);
     DataFrame df;
-    df.size = fsrcf->length() + FlipFrame::size();
+    df.size = fsrcf->length() + SpdyFrame::size();
     size_t df_size = df.size;
     df.data = fsrcf->data();
     df.delete_when_done = true;
@@ -1588,18 +1593,18 @@
   }
 
   void SendDataFrameImpl(uint32 stream_id, const char* data, int64 len,
-                         FlipDataFlags flags, bool compress) {
+                         SpdyDataFlags flags, bool compress) {
     // Force compression off if disabled via command line.
     if (!FLAGS_use_compression)
-      flags = static_cast<FlipDataFlags>(flags & ~DATA_FLAG_COMPRESSED);
+      flags = static_cast<SpdyDataFlags>(flags & ~DATA_FLAG_COMPRESSED);
 
     // TODO(mbelshe):  We can't compress here - before going into the
     //                 priority queue.  Compression needs to be done
     //                 with late binding.
-    FlipDataFrame* fdf = framer_->CreateDataFrame(stream_id, data, len,
+    SpdyDataFrame* fdf = framer_->CreateDataFrame(stream_id, data, len,
                                                   flags);
     DataFrame df;
-    df.size = fdf->length() + FlipFrame::size();
+    df.size = fdf->length() + SpdyFrame::size();
     df.data = fdf->data();
     df.delete_when_done = true;
     EnqueueDataFrame(df);
@@ -1710,28 +1715,13 @@
     virtual void ProcessTrailerInput(const char *input, size_t size) {}
     virtual void ProcessHeaders(const BalsaHeaders& headers) {
       VLOG(2) << "Got new request!";
-      // requests started with /testing are loadtime measurement related
-      // urls, use LoadtimeMeasurement class to handle them.
-      if (headers.request_uri().as_string().find("/testing") == 0) {
-        string output;
-        global_loadtime_measurement.ProcessRequest(
-            headers.request_uri().as_string(), output);
-        SendOKResponse(stream_id_, &output);
-        stream_id_ += 2;
-      } else {
-        string filename;
-        if (FLAGS_need_to_encode_url) {
-          filename = net::UrlToFilenameEncoder::Encode(
-              headers.GetHeader("Host").as_string() +
-              headers.request_uri().as_string(),
-              headers.request_method().as_string() + "_/");
-        } else {
-         filename = headers.request_method().as_string() + "_" +
-                    headers.request_uri().as_string();
-        }
-        NewStream(stream_id_, 0, filename);
-        stream_id_ += 2;
-      }
+      string host = UrlUtilities::GetUrlHost(
+          headers.GetHeader("Host").as_string());
+      string method = headers.request_method().as_string();
+      string filename = EncodeURL(headers.request_uri().as_string(), host,
+          method);
+      NewStream(stream_id_, 0, filename);
+      stream_id_ += 2;
     }
     virtual void ProcessRequestFirstLine(const char* line_input,
                                          size_t line_length,
@@ -1843,7 +1833,7 @@
     SendDataFrameImpl(stream_id, data, len, flags, compress);
   }
 
-  BalsaFrame* flip_framer() { return framer_; }
+  BalsaFrame* spdy_framer() { return framer_; }
 
  private:
   void SendEOFImpl(uint32 stream_id) {
@@ -1858,7 +1848,7 @@
     BalsaHeaders my_headers;
     my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "404", "Not Found");
     my_headers.RemoveAllOfHeader("content-length");
-    my_headers.HackHeader("transfer-encoding", "chunked");
+    my_headers.AppendHeader("transfer-encoding", "chunked");
     SendSynReplyImpl(stream_id, my_headers);
     SendDataFrame(stream_id, "wtf?", 4, 0, false);
     SendEOFImpl(stream_id);
@@ -1869,7 +1859,7 @@
     BalsaHeaders my_headers;
     my_headers.SetFirstlineFromStringPieces("HTTP/1.1", "200", "OK");
     my_headers.RemoveAllOfHeader("content-length");
-    my_headers.HackHeader("transfer-encoding", "chunked");
+    my_headers.AppendHeader("transfer-encoding", "chunked");
     SendSynReplyImpl(stream_id, my_headers);
     SendDataFrame(stream_id, output->c_str(), output->size(), 0, false);
     SendEOFImpl(stream_id);
@@ -2110,15 +2100,14 @@
   // SMServerConnections will use this:
   virtual void SMServerConnectionDone(SMServerConnection* sc) {
     VLOG(3) << "Done with server connection: " << sc;
-    sc->close_record_fd();
     tmp_unused_server_connections_.push_back(sc);
   }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 
-SMInterface* NewFlipSM(SMServerConnection* connection) {
-  return new FlipSM(connection);
+SMInterface* NewSpdySM(SMServerConnection* connection) {
+  return new SpdySM(connection);
 }
 
 SMInterface* NewHTTPSM(SMServerConnection* connection) {
@@ -2148,7 +2137,7 @@
     int on = 1;
     int rc;
     rc = setsockopt(listening_socket, IPPROTO_TCP,  TCP_NODELAY,
-                    reinterpret_cast<char *>(&on), sizeof(on));
+                    reinterpret_cast<char*>(&on), sizeof(on));
     if (rc < 0) {
       close(listening_socket);
       LOG(FATAL) << "setsockopt() failed fd=" << listening_socket << "\n";
@@ -2189,7 +2178,7 @@
 int main(int argc, char**argv) {
   bool use_ssl = FLAGS_use_ssl;
   int response_count_until_close = FLAGS_response_count_until_close;
-  int flip_port = FLAGS_flip_port;
+  int spdy_port = FLAGS_spdy_port;
   int port = FLAGS_port;
   int backlog_size = FLAGS_accept_backlog_size;
   bool reuseport = FLAGS_reuseport;
@@ -2198,18 +2187,19 @@
   int accepts_per_wake = FLAGS_accepts_per_wake;
   int num_threads = 1;
 
-  MemoryCache flip_memory_cache;
-  flip_memory_cache.AddFiles();
+
+  MemoryCache spdy_memory_cache;
+  spdy_memory_cache.AddFiles();
 
   MemoryCache http_memory_cache;
-  http_memory_cache.CloneFrom(flip_memory_cache);
+  http_memory_cache.CloneFrom(spdy_memory_cache);
 
   LOG(INFO) <<
     "Starting up with the following state: \n"
     "                      use_ssl: " << use_ssl << "\n"
     "   response_count_until_close: " << response_count_until_close << "\n"
     "                         port: " << port << "\n"
-    "                    flip_port: " << flip_port << "\n"
+    "                    spdy_port: " << spdy_port << "\n"
     "                 backlog_size: " << backlog_size << "\n"
     "                    reuseport: " << BoolToStr(reuseport) << "\n"
     "                     no_nagle: " << BoolToStr(no_nagle) << "\n"
@@ -2221,7 +2211,7 @@
 
   if (use_ssl) {
     global_ssl_state = new GlobalSSLState;
-    flip_init_ssl(global_ssl_state);
+    spdy_init_ssl(global_ssl_state);
   } else {
     global_ssl_state = NULL;
   }
@@ -2229,31 +2219,31 @@
   vector<SMAcceptorThread*> sm_worker_threads_;
 
   {
-    // flip
+    // spdy
     int listen_fd = -1;
 
     if (reuseport || listen_fd == -1) {
-      listen_fd = CreateListeningSocket(flip_port, backlog_size,
+      listen_fd = CreateListeningSocket(spdy_port, backlog_size,
                                         reuseport, no_nagle);
       if (listen_fd < 0) {
-        LOG(FATAL) << "Unable to open listening socket on flip_port: "
-          << flip_port;
+        LOG(FATAL) << "Unable to open listening socket on spdy_port: "
+          << spdy_port;
       } else {
-        LOG(INFO) << "Listening for flip on port: " << flip_port;
+        LOG(INFO) << "Listening for spdy on port: " << spdy_port;
       }
     }
     sm_worker_threads_.push_back(
         new SMAcceptorThread(listen_fd,
                              accepts_per_wake,
-                             &NewFlipSM,
-                             &flip_memory_cache));
-    // Note that flip_memory_cache is not threadsafe, it is merely
+                             &NewSpdySM,
+                             &spdy_memory_cache));
+    // Note that spdy_memory_cache is not threadsafe, it is merely
     // thread compatible. Thus, if ever we are to spawn multiple threads,
     // we either must make the MemoryCache threadsafe, or use
     // a separate MemoryCache for each thread.
     //
     // The latter is what is currently being done as we spawn
-    // two threads (one for flip, one for http).
+    // two threads (one for spdy, one for http).
     sm_worker_threads_.back()->InitWorker();
     sm_worker_threads_.back()->Start();
   }
@@ -2275,13 +2265,13 @@
                              accepts_per_wake,
                              &NewHTTPSM,
                              &http_memory_cache));
-    // Note that flip_memory_cache is not threadsafe, it is merely
+    // Note that spdy_memory_cache is not threadsafe, it is merely
     // thread compatible. Thus, if ever we are to spawn multiple threads,
     // we either must make the MemoryCache threadsafe, or use
     // a separate MemoryCache for each thread.
     //
     // The latter is what is currently being done as we spawn
-    // two threads (one for flip, one for http).
+    // two threads (one for spdy, one for http).
     sm_worker_threads_.back()->InitWorker();
     sm_worker_threads_.back()->Start();
   }
diff --git a/net/tools/flip_server/other_defines.h b/net/tools/flip_server/other_defines.h
index 05545e1..ed824e4 100644
--- a/net/tools/flip_server/other_defines.h
+++ b/net/tools/flip_server/other_defines.h
@@ -1,13 +1,6 @@
 #ifndef NET_TOOLS_FLIP_SERVER_OTHER_DEFINES
 #define NET_TOOLS_FLIP_SERVER_OTHER_DEFINES
 
-#define CHECK_EQ(X, Y) CHECK((X) == (Y))
-#define CHECK_NE(X, Y) CHECK((X) != (Y))
-#define CHECK_GE(X, Y) CHECK((X) >= (Y))
-#define CHECK_GT(X, Y) CHECK((X) > (Y))
-#define CHECK_LE(X, Y) CHECK((X) <= (Y))
-#define CHECK_LT(X, Y) CHECK((X) < (Y))
-
 class NullStream {
  public:
   NullStream() {}
diff --git a/net/tools/hresolv/hresolv.cc b/net/tools/hresolv/hresolv.cc
index f092703..d524bd7 100644
--- a/net/tools/hresolv/hresolv.cc
+++ b/net/tools/hresolv/hresolv.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -27,6 +27,7 @@
 #include "base/condition_variable.h"
 #include "base/file_path.h"
 #include "base/file_util.h"
+#include "base/message_loop.h"
 #include "base/string_util.h"
 #include "base/thread.h"
 #include "base/time.h"
@@ -50,7 +51,7 @@
   {AI_V4MAPPED, "AI_V4MAPPED"},
   {AI_ALL, "AI_ALL"},
   {AI_ADDRCONFIG, "AI_ADDRCONFIG"},
-#if defined(OS_LINUX) || defined(OS_WIN)
+#if !defined(OS_MACOSX)
   {AI_NUMERICSERV, "AI_NUMERICSERV"},
 #endif
 };
@@ -185,7 +186,7 @@
         invoker_(invoker),
         ALLOW_THIS_IN_INITIALIZER_LIST(
             io_callback_(this, &DelayedResolve::OnResolveComplete)) {
-   }
+  }
 
   void Start() {
     net::CompletionCallback* callback = (is_async_) ? &io_callback_ : NULL;
@@ -194,7 +195,7 @@
                                 &address_list_,
                                 callback,
                                 NULL,
-                                NULL);
+                                net::BoundNetLog());
     if (rv != net::ERR_IO_PENDING) {
       OnResolveComplete(rv);
     }
@@ -353,14 +354,18 @@
 }
 
 bool ReadHostsAndTimesFromLooseValues(
-    const std::vector<std::wstring>& loose_args,
+    const std::vector<CommandLine::StringType>& args,
     std::vector<HostAndTime>* hosts_and_times) {
-  std::vector<std::wstring>::const_iterator loose_args_end = loose_args.end();
-  for (std::vector<std::wstring>::const_iterator it = loose_args.begin();
-       it != loose_args_end;
+  for (std::vector<CommandLine::StringType>::const_iterator it =
+           args.begin();
+       it != args.end();
        ++it) {
     // TODO(cbentzel): Read time offset.
+#if defined(OS_WIN)
     HostAndTime host_and_time = {WideToASCII(*it), 0};
+#else
+    HostAndTime host_and_time = {*it, 0};
+#endif
     hosts_and_times->push_back(host_and_time);
   }
   return true;
@@ -430,7 +435,7 @@
   // file into memory.
   std::vector<HostAndTime> hosts_and_times;
   if (options.input_path.empty()) {
-    if (!ReadHostsAndTimesFromLooseValues(command_line->GetLooseValues(),
+    if (!ReadHostsAndTimesFromLooseValues(command_line->args(),
                                           &hosts_and_times)) {
       exit(1);
     }
@@ -446,7 +451,7 @@
       base::TimeDelta::FromSeconds(0));
 
   scoped_refptr<net::HostResolver> host_resolver(
-      new net::HostResolverImpl(NULL, cache, NULL, 100u));
+      new net::HostResolverImpl(NULL, cache, 100u));
   ResolverInvoker invoker(host_resolver.get());
   invoker.ResolveAll(hosts_and_times, options.async);
 
diff --git a/net/tools/spdyshark/AUTHORS b/net/tools/spdyshark/AUTHORS
new file mode 100644
index 0000000..5643b20
--- /dev/null
+++ b/net/tools/spdyshark/AUTHORS
@@ -0,0 +1,2 @@
+Author:
+Eric Shienbrood <ers@google.com>
diff --git a/net/tools/spdyshark/COPYING b/net/tools/spdyshark/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/net/tools/spdyshark/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/net/tools/spdyshark/ChangeLog b/net/tools/spdyshark/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/net/tools/spdyshark/ChangeLog
diff --git a/net/tools/spdyshark/INSTALL b/net/tools/spdyshark/INSTALL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/net/tools/spdyshark/INSTALL
diff --git a/net/tools/spdyshark/Makefile.am b/net/tools/spdyshark/Makefile.am
new file mode 100644
index 0000000..b707bae
--- /dev/null
+++ b/net/tools/spdyshark/Makefile.am
@@ -0,0 +1,126 @@
+# Makefile.am
+# Automake file for SPDY plugin
+#
+# $Id$
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+
+INCLUDES = -I$(top_srcdir) -I$(includedir)
+
+include Makefile.common
+
+if HAVE_WARNINGS_AS_ERRORS
+AM_CFLAGS = -Werror
+endif
+
+plugin_LTLIBRARIES = spdy.la
+spdy_la_SOURCES = \
+	plugin.c \
+	moduleinfo.h \
+	$(DISSECTOR_SRC) \
+	$(DISSECTOR_SUPPORT_SRC) \
+	$(DISSECTOR_INCLUDES)
+spdy_la_LDFLAGS = -module -avoid-version
+spdy_la_LIBADD = @PLUGIN_LIBS@
+
+# Libs must be cleared, or else libtool won't create a shared module.
+# If your module needs to be linked against any particular libraries,
+# add them here.
+LIBS =
+
+#
+# Build plugin.c, which contains the plugin version[] string, a
+# function plugin_register() that calls the register routines for all
+# protocols, and a function plugin_reg_handoff() that calls the handoff
+# registration routines for all protocols.
+#
+# We do this by scanning sources.  If that turns out to be too slow,
+# maybe we could just require every .o file to have an register routine
+# of a given name (packet-aarp.o -> proto_register_aarp, etc.).
+#
+# Formatting conventions:  The name of the proto_register_* routines an
+# proto_reg_handoff_* routines must start in column zero, or must be
+# preceded only by "void " starting in column zero, and must not be
+# inside #if.
+#
+# DISSECTOR_SRC is assumed to have all the files that need to be scanned.
+#
+# For some unknown reason, having a big "for" loop in the Makefile
+# to scan all the files doesn't work with some "make"s; they seem to
+# pass only the first few names in the list to the shell, for some
+# reason.
+#
+# Therefore, we have a script to generate the plugin.c file.
+# The shell script runs slowly, as multiple greps and seds are run
+# for each input file; this is especially slow on Windows.  Therefore,
+# if Python is present (as indicated by PYTHON being defined), we run
+# a faster Python script to do that work instead.
+#
+# The first argument is the directory in which the source files live.
+# The second argument is "plugin", to indicate that we should build
+# a plugin.c file for a plugin.
+# All subsequent arguments are the files to scan.
+#
+plugin.c: $(DISSECTOR_SRC) $(top_srcdir)/tools/make-dissector-reg \
+    $(top_srcdir)/tools/make-dissector-reg.py
+	@if test -n "$(PYTHON)"; then \
+		echo Making plugin.c with python ; \
+		$(PYTHON) $(top_srcdir)/tools/make-dissector-reg.py $(srcdir) \
+		    plugin $(DISSECTOR_SRC) ; \
+	else \
+		echo Making plugin.c with shell script ; \
+		$(top_srcdir)/tools/make-dissector-reg $(srcdir) \
+		    $(plugin_src) plugin $(DISSECTOR_SRC) ; \
+	fi
+
+#
+# Currently plugin.c can be included in the distribution because
+# we always build all protocol dissectors. We used to have to check
+# whether or not to build the snmp dissector. If we again need to
+# variably build something, making plugin.c non-portable, uncomment
+# the dist-hook line below.
+#
+# Oh, yuk.  We don't want to include "plugin.c" in the distribution, as
+# its contents depend on the configuration, and therefore we want it
+# to be built when the first "make" is done; however, Automake insists
+# on putting *all* source into the distribution.
+#
+# We work around this by having a "dist-hook" rule that deletes
+# "plugin.c", so that "dist" won't pick it up.
+#
+#dist-hook:
+#	@rm -f $(distdir)/plugin.c
+
+CLEANFILES = \
+	spdy \
+	*~
+
+MAINTAINERCLEANFILES = \
+	Makefile.in	\
+	plugin.c
+
+EXTRA_DIST = \
+	Makefile.common		\
+	Makefile.nmake		\
+	moduleinfo.nmake	\
+	plugin.rc.in
+
+checkapi:
+	$(PERL) $(top_srcdir)/tools/checkAPIs.pl -g abort -g termoutput $(DISSECTOR_SRC)
diff --git a/net/tools/spdyshark/Makefile.common b/net/tools/spdyshark/Makefile.common
new file mode 100644
index 0000000..9386f46
--- /dev/null
+++ b/net/tools/spdyshark/Makefile.common
@@ -0,0 +1,40 @@
+# Makefile.common for SPDY plugin
+#     Contains the stuff from Makefile.am and Makefile.nmake that is
+#     a) common to both files and
+#     b) portable between both files
+#
+# $Id$
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+# the name of the plugin
+PLUGIN_NAME = spdy
+
+# the dissector sources (without any helpers)
+DISSECTOR_SRC = \
+	packet-spdy.c
+
+# corresponding headers
+DISSECTOR_INCLUDES =	\
+	packet-spdy.h
+
+# Dissector helpers.  They're included in the source files in this
+# directory, but they're not dissectors themselves, i.e. they're not
+# used to generate "register.c").
+#DISSECTOR_SUPPORT_SRC =
diff --git a/net/tools/spdyshark/Makefile.nmake b/net/tools/spdyshark/Makefile.nmake
new file mode 100644
index 0000000..d554918
--- /dev/null
+++ b/net/tools/spdyshark/Makefile.nmake
@@ -0,0 +1,104 @@
+# Makefile.nmake
+# nmake file for Wireshark plugin
+#
+# $Id: Makefile.nmake 27579 2009-03-02 18:57:35Z gerald $
+#
+
+include ..\..\config.nmake
+include moduleinfo.nmake
+
+include Makefile.common
+
+CFLAGS=/WX /Zi /DHAVE_CONFIG_H /I../.. $(GLIB_CFLAGS) $(ZLIB_CFLAGS) \
+	/I$(PCAP_DIR)\include -D_U_="" $(LOCAL_CFLAGS)
+
+.c.obj::
+	$(CC) $(CFLAGS) -Fd.\ -c $<
+
+LDFLAGS = $(PLUGIN_LDFLAGS)
+
+!IFDEF ENABLE_LIBWIRESHARK
+LINK_PLUGIN_WITH=..\..\epan\libwireshark.lib $(ZLIB_LIBS)
+CFLAGS=/DHAVE_WIN32_LIBWIRESHARK_LIB /D_NEED_VAR_IMPORT_ $(CFLAGS)
+
+DISSECTOR_OBJECTS = $(DISSECTOR_SRC:.c=.obj)
+
+DISSECTOR_SUPPORT_OBJECTS = $(DISSECTOR_SUPPORT_SRC:.c=.obj)
+
+OBJECTS = $(DISSECTOR_OBJECTS) $(DISSECTOR_SUPPORT_OBJECTS) plugin.obj
+
+RESOURCE=$(PLUGIN_NAME).res
+
+all: $(PLUGIN_NAME).dll
+
+$(PLUGIN_NAME).rc : moduleinfo.nmake
+	sed -e s/@PLUGIN_NAME@/$(PLUGIN_NAME)/ \
+	-e s/@RC_MODULE_VERSION@/$(RC_MODULE_VERSION)/ \
+	-e s/@RC_VERSION@/$(RC_VERSION)/ \
+	-e s/@MODULE_VERSION@/$(MODULE_VERSION)/ \
+	-e s/@PACKAGE@/$(PACKAGE)/ \
+	-e s/@VERSION@/$(VERSION)/ \
+	-e s/@MSVC_VARIANT@/$(MSVC_VARIANT)/ \
+	< plugin.rc.in > $@
+
+$(PLUGIN_NAME).dll $(PLUGIN_NAME).exp $(PLUGIN_NAME).lib : $(OBJECTS) $(LINK_PLUGIN_WITH) $(RESOURCE)
+	link -dll /out:$(PLUGIN_NAME).dll $(LDFLAGS) $(OBJECTS) $(LINK_PLUGIN_WITH) \
+	$(GLIB_LIBS) $(RESOURCE)
+
+#
+# Build plugin.c, which contains the plugin version[] string, a
+# function plugin_register() that calls the register routines for all
+# protocols, and a function plugin_reg_handoff() that calls the handoff
+# registration routines for all protocols.
+#
+# We do this by scanning sources.  If that turns out to be too slow,
+# maybe we could just require every .o file to have an register routine
+# of a given name (packet-aarp.o -> proto_register_aarp, etc.).
+#
+# Formatting conventions:  The name of the proto_register_* routines an
+# proto_reg_handoff_* routines must start in column zero, or must be
+# preceded only by "void " starting in column zero, and must not be
+# inside #if.
+#
+# DISSECTOR_SRC is assumed to have all the files that need to be scanned.
+#
+# For some unknown reason, having a big "for" loop in the Makefile
+# to scan all the files doesn't work with some "make"s; they seem to
+# pass only the first few names in the list to the shell, for some
+# reason.
+#
+# Therefore, we have a script to generate the plugin.c file.
+# The shell script runs slowly, as multiple greps and seds are run
+# for each input file; this is especially slow on Windows.  Therefore,
+# if Python is present (as indicated by PYTHON being defined), we run
+# a faster Python script to do that work instead.
+#
+# The first argument is the directory in which the source files live.
+# The second argument is "plugin", to indicate that we should build
+# a plugin.c file for a plugin.
+# All subsequent arguments are the files to scan.
+#
+!IFDEF PYTHON
+plugin.c: $(DISSECTOR_SRC) moduleinfo.h ../../tools/make-dissector-reg.py
+	@echo Making plugin.c (using python)
+	@$(PYTHON) "../../tools/make-dissector-reg.py" . plugin $(DISSECTOR_SRC)
+!ELSE
+plugin.c: $(DISSECTOR_SRC) moduleinfo.h ../../tools/make-dissector-reg
+	@echo Making plugin.c (using sh)
+	@$(SH) ../../tools/make-dissector-reg . plugin $(DISSECTOR_SRC)
+!ENDIF
+
+!ENDIF
+
+clean:
+	rm -f $(OBJECTS) $(RESOURCE) plugin.c *.pdb \
+	    $(PLUGIN_NAME).dll $(PLUGIN_NAME).dll.manifest $(PLUGIN_NAME).lib \
+	    $(PLUGIN_NAME).exp $(PLUGIN_NAME).rc
+
+distclean: clean
+
+maintainer-clean: distclean
+
+checkapi:
+# TODO: Fix api's :)
+#	$(PERL) ../../tools/checkAPIs.pl -g abort -g termoutput $(DISSECTOR_SRC)
diff --git a/net/tools/spdyshark/NEWS b/net/tools/spdyshark/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/net/tools/spdyshark/NEWS
diff --git a/net/tools/spdyshark/moduleinfo.h b/net/tools/spdyshark/moduleinfo.h
new file mode 100644
index 0000000..9e5f7c8
--- /dev/null
+++ b/net/tools/spdyshark/moduleinfo.h
@@ -0,0 +1,16 @@
+/* Included *after* config.h, in order to re-define these macros */
+
+#ifdef PACKAGE
+#undef PACKAGE
+#endif
+
+/* Name of package */
+#define PACKAGE "spdy"
+
+
+#ifdef VERSION
+#undef VERSION
+#endif
+
+/* Version number of package */
+#define VERSION "0.1.0"
diff --git a/net/tools/spdyshark/moduleinfo.nmake b/net/tools/spdyshark/moduleinfo.nmake
new file mode 100644
index 0000000..bbdf766
--- /dev/null
+++ b/net/tools/spdyshark/moduleinfo.nmake
@@ -0,0 +1,28 @@
+#
+# $Id$
+#
+
+# The name
+PACKAGE=spdy
+
+# The version
+MODULE_VERSION_MAJOR=0
+MODULE_VERSION_MINOR=1
+MODULE_VERSION_MICRO=0
+MODULE_VERSION_EXTRA=0
+
+#
+# The RC_VERSION should be comma-separated, not dot-separated, 
+# as per Graham Bloice's message in
+#
+#	http://www.ethereal.com/lists/ethereal-dev/200303/msg00283.html
+#
+# "The RC_VERSION variable in config.nmake should be comma separated. 
+# This allows the resources to be built correctly and the version
+# number to be correctly displayed in the explorer properties dialog
+# for the executables, and XP's tooltip, rather than 0.0.0.0."
+#
+
+MODULE_VERSION=$(MODULE_VERSION_MAJOR).$(MODULE_VERSION_MINOR).$(MODULE_VERSION_MICRO).$(MODULE_VERSION_EXTRA)
+RC_MODULE_VERSION=$(MODULE_VERSION_MAJOR),$(MODULE_VERSION_MINOR),$(MODULE_VERSION_MICRO),$(MODULE_VERSION_EXTRA)
+
diff --git a/net/tools/spdyshark/packet-spdy.c b/net/tools/spdyshark/packet-spdy.c
new file mode 100644
index 0000000..becc585
--- /dev/null
+++ b/net/tools/spdyshark/packet-spdy.c
@@ -0,0 +1,1532 @@
+/* packet-spdy.c
+ * Routines for SPDY packet disassembly
+ * For now, the protocol spec can be found at
+ * http://dev.chromium.org/spdy/spdy-protocol
+ *
+ * Copyright 2010, Google Inc.
+ * Eric Shienbrood <ers@google.com>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Originally based on packet-http.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <ctype.h>
+
+#include <glib.h>
+#include <epan/conversation.h>
+#include <epan/packet.h>
+#include <epan/strutil.h>
+#include <epan/base64.h>
+#include <epan/emem.h>
+#include <epan/stats_tree.h>
+
+#include <epan/req_resp_hdrs.h>
+#include "packet-spdy.h"
+#include <epan/dissectors/packet-tcp.h>
+#include <epan/dissectors/packet-ssl.h>
+#include <epan/prefs.h>
+#include <epan/expert.h>
+#include <epan/uat.h>
+
+#define SPDY_FIN  0x01
+
+/* The types of SPDY control frames */
+typedef enum _spdy_type {
+	SPDY_DATA,
+	SPDY_SYN_STREAM,
+	SPDY_SYN_REPLY,
+	SPDY_FIN_STREAM,
+	SPDY_HELLO,
+	SPDY_NOOP,
+	SPDY_PING,
+	SPDY_INVALID
+} spdy_frame_type_t;
+
+static const char *frame_type_names[] = {
+  "DATA", "SYN_STREAM", "SYN_REPLY", "FIN_STREAM", "HELLO", "NOOP",
+  "PING", "INVALID"
+};
+
+/*
+ * This structure will be tied to each SPDY frame.
+ * Note that there may be multiple SPDY frames
+ * in one packet.
+ */
+typedef struct _spdy_frame_info_t {
+    guint32 stream_id;
+    guint8 *header_block;
+    guint   header_block_len;
+    guint16 frame_type;
+} spdy_frame_info_t;
+
+/*
+ * This structures keeps track of all the data frames
+ * associated with a stream, so that they can be
+ * reassembled into a single chunk.
+ */
+typedef struct _spdy_data_frame_t {
+    guint8 *data;
+    guint32 length;
+    guint32 framenum;
+} spdy_data_frame_t;
+
+typedef struct _spdy_stream_info_t {
+    gchar *content_type;
+    gchar *content_type_parameters;
+    gchar *content_encoding;
+    GSList *data_frames;
+    tvbuff_t *assembled_data;
+    guint num_data_frames;
+} spdy_stream_info_t;
+
+#include <epan/tap.h>
+
+
+static int spdy_tap = -1;
+static int spdy_eo_tap = -1;
+
+static int proto_spdy = -1;
+static int hf_spdy_syn_stream = -1;
+static int hf_spdy_syn_reply = -1;
+static int hf_spdy_control_bit = -1;
+static int hf_spdy_version = -1;
+static int hf_spdy_type = -1;
+static int hf_spdy_flags = -1;
+static int hf_spdy_flags_fin = -1;
+static int hf_spdy_length = -1;
+static int hf_spdy_header = -1;
+static int hf_spdy_header_name = -1;
+static int hf_spdy_header_name_text = -1;
+static int hf_spdy_header_value = -1;
+static int hf_spdy_header_value_text = -1;
+static int hf_spdy_streamid = -1;
+static int hf_spdy_associated_streamid = -1;
+static int hf_spdy_priority = -1;
+static int hf_spdy_num_headers = -1;
+static int hf_spdy_num_headers_string = -1;
+
+static gint ett_spdy = -1;
+static gint ett_spdy_syn_stream = -1;
+static gint ett_spdy_syn_reply = -1;
+static gint ett_spdy_fin_stream = -1;
+static gint ett_spdy_flags = -1;
+static gint ett_spdy_header = -1;
+static gint ett_spdy_header_name = -1;
+static gint ett_spdy_header_value = -1;
+
+static gint ett_spdy_encoded_entity = -1;
+
+static dissector_handle_t data_handle;
+static dissector_handle_t media_handle;
+static dissector_handle_t spdy_handle;
+
+/* Stuff for generation/handling of fields for custom HTTP headers */
+typedef struct _header_field_t {
+	gchar* header_name;
+	gchar* header_desc;
+} header_field_t;
+
+/*
+ * desegmentation of SPDY control frames
+ * (when we are over TCP or another protocol providing the desegmentation API)
+ */
+static gboolean spdy_desegment_control_frames = TRUE;
+
+/*
+ * desegmentation of SPDY data frames bodies
+ * (when we are over TCP or another protocol providing the desegmentation API)
+ * TODO let the user filter on content-type the bodies he wants desegmented
+ */
+static gboolean spdy_desegment_data_frames = TRUE;
+
+static gboolean spdy_assemble_entity_bodies = TRUE;
+
+/*
+ * Decompression of zlib encoded entities.
+ */
+#ifdef HAVE_LIBZ
+static gboolean spdy_decompress_body = TRUE;
+static gboolean spdy_decompress_headers = TRUE;
+#else
+static gboolean spdy_decompress_body = FALSE;
+static gboolean spdy_decompress_headers = FALSE;
+#endif
+static gboolean spdy_debug = FALSE;
+
+#define TCP_PORT_DAAP			3689
+
+/*
+ * SSDP is implemented atop HTTP (yes, it really *does* run over UDP).
+ */
+#define TCP_PORT_SSDP			1900
+#define UDP_PORT_SSDP			1900
+
+/*
+ * tcp and ssl ports
+ */
+
+#define TCP_DEFAULT_RANGE "80,8080"
+#define SSL_DEFAULT_RANGE "443"
+
+static range_t *global_spdy_tcp_range = NULL;
+static range_t *global_spdy_ssl_range = NULL;
+
+static range_t *spdy_tcp_range = NULL;
+static range_t *spdy_ssl_range = NULL;
+
+static const value_string vals_status_code[] = {
+	{ 100, "Continue" },
+	{ 101, "Switching Protocols" },
+	{ 102, "Processing" },
+	{ 199, "Informational - Others" },
+
+	{ 200, "OK"},
+	{ 201, "Created"},
+	{ 202, "Accepted"},
+	{ 203, "Non-authoritative Information"},
+	{ 204, "No Content"},
+	{ 205, "Reset Content"},
+	{ 206, "Partial Content"},
+	{ 207, "Multi-Status"},
+	{ 299, "Success - Others"},
+
+	{ 300, "Multiple Choices"},
+	{ 301, "Moved Permanently"},
+	{ 302, "Found"},
+	{ 303, "See Other"},
+	{ 304, "Not Modified"},
+	{ 305, "Use Proxy"},
+	{ 307, "Temporary Redirect"},
+	{ 399, "Redirection - Others"},
+
+	{ 400, "Bad Request"},
+	{ 401, "Unauthorized"},
+	{ 402, "Payment Required"},
+	{ 403, "Forbidden"},
+	{ 404, "Not Found"},
+	{ 405, "Method Not Allowed"},
+	{ 406, "Not Acceptable"},
+	{ 407, "Proxy Authentication Required"},
+	{ 408, "Request Time-out"},
+	{ 409, "Conflict"},
+	{ 410, "Gone"},
+	{ 411, "Length Required"},
+	{ 412, "Precondition Failed"},
+	{ 413, "Request Entity Too Large"},
+	{ 414, "Request-URI Too Long"},
+	{ 415, "Unsupported Media Type"},
+	{ 416, "Requested Range Not Satisfiable"},
+	{ 417, "Expectation Failed"},
+	{ 418, "I'm a teapot"},         /* RFC 2324 */
+	{ 422, "Unprocessable Entity"},
+	{ 423, "Locked"},
+	{ 424, "Failed Dependency"},
+	{ 499, "Client Error - Others"},
+
+	{ 500, "Internal Server Error"},
+	{ 501, "Not Implemented"},
+	{ 502, "Bad Gateway"},
+	{ 503, "Service Unavailable"},
+	{ 504, "Gateway Time-out"},
+	{ 505, "HTTP Version not supported"},
+	{ 507, "Insufficient Storage"},
+	{ 599, "Server Error - Others"},
+
+	{ 0,	NULL}
+};
+
+static const char spdy_dictionary[] =
+  "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
+  "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
+  "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
+  "-agent10010120020120220320420520630030130230330430530630740040140240340440"
+  "5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
+  "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
+  "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
+  "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
+  "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
+  "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
+  "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
+  "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
+  ".1statusversionurl";
+
+static void reset_decompressors(void)
+{
+    if (spdy_debug) printf("Should reset SPDY decompressors\n");
+}
+
+static spdy_conv_t *
+get_spdy_conversation_data(packet_info *pinfo)
+{
+    conversation_t  *conversation;
+    spdy_conv_t *conv_data;
+    int retcode;
+
+    conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
+    if (spdy_debug) {
+        printf("\n===========================================\n\n");
+        printf("Conversation for frame #%d is %p\n", pinfo->fd->num, conversation);
+        if (conversation)
+            printf("  conv_data=%p\n", conversation_get_proto_data(conversation, proto_spdy));
+    }
+
+    if(!conversation)  /* Conversation does not exist yet - create it */
+	conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
+
+    /* Retrieve information from conversation
+    */
+    conv_data = conversation_get_proto_data(conversation, proto_spdy);
+    if(!conv_data) {
+	/* Setup the conversation structure itself */
+	conv_data = se_alloc0(sizeof(spdy_conv_t));
+
+	conv_data->streams = NULL;
+	if (spdy_decompress_headers) {
+	    conv_data->rqst_decompressor = se_alloc0(sizeof(z_stream));
+	    conv_data->rply_decompressor = se_alloc0(sizeof(z_stream));
+	    retcode = inflateInit(conv_data->rqst_decompressor);
+	    if (retcode == Z_OK)
+		retcode = inflateInit(conv_data->rply_decompressor);
+	    if (retcode != Z_OK)
+		printf("frame #%d: inflateInit() failed: %d\n", pinfo->fd->num, retcode);
+	    else if (spdy_debug)
+		printf("created decompressor\n");
+	    conv_data->dictionary_id = adler32(0L, Z_NULL, 0);
+	    conv_data->dictionary_id = adler32(conv_data->dictionary_id,
+					       spdy_dictionary,
+					       sizeof(spdy_dictionary));
+	}
+
+	conversation_add_proto_data(conversation, proto_spdy, conv_data);
+	register_postseq_cleanup_routine(reset_decompressors);
+    }
+    return conv_data;
+}
+
+static void
+spdy_save_stream_info(spdy_conv_t *conv_data,
+		      guint32 stream_id,
+		      gchar *content_type,
+                      gchar *content_type_params,
+		      gchar *content_encoding)
+{
+    spdy_stream_info_t *si;
+
+    if (conv_data->streams == NULL)
+	conv_data->streams = g_array_new(FALSE, TRUE, sizeof(spdy_stream_info_t *));
+    if (stream_id < conv_data->streams->len)
+	DISSECTOR_ASSERT(g_array_index(conv_data->streams, spdy_stream_info_t*, stream_id) == NULL);
+    else
+        g_array_set_size(conv_data->streams, stream_id+1);
+    si = se_alloc(sizeof(spdy_stream_info_t));
+    si->content_type = content_type;
+    si->content_type_parameters = content_type_params;
+    si->content_encoding = content_encoding;
+    si->data_frames = NULL;
+    si->num_data_frames = 0;
+    si->assembled_data = NULL;
+    g_array_index(conv_data->streams, spdy_stream_info_t*, stream_id) = si;
+    if (spdy_debug)
+        printf("Saved stream info for ID %u, content type %s\n", stream_id, content_type);
+}
+
+static spdy_stream_info_t *
+spdy_get_stream_info(spdy_conv_t *conv_data, guint32 stream_id)
+{
+    if (conv_data->streams == NULL || stream_id >= conv_data->streams->len)
+	return NULL;
+    else
+	return g_array_index(conv_data->streams, spdy_stream_info_t*, stream_id);
+}
+
+static void
+spdy_add_data_chunk(spdy_conv_t *conv_data, guint32 stream_id, guint32 frame,
+		    guint8 *data, guint32 length)
+{
+    spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
+
+    if (si == NULL) {
+	if (spdy_debug) printf("No stream_info found for stream %d\n", stream_id);
+    } else {
+	spdy_data_frame_t *df = g_malloc(sizeof(spdy_data_frame_t));
+	df->data = data;
+	df->length = length;
+	df->framenum = frame;
+	si->data_frames = g_slist_append(si->data_frames, df);
+	++si->num_data_frames;
+	if (spdy_debug)
+	    printf("Saved %u bytes of data for stream %u frame %u\n",
+		    length, stream_id, df->framenum);
+    }
+}
+
+static void
+spdy_increment_data_chunk_count(spdy_conv_t *conv_data, guint32 stream_id)
+{
+    spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
+    if (si != NULL)
+	++si->num_data_frames;
+}
+
+/*
+ * Return the number of data frames saved so far for the specified stream.
+ */
+static guint
+spdy_get_num_data_frames(spdy_conv_t *conv_data, guint32 stream_id)
+{
+    spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
+
+    return si == NULL ? 0 : si->num_data_frames;
+}
+
+static spdy_stream_info_t *
+spdy_assemble_data_frames(spdy_conv_t *conv_data, guint32 stream_id)
+{
+    spdy_stream_info_t *si = spdy_get_stream_info(conv_data, stream_id);
+    tvbuff_t *tvb;
+
+    if (si == NULL)
+	return NULL;
+
+    /*
+     * Compute the total amount of data and concatenate the
+     * data chunks, if it hasn't already been done.
+     */
+    if (si->assembled_data == NULL) {
+	spdy_data_frame_t *df;
+	guint8 *data;
+	guint32 datalen;
+	guint32 offset;
+	guint32 framenum;
+	GSList *dflist = si->data_frames;
+	if (dflist == NULL)
+	    return si;
+	dflist = si->data_frames;
+	datalen = 0;
+	/*
+	 * I'd like to use a composite tvbuff here, but since
+	 * only a real-data tvbuff can be the child of another
+	 * tvb, I can't. It would be nice if this limitation
+	 * could be fixed.
+	 */
+	while (dflist != NULL) {
+	    df = dflist->data;
+	    datalen += df->length;
+	    dflist = g_slist_next(dflist);
+	}
+	if (datalen != 0) {
+	    data = se_alloc(datalen);
+	    dflist = si->data_frames;
+	    offset = 0;
+	    framenum = 0;
+	    while (dflist != NULL) {
+		df = dflist->data;
+		memcpy(data+offset, df->data, df->length);
+		offset += df->length;
+		dflist = g_slist_next(dflist);
+	    }
+	    tvb = tvb_new_real_data(data, datalen, datalen);
+	    si->assembled_data = tvb;
+	}
+    }
+    return si;
+}
+
+static void
+spdy_discard_data_frames(spdy_stream_info_t *si)
+{
+    GSList *dflist = si->data_frames;
+    spdy_data_frame_t *df;
+
+    if (dflist == NULL)
+	return;
+    while (dflist != NULL) {
+	df = dflist->data;
+	if (df->data != NULL) {
+	    g_free(df->data);
+	    df->data = NULL;
+	}
+	dflist = g_slist_next(dflist);
+    }
+    /*g_slist_free(si->data_frames);
+    si->data_frames = NULL; */
+}
+
+// TODO(cbentzel): tvb_child_uncompress should be exported by wireshark.
+static tvbuff_t* spdy_tvb_child_uncompress(tvbuff_t *parent _U_, tvbuff_t *tvb,
+                                           int offset, int comprlen)
+{
+	tvbuff_t *new_tvb = tvb_uncompress(tvb, offset, comprlen);
+	if (new_tvb)
+		tvb_set_child_real_data_tvbuff (parent, new_tvb);
+	return new_tvb;
+}
+
+static int
+dissect_spdy_data_frame(tvbuff_t *tvb, int offset,
+			packet_info *pinfo,
+			proto_tree *top_level_tree,
+			proto_tree *spdy_tree,
+			proto_item *spdy_proto,
+			spdy_conv_t *conv_data)
+{
+    guint32	stream_id;
+    guint8	flags;
+    guint32	frame_length;
+    proto_item	*ti;
+    proto_tree	*flags_tree;
+    guint32	reported_datalen;
+    guint32	datalen;
+    dissector_table_t media_type_subdissector_table;
+    dissector_table_t port_subdissector_table;
+    dissector_handle_t handle;
+    guint	num_data_frames;
+    gboolean    dissected;
+
+    stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
+    flags = tvb_get_guint8(tvb, offset+4);
+    frame_length = tvb_get_ntoh24(tvb, offset+5);
+
+    if (spdy_debug)
+	printf("Data frame [stream_id=%u flags=0x%x length=%d]\n",
+		stream_id, flags, frame_length);
+    if (spdy_tree) proto_item_append_text(spdy_tree, ", data frame");
+    col_add_fstr(pinfo->cinfo, COL_INFO, "DATA[%u] length=%d",
+	    stream_id, frame_length);
+
+    proto_item_append_text(spdy_proto, ":%s stream=%d length=%d",
+	    flags & SPDY_FIN ? " [FIN]" : "",
+	    stream_id, frame_length);
+
+    proto_tree_add_boolean(spdy_tree, hf_spdy_control_bit, tvb, offset, 1, 0);
+    proto_tree_add_uint(spdy_tree, hf_spdy_streamid, tvb, offset, 4, stream_id);
+    ti = proto_tree_add_uint_format(spdy_tree, hf_spdy_flags, tvb, offset+4, 1, flags,
+	    "Flags: 0x%02x%s", flags, flags&SPDY_FIN ? " (FIN)" : "");
+
+    flags_tree = proto_item_add_subtree(ti, ett_spdy_flags);
+    proto_tree_add_boolean(flags_tree, hf_spdy_flags_fin, tvb, offset+4, 1, flags);
+    proto_tree_add_uint(spdy_tree, hf_spdy_length, tvb, offset+5, 3, frame_length);
+
+    datalen = tvb_length_remaining(tvb, offset);
+    if (datalen > frame_length)
+	datalen = frame_length;
+
+    reported_datalen = tvb_reported_length_remaining(tvb, offset);
+    if (reported_datalen > frame_length)
+	reported_datalen = frame_length;
+
+    num_data_frames = spdy_get_num_data_frames(conv_data, stream_id);
+    if (datalen != 0 || num_data_frames != 0) {
+	/*
+	 * There's stuff left over; process it.
+	 */
+	tvbuff_t *next_tvb = NULL;
+	tvbuff_t    *data_tvb = NULL;
+	spdy_stream_info_t *si = NULL;
+	void *save_private_data = NULL;
+	guint8 *copied_data;
+	gboolean private_data_changed = FALSE;
+	gboolean is_single_chunk = FALSE;
+	gboolean have_entire_body;
+
+	/*
+	 * Create a tvbuff for the payload.
+	 */
+	if (datalen != 0) {
+	    next_tvb = tvb_new_subset(tvb, offset+8, datalen,
+				      reported_datalen);
+            is_single_chunk = num_data_frames == 0 && (flags & SPDY_FIN) != 0;
+            if (!pinfo->fd->flags.visited) {
+                if (!is_single_chunk) {
+                    if (spdy_assemble_entity_bodies) {
+                        copied_data = tvb_memdup(next_tvb, 0, datalen);
+                        spdy_add_data_chunk(conv_data, stream_id, pinfo->fd->num,
+                                copied_data, datalen);
+                    } else
+                        spdy_increment_data_chunk_count(conv_data, stream_id);
+                }
+            }
+        } else
+            is_single_chunk = (num_data_frames == 1);
+
+	if (!(flags & SPDY_FIN)) {
+	    col_set_fence(pinfo->cinfo, COL_INFO);
+	    col_add_fstr(pinfo->cinfo, COL_INFO, " (partial entity)");
+            proto_item_append_text(spdy_proto, " (partial entity body)");
+            /* would like the proto item to say */
+            /* " (entity body fragment N of M)" */
+	    goto body_dissected;
+	}
+	have_entire_body = is_single_chunk;
+	/*
+	 * On seeing the last data frame in a stream, we can
+	 * reassemble the frames into one data block.
+	 */
+	si = spdy_assemble_data_frames(conv_data, stream_id);
+	if (si == NULL)
+	    goto body_dissected;
+	data_tvb = si->assembled_data;
+	if (spdy_assemble_entity_bodies)
+	    have_entire_body = TRUE;
+
+	if (!have_entire_body)
+	    goto body_dissected;
+
+	if (data_tvb == NULL)
+	    data_tvb = next_tvb;
+	else
+	    add_new_data_source(pinfo, data_tvb, "Assembled entity body");
+
+	if (have_entire_body && si->content_encoding != NULL &&
+	    g_ascii_strcasecmp(si->content_encoding, "identity") != 0) {
+	    /*
+	     * We currently can't handle, for example, "compress";
+	     * just handle them as data for now.
+	     *
+	     * After July 7, 2004 the LZW patent expires, so support
+	     * might be added then.  However, I don't think that
+	     * anybody ever really implemented "compress", due to
+	     * the aforementioned patent.
+	     */
+	    tvbuff_t *uncomp_tvb = NULL;
+	    proto_item *e_ti = NULL;
+	    proto_item *ce_ti = NULL;
+	    proto_tree *e_tree = NULL;
+
+	    if (spdy_decompress_body &&
+		    (g_ascii_strcasecmp(si->content_encoding, "gzip") == 0 ||
+		     g_ascii_strcasecmp(si->content_encoding, "deflate")
+		     == 0)) {
+              uncomp_tvb = spdy_tvb_child_uncompress(tvb, data_tvb, 0,
+                                                     tvb_length(data_tvb));
+	    }
+	    /*
+	     * Add the encoded entity to the protocol tree
+	     */
+	    e_ti = proto_tree_add_text(top_level_tree, data_tvb,
+		    0, tvb_length(data_tvb),
+		    "Content-encoded entity body (%s): %u bytes",
+		    si->content_encoding,
+		    tvb_length(data_tvb));
+	    e_tree = proto_item_add_subtree(e_ti, ett_spdy_encoded_entity);
+	    if (si->num_data_frames > 1) {
+		GSList *dflist;
+		spdy_data_frame_t *df;
+		guint32 framenum;
+		ce_ti = proto_tree_add_text(e_tree, data_tvb, 0,
+			tvb_length(data_tvb),
+			"Assembled from %d frames in packet(s)", si->num_data_frames);
+		dflist = si->data_frames;
+		framenum = 0;
+		while (dflist != NULL) {
+		    df = dflist->data;
+		    if (framenum != df->framenum) {
+			proto_item_append_text(ce_ti, " #%u", df->framenum);
+			framenum = df->framenum;
+		    }
+		    dflist = g_slist_next(dflist);
+		  }
+	    }
+
+	    if (uncomp_tvb != NULL) {
+		/*
+		 * Decompression worked
+		 */
+
+		/* XXX - Don't free this, since it's possible
+		 * that the data was only partially
+		 * decompressed, such as when desegmentation
+		 * isn't enabled.
+		 *
+		 tvb_free(next_tvb);
+		 */
+		proto_item_append_text(e_ti, " -> %u bytes", tvb_length(uncomp_tvb));
+		data_tvb = uncomp_tvb;
+		add_new_data_source(pinfo, data_tvb, "Uncompressed entity body");
+	    } else {
+		if (spdy_decompress_body)
+		    proto_item_append_text(e_ti, " [Error: Decompression failed]");
+		call_dissector(data_handle, data_tvb, pinfo, e_tree);
+
+		goto body_dissected;
+	    }
+	}
+	if (si != NULL)
+	    spdy_discard_data_frames(si);
+	/*
+	 * Do subdissector checks.
+	 *
+	 * First, check whether some subdissector asked that they
+	 * be called if something was on some particular port.
+	 */
+
+	port_subdissector_table = find_dissector_table("http.port");
+	media_type_subdissector_table = find_dissector_table("media_type");
+	if (have_entire_body && port_subdissector_table != NULL)
+	    handle = dissector_get_port_handle(port_subdissector_table,
+		    pinfo->match_port);
+	else
+	    handle = NULL;
+	if (handle == NULL && have_entire_body && si->content_type != NULL &&
+		media_type_subdissector_table != NULL) {
+	    /*
+	     * We didn't find any subdissector that
+	     * registered for the port, and we have a
+	     * Content-Type value.  Is there any subdissector
+	     * for that content type?
+	     */
+	    save_private_data = pinfo->private_data;
+	    private_data_changed = TRUE;
+
+	    if (si->content_type_parameters)
+		pinfo->private_data = ep_strdup(si->content_type_parameters);
+	    else
+		pinfo->private_data = NULL;
+	    /*
+	     * Calling the string handle for the media type
+	     * dissector table will set pinfo->match_string
+	     * to si->content_type for us.
+	     */
+	    pinfo->match_string = si->content_type;
+	    handle = dissector_get_string_handle(
+		    media_type_subdissector_table,
+		    si->content_type);
+	}
+	if (handle != NULL) {
+	    /*
+	     * We have a subdissector - call it.
+	     */
+	    dissected = call_dissector(handle, data_tvb, pinfo, top_level_tree);
+	} else
+	    dissected = FALSE;
+
+	if (dissected) {
+	    /*
+	     * The subdissector dissected the body.
+	     * Fix up the top-level item so that it doesn't
+	     * include the stuff for that protocol.
+	     */
+	    if (ti != NULL)
+		proto_item_set_len(ti, offset);
+	} else if (have_entire_body && si->content_type != NULL) {
+	    /*
+	     * Calling the default media handle if there is a content-type that
+	     * wasn't handled above.
+	     */
+	    call_dissector(media_handle, next_tvb, pinfo, top_level_tree);
+	} else {
+	    /* Call the default data dissector */
+	    call_dissector(data_handle, next_tvb, pinfo, top_level_tree);
+	}
+
+body_dissected:
+	/*
+	 * Do *not* attempt at freeing the private data;
+	 * it may be in use by subdissectors.
+	 */
+	if (private_data_changed) /*restore even NULL value*/
+	    pinfo->private_data = save_private_data;
+	/*
+	 * We've processed "datalen" bytes worth of data
+	 * (which may be no data at all); advance the
+	 * offset past whatever data we've processed.
+	 */
+    }
+    return frame_length + 8;
+}
+
+static guint8 *
+spdy_decompress_header_block(tvbuff_t *tvb, z_streamp decomp,
+			     guint32 dictionary_id, int offset,
+			     guint32 length, guint *uncomp_length)
+{
+    int retcode;
+    size_t bufsize = 16384;
+    const guint8 *hptr = tvb_get_ptr(tvb, offset, length);
+    guint8 *uncomp_block = ep_alloc(bufsize);
+    decomp->next_in = (Bytef *)hptr;
+    decomp->avail_in = length;
+    decomp->next_out = uncomp_block;
+    decomp->avail_out = bufsize;
+    retcode = inflate(decomp, Z_SYNC_FLUSH);
+    if (retcode == Z_NEED_DICT) {
+	if (decomp->adler != dictionary_id) {
+	    printf("decompressor wants dictionary %#x, but we have %#x\n",
+		   (guint)decomp->adler, dictionary_id);
+	} else {
+	    retcode = inflateSetDictionary(decomp,
+					   spdy_dictionary,
+					   sizeof(spdy_dictionary));
+	    if (retcode == Z_OK)
+		retcode = inflate(decomp, Z_SYNC_FLUSH);
+	}
+    }
+
+    if (retcode != Z_OK) {
+	return NULL;
+    } else {
+	*uncomp_length = bufsize - decomp->avail_out;
+        if (spdy_debug)
+            printf("Inflation SUCCEEDED. uncompressed size=%d\n", *uncomp_length);
+	if (decomp->avail_in != 0)
+	    if (spdy_debug)
+		printf("  but there were %d input bytes left over\n", decomp->avail_in);
+    }
+    return se_memdup(uncomp_block, *uncomp_length);
+}
+
+/*
+ * Try to determine heuristically whether the header block is
+ * compressed. For an uncompressed block, the first two bytes
+ * gives the number of headers. Each header name and value is
+ * a two-byte length followed by ASCII characters.
+ */
+static gboolean
+spdy_check_header_compression(tvbuff_t *tvb,
+				       int offset,
+				       guint32 frame_length)
+{
+    guint16 length;
+    if (!tvb_bytes_exist(tvb, offset, 6))
+	return 1;
+    length = tvb_get_ntohs(tvb, offset);
+    if (length > frame_length)
+	return 1;
+    length = tvb_get_ntohs(tvb, offset+2);
+    if (length > frame_length)
+	return 1;
+    if (spdy_debug) printf("Looks like the header block is not compressed\n");
+    return 0;
+}
+
+// TODO(cbentzel): Change wireshark to export p_remove_proto_data, rather
+// than duplicating code here.
+typedef struct _spdy_frame_proto_data {
+  int proto;
+  void *proto_data;
+} spdy_frame_proto_data;
+
+static gint spdy_p_compare(gconstpointer a, gconstpointer b)
+{
+  const spdy_frame_proto_data *ap = (const spdy_frame_proto_data *)a;
+  const spdy_frame_proto_data *bp = (const spdy_frame_proto_data *)b;
+
+  if (ap -> proto > bp -> proto)
+    return 1;
+  else if (ap -> proto == bp -> proto)
+    return 0;
+  else
+    return -1;
+
+}
+
+static void spdy_p_remove_proto_data(frame_data *fd, int proto)
+{
+  spdy_frame_proto_data temp;
+  GSList *item;
+
+  temp.proto = proto;
+  temp.proto_data = NULL;
+
+  item = g_slist_find_custom(fd->pfd, (gpointer *)&temp, spdy_p_compare);
+
+  if (item) {
+    fd->pfd = g_slist_remove(fd->pfd, item->data);
+  }
+}
+
+static spdy_frame_info_t *
+spdy_save_header_block(frame_data *fd,
+	guint32 stream_id,
+	guint frame_type,
+	guint8 *header,
+	guint length)
+{
+    GSList *filist = p_get_proto_data(fd, proto_spdy);
+    spdy_frame_info_t *frame_info = se_alloc(sizeof(spdy_frame_info_t));
+    if (filist != NULL)
+      spdy_p_remove_proto_data(fd, proto_spdy);
+    frame_info->stream_id = stream_id;
+    frame_info->header_block = header;
+    frame_info->header_block_len = length;
+    frame_info->frame_type = frame_type;
+    filist = g_slist_append(filist, frame_info);
+    p_add_proto_data(fd, proto_spdy, filist);
+    return frame_info;
+    /* TODO(ers) these need to get deleted when no longer needed */
+}
+
+static spdy_frame_info_t *
+spdy_find_saved_header_block(frame_data *fd,
+			     guint32 stream_id,
+			     guint16 frame_type)
+{
+    GSList *filist = p_get_proto_data(fd, proto_spdy);
+    while (filist != NULL) {
+	spdy_frame_info_t *fi = filist->data;
+	if (fi->stream_id == stream_id && fi->frame_type == frame_type)
+	    return fi;
+	filist = g_slist_next(filist);
+    }
+    return NULL;
+}
+
+/*
+ * Given a content type string that may contain optional parameters,
+ * return the parameter string, if any, otherwise return NULL. This
+ * also has the side effect of null terminating the content type
+ * part of the original string.
+ */
+static gchar *
+spdy_parse_content_type(gchar *content_type)
+{
+    gchar *cp = content_type;
+
+    while (*cp != '\0' && *cp != ';' && !isspace(*cp)) {
+	*cp = tolower(*cp);
+	++cp;
+    }
+    if (*cp == '\0')
+	cp = NULL;
+
+    if (cp != NULL) {
+	*cp++ = '\0';
+	while (*cp == ';' || isspace(*cp))
+	    ++cp;
+	if (*cp != '\0')
+	    return cp;
+    }
+    return NULL;
+}
+
+static int
+dissect_spdy_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
+		     proto_tree *tree, spdy_conv_t *conv_data)
+{
+    guint8		control_bit;
+    guint16		version;
+    guint16		frame_type;
+    guint8		flags;
+    guint32		frame_length;
+    guint32		stream_id;
+    guint32             associated_stream_id;
+    gint		priority;
+    guint16		num_headers;
+    guint32		fin_status;
+    guint8		*frame_header;
+    const char		*proto_tag;
+    const char		*frame_type_name;
+    proto_tree		*spdy_tree = NULL;
+    proto_item		*ti = NULL;
+    proto_item		*spdy_proto = NULL;
+    int			orig_offset;
+    int			hoffset;
+    int			hdr_offset = 0;
+    spdy_frame_type_t	spdy_type;
+    proto_tree		*sub_tree;
+    proto_tree		*flags_tree;
+    tvbuff_t		*header_tvb = NULL;
+    gboolean		headers_compressed;
+    gchar		*hdr_verb = NULL;
+    gchar		*hdr_url = NULL;
+    gchar		*hdr_version = NULL;
+    gchar		*content_type = NULL;
+    gchar		*content_encoding = NULL;
+
+    /*
+     * Minimum size for a SPDY frame is 8 bytes.
+     */
+    if (tvb_reported_length_remaining(tvb, offset) < 8)
+	return -1;
+
+    proto_tag = "SPDY";
+
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+	col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_tag);
+
+    /*
+     * Is this a control frame or a data frame?
+     */
+    orig_offset = offset;
+    control_bit = tvb_get_bits8(tvb, offset << 3, 1);
+    if (control_bit) {
+	version = tvb_get_bits16(tvb, (offset << 3) + 1, 15, FALSE);
+	frame_type = tvb_get_ntohs(tvb, offset+2);
+	if (frame_type >= SPDY_INVALID) {
+	    return -1;
+	}
+	frame_header = ep_tvb_memdup(tvb, offset, 16);
+    } else {
+        version = 1;  /* avoid gcc warning */
+	frame_type = SPDY_DATA;
+        frame_header = NULL;    /* avoid gcc warning */
+    }
+    frame_type_name = frame_type_names[frame_type];
+    offset += 4;
+    flags = tvb_get_guint8(tvb, offset);
+    frame_length = tvb_get_ntoh24(tvb, offset+1);
+    offset += 4;
+    /*
+     * Make sure there's as much data as the frame header says there is.
+     */
+    if ((guint)tvb_reported_length_remaining(tvb, offset) < frame_length) {
+	if (spdy_debug)
+	    printf("Not enough header data: %d vs. %d\n",
+		    frame_length, tvb_reported_length_remaining(tvb, offset));
+	return -1;
+    }
+    if (tree) {
+	spdy_proto = proto_tree_add_item(tree, proto_spdy, tvb, orig_offset, frame_length+8, FALSE);
+	spdy_tree = proto_item_add_subtree(spdy_proto, ett_spdy);
+    }
+
+    if (control_bit) {
+	if (spdy_debug)
+	    printf("Control frame [version=%d type=%d flags=0x%x length=%d]\n",
+		    version, frame_type, flags, frame_length);
+	if (tree) proto_item_append_text(spdy_tree, ", control frame");
+    } else {
+	return dissect_spdy_data_frame(tvb, orig_offset, pinfo, tree,
+				spdy_tree, spdy_proto, conv_data);
+    }
+    num_headers = 0;
+    sub_tree = NULL;    /* avoid gcc warning */
+    switch (frame_type) {
+	case SPDY_SYN_STREAM:
+	case SPDY_SYN_REPLY:
+	    if (tree) {
+		int hf;
+		hf = frame_type == SPDY_SYN_STREAM ? hf_spdy_syn_stream : hf_spdy_syn_reply;
+		ti = proto_tree_add_bytes(spdy_tree, hf, tvb,
+					  orig_offset, 16, frame_header);
+		sub_tree = proto_item_add_subtree(ti, ett_spdy_syn_stream);
+	    }
+	    stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
+	    offset += 4;
+            if (frame_type == SPDY_SYN_STREAM) {
+                associated_stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
+                offset += 4;
+                priority = tvb_get_bits8(tvb, offset << 3, 2);
+                offset += 2;
+            } else {
+                // The next two bytes have no meaning in SYN_REPLY
+                offset += 2;
+            }
+            if (tree) {
+		proto_tree_add_boolean(sub_tree, hf_spdy_control_bit, tvb, orig_offset, 1, control_bit);
+		proto_tree_add_uint(sub_tree, hf_spdy_version, tvb, orig_offset, 2, version);
+		proto_tree_add_uint(sub_tree, hf_spdy_type, tvb, orig_offset+2, 2, frame_type);
+		ti = proto_tree_add_uint_format(sub_tree, hf_spdy_flags, tvb, orig_offset+4, 1, flags,
+						"Flags: 0x%02x%s", flags, flags&SPDY_FIN ? " (FIN)" : "");
+		flags_tree = proto_item_add_subtree(ti, ett_spdy_flags);
+		proto_tree_add_boolean(flags_tree, hf_spdy_flags_fin, tvb, orig_offset+4, 1, flags);
+		proto_tree_add_uint(sub_tree, hf_spdy_length, tvb, orig_offset+5, 3, frame_length);
+		proto_tree_add_uint(sub_tree, hf_spdy_streamid, tvb, orig_offset+8, 4, stream_id);
+                if (frame_type == SPDY_SYN_STREAM) {
+                     proto_tree_add_uint(sub_tree, hf_spdy_associated_streamid, tvb, orig_offset+12, 4, associated_stream_id);
+                     proto_tree_add_uint(sub_tree, hf_spdy_priority, tvb, orig_offset+16, 1, priority);
+                }
+		proto_item_append_text(spdy_proto, ": %s%s stream=%d length=%d",
+				       frame_type_name,
+				       flags & SPDY_FIN ? " [FIN]" : "",
+				       stream_id, frame_length);
+                if (spdy_debug)
+                    printf("  stream ID=%u priority=%d\n", stream_id, priority);
+	    }
+	    break;
+
+	case SPDY_FIN_STREAM:
+	    stream_id = tvb_get_bits32(tvb, (offset << 3) + 1, 31, FALSE);
+	    fin_status = tvb_get_ntohl(tvb, offset);
+	    // TODO(ers) fill in tree and summary
+	    offset += 8;
+	    break;
+
+	case SPDY_HELLO:
+	    // TODO(ers) fill in tree and summary
+            stream_id = 0;      /* avoid gcc warning */
+	    break;
+
+	default:
+            stream_id = 0;      /* avoid gcc warning */
+	    return -1;
+	    break;
+    }
+
+    /*
+     * Process the name-value pairs one at a time, after possibly
+     * decompressing the header block.
+     */
+    if (frame_type == SPDY_SYN_STREAM || frame_type == SPDY_SYN_REPLY) {
+	headers_compressed = spdy_check_header_compression(tvb, offset, frame_length);
+	if (!spdy_decompress_headers || !headers_compressed) {
+	    header_tvb = tvb;
+	    hdr_offset = offset;
+	} else {
+	    spdy_frame_info_t *per_frame_info =
+		    spdy_find_saved_header_block(pinfo->fd,
+						 stream_id,
+						 frame_type == SPDY_SYN_REPLY);
+	    if (per_frame_info == NULL) {
+		guint uncomp_length;
+		z_streamp decomp = frame_type == SPDY_SYN_STREAM ?
+			conv_data->rqst_decompressor : conv_data->rply_decompressor;
+		guint8 *uncomp_ptr =
+			spdy_decompress_header_block(tvb, decomp,
+						     conv_data->dictionary_id,
+						     offset,
+                                                     frame_length + 8 - (offset - orig_offset),
+                                                     &uncomp_length);
+		if (uncomp_ptr == NULL) {         /* decompression failed */
+                    if (spdy_debug)
+                        printf("Frame #%d: Inflation failed\n", pinfo->fd->num);
+		    proto_item_append_text(spdy_proto, " [Error: Header decompression failed]");
+		    // Should we just bail here?
+                } else {
+                    if (spdy_debug)
+                        printf("Saving %u bytes of uncomp hdr\n", uncomp_length);
+                    per_frame_info =
+                        spdy_save_header_block(pinfo->fd, stream_id, frame_type == SPDY_SYN_REPLY,
+                                uncomp_ptr, uncomp_length);
+                }
+	    } else if (spdy_debug) {
+		printf("Found uncompressed header block len %u for stream %u frame_type=%d\n",
+		       per_frame_info->header_block_len,
+		       per_frame_info->stream_id,
+		       per_frame_info->frame_type);
+	    }
+            if (per_frame_info != NULL) {
+                header_tvb = tvb_new_child_real_data(tvb,
+						 per_frame_info->header_block,
+						 per_frame_info->header_block_len,
+						 per_frame_info->header_block_len);
+                add_new_data_source(pinfo, header_tvb, "Uncompressed headers");
+                hdr_offset = 0;
+            }
+	}
+        offset = orig_offset + 8 + frame_length;
+	num_headers = tvb_get_ntohs(header_tvb, hdr_offset);
+	hdr_offset += 2;
+	if (header_tvb == NULL ||
+                (headers_compressed && !spdy_decompress_headers)) {
+	    num_headers = 0;
+	    ti = proto_tree_add_string(sub_tree, hf_spdy_num_headers_string,
+				  tvb, 
+				  frame_type == SPDY_SYN_STREAM ? orig_offset+18 : orig_offset + 14, 
+				  2,
+				  "Unknown (header block is compressed)");
+	} else
+	    ti = proto_tree_add_uint(sub_tree, hf_spdy_num_headers,
+				tvb, 
+				frame_type == SPDY_SYN_STREAM ? orig_offset+18 : orig_offset +14, 
+				2, num_headers);
+    }
+    spdy_type = SPDY_INVALID;		/* type not known yet */
+    if (spdy_debug)
+        printf("  %d Headers:\n", num_headers);
+    if (num_headers > frame_length) {
+	printf("Number of headers is greater than frame length!\n");
+        proto_item_append_text(ti, " [Error: Number of headers is larger than frame length]");
+	col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]", frame_type_name, stream_id);
+	return frame_length+8;
+    }
+    hdr_verb = hdr_url = hdr_version = content_type = content_encoding = NULL;
+    while (num_headers-- && tvb_reported_length_remaining(header_tvb, hdr_offset) != 0) {
+	gchar *header_name;
+	gchar *header_value;
+	proto_tree *header_tree;
+	proto_tree *name_tree;
+	proto_tree *value_tree;
+	proto_item *header;
+	gint16 length;
+	gint header_length = 0;
+
+	hoffset = hdr_offset;
+
+	header = proto_tree_add_item(spdy_tree, hf_spdy_header, header_tvb,
+				 hdr_offset, frame_length, FALSE);
+	header_tree = proto_item_add_subtree(header, ett_spdy_header);
+
+	length = tvb_get_ntohs(header_tvb, hdr_offset);
+	hdr_offset += 2;
+	header_name = (gchar *)tvb_get_ephemeral_string(header_tvb, hdr_offset, length);
+	hdr_offset += length;
+	header_length += hdr_offset - hoffset;
+	if (tree) {
+	    ti = proto_tree_add_text(header_tree, header_tvb, hoffset, length+2, "Name: %s", 
+				     header_name);
+	    name_tree = proto_item_add_subtree(ti, ett_spdy_header_name);
+	    proto_tree_add_uint(name_tree, hf_spdy_length, header_tvb, hoffset, 2, length);
+	    proto_tree_add_string_format(name_tree, hf_spdy_header_name_text, header_tvb, hoffset+2, length,
+					 header_name, "Text: %s", format_text(header_name, length));
+	}
+
+	hoffset = hdr_offset;
+	length = tvb_get_ntohs(header_tvb, hdr_offset);
+	hdr_offset += 2;
+	header_value = (gchar *)tvb_get_ephemeral_string(header_tvb, hdr_offset, length);
+	hdr_offset += length;
+	header_length += hdr_offset - hoffset;
+	if (tree) {
+	    ti = proto_tree_add_text(header_tree, header_tvb, hoffset, length+2, "Value: %s", 
+				     header_value);
+	    value_tree = proto_item_add_subtree(ti, ett_spdy_header_value);
+	    proto_tree_add_uint(value_tree, hf_spdy_length, header_tvb, hoffset, 2, length);
+	    proto_tree_add_string_format(value_tree, hf_spdy_header_value_text, header_tvb, hoffset+2, length,
+					 header_value, "Text: %s", format_text(header_value, length));
+	    proto_item_append_text(header, ": %s: %s", header_name, header_value);
+	    proto_item_set_len(header, header_length);
+	}
+	if (spdy_debug) printf("    %s: %s\n", header_name, header_value);
+	/*
+	 * TODO(ers) check that the header name contains only legal characters.
+	 */
+	if (g_ascii_strcasecmp(header_name, "method") == 0 ||
+	    g_ascii_strcasecmp(header_name, "status") == 0) {
+	    hdr_verb = header_value;
+	} else if (g_ascii_strcasecmp(header_name, "url") == 0) {
+	    hdr_url = header_value;
+	} else if (g_ascii_strcasecmp(header_name, "version") == 0) {
+	    hdr_version = header_value;
+	} else if (g_ascii_strcasecmp(header_name, "content-type") == 0) {
+	    content_type = se_strdup(header_value);
+	} else if (g_ascii_strcasecmp(header_name, "content-encoding") == 0) {
+	    content_encoding = se_strdup(header_value);
+	}
+    }
+    if (hdr_version != NULL) {
+	if (hdr_url != NULL) {
+	    col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]: %s %s %s",
+			 frame_type_name, stream_id, hdr_verb, hdr_url, hdr_version);
+	} else {
+	    col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]: %s %s",
+			 frame_type_name, stream_id, hdr_verb, hdr_version);
+	}
+    } else {
+	col_add_fstr(pinfo->cinfo, COL_INFO, "%s[%d]", frame_type_name, stream_id);
+    }
+    /*
+     * If we expect data on this stream, we need to remember the content
+     * type and content encoding.
+     */
+    if (content_type != NULL && !pinfo->fd->flags.visited) {
+        gchar *content_type_params = spdy_parse_content_type(content_type);
+	spdy_save_stream_info(conv_data, stream_id, content_type,
+                              content_type_params, content_encoding);
+    }
+
+    return offset - orig_offset;
+}
+
+static int
+dissect_spdy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+    spdy_conv_t	*conv_data;
+    int		offset = 0;
+    int		len;
+    int		firstpkt = 1;
+
+    /*
+     * The first byte of a SPDY packet must be either 0 or
+     * 0x80. If it's not, assume that this is not SPDY.
+     * (In theory, a data frame could have a stream ID
+     * >= 2^24, in which case it won't have 0 for a first
+     * byte, but this is a pretty reliable heuristic for
+     * now.)
+     */
+    guint8 first_byte = tvb_get_guint8(tvb, 0);
+    if (first_byte != 0x80 && first_byte != 0x0)
+	  return 0;
+
+    conv_data = get_spdy_conversation_data(pinfo);
+
+    while (tvb_reported_length_remaining(tvb, offset) != 0) {
+	if (!firstpkt) {
+	    col_add_fstr(pinfo->cinfo, COL_INFO, " >> ");
+	    col_set_fence(pinfo->cinfo, COL_INFO);
+	}
+	len = dissect_spdy_message(tvb, offset, pinfo, tree, conv_data);
+	if (len <= 0)
+	    return 0;
+	offset += len;
+	/*
+	 * OK, we've set the Protocol and Info columns for the
+	 * first SPDY message; set a fence so that subsequent
+	 * SPDY messages don't overwrite the Info column.
+	 */
+	col_set_fence(pinfo->cinfo, COL_INFO);
+	firstpkt = 0;
+    }
+    return 1;
+}
+
+static gboolean
+dissect_spdy_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+    if (!value_is_in_range(global_spdy_tcp_range, pinfo->destport) &&
+            !value_is_in_range(global_spdy_tcp_range, pinfo->srcport))
+        return FALSE;
+    return dissect_spdy(tvb, pinfo, tree) != 0;
+}
+
+static void reinit_spdy(void)
+{
+}
+
+// NMAKE complains about flags_set_truth not being constant. Duplicate
+// the values inside of it.
+static const true_false_string tfs_spdy_set_notset = { "Set", "Not set" };
+
+void
+proto_register_spdy(void)
+{
+    static hf_register_info hf[] = {
+	{ &hf_spdy_syn_stream,
+	    { "Syn Stream",	"spdy.syn_stream",
+		FT_BYTES, BASE_NONE, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_syn_reply,
+	    { "Syn Reply",	"spdy.syn_reply",
+		FT_BYTES, BASE_NONE, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_control_bit,
+	    { "Control bit",	"spdy.control_bit",
+		FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+		"TRUE if SPDY control frame", HFILL }},
+	{ &hf_spdy_version,
+	    { "Version",	"spdy.version",
+		FT_UINT16, BASE_DEC, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_type,
+	    { "Type",		"spdy.type",
+		FT_UINT16, BASE_DEC, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_flags,
+	    { "Flags",		"spdy.flags",
+		FT_UINT8, BASE_HEX, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_flags_fin,
+	    { "Fin",		"spdy.flags.fin",
+                FT_BOOLEAN, 8, TFS(&tfs_spdy_set_notset),
+                SPDY_FIN, "", HFILL }},
+	{ &hf_spdy_length,
+	    { "Length",		"spdy.length",
+		FT_UINT24, BASE_DEC, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_header,
+	    { "Header",		"spdy.header",
+		FT_NONE, BASE_NONE, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_header_name,
+	    { "Name",		"spdy.header.name",
+		FT_NONE, BASE_NONE, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_header_name_text,
+	    { "Text",		"spdy.header.name.text",
+		FT_STRING, BASE_NONE, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_header_value,
+	    { "Value",		"spdy.header.value",
+		FT_NONE, BASE_NONE, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_header_value_text,
+	    { "Text",		"spdy.header.value.text",
+		FT_STRING, BASE_NONE, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_streamid,
+	    { "Stream ID",	"spdy.streamid",
+		FT_UINT32, BASE_DEC, NULL, 0x0,
+		"", HFILL }},
+        { &hf_spdy_associated_streamid,
+	    { "Associated Stream ID",	"spdy.associated.streamid",
+		FT_UINT32, BASE_DEC, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_priority,
+	    { "Priority",	"spdy.priority",
+		FT_UINT8, BASE_DEC, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_num_headers,
+	    { "Number of headers", "spdy.numheaders",
+		FT_UINT16, BASE_DEC, NULL, 0x0,
+		"", HFILL }},
+	{ &hf_spdy_num_headers_string,
+	    { "Number of headers", "spdy.numheaders",
+		FT_STRING, BASE_NONE, NULL, 0x0,
+		"", HFILL }},
+    };
+    static gint *ett[] = {
+	&ett_spdy,
+	&ett_spdy_syn_stream,
+	&ett_spdy_syn_reply,
+	&ett_spdy_fin_stream,
+	&ett_spdy_flags,
+	&ett_spdy_header,
+	&ett_spdy_header_name,
+	&ett_spdy_header_value,
+	&ett_spdy_encoded_entity,
+    };
+
+    module_t *spdy_module;
+
+    proto_spdy = proto_register_protocol("SPDY", "SPDY", "spdy");
+    proto_register_field_array(proto_spdy, hf, array_length(hf));
+    proto_register_subtree_array(ett, array_length(ett));
+    new_register_dissector("spdy", dissect_spdy, proto_spdy);
+    spdy_module = prefs_register_protocol(proto_spdy, reinit_spdy);
+    prefs_register_bool_preference(spdy_module, "desegment_headers",
+				   "Reassemble SPDY control frames spanning multiple TCP segments",
+				   "Whether the SPDY dissector should reassemble control frames "
+				   "spanning multiple TCP segments. "
+				   "To use this option, you must also enable "
+				   "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
+				   &spdy_desegment_control_frames);
+    prefs_register_bool_preference(spdy_module, "desegment_body",
+				   "Reassemble SPDY bodies spanning multiple TCP segments",
+				   "Whether the SPDY dissector should reassemble "
+				   "data frames spanning multiple TCP segments. "
+				   "To use this option, you must also enable "
+				   "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
+				   &spdy_desegment_data_frames);
+    prefs_register_bool_preference(spdy_module, "assemble_data_frames",
+				   "Assemble SPDY bodies that consist of multiple DATA frames",
+				   "Whether the SPDY dissector should reassemble multiple "
+				   "data frames into an entity body.",
+				   &spdy_assemble_entity_bodies);
+#ifdef HAVE_LIBZ
+    prefs_register_bool_preference(spdy_module, "decompress_headers",
+				   "Uncompress SPDY headers",
+				   "Whether to uncompress SPDY headers.",
+				   &spdy_decompress_headers);
+    prefs_register_bool_preference(spdy_module, "decompress_body",
+				   "Uncompress entity bodies",
+				   "Whether to uncompress entity bodies that are compressed "
+				   "using \"Content-Encoding: \"",
+				   &spdy_decompress_body);
+#endif
+    prefs_register_bool_preference(spdy_module, "debug_output",
+				   "Print debug info on stdout",
+				   "Print debug info on stdout",
+				   &spdy_debug);
+#if 0
+    prefs_register_string_preference(ssl_module, "debug_file", "SPDY debug file",
+				     "Redirect SPDY debug to file name; "
+				     "leave empty to disable debugging, "
+				     "or use \"" SPDY_DEBUG_USE_STDOUT "\""
+				     " to redirect output to stdout\n",
+				     (const gchar **)&sdpy_debug_file_name);
+#endif
+    prefs_register_obsolete_preference(spdy_module, "tcp_alternate_port");
+
+    range_convert_str(&global_spdy_tcp_range, TCP_DEFAULT_RANGE, 65535);
+    spdy_tcp_range = range_empty();
+    prefs_register_range_preference(spdy_module, "tcp.port", "TCP Ports",
+				    "TCP Ports range",
+				    &global_spdy_tcp_range, 65535);
+
+    range_convert_str(&global_spdy_ssl_range, SSL_DEFAULT_RANGE, 65535);
+    spdy_ssl_range = range_empty();
+    prefs_register_range_preference(spdy_module, "ssl.port", "SSL/TLS Ports",
+				    "SSL/TLS Ports range",
+				    &global_spdy_ssl_range, 65535);
+
+    spdy_handle = new_create_dissector_handle(dissect_spdy, proto_spdy);
+    /*
+     * Register for tapping
+     */
+    spdy_tap = register_tap("spdy"); /* SPDY statistics tap */
+    spdy_eo_tap = register_tap("spdy_eo"); /* SPDY Export Object tap */
+}
+
+void
+proto_reg_handoff_spdy(void)
+{
+    data_handle = find_dissector("data");
+    media_handle = find_dissector("media");
+    heur_dissector_add("tcp", dissect_spdy_heur, proto_spdy);
+}
+
+/*
+ * Content-Type: message/http
+ */
+
+static gint proto_message_spdy = -1;
+static gint ett_message_spdy = -1;
+
+static void
+dissect_message_spdy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+	proto_tree	*subtree;
+	proto_item	*ti;
+	gint		offset = 0, next_offset;
+	gint		len;
+
+	if (check_col(pinfo->cinfo, COL_INFO))
+		col_append_str(pinfo->cinfo, COL_INFO, " (message/spdy)");
+	if (tree) {
+		ti = proto_tree_add_item(tree, proto_message_spdy,
+				tvb, 0, -1, FALSE);
+		subtree = proto_item_add_subtree(ti, ett_message_spdy);
+		while (tvb_reported_length_remaining(tvb, offset) != 0) {
+			len = tvb_find_line_end(tvb, offset,
+					tvb_ensure_length_remaining(tvb, offset),
+					&next_offset, FALSE);
+			if (len == -1)
+				break;
+			proto_tree_add_text(subtree, tvb, offset, next_offset - offset,
+					"%s", tvb_format_text(tvb, offset, len));
+			offset = next_offset;
+		}
+	}
+}
+
+void
+proto_register_message_spdy(void)
+{
+	static gint *ett[] = {
+		&ett_message_spdy,
+	};
+
+	proto_message_spdy = proto_register_protocol(
+			"Media Type: message/spdy",
+			"message/spdy",
+			"message-spdy"
+	);
+	proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_message_spdy(void)
+{
+	dissector_handle_t message_spdy_handle;
+
+	message_spdy_handle = create_dissector_handle(dissect_message_spdy,
+			proto_message_spdy);
+
+	dissector_add_string("media_type", "message/spdy", message_spdy_handle);
+
+	reinit_spdy();
+}
diff --git a/net/tools/spdyshark/packet-spdy.h b/net/tools/spdyshark/packet-spdy.h
new file mode 100644
index 0000000..02b586b
--- /dev/null
+++ b/net/tools/spdyshark/packet-spdy.h
@@ -0,0 +1,46 @@
+/* packet-spdy.h
+ *
+ * Copyright 2010, Google Inc.
+ * Eric Shienbrood <ers@google.com>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __PACKET_SPDY_H__
+#define __PACKET_SPDY_H__
+
+#include <epan/packet.h>
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+
+/*
+ * Conversation data - used for assembling multi-data-frame
+ * entities and for decompressing request & reply header blocks.
+ */
+typedef struct _spdy_conv_t {
+    z_streamp rqst_decompressor;
+    z_streamp rply_decompressor;
+    guint32   dictionary_id;
+    GArray    *streams;
+} spdy_conv_t;
+
+#endif /* __PACKET_SPDY_H__ */
diff --git a/net/tools/spdyshark/plugin.rc.in b/net/tools/spdyshark/plugin.rc.in
new file mode 100644
index 0000000..568dc07
--- /dev/null
+++ b/net/tools/spdyshark/plugin.rc.in
@@ -0,0 +1,34 @@
+#include "winver.h"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @RC_MODULE_VERSION@
+ PRODUCTVERSION @RC_VERSION@
+ FILEFLAGSMASK 0x0L
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "The Wireshark developer community, http://www.wireshark.org/\0"
+            VALUE "FileDescription", "@PACKAGE@ dissector\0"
+            VALUE "FileVersion", "@MODULE_VERSION@\0"
+            VALUE "InternalName", "@PACKAGE@ @MODULE_VERSION@\0"
+            VALUE "LegalCopyright", "Copyright © 1998 Gerald Combs <gerald@wireshark.org>, Gilbert Ramirez <gram@alumni.rice.edu> and others\0"
+            VALUE "OriginalFilename", "@PLUGIN_NAME@.dll\0"
+            VALUE "ProductName", "Wireshark\0"
+            VALUE "ProductVersion", "@VERSION@\0"
+            VALUE "Comments", "Build with @MSVC_VARIANT@\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
diff --git a/net/tools/testserver/chromiumsync.py b/net/tools/testserver/chromiumsync.py
new file mode 100755
index 0000000..a15d265
--- /dev/null
+++ b/net/tools/testserver/chromiumsync.py
@@ -0,0 +1,667 @@
+#!/usr/bin/python2.4
+# Copyright (c) 2010 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.
+
+"""An implementation of the server side of the Chromium sync protocol.
+
+The details of the protocol are described mostly by comments in the protocol
+buffer definition at chrome/browser/sync/protocol/sync.proto.
+"""
+
+import operator
+import random
+import threading
+
+import autofill_specifics_pb2
+import bookmark_specifics_pb2
+import extension_specifics_pb2
+import nigori_specifics_pb2
+import password_specifics_pb2
+import preference_specifics_pb2
+import theme_specifics_pb2
+import typed_url_specifics_pb2
+import sync_pb2
+
+# An enumeration of the various kinds of data that can be synced.
+# Over the wire, this enumeration is not used: a sync object's type is
+# inferred by which EntitySpecifics extension it has.  But in the context
+# of a program, it is useful to have an enumeration.
+ALL_TYPES = (
+    TOP_LEVEL,  # The type of the 'Google Chrome' folder.
+    AUTOFILL,
+    BOOKMARK,
+    EXTENSIONS,
+    NIGORI,
+    PASSWORD,
+    PREFERENCE,
+    # SESSION,
+    THEME,
+    TYPED_URL) = range(9)
+
+# Given a sync type from ALL_TYPES, find the extension token corresponding
+# to that datatype.  Note that TOP_LEVEL has no such token.
+SYNC_TYPE_TO_EXTENSION = {
+    AUTOFILL: autofill_specifics_pb2.autofill,
+    BOOKMARK: bookmark_specifics_pb2.bookmark,
+    EXTENSIONS: extension_specifics_pb2.extension,
+    NIGORI: nigori_specifics_pb2.nigori,
+    PASSWORD: password_specifics_pb2.password,
+    PREFERENCE: preference_specifics_pb2.preference,
+    # SESSION: session_specifics_pb2.session,     # Disabled
+    THEME: theme_specifics_pb2.theme,
+    TYPED_URL: typed_url_specifics_pb2.typed_url,
+    }
+
+# The parent ID used to indicate a top-level node.
+ROOT_ID = '0'
+
+def GetEntryType(entry):
+  """Extract the sync type from a SyncEntry.
+
+  Args:
+    entry: A SyncEntity protobuf object whose type to determine.
+  Returns:
+    A value from ALL_TYPES if the entry's type can be determined, or None
+    if the type cannot be determined.
+  """
+  if entry.server_defined_unique_tag == 'google_chrome':
+    return TOP_LEVEL
+  entry_types = GetEntryTypesFromSpecifics(entry.specifics)
+  if not entry_types:
+    return None
+  # It is presupposed that the entry has at most one specifics extension
+  # present.  If there is more than one, either there's a bug, or else
+  # the caller should use GetEntryTypes.
+  if len(entry_types) > 1:
+    raise 'GetEntryType called with multiple extensions present.'
+  return entry_types[0]
+
+def GetEntryTypesFromSpecifics(specifics):
+  """Determine the sync types indicated by an EntitySpecifics's extension(s).
+
+  If the specifics have more than one recognized extension (as commonly
+  happens with the requested_types field of GetUpdatesMessage), all types
+  will be returned.  Callers must handle the possibility of the returned
+  value having more than one item.
+
+  Args:
+    specifics: A EntitySpecifics protobuf message whose extensions to
+      enumerate.
+  Returns:
+    A list of the sync types (values from ALL_TYPES) assocated with each
+    recognized extension of the specifics message.
+  """
+  entry_types = []
+  for data_type, extension in SYNC_TYPE_TO_EXTENSION.iteritems():
+    if specifics.HasExtension(extension):
+      entry_types.append(data_type)
+  return entry_types
+
+def GetRequestedTypes(get_updates_message):
+  """Determine the sync types requested by a client GetUpdates operation."""
+  types = GetEntryTypesFromSpecifics(
+      get_updates_message.requested_types)
+  if types:
+    types.append(TOP_LEVEL)
+  return types
+
+def GetDefaultEntitySpecifics(data_type):
+  """Get an EntitySpecifics having a sync type's default extension value.
+  """
+  specifics = sync_pb2.EntitySpecifics()
+  if data_type in SYNC_TYPE_TO_EXTENSION:
+    extension_handle = SYNC_TYPE_TO_EXTENSION[data_type]
+    specifics.Extensions[extension_handle].SetInParent()
+  return specifics
+
+def DeepCopyOfProto(proto):
+  """Return a deep copy of a protocol buffer."""
+  new_proto = type(proto)()
+  new_proto.MergeFrom(proto)
+  return new_proto
+
+
+class PermanentItem(object):
+  """A specification of one server-created permanent item.
+
+  Attributes:
+    tag: A known-to-the-client value that uniquely identifies a server-created
+      permanent item.
+    name: The human-readable display name for this item.
+    parent_tag: The tag of the permanent item's parent.  If ROOT_ID, indicates
+      a top-level item.  Otherwise, this must be the tag value of some other
+      server-created permanent item.
+    sync_type: A value from ALL_TYPES, giving the datatype of this permanent
+      item.  This controls which types of client GetUpdates requests will
+      cause the permanent item to be created and returned.
+  """
+
+  def __init__(self, tag, name, parent_tag, sync_type):
+    self.tag = tag
+    self.name = name
+    self.parent_tag = parent_tag
+    self.sync_type = sync_type
+
+class SyncDataModel(object):
+  """Models the account state of one sync user.
+  """
+  _BATCH_SIZE = 100
+
+  # Specify all the permanent items that a model might need.
+  _PERMANENT_ITEM_SPECS = [
+      PermanentItem('google_chrome', name='Google Chrome',
+                    parent_tag=ROOT_ID, sync_type=TOP_LEVEL),
+      PermanentItem('google_chrome_bookmarks', name='Bookmarks',
+                    parent_tag='google_chrome', sync_type=BOOKMARK),
+      PermanentItem('bookmark_bar', name='Bookmark Bar',
+                    parent_tag='google_chrome_bookmarks', sync_type=BOOKMARK),
+      PermanentItem('other_bookmarks', name='Other Bookmarks',
+                    parent_tag='google_chrome_bookmarks', sync_type=BOOKMARK),
+      PermanentItem('google_chrome_preferences', name='Preferences',
+                    parent_tag='google_chrome', sync_type=PREFERENCE),
+      PermanentItem('google_chrome_autofill', name='Autofill',
+                    parent_tag='google_chrome', sync_type=AUTOFILL),
+      PermanentItem('google_chrome_extensions', name='Extensions',
+                    parent_tag='google_chrome', sync_type=EXTENSIONS),
+      PermanentItem('google_chrome_passwords', name='Passwords',
+                    parent_tag='google_chrome', sync_type=PASSWORD),
+      # TODO(rsimha): Disabled since the protocol does not support it yet.
+      # PermanentItem('google_chrome_sessions', name='Sessions',
+      #               parent_tag='google_chrome', SESSION),
+      PermanentItem('google_chrome_themes', name='Themes',
+                    parent_tag='google_chrome', sync_type=THEME),
+      PermanentItem('google_chrome_typed_urls', name='Typed URLs',
+                    parent_tag='google_chrome', sync_type=TYPED_URL),
+      PermanentItem('google_chrome_nigori', name='Nigori',
+                    parent_tag='google_chrome', sync_type=NIGORI),
+      ]
+
+  def __init__(self):
+    self._version = 0
+
+    # Monotonically increasing version number.  The next object change will
+    # take on this value + 1.
+    self._entries = {}
+
+    # TODO(nick): uuid.uuid1() is better, but python 2.5 only.
+    self.store_birthday = '%0.30f' % random.random()
+
+  def _SaveEntry(self, entry):
+    """Insert or update an entry in the change log, and give it a new version.
+
+    The ID fields of this entry are assumed to be valid server IDs.  This
+    entry will be updated with a new version number and sync_timestamp.
+
+    Args:
+      entry: The entry to be added or updated.
+    """
+    self._version = self._version + 1
+    entry.version = self._version
+    entry.sync_timestamp = self._version
+
+    # Preserve the originator info, which the client is not required to send
+    # when updating.
+    base_entry = self._entries.get(entry.id_string)
+    if base_entry:
+      entry.originator_cache_guid = base_entry.originator_cache_guid
+      entry.originator_client_item_id = base_entry.originator_client_item_id
+
+    self._entries[entry.id_string] = DeepCopyOfProto(entry)
+
+  def _ServerTagToId(self, tag):
+    """Determine the server ID from a server-unique tag.
+
+    The resulting value is guaranteed not to collide with the other ID
+    generation methods.
+
+    Args:
+      tag: The unique, known-to-the-client tag of a server-generated item.
+    """
+    if tag and tag != ROOT_ID:
+      return '<server tag>%s' % tag
+    else:
+      return tag
+
+  def _ClientTagToId(self, tag):
+    """Determine the server ID from a client-unique tag.
+
+    The resulting value is guaranteed not to collide with the other ID
+    generation methods.
+
+    Args:
+      tag: The unique, opaque-to-the-server tag of a client-tagged item.
+    """
+    return '<client tag>%s' % tag
+
+  def _ClientIdToId(self, client_guid, client_item_id):
+    """Compute a unique server ID from a client-local ID tag.
+
+    The resulting value is guaranteed not to collide with the other ID
+    generation methods.
+
+    Args:
+      client_guid: A globally unique ID that identifies the client which
+        created this item.
+      client_item_id: An ID that uniquely identifies this item on the client
+        which created it.
+    """
+    # Using the client ID info is not required here (we could instead generate
+    # a random ID), but it's useful for debugging.
+    return '<server ID originally>%s/%s' % (client_guid, client_item_id)
+
+  def _WritePosition(self, entry, parent_id, prev_id=None):
+    """Convert from a relative position into an absolute, numeric position.
+
+    Clients specify positions using the predecessor-based references; the
+    server stores and reports item positions using sparse integer values.
+    This method converts from the former to the latter.
+
+    Args:
+      entry: The entry for which to compute a position.  Its ID field are
+        assumed to be server IDs.  This entry will have its parent_id_string
+        and position_in_parent fields updated; its insert_after_item_id field
+        will be cleared.
+      parent_id: The ID of the entry intended as the new parent.
+      prev_id: The ID of the entry intended as the new predecessor.  If this
+        is None, or an ID of an object which is not a child of the new parent,
+        the entry will be positioned at the end (right) of the ordering.  If
+        the empty ID (''), this will be positioned at the front (left) of the
+        ordering.  Otherwise, the entry will be given a position_in_parent
+        value placing it just after (to the right of) the new predecessor.
+    """
+    PREFERRED_GAP = 2 ** 20
+    # Compute values at the beginning or end.
+    def ExtendRange(current_limit_entry, sign_multiplier):
+      if current_limit_entry.id_string == entry.id_string:
+        step = 0
+      else:
+        step = sign_multiplier * PREFERRED_GAP
+      return current_limit_entry.position_in_parent + step
+
+    siblings = [x for x in self._entries.values()
+                if x.parent_id_string == parent_id and not x.deleted]
+    siblings = sorted(siblings, key=operator.attrgetter('position_in_parent'))
+    if prev_id == entry.id_string:
+      prev_id = ''
+    if not siblings:
+      # First item in this container; start in the middle.
+      entry.position_in_parent = 0
+    elif prev_id == '':
+      # A special value in the protocol.  Insert at first position.
+      entry.position_in_parent = ExtendRange(siblings[0], -1)
+    else:
+      # Consider items along with their successors.
+      for a, b in zip(siblings, siblings[1:]):
+        if a.id_string != prev_id:
+          continue
+        elif b.id_string == entry.id_string:
+          # We're already in place; don't change anything.
+          entry.position_in_parent = b.position_in_parent
+        else:
+          # Interpolate new position between two others.
+          entry.position_in_parent = (
+              a.position_in_parent * 7 + b.position_in_parent) / 8
+        break
+      else:
+        # Insert at end. Includes the case where prev_id is None.
+        entry.position_in_parent = ExtendRange(siblings[-1], +1)
+
+    entry.parent_id_string = parent_id
+    entry.ClearField('insert_after_item_id')
+
+  def _ItemExists(self, id_string):
+    """Determine whether an item exists in the changelog."""
+    return id_string in self._entries
+
+  def _CreatePermanentItem(self, spec):
+    """Create one permanent item from its spec, if it doesn't exist.
+
+    The resulting item is added to the changelog.
+
+    Args:
+      spec: A PermanentItem object holding the properties of the item to create.
+    """
+    id_string = self._ServerTagToId(spec.tag)
+    if self._ItemExists(id_string):
+      return
+    print 'Creating permanent item: %s' % spec.name
+    entry = sync_pb2.SyncEntity()
+    entry.id_string = id_string
+    entry.non_unique_name = spec.name
+    entry.name = spec.name
+    entry.server_defined_unique_tag = spec.tag
+    entry.folder = True
+    entry.deleted = False
+    entry.specifics.CopyFrom(GetDefaultEntitySpecifics(spec.sync_type))
+    self._WritePosition(entry, self._ServerTagToId(spec.parent_tag))
+    self._SaveEntry(entry)
+
+  def _CreatePermanentItems(self, requested_types):
+    """Ensure creation of all permanent items for a given set of sync types.
+
+    Args:
+      requested_types: A list of sync data types from ALL_TYPES.
+        Permanent items of only these types will be created.
+    """
+    for spec in self._PERMANENT_ITEM_SPECS:
+      if spec.sync_type in requested_types:
+        self._CreatePermanentItem(spec)
+
+  def GetChangesFromTimestamp(self, requested_types, timestamp):
+    """Get entries which have changed since a given timestamp, oldest first.
+
+    The returned entries are limited to being _BATCH_SIZE many.  The entries
+    are returned in strict version order.
+
+    Args:
+      requested_types: A list of sync data types from ALL_TYPES.
+        Only items of these types will be retrieved; others will be filtered
+        out.
+      timestamp: A timestamp / version number.  Only items that have changed
+        more recently than this value will be retrieved; older items will
+        be filtered out.
+    Returns:
+      A tuple of (version, entries).  Version is a new timestamp value, which
+      should be used as the starting point for the next query.  Entries is the
+      batch of entries meeting the current timestamp query.
+    """
+    if timestamp == 0:
+      self._CreatePermanentItems(requested_types)
+    change_log = sorted(self._entries.values(),
+                        key=operator.attrgetter('version'))
+    new_changes = [x for x in change_log if x.version > timestamp]
+    # Pick batch_size new changes, and then filter them.  This matches
+    # the RPC behavior of the production sync server.
+    batch = new_changes[:self._BATCH_SIZE]
+    if not batch:
+      # Client is up to date.
+      return (timestamp, [])
+
+    # Restrict batch to requested types.  Tombstones are untyped
+    # and will always get included.
+    filtered = []
+    for x in batch:
+      if (GetEntryType(x) in requested_types) or x.deleted:
+        filtered.append(DeepCopyOfProto(x))
+    # The new client timestamp is the timestamp of the last item in the
+    # batch, even if that item was filtered out.
+    return (batch[-1].version, filtered)
+
+  def _CheckVersionForCommit(self, entry):
+    """Perform an optimistic concurrency check on the version number.
+
+    Clients are only allowed to commit if they report having seen the most
+    recent version of an object.
+
+    Args:
+      entry: A sync entity from the client.  It is assumed that ID fields
+        have been converted to server IDs.
+    Returns:
+      A boolean value indicating whether the client's version matches the
+      newest server version for the given entry.
+    """
+    if entry.id_string in self._entries:
+      if (self._entries[entry.id_string].version != entry.version and
+          not self._entries[entry.id_string].deleted):
+        # Version mismatch that is not a tombstone recreation.
+        return False
+    else:
+      if entry.version != 0:
+        # Edit to an item that does not exist.
+        return False
+    return True
+
+  def _CheckParentIdForCommit(self, entry):
+    """Check that the parent ID referenced in a SyncEntity actually exists.
+
+    Args:
+      entry: A sync entity from the client.  It is assumed that ID fields
+        have been converted to server IDs.
+    Returns:
+      A boolean value indicating whether the entity's parent ID is an object
+      that actually exists (and is not deleted) in the current account state.
+    """
+    if entry.parent_id_string == ROOT_ID:
+      # This is generally allowed.
+      return True
+    if entry.parent_id_string not in self._entries:
+      print 'Warning: Client sent unknown ID.  Should never happen.'
+      return False
+    if entry.parent_id_string == entry.id_string:
+      print 'Warning: Client sent circular reference.  Should never happen.'
+      return False
+    if self._entries[entry.parent_id_string].deleted:
+      # This can happen in a race condition between two clients.
+      return False
+    if not self._entries[entry.parent_id_string].folder:
+      print 'Warning: Client sent non-folder parent.  Should never happen.'
+      return False
+    return True
+
+  def _RewriteIdsAsServerIds(self, entry, cache_guid, commit_session):
+    """Convert ID fields in a client sync entry to server IDs.
+
+    A commit batch sent by a client may contain new items for which the
+    server has not generated IDs yet.  And within a commit batch, later
+    items are allowed to refer to earlier items.  This method will
+    generate server IDs for new items, as well as rewrite references
+    to items whose server IDs were generated earlier in the batch.
+
+    Args:
+      entry: The client sync entry to modify.
+      cache_guid: The globally unique ID of the client that sent this
+        commit request.
+      commit_session: A dictionary mapping the original IDs to the new server
+        IDs, for any items committed earlier in the batch.
+    """
+    if entry.version == 0:
+      if entry.HasField('client_defined_unique_tag'):
+        # When present, this should determine the item's ID.
+        new_id = self._ClientTagToId(entry.client_defined_unique_tag)
+      else:
+        new_id = self._ClientIdToId(cache_guid, entry.id_string)
+        entry.originator_cache_guid = cache_guid
+        entry.originator_client_item_id = entry.id_string
+      commit_session[entry.id_string] = new_id  # Remember the remapping.
+      entry.id_string = new_id
+    if entry.parent_id_string in commit_session:
+      entry.parent_id_string = commit_session[entry.parent_id_string]
+    if entry.insert_after_item_id in commit_session:
+      entry.insert_after_item_id = commit_session[entry.insert_after_item_id]
+
+  def CommitEntry(self, entry, cache_guid, commit_session):
+    """Attempt to commit one entry to the user's account.
+
+    Args:
+      entry: A SyncEntity protobuf representing desired object changes.
+      cache_guid: A string value uniquely identifying the client; this
+        is used for ID generation and will determine the originator_cache_guid
+        if the entry is new.
+      commit_session: A dictionary mapping client IDs to server IDs for any
+        objects committed earlier this session.  If the entry gets a new ID
+        during commit, the change will be recorded here.
+    Returns:
+      A SyncEntity reflecting the post-commit value of the entry, or None
+      if the entry was not committed due to an error.
+    """
+    entry = DeepCopyOfProto(entry)
+
+    # Generate server IDs for this entry, and write generated server IDs
+    # from earlier entries into the message's fields, as appropriate.  The
+    # ID generation state is stored in 'commit_session'.
+    self._RewriteIdsAsServerIds(entry, cache_guid, commit_session)
+
+    # Perform the optimistic concurrency check on the entry's version number.
+    # Clients are not allowed to commit unless they indicate that they've seen
+    # the most recent version of an object.
+    if not self._CheckVersionForCommit(entry):
+      return None
+
+    # Check the validity of the parent ID; it must exist at this point.
+    # TODO(nick): Implement cycle detection and resolution.
+    if not self._CheckParentIdForCommit(entry):
+      return None
+
+    # At this point, the commit is definitely going to happen.
+
+    # Deletion works by storing a limited record for an entry, called a
+    # tombstone.  A sync server must track deleted IDs forever, since it does
+    # not keep track of client knowledge (there's no deletion ACK event).
+    if entry.deleted:
+      # Only the ID, version and deletion state are preserved on a tombstone.
+      # TODO(nick): Does the production server not preserve the type?  Not
+      # doing so means that tombstones cannot be filtered based on
+      # requested_types at GetUpdates time.
+      tombstone = sync_pb2.SyncEntity()
+      tombstone.id_string = entry.id_string
+      tombstone.deleted = True
+      tombstone.name = ''
+      entry = tombstone
+    else:
+      # Comments in sync.proto detail how the representation of positional
+      # ordering works: the 'insert_after_item_id' field specifies a
+      # predecessor during Commit operations, but the 'position_in_parent'
+      # field provides an absolute ordering in GetUpdates contexts.  Here
+      # we convert from the former to the latter.  Specifically, we'll
+      # generate a numeric position placing the item just after the object
+      # identified by 'insert_after_item_id', and then clear the
+      # 'insert_after_item_id' field so that it's not sent back to the client
+      # during later GetUpdates requests.
+      if entry.HasField('insert_after_item_id'):
+        self._WritePosition(entry, entry.parent_id_string,
+                            entry.insert_after_item_id)
+      else:
+        self._WritePosition(entry, entry.parent_id_string)
+
+    # Preserve the originator info, which the client is not required to send
+    # when updating.
+    base_entry = self._entries.get(entry.id_string)
+    if base_entry and not entry.HasField("originator_cache_guid"):
+      entry.originator_cache_guid = base_entry.originator_cache_guid
+      entry.originator_client_item_id = base_entry.originator_client_item_id
+
+    # Commit the change.  This also updates the version number.
+    self._SaveEntry(entry)
+    # TODO(nick): Handle recursive deletion.
+    return entry
+
+class TestServer(object):
+  """An object to handle requests for one (and only one) Chrome Sync account.
+
+  TestServer consumes the sync command messages that are the outermost
+  layers of the protocol, performs the corresponding actions on its
+  SyncDataModel, and constructs an appropropriate response message.
+  """
+
+  def __init__(self):
+    # The implementation supports exactly one account; its state is here.
+    self.account = SyncDataModel()
+    self.account_lock = threading.Lock()
+
+  def HandleCommand(self, raw_request):
+    """Decode and handle a sync command from a raw input of bytes.
+
+    This is the main entry point for this class.  It is safe to call this
+    method from multiple threads.
+
+    Args:
+      raw_request: An iterable byte sequence to be interpreted as a sync
+        protocol command.
+    Returns:
+      A tuple (response_code, raw_response); the first value is an HTTP
+      result code, while the second value is a string of bytes which is the
+      serialized reply to the command.
+    """
+    self.account_lock.acquire()
+    try:
+      request = sync_pb2.ClientToServerMessage()
+      request.MergeFromString(raw_request)
+      contents = request.message_contents
+
+      response = sync_pb2.ClientToServerResponse()
+      response.error_code = sync_pb2.ClientToServerResponse.SUCCESS
+      response.store_birthday = self.account.store_birthday
+
+      if contents == sync_pb2.ClientToServerMessage.AUTHENTICATE:
+        print 'Authenticate'
+        # We accept any authentication token, and support only one account.
+        # TODO(nick): Mock out the GAIA authentication as well; hook up here.
+        response.authenticate.user.email = 'syncjuser@chromium'
+        response.authenticate.user.display_name = 'Sync J User'
+      elif contents == sync_pb2.ClientToServerMessage.COMMIT:
+        print 'Commit'
+        self.HandleCommit(request.commit, response.commit)
+      elif contents == sync_pb2.ClientToServerMessage.GET_UPDATES:
+        print ('GetUpdates from timestamp %d' %
+            request.get_updates.from_timestamp)
+        self.HandleGetUpdates(request.get_updates, response.get_updates)
+      return (200, response.SerializeToString())
+    finally:
+      self.account_lock.release()
+
+  def HandleCommit(self, commit_message, commit_response):
+    """Respond to a Commit request by updating the user's account state.
+
+    Commit attempts stop after the first error, returning a CONFLICT result
+    for any unattempted entries.
+
+    Args:
+      commit_message: A sync_pb.CommitMessage protobuf holding the content
+        of the client's request.
+      commit_response: A sync_pb.CommitResponse protobuf into which a reply
+        to the client request will be written.
+    """
+    commit_response.SetInParent()
+    batch_failure = False
+    session = {}  # Tracks ID renaming during the commit operation.
+    guid = commit_message.cache_guid
+    for entry in commit_message.entries:
+      server_entry = None
+      if not batch_failure:
+        # Try to commit the change to the account.
+        server_entry = self.account.CommitEntry(entry, guid, session)
+
+      # An entryresponse is returned in both success and failure cases.
+      reply = commit_response.entryresponse.add()
+      if not server_entry:
+        reply.response_type = sync_pb2.CommitResponse.CONFLICT
+        reply.error_message = 'Conflict.'
+        batch_failure = True  # One failure halts the batch.
+      else:
+        reply.response_type = sync_pb2.CommitResponse.SUCCESS
+        # These are the properties that the server is allowed to override
+        # during commit; the client wants to know their values at the end
+        # of the operation.
+        reply.id_string = server_entry.id_string
+        if not server_entry.deleted:
+          # Note: the production server doesn't actually send the
+          # parent_id_string on commit responses, so we don't either.
+          reply.position_in_parent = server_entry.position_in_parent
+          reply.version = server_entry.version
+          reply.name = server_entry.name
+          reply.non_unique_name = server_entry.non_unique_name
+        else:
+          reply.version = entry.version + 1
+
+  def HandleGetUpdates(self, update_request, update_response):
+    """Respond to a GetUpdates request by querying the user's account.
+
+    Args:
+      update_request: A sync_pb.GetUpdatesMessage protobuf holding the content
+        of the client's request.
+      update_response: A sync_pb.GetUpdatesResponse protobuf into which a reply
+        to the client request will be written.
+    """
+    update_response.SetInParent()
+    requested_types = GetRequestedTypes(update_request)
+    new_timestamp, entries = self.account.GetChangesFromTimestamp(
+        requested_types, update_request.from_timestamp)
+
+    # If the client is up to date, we are careful not to set the
+    # new_timestamp field.
+    if new_timestamp != update_request.from_timestamp:
+      update_response.new_timestamp = new_timestamp
+      for e in entries:
+        reply = update_response.entries.add()
+        reply.CopyFrom(e)
diff --git a/net/tools/testserver/chromiumsync_test.py b/net/tools/testserver/chromiumsync_test.py
new file mode 100755
index 0000000..bb73d05
--- /dev/null
+++ b/net/tools/testserver/chromiumsync_test.py
@@ -0,0 +1,317 @@
+#!/usr/bin/python2.4
+# Copyright (c) 2010 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.
+
+"""Tests exercising chromiumsync and SyncDataModel."""
+
+import unittest
+
+from google.protobuf import text_format
+
+import chromiumsync
+import sync_pb2
+
+class SyncDataModelTest(unittest.TestCase):
+  def setUp(self):
+    self.model = chromiumsync.SyncDataModel()
+
+  def AddToModel(self, proto):
+    self.model._entries[proto.id_string] = proto
+
+  def testPermanentItemSpecs(self):
+    SPECS = chromiumsync.SyncDataModel._PERMANENT_ITEM_SPECS
+    # parent_tags must be declared before use.
+    declared_specs = set(['0'])
+    for spec in SPECS:
+      self.assertTrue(spec.parent_tag in declared_specs)
+      declared_specs.add(spec.tag)
+    # Every sync datatype should have a permanent folder associated with it.
+    unique_datatypes = set([x.sync_type for x in SPECS])
+    self.assertEqual(unique_datatypes,
+                     set(chromiumsync.ALL_TYPES))
+
+  def testSaveEntry(self):
+    proto = sync_pb2.SyncEntity()
+    proto.id_string = 'abcd';
+    proto.version = 0;
+    self.assertFalse(self.model._ItemExists(proto.id_string))
+    self.model._SaveEntry(proto)
+    self.assertEqual(1, proto.version)
+    self.assertTrue(self.model._ItemExists(proto.id_string))
+    self.model._SaveEntry(proto)
+    self.assertEqual(2, proto.version)
+    proto.version = 0
+    self.assertTrue(self.model._ItemExists(proto.id_string))
+    self.assertEqual(2, self.model._entries[proto.id_string].version)
+
+  def testWritePosition(self):
+    def MakeProto(id_string, parent, position):
+      proto = sync_pb2.SyncEntity()
+      proto.id_string = id_string
+      proto.position_in_parent = position
+      proto.parent_id_string = parent
+      self.AddToModel(proto)
+
+    MakeProto('a', 'X', 1000)
+    MakeProto('b', 'X', 1800)
+    MakeProto('c', 'X', 2600)
+    MakeProto('a1', 'Z', 1007)
+    MakeProto('a2', 'Z', 1807)
+    MakeProto('a3', 'Z', 2607)
+    MakeProto('s', 'Y', 10000)
+
+    def AssertPositionResult(my_id, parent_id, prev_id, expected_position):
+      entry = sync_pb2.SyncEntity()
+      entry.id_string = my_id
+      self.model._WritePosition(entry, parent_id, prev_id)
+      self.assertEqual(expected_position, entry.position_in_parent)
+      self.assertEqual(parent_id, entry.parent_id_string)
+      self.assertFalse(entry.HasField('insert_after_item_id'))
+
+    AssertPositionResult('new', 'new_parent', '', 0)
+    AssertPositionResult('new', 'Y', '', 10000 - (2 ** 20))
+    AssertPositionResult('new', 'Y', 's', 10000 + (2 ** 20))
+    AssertPositionResult('s', 'Y', '', 10000)
+    AssertPositionResult('s', 'Y', 's', 10000)
+    AssertPositionResult('a1', 'Z', '', 1007)
+
+    AssertPositionResult('new', 'X', '', 1000 - (2 ** 20))
+    AssertPositionResult('new', 'X', 'a', 1100)
+    AssertPositionResult('new', 'X', 'b', 1900)
+    AssertPositionResult('new', 'X', 'c', 2600 + (2 ** 20))
+
+    AssertPositionResult('a1', 'X', '', 1000 - (2 ** 20))
+    AssertPositionResult('a1', 'X', 'a', 1100)
+    AssertPositionResult('a1', 'X', 'b', 1900)
+    AssertPositionResult('a1', 'X', 'c', 2600 + (2 ** 20))
+
+    AssertPositionResult('a', 'X', '', 1000)
+    AssertPositionResult('a', 'X', 'b', 1900)
+    AssertPositionResult('a', 'X', 'c', 2600 + (2 ** 20))
+
+    AssertPositionResult('b', 'X', '', 1000 - (2 ** 20))
+    AssertPositionResult('b', 'X', 'a', 1800)
+    AssertPositionResult('b', 'X', 'c', 2600 + (2 ** 20))
+
+    AssertPositionResult('c', 'X', '', 1000 - (2 ** 20))
+    AssertPositionResult('c', 'X', 'a', 1100)
+    AssertPositionResult('c', 'X', 'b', 2600)
+
+  def testCreatePermanentItems(self):
+    self.model._CreatePermanentItems(chromiumsync.ALL_TYPES)
+    self.assertEqual(len(chromiumsync.ALL_TYPES) + 2,
+                     len(self.model._entries))
+
+  def ExpectedPermanentItemCount(self, sync_type):
+    if sync_type == chromiumsync.BOOKMARK:
+      return 4
+    elif sync_type == chromiumsync.TOP_LEVEL:
+      return 1
+    else:
+      return 2
+
+  def testGetChangesFromTimestampZeroForEachType(self):
+    for sync_type in chromiumsync.ALL_TYPES:
+      self.model = chromiumsync.SyncDataModel()
+      request_types = [sync_type, chromiumsync.TOP_LEVEL]
+
+      version, changes = self.model.GetChangesFromTimestamp(request_types, 0)
+
+      expected_count = self.ExpectedPermanentItemCount(sync_type)
+      self.assertEqual(expected_count, version)
+      self.assertEqual(expected_count, len(changes))
+      self.assertEqual('google_chrome', changes[0].server_defined_unique_tag)
+      for change in changes:
+        self.assertTrue(change.HasField('server_defined_unique_tag'))
+        self.assertEqual(change.version, change.sync_timestamp)
+        self.assertTrue(change.version <= version)
+
+      # Test idempotence: another GetUpdates from ts=0 shouldn't recreate.
+      version, changes = self.model.GetChangesFromTimestamp(request_types, 0)
+      self.assertEqual(expected_count, version)
+      self.assertEqual(expected_count, len(changes))
+
+      # Doing a wider GetUpdates from timestamp zero shouldn't recreate either.
+      new_version, changes = self.model.GetChangesFromTimestamp(
+          chromiumsync.ALL_TYPES, 0)
+      self.assertEqual(len(chromiumsync.SyncDataModel._PERMANENT_ITEM_SPECS),
+          new_version)
+      self.assertEqual(new_version, len(changes))
+      version, changes = self.model.GetChangesFromTimestamp(request_types, 0)
+      self.assertEqual(new_version, version)
+      self.assertEqual(expected_count, len(changes))
+
+  def testBatchSize(self):
+    for sync_type in chromiumsync.ALL_TYPES[1:]:
+      specifics = chromiumsync.GetDefaultEntitySpecifics(sync_type)
+      self.model = chromiumsync.SyncDataModel()
+      request_types = [sync_type, chromiumsync.TOP_LEVEL]
+
+      for i in range(self.model._BATCH_SIZE*3):
+        entry = sync_pb2.SyncEntity()
+        entry.id_string = 'batch test %d' % i
+        entry.specifics.CopyFrom(specifics)
+        self.model._SaveEntry(entry)
+      version, changes = self.model.GetChangesFromTimestamp(request_types, 0)
+      self.assertEqual(self.model._BATCH_SIZE, version)
+      version, changes = self.model.GetChangesFromTimestamp(request_types,
+          version)
+      self.assertEqual(self.model._BATCH_SIZE*2, version)
+      version, changes = self.model.GetChangesFromTimestamp(request_types,
+          version)
+      self.assertEqual(self.model._BATCH_SIZE*3, version)
+      expected_dingleberry = self.ExpectedPermanentItemCount(sync_type)
+      version, changes = self.model.GetChangesFromTimestamp(request_types,
+          version)
+      self.assertEqual(self.model._BATCH_SIZE*3 + expected_dingleberry,
+          version)
+
+      # Now delete a third of the items.
+      for i in xrange(self.model._BATCH_SIZE*3 - 1, 0, -3):
+        entry = sync_pb2.SyncEntity()
+        entry.id_string = 'batch test %d' % i
+        entry.deleted = True
+        self.model._SaveEntry(entry)
+
+      # The batch counts shouldn't change.
+      version, changes = self.model.GetChangesFromTimestamp(request_types, 0)
+      self.assertEqual(self.model._BATCH_SIZE, len(changes))
+      version, changes = self.model.GetChangesFromTimestamp(request_types,
+          version)
+      self.assertEqual(self.model._BATCH_SIZE, len(changes))
+      version, changes = self.model.GetChangesFromTimestamp(request_types,
+          version)
+      self.assertEqual(self.model._BATCH_SIZE, len(changes))
+      expected_dingleberry = self.ExpectedPermanentItemCount(sync_type)
+      version, changes = self.model.GetChangesFromTimestamp(request_types,
+          version)
+      self.assertEqual(expected_dingleberry, len(changes))
+      self.assertEqual(self.model._BATCH_SIZE*4 + expected_dingleberry, version)
+
+  def testCommitEachDataType(self):
+    for sync_type in chromiumsync.ALL_TYPES[1:]:
+      specifics = chromiumsync.GetDefaultEntitySpecifics(sync_type)
+      self.model = chromiumsync.SyncDataModel()
+      my_cache_guid = '112358132134'
+      parent = 'foobar'
+      commit_session = {}
+
+      # Start with a GetUpdates from timestamp 0, to populate permanent items.
+      original_version, original_changes = (
+          self.model.GetChangesFromTimestamp([sync_type], 0))
+
+      def DoCommit(original=None, id='', name=None, parent=None, prev=None):
+        proto = sync_pb2.SyncEntity()
+        if original is not None:
+          proto.version = original.version
+          proto.id_string = original.id_string
+          proto.parent_id_string = original.parent_id_string
+          proto.name = original.name
+        else:
+          proto.id_string = id
+          proto.version = 0
+        proto.specifics.CopyFrom(specifics)
+        if name is not None:
+          proto.name = name
+        if parent:
+          proto.parent_id_string = parent.id_string
+        if prev:
+          proto.insert_after_item_id = prev.id_string
+        else:
+          proto.insert_after_item_id = ''
+        proto.folder = True
+        proto.deleted = False
+        result = self.model.CommitEntry(proto, my_cache_guid, commit_session)
+        self.assertTrue(result)
+        return (proto, result)
+
+      # Commit a new item.
+      proto1, result1 = DoCommit(name='namae', id='Foo',
+                                 parent=original_changes[-1])
+      # Commit an item whose parent is another item (referenced via the
+      # pre-commit ID).
+      proto2, result2 = DoCommit(name='Secondo', id='Bar',
+                                 parent=proto1)
+        # Commit a sibling of the second item.
+      proto3, result3 = DoCommit(name='Third!', id='Baz',
+                                 parent=proto1, prev=proto2)
+
+      self.assertEqual(3, len(commit_session))
+      for p, r in [(proto1, result1), (proto2, result2), (proto3, result3)]:
+        self.assertNotEqual(r.id_string, p.id_string)
+        self.assertEqual(r.originator_client_item_id, p.id_string)
+        self.assertEqual(r.originator_cache_guid, my_cache_guid)
+        self.assertTrue(r is not self.model._entries[r.id_string],
+            "Commit result didn't make a defensive copy.")
+        self.assertTrue(p is not self.model._entries[r.id_string],
+            "Commit result didn't make a defensive copy.")
+        self.assertEqual(commit_session.get(p.id_string), r.id_string)
+        self.assertTrue(r.version > original_version)
+      self.assertEqual(result1.parent_id_string, proto1.parent_id_string)
+      self.assertEqual(result2.parent_id_string, result1.id_string)
+      version, changes = self.model.GetChangesFromTimestamp([sync_type],
+          original_version)
+      self.assertEqual(3, len(changes))
+      self.assertEqual(original_version + 3, version)
+      self.assertEqual([result1, result2, result3], changes)
+      for c in changes:
+        self.assertTrue(c is not self.model._entries[c.id_string],
+            "GetChanges didn't make a defensive copy.")
+      self.assertTrue(result2.position_in_parent < result3.position_in_parent)
+      self.assertEqual(0, result2.position_in_parent)
+
+      # Now update the items so that the second item is the parent of the
+      # first; with the first sandwiched between two new items (4 and 5).
+      # Do this in a new commit session, meaning we'll reference items from
+      # the first batch by their post-commit, server IDs.
+      commit_session = {}
+      old_cache_guid = my_cache_guid
+      my_cache_guid = 'A different GUID'
+      proto2b, result2b = DoCommit(original=result2,
+                                   parent=original_changes[-1])
+      proto4, result4 = DoCommit(id='ID4', name='Four',
+                                 parent=result2, prev=None)
+      proto1b, result1b = DoCommit(original=result1,
+                                   parent=result2, prev=proto4)
+      proto5, result5 = DoCommit(id='ID5', name='Five', parent=result2,
+                                 prev=result1)
+
+      self.assertEqual(2, len(commit_session),
+          'Only new items in second batch should be in the session')
+      for p, r, original in [(proto2b, result2b, proto2),
+                             (proto4, result4, proto4),
+                             (proto1b, result1b, proto1),
+                             (proto5, result5, proto5)]:
+        self.assertEqual(r.originator_client_item_id, original.id_string)
+        if original is not p:
+          self.assertEqual(r.id_string, p.id_string,
+              'Ids should be stable after first commit')
+          self.assertEqual(r.originator_cache_guid, old_cache_guid)
+        else:
+          self.assertNotEqual(r.id_string, p.id_string)
+          self.assertEqual(r.originator_cache_guid, my_cache_guid)
+          self.assertEqual(commit_session.get(p.id_string), r.id_string)
+        self.assertTrue(r is not self.model._entries[r.id_string],
+            "Commit result didn't make a defensive copy.")
+        self.assertTrue(p is not self.model._entries[r.id_string],
+            "Commit didn't make a defensive copy.")
+        self.assertTrue(r.version > p.version)
+      version, changes = self.model.GetChangesFromTimestamp([sync_type],
+          original_version)
+      self.assertEqual(5, len(changes))
+      self.assertEqual(original_version + 7, version)
+      self.assertEqual([result3, result2b, result4, result1b, result5], changes)
+      for c in changes:
+        self.assertTrue(c is not self.model._entries[c.id_string],
+            "GetChanges didn't make a defensive copy.")
+      self.assertTrue(result4.parent_id_string ==
+                      result1b.parent_id_string ==
+                      result5.parent_id_string ==
+                      result2b.id_string)
+      self.assertTrue(result4.position_in_parent <
+                      result1b.position_in_parent <
+                      result5.position_in_parent)
+
+if __name__ == '__main__':
+  unittest.main()
\ No newline at end of file
diff --git a/net/tools/testserver/run_testserver.cc b/net/tools/testserver/run_testserver.cc
new file mode 100644
index 0000000..716e320
--- /dev/null
+++ b/net/tools/testserver/run_testserver.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2010 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 <stdio.h>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "net/url_request/url_request_unittest.h"
+
+static void PrintUsage() {
+  printf("run_testserver --doc-root=relpath [--http|--https|--ftp]\n");
+  printf("(NOTE: relpath should be relative to the 'src' directory)\n");
+}
+
+int main(int argc, const char* argv[]) {
+  base::AtExitManager at_exit_manager;
+  MessageLoopForIO message_loop;
+
+  // Process command line
+  CommandLine::Init(argc, argv);
+  CommandLine* command_line = CommandLine::ForCurrentProcess();
+
+  if (command_line->GetSwitchCount() == 0 ||
+      command_line->HasSwitch("help")) {
+    PrintUsage();
+    return -1;
+  }
+
+  std::string protocol;
+  int port;
+  if (command_line->HasSwitch("https")) {
+    protocol = "https";
+    port = net::TestServerLauncher::kOKHTTPSPort;
+  } else if (command_line->HasSwitch("ftp")) {
+    protocol = "ftp";
+    port = kFTPDefaultPort;
+  } else {
+    protocol = "http";
+    port = kHTTPDefaultPort;
+  }
+  std::wstring doc_root = command_line->GetSwitchValue("doc-root");
+  if (doc_root.empty()) {
+    printf("Error: --doc-root must be specified\n");
+    PrintUsage();
+    return -1;
+  }
+
+  // Launch testserver
+  scoped_refptr<BaseTestServer> test_server;
+  if (protocol == "https") {
+    test_server = HTTPSTestServer::CreateGoodServer(doc_root);
+  } else if (protocol == "ftp") {
+    test_server = FTPTestServer::CreateServer(doc_root);
+  } else if (protocol == "http") {
+    test_server = HTTPTestServer::CreateServer(doc_root, NULL);
+  } else {
+    NOTREACHED();
+  }
+
+  printf("testserver running at %s://%s:%d (type ctrl+c to exit)\n",
+         protocol.c_str(),
+         net::TestServerLauncher::kHostName,
+         port);
+
+  message_loop.Run();
+  return 0;
+}
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
index a14a3ac..8e3df5e 100644
--- a/net/tools/testserver/testserver.py
+++ b/net/tools/testserver/testserver.py
@@ -1,5 +1,5 @@
 #!/usr/bin/python2.4
-# Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+# Copyright (c) 2006-2010 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.
 
@@ -22,9 +22,13 @@
 import SocketServer
 import sys
 import time
+import urllib2
+
+import pyftpdlib.ftpserver
 import tlslite
 import tlslite.api
-import pyftpdlib.ftpserver
+
+import chromiumsync
 
 try:
   import hashlib
@@ -47,7 +51,7 @@
 
   def serve_forever(self):
     self.stop = False
-    self.nonce = None
+    self.nonce_time = None
     while not self.stop:
       self.handle_request()
     self.socket.close()
@@ -125,11 +129,14 @@
       self.ContentTypeHandler,
       self.ServerRedirectHandler,
       self.ClientRedirectHandler,
+      self.ChromiumSyncTimeHandler,
+      self.MultipartHandler,
       self.DefaultResponseHandler]
     self._post_handlers = [
       self.WriteFile,
       self.EchoTitleHandler,
       self.EchoAllHandler,
+      self.ChromiumSyncCommandHandler,
       self.EchoHandler] + self._get_handlers
     self._put_handlers = [
       self.WriteFile,
@@ -148,6 +155,8 @@
     BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request,
                                                    client_address,
                                                    socket_server)
+  # Class variable; shared across requests.
+  _sync_handler = chromiumsync.TestServer()
 
   def _ShouldHandleRequest(self, handler_name):
     """Determines if the path can be handled by the handler.
@@ -813,27 +822,34 @@
 
     return True
 
-  def AuthDigestHandler(self):
-    """This handler tests 'Digest' authentication.  It just sends a page with
-    title 'user/pass' if you succeed."""
+  def GetNonce(self, force_reset=False):
+   """Returns a nonce that's stable per request path for the server's lifetime.
 
+   This is a fake implementation. A real implementation would only use a given
+   nonce a single time (hence the name n-once). However, for the purposes of
+   unittesting, we don't care about the security of the nonce.
+
+   Args:
+     force_reset: Iff set, the nonce will be changed. Useful for testing the
+         "stale" response.
+   """
+   if force_reset or not self.server.nonce_time:
+     self.server.nonce_time = time.time()
+   return _new_md5('privatekey%s%d' %
+                   (self.path, self.server.nonce_time)).hexdigest()
+
+  def AuthDigestHandler(self):
+    """This handler tests 'Digest' authentication.
+
+    It just sends a page with title 'user/pass' if you succeed.
+
+    A stale response is sent iff "stale" is present in the request path.
+    """
     if not self._ShouldHandleRequest("/auth-digest"):
       return False
 
-    # Periodically generate a new nonce.  Technically we should incorporate
-    # the request URL into this, but we don't care for testing.
-    nonce_life = 10
-    stale = False
-    if (not self.server.nonce or
-        (time.time() - self.server.nonce_time > nonce_life)):
-      if self.server.nonce:
-        stale = True
-      self.server.nonce_time = time.time()
-      self.server.nonce = \
-          _new_md5(time.ctime(self.server.nonce_time) +
-                   'privatekey').hexdigest()
-
-    nonce = self.server.nonce
+    stale = 'stale' in self.path
+    nonce = self.GetNonce(force_reset=stale)
     opaque = _new_md5('opaque').hexdigest()
     password = 'secret'
     realm = 'testrealm'
@@ -988,6 +1004,61 @@
 
     return True
 
+  def ChromiumSyncTimeHandler(self):
+    """Handle Chromium sync .../time requests.
+
+    The syncer sometimes checks server reachability by examining /time.
+    """
+    test_name = "/chromiumsync/time"
+    if not self._ShouldHandleRequest(test_name):
+      return False
+
+    self.send_response(200)
+    self.send_header('Content-type', 'text/html')
+    self.end_headers()
+    return True
+
+  def ChromiumSyncCommandHandler(self):
+    """Handle a chromiumsync command arriving via http.
+
+    This covers all sync protocol commands: authentication, getupdates, and
+    commit.
+    """
+    test_name = "/chromiumsync/command"
+    if not self._ShouldHandleRequest(test_name):
+      return False
+
+    length = int(self.headers.getheader('content-length'))
+    raw_request = self.rfile.read(length)
+
+    http_response, raw_reply = self._sync_handler.HandleCommand(raw_request)
+    self.send_response(http_response)
+    self.end_headers()
+    self.wfile.write(raw_reply)
+    return True
+
+  def MultipartHandler(self):
+    """Send a multipart response (10 text/html pages)."""
+    test_name = "/multipart"
+    if not self._ShouldHandleRequest(test_name):
+      return False
+
+    num_frames = 10
+    bound = '12345'
+    self.send_response(200)
+    self.send_header('Content-type',
+                     'multipart/x-mixed-replace;boundary=' + bound)
+    self.end_headers()
+
+    for i in xrange(num_frames):
+      self.wfile.write('--' + bound + '\r\n')
+      self.wfile.write('Content-type: text/html\r\n\r\n')
+      self.wfile.write('<title>page ' + str(i) + '</title>')
+      self.wfile.write('page ' + str(i))
+
+    self.wfile.write('--' + bound + '--')
+    return True
+
   def DefaultResponseHandler(self):
     """This is the catch-all response handler for requests that aren't handled
     by one of the special handlers above.
@@ -1095,13 +1166,24 @@
     # Create the default path to our data dir, relative to the exe dir.
     my_data_dir = os.path.dirname(sys.argv[0])
     my_data_dir = os.path.join(my_data_dir, "..", "..", "..", "..",
-                                   "test", "data")
+                               "test", "data")
 
     #TODO(ibrar): Must use Find* funtion defined in google\tools
     #i.e my_data_dir = FindUpward(my_data_dir, "test", "data")
 
   return my_data_dir
 
+def TryKillingOldServer(port):
+  # Note that an HTTP /kill request to the FTP server has the effect of
+  # killing it.
+  for protocol in ["http", "https"]:
+    try:
+      urllib2.urlopen("%s://localhost:%d/kill" % (protocol, port)).read()
+      print "Killed old server instance on port %d (via %s)" % (port, protocol)
+    except urllib2.URLError:
+      # Common case, indicates no server running.
+      pass
+
 def main(options, args):
   # redirect output to a log file so it doesn't spam the unit test output
   logfile = open('testserver.log', 'w')
@@ -1109,6 +1191,9 @@
 
   port = options.port
 
+  # Try to free up the port if there's an orphaned old instance.
+  TryKillingOldServer(port)
+
   if options.server_type == SERVER_HTTP:
     if options.cert:
       # let's make sure the cert file exists.
diff --git a/net/tools/tld_cleanup/README b/net/tools/tld_cleanup/README
index 8eab5ee..f696843 100644
--- a/net/tools/tld_cleanup/README
+++ b/net/tools/tld_cleanup/README
@@ -1,3 +1,12 @@
-When updating src/net/base/effective_tld_names.dat, re-run tld_cleanup which
-will re-generate src/net/base/effective_tld_names.cc.  Check in the updated
-effective_tld_names.dat and effective_tld_names.cc together.
\ No newline at end of file
+When updating src/net/base/effective_tld_names.dat:
+
+1. Build tld_cleanup.exe (the "(net)" > "tld_cleanup" project)
+2. Run it (no arguments needed), typically from src/chrome/Release or
+   src/chrome/Debug. It will re-generate
+   src/net/base/effective_tld_names.gperf.
+3. Run gperf on the new effective_tld_names.gperf:
+     gperf -a -L "C++" -C -c -o -t -k '*' -NFindDomain -D -m 5 \
+        effective_tld_names.gperf > effective_tld_names.cc
+   It will produce a new effective_tld_names.cc.
+4. Check in the updated effective_tld_names.dat, effective_tld_names.gperf,
+   and effective_tld_names.cc together.
diff --git a/net/tools/tld_cleanup/tld_cleanup.cc b/net/tools/tld_cleanup/tld_cleanup.cc
index 8a427ca..e98b95d 100644
--- a/net/tools/tld_cleanup/tld_cleanup.cc
+++ b/net/tools/tld_cleanup/tld_cleanup.cc
@@ -8,9 +8,8 @@
 // generate a perfect hash map.  The benefit of this approach is that no time is
 // spent on program initialization to generate the map of this data.
 //
-// After running this program, "effective_tld_names.gperf" is generated.  Run
-// gperf using the following command line:
-//   gperf -a -L "C++" -C -c -o -t -k '*' -NFindDomain -D -m 5 effective_tld_names.gperf > effective_tld_names.c
+// Running this program finds "effective_tld_names.cc" in the expected location
+// in the source checkout and generates "effective_tld_names.gperf" next to it.
 //
 // Any errors or warnings from this program are recorded in tld_cleanup.log.
 //
diff --git a/net/url_request/https_prober.cc b/net/url_request/https_prober.cc
new file mode 100644
index 0000000..a7163ba
--- /dev/null
+++ b/net/url_request/https_prober.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2009 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/url_request/https_prober.h"
+
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+
+namespace net {
+
+bool HTTPSProber::HaveProbed(const std::string& host) const {
+  return probed_.find(host) != probed_.end();
+}
+
+bool HTTPSProber::InFlight(const std::string& host) const {
+  return inflight_probes_.find(host) != inflight_probes_.end();
+}
+
+bool HTTPSProber::ProbeHost(const std::string& host, URLRequestContext* ctx,
+                            HTTPSProberDelegate* delegate) {
+  if (HaveProbed(host) || InFlight(host)) {
+    return false;
+  }
+
+  inflight_probes_[host] = delegate;
+
+  GURL url("https://" + host);
+  DCHECK_EQ(url.host(), host);
+
+  URLRequest* req = new URLRequest(url, this);
+  req->set_context(ctx);
+  req->Start();
+  return true;
+}
+
+void HTTPSProber::Success(URLRequest* request) {
+  DoCallback(request, true);
+}
+
+void HTTPSProber::Failure(URLRequest* request) {
+  DoCallback(request, false);
+}
+
+void HTTPSProber::DoCallback(URLRequest* request, bool result) {
+  std::map<std::string, HTTPSProberDelegate*>::iterator i =
+    inflight_probes_.find(request->original_url().host());
+  DCHECK(i != inflight_probes_.end());
+
+  HTTPSProberDelegate* delegate = i->second;
+  inflight_probes_.erase(i);
+  probed_.insert(request->original_url().host());
+  delete request;
+  delegate->ProbeComplete(result);
+}
+
+void HTTPSProber::OnAuthRequired(URLRequest* request,
+                                 net::AuthChallengeInfo* auth_info) {
+  Success(request);
+}
+
+void HTTPSProber::OnSSLCertificateError(URLRequest* request,
+                                        int cert_error,
+                                        net::X509Certificate* cert) {
+  request->ContinueDespiteLastError();
+}
+
+void HTTPSProber::OnResponseStarted(URLRequest* request) {
+  if (request->status().status() == URLRequestStatus::SUCCESS) {
+    Success(request);
+  } else {
+    Failure(request);
+  }
+}
+
+void HTTPSProber::OnReadCompleted(URLRequest* request, int bytes_read) {
+  NOTREACHED();
+}
+
+}  // namespace net
diff --git a/net/url_request/https_prober.h b/net/url_request/https_prober.h
new file mode 100644
index 0000000..c1c9941
--- /dev/null
+++ b/net/url_request/https_prober.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2010 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_BASE_HTTPS_PROBER_H_
+#define NET_BASE_HTTPS_PROBER_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/singleton.h"
+#include "base/task.h"
+#include "net/url_request/url_request.h"
+
+class URLRequestContext;
+
+namespace net {
+
+// This should be scoped inside HTTPSProber, but VC cannot compile
+// HTTPProber::Delegate when HTTPSProber also inherits from
+// URLRequest::Delegate.
+class HTTPSProberDelegate {
+ public:
+  virtual void ProbeComplete(bool result) = 0;
+ protected:
+  virtual ~HTTPSProberDelegate() {}
+};
+
+// HTTPSProber is a singleton object that manages HTTPS probes. A HTTPS probe
+// determines if we can connect to a given host over HTTPS. It's used when
+// transparently upgrading from HTTP to HTTPS (for example, for SPDY).
+class HTTPSProber : public URLRequest::Delegate {
+ public:
+  HTTPSProber() {}
+
+  // HaveProbed returns true if the given host is known to have been probed
+  // since the browser was last started.
+  bool HaveProbed(const std::string& host) const;
+
+  // InFlight returns true iff a probe for the given host is currently active.
+  bool InFlight(const std::string& host) const;
+
+  // ProbeHost starts a new probe for the given host. If the host is known to
+  // have been probed since the browser was started, false is returned and no
+  // other action is taken. If a probe to the given host in currently inflight,
+  // false will be returned, and no other action is taken. Otherwise, a new
+  // probe is started, true is returned and the Delegate will be called with the
+  // results (true means a successful handshake).
+  bool ProbeHost(const std::string& host, URLRequestContext* ctx,
+                 HTTPSProberDelegate* delegate);
+
+  // Implementation of URLRequest::Delegate
+  void OnAuthRequired(URLRequest* request,
+                      net::AuthChallengeInfo* auth_info);
+  void OnSSLCertificateError(URLRequest* request,
+                             int cert_error,
+                             net::X509Certificate* cert);
+  void OnResponseStarted(URLRequest* request);
+  void OnReadCompleted(URLRequest* request, int bytes_read);
+
+ private:
+  void Success(URLRequest* request);
+  void Failure(URLRequest* request);
+  void DoCallback(URLRequest* request, bool result);
+
+  std::map<std::string, HTTPSProberDelegate*> inflight_probes_;
+  std::set<std::string> probed_;
+
+  friend struct DefaultSingletonTraits<HTTPSProber>;
+  DISALLOW_COPY_AND_ASSIGN(HTTPSProber);
+};
+
+}  // namespace net
+#endif
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 7a09123..9f226c2 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 20010 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.
 
@@ -9,8 +9,8 @@
 #include "base/singleton.h"
 #include "base/stats_counters.h"
 #include "net/base/load_flags.h"
-#include "net/base/load_log.h"
 #include "net/base/net_errors.h"
+#include "net/base/net_log.h"
 #include "net/base/ssl_cert_request_info.h"
 #include "net/base/upload_data.h"
 #include "net/http/http_response_headers.h"
@@ -18,19 +18,33 @@
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_job.h"
 #include "net/url_request/url_request_job_manager.h"
+#include "net/url_request/url_request_netlog_params.h"
 
 using base::Time;
 using net::UploadData;
 using std::string;
 using std::wstring;
 
-// Max number of http redirects to follow.  Same number as gecko.
-static const int kMaxRedirects = 20;
+namespace {
 
-static URLRequestJobManager* GetJobManager() {
+// Max number of http redirects to follow.  Same number as gecko.
+const int kMaxRedirects = 20;
+
+URLRequestJobManager* GetJobManager() {
   return Singleton<URLRequestJobManager>::get();
 }
 
+// Discard headers which have meaning in POST (Content-Length, Content-Type,
+// Origin).
+void StripPostSpecificHeaders(net::HttpRequestHeaders* headers) {
+  // These are headers that may be attached to a POST.
+  headers->RemoveHeader(net::HttpRequestHeaders::kContentLength);
+  headers->RemoveHeader(net::HttpRequestHeaders::kContentType);
+  headers->RemoveHeader(net::HttpRequestHeaders::kOrigin);
+}
+
+}  // namespace
+
 ///////////////////////////////////////////////////////////////////////////////
 // URLRequest
 
@@ -44,8 +58,7 @@
       enable_profiling_(false),
       redirect_limit_(kMaxRedirects),
       final_upload_progress_(0),
-      priority_(net::LOWEST),
-      ALLOW_THIS_IN_INITIALIZER_LIST(request_tracker_node_(this)) {
+      priority_(net::LOWEST) {
   SIMPLE_STATS_COUNTER("URLRequestCount");
 
   // Sanity check out environment.
@@ -87,12 +100,16 @@
   upload_->AppendBytes(bytes, bytes_len);
 }
 
-void URLRequest::AppendFileRangeToUpload(const FilePath& file_path,
-                                         uint64 offset, uint64 length) {
+void URLRequest::AppendFileRangeToUpload(
+    const FilePath& file_path,
+    uint64 offset,
+    uint64 length,
+    const base::Time& expected_modification_time) {
   DCHECK(file_path.value().length() > 0 && length > 0);
   if (!upload_)
     upload_ = new UploadData();
-  upload_->AppendFileRange(file_path, offset, length);
+  upload_->AppendFileRange(file_path, offset, length,
+                           expected_modification_time);
 }
 
 void URLRequest::set_upload(net::UploadData* upload) {
@@ -121,17 +138,10 @@
   NOTREACHED() << "implement me!";
 }
 
-void URLRequest::SetExtraRequestHeaders(const string& headers) {
+void URLRequest::SetExtraRequestHeaders(
+    const net::HttpRequestHeaders& headers) {
   DCHECK(!is_pending_);
-  if (headers.empty()) {
-    extra_request_headers_.clear();
-  } else {
-#ifndef NDEBUG
-    size_t crlf = headers.rfind("\r\n", headers.size() - 1);
-    DCHECK(crlf != headers.size() - 2) << "headers must not end with CRLF";
-#endif
-    extra_request_headers_ = headers + "\r\n";
-  }
+  extra_request_headers_ = headers;
 
   // NOTE: This method will likely become non-trivial once the other setters
   // for request headers are implemented.
@@ -256,7 +266,10 @@
   DCHECK(!is_pending_);
   DCHECK(!job_);
 
-  net::LoadLog::BeginEvent(load_log_, net::LoadLog::TYPE_URL_REQUEST_START);
+  net_log_.BeginEvent(
+      net::NetLog::TYPE_URL_REQUEST_START_JOB,
+      new URLRequestStartEventParameters(
+          url_, method_, load_flags_, priority_));
 
   job_ = job;
   job_->SetExtraRequestHeaders(extra_request_headers_);
@@ -327,7 +340,7 @@
   // about being called recursively.
 }
 
-bool URLRequest::Read(net::IOBuffer* dest, int dest_size, int *bytes_read) {
+bool URLRequest::Read(net::IOBuffer* dest, int dest_size, int* bytes_read) {
   DCHECK(job_);
   DCHECK(bytes_read);
   DCHECK(!job_->is_done());
@@ -347,6 +360,11 @@
   return job_->Read(dest, dest_size, bytes_read);
 }
 
+void URLRequest::StopCaching() {
+  DCHECK(job_);
+  job_->StopCaching();
+}
+
 void URLRequest::ReceivedRedirect(const GURL& location, bool* defer_redirect) {
   URLRequestJob* job = GetJobManager()->MaybeInterceptRedirect(this, location);
   if (job) {
@@ -357,10 +375,10 @@
 }
 
 void URLRequest::ResponseStarted() {
+  scoped_refptr<net::NetLog::EventParameters> params;
   if (!status_.is_success())
-    net::LoadLog::AddErrorCode(load_log_, status_.os_error());
-
-  net::LoadLog::EndEvent(load_log_, net::LoadLog::TYPE_URL_REQUEST_START);
+    params = new net::NetLogIntegerParameter("net_error", status_.os_error());
+  net_log_.EndEvent(net::NetLog::TYPE_URL_REQUEST_START_JOB, params);
 
   URLRequestJob* job = GetJobManager()->MaybeInterceptResponse(this);
   if (job) {
@@ -371,8 +389,8 @@
 }
 
 void URLRequest::FollowDeferredRedirect() {
-  DCHECK(job_);
-  DCHECK(status_.is_success());
+  CHECK(job_);
+  CHECK(status_.is_success());
 
   job_->FollowDeferredRedirect();
 }
@@ -406,6 +424,10 @@
 void URLRequest::PrepareToRestart() {
   DCHECK(job_);
 
+  // Close the current URL_REQUEST_START_JOB, since we will be starting a new
+  // one.
+  net_log_.EndEvent(net::NetLog::TYPE_URL_REQUEST_START_JOB, NULL);
+
   job_->Kill();
   OrphanJob();
 
@@ -420,22 +442,12 @@
   job_ = NULL;
 }
 
-// static
-std::string URLRequest::StripPostSpecificHeaders(const std::string& headers) {
-  // These are headers that may be attached to a POST.
-  static const char* const kPostHeaders[] = {
-      "content-type",
-      "content-length",
-      "origin"
-  };
-  return net::HttpUtil::StripHeaders(
-      headers, kPostHeaders, arraysize(kPostHeaders));
-}
-
 int URLRequest::Redirect(const GURL& location, int http_status_code) {
-  if (net::LoadLog::IsUnbounded(load_log_)) {
-    net::LoadLog::AddString(load_log_, StringPrintf("Redirected (%d) to %s",
-        http_status_code, location.spec().c_str()));
+  if (net_log_.HasListener()) {
+    net_log_.AddEvent(
+        net::NetLog::TYPE_URL_REQUEST_REDIRECTED,
+        new net::NetLogStringParameter(
+            "location", location.possibly_invalid_spec()));
   }
   if (redirect_limit_ <= 0) {
     DLOG(INFO) << "disallowing redirect: exceeds limit";
@@ -476,10 +488,7 @@
     // the inclusion of a multipart Content-Type header in GET can cause
     // problems with some servers:
     // http://code.google.com/p/chromium/issues/detail?id=843
-    //
-    // TODO(eroman): It would be better if this data was structured into
-    // specific fields/flags, rather than a stew of extra headers.
-    extra_request_headers_ = StripPostSpecificHeaders(extra_request_headers_);
+    StripPostSpecificHeaders(&extra_request_headers_);
   }
 
   if (!final_upload_progress_)
@@ -499,18 +508,15 @@
 
   context_ = context;
 
-  // If the context this request belongs to has changed, update the tracker(s).
+  // If the context this request belongs to has changed, update the tracker.
   if (prev_context != context) {
-    if (prev_context)
-      prev_context->url_request_tracker()->Remove(this);
-    if (context) {
-      if (!load_log_) {
-        // Create the LoadLog -- we waited until now to create it so we know
-        // what constraints the URLRequestContext is enforcing on log levels.
-        load_log_ = context->url_request_tracker()->CreateLoadLog();
-      }
+    net_log_.EndEvent(net::NetLog::TYPE_REQUEST_ALIVE, NULL);
+    net_log_ = net::BoundNetLog();
 
-      context->url_request_tracker()->Add(this);
+    if (context) {
+      net_log_ = net::BoundNetLog::Make(context->net_log(),
+                                        net::NetLog::SOURCE_URL_REQUEST);
+      net_log_.BeginEvent(net::NetLog::TYPE_REQUEST_ALIVE, NULL);
     }
   }
 }
@@ -533,10 +539,3 @@
 void URLRequest::SetUserData(const void* key, UserData* data) {
   user_data_[key] = linked_ptr<UserData>(data);
 }
-
-void URLRequest::GetInfoForTracker(
-    RequestTracker<URLRequest>::RecentRequestInfo* info) const {
-  DCHECK(info);
-  info->original_url = original_url_;
-  info->load_log = load_log_;
-}
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index e586e28..7256ec0 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -10,17 +10,16 @@
 #include <vector>
 
 #include "base/leak_tracker.h"
-#include "base/linked_list.h"
 #include "base/linked_ptr.h"
 #include "base/logging.h"
+#include "base/non_thread_safe.h"
 #include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
 #include "googleurl/src/gurl.h"
-#include "net/base/load_log.h"
 #include "net/base/load_states.h"
+#include "net/base/net_log.h"
 #include "net/base/request_priority.h"
+#include "net/http/http_request_headers.h"
 #include "net/http/http_response_info.h"
-#include "net/url_request/request_tracker.h"
 #include "net/url_request/url_request_status.h"
 
 namespace base {
@@ -54,7 +53,7 @@
 //
 // NOTE: All usage of all instances of this class should be on the same thread.
 //
-class URLRequest {
+class URLRequest : public NonThreadSafe {
  public:
   // Derive from this class and add your own data members to associate extra
   // information with a URLRequest. Use GetUserData(key) and SetUserData()
@@ -187,6 +186,20 @@
       request->Cancel();
     }
 
+    // Called when reading cookies. |blocked_by_policy| is true if access to
+    // cookies was denied due to content settings. This method will never be
+    // invoked when LOAD_DO_NOT_SEND_COOKIES is specified.
+    virtual void OnGetCookies(URLRequest* request, bool blocked_by_policy) {
+    }
+
+    // Called when a cookie is set. |blocked_by_policy| is true if the cookie
+    // was rejected due to content settings. This method will never be invoked
+    // when LOAD_DO_NOT_SAVE_COOKIES is specified.
+    virtual void OnSetCookie(URLRequest* request,
+                             const std::string& cookie_line,
+                             bool blocked_by_policy) {
+    }
+
     // After calling Start(), the delegate will receive an OnResponseStarted
     // callback when the request has completed.  If an error occurred, the
     // request->status() will be set.  On success, all redirects have been
@@ -292,12 +305,16 @@
   //
   // When uploading data, bytes_len must be non-zero.
   // When uploading a file range, length must be non-zero. If length
-  // exceeds the end-of-file, the upload is clipped at end-of-file.
+  // exceeds the end-of-file, the upload is clipped at end-of-file. If the
+  // expected modification time is provided (non-zero), it will be used to
+  // check if the underlying file has been changed or not. The granularity of
+  // the time comparison is 1 second since time_t precision is used in WebKit.
   void AppendBytesToUpload(const char* bytes, int bytes_len);
   void AppendFileRangeToUpload(const FilePath& file_path,
-                               uint64 offset, uint64 length);
+                               uint64 offset, uint64 length,
+                               const base::Time& expected_modification_time);
   void AppendFileToUpload(const FilePath& file_path) {
-    AppendFileRangeToUpload(file_path, 0, kuint64max);
+    AppendFileRangeToUpload(file_path, 0, kuint64max, base::Time());
   }
 
   // Set the upload data directly.
@@ -316,17 +333,14 @@
   void SetExtraRequestHeaderByName(const std::string& name,
                                    const std::string& value, bool overwrite);
 
-  // Sets all extra request headers, from a \r\n-delimited string.  Any extra
-  // request headers set by other methods are overwritten by this method.  This
-  // method may only be called before Start() is called.  It is an error to
-  // call it later.
-  //
-  // Note: \r\n is only used to separate the headers in the string if there
-  // are multiple headers.  The last header in the string must not be followed
-  // by \r\n.
-  void SetExtraRequestHeaders(const std::string& headers);
+  // Sets all extra request headers.  Any extra request headers set by other
+  // methods are overwritten by this method.  This method may only be called
+  // before Start() is called.  It is an error to call it later.
+  void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers);
 
-  const std::string& extra_request_headers() { return extra_request_headers_; }
+  const net::HttpRequestHeaders& extra_request_headers() const {
+    return extra_request_headers_;
+  }
 
   // Returns the current load state for the request.
   net::LoadState GetLoadState() const;
@@ -362,11 +376,29 @@
   // Indicate if this response was fetched from disk cache.
   bool was_cached() const { return response_info_.was_cached; }
 
-  // Returns true if the URLRequest was delivered with SPDY.
+  // True if response could use alternate protocol. However, browser will
+  // ingore the alternate protocol if spdy is not enabled.
   bool was_fetched_via_spdy() const {
     return response_info_.was_fetched_via_spdy;
   }
 
+  // Returns true if the URLRequest was delivered after NPN is negotiated,
+  // using either SPDY or HTTP.
+  bool was_npn_negotiated() const {
+    return response_info_.was_npn_negotiated;
+  }
+
+  // Returns true if the URLRequest was delivered when the alertnate protocol
+  // is available.
+  bool was_alternate_protocol_available() const {
+    return response_info_.was_alternate_protocol_available;
+  }
+
+  // Returns true if the URLRequest was delivered through a proxy.
+  bool was_fetched_via_proxy() const {
+    return response_info_.was_fetched_via_proxy;
+  }
+
   // Get all response headers, as a HttpResponseHeaders object.  See comments
   // in HttpResponseHeaders class as to the format of the data.
   net::HttpResponseHeaders* response_headers() const;
@@ -454,7 +486,14 @@
   //
   // If a read error occurs, Read returns false and the request->status
   // will be set to an error.
-  bool Read(net::IOBuffer* buf, int max_bytes, int *bytes_read);
+  bool Read(net::IOBuffer* buf, int max_bytes, int* bytes_read);
+
+  // If this request is being cached by the HTTP cache, stop subsequent caching.
+  // Note that this method has no effect on other (simultaneous or not) requests
+  // for the same resource. The typical example is a request that results in
+  // the data being stored to disk (downloaded instead of rendered) so we don't
+  // want to store it twice.
+  void StopCaching();
 
   // This method may be called to follow a redirect that was deferred in
   // response to an OnReceivedRedirect call.
@@ -495,7 +534,7 @@
   URLRequestContext* context();
   void set_context(URLRequestContext* context);
 
-  net::LoadLog* load_log() { return load_log_; }
+  const net::BoundNetLog& net_log() const { return net_log_; }
 
   // Returns the expected content size if available
   int64 GetExpectedContentSize() const;
@@ -536,13 +575,12 @@
 
  private:
   friend class URLRequestJob;
-  friend class RequestTracker<URLRequest>;
 
   void StartJob(URLRequestJob* job);
 
   // Restarting involves replacing the current job with a new one such as what
   // happens when following a HTTP redirect.
-  void RestartWithJob(URLRequestJob *job);
+  void RestartWithJob(URLRequestJob* job);
   void PrepareToRestart();
 
   // Detaches the job from this request in preparation for this object going
@@ -554,22 +592,13 @@
   // passed values.
   void DoCancel(int os_error, const net::SSLInfo& ssl_info);
 
-  // Discard headers which have meaning in POST (Content-Length, Content-Type,
-  // Origin).
-  static std::string StripPostSpecificHeaders(const std::string& headers);
-
-  // Gets the goodies out of this that we want to show the user later on the
-  // chrome://net-internals/ page.
-  void GetInfoForTracker(
-      RequestTracker<URLRequest>::RecentRequestInfo* info) const;
-
   // Contextual information used for this request (can be NULL). This contains
   // most of the dependencies which are shared between requests (disk cache,
   // cookie store, socket poool, etc.)
   scoped_refptr<URLRequestContext> context_;
 
   // Tracks the time spent in various load states throughout this request.
-  scoped_refptr<net::LoadLog> load_log_;
+  net::BoundNetLog net_log_;
 
   scoped_refptr<URLRequestJob> job_;
   scoped_refptr<net::UploadData> upload_;
@@ -578,7 +607,7 @@
   GURL first_party_for_cookies_;
   std::string method_;  // "GET", "POST", etc. Should be all uppercase.
   std::string referrer_;
-  std::string extra_request_headers_;
+  net::HttpRequestHeaders extra_request_headers_;
   int load_flags_;  // Flags indicating the request type for the load;
                     // expected values are LOAD_* enums above.
 
@@ -616,7 +645,6 @@
   // this to determine which URLRequest to allocate sockets to first.
   net::RequestPriority priority_;
 
-  RequestTracker<URLRequest>::Node request_tracker_node_;
   base::LeakTracker<URLRequest> leak_tracker_;
 
   DISALLOW_COPY_AND_ASSIGN(URLRequest);
diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h
index d72f84d..0f7fd51 100644
--- a/net/url_request/url_request_context.h
+++ b/net/url_request/url_request_context.h
@@ -10,35 +10,46 @@
 #ifndef NET_URL_REQUEST_URL_REQUEST_CONTEXT_H_
 #define NET_URL_REQUEST_URL_REQUEST_CONTEXT_H_
 
+#include "base/non_thread_safe.h"
 #include "base/ref_counted.h"
 #include "base/string_util.h"
 #include "net/base/cookie_store.h"
 #include "net/base/host_resolver.h"
+#include "net/base/net_log.h"
 #include "net/base/ssl_config_service.h"
 #include "net/base/transport_security_state.h"
 #include "net/ftp/ftp_auth_cache.h"
 #include "net/proxy/proxy_service.h"
-#include "net/url_request/request_tracker.h"
 
 namespace net {
 class CookiePolicy;
 class FtpTransactionFactory;
+class HttpAuthHandlerFactory;
+class HttpNetworkDelegate;
 class HttpTransactionFactory;
 class SocketStream;
 }
 class URLRequest;
 
 // Subclass to provide application-specific context for URLRequest instances.
-class URLRequestContext :
-    public base::RefCountedThreadSafe<URLRequestContext> {
+class URLRequestContext
+    : public base::RefCountedThreadSafe<URLRequestContext>,
+      public NonThreadSafe {
  public:
   URLRequestContext()
-      : http_transaction_factory_(NULL),
+      : net_log_(NULL),
+        http_transaction_factory_(NULL),
         ftp_transaction_factory_(NULL),
+        http_auth_handler_factory_(NULL),
+        network_delegate_(NULL),
         cookie_policy_(NULL),
         transport_security_state_(NULL) {
   }
 
+  net::NetLog* net_log() const {
+    return net_log_;
+  }
+
   net::HostResolver* host_resolver() const {
     return host_resolver_;
   }
@@ -77,22 +88,18 @@
   // Gets the FTP authentication cache for this context.
   net::FtpAuthCache* ftp_auth_cache() { return &ftp_auth_cache_; }
 
+  // Gets the HTTP Authentication Handler Factory for this context.
+  // The factory is only valid for the lifetime of this URLRequestContext
+  net::HttpAuthHandlerFactory* http_auth_handler_factory() {
+    return http_auth_handler_factory_;
+  }
+
   // Gets the value of 'Accept-Charset' header field.
   const std::string& accept_charset() const { return accept_charset_; }
 
   // Gets the value of 'Accept-Language' header field.
   const std::string& accept_language() const { return accept_language_; }
 
-  // Gets the tracker for URLRequests associated with this context.
-  RequestTracker<URLRequest>* url_request_tracker() {
-    return &url_request_tracker_;
-  }
-
-  // Gets the tracker for SocketStreams associated with this context.
-  RequestTracker<net::SocketStream>* socket_stream_tracker() {
-    return &socket_stream_tracker_;
-  }
-
   // Gets the UA string to use for the given URL.  Pass an invalid URL (such as
   // GURL()) to get the default UA string.  Subclasses should override this
   // method to provide a UA string.
@@ -107,20 +114,6 @@
     referrer_charset_ = charset;
   }
 
-  // Called before adding cookies to requests. Returns true if cookie can
-  // be added to the request. The cookie might still be modified though.
-  virtual bool InterceptRequestCookies(const URLRequest* request,
-                                       const std::string& cookies) const {
-    return true;
-  }
-
-  // Called before adding cookies from respones to the cookie monster. Returns
-  // true if the cookie can be added. The cookie might still be modified though.
-  virtual bool InterceptResponseCookie(const URLRequest* request,
-                                       const std::string& cookie) const {
-    return true;
-  }
-
  protected:
   friend class base::RefCountedThreadSafe<URLRequestContext>;
 
@@ -128,11 +121,14 @@
 
   // The following members are expected to be initialized and owned by
   // subclasses.
+  net::NetLog* net_log_;
   scoped_refptr<net::HostResolver> host_resolver_;
   scoped_refptr<net::ProxyService> proxy_service_;
   scoped_refptr<net::SSLConfigService> ssl_config_service_;
   net::HttpTransactionFactory* http_transaction_factory_;
   net::FtpTransactionFactory* ftp_transaction_factory_;
+  net::HttpAuthHandlerFactory* http_auth_handler_factory_;
+  net::HttpNetworkDelegate* network_delegate_;
   scoped_refptr<net::CookieStore> cookie_store_;
   net::CookiePolicy* cookie_policy_;
   scoped_refptr<net::TransportSecurityState> transport_security_state_;
@@ -144,12 +140,6 @@
   // filename for file download.
   std::string referrer_charset_;
 
-  // Tracks the requests associated with this context.
-  RequestTracker<URLRequest> url_request_tracker_;
-
-  // Trakcs the socket streams associated with this context.
-  RequestTracker<net::SocketStream> socket_stream_tracker_;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(URLRequestContext);
 };
diff --git a/net/url_request/url_request_file_dir_job.cc b/net/url_request/url_request_file_dir_job.cc
index 07977b5..19a1aaf 100644
--- a/net/url_request/url_request_file_dir_job.cc
+++ b/net/url_request/url_request_file_dir_job.cc
@@ -6,8 +6,8 @@
 
 #include "base/file_util.h"
 #include "base/message_loop.h"
-#include "base/string_util.h"
 #include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
 #include "base/time.h"
 #include "googleurl/src/gurl.h"
 #include "net/base/io_buffer.h"
@@ -211,24 +211,3 @@
     }
   }
 }
-
-bool URLRequestFileDirJob::IsRedirectResponse(
-    GURL* location, int* http_status_code) {
-  // If the URL did not have a trailing slash, treat the response as a redirect
-  // to the URL with a trailing slash appended.
-  std::string path = request_->url().path();
-  if (path.empty() || (path[path.size() - 1] != '/')) {
-    // This happens when we discovered the file is a directory, so needs a
-    // slash at the end of the path.
-    std::string new_path = path;
-    new_path.push_back('/');
-    GURL::Replacements replacements;
-    replacements.SetPathStr(new_path);
-
-    *location = request_->url().ReplaceComponents(replacements);
-    *http_status_code = 301;  // simulate a permanent redirect
-    return true;
-  }
-
-  return false;
-}
diff --git a/net/url_request/url_request_file_dir_job.h b/net/url_request/url_request_file_dir_job.h
index 24a6c72..0322f10 100644
--- a/net/url_request/url_request_file_dir_job.h
+++ b/net/url_request/url_request_file_dir_job.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -25,7 +25,6 @@
   virtual bool ReadRawData(net::IOBuffer* buf, int buf_size, int *bytes_read);
   virtual bool GetMimeType(std::string* mime_type) const;
   virtual bool GetCharset(std::string* charset);
-  virtual bool IsRedirectResponse(GURL* location, int* http_status_code);
 
   // DirectoryLister::DirectoryListerDelegate methods:
   virtual void OnListFile(const file_util::FileEnumerator::FindInfo& data);
@@ -63,7 +62,7 @@
   scoped_refptr<net::IOBuffer> read_buffer_;
   int read_buffer_length_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(URLRequestFileDirJob);
+  DISALLOW_COPY_AND_ASSIGN(URLRequestFileDirJob);
 };
 
 #endif  // NET_URL_REQUEST_URL_REQUEST_FILE_DIR_JOB_H__
diff --git a/net/url_request/url_request_file_job.cc b/net/url_request/url_request_file_job.cc
index 8e1b7dc..003a29d 100644
--- a/net/url_request/url_request_file_job.cc
+++ b/net/url_request/url_request_file_job.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -80,8 +80,15 @@
 URLRequestJob* URLRequestFileJob::Factory(
     URLRequest* request, const std::string& scheme) {
   FilePath file_path;
+
+  // We need to decide whether to create URLRequestFileJob for file access or
+  // URLRequestFileDirJob for directory access. To avoid accessing the
+  // filesystem, we only look at the path string here.
+  // The code in the URLRequestFileJob::Start() method discovers that a path,
+  // which doesn't end with a slash, should really be treated as a directory,
+  // and it then redirects to the URLRequestFileDirJob.
   if (net::FileURLToFilePath(request->url(), &file_path) &&
-      file_util::EnsureEndsWithSeparator(&file_path) &&
+      file_util::EndsWithSeparator(file_path) &&
       file_path.IsAbsolute())
     return new URLRequestFileDirJob(request, file_path);
 
@@ -188,18 +195,23 @@
   return net::GetMimeTypeFromFile(file_path_, mime_type);
 }
 
-void URLRequestFileJob::SetExtraRequestHeaders(const std::string& headers) {
-  // We only care about "Range" header here.
-  std::vector<net::HttpByteRange> ranges;
-  if (net::HttpUtil::ParseRanges(headers, &ranges)) {
-    if (ranges.size() == 1) {
-      byte_range_ = ranges[0];
-    } else {
-      // We don't support multiple range requests in one single URL request,
-      // because we need to do multipart encoding here.
-      // TODO(hclam): decide whether we want to support multiple range requests.
-      NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
-                 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
+void URLRequestFileJob::SetExtraRequestHeaders(
+    const net::HttpRequestHeaders& headers) {
+  std::string range_header;
+  if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
+    // We only care about "Range" header here.
+    std::vector<net::HttpByteRange> ranges;
+    if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
+      if (ranges.size() == 1) {
+        byte_range_ = ranges[0];
+      } else {
+        // We don't support multiple range requests in one single URL request,
+        // because we need to do multipart encoding here.
+        // TODO(hclam): decide whether we want to support multiple range
+        // requests.
+        NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
+                                    net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
+      }
     }
   }
 }
@@ -214,15 +226,20 @@
   if (!request_)
     return;
 
+  is_directory_ = file_info.is_directory;
+
   int rv = net::OK;
-  // We use URLRequestFileJob to handle valid and invalid files as well as
-  // invalid directories. For a directory to be invalid, it must either not
-  // exist, or be "\" on Windows. (Windows resolves "\" to "C:\", thus
-  // reporting it as existent.) On POSIX, we don't count any existent
-  // directory as invalid.
-  if (!exists || file_info.is_directory) {
+  // We use URLRequestFileJob to handle files as well as directories without
+  // trailing slash.
+  // If a directory does not exist, we return ERR_FILE_NOT_FOUND. Otherwise,
+  // we will append trailing slash and redirect to FileDirJob.
+  // A special case is "\" on Windows. We should resolve as invalid.
+  // However, Windows resolves "\" to "C:\", thus reports it as existent.
+  // So what happens is we append it with trailing slash and redirect it to
+  // FileDirJob where it is resolved as invalid.
+  if (!exists) {
     rv = net::ERR_FILE_NOT_FOUND;
-  } else {
+  } else if (!is_directory_) {
     int flags = base::PLATFORM_FILE_OPEN |
                 base::PLATFORM_FILE_READ |
                 base::PLATFORM_FILE_ASYNC;
@@ -273,15 +290,25 @@
   NotifyReadComplete(result);
 }
 
-bool URLRequestFileJob::IsRedirectResponse(
-    GURL* location, int* http_status_code) {
-#if defined(OS_WIN)
-  std::wstring extension =
-      file_util::GetFileExtensionFromPath(file_path_.value());
+bool URLRequestFileJob::IsRedirectResponse(GURL* location,
+                                           int* http_status_code) {
+  if (is_directory_) {
+    // This happens when we discovered the file is a directory, so needs a
+    // slash at the end of the path.
+    std::string new_path = request_->url().path();
+    new_path.push_back('/');
+    GURL::Replacements replacements;
+    replacements.SetPathStr(new_path);
 
+    *location = request_->url().ReplaceComponents(replacements);
+    *http_status_code = 301;  // simulate a permanent redirect
+    return true;
+  }
+
+#if defined(OS_WIN)
   // Follow a Windows shortcut.
   // We just resolve .lnk file, ignore others.
-  if (!LowerCaseEqualsASCII(extension, "lnk"))
+  if (!LowerCaseEqualsASCII(file_path_.Extension(), ".lnk"))
     return false;
 
   FilePath new_path = file_path_;
diff --git a/net/url_request/url_request_file_job.h b/net/url_request/url_request_file_job.h
index 8cb28bd..aed6859 100644
--- a/net/url_request/url_request_file_job.h
+++ b/net/url_request/url_request_file_job.h
@@ -30,7 +30,7 @@
   virtual bool GetContentEncodings(
       std::vector<Filter::FilterType>* encoding_type);
   virtual bool GetMimeType(std::string* mime_type) const;
-  virtual void SetExtraRequestHeaders(const std::string& headers);
+  virtual void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers);
 
   static URLRequest::ProtocolFactory Factory;
 
diff --git a/net/url_request/url_request_filter.h b/net/url_request/url_request_filter.h
index 3f255b8..d81e68c 100644
--- a/net/url_request/url_request_filter.h
+++ b/net/url_request/url_request_filter.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 //
@@ -79,7 +79,7 @@
   // Singleton instance.
   static URLRequestFilter* shared_instance_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(URLRequestFilter);
+  DISALLOW_COPY_AND_ASSIGN(URLRequestFilter);
 };
 
 #endif  // NET_URL_REQUEST_URL_REQUEST_FILTER_H_
diff --git a/net/url_request/url_request_ftp_job.cc b/net/url_request/url_request_ftp_job.cc
new file mode 100644
index 0000000..f8746fc
--- /dev/null
+++ b/net/url_request/url_request_ftp_job.cc
@@ -0,0 +1,244 @@
+// Copyright (c) 2009 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/url_request/url_request_ftp_job.h"
+
+#include "base/compiler_specific.h"
+#include "base/message_loop.h"
+#include "net/base/auth.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_util.h"
+#include "net/ftp/ftp_response_info.h"
+#include "net/ftp/ftp_transaction_factory.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_error_job.h"
+
+URLRequestFtpJob::URLRequestFtpJob(URLRequest* request)
+    : URLRequestJob(request),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          start_callback_(this, &URLRequestFtpJob::OnStartCompleted)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(
+          read_callback_(this, &URLRequestFtpJob::OnReadCompleted)),
+      read_in_progress_(false),
+      context_(request->context()) {
+}
+
+URLRequestFtpJob::~URLRequestFtpJob() {
+}
+
+// static
+URLRequestJob* URLRequestFtpJob::Factory(URLRequest* request,
+                                         const std::string& scheme) {
+  DCHECK_EQ(scheme, "ftp");
+
+  int port = request->url().IntPort();
+  if (request->url().has_port() &&
+    !net::IsPortAllowedByFtp(port) && !net::IsPortAllowedByOverride(port))
+    return new URLRequestErrorJob(request, net::ERR_UNSAFE_PORT);
+
+  DCHECK(request->context());
+  DCHECK(request->context()->ftp_transaction_factory());
+  return new URLRequestFtpJob(request);
+}
+
+bool URLRequestFtpJob::GetMimeType(std::string* mime_type) const {
+  if (transaction_->GetResponseInfo()->is_directory_listing) {
+    *mime_type = "text/vnd.chromium.ftp-dir";
+    return true;
+  }
+  return false;
+}
+
+void URLRequestFtpJob::Start() {
+  DCHECK(!transaction_.get());
+  request_info_.url = request_->url();
+  StartTransaction();
+}
+
+void URLRequestFtpJob::Kill() {
+  if (!transaction_.get())
+    return;
+  DestroyTransaction();
+  URLRequestJob::Kill();
+}
+
+net::LoadState URLRequestFtpJob::GetLoadState() const {
+  return transaction_.get() ?
+      transaction_->GetLoadState() : net::LOAD_STATE_IDLE;
+}
+
+bool URLRequestFtpJob::NeedsAuth() {
+  // Note that we only have to worry about cases where an actual FTP server
+  // requires auth (and not a proxy), because connecting to FTP via proxy
+  // effectively means the browser communicates via HTTP, and uses HTTP's
+  // Proxy-Authenticate protocol when proxy servers require auth.
+  return server_auth_ && server_auth_->state == net::AUTH_STATE_NEED_AUTH;
+}
+
+void URLRequestFtpJob::GetAuthChallengeInfo(
+    scoped_refptr<net::AuthChallengeInfo>* result) {
+  DCHECK((server_auth_ != NULL) &&
+         (server_auth_->state == net::AUTH_STATE_NEED_AUTH));
+  scoped_refptr<net::AuthChallengeInfo> auth_info = new net::AuthChallengeInfo;
+  auth_info->is_proxy = false;
+  auth_info->host_and_port = ASCIIToWide(
+      net::GetHostAndPort(request_->url()));
+  auth_info->scheme = L"";
+  auth_info->realm = L"";
+  result->swap(auth_info);
+}
+
+void URLRequestFtpJob::SetAuth(const std::wstring& username,
+                               const std::wstring& password) {
+  DCHECK(NeedsAuth());
+  server_auth_->state = net::AUTH_STATE_HAVE_AUTH;
+  server_auth_->username = username;
+  server_auth_->password = password;
+
+  request_->context()->ftp_auth_cache()->Add(request_->url().GetOrigin(),
+                                             username, password);
+
+  RestartTransactionWithAuth();
+}
+
+void URLRequestFtpJob::CancelAuth() {
+  DCHECK(NeedsAuth());
+  server_auth_->state = net::AUTH_STATE_CANCELED;
+
+  // Once the auth is cancelled, we proceed with the request as though
+  // there were no auth.  Schedule this for later so that we don't cause
+  // any recursing into the caller as a result of this call.
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      this, &URLRequestFtpJob::OnStartCompleted, net::OK));
+}
+
+bool URLRequestFtpJob::ReadRawData(net::IOBuffer* buf,
+                                   int buf_size,
+                                   int *bytes_read) {
+  DCHECK_NE(buf_size, 0);
+  DCHECK(bytes_read);
+  DCHECK(!read_in_progress_);
+
+  int rv = transaction_->Read(buf, buf_size, &read_callback_);
+  if (rv >= 0) {
+    *bytes_read = rv;
+    return true;
+  }
+
+  if (rv == net::ERR_IO_PENDING) {
+    read_in_progress_ = true;
+    SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+  } else {
+    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
+  }
+  return false;
+}
+
+void URLRequestFtpJob::OnStartCompleted(int result) {
+  // If the request was destroyed, then there is no more work to do.
+  if (!request_ || !request_->delegate())
+    return;
+  // If the transaction was destroyed, then the job was cancelled, and
+  // we can just ignore this notification.
+  if (!transaction_.get())
+    return;
+  // Clear the IO_PENDING status
+  SetStatus(URLRequestStatus());
+
+  // FTP obviously doesn't have HTTP Content-Length header. We have to pass
+  // the content size information manually.
+  set_expected_content_size(
+      transaction_->GetResponseInfo()->expected_content_size);
+
+  if (result == net::OK) {
+    NotifyHeadersComplete();
+  } else if (transaction_->GetResponseInfo()->needs_auth) {
+    GURL origin = request_->url().GetOrigin();
+    if (server_auth_ && server_auth_->state == net::AUTH_STATE_HAVE_AUTH) {
+      request_->context()->ftp_auth_cache()->Remove(origin,
+                                                    server_auth_->username,
+                                                    server_auth_->password);
+    } else if (!server_auth_) {
+      server_auth_ = new net::AuthData();
+    }
+    server_auth_->state = net::AUTH_STATE_NEED_AUTH;
+
+    net::FtpAuthCache::Entry* cached_auth =
+        request_->context()->ftp_auth_cache()->Lookup(origin);
+
+    if (cached_auth) {
+      // Retry using cached auth data.
+      SetAuth(cached_auth->username, cached_auth->password);
+    } else {
+      // Prompt for a username/password.
+      NotifyHeadersComplete();
+    }
+  } else {
+    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
+  }
+}
+
+void URLRequestFtpJob::OnReadCompleted(int result) {
+  read_in_progress_ = false;
+  if (result == 0) {
+    NotifyDone(URLRequestStatus());
+  } else if (result < 0) {
+    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
+  } else {
+    // Clear the IO_PENDING status
+    SetStatus(URLRequestStatus());
+  }
+  NotifyReadComplete(result);
+}
+
+void URLRequestFtpJob::RestartTransactionWithAuth() {
+  DCHECK(server_auth_ && server_auth_->state == net::AUTH_STATE_HAVE_AUTH);
+
+  // No matter what, we want to report our status as IO pending since we will
+  // be notifying our consumer asynchronously via OnStartCompleted.
+  SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+
+  int rv = transaction_->RestartWithAuth(server_auth_->username,
+                                         server_auth_->password,
+                                         &start_callback_);
+  if (rv == net::ERR_IO_PENDING)
+    return;
+
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      this, &URLRequestFtpJob::OnStartCompleted, rv));
+}
+
+void URLRequestFtpJob::StartTransaction() {
+  // Create a transaction.
+  DCHECK(!transaction_.get());
+  DCHECK(request_->context());
+  DCHECK(request_->context()->ftp_transaction_factory());
+
+  transaction_.reset(
+  request_->context()->ftp_transaction_factory()->CreateTransaction());
+
+  // No matter what, we want to report our status as IO pending since we will
+  // be notifying our consumer asynchronously via OnStartCompleted.
+  SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+  int rv;
+  if (transaction_.get()) {
+    rv = transaction_->Start(
+        &request_info_, &start_callback_, request_->net_log());
+    if (rv == net::ERR_IO_PENDING)
+      return;
+  } else {
+    rv = net::ERR_FAILED;
+  }
+  // The transaction started synchronously, but we need to notify the
+  // URLRequest delegate via the message loop.
+  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+      this, &URLRequestFtpJob::OnStartCompleted, rv));
+}
+
+void URLRequestFtpJob::DestroyTransaction() {
+  DCHECK(transaction_.get());
+
+  transaction_.reset();
+}
diff --git a/net/url_request/url_request_ftp_job.h b/net/url_request/url_request_ftp_job.h
new file mode 100644
index 0000000..453543f
--- /dev/null
+++ b/net/url_request/url_request_ftp_job.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2009 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_URL_REQUEST_URL_REQUEST_FTP_JOB_H_
+#define NET_URL_REQUEST_URL_REQUEST_FTP_JOB_H_
+
+#include <string>
+
+#include "net/base/auth.h"
+#include "net/base/completion_callback.h"
+#include "net/ftp/ftp_request_info.h"
+#include "net/ftp/ftp_transaction.h"
+#include "net/url_request/url_request_job.h"
+
+class URLRequestContext;
+
+namespace net {
+struct list_state;
+}
+
+// A URLRequestJob subclass that is built on top of FtpTransaction. It
+// provides an implementation for FTP.
+class URLRequestFtpJob : public URLRequestJob {
+ public:
+
+  explicit URLRequestFtpJob(URLRequest* request);
+
+  static URLRequestJob* Factory(URLRequest* request, const std::string& scheme);
+
+  // URLRequestJob methods:
+  virtual bool GetMimeType(std::string* mime_type) const;
+
+ private:
+  virtual ~URLRequestFtpJob();
+
+  // URLRequestJob methods:
+  virtual void Start();
+  virtual void Kill();
+  virtual net::LoadState GetLoadState() const;
+  virtual bool NeedsAuth();
+  virtual void GetAuthChallengeInfo(
+      scoped_refptr<net::AuthChallengeInfo>* auth_info);
+  virtual void SetAuth(const std::wstring& username,
+                       const std::wstring& password);
+  virtual void CancelAuth();
+
+  // TODO(ibrar):  Yet to give another look at this function.
+  virtual uint64 GetUploadProgress() const { return 0; }
+  virtual bool ReadRawData(net::IOBuffer* buf, int buf_size, int *bytes_read);
+
+  void DestroyTransaction();
+  void StartTransaction();
+
+  void OnStartCompleted(int result);
+  void OnReadCompleted(int result);
+
+  void RestartTransactionWithAuth();
+
+  void LogFtpServerType(char server_type);
+
+  net::FtpRequestInfo request_info_;
+  scoped_ptr<net::FtpTransaction> transaction_;
+
+  net::CompletionCallbackImpl<URLRequestFtpJob> start_callback_;
+  net::CompletionCallbackImpl<URLRequestFtpJob> read_callback_;
+
+  bool read_in_progress_;
+
+  scoped_refptr<net::AuthData> server_auth_;
+
+  // Keep a reference to the url request context to be sure it's not deleted
+  // before us.
+  scoped_refptr<URLRequestContext> context_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLRequestFtpJob);
+};
+
+#endif  // NET_URL_REQUEST_URL_REQUEST_FTP_JOB_H_
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 25b0f33..9a03213 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -15,23 +15,26 @@
 #include "net/base/cert_status_flags.h"
 #include "net/base/cookie_policy.h"
 #include "net/base/filter.h"
-#include "net/base/https_prober.h"
 #include "net/base/transport_security_state.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_util.h"
 #include "net/base/sdch_manager.h"
 #include "net/base/ssl_cert_request_info.h"
+#include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
 #include "net/http/http_transaction.h"
 #include "net/http/http_transaction_factory.h"
 #include "net/http/http_util.h"
+#include "net/url_request/https_prober.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_error_job.h"
 #include "net/url_request/url_request_redirect_job.h"
 
+static const char kAvailDictionaryHeader[] = "Avail-Dictionary";
+
 // TODO(darin): make sure the port blocking code is not lost
 // static
 URLRequestJob* URLRequestHttpJob::Factory(URLRequest* request,
@@ -128,9 +131,9 @@
 }
 
 void URLRequestHttpJob::SetExtraRequestHeaders(
-    const std::string& headers) {
+    const net::HttpRequestHeaders& headers) {
   DCHECK(!transaction_.get()) << "cannot change once started";
-  request_info_.extra_headers = headers;
+  request_info_.extra_headers.CopyFrom(headers);
 }
 
 void URLRequestHttpJob::Start() {
@@ -146,8 +149,9 @@
   request_info_.priority = request_->priority();
 
   if (request_->context()) {
-    request_info_.user_agent =
-        request_->context()->GetUserAgent(request_->url());
+    request_info_.extra_headers.SetHeader(
+        net::HttpRequestHeaders::kUserAgent,
+        request_->context()->GetUserAgent(request_->url()));
   }
 
   AddExtraHeaders();
@@ -334,9 +338,8 @@
   // Update the cookies, since the cookie store may have been updated from the
   // headers in the 401/407. Since cookies were already appended to
   // extra_headers, we need to strip them out before adding them again.
-  static const char* const cookie_name[] = { "cookie" };
-  request_info_.extra_headers = net::HttpUtil::StripHeaders(
-      request_info_.extra_headers, cookie_name, arraysize(cookie_name));
+  request_info_.extra_headers.RemoveHeader(
+      net::HttpRequestHeaders::kCookie);
 
   AddCookieHeaderAndStart();
 }
@@ -429,20 +432,34 @@
   return false;
 }
 
+void URLRequestHttpJob::StopCaching() {
+  if (transaction_.get())
+    transaction_->StopCaching();
+}
+
 void URLRequestHttpJob::OnCanGetCookiesCompleted(int policy) {
   // If the request was destroyed, then there is no more work to do.
   if (request_ && request_->delegate()) {
-    if (policy == net::OK && request_->context()->cookie_store()) {
+    if (policy == net::ERR_ACCESS_DENIED) {
+      request_->delegate()->OnGetCookies(request_, true);
+    } else if (policy == net::OK && request_->context()->cookie_store()) {
+      request_->delegate()->OnGetCookies(request_, false);
       net::CookieOptions options;
       options.set_include_httponly();
       std::string cookies =
           request_->context()->cookie_store()->GetCookiesWithOptions(
               request_->url(), options);
-      if (request_->context()->InterceptRequestCookies(request_, cookies) &&
-          !cookies.empty())
-        request_info_.extra_headers += "Cookie: " + cookies + "\r\n";
+      if (!cookies.empty()) {
+        request_info_.extra_headers.SetHeader(
+            net::HttpRequestHeaders::kCookie, cookies);
+      }
     }
-    StartTransaction();
+    // We may have been canceled within OnGetCookies.
+    if (GetStatus().is_success()) {
+      StartTransaction();
+    } else {
+      NotifyCanceled();
+    }
   }
   Release();  // Balance AddRef taken in AddCookieHeaderAndStart
 }
@@ -450,16 +467,33 @@
 void URLRequestHttpJob::OnCanSetCookieCompleted(int policy) {
   // If the request was destroyed, then there is no more work to do.
   if (request_ && request_->delegate()) {
-    if (policy == net::OK && request_->context()->cookie_store()) {
+    if (policy == net::ERR_ACCESS_DENIED) {
+      request_->delegate()->OnSetCookie(
+          request_,
+          response_cookies_[response_cookies_save_index_],
+          true);
+    } else if ((policy == net::OK || policy == net::OK_FOR_SESSION_ONLY) &&
+               request_->context()->cookie_store()) {
       // OK to save the current response cookie now.
       net::CookieOptions options;
       options.set_include_httponly();
+      if (policy == net::OK_FOR_SESSION_ONLY)
+        options.set_force_session();
       request_->context()->cookie_store()->SetCookieWithOptions(
           request_->url(), response_cookies_[response_cookies_save_index_],
           options);
+      request_->delegate()->OnSetCookie(
+          request_,
+          response_cookies_[response_cookies_save_index_],
+          false);
     }
     response_cookies_save_index_++;
-    SaveNextCookie();
+    // We may have been canceled within OnSetCookie.
+    if (GetStatus().is_success()) {
+      SaveNextCookie();
+    } else {
+      NotifyCanceled();
+    }
   }
   Release();  // Balance AddRef taken in SaveNextCookie
 }
@@ -569,23 +603,12 @@
   URLRequestJob::NotifyHeadersComplete();
 }
 
-#if defined(OS_WIN)
-#pragma optimize("", off)
-#pragma warning(disable:4748)
-#endif
 void URLRequestHttpJob::DestroyTransaction() {
-  CHECK(transaction_.get());
-  // TODO(rvargas): remove this after finding the cause for bug 31723.
-  char local_obj[sizeof(*this)];
-  memcpy(local_obj, this, sizeof(local_obj));
+  DCHECK(transaction_.get());
 
   transaction_.reset();
   response_info_ = NULL;
 }
-#if defined(OS_WIN)
-#pragma warning(default:4748)
-#pragma optimize("", on)
-#endif
 
 void URLRequestHttpJob::StartTransaction() {
   // NOTE: This method assumes that request_info_ is already setup properly.
@@ -606,7 +629,7 @@
         &transaction_);
     if (rv == net::OK) {
       rv = transaction_->Start(
-          &request_info_, &start_callback_, request_->load_log());
+          &request_info_, &start_callback_, request_->net_log());
     }
   }
 
@@ -654,14 +677,16 @@
   // these headers.  Some proxies deliberately corrupt Accept-Encoding headers.
   if (!advertise_sdch) {
     // Tell the server what compression formats we support (other than SDCH).
-    request_info_.extra_headers += "Accept-Encoding: gzip,deflate\r\n";
+    request_info_.extra_headers.SetHeader(
+        net::HttpRequestHeaders::kAcceptEncoding, "gzip,deflate");
   } else {
     // Include SDCH in acceptable list.
-    request_info_.extra_headers += "Accept-Encoding: "
-        "gzip,deflate,sdch\r\n";
+    request_info_.extra_headers.SetHeader(
+        net::HttpRequestHeaders::kAcceptEncoding, "gzip,deflate,sdch");
     if (!avail_dictionaries.empty()) {
-      request_info_.extra_headers += "Avail-Dictionary: "
-          + avail_dictionaries + "\r\n";
+      request_info_.extra_headers.SetHeader(
+          kAvailDictionaryHeader,
+          avail_dictionaries);
       sdch_dictionary_advertised_ = true;
       // Since we're tagging this transaction as advertising a dictionary, we'll
       // definately employ an SDCH filter (or tentative sdch filter) when we get
@@ -675,12 +700,18 @@
   if (context) {
     // Only add default Accept-Language and Accept-Charset if the request
     // didn't have them specified.
-    net::HttpUtil::AppendHeaderIfMissing("Accept-Language",
-                                         context->accept_language(),
-                                         &request_info_.extra_headers);
-    net::HttpUtil::AppendHeaderIfMissing("Accept-Charset",
-                                         context->accept_charset(),
-                                         &request_info_.extra_headers);
+    if (!request_info_.extra_headers.HasHeader(
+        net::HttpRequestHeaders::kAcceptLanguage)) {
+      request_info_.extra_headers.SetHeader(
+          net::HttpRequestHeaders::kAcceptLanguage,
+          context->accept_language());
+    }
+    if (!request_info_.extra_headers.HasHeader(
+        net::HttpRequestHeaders::kAcceptCharset)) {
+      request_info_.extra_headers.SetHeader(
+          net::HttpRequestHeaders::kAcceptCharset,
+          context->accept_charset());
+    }
   }
 }
 
@@ -694,7 +725,7 @@
   int policy = net::OK;
 
   if (request_info_.load_flags & net::LOAD_DO_NOT_SEND_COOKIES) {
-    policy = net::ERR_ACCESS_DENIED;
+    policy = net::ERR_FAILED;
   } else if (request_->context()->cookie_policy()) {
     policy = request_->context()->cookie_policy()->CanGetCookies(
         request_->url(),
@@ -740,7 +771,7 @@
   int policy = net::OK;
 
   if (request_info_.load_flags & net::LOAD_DO_NOT_SAVE_COOKIES) {
-    policy = net::ERR_ACCESS_DENIED;
+    policy = net::ERR_FAILED;
   } else if (request_->context()->cookie_policy()) {
     policy = request_->context()->cookie_policy()->CanSetCookie(
         request_->url(),
@@ -761,10 +792,8 @@
   std::string value;
 
   void* iter = NULL;
-  while (response_info->headers->EnumerateHeader(&iter, name, &value)) {
-    if (request_->context()->InterceptResponseCookie(request_, value))
-      cookies->push_back(value);
-  }
+  while (response_info->headers->EnumerateHeader(&iter, name, &value))
+    cookies->push_back(value);
 }
 
 class HTTPSProberDelegate : public net::HTTPSProberDelegate {
@@ -810,8 +839,7 @@
 
   const bool https = response_info_->ssl_info.is_valid();
   const bool valid_https =
-      https &&
-      !(response_info_->ssl_info.cert_status & net::CERT_STATUS_ALL_ERRORS);
+      https && !net::IsCertStatusError(response_info_->ssl_info.cert_status);
 
   std::string name = "Strict-Transport-Security";
   std::string value;
@@ -879,7 +907,7 @@
       continue;
     }
 
-    net::HTTPSProberDelegate* delegate =
+    HTTPSProberDelegate* delegate =
         new HTTPSProberDelegate(request_info_.url.host(), max_age,
                                 include_subdomains,
                                 ctx->transport_security_state());
diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h
index e00e3c4..279cdd4 100644
--- a/net/url_request/url_request_http_job.h
+++ b/net/url_request/url_request_http_job.h
@@ -32,7 +32,7 @@
 
   // URLRequestJob methods:
   virtual void SetUpload(net::UploadData* upload);
-  virtual void SetExtraRequestHeaders(const std::string& headers);
+  virtual void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers);
   virtual void Start();
   virtual void Kill();
   virtual net::LoadState GetLoadState() const;
@@ -55,6 +55,7 @@
   virtual void ContinueWithCertificate(net::X509Certificate* client_cert);
   virtual void ContinueDespiteLastError();
   virtual bool ReadRawData(net::IOBuffer* buf, int buf_size, int *bytes_read);
+  virtual void StopCaching();
 
   // Shadows URLRequestJob's version of this method so we can grab cookies.
   void NotifyHeadersComplete();
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index 63611fd..23a7d85 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -1,14 +1,16 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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/url_request/url_request_job.h"
 
+#include "base/histogram.h"
 #include "base/message_loop.h"
 #include "base/string_util.h"
 #include "net/base/auth.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
+#include "net/base/mime_util.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_response_headers.h"
 #include "net/url_request/url_request.h"
@@ -24,10 +26,13 @@
 
 URLRequestJob::URLRequestJob(URLRequest* request)
     : request_(request),
+      prefilter_bytes_read_(0),
+      postfilter_bytes_read_(0),
+      is_compressible_content_(false),
+      is_compressed_(false),
       done_(false),
       filter_needs_more_output_space_(false),
-      read_buffer_(NULL),
-      read_buffer_len_(0),
+      filtered_read_buffer_len_(0),
       has_handled_response_(false),
       expected_content_size_(-1),
       deferred_redirect_status_code_(-1),
@@ -49,6 +54,13 @@
   g_url_request_job_tracker.RemoveJob(this);
 }
 
+void URLRequestJob::SetUpload(net::UploadData* upload) {
+}
+
+void URLRequestJob::SetExtraRequestHeaders(
+    const net::HttpRequestHeaders& headers) {
+}
+
 void URLRequestJob::Kill() {
   // Make sure the request is notified that we are done.  We assume that the
   // request took care of setting its error status before calling Kill.
@@ -60,10 +72,6 @@
   request_ = NULL;
 }
 
-bool URLRequestJob::IsDownload() const {
-  return (load_flags_ & net::LOAD_IS_DOWNLOAD) != 0;
-}
-
 void URLRequestJob::SetupFilter() {
   std::vector<Filter::FilterType> encoding_types;
   if (GetContentEncodings(&encoding_types)) {
@@ -87,6 +95,14 @@
   return true;
 }
 
+bool URLRequestJob::IsSafeRedirect(const GURL& location) {
+  return true;
+}
+
+bool URLRequestJob::NeedsAuth() {
+  return false;
+}
+
 void URLRequestJob::GetAuthChallengeInfo(
     scoped_refptr<net::AuthChallengeInfo>* auth_info) {
   // This will only be called if NeedsAuth() returns true, in which
@@ -143,6 +159,10 @@
   return filter_input_byte_count_;
 }
 
+bool URLRequestJob::GetMimeType(std::string* mime_type) const {
+  return false;
+}
+
 bool URLRequestJob::GetURL(GURL* gurl) const {
   if (!request_)
     return false;
@@ -156,6 +176,18 @@
   return request_->request_time();
 };
 
+bool URLRequestJob::IsCachedContent() const {
+  return false;
+}
+
+int URLRequestJob::GetResponseCode() const {
+  return -1;
+}
+
+int URLRequestJob::GetInputStreamBufferSize() const {
+  return kFilterBufSize;
+}
+
 // This function calls ReadData to get stream data. If a filter exists, passes
 // the data to the attached filter. Then returns the output from filter back to
 // the caller.
@@ -165,19 +197,19 @@
   DCHECK_LT(buf_size, 1000000);  // sanity check
   DCHECK(buf);
   DCHECK(bytes_read);
+  DCHECK(filtered_read_buffer_ == NULL);
+  DCHECK_EQ(0, filtered_read_buffer_len_);
 
   *bytes_read = 0;
 
   // Skip Filter if not present
   if (!filter_.get()) {
-    rv = ReadRawData(buf, buf_size, bytes_read);
-    if (rv && *bytes_read > 0)
-      RecordBytesRead(*bytes_read);
+    rv = ReadRawDataHelper(buf, buf_size, bytes_read);
   } else {
     // Save the caller's buffers while we do IO
     // in the filter's buffers.
-    read_buffer_ = buf;
-    read_buffer_len_ = buf_size;
+    filtered_read_buffer_ = buf;
+    filtered_read_buffer_len_ = buf_size;
 
     if (ReadFilteredData(bytes_read)) {
       rv = true;   // we have data to return
@@ -190,7 +222,43 @@
   return rv;
 }
 
-bool URLRequestJob::ReadRawDataForFilter(int *bytes_read) {
+void URLRequestJob::StopCaching() {
+  // Nothing to do here.
+}
+
+net::LoadState URLRequestJob::GetLoadState() const {
+  return net::LOAD_STATE_IDLE;
+}
+
+uint64 URLRequestJob::GetUploadProgress() const {
+  return 0;
+}
+
+bool URLRequestJob::GetCharset(std::string* charset) {
+  return false;
+}
+
+void URLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
+}
+
+bool URLRequestJob::GetResponseCookies(std::vector<std::string>* cookies) {
+  return false;
+}
+
+bool URLRequestJob::GetContentEncodings(
+    std::vector<Filter::FilterType>* encoding_types) {
+  return false;
+}
+
+bool URLRequestJob::IsDownload() const {
+  return (load_flags_ & net::LOAD_IS_DOWNLOAD) != 0;
+}
+
+bool URLRequestJob::IsSdchResponse() const {
+  return false;
+}
+
+bool URLRequestJob::ReadRawDataForFilter(int* bytes_read) {
   bool rv = false;
 
   DCHECK(bytes_read);
@@ -204,9 +272,7 @@
   if (!filter_->stream_data_len() && !is_done()) {
     net::IOBuffer* stream_buffer = filter_->stream_buffer();
     int stream_buffer_size = filter_->stream_buffer_size();
-    rv = ReadRawData(stream_buffer, stream_buffer_size, bytes_read);
-    if (rv && *bytes_read > 0)
-      RecordBytesRead(*bytes_read);
+    rv = ReadRawDataHelper(stream_buffer, stream_buffer_size, bytes_read);
   }
   return rv;
 }
@@ -224,11 +290,12 @@
   filter_->FlushStreamBuffer(bytes_read);
 }
 
-bool URLRequestJob::ReadFilteredData(int *bytes_read) {
+bool URLRequestJob::ReadFilteredData(int* bytes_read) {
   DCHECK(filter_.get());  // don't add data if there is no filter
-  DCHECK(read_buffer_ != NULL);  // we need to have a buffer to fill
-  DCHECK_GT(read_buffer_len_, 0);  // sanity check
-  DCHECK_LT(read_buffer_len_, 1000000);  // sanity check
+  DCHECK(filtered_read_buffer_ != NULL);  // we need to have a buffer to fill
+  DCHECK_GT(filtered_read_buffer_len_, 0);  // sanity check
+  DCHECK_LT(filtered_read_buffer_len_, 1000000);  // sanity check
+  DCHECK(raw_read_buffer_ == NULL);  // there should be no raw read buffer yet
 
   bool rv = false;
   *bytes_read = 0;
@@ -254,10 +321,11 @@
   if ((filter_->stream_data_len() || filter_needs_more_output_space_)
       && !is_done()) {
     // Get filtered data.
-    int filtered_data_len = read_buffer_len_;
+    int filtered_data_len = filtered_read_buffer_len_;
     Filter::FilterStatus status;
     int output_buffer_size = filtered_data_len;
-    status = filter_->ReadData(read_buffer_->data(), &filtered_data_len);
+    status = filter_->ReadData(filtered_read_buffer_->data(),
+                               &filtered_data_len);
 
     if (filter_needs_more_output_space_ && 0 == filtered_data_len) {
       // filter_needs_more_output_space_ was mistaken... there are no more bytes
@@ -321,10 +389,29 @@
 
   if (rv) {
     // When we successfully finished a read, we no longer need to
-    // save the caller's buffers.  For debugging purposes, we clear
-    // them out.
-    read_buffer_ = NULL;
-    read_buffer_len_ = 0;
+    // save the caller's buffers. Release our reference.
+    filtered_read_buffer_ = NULL;
+    filtered_read_buffer_len_ = 0;
+  }
+  return rv;
+}
+
+bool URLRequestJob::ReadRawDataHelper(net::IOBuffer* buf, int buf_size,
+                                      int* bytes_read) {
+  DCHECK(!request_->status().is_io_pending());
+  DCHECK(raw_read_buffer_ == NULL);
+
+  // Keep a pointer to the read buffer, so we have access to it in the
+  // OnRawReadComplete() callback in the event that the read completes
+  // asynchronously.
+  raw_read_buffer_ = buf;
+  bool rv = ReadRawData(buf, buf_size, bytes_read);
+
+  if (!request_->status().is_io_pending()) {
+    // If the read completes synchronously, either success or failure,
+    // invoke the OnRawReadComplete callback so we can account for the
+    // completed read.
+    OnRawReadComplete(*bytes_read);
   }
   return rv;
 }
@@ -412,14 +499,28 @@
   }
 
   has_handled_response_ = true;
-  if (request_->status().is_success())
+  if (request_->status().is_success()) {
     SetupFilter();
 
+    // Check if this content appears to be compressible.
+    std::string mime_type;
+    if (GetMimeType(&mime_type) &&
+        (net::IsSupportedJavascriptMimeType(mime_type.c_str()) ||
+        net::IsSupportedNonImageMimeType(mime_type.c_str()))) {
+      is_compressible_content_ = true;
+    }
+  }
+
   if (!filter_.get()) {
     std::string content_length;
     request_->GetResponseHeaderByName("content-length", &content_length);
     if (!content_length.empty())
       expected_content_size_ = StringToInt64(content_length);
+  } else {
+    // Chrome today only sends "Accept-Encoding" for compression schemes.
+    // So, if there is a filter on the response, we know that the content
+    // was compressed.
+    is_compressed_ = true;
   }
 
   request_->ResponseStarted();
@@ -445,8 +546,7 @@
   // The headers should be complete before reads complete
   DCHECK(has_handled_response_);
 
-  if (bytes_read > 0)
-    RecordBytesRead(bytes_read);
+  OnRawReadComplete(bytes_read);
 
   // Don't notify if we had an error.
   if (!request_->status().is_success())
@@ -459,15 +559,19 @@
   // survival until we can get out of this method.
   scoped_refptr<URLRequestJob> self_preservation = this;
 
+  prefilter_bytes_read_ += bytes_read;
   if (filter_.get()) {
     // Tell the filter that it has more data
     FilteredDataRead(bytes_read);
 
     // Filter the data.
     int filter_bytes_read = 0;
-    if (ReadFilteredData(&filter_bytes_read))
+    if (ReadFilteredData(&filter_bytes_read)) {
+      postfilter_bytes_read_ += filter_bytes_read;
       request_->delegate()->OnReadCompleted(request_, filter_bytes_read);
+    }
   } else {
+    postfilter_bytes_read_ += bytes_read;
     request_->delegate()->OnReadCompleted(request_, bytes_read);
   }
 }
@@ -478,6 +582,8 @@
     return;
   done_ = true;
 
+  RecordCompressionHistograms();
+
   if (is_profiling() && metrics_->total_bytes_read_ > 0) {
     // There are valid IO statistics. Fill in other fields of metrics for
     // profiling consumers to retrieve information.
@@ -553,6 +659,14 @@
     return filter_.get() && filter_->stream_data_len();
 }
 
+void URLRequestJob::OnRawReadComplete(int bytes_read) {
+  DCHECK(raw_read_buffer_);
+  if (bytes_read > 0) {
+    RecordBytesRead(bytes_read);
+  }
+  raw_read_buffer_ = NULL;
+}
+
 void URLRequestJob::RecordBytesRead(int bytes_read) {
   if (is_profiling()) {
     ++(metrics_->number_of_read_IO_);
@@ -560,7 +674,8 @@
   }
   filter_input_byte_count_ += bytes_read;
   UpdatePacketReadTimes();  // Facilitate stats recording if it is active.
-  g_url_request_job_tracker.OnBytesRead(this, bytes_read);
+  g_url_request_job_tracker.OnBytesRead(this, raw_read_buffer_->data(),
+                                        bytes_read);
 }
 
 const URLRequestStatus URLRequestJob::GetStatus() {
@@ -743,3 +858,74 @@
       return;
   }
 }
+
+// The common type of histogram we use for all compression-tracking histograms.
+#define COMPRESSION_HISTOGRAM(name, sample) \
+    do { \
+      UMA_HISTOGRAM_CUSTOM_COUNTS("Net.Compress." name, sample, \
+                                  500, 1000000, 100); \
+    } while(0)
+
+void URLRequestJob::RecordCompressionHistograms() {
+  if (IsCachedContent() ||          // Don't record cached content
+      !GetStatus().is_success() ||  // Don't record failed content
+      !is_compressible_content_ ||  // Only record compressible content
+      !prefilter_bytes_read_)       // Zero-byte responses aren't useful.
+    return;
+
+  // Miniature requests aren't really compressible.  Don't count them.
+  const int kMinSize = 16;
+  if (prefilter_bytes_read_ < kMinSize)
+    return;
+
+  // Only record for http or https urls.
+  bool is_http = request_->url().SchemeIs("http");
+  bool is_https = request_->url().SchemeIs("https");
+  if (!is_http && !is_https)
+    return;
+
+  const net::HttpResponseInfo& response = request_->response_info_;
+  int compressed_B = prefilter_bytes_read_;
+  int decompressed_B = postfilter_bytes_read_;
+
+  // We want to record how often downloaded resources are compressed.
+  // But, we recognize that different protocols may have different
+  // properties.  So, for each request, we'll put it into one of 3
+  // groups:
+  //      a) SSL resources
+  //         Proxies cannot tamper with compression headers with SSL.
+  //      b) Non-SSL, loaded-via-proxy resources
+  //         In this case, we know a proxy might have interfered.
+  //      c) Non-SSL, loaded-without-proxy resources
+  //         In this case, we know there was no explicit proxy.  However,
+  //         it is possible that a transparent proxy was still interfering.
+  //
+  // For each group, we record the same 3 histograms.
+
+  if (is_https) {
+    if (is_compressed_) {
+      COMPRESSION_HISTOGRAM("SSL.BytesBeforeCompression", compressed_B);
+      COMPRESSION_HISTOGRAM("SSL.BytesAfterCompression", decompressed_B);
+    } else {
+      COMPRESSION_HISTOGRAM("SSL.ShouldHaveBeenCompressed", decompressed_B);
+    }
+    return;
+  }
+
+  if (response.was_fetched_via_proxy) {
+    if (is_compressed_) {
+      COMPRESSION_HISTOGRAM("Proxy.BytesBeforeCompression", compressed_B);
+      COMPRESSION_HISTOGRAM("Proxy.BytesAfterCompression", decompressed_B);
+    } else {
+      COMPRESSION_HISTOGRAM("Proxy.ShouldHaveBeenCompressed", decompressed_B);
+    }
+    return;
+  }
+
+  if (is_compressed_) {
+    COMPRESSION_HISTOGRAM("NoProxy.BytesBeforeCompression", compressed_B);
+    COMPRESSION_HISTOGRAM("NoProxy.BytesAfterCompression", decompressed_B);
+  } else {
+    COMPRESSION_HISTOGRAM("NoProxy.ShouldHaveBeenCompressed", decompressed_B);
+  }
+}
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index 2aeb8ff..da6fc16 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -17,6 +17,7 @@
 
 namespace net {
 class AuthChallengeInfo;
+class HttpRequestHeaders;
 class HttpResponseInfo;
 class IOBuffer;
 class UploadData;
@@ -50,10 +51,10 @@
 
   // Sets the upload data, most requests have no upload data, so this is a NOP.
   // Job types supporting upload data will override this.
-  virtual void SetUpload(net::UploadData* upload) { }
+  virtual void SetUpload(net::UploadData* upload);
 
   // Sets extra request headers for Job types that support request headers.
-  virtual void SetExtraRequestHeaders(const std::string& headers) { }
+  virtual void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers);
 
   // If any error occurs while starting the Job, NotifyStartError should be
   // called.
@@ -90,29 +91,31 @@
   // bytes read, 0 when there is no more data, or -1 if there was an error.
   // This is just the backend for URLRequest::Read, see that function for more
   // info.
-  bool Read(net::IOBuffer* buf, int buf_size, int *bytes_read);
+  bool Read(net::IOBuffer* buf, int buf_size, int* bytes_read);
+
+  // Stops further caching of this request, if any. For more info, see
+  // URLRequest::StopCaching().
+  virtual void StopCaching();
 
   // Called to fetch the current load state for the job.
-  virtual net::LoadState GetLoadState() const { return net::LOAD_STATE_IDLE; }
+  virtual net::LoadState GetLoadState() const;
 
   // Called to get the upload progress in bytes.
-  virtual uint64 GetUploadProgress() const { return 0; }
+  virtual uint64 GetUploadProgress() const;
 
   // Called to fetch the charset for this request.  Only makes sense for some
   // types of requests. Returns true on success.  Calling this on a type that
   // doesn't have a charset will return false.
-  virtual bool GetCharset(std::string* charset) { return false; }
+  virtual bool GetCharset(std::string* charset);
 
   // Called to get response info.
-  virtual void GetResponseInfo(net::HttpResponseInfo* info) {}
+  virtual void GetResponseInfo(net::HttpResponseInfo* info);
 
   // Returns the cookie values included in the response, if applicable.
   // Returns true if applicable.
   // NOTE: This removes the cookies from the job, so it will only return
   //       useful results once per job.
-  virtual bool GetResponseCookies(std::vector<std::string>* cookies) {
-    return false;
-  }
+  virtual bool GetResponseCookies(std::vector<std::string>* cookies);
 
   // Called to fetch the encoding types for this request. Only makes sense for
   // some types of requests. Returns true on success. Calling this on a request
@@ -125,16 +128,14 @@
   // in the reverse order (in the above example, ungzip first, and then sdch
   // expand).
   virtual bool GetContentEncodings(
-      std::vector<Filter::FilterType>* encoding_types) {
-    return false;
-  }
+      std::vector<Filter::FilterType>* encoding_types);
 
   // Find out if this is a download.
   virtual bool IsDownload() const;
 
   // Find out if this is a response to a request that advertised an SDCH
   // dictionary.  Only makes sense for some types of requests.
-  virtual bool IsSdchResponse() const { return false; }
+  virtual bool IsSdchResponse() const;
 
   // Called to setup stream filter for this request. An example of filter is
   // content encoding/decoding.
@@ -157,14 +158,12 @@
   // location.  This may be used to implement protocol-specific restrictions.
   // If this function returns false, then the URLRequest will fail reporting
   // net::ERR_UNSAFE_REDIRECT.
-  virtual bool IsSafeRedirect(const GURL& location) {
-    return true;
-  }
+  virtual bool IsSafeRedirect(const GURL& location);
 
   // Called to determine if this response is asking for authentication.  Only
   // makes sense for some types of requests.  The caller is responsible for
   // obtaining the credentials passing them to SetAuth.
-  virtual bool NeedsAuth() { return false; }
+  virtual bool NeedsAuth();
 
   // Fills the authentication info with the server's response.
   virtual void GetAuthChallengeInfo(
@@ -207,13 +206,13 @@
 
   // FilterContext methods:
   // These methods are not applicable to all connections.
-  virtual bool GetMimeType(std::string* mime_type) const { return false; }
+  virtual bool GetMimeType(std::string* mime_type) const;
   virtual bool GetURL(GURL* gurl) const;
   virtual base::Time GetRequestTime() const;
-  virtual bool IsCachedContent() const { return false; }
+  virtual bool IsCachedContent() const;
   virtual int64 GetByteReadCount() const;
-  virtual int GetResponseCode() const { return -1; }
-  virtual int GetInputStreamBufferSize() const { return kFilterBufSize; }
+  virtual int GetResponseCode() const;
+  virtual int GetInputStreamBufferSize() const;
   virtual void RecordPacketStats(StatisticSelector statistic) const;
 
  protected:
@@ -294,6 +293,15 @@
   // Contains IO performance measurement when profiling is enabled.
   scoped_ptr<URLRequestJobMetrics> metrics_;
 
+  // The number of bytes read before passing to the filter.
+  int prefilter_bytes_read_;
+  // The number of bytes read after passing through the filter.
+  int postfilter_bytes_read_;
+  // True when (we believe) the content in this URLRequest was compressible.
+  bool is_compressible_content_;
+  // True when the content in this URLRequest was compressed.
+  bool is_compressed_;
+
  private:
   // Size of filter input buffers used by this class.
   static const int kFilterBufSize;
@@ -303,13 +311,23 @@
   // an error occurred (or we are waiting for IO to complete).
   bool ReadRawDataForFilter(int *bytes_read);
 
+  // Invokes ReadRawData and records bytes read if the read completes
+  // synchronously.
+  bool ReadRawDataHelper(net::IOBuffer* buf, int buf_size, int* bytes_read);
+
   // Called in response to a redirect that was not canceled to follow the
   // redirect. The current job will be replaced with a new job loading the
   // given redirect destination.
   void FollowRedirect(const GURL& location, int http_status_code);
 
-  // Updates the profiling info and notifies observers that bytes_read bytes
-  // have been read.
+  // Called after every raw read. If |bytes_read| is > 0, this indicates
+  // a successful read of |bytes_read| unfiltered bytes. If |bytes_read|
+  // is 0, this indicates that there is no additional data to read. If
+  // |bytes_read| is < 0, an error occurred and no bytes were read.
+  void OnRawReadComplete(int bytes_read);
+
+  // Updates the profiling info and notifies observers that an additional
+  // |bytes_read| unfiltered bytes have been read for this job.
   void RecordBytesRead(int bytes_read);
 
   // Called to query whether there is data available in the filter to be read
@@ -319,6 +337,8 @@
   // Record packet arrival times for possible use in histograms.
   void UpdatePacketReadTimes();
 
+  void RecordCompressionHistograms();
+
   // Indicates that the job is done producing data, either it has completed
   // all the data or an error has been encountered. Set exclusively by
   // NotifyDone so that it is kept in sync with the request.
@@ -338,8 +358,12 @@
   // processing the filtered data, we return the data in the caller's buffer.
   // While the async IO is in progress, we save the user buffer here, and
   // when the IO completes, we fill this in.
-  net::IOBuffer *read_buffer_;
-  int read_buffer_len_;
+  scoped_refptr<net::IOBuffer> filtered_read_buffer_;
+  int filtered_read_buffer_len_;
+
+  // We keep a pointer to the read buffer while asynchronous reads are
+  // in progress, so we are able to pass those bytes to job observers.
+  scoped_refptr<net::IOBuffer> raw_read_buffer_;
 
   // Used by HandleResponseIfNecessary to track whether we've sent the
   // OnResponseStarted callback and potentially redirect callbacks as well.
@@ -366,7 +390,7 @@
   // as gathered here is post-SSL, and post-cache-fetch, and does not reflect
   // true packet arrival times in such cases.
 
-  // Total number of bytes read from network (or cache) and and typically handed
+  // Total number of bytes read from network (or cache) and typically handed
   // to filter to process.  Used to histogram compression ratios, and error
   // recovery scenarios in filters.
   int64 filter_input_byte_count_;
diff --git a/net/url_request/url_request_job_manager.cc b/net/url_request/url_request_job_manager.cc
index 7cd934e..0615280 100644
--- a/net/url_request/url_request_job_manager.cc
+++ b/net/url_request/url_request_job_manager.cc
@@ -14,7 +14,7 @@
 #include "net/url_request/url_request_data_job.h"
 #include "net/url_request/url_request_error_job.h"
 #include "net/url_request/url_request_file_job.h"
-#include "net/url_request/url_request_new_ftp_job.h"
+#include "net/url_request/url_request_ftp_job.h"
 #include "net/url_request/url_request_http_job.h"
 
 // The built-in set of protocol factories
@@ -31,7 +31,7 @@
   { "http", URLRequestHttpJob::Factory },
   { "https", URLRequestHttpJob::Factory },
   { "file", URLRequestFileJob::Factory },
-  { "ftp", URLRequestNewFtpJob::Factory },
+  { "ftp", URLRequestFtpJob::Factory },
   { "about", URLRequestAboutJob::Factory },
   { "data", URLRequestDataJob::Factory },
 };
diff --git a/net/url_request/url_request_job_manager.h b/net/url_request/url_request_job_manager.h
index 9930e17..d8934e6 100644
--- a/net/url_request/url_request_job_manager.h
+++ b/net/url_request/url_request_job_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -97,7 +97,7 @@
   }
 #endif
 
-  DISALLOW_EVIL_CONSTRUCTORS(URLRequestJobManager);
+  DISALLOW_COPY_AND_ASSIGN(URLRequestJobManager);
 };
 
 #endif  // NET_URL_REQUEST_URL_REQUEST_JOB_MANAGER_H__
diff --git a/net/url_request/url_request_job_metrics.cc b/net/url_request/url_request_job_metrics.cc
index eea21fa..e0726da 100644
--- a/net/url_request/url_request_job_metrics.cc
+++ b/net/url_request/url_request_job_metrics.cc
@@ -6,6 +6,7 @@
 
 #include "base/basictypes.h"
 #include "base/string_util.h"
+#include "base/utf_string_conversions.h"
 
 using base::TimeDelta;
 
diff --git a/net/url_request/url_request_job_tracker.cc b/net/url_request/url_request_job_tracker.cc
index 1f5b33c..e3e5d36 100644
--- a/net/url_request/url_request_job_tracker.cc
+++ b/net/url_request/url_request_job_tracker.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -51,7 +51,8 @@
 }
 
 void URLRequestJobTracker::OnBytesRead(URLRequestJob* job,
+                                       const char* buf,
                                        int byte_count) {
   FOR_EACH_OBSERVER(JobObserver, observers_,
-                    OnBytesRead(job, byte_count));
+                    OnBytesRead(job, buf, byte_count));
 }
diff --git a/net/url_request/url_request_job_tracker.h b/net/url_request/url_request_job_tracker.h
index 5f12fc7..e03b71f 100644
--- a/net/url_request/url_request_job_tracker.h
+++ b/net/url_request/url_request_job_tracker.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -43,9 +43,13 @@
     virtual void OnJobRedirect(URLRequestJob* job, const GURL& location,
                                int status_code) = 0;
 
-    // Called when a new chunk of bytes has been read for the given job. The
-    // byte count is the number of bytes for that read event only.
-    virtual void OnBytesRead(URLRequestJob* job, int byte_count) = 0;
+    // Called when a new chunk of unfiltered bytes has been read for
+    // the given job. |byte_count| is the number of bytes for that
+    // read event only. |buf| is a pointer to the data buffer that
+    // contains those bytes. The data in |buf| is only valid for the
+    // duration of the OnBytesRead callback.
+    virtual void OnBytesRead(URLRequestJob* job, const char* buf,
+                             int byte_count) = 0;
 
     virtual ~JobObserver() {}
   };
@@ -74,7 +78,7 @@
                      int status_code);
 
   // Bytes read notifications.
-  void OnBytesRead(URLRequestJob* job, int byte_count);
+  void OnBytesRead(URLRequestJob* job, const char* buf, int byte_count);
 
   // allows iteration over all active jobs
   JobIterator begin() const {
diff --git a/net/url_request/url_request_job_tracker_unittest.cc b/net/url_request/url_request_job_tracker_unittest.cc
new file mode 100644
index 0000000..3ddbcc2
--- /dev/null
+++ b/net/url_request/url_request_job_tracker_unittest.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2010 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.h>
+#include <algorithm>
+#include <string>
+#include <vector>
+#include "base/message_loop.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/filter.h"
+#include "net/base/io_buffer.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_job_tracker.h"
+#include "net/url_request/url_request_status.h"
+#include "net/url_request/url_request_unittest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+using testing::Eq;
+using testing::InSequence;
+using testing::NotNull;
+using testing::StrictMock;
+
+namespace {
+
+const char kBasic[] = "Hello\n";
+
+// The above string "Hello\n", gzip compressed.
+const unsigned char kCompressed[] = {
+  0x1f, 0x8b, 0x08, 0x08, 0x38, 0x18, 0x2e, 0x4c, 0x00, 0x03, 0x63,
+  0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x2e, 0x68,
+  0x74, 0x6d, 0x6c, 0x00, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0xe7, 0x02,
+  0x00, 0x16, 0x35, 0x96, 0x31, 0x06, 0x00, 0x00, 0x00
+};
+
+bool GetResponseBody(const GURL& url, std::string* out_body) {
+  if (url.spec() == "test:basic") {
+    *out_body = kBasic;
+  } else if (url.spec() == "test:compressed") {
+    out_body->assign(reinterpret_cast<const char*>(kCompressed),
+                     sizeof(kCompressed));
+  } else {
+    return false;
+  }
+
+  return true;
+}
+
+class MockJobObserver : public URLRequestJobTracker::JobObserver {
+ public:
+  MOCK_METHOD1(OnJobAdded, void(URLRequestJob* job));
+  MOCK_METHOD1(OnJobRemoved, void(URLRequestJob* job));
+  MOCK_METHOD2(OnJobDone, void(URLRequestJob* job,
+                               const URLRequestStatus& status));
+  MOCK_METHOD3(OnJobRedirect, void(URLRequestJob* job,
+                                   const GURL& location,
+                                   int status_code));
+  MOCK_METHOD3(OnBytesRead, void(URLRequestJob* job,
+                                 const char* buf,
+                                 int byte_count));
+};
+
+// A URLRequestJob that returns static content for given URLs. We do
+// not use URLRequestTestJob here because URLRequestTestJob fakes
+// async operations by calling ReadRawData synchronously in an async
+// callback. This test requires a URLRequestJob that returns false for
+// async reads, in order to exercise the real async read codepath.
+class URLRequestJobTrackerTestJob : public URLRequestJob {
+ public:
+  URLRequestJobTrackerTestJob(URLRequest* request, bool async_reads)
+      : URLRequestJob(request), async_reads_(async_reads) {}
+
+  void Start() {
+    ASSERT_TRUE(GetResponseBody(request_->url(), &response_data_));
+
+    // Start reading asynchronously so that all error reporting and data
+    // callbacks happen as they would for network requests.
+    MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+        this, &URLRequestJobTrackerTestJob::NotifyHeadersComplete));
+  }
+
+  bool ReadRawData(net::IOBuffer* buf, int buf_size,
+                   int *bytes_read) {
+    const size_t bytes_to_read = std::min(
+        response_data_.size(), static_cast<size_t>(buf_size));
+
+    // Regardless of whether we're performing a sync or async read,
+    // copy the data into the caller's buffer now. That way we don't
+    // have to hold on to the buffers in the async case.
+    memcpy(buf->data(), response_data_.data(), bytes_to_read);
+    response_data_.erase(0, bytes_to_read);
+
+    if (async_reads_) {
+      SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
+      MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+          this, &URLRequestJobTrackerTestJob::OnReadCompleted,
+          bytes_to_read));
+    } else {
+      SetStatus(URLRequestStatus());
+      *bytes_read = bytes_to_read;
+    }
+    return !async_reads_;
+  }
+
+  void OnReadCompleted(int status) {
+    if (status == 0) {
+      NotifyDone(URLRequestStatus());
+    } else if (status > 0) {
+      SetStatus(URLRequestStatus());
+    } else {
+      ASSERT_FALSE(true) << "Unexpected OnReadCompleted callback.";
+    }
+
+    NotifyReadComplete(status);
+  }
+
+  bool GetContentEncodings(
+      std::vector<Filter::FilterType>* encoding_types) {
+    if (request_->url().spec() == "test:basic") {
+      return false;
+    } else if (request_->url().spec() == "test:compressed") {
+      encoding_types->push_back(Filter::FILTER_TYPE_GZIP);
+      return true;
+    } else {
+      return URLRequestJob::GetContentEncodings(encoding_types);
+    }
+  }
+
+  // The data to send, will be set in Start().
+  std::string response_data_;
+
+  // Should reads be synchronous or asynchronous?
+  const bool async_reads_;
+};
+
+// Google Mock Matcher to check two URLRequestStatus instances for
+// equality.
+MATCHER_P(StatusEq, other, "") {
+  return (arg.status() == other.status() &&
+          arg.os_error() == other.os_error());
+}
+
+// Google Mock Matcher to check that two blocks of memory are equal.
+MATCHER_P2(MemEq, other, len, "") {
+  return memcmp(arg, other, len) == 0;
+}
+
+class URLRequestJobTrackerTest : public PlatformTest {
+ protected:
+  static void SetUpTestCase() {
+    URLRequest::RegisterProtocolFactory("test", &Factory);
+  }
+
+  virtual void SetUp() {
+    g_async_reads = true;
+  }
+
+  void AssertJobTrackerCallbacks(const char* url) {
+    InSequence seq;
+    testing::StrictMock<MockJobObserver> observer;
+
+    const GURL gurl(url);
+    std::string body;
+    ASSERT_TRUE(GetResponseBody(gurl, &body));
+
+    // We expect to receive one call for each method on the JobObserver,
+    // in the following order:
+    EXPECT_CALL(observer, OnJobAdded(NotNull()));
+    EXPECT_CALL(observer, OnBytesRead(NotNull(),
+                                      MemEq(body.data(), body.size()),
+                                      Eq(static_cast<int>(body.size()))));
+    EXPECT_CALL(observer, OnJobDone(NotNull(), StatusEq(URLRequestStatus())));
+    EXPECT_CALL(observer, OnJobRemoved(NotNull()));
+
+    // Attach our observer and perform the resource fetch.
+    g_url_request_job_tracker.AddObserver(&observer);
+    Fetch(gurl);
+    g_url_request_job_tracker.RemoveObserver(&observer);
+  }
+
+  void Fetch(const GURL& url) {
+    TestDelegate d;
+    {
+      URLRequest request(url, &d);
+      request.Start();
+      MessageLoop::current()->RunAllPending();
+    }
+
+    // A few sanity checks to make sure that the delegate also
+    // receives the expected callbacks.
+    EXPECT_EQ(1, d.response_started_count());
+    EXPECT_FALSE(d.received_data_before_response());
+    EXPECT_STREQ(kBasic, d.data_received().c_str());
+  }
+
+  static URLRequest::ProtocolFactory Factory;
+  static bool g_async_reads;
+};
+
+// static
+URLRequestJob* URLRequestJobTrackerTest::Factory(URLRequest* request,
+                                                 const std::string& scheme) {
+  return new URLRequestJobTrackerTestJob(request, g_async_reads);
+}
+
+// static
+bool URLRequestJobTrackerTest::g_async_reads = true;
+
+TEST_F(URLRequestJobTrackerTest, BasicAsync) {
+  g_async_reads = true;
+  AssertJobTrackerCallbacks("test:basic");
+}
+
+TEST_F(URLRequestJobTrackerTest, BasicSync) {
+  g_async_reads = false;
+  AssertJobTrackerCallbacks("test:basic");
+}
+
+TEST_F(URLRequestJobTrackerTest, CompressedAsync) {
+  g_async_reads = true;
+  AssertJobTrackerCallbacks("test:compressed");
+}
+
+TEST_F(URLRequestJobTrackerTest, CompressedSync) {
+  g_async_reads = false;
+  AssertJobTrackerCallbacks("test:compressed");
+}
+
+}  // namespace
diff --git a/net/url_request/url_request_netlog_params.cc b/net/url_request/url_request_netlog_params.cc
new file mode 100644
index 0000000..3693ee9
--- /dev/null
+++ b/net/url_request/url_request_netlog_params.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2010 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/url_request/url_request_netlog_params.h"
+
+#include "base/values.h"
+
+URLRequestStartEventParameters::URLRequestStartEventParameters(
+    const GURL& url,
+    const std::string& method,
+    int load_flags,
+    net::RequestPriority priority)
+    : url_(url),
+      method_(method),
+      load_flags_(load_flags),
+      priority_(priority) {
+}
+
+Value* URLRequestStartEventParameters::ToValue() const {
+  DictionaryValue* dict = new DictionaryValue();
+  dict->SetString(L"url", url_.possibly_invalid_spec());
+  dict->SetString(L"method", method_);
+  dict->SetInteger(L"load_flags", load_flags_);
+  dict->SetInteger(L"priority", static_cast<int>(priority_));
+  return dict;
+}
diff --git a/net/url_request/url_request_netlog_params.h b/net/url_request/url_request_netlog_params.h
new file mode 100644
index 0000000..d84052a
--- /dev/null
+++ b/net/url_request/url_request_netlog_params.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2010 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_URL_REQUEST_URL_REQUEST_NETLOG_PARAMS_H_
+#define NET_URL_REQUEST_URL_REQUEST_NETLOG_PARAMS_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_log.h"
+#include "net/base/request_priority.h"
+
+// Holds the parameters to emit to the NetLog when starting a URLRequest.
+class URLRequestStartEventParameters : public net::NetLog::EventParameters {
+ public:
+  URLRequestStartEventParameters(const GURL& url,
+                                 const std::string& method,
+                                 int load_flags,
+                                 net::RequestPriority priority);
+
+  const GURL& url() const {
+    return url_;
+  }
+
+  int load_flags() const {
+    return load_flags_;
+  }
+
+  virtual Value* ToValue() const;
+
+ private:
+  const GURL url_;
+  const std::string method_;
+  const int load_flags_;
+  const net::RequestPriority priority_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLRequestStartEventParameters);
+};
+
+#endif  // NET_URL_REQUEST_URL_REQUEST_NETLOG_PARAMS_H_
diff --git a/net/url_request/url_request_simple_job.h b/net/url_request/url_request_simple_job.h
index 786d2e4..4ea856c 100644
--- a/net/url_request/url_request_simple_job.h
+++ b/net/url_request/url_request_simple_job.h
@@ -28,9 +28,10 @@
                        std::string* charset,
                        std::string* data) const = 0;
 
- private:
+ protected:
   void StartAsync();
 
+ private:
   std::string mime_type_;
   std::string charset_;
   std::string data_;
diff --git a/net/url_request/url_request_test_job.cc b/net/url_request/url_request_test_job.cc
index b1e3b5c..363d178 100644
--- a/net/url_request/url_request_test_job.cc
+++ b/net/url_request/url_request_test_job.cc
@@ -183,6 +183,12 @@
     info->headers = response_headers_;
 }
 
+int URLRequestTestJob::GetResponseCode() const {
+  if (response_headers_)
+    return response_headers_->response_code();
+  return -1;
+}
+
 bool URLRequestTestJob::IsRedirectResponse(GURL* location,
                                            int* http_status_code) {
   if (!response_headers_)
diff --git a/net/url_request/url_request_test_job.h b/net/url_request/url_request_test_job.h
index 4daabf6..f618c07 100644
--- a/net/url_request/url_request_test_job.h
+++ b/net/url_request/url_request_test_job.h
@@ -93,6 +93,7 @@
   virtual void Kill();
   virtual bool GetMimeType(std::string* mime_type) const;
   virtual void GetResponseInfo(net::HttpResponseInfo* info);
+  virtual int GetResponseCode() const;
   virtual bool IsRedirectResponse(GURL* location, int* http_status_code);
 
  protected:
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index a94158a..bd4e56a 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -26,8 +26,8 @@
 #include "net/base/cookie_monster.h"
 #include "net/base/cookie_policy.h"
 #include "net/base/load_flags.h"
-#include "net/base/load_log.h"
-#include "net/base/load_log_unittest.h"
+#include "net/base/net_log.h"
+#include "net/base/net_log_unittest.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_module.h"
 #include "net/base/net_util.h"
@@ -36,9 +36,10 @@
 #include "net/ftp/ftp_network_layer.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_network_layer.h"
+#include "net/http/http_request_headers.h"
 #include "net/http/http_response_headers.h"
 #include "net/proxy/proxy_service.h"
-#include "net/socket/ssl_test_util.h"
+#include "net/test/test_server.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_file_dir_job.h"
 #include "net/url_request/url_request_http_job.h"
@@ -122,7 +123,7 @@
     }
     uploadBytes[kMsgSize] = '\0';
 
-    scoped_refptr<URLRequestContext> context = new URLRequestTestContext();
+    scoped_refptr<URLRequestContext> context = new TestURLRequestContext();
 
     for (int i = 0; i < kIterations; ++i) {
       TestDelegate d;
@@ -217,16 +218,7 @@
     EXPECT_FALSE(d.received_data_before_response());
     EXPECT_NE(0, d.bytes_received());
 
-    // The first part of the log will be for URL_REQUEST_START.
-    // After that, there should be an HTTP_TRANSACTION_READ_BODY
-    EXPECT_TRUE(net::LogContainsBeginEvent(
-        *r.load_log(), 0, net::LoadLog::TYPE_URL_REQUEST_START));
-    EXPECT_TRUE(net::LogContainsEndEvent(
-        *r.load_log(), -3, net::LoadLog::TYPE_URL_REQUEST_START));
-    EXPECT_TRUE(net::LogContainsBeginEvent(
-        *r.load_log(), -2, net::LoadLog::TYPE_HTTP_TRANSACTION_READ_BODY));
-    EXPECT_TRUE(net::LogContainsEndEvent(
-        *r.load_log(), -1, net::LoadLog::TYPE_HTTP_TRANSACTION_READ_BODY));
+    // TODO(eroman): Add back the NetLog tests...
   }
 }
 
@@ -283,42 +275,8 @@
 class HTTPSRequestTest : public testing::Test {
 };
 
-#if defined(OS_MACOSX)
-// Status 6/19/09:
-//
-// If these tests are enabled on OSX, the first one (HTTPSGetTest)
-// will fail.  I didn't track it down explicitly, but did observe that
-// the testserver.py kills itself (e.g. "process_util_posix.cc(84)]
-// Unable to terminate process.").  tlslite and testserver.py are hard
-// to debug (redirection of stdout/stderr to a file so you can't see
-// errors; lots of naked "except:" statements, etc), but I did track
-// down an SSL auth failure as one cause of it deciding to die
-// silently.
-//
-// The next test, HTTPSMismatchedTest, will look like it hangs by
-// looping over calls to SSLHandshake() (Security framework call) as
-// called from SSLClientSocketMac::DoHandshake().  Return values are a
-// repeating pattern of -9803 (come back later) and -9812 (cert valid
-// but root not trusted).  If you don't have the cert in your keychain
-// as documented on http://dev.chromium.org/developers/testing, the
-// -9812 becomes a -9813 (no root cert).  Interestingly, the handshake
-// also appears to be a failure point for other disabled tests, such
-// as (SSLClientSocketTest,Connect) in
-// net/base/ssl_client_socket_unittest.cc.
-//
-// Old comment (not sure if obsolete):
-// ssl_client_socket_mac.cc crashes currently in GetSSLInfo
-// when called on a connection with an unrecognized certificate
-#define MAYBE_HTTPSGetTest        DISABLED_HTTPSGetTest
-#define MAYBE_HTTPSMismatchedTest DISABLED_HTTPSMismatchedTest
-#define MAYBE_HTTPSExpiredTest    DISABLED_HTTPSExpiredTest
-#else
-#define MAYBE_HTTPSGetTest        HTTPSGetTest
-#define MAYBE_HTTPSMismatchedTest HTTPSMismatchedTest
-#define MAYBE_HTTPSExpiredTest    HTTPSExpiredTest
-#endif
 
-TEST_F(HTTPSRequestTest, MAYBE_HTTPSGetTest) {
+TEST_F(HTTPSRequestTest, HTTPSGetTest) {
   // Note: tools/testserver/testserver.py does not need
   // a working document root to server the pages / and /hello.html,
   // so this test doesn't really need to specify a document root.
@@ -342,7 +300,7 @@
   }
 }
 
-TEST_F(HTTPSRequestTest, MAYBE_HTTPSMismatchedTest) {
+TEST_F(HTTPSRequestTest, HTTPSMismatchedTest) {
   scoped_refptr<HTTPSTestServer> server =
       HTTPSTestServer::CreateMismatchedServer(L"net/data/ssl");
   ASSERT_TRUE(NULL != server.get());
@@ -370,7 +328,7 @@
   }
 }
 
-TEST_F(HTTPSRequestTest, MAYBE_HTTPSExpiredTest) {
+TEST_F(HTTPSRequestTest, HTTPSExpiredTest) {
   scoped_refptr<HTTPSTestServer> server =
       HTTPSTestServer::CreateExpiredServer(L"net/data/ssl");
   ASSERT_TRUE(NULL != server.get());
@@ -493,7 +451,7 @@
 
 TEST_F(URLRequestTestHTTP, CancelTest5) {
   ASSERT_TRUE(NULL != server_.get());
-  scoped_refptr<URLRequestContext> context = new URLRequestTestContext();
+  scoped_refptr<URLRequestContext> context = new TestURLRequestContext();
 
   // populate cache
   {
@@ -696,9 +654,11 @@
   {
     TestURLRequest r(temp_url, &d);
 
-    r.SetExtraRequestHeaders(
-        StringPrintf("Range: bytes=%" PRIuS "-%" PRIuS "\n",
-                     first_byte_position, last_byte_position));
+    net::HttpRequestHeaders headers;
+    headers.SetHeader(net::HttpRequestHeaders::kRange,
+                      StringPrintf("bytes=%" PRIuS "-%" PRIuS,
+                                   first_byte_position, last_byte_position));
+    r.SetExtraRequestHeaders(headers);
     r.Start();
     EXPECT_TRUE(r.is_pending());
 
@@ -737,8 +697,11 @@
   {
     TestURLRequest r(temp_url, &d);
 
-    r.SetExtraRequestHeaders(StringPrintf("Range: bytes=%" PRIuS "-\n",
-                                          first_byte_position));
+    net::HttpRequestHeaders headers;
+    headers.SetHeader(net::HttpRequestHeaders::kRange,
+                      StringPrintf("bytes=%" PRIuS "-",
+                                   first_byte_position));
+    r.SetExtraRequestHeaders(headers);
     r.Start();
     EXPECT_TRUE(r.is_pending());
 
@@ -771,7 +734,10 @@
   {
     TestURLRequest r(temp_url, &d);
 
-    r.SetExtraRequestHeaders(StringPrintf("Range: bytes=0-0,10-200,200-300\n"));
+    net::HttpRequestHeaders headers;
+    headers.SetHeader(net::HttpRequestHeaders::kRange,
+                      "bytes=0-0,10-200,200-300");
+    r.SetExtraRequestHeaders(headers);
     r.Start();
     EXPECT_TRUE(r.is_pending());
 
@@ -795,21 +761,6 @@
   }
 }
 
-// This test is disabled because it fails on some computers due to proxies
-// returning a page in response to this request rather than reporting failure.
-TEST_F(URLRequestTest, DISABLED_DnsFailureTest) {
-  TestDelegate d;
-  {
-    URLRequest r(GURL("http://thisisnotavalidurl0123456789foo.com/"), &d);
-
-    r.Start();
-    EXPECT_TRUE(r.is_pending());
-
-    MessageLoop::current()->Run();
-    EXPECT_TRUE(d.request_failed());
-  }
-}
-
 TEST_F(URLRequestTestHTTP, ResponseHeadersTest) {
   ASSERT_TRUE(NULL != server_.get());
   TestDelegate d;
@@ -962,26 +913,29 @@
   path = path.Append(FILE_PATH_LITERAL("url_request_unittest"));
 
   TestDelegate d;
-  d.set_quit_on_redirect(true);
   TestURLRequest req(net::FilePathToFileURL(path), &d);
   req.Start();
   MessageLoop::current()->Run();
 
-  // Let the directory lister have time to finish its work, which will
-  // cause the URLRequestFileDirJob's ref count to drop to 1.
-  URLRequestFileDirJob* job = static_cast<URLRequestFileDirJob*>(req.job());
-  while (!job->list_complete()) {
-    PlatformThread::Sleep(10);
-    MessageLoop::current()->RunAllPending();
-  }
-
-  // Should not crash during this call!
-  req.FollowDeferredRedirect();
-
-  // Flush event queue.
-  MessageLoop::current()->RunAllPending();
+  ASSERT_EQ(1, d.received_redirect_count());
+  ASSERT_LT(0, d.bytes_received());
+  ASSERT_FALSE(d.request_failed());
+  ASSERT_TRUE(req.status().is_success());
 }
 
+#if defined(OS_WIN)
+// Don't accept the url "file:///" on windows. See http://crbug.com/1474.
+TEST_F(URLRequestTest, FileDirRedirectSingleSlash) {
+  TestDelegate d;
+  TestURLRequest req(GURL("file:///"), &d);
+  req.Start();
+  MessageLoop::current()->Run();
+
+  ASSERT_EQ(1, d.received_redirect_count());
+  ASSERT_FALSE(req.status().is_success());
+}
+#endif
+
 TEST_F(URLRequestTestHTTP, RestrictRedirects) {
   ASSERT_TRUE(NULL != server_.get());
 
@@ -1094,14 +1048,16 @@
 TEST_F(URLRequestTestHTTP, VaryHeader) {
   ASSERT_TRUE(NULL != server_.get());
 
-  scoped_refptr<URLRequestContext> context = new URLRequestTestContext();
+  scoped_refptr<URLRequestContext> context = new TestURLRequestContext();
 
   // populate the cache
   {
     TestDelegate d;
     URLRequest req(server_->TestServerPage("echoheader?foo"), &d);
     req.set_context(context);
-    req.SetExtraRequestHeaders("foo:1");
+    net::HttpRequestHeaders headers;
+    headers.SetHeader("foo", "1");
+    req.SetExtraRequestHeaders(headers);
     req.Start();
     MessageLoop::current()->Run();
   }
@@ -1111,7 +1067,9 @@
     TestDelegate d;
     URLRequest req(server_->TestServerPage("echoheader?foo"), &d);
     req.set_context(context);
-    req.SetExtraRequestHeaders("foo:1");
+    net::HttpRequestHeaders headers;
+    headers.SetHeader("foo", "1");
+    req.SetExtraRequestHeaders(headers);
     req.Start();
     MessageLoop::current()->Run();
 
@@ -1123,7 +1081,9 @@
     TestDelegate d;
     URLRequest req(server_->TestServerPage("echoheader?foo"), &d);
     req.set_context(context);
-    req.SetExtraRequestHeaders("foo:2");
+    net::HttpRequestHeaders headers;
+    headers.SetHeader("foo", "2");
+    req.SetExtraRequestHeaders(headers);
     req.Start();
     MessageLoop::current()->Run();
 
@@ -1132,7 +1092,7 @@
 }
 
 TEST_F(URLRequestTestHTTP, BasicAuth) {
-  scoped_refptr<URLRequestContext> context = new URLRequestTestContext();
+  scoped_refptr<URLRequestContext> context = new TestURLRequestContext();
   ASSERT_TRUE(NULL != server_.get());
 
   // populate the cache
@@ -1183,7 +1143,7 @@
   // Request a page that will give a 401 containing a Set-Cookie header.
   // Verify that when the transaction is restarted, it includes the new cookie.
   {
-    scoped_refptr<URLRequestContext> context = new URLRequestTestContext();
+    scoped_refptr<URLRequestContext> context = new TestURLRequestContext();
     TestDelegate d;
     d.set_username(L"user");
     d.set_password(L"secret");
@@ -1204,7 +1164,7 @@
   // Same test as above, except this time the restart is initiated earlier
   // (without user intervention since identity is embedded in the URL).
   {
-    scoped_refptr<URLRequestContext> context = new URLRequestTestContext();
+    scoped_refptr<URLRequestContext> context = new TestURLRequestContext();
     TestDelegate d;
 
     GURL::Replacements replacements;
@@ -1232,7 +1192,7 @@
   scoped_refptr<HTTPTestServer> server =
       HTTPTestServer::CreateServer(L"", NULL);
   ASSERT_TRUE(NULL != server.get());
-  scoped_refptr<URLRequestContext> context = new URLRequestTestContext();
+  scoped_refptr<URLRequestContext> context = new TestURLRequestContext();
 
   // Set up a cookie.
   {
@@ -1241,6 +1201,8 @@
     req.set_context(context);
     req.Start();
     MessageLoop::current()->Run();
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 
   // Verify that the cookie is set.
@@ -1253,6 +1215,8 @@
 
     EXPECT_TRUE(d.data_received().find("CookieToNotSend=1")
                 != std::string::npos);
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 
   // Verify that the cookie isn't sent when LOAD_DO_NOT_SEND_COOKIES is set.
@@ -1266,6 +1230,10 @@
 
     EXPECT_TRUE(d.data_received().find("Cookie: CookieToNotSend=1")
                 == std::string::npos);
+
+    // LOAD_DO_NOT_SEND_COOKIES does not trigger OnGetCookies.
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 }
 
@@ -1273,7 +1241,7 @@
   scoped_refptr<HTTPTestServer> server =
       HTTPTestServer::CreateServer(L"", NULL);
   ASSERT_TRUE(NULL != server.get());
-  scoped_refptr<URLRequestContext> context = new URLRequestTestContext();
+  scoped_refptr<URLRequestContext> context = new TestURLRequestContext();
 
   // Set up a cookie.
   {
@@ -1283,6 +1251,10 @@
     req.set_context(context);
     req.Start();
     MessageLoop::current()->Run();
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
+    EXPECT_EQ(1, d.set_cookie_count());
   }
 
   // Try to set-up another cookie and update the previous cookie.
@@ -1295,6 +1267,11 @@
     req.Start();
 
     MessageLoop::current()->Run();
+
+    // LOAD_DO_NOT_SAVE_COOKIES does not trigger OnSetCookie.
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
+    EXPECT_EQ(0, d.set_cookie_count());
   }
 
   // Verify the cookies weren't saved or updated.
@@ -1309,6 +1286,10 @@
                 == std::string::npos);
     EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2")
                 != std::string::npos);
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
+    EXPECT_EQ(0, d.set_cookie_count());
   }
 }
 
@@ -1316,7 +1297,7 @@
   scoped_refptr<HTTPTestServer> server =
       HTTPTestServer::CreateServer(L"", NULL);
   ASSERT_TRUE(NULL != server.get());
-  scoped_refptr<URLRequestTestContext> context = new URLRequestTestContext();
+  scoped_refptr<TestURLRequestContext> context = new TestURLRequestContext();
 
   // Set up a cookie.
   {
@@ -1325,6 +1306,9 @@
     req.set_context(context);
     req.Start();
     MessageLoop::current()->Run();
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 
   // Verify that the cookie is set.
@@ -1337,6 +1321,9 @@
 
     EXPECT_TRUE(d.data_received().find("CookieToNotSend=1")
                 != std::string::npos);
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 
   // Verify that the cookie isn't sent.
@@ -1354,6 +1341,9 @@
                 == std::string::npos);
 
     context->set_cookie_policy(NULL);
+
+    EXPECT_EQ(1, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 }
 
@@ -1361,7 +1351,7 @@
   scoped_refptr<HTTPTestServer> server =
       HTTPTestServer::CreateServer(L"", NULL);
   ASSERT_TRUE(NULL != server.get());
-  scoped_refptr<URLRequestTestContext> context = new URLRequestTestContext();
+  scoped_refptr<TestURLRequestContext> context = new TestURLRequestContext();
 
   // Set up a cookie.
   {
@@ -1371,6 +1361,9 @@
     req.set_context(context);
     req.Start();
     MessageLoop::current()->Run();
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 
   // Try to set-up another cookie and update the previous cookie.
@@ -1387,6 +1380,9 @@
     MessageLoop::current()->Run();
 
     context->set_cookie_policy(NULL);
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(2, d.blocked_set_cookie_count());
   }
 
 
@@ -1402,6 +1398,9 @@
                 == std::string::npos);
     EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2")
                 != std::string::npos);
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 }
 
@@ -1409,7 +1408,7 @@
   scoped_refptr<HTTPTestServer> server =
       HTTPTestServer::CreateServer(L"", NULL);
   ASSERT_TRUE(NULL != server.get());
-  scoped_refptr<URLRequestTestContext> context = new URLRequestTestContext();
+  scoped_refptr<TestURLRequestContext> context = new TestURLRequestContext();
 
   // Set up a cookie.
   {
@@ -1418,6 +1417,9 @@
     req.set_context(context);
     req.Start();
     MessageLoop::current()->Run();
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 
   // Verify that the cookie is set.
@@ -1430,6 +1432,9 @@
 
     EXPECT_TRUE(d.data_received().find("CookieToNotSend=1")
                 != std::string::npos);
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 
   // Verify that the cookie isn't sent.
@@ -1448,6 +1453,9 @@
                 == std::string::npos);
 
     context->set_cookie_policy(NULL);
+
+    EXPECT_EQ(1, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 }
 
@@ -1455,7 +1463,7 @@
   scoped_refptr<HTTPTestServer> server =
       HTTPTestServer::CreateServer(L"", NULL);
   ASSERT_TRUE(NULL != server.get());
-  scoped_refptr<URLRequestTestContext> context = new URLRequestTestContext();
+  scoped_refptr<TestURLRequestContext> context = new TestURLRequestContext();
 
   // Set up a cookie.
   {
@@ -1465,6 +1473,9 @@
     req.set_context(context);
     req.Start();
     MessageLoop::current()->Run();
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 
   // Try to set-up another cookie and update the previous cookie.
@@ -1482,6 +1493,9 @@
     MessageLoop::current()->Run();
 
     context->set_cookie_policy(NULL);
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(2, d.blocked_set_cookie_count());
   }
 
   // Verify the cookies weren't saved or updated.
@@ -1496,14 +1510,17 @@
                 == std::string::npos);
     EXPECT_TRUE(d.data_received().find("CookieToNotUpdate=2")
                 != std::string::npos);
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 }
 
-TEST_F(URLRequestTest, CancelTest_DuringCookiePolicy) {
+TEST_F(URLRequestTest, CancelTest_During_CookiePolicy) {
   scoped_refptr<HTTPTestServer> server =
       HTTPTestServer::CreateServer(L"", NULL);
   ASSERT_TRUE(NULL != server.get());
-  scoped_refptr<URLRequestTestContext> context = new URLRequestTestContext();
+  scoped_refptr<TestURLRequestContext> context = new TestURLRequestContext();
 
   TestCookiePolicy cookie_policy(TestCookiePolicy::ASYNC);
   context->set_cookie_policy(&cookie_policy);
@@ -1516,10 +1533,113 @@
     req.set_context(context);
     req.Start();  // Triggers an asynchronous cookie policy check.
 
-    // But, now we cancel the request.  This should not cause a crash.
+    // But, now we cancel the request by letting it go out of scope.  This
+    // should not cause a crash.
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
   }
 
   context->set_cookie_policy(NULL);
+
+  // Let the cookie policy complete.  Make sure it handles the destruction of
+  // the URLRequest properly.
+  MessageLoop::current()->RunAllPending();
+}
+
+TEST_F(URLRequestTest, CancelTest_During_OnGetCookies) {
+  scoped_refptr<HTTPTestServer> server =
+      HTTPTestServer::CreateServer(L"", NULL);
+  ASSERT_TRUE(NULL != server.get());
+  scoped_refptr<TestURLRequestContext> context = new TestURLRequestContext();
+
+  TestCookiePolicy cookie_policy(TestCookiePolicy::NO_GET_COOKIES);
+  context->set_cookie_policy(&cookie_policy);
+
+  // Set up a cookie.
+  {
+    TestDelegate d;
+    d.set_cancel_in_get_cookies_blocked(true);
+    URLRequest req(server->TestServerPage("set-cookie?A=1&B=2&C=3"),
+                   &d);
+    req.set_context(context);
+    req.Start();  // Triggers an asynchronous cookie policy check.
+
+    MessageLoop::current()->Run();
+
+    EXPECT_EQ(URLRequestStatus::CANCELED, req.status().status());
+
+    EXPECT_EQ(1, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
+  }
+
+  context->set_cookie_policy(NULL);
+}
+
+TEST_F(URLRequestTest, CancelTest_During_OnSetCookie) {
+  scoped_refptr<HTTPTestServer> server =
+      HTTPTestServer::CreateServer(L"", NULL);
+  ASSERT_TRUE(NULL != server.get());
+  scoped_refptr<TestURLRequestContext> context = new TestURLRequestContext();
+
+  TestCookiePolicy cookie_policy(TestCookiePolicy::NO_SET_COOKIE);
+  context->set_cookie_policy(&cookie_policy);
+
+  // Set up a cookie.
+  {
+    TestDelegate d;
+    d.set_cancel_in_set_cookie_blocked(true);
+    URLRequest req(server->TestServerPage("set-cookie?A=1&B=2&C=3"),
+                   &d);
+    req.set_context(context);
+    req.Start();  // Triggers an asynchronous cookie policy check.
+
+    MessageLoop::current()->Run();
+
+    EXPECT_EQ(URLRequestStatus::CANCELED, req.status().status());
+
+    // Even though the response will contain 3 set-cookie headers, we expect
+    // only one to be blocked as that first one will cause OnSetCookie to be
+    // called, which will cancel the request.  Once canceled, it should not
+    // attempt to set further cookies.
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(1, d.blocked_set_cookie_count());
+  }
+
+  context->set_cookie_policy(NULL);
+}
+
+TEST_F(URLRequestTest, CookiePolicy_ForceSession) {
+  scoped_refptr<HTTPTestServer> server =
+      HTTPTestServer::CreateServer(L"", NULL);
+  ASSERT_TRUE(NULL != server.get());
+  scoped_refptr<TestURLRequestContext> context = new TestURLRequestContext();
+
+  TestCookiePolicy cookie_policy(TestCookiePolicy::FORCE_SESSION);
+  context->set_cookie_policy(&cookie_policy);
+
+  // Set up a cookie.
+  {
+    TestDelegate d;
+    URLRequest req(server->TestServerPage(
+        "set-cookie?A=1;expires=\"Fri, 05 Feb 2010 23:42:01 GMT\""), &d);
+    req.set_context(context);
+    req.Start();  // Triggers an asynchronous cookie policy check.
+
+    MessageLoop::current()->Run();
+
+    EXPECT_EQ(0, d.blocked_get_cookies_count());
+    EXPECT_EQ(0, d.blocked_set_cookie_count());
+  }
+
+  // Now, check the cookie store.
+  net::CookieMonster::CookieList cookies =
+      context->cookie_store()->GetCookieMonster()->GetAllCookies();
+  EXPECT_EQ(1U, cookies.size());
+  EXPECT_FALSE(cookies[0].IsPersistent());
+
+  context->set_cookie_policy(NULL);
 }
 
 // In this test, we do a POST which the server will 302 redirect.
@@ -1535,7 +1655,8 @@
   req.set_upload(CreateSimpleUploadData(kData));
 
   // Set headers (some of which are specific to the POST).
-  req.SetExtraRequestHeaders(
+  net::HttpRequestHeaders headers;
+  headers.AddHeadersFromString(
     "Content-Type: multipart/form-data; "
     "boundary=----WebKitFormBoundaryAADeAA+NAAWMAAwZ\r\n"
     "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,"
@@ -1544,6 +1665,7 @@
     "Accept-Charset: ISO-8859-1,*,utf-8\r\n"
     "Content-Length: 11\r\n"
     "Origin: http://localhost:1337/");
+  req.SetExtraRequestHeaders(headers);
   req.Start();
   MessageLoop::current()->Run();
 
@@ -1572,8 +1694,10 @@
       &d);
   req.set_method("POST");
   req.set_upload(CreateSimpleUploadData(kData).get());
-  req.SetExtraRequestHeaders(
-      "Content-Length: " + UintToString(sizeof(kData) - 1));
+  net::HttpRequestHeaders headers;
+  headers.SetHeader(net::HttpRequestHeaders::kContentLength,
+                    UintToString(arraysize(kData) - 1));
+  req.SetExtraRequestHeaders(headers);
   req.Start();
   MessageLoop::current()->Run();
   EXPECT_EQ("POST", req.method());
@@ -2330,7 +2454,7 @@
   ASSERT_TRUE(NULL != server_.get());
   TestDelegate d;
   TestURLRequest req(server_->TestServerPage("echoheader?Accept-Language"), &d);
-  req.set_context(new URLRequestTestContext());
+  req.set_context(new TestURLRequestContext());
   req.Start();
   MessageLoop::current()->Run();
   EXPECT_EQ(req.context()->accept_language(), d.data_received());
@@ -2343,8 +2467,10 @@
   TestDelegate d;
   TestURLRequest
       req(server_->TestServerPage("echoheaderoverride?Accept-Language"), &d);
-  req.set_context(new URLRequestTestContext());
-  req.SetExtraRequestHeaders("Accept-Language: ru");
+  req.set_context(new TestURLRequestContext());
+  net::HttpRequestHeaders headers;
+  headers.SetHeader(net::HttpRequestHeaders::kAcceptLanguage, "ru");
+  req.SetExtraRequestHeaders(headers);
   req.Start();
   MessageLoop::current()->Run();
   EXPECT_EQ(std::string("ru"), d.data_received());
@@ -2355,7 +2481,7 @@
   ASSERT_TRUE(NULL != server_.get());
   TestDelegate d;
   TestURLRequest req(server_->TestServerPage("echoheader?Accept-Charset"), &d);
-  req.set_context(new URLRequestTestContext());
+  req.set_context(new TestURLRequestContext());
   req.Start();
   MessageLoop::current()->Run();
   EXPECT_EQ(req.context()->accept_charset(), d.data_received());
@@ -2368,8 +2494,10 @@
   TestDelegate d;
   TestURLRequest
       req(server_->TestServerPage("echoheaderoverride?Accept-Charset"), &d);
-  req.set_context(new URLRequestTestContext());
-  req.SetExtraRequestHeaders("Accept-Charset: koi-8r");
+  req.set_context(new TestURLRequestContext());
+  net::HttpRequestHeaders headers;
+  headers.SetHeader(net::HttpRequestHeaders::kAcceptCharset, "koi-8r");
+  req.SetExtraRequestHeaders(headers);
   req.Start();
   MessageLoop::current()->Run();
   EXPECT_EQ(std::string("koi-8r"), d.data_received());
diff --git a/net/url_request/url_request_unittest.h b/net/url_request/url_request_unittest.h
index 6c1be78..8f090ef 100644
--- a/net/url_request/url_request_unittest.h
+++ b/net/url_request/url_request_unittest.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 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.
 
@@ -17,9 +17,9 @@
 #include "base/message_loop.h"
 #include "base/path_service.h"
 #include "base/process_util.h"
-#include "base/string_util.h"
 #include "base/thread.h"
 #include "base/time.h"
+#include "base/utf_string_conversions.h"
 #include "base/waitable_event.h"
 #include "net/base/cookie_monster.h"
 #include "net/base/cookie_policy.h"
@@ -30,9 +30,10 @@
 #include "net/base/ssl_config_service_defaults.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/ftp/ftp_network_layer.h"
+#include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_network_layer.h"
-#include "net/socket/ssl_test_util.h"
+#include "net/test/test_server.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "net/proxy/proxy_service.h"
@@ -53,7 +54,8 @@
   enum Options {
     NO_GET_COOKIES = 1 << 0,
     NO_SET_COOKIE  = 1 << 1,
-    ASYNC          = 1 << 2
+    ASYNC          = 1 << 2,
+    FORCE_SESSION  = 1 << 3,
   };
 
   explicit TestCookiePolicy(int options_bit_mask)
@@ -93,6 +95,9 @@
     if (options_ & NO_SET_COOKIE)
       return net::ERR_ACCESS_DENIED;
 
+    if (options_ & FORCE_SESSION)
+      return net::OK_FOR_SESSION_ONLY;
+
     return net::OK;
   }
 
@@ -126,15 +131,17 @@
 class TestURLRequestContext : public URLRequestContext {
  public:
   TestURLRequestContext() {
-    host_resolver_ = net::CreateSystemHostResolver(NULL);
+    host_resolver_ =
+        net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism);
     proxy_service_ = net::ProxyService::CreateNull();
     Init();
   }
 
   explicit TestURLRequestContext(const std::string& proxy) {
-    host_resolver_ = net::CreateSystemHostResolver(NULL);
+    host_resolver_ =
+        net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism);
     net::ProxyConfig proxy_config;
-    proxy_config.proxy_rules.ParseFromString(proxy);
+    proxy_config.proxy_rules().ParseFromString(proxy);
     proxy_service_ = net::ProxyService::CreateFixed(proxy_config);
     Init();
   }
@@ -147,28 +154,29 @@
   virtual ~TestURLRequestContext() {
     delete ftp_transaction_factory_;
     delete http_transaction_factory_;
+    delete http_auth_handler_factory_;
   }
 
  private:
   void Init() {
     ftp_transaction_factory_ = new net::FtpNetworkLayer(host_resolver_);
     ssl_config_service_ = new net::SSLConfigServiceDefaults;
-    http_transaction_factory_ =
-        new net::HttpCache(
-          net::HttpNetworkLayer::CreateFactory(NULL, host_resolver_,
-                                               proxy_service_,
-                                               ssl_config_service_),
-          disk_cache::CreateInMemoryCacheBackend(0));
+    http_auth_handler_factory_ = net::HttpAuthHandlerFactory::CreateDefault();
+    http_transaction_factory_ = new net::HttpCache(
+        net::HttpNetworkLayer::CreateFactory(host_resolver_,
+                                             proxy_service_,
+                                             ssl_config_service_,
+                                             http_auth_handler_factory_,
+                                             network_delegate_,
+                                             NULL),
+        net::HttpCache::DefaultBackend::InMemory(0));
     // In-memory cookie store.
-    cookie_store_ = new net::CookieMonster();
+    cookie_store_ = new net::CookieMonster(NULL, NULL);
     accept_language_ = "en-us,fr";
     accept_charset_ = "iso-8859-1,*,utf-8";
   }
 };
 
-// TODO(phajdan.jr): Migrate callers to the new name and remove the typedef.
-typedef TestURLRequestContext URLRequestTestContext;
-
 //-----------------------------------------------------------------------------
 
 class TestURLRequest : public URLRequest {
@@ -188,12 +196,17 @@
         cancel_in_rs_(false),
         cancel_in_rd_(false),
         cancel_in_rd_pending_(false),
+        cancel_in_getcookiesblocked_(false),
+        cancel_in_setcookieblocked_(false),
         quit_on_complete_(true),
         quit_on_redirect_(false),
         allow_certificate_errors_(false),
         response_started_count_(0),
         received_bytes_count_(0),
         received_redirect_count_(0),
+        blocked_get_cookies_count_(0),
+        blocked_set_cookie_count_(0),
+        set_cookie_count_(0),
         received_data_before_response_(false),
         request_failed_(false),
         have_certificate_errors_(false),
@@ -296,12 +309,38 @@
       request->Cancel();
   }
 
+  virtual void OnGetCookies(URLRequest* request, bool blocked_by_policy) {
+    if (blocked_by_policy) {
+      blocked_get_cookies_count_++;
+      if (cancel_in_getcookiesblocked_)
+        request->Cancel();
+    }
+  }
+
+  virtual void OnSetCookie(URLRequest* request,
+                           const std::string& cookie_line,
+                           bool blocked_by_policy) {
+    if (blocked_by_policy) {
+      blocked_set_cookie_count_++;
+      if (cancel_in_setcookieblocked_)
+        request->Cancel();
+    } else {
+      set_cookie_count_++;
+    }
+  }
+
   void set_cancel_in_received_redirect(bool val) { cancel_in_rr_ = val; }
   void set_cancel_in_response_started(bool val) { cancel_in_rs_ = val; }
   void set_cancel_in_received_data(bool val) { cancel_in_rd_ = val; }
   void set_cancel_in_received_data_pending(bool val) {
     cancel_in_rd_pending_ = val;
   }
+  void set_cancel_in_get_cookies_blocked(bool val) {
+    cancel_in_getcookiesblocked_ = val;
+  }
+  void set_cancel_in_set_cookie_blocked(bool val) {
+    cancel_in_setcookieblocked_ = val;
+  }
   void set_quit_on_complete(bool val) { quit_on_complete_ = val; }
   void set_quit_on_redirect(bool val) { quit_on_redirect_ = val; }
   void set_allow_certificate_errors(bool val) {
@@ -315,6 +354,9 @@
   int bytes_received() const { return static_cast<int>(data_received_.size()); }
   int response_started_count() const { return response_started_count_; }
   int received_redirect_count() const { return received_redirect_count_; }
+  int blocked_get_cookies_count() const { return blocked_get_cookies_count_; }
+  int blocked_set_cookie_count() const { return blocked_set_cookie_count_; }
+  int set_cookie_count() const { return set_cookie_count_; }
   bool received_data_before_response() const {
     return received_data_before_response_;
   }
@@ -328,6 +370,8 @@
   bool cancel_in_rs_;
   bool cancel_in_rd_;
   bool cancel_in_rd_pending_;
+  bool cancel_in_getcookiesblocked_;
+  bool cancel_in_setcookieblocked_;
   bool quit_on_complete_;
   bool quit_on_redirect_;
   bool allow_certificate_errors_;
@@ -339,6 +383,9 @@
   int response_started_count_;
   int received_bytes_count_;
   int received_redirect_count_;
+  int blocked_get_cookies_count_;
+  int blocked_set_cookie_count_;
+  int set_cookie_count_;
   bool received_data_before_response_;
   bool request_failed_;
   bool have_certificate_errors_;
@@ -395,12 +442,6 @@
                 "@" + host_name_ + ":" + port_str_ + "/" + path);
   }
 
-  // Deprecated in favor of TestServerPage.
-  // TODO(phajdan.jr): Remove TestServerPageW.
-  GURL TestServerPageW(const std::wstring& path) {
-    return TestServerPage(WideToUTF8(path));
-  }
-
   virtual bool MakeGETRequest(const std::string& page_name) = 0;
 
   FilePath GetDataDirectory() {
diff --git a/net/url_request/view_cache_helper.cc b/net/url_request/view_cache_helper.cc
index b818ba5..92701b7 100644
--- a/net/url_request/view_cache_helper.cc
+++ b/net/url_request/view_cache_helper.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -7,6 +7,7 @@
 #include "base/string_util.h"
 #include "net/base/escape.h"
 #include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_response_headers.h"
@@ -19,7 +20,9 @@
 #define VIEW_CACHE_TAIL \
   "</table></body></html>"
 
-static void HexDump(const char *buf, size_t buf_len, std::string* result) {
+namespace {
+
+void HexDump(const char *buf, size_t buf_len, std::string* result) {
   const size_t kMaxRows = 16;
   int offset = 0;
 
@@ -56,8 +59,8 @@
   }
 }
 
-static std::string FormatEntryInfo(disk_cache::Entry* entry,
-                                   const std::string& url_prefix) {
+std::string FormatEntryInfo(disk_cache::Entry* entry,
+                            const std::string& url_prefix) {
   std::string key = entry->GetKey();
   GURL url = GURL(url_prefix + key);
   std::string row =
@@ -66,115 +69,271 @@
   return row;
 }
 
-static std::string FormatEntryDetails(disk_cache::Entry* entry) {
-  std::string result = EscapeForHTML(entry->GetKey());
+}  // namespace.
 
-  net::HttpResponseInfo response;
-  bool truncated;
-  if (net::HttpCache::ReadResponseInfo(entry, &response, &truncated) &&
-      response.headers) {
-    if (truncated)
-      result.append("<pre>RESPONSE_INFO_TRUNCATED</pre>");
+namespace net {
 
-    result.append("<hr><pre>");
-    result.append(EscapeForHTML(response.headers->GetStatusLine()));
-    result.push_back('\n');
+ViewCacheHelper::~ViewCacheHelper() {
+  if (entry_)
+    entry_->Close();
 
-    void* iter = NULL;
-    std::string name, value;
-    while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) {
-      result.append(EscapeForHTML(name));
-      result.append(": ");
-      result.append(EscapeForHTML(value));
-      result.push_back('\n');
-    }
-    result.append("</pre>");
-  }
-
-  for (int i = 0; i < 2; ++i) {
-    result.append("<hr><pre>");
-
-    int data_size = entry->GetDataSize(i);
-
-    if (data_size) {
-      scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_size);
-      if (entry->ReadData(i, 0, buffer, data_size, NULL) == data_size)
-        HexDump(buffer->data(), data_size, &result);
-    }
-
-    result.append("</pre>");
-  }
-
-  return result;
+  // Cancel any pending entry callback.
+  entry_callback_->Cancel();
 }
 
-static disk_cache::Backend* GetDiskCache(URLRequestContext* context) {
-  if (!context)
-    return NULL;
+int ViewCacheHelper::GetEntryInfoHTML(const std::string& key,
+                                      URLRequestContext* context,
+                                      std::string* out,
+                                      CompletionCallback* callback) {
+  return GetInfoHTML(key, context, std::string(), out, callback);
+}
 
-  if (!context->http_transaction_factory())
-    return NULL;
+int ViewCacheHelper::GetContentsHTML(URLRequestContext* context,
+                                     const std::string& url_prefix,
+                                     std::string* out,
+                                     CompletionCallback* callback) {
+  return GetInfoHTML(std::string(), context, url_prefix, out, callback);
+}
 
-  net::HttpCache* http_cache = context->http_transaction_factory()->GetCache();
+//-----------------------------------------------------------------------------
+
+int ViewCacheHelper::GetInfoHTML(const std::string& key,
+                                 URLRequestContext* context,
+                                 const std::string& url_prefix,
+                                 std::string* out,
+                                 CompletionCallback* callback) {
+  DCHECK(!callback_);
+  DCHECK(context);
+  key_ = key;
+  context_ = context;
+  url_prefix_ = url_prefix;
+  data_ = out;
+  next_state_ = STATE_GET_BACKEND;
+  int rv = DoLoop(OK);
+
+  if (rv == ERR_IO_PENDING)
+    callback_ = callback;
+
+  return rv;
+}
+
+void ViewCacheHelper::DoCallback(int rv) {
+  DCHECK_NE(ERR_IO_PENDING, rv);
+  DCHECK(callback_);
+
+  CompletionCallback* c = callback_;
+  callback_ = NULL;
+  c->Run(rv);
+}
+
+void ViewCacheHelper::HandleResult(int rv) {
+  DCHECK_NE(ERR_IO_PENDING, rv);
+  DCHECK_NE(ERR_FAILED, rv);
+  context_ = NULL;
+  if (callback_)
+    DoCallback(rv);
+}
+
+int ViewCacheHelper::DoLoop(int result) {
+  DCHECK(next_state_ != STATE_NONE);
+
+  int rv = result;
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_GET_BACKEND:
+        DCHECK_EQ(OK, rv);
+        rv = DoGetBackend();
+        break;
+      case STATE_GET_BACKEND_COMPLETE:
+        rv = DoGetBackendComplete(rv);
+        break;
+      case STATE_OPEN_NEXT_ENTRY:
+        DCHECK_EQ(OK, rv);
+        rv = DoOpenNextEntry();
+        break;
+      case STATE_OPEN_NEXT_ENTRY_COMPLETE:
+        rv = DoOpenNextEntryComplete(rv);
+        break;
+      case STATE_OPEN_ENTRY:
+        DCHECK_EQ(OK, rv);
+        rv = DoOpenEntry();
+        break;
+      case STATE_OPEN_ENTRY_COMPLETE:
+        rv = DoOpenEntryComplete(rv);
+        break;
+      case STATE_READ_RESPONSE:
+        DCHECK_EQ(OK, rv);
+        rv = DoReadResponse();
+        break;
+      case STATE_READ_RESPONSE_COMPLETE:
+        rv = DoReadResponseComplete(rv);
+        break;
+      case STATE_READ_DATA:
+        DCHECK_EQ(OK, rv);
+        rv = DoReadData();
+        break;
+      case STATE_READ_DATA_COMPLETE:
+        rv = DoReadDataComplete(rv);
+        break;
+
+      default:
+        NOTREACHED() << "bad state";
+        rv = ERR_FAILED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+
+  if (rv != ERR_IO_PENDING)
+    HandleResult(rv);
+
+  return rv;
+}
+
+int ViewCacheHelper::DoGetBackend() {
+  next_state_ = STATE_GET_BACKEND_COMPLETE;
+
+  if (!context_->http_transaction_factory())
+    return ERR_FAILED;
+
+  net::HttpCache* http_cache = context_->http_transaction_factory()->GetCache();
   if (!http_cache)
-    return NULL;
+    return ERR_FAILED;
 
-  return http_cache->GetBackend();
+  return http_cache->GetBackend(&disk_cache_, &cache_callback_);
 }
 
-static std::string FormatStatistics(disk_cache::Backend* disk_cache) {
-  std::vector<std::pair<std::string, std::string> > stats;
-  disk_cache->GetStats(&stats);
-  std::string result;
-
-  for (size_t index = 0; index < stats.size(); index++) {
-    result.append(stats[index].first);
-    result.append(": ");
-    result.append(stats[index].second);
-    result.append("<br/>\n");
+int ViewCacheHelper::DoGetBackendComplete(int result) {
+  if (result == ERR_FAILED) {
+    data_->append("no disk cache");
+    return OK;
   }
 
-  return result;
+  DCHECK_EQ(OK, result);
+  if (key_.empty()) {
+    data_->assign(VIEW_CACHE_HEAD);
+    DCHECK(!iter_);
+    next_state_ = STATE_OPEN_NEXT_ENTRY;
+    return OK;
+  }
+
+  next_state_ = STATE_OPEN_ENTRY;
+  return OK;
 }
 
-// static
-void ViewCacheHelper::GetEntryInfoHTML(const std::string& key,
-                                       URLRequestContext* context,
-                                       const std::string& url_prefix,
-                                       std::string* data) {
-  disk_cache::Backend* disk_cache = GetDiskCache(context);
-  if (!disk_cache) {
-    data->assign("no disk cache");
-    return;
+int ViewCacheHelper::DoOpenNextEntry() {
+  next_state_ = STATE_OPEN_NEXT_ENTRY_COMPLETE;
+  return disk_cache_->OpenNextEntry(&iter_, &entry_, &cache_callback_);
+}
+
+int ViewCacheHelper::DoOpenNextEntryComplete(int result) {
+  if (result == ERR_FAILED) {
+    data_->append(VIEW_CACHE_TAIL);
+    return OK;
   }
 
-  if (key.empty()) {
-    data->assign(VIEW_CACHE_HEAD);
-    void* iter = NULL;
-    disk_cache::Entry* entry;
-    while (disk_cache->OpenNextEntry(&iter, &entry)) {
-      data->append(FormatEntryInfo(entry, url_prefix));
-      entry->Close();
+  DCHECK_EQ(OK, result);
+  data_->append(FormatEntryInfo(entry_, url_prefix_));
+  entry_->Close();
+  entry_ = NULL;
+
+  next_state_ = STATE_OPEN_NEXT_ENTRY;
+  return OK;
+}
+
+int ViewCacheHelper::DoOpenEntry() {
+  next_state_ = STATE_OPEN_ENTRY_COMPLETE;
+  return disk_cache_->OpenEntry(key_, &entry_, &cache_callback_);
+}
+
+int ViewCacheHelper::DoOpenEntryComplete(int result) {
+  if (result == ERR_FAILED) {
+    data_->append("no matching cache entry for: " + EscapeForHTML(key_));
+    return OK;
+  }
+
+  data_->assign(VIEW_CACHE_HEAD);
+  data_->append(EscapeForHTML(entry_->GetKey()));
+  next_state_ = STATE_READ_RESPONSE;
+  return OK;
+}
+
+int ViewCacheHelper::DoReadResponse() {
+  next_state_ = STATE_READ_RESPONSE_COMPLETE;
+  buf_len_ = entry_->GetDataSize(0);
+  entry_callback_->AddRef();
+  if (!buf_len_)
+    return buf_len_;
+
+  buf_ = new net::IOBuffer(buf_len_);
+  return entry_->ReadData(0, 0, buf_, buf_len_, entry_callback_);
+}
+
+int ViewCacheHelper::DoReadResponseComplete(int result) {
+  entry_callback_->Release();
+  if (result && result == buf_len_) {
+    net::HttpResponseInfo response;
+    bool truncated;
+    if (net::HttpCache::ParseResponseInfo(buf_->data(), buf_len_, &response,
+                                          &truncated) &&
+        response.headers) {
+      if (truncated)
+        data_->append("<pre>RESPONSE_INFO_TRUNCATED</pre>");
+
+      data_->append("<hr><pre>");
+      data_->append(EscapeForHTML(response.headers->GetStatusLine()));
+      data_->push_back('\n');
+
+      void* iter = NULL;
+      std::string name, value;
+      while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) {
+        data_->append(EscapeForHTML(name));
+        data_->append(": ");
+        data_->append(EscapeForHTML(value));
+        data_->push_back('\n');
+      }
+      data_->append("</pre>");
     }
-    data->append(VIEW_CACHE_TAIL);
+  }
+
+  index_ = 0;
+  next_state_ = STATE_READ_DATA;
+  return OK;
+}
+
+int ViewCacheHelper::DoReadData() {
+  data_->append("<hr><pre>");
+
+  next_state_ = STATE_READ_DATA_COMPLETE;
+  buf_len_ = entry_->GetDataSize(index_);
+  entry_callback_->AddRef();
+  if (!buf_len_)
+    return buf_len_;
+
+  buf_ = new net::IOBuffer(buf_len_);
+  return entry_->ReadData(index_, 0, buf_, buf_len_, entry_callback_);
+}
+
+int ViewCacheHelper::DoReadDataComplete(int result) {
+  entry_callback_->Release();
+  if (result && result == buf_len_) {
+    HexDump(buf_->data(), buf_len_, data_);
+  }
+  data_->append("</pre>");
+  index_++;
+  if (index_ < net::HttpCache::kNumCacheEntryDataIndices) {
+    next_state_ = STATE_READ_DATA;
   } else {
-    disk_cache::Entry* entry;
-    if (disk_cache->OpenEntry(key, &entry)) {
-      data->assign(FormatEntryDetails(entry));
-      entry->Close();
-    } else {
-      data->assign("no matching cache entry for: " + key);
-    }
+    data_->append(VIEW_CACHE_TAIL);
+    entry_->Close();
+    entry_ = NULL;
   }
+  return OK;
 }
 
-// static
-void ViewCacheHelper::GetStatisticsHTML(URLRequestContext* context,
-                                        std::string* data) {
-  disk_cache::Backend* disk_cache = GetDiskCache(context);
-  if (!disk_cache) {
-    data->append("no disk cache");
-    return;
-  }
-  data->append(FormatStatistics(disk_cache));
+void ViewCacheHelper::OnIOComplete(int result) {
+  DoLoop(result);
 }
+
+}  // namespace net.
diff --git a/net/url_request/view_cache_helper.h b/net/url_request/view_cache_helper.h
index 2648699..777775a 100644
--- a/net/url_request/view_cache_helper.h
+++ b/net/url_request/view_cache_helper.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2006-2010 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.
 
@@ -7,18 +7,113 @@
 
 #include <string>
 
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+
 class URLRequestContext;
 
+namespace disk_cache {
+class Backend;
+class Entry;
+}
+
+namespace net {
+
 class ViewCacheHelper {
  public:
-  // Formats the cache information for |key| as HTML.
-  static void GetEntryInfoHTML(const std::string& key,
-                               URLRequestContext* context,
-                               const std::string& url_prefix,
-                               std::string* out);
+  ViewCacheHelper()
+      : disk_cache_(NULL), entry_(NULL), iter_(NULL), buf_len_(0), index_(0),
+        data_(NULL), callback_(NULL), next_state_(STATE_NONE),
+        ALLOW_THIS_IN_INITIALIZER_LIST(
+            cache_callback_(this, &ViewCacheHelper::OnIOComplete)),
+        ALLOW_THIS_IN_INITIALIZER_LIST(
+            entry_callback_(new CancelableCompletionCallback<ViewCacheHelper>(
+                this, &ViewCacheHelper::OnIOComplete))) {}
+  ~ViewCacheHelper();
 
-  static void GetStatisticsHTML(URLRequestContext* context,
-                                std::string* out);
+  // Formats the cache information for |key| as HTML. Returns a net error code.
+  // If this method returns ERR_IO_PENDING, |callback| will be notified when the
+  // operation completes. |out| must remain valid until this operation completes
+  // or the object is destroyed.
+  int GetEntryInfoHTML(const std::string& key, URLRequestContext* context,
+                       std::string* out, CompletionCallback* callback);
+
+  // Formats the cache contents as HTML. Returns a net error code.
+  // If this method returns ERR_IO_PENDING, |callback| will be notified when the
+  // operation completes. |out| must remain valid until this operation completes
+  // or the object is destroyed. |url_prefix| will be prepended to each entry
+  // key as a link to the entry.
+  int GetContentsHTML(URLRequestContext* context, const std::string& url_prefix,
+                      std::string* out, CompletionCallback* callback);
+
+ private:
+  enum State {
+    STATE_NONE,
+    STATE_GET_BACKEND,
+    STATE_GET_BACKEND_COMPLETE,
+    STATE_OPEN_NEXT_ENTRY,
+    STATE_OPEN_NEXT_ENTRY_COMPLETE,
+    STATE_OPEN_ENTRY,
+    STATE_OPEN_ENTRY_COMPLETE,
+    STATE_READ_RESPONSE,
+    STATE_READ_RESPONSE_COMPLETE,
+    STATE_READ_DATA,
+    STATE_READ_DATA_COMPLETE
+  };
+
+  // Implements GetEntryInfoHTML and GetContentsHTML.
+  int GetInfoHTML(const std::string& key, URLRequestContext* context,
+                  const std::string& url_prefix, std::string* out,
+                  CompletionCallback* callback);
+
+  // This is a helper function used to trigger a completion callback. It may
+  // only be called if callback_ is non-null.
+  void DoCallback(int rv);
+
+  // This will trigger the completion callback if appropriate.
+  void HandleResult(int rv);
+
+  // Runs the state transition loop.
+  int DoLoop(int result);
+
+  // Each of these methods corresponds to a State value. If there is an
+  // argument, the value corresponds to the return of the previous state or
+  // corresponding callback.
+  int DoGetBackend();
+  int DoGetBackendComplete(int result);
+  int DoOpenNextEntry();
+  int DoOpenNextEntryComplete(int result);
+  int DoOpenEntry();
+  int DoOpenEntryComplete(int result);
+  int DoReadResponse();
+  int DoReadResponseComplete(int result);
+  int DoReadData();
+  int DoReadDataComplete(int result);
+
+  // Called to signal completion of asynchronous IO.
+  void OnIOComplete(int result);
+
+  scoped_refptr<URLRequestContext> context_;
+  disk_cache::Backend* disk_cache_;
+  disk_cache::Entry* entry_;
+  void* iter_;
+  scoped_refptr<net::IOBuffer> buf_;
+  int buf_len_;
+  int index_;
+
+  std::string key_;
+  std::string url_prefix_;
+  std::string* data_;
+  CompletionCallback* callback_;
+
+  State next_state_;
+
+  CompletionCallbackImpl<ViewCacheHelper> cache_callback_;
+  scoped_refptr<CancelableCompletionCallback<ViewCacheHelper> > entry_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ViewCacheHelper);
 };
 
+}  // namespace net.
+
 #endif  // NET_URL_REQUEST_VIEW_CACHE_HELPER_H_
diff --git a/net/url_request/view_cache_helper_unittest.cc b/net/url_request/view_cache_helper_unittest.cc
new file mode 100644
index 0000000..e82ff15
--- /dev/null
+++ b/net/url_request/view_cache_helper_unittest.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2010 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/url_request/view_cache_helper.h"
+
+#include "base/pickle.h"
+#include "net/base/test_completion_callback.h"
+#include "net/disk_cache/disk_cache.h"
+#include "net/http/http_cache.h"
+#include "net/url_request/url_request_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestURLRequestContext : public URLRequestContext {
+ public:
+  TestURLRequestContext();
+
+  // Gets a pointer to the cache backend.
+  disk_cache::Backend* GetBackend();
+
+ private:
+  net::HttpCache cache_;
+};
+
+TestURLRequestContext::TestURLRequestContext()
+    : cache_(reinterpret_cast<net::HttpTransactionFactory*>(NULL),
+             net::HttpCache::DefaultBackend::InMemory(0)) {
+  http_transaction_factory_ = &cache_;
+}
+
+void WriteHeaders(disk_cache::Entry* entry, int flags, const std::string data) {
+  if (data.empty())
+    return;
+
+  Pickle pickle;
+  pickle.WriteInt(flags | 1);  // Version 1.
+  pickle.WriteInt64(0);
+  pickle.WriteInt64(0);
+  pickle.WriteString(data);
+
+  scoped_refptr<net::WrappedIOBuffer> buf = new net::WrappedIOBuffer(
+      reinterpret_cast<const char*>(pickle.data()));
+  int len = static_cast<int>(pickle.size());
+
+  TestCompletionCallback cb;
+  int rv = entry->WriteData(0, 0, buf, len, &cb, true);
+  ASSERT_EQ(len, cb.GetResult(rv));
+}
+
+void WriteData(disk_cache::Entry* entry, int index, const std::string data) {
+  if (data.empty())
+    return;
+
+  int len = data.length();
+  scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(len));
+  memcpy(buf->data(), data.data(), data.length());
+
+  TestCompletionCallback cb;
+  int rv = entry->WriteData(index, 0, buf, len, &cb, true);
+  ASSERT_EQ(len, cb.GetResult(rv));
+}
+
+void WriteToEntry(disk_cache::Backend* cache, const std::string key,
+                  const std::string data0, const std::string data1,
+                  const std::string data2) {
+  TestCompletionCallback cb;
+  disk_cache::Entry* entry;
+  int rv = cache->CreateEntry(key, &entry, &cb);
+  rv = cb.GetResult(rv);
+  if (rv != net::OK) {
+    rv = cache->OpenEntry(key, &entry, &cb);
+    ASSERT_EQ(net::OK, cb.GetResult(rv));
+  }
+
+  WriteHeaders(entry, 0, data0);
+  WriteData(entry, 1, data1);
+  WriteData(entry, 2, data2);
+
+  entry->Close();
+}
+
+void FillCache(URLRequestContext* context) {
+  TestCompletionCallback cb;
+  disk_cache::Backend* cache;
+  int rv =
+      context->http_transaction_factory()->GetCache()->GetBackend(&cache, &cb);
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
+
+  std::string empty;
+  WriteToEntry(cache, "first", "some", empty, empty);
+  WriteToEntry(cache, "second", "only hex_dumped", "same", "kind");
+  WriteToEntry(cache, "third", empty, "another", "thing");
+}
+
+}  // namespace.
+
+TEST(ViewCacheHelper, EmptyCache) {
+  scoped_refptr<TestURLRequestContext> context(new TestURLRequestContext());
+  net::ViewCacheHelper helper;
+
+  TestCompletionCallback cb;
+  std::string prefix, data;
+  int rv = helper.GetContentsHTML(context, prefix, &data, &cb);
+  EXPECT_EQ(net::OK, cb.GetResult(rv));
+  EXPECT_FALSE(data.empty());
+}
+
+TEST(ViewCacheHelper, ListContents) {
+  scoped_refptr<TestURLRequestContext> context(new TestURLRequestContext());
+  net::ViewCacheHelper helper;
+
+  FillCache(context);
+
+  std::string prefix, data;
+  TestCompletionCallback cb;
+  int rv = helper.GetContentsHTML(context, prefix, &data, &cb);
+  EXPECT_EQ(net::OK, cb.GetResult(rv));
+
+  EXPECT_EQ(0U, data.find("<html>"));
+  EXPECT_NE(std::string::npos, data.find("</html>"));
+  EXPECT_NE(std::string::npos, data.find("first"));
+  EXPECT_NE(std::string::npos, data.find("second"));
+  EXPECT_NE(std::string::npos, data.find("third"));
+
+  EXPECT_EQ(std::string::npos, data.find("some"));
+  EXPECT_EQ(std::string::npos, data.find("same"));
+  EXPECT_EQ(std::string::npos, data.find("thing"));
+}
+
+TEST(ViewCacheHelper, DumpEntry) {
+  scoped_refptr<TestURLRequestContext> context(new TestURLRequestContext());
+  net::ViewCacheHelper helper;
+
+  FillCache(context);
+
+  std::string data;
+  TestCompletionCallback cb;
+  int rv = helper.GetEntryInfoHTML("second", context, &data, &cb);
+  EXPECT_EQ(net::OK, cb.GetResult(rv));
+
+  EXPECT_EQ(0U, data.find("<html>"));
+  EXPECT_NE(std::string::npos, data.find("</html>"));
+
+  EXPECT_NE(std::string::npos, data.find("hex_dumped"));
+  EXPECT_NE(std::string::npos, data.find("same"));
+  EXPECT_NE(std::string::npos, data.find("kind"));
+
+  EXPECT_EQ(std::string::npos, data.find("first"));
+  EXPECT_EQ(std::string::npos, data.find("third"));
+  EXPECT_EQ(std::string::npos, data.find("some"));
+  EXPECT_EQ(std::string::npos, data.find("another"));
+}
+
+// Makes sure the links are correct.
+TEST(ViewCacheHelper, Prefix) {
+  scoped_refptr<TestURLRequestContext> context(new TestURLRequestContext());
+  net::ViewCacheHelper helper;
+
+  FillCache(context);
+
+  std::string key, data;
+  std::string prefix("prefix:");
+  TestCompletionCallback cb;
+  int rv = helper.GetContentsHTML(context, prefix, &data, &cb);
+  EXPECT_EQ(net::OK, cb.GetResult(rv));
+
+  EXPECT_EQ(0U, data.find("<html>"));
+  EXPECT_NE(std::string::npos, data.find("</html>"));
+  EXPECT_NE(std::string::npos, data.find("<a href=\"prefix:first\">"));
+  EXPECT_NE(std::string::npos, data.find("<a href=\"prefix:second\">"));
+  EXPECT_NE(std::string::npos, data.find("<a href=\"prefix:third\">"));
+}
+
+TEST(ViewCacheHelper, TruncatedFlag) {
+  scoped_refptr<TestURLRequestContext> context(new TestURLRequestContext());
+  net::ViewCacheHelper helper;
+
+  TestCompletionCallback cb;
+  disk_cache::Backend* cache;
+  int rv =
+      context->http_transaction_factory()->GetCache()->GetBackend(&cache, &cb);
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
+
+  std::string key("the key");
+  disk_cache::Entry* entry;
+  rv = cache->CreateEntry(key, &entry, &cb);
+  ASSERT_EQ(net::OK, cb.GetResult(rv));
+
+  // RESPONSE_INFO_TRUNCATED defined on response_info.cc
+  int flags = 1 << 12;
+  WriteHeaders(entry, flags, "something");
+  entry->Close();
+
+  std::string data;
+  rv = helper.GetEntryInfoHTML(key, context, &data, &cb);
+  EXPECT_EQ(net::OK, cb.GetResult(rv));
+
+  EXPECT_NE(std::string::npos, data.find("RESPONSE_INFO_TRUNCATED"));
+}
diff --git a/net/websockets/websocket.cc b/net/websockets/websocket.cc
index ad7acaf..fa6f180 100644
--- a/net/websockets/websocket.cc
+++ b/net/websockets/websocket.cc
@@ -8,40 +8,30 @@
 #include "net/websockets/websocket.h"
 
 #include "base/message_loop.h"
-#include "net/http/http_response_headers.h"
-#include "net/http/http_util.h"
+#include "net/websockets/websocket_handshake.h"
+#include "net/websockets/websocket_handshake_draft75.h"
 
 namespace net {
 
-static const int kWebSocketPort = 80;
-static const int kSecureWebSocketPort = 443;
-
-static const char kServerHandshakeHeader[] =
-    "HTTP/1.1 101 Web Socket Protocol Handshake\r\n";
-static const size_t kServerHandshakeHeaderLength =
-    sizeof(kServerHandshakeHeader) - 1;
-
-static const char kUpgradeHeader[] = "Upgrade: WebSocket\r\n";
-static const size_t kUpgradeHeaderLength = sizeof(kUpgradeHeader) - 1;
-
-static const char kConnectionHeader[] = "Connection: Upgrade\r\n";
-static const size_t kConnectionHeaderLength = sizeof(kConnectionHeader) - 1;
-
-bool WebSocket::Request::is_secure() const {
-  return url_.SchemeIs("wss");
-}
+static const char kClosingFrame[2] = {'\xff', '\x00'};
+static int64 kClosingHandshakeTimeout = 1000;  // msec.
 
 WebSocket::WebSocket(Request* request, WebSocketDelegate* delegate)
     : ready_state_(INITIALIZED),
-      mode_(MODE_INCOMPLETE),
       request_(request),
+      handshake_(NULL),
       delegate_(delegate),
       origin_loop_(MessageLoop::current()),
       socket_stream_(NULL),
       max_pending_send_allowed_(0),
       current_read_buf_(NULL),
       read_consumed_len_(0),
-      current_write_buf_(NULL) {
+      current_write_buf_(NULL),
+      server_closing_handshake_(false),
+      client_closing_handshake_(false),
+      closing_handshake_started_(false),
+      force_close_task_(NULL),
+      closing_handshake_timeout_(kClosingHandshakeTimeout) {
   DCHECK(request_.get());
   DCHECK(delegate_);
   DCHECK(origin_loop_);
@@ -74,6 +64,13 @@
 }
 
 void WebSocket::Send(const std::string& msg) {
+  if (ready_state_ == CLOSING || ready_state_ == CLOSED) {
+    return;
+  }
+  if (client_closing_handshake_) {
+    // We must not send any data after we start the WebSocket closing handshake.
+    return;
+  }
   DCHECK(ready_state_ == OPEN);
   DCHECK(MessageLoop::current() == origin_loop_);
 
@@ -89,6 +86,50 @@
 void WebSocket::Close() {
   DCHECK(MessageLoop::current() == origin_loop_);
 
+  // If connection has not yet started, do nothing.
+  if (ready_state_ == INITIALIZED) {
+    DCHECK(!socket_stream_);
+    ready_state_ = CLOSED;
+    return;
+  }
+
+  // If the readyState attribute is in the CLOSING or CLOSED state, do nothing
+  if (ready_state_ == CLOSING || ready_state_ == CLOSED)
+    return;
+
+  if (request_->version() == DRAFT75) {
+    DCHECK(socket_stream_);
+    socket_stream_->Close();
+    return;
+  }
+
+  // If the WebSocket connection is not yet established, fail the WebSocket
+  // connection and set the readyState attribute's value to CLOSING.
+  if (ready_state_ == CONNECTING) {
+    ready_state_ = CLOSING;
+    origin_loop_->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this, &WebSocket::FailConnection));
+  }
+
+  // If the WebSocket closing handshake has not yet been started, start
+  // the WebSocket closing handshake and set the readyState attribute's value
+  // to CLOSING.
+  if (!closing_handshake_started_) {
+    ready_state_ = CLOSING;
+    origin_loop_->PostTask(
+        FROM_HERE,
+        NewRunnableMethod(this, &WebSocket::StartClosingHandshake));
+  }
+
+  // Otherwise, set the readyState attribute's value to CLOSING.
+  ready_state_ = CLOSING;
+}
+
+void WebSocket::DetachDelegate() {
+  if (!delegate_)
+    return;
+  delegate_ = NULL;
   if (ready_state_ == INITIALIZED) {
     DCHECK(!socket_stream_);
     ready_state_ = CLOSED;
@@ -97,17 +138,9 @@
   if (ready_state_ != CLOSED) {
     DCHECK(socket_stream_);
     socket_stream_->Close();
-    return;
   }
 }
 
-void WebSocket::DetachDelegate() {
-  if (!delegate_)
-    return;
-  delegate_ = NULL;
-  Close();
-}
-
 void WebSocket::OnConnected(SocketStream* socket_stream,
                             int max_pending_send_allowed) {
   DCHECK(socket_stream == socket_stream_);
@@ -119,7 +152,23 @@
   read_consumed_len_ = 0;
 
   DCHECK(!current_write_buf_);
-  const std::string msg = request_->CreateClientHandshakeMessage();
+  DCHECK(!handshake_.get());
+  switch (request_->version()) {
+    case DEFAULT_VERSION:
+      handshake_.reset(new WebSocketHandshake(
+          request_->url(), request_->origin(), request_->location(),
+          request_->protocol()));
+      break;
+    case DRAFT75:
+      handshake_.reset(new WebSocketHandshakeDraft75(
+          request_->url(), request_->origin(), request_->location(),
+          request_->protocol()));
+      break;
+    default:
+      NOTREACHED() << "Unexpected protocol version:" << request_->version();
+  }
+
+  const std::string msg = handshake_->CreateClientHandshakeMessage();
   IOBufferWithSize* buf = new IOBufferWithSize(msg.size());
   memcpy(buf->data(), msg.data(), msg.size());
   pending_write_bufs_.push_back(buf);
@@ -154,191 +203,38 @@
 }
 
 void WebSocket::OnError(const SocketStream* socket_stream, int error) {
-  origin_loop_->PostTask(FROM_HERE,
-                         NewRunnableMethod(this, &WebSocket::DoError, error));
-}
-
-std::string WebSocket::Request::CreateClientHandshakeMessage() const {
-  std::string msg;
-  msg = "GET ";
-  msg += url_.path();
-  if (url_.has_query()) {
-    msg += "?";
-    msg += url_.query();
-  }
-  msg += " HTTP/1.1\r\n";
-  msg += kUpgradeHeader;
-  msg += kConnectionHeader;
-  msg += "Host: ";
-  msg += StringToLowerASCII(url_.host());
-  if (url_.has_port()) {
-    bool secure = is_secure();
-    int port = url_.EffectiveIntPort();
-    if ((!secure &&
-         port != kWebSocketPort && port != url_parse::PORT_UNSPECIFIED) ||
-        (secure &&
-         port != kSecureWebSocketPort && port != url_parse::PORT_UNSPECIFIED)) {
-      msg += ":";
-      msg += IntToString(port);
-    }
-  }
-  msg += "\r\n";
-  msg += "Origin: ";
-  // It's OK to lowercase the origin as the Origin header does not contain
-  // the path or query portions, as per
-  // http://tools.ietf.org/html/draft-abarth-origin-00.
-  //
-  // TODO(satorux): Should we trim the port portion here if it's 80 for
-  // http:// or 443 for https:// ? Or can we assume it's done by the
-  // client of the library?
-  msg += StringToLowerASCII(origin_);
-  msg += "\r\n";
-  if (!protocol_.empty()) {
-    msg += "WebSocket-Protocol: ";
-    msg += protocol_;
-    msg += "\r\n";
-  }
-  // TODO(ukai): Add cookie if necessary.
-  msg += "\r\n";
-  return msg;
-}
-
-int WebSocket::CheckHandshake() {
-  DCHECK(current_read_buf_);
-  DCHECK(ready_state_ == CONNECTING);
-  mode_ = MODE_INCOMPLETE;
-  const char *start = current_read_buf_->StartOfBuffer() + read_consumed_len_;
-  const char *p = start;
-  size_t len = current_read_buf_->offset() - read_consumed_len_;
-  if (len < kServerHandshakeHeaderLength) {
-    return -1;
-  }
-  if (!memcmp(p, kServerHandshakeHeader, kServerHandshakeHeaderLength)) {
-    mode_ = MODE_NORMAL;
-  } else {
-    int eoh = HttpUtil::LocateEndOfHeaders(p, len);
-    if (eoh < 0)
-      return -1;
-    scoped_refptr<HttpResponseHeaders> headers(
-        new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(p, eoh)));
-    if (headers->response_code() == 407) {
-      mode_ = MODE_AUTHENTICATE;
-      // TODO(ukai): Implement authentication handlers.
-    }
-    DLOG(INFO) << "non-normal websocket connection. "
-               << "response_code=" << headers->response_code()
-               << " mode=" << mode_;
-    // Invalid response code.
-    ready_state_ = CLOSED;
-    return eoh;
-  }
-  const char* end = p + len + 1;
-  p += kServerHandshakeHeaderLength;
-
-  if (mode_ == MODE_NORMAL) {
-    size_t header_size = end - p;
-    if (header_size < kUpgradeHeaderLength)
-      return -1;
-    if (memcmp(p, kUpgradeHeader, kUpgradeHeaderLength)) {
-      DLOG(INFO) << "Bad Upgrade Header "
-                 << std::string(p, kUpgradeHeaderLength);
-      ready_state_ = CLOSED;
-      return p - start;
-    }
-    p += kUpgradeHeaderLength;
-
-    header_size = end - p;
-    if (header_size < kConnectionHeaderLength)
-      return -1;
-    if (memcmp(p, kConnectionHeader, kConnectionHeaderLength)) {
-      DLOG(INFO) << "Bad Connection Header "
-                 << std::string(p, kConnectionHeaderLength);
-      ready_state_ = CLOSED;
-      return p - start;
-    }
-    p += kConnectionHeaderLength;
-  }
-  int eoh = HttpUtil::LocateEndOfHeaders(start, len);
-  if (eoh == -1)
-    return eoh;
-  scoped_refptr<HttpResponseHeaders> headers(
-      new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(start, eoh)));
-  if (!ProcessHeaders(*headers)) {
-    DLOG(INFO) << "Process Headers failed: "
-               << std::string(start, eoh);
-    ready_state_ = CLOSED;
-    return eoh;
-  }
-  switch (mode_) {
-    case MODE_NORMAL:
-      if (CheckResponseHeaders()) {
-        ready_state_ = OPEN;
-      } else {
-        ready_state_ = CLOSED;
-      }
-      break;
-    default:
-      ready_state_ = CLOSED;
-      break;
-  }
-  if (ready_state_ == CLOSED)
-    DLOG(INFO) << "CheckHandshake mode=" << mode_
-               << " " << std::string(start, eoh);
-  return eoh;
-}
-
-// Gets the value of the specified header.
-// It assures only one header of |name| in |headers|.
-// Returns true iff single header of |name| is found in |headers|
-// and |value| is filled with the value.
-// Returns false otherwise.
-static bool GetSingleHeader(const HttpResponseHeaders& headers,
-                            const std::string& name,
-                            std::string* value) {
-  std::string first_value;
-  void* iter = NULL;
-  if (!headers.EnumerateHeader(&iter, name, &first_value))
-    return false;
-
-  // Checks no more |name| found in |headers|.
-  // Second call of EnumerateHeader() must return false.
-  std::string second_value;
-  if (headers.EnumerateHeader(&iter, name, &second_value))
-    return false;
-  *value = first_value;
-  return true;
-}
-
-bool WebSocket::ProcessHeaders(const HttpResponseHeaders& headers) {
-  if (!GetSingleHeader(headers, "websocket-origin", &ws_origin_))
-    return false;
-
-  if (!GetSingleHeader(headers, "websocket-location", &ws_location_))
-    return false;
-
-  if (!request_->protocol().empty()
-      && !GetSingleHeader(headers, "websocket-protocol", &ws_protocol_))
-    return false;
-  return true;
-}
-
-bool WebSocket::CheckResponseHeaders() const {
-  DCHECK(mode_ == MODE_NORMAL);
-  if (!LowerCaseEqualsASCII(request_->origin(), ws_origin_.c_str()))
-    return false;
-  if (request_->location() != ws_location_)
-    return false;
-  if (request_->protocol() != ws_protocol_)
-    return false;
-  return true;
+  origin_loop_->PostTask(
+      FROM_HERE, NewRunnableMethod(this, &WebSocket::DoSocketError, error));
 }
 
 void WebSocket::SendPending() {
   DCHECK(MessageLoop::current() == origin_loop_);
-  DCHECK(socket_stream_);
+  if (!socket_stream_) {
+    DCHECK_EQ(CLOSED, ready_state_);
+    return;
+  }
   if (!current_write_buf_) {
-    if (pending_write_bufs_.empty())
+    if (pending_write_bufs_.empty()) {
+      if (client_closing_handshake_) {
+        // Already sent 0xFF and 0x00 bytes.
+        // *The WebSocket closing handshake has started.*
+        closing_handshake_started_ = true;
+        if (server_closing_handshake_) {
+          // 4.2 3-8-3 If the WebSocket connection is not already closed,
+          // then close the WebSocket connection.
+          // *The WebSocket closing handshake has finished*
+          socket_stream_->Close();
+        } else {
+          // 5. Wait a user-agent-determined length of time, or until the
+          // WebSocket connection is closed.
+          force_close_task_ =
+              NewRunnableMethod(this, &WebSocket::DoForceCloseConnection);
+          origin_loop_->PostDelayedTask(
+              FROM_HERE, force_close_task_, closing_handshake_timeout_);
+        }
+      }
       return;
+    }
     current_write_buf_ = new DrainableIOBuffer(
         pending_write_bufs_.front(), pending_write_bufs_.front()->size());
   }
@@ -352,21 +248,28 @@
 
 void WebSocket::DoReceivedData() {
   DCHECK(MessageLoop::current() == origin_loop_);
+  scoped_refptr<WebSocket> protect(this);
   switch (ready_state_) {
     case CONNECTING:
       {
-        int eoh = CheckHandshake();
+        DCHECK(handshake_.get());
+        DCHECK(current_read_buf_);
+        const char* data =
+            current_read_buf_->StartOfBuffer() + read_consumed_len_;
+        size_t len = current_read_buf_->offset() - read_consumed_len_;
+        int eoh = handshake_->ReadServerHandshake(data, len);
         if (eoh < 0) {
           // Not enough data,  Retry when more data is available.
           return;
         }
         SkipReadBuffer(eoh);
       }
-      if (ready_state_ != OPEN) {
+      if (handshake_->mode() != WebSocketHandshake::MODE_CONNECTED) {
         // Handshake failed.
         socket_stream_->Close();
         return;
       }
+      ready_state_ = OPEN;
       if (delegate_)
         delegate_->OnOpen(this);
       if (current_read_buf_->offset() == read_consumed_len_) {
@@ -375,6 +278,7 @@
       }
       // FALL THROUGH
     case OPEN:
+    case CLOSING:  // need to process closing-frame from server.
       ProcessFrameData();
       break;
 
@@ -389,6 +293,11 @@
 
 void WebSocket::ProcessFrameData() {
   DCHECK(current_read_buf_);
+  if (server_closing_handshake_) {
+    // Any data on the connection after the 0xFF frame is discarded.
+    return;
+  }
+  scoped_refptr<WebSocket> protect(this);
   const char* start_frame =
       current_read_buf_->StartOfBuffer() + read_consumed_len_;
   const char* next_frame = start_frame;
@@ -396,6 +305,10 @@
   const char* end =
       current_read_buf_->StartOfBuffer() + current_read_buf_->offset();
   while (p < end) {
+    // Let /error/ be false.
+    bool error = false;
+
+    // Handle the /frame type/ byte as follows.
     unsigned char frame_byte = static_cast<unsigned char>(*p++);
     if ((frame_byte & 0x80) == 0x80) {
       int length = 0;
@@ -417,7 +330,30 @@
       if (p + length < end) {
         p += length;
         next_frame = p;
+        if (request_->version() != DRAFT75 &&
+            frame_byte == 0xFF && length == 0) {
+          // 4.2 Data framing 3. Handle the /frame type/ byte.
+          // 8. If the /frame type/ is 0xFF and the /length/ was 0, then
+          // run the following substeps:
+          // 1. If the WebSocket closing handshake has not yet started, then
+          // start the WebSocket closing handshake.
+          server_closing_handshake_ = true;
+          if (!closing_handshake_started_) {
+            origin_loop_->PostTask(
+                FROM_HERE,
+                NewRunnableMethod(this, &WebSocket::StartClosingHandshake));
+          } else {
+            // If the WebSocket closing handshake has been started and
+            // the WebSocket connection is not already closed, then close
+            // the WebSocket connection.
+            socket_stream_->Close();
+          }
+          return;
+        }
+        // 4.2 3-8 Otherwise, let /error/ be true.
+        error = true;
       } else {
+        // Not enough data in buffer.
         break;
       }
     } else {
@@ -425,12 +361,21 @@
       while (p < end && *p != '\xff')
         ++p;
       if (p < end && *p == '\xff') {
-        if (frame_byte == 0x00 && delegate_)
-          delegate_->OnMessage(this, std::string(msg_start, p - msg_start));
+        if (frame_byte == 0x00) {
+          if (delegate_) {
+            delegate_->OnMessage(this, std::string(msg_start, p - msg_start));
+          }
+        } else {
+          // Otherwise, discard the data and let /error/ to be true.
+          error = true;
+        }
         ++p;
         next_frame = p;
       }
     }
+    // If /error/ is true, then *a WebSocket error has been detected.*
+    if (error && delegate_)
+      delegate_->OnError(this);
   }
   SkipReadBuffer(next_frame - start_frame);
 }
@@ -473,8 +418,50 @@
   }
 }
 
+void WebSocket::StartClosingHandshake() {
+  // 4.2 *start the WebSocket closing handshake*.
+  if (closing_handshake_started_ || client_closing_handshake_) {
+    // 1. If the WebSocket closing handshake has started, then abort these
+    // steps.
+    return;
+  }
+  // 2.,3. Send a 0xFF and 0x00 byte to the server.
+  client_closing_handshake_ = true;
+  IOBufferWithSize* buf = new IOBufferWithSize(2);
+  memcpy(buf->data(), kClosingFrame, 2);
+  pending_write_bufs_.push_back(buf);
+  SendPending();
+}
+
+void WebSocket::DoForceCloseConnection() {
+  // 4.2 *start the WebSocket closing handshake*
+  // 6. If the WebSocket connection is not already closed, then close the
+  // WebSocket connection.  (If this happens, then the closing handshake
+  // doesn't finish.)
+  DCHECK(MessageLoop::current() == origin_loop_);
+  force_close_task_ = NULL;
+  FailConnection();
+}
+
+void WebSocket::FailConnection() {
+  DCHECK(MessageLoop::current() == origin_loop_);
+  // 6.1 Client-initiated closure.
+  // *fail the WebSocket connection*.
+  // the user agent must close the WebSocket connection, and may report the
+  // problem to the user.
+  if (!socket_stream_)
+    return;
+  socket_stream_->Close();
+}
+
 void WebSocket::DoClose() {
   DCHECK(MessageLoop::current() == origin_loop_);
+  if (force_close_task_) {
+    // WebSocket connection is closed while waiting a user-agent-determined
+    // length of time after *The WebSocket closing handshake has started*.
+    force_close_task_->Cancel();
+    force_close_task_ = NULL;
+  }
   WebSocketDelegate* delegate = delegate_;
   delegate_ = NULL;
   ready_state_ = CLOSED;
@@ -482,14 +469,15 @@
     return;
   socket_stream_ = NULL;
   if (delegate)
-    delegate->OnClose(this);
+    delegate->OnClose(this,
+                      server_closing_handshake_ && closing_handshake_started_);
   Release();
 }
 
-void WebSocket::DoError(int error) {
+void WebSocket::DoSocketError(int error) {
   DCHECK(MessageLoop::current() == origin_loop_);
   if (delegate_)
-    delegate_->OnError(this, error);
+    delegate_->OnSocketError(this, error);
 }
 
 }  // namespace net
diff --git a/net/websockets/websocket.h b/net/websockets/websocket.h
index 0cf95db..373c5e4 100644
--- a/net/websockets/websocket.h
+++ b/net/websockets/websocket.h
@@ -15,6 +15,7 @@
 #include <string>
 
 #include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
 #include "googleurl/src/gurl.h"
 #include "net/base/io_buffer.h"
 #include "net/socket_stream/socket_stream.h"
@@ -26,9 +27,9 @@
 
 class ClientSocketFactory;
 class HostResolver;
-class HttpResponseHeaders;
 
 class WebSocket;
+class WebSocketHandshake;
 
 // Delegate methods will be called on the same message loop as
 // WebSocket is constructed.
@@ -43,11 +44,14 @@
   // |msg| should be in UTF-8.
   virtual void OnMessage(WebSocket* socket, const std::string& msg) = 0;
 
+  // Called when WebSocket error has been detected.
+  virtual void OnError(WebSocket* socket) {}
+
   // Called when |socket| is closed.
-  virtual void OnClose(WebSocket* socket) = 0;
+  virtual void OnClose(WebSocket* socket, bool was_clean) = 0;
 
   // Called when an error occured on |socket|.
-  virtual void OnError(const WebSocket* socket, int error) {}
+  virtual void OnSocketError(const WebSocket* socket, int error) {}
 };
 
 class WebSocket : public base::RefCountedThreadSafe<WebSocket>,
@@ -57,27 +61,34 @@
     INITIALIZED = -1,
     CONNECTING = 0,
     OPEN = 1,
-    CLOSED = 2,
+    CLOSING = 2,
+    CLOSED = 3,
+  };
+  enum ProtocolVersion {
+    DEFAULT_VERSION = 0,
+    DRAFT75 = 1,
   };
   class Request {
    public:
     Request(const GURL& url, const std::string protocol,
             const std::string origin, const std::string location,
+            ProtocolVersion version,
             URLRequestContext* context)
         : url_(url),
           protocol_(protocol),
           origin_(origin),
           location_(location),
+          version_(version),
           context_(context),
           host_resolver_(NULL),
           client_socket_factory_(NULL) {}
     ~Request() {}
 
     const GURL& url() const { return url_; }
-    bool is_secure() const;
     const std::string& protocol() const { return protocol_; }
     const std::string& origin() const { return origin_; }
     const std::string& location() const { return location_; }
+    ProtocolVersion version() const { return version_; }
     URLRequestContext* context() const { return context_; }
 
     // Sets an alternative HostResolver. For testing purposes only.
@@ -95,14 +106,12 @@
       return client_socket_factory_;
     }
 
-    // Creates the client handshake message from |this|.
-    std::string CreateClientHandshakeMessage() const;
-
    private:
     GURL url_;
     std::string protocol_;
     std::string origin_;
     std::string location_;
+    ProtocolVersion version_;
     scoped_refptr<URLRequestContext> context_;
 
     scoped_refptr<HostResolver> host_resolver_;
@@ -147,9 +156,6 @@
   virtual void OnError(const SocketStream* socket, int error);
 
  private:
-  enum Mode {
-    MODE_INCOMPLETE, MODE_NORMAL, MODE_AUTHENTICATE,
-  };
   typedef std::deque< scoped_refptr<IOBufferWithSize> > PendingDataQueue;
 
   friend class WebSocketTest;
@@ -157,24 +163,6 @@
   friend class base::RefCountedThreadSafe<WebSocket>;
   virtual ~WebSocket();
 
-  // Checks handshake.
-  // Prerequisite: Server handshake message is received in |current_read_buf_|.
-  // Returns number of bytes for server handshake message,
-  // or negative if server handshake message is not received fully yet.
-  int CheckHandshake();
-
-  // Processes server handshake message, parsed as |headers|, and updates
-  // |ws_origin_|, |ws_location_| and |ws_protocol_|.
-  // Returns true if it's ok.
-  // Returns false otherwise (e.g. duplicate WebSocket-Origin: header, etc.)
-  bool ProcessHeaders(const HttpResponseHeaders& headers);
-
-  // Checks |ws_origin_|, |ws_location_| and |ws_protocol_| are valid
-  // against |request_|.
-  // Returns true if it's ok.
-  // Returns false otherwise (e.g. origin mismatch, etc.)
-  bool CheckResponseHeaders() const;
-
   // Sends pending data in |current_write_buf_| and/or |pending_write_bufs_|.
   void SendPending();
 
@@ -190,23 +178,21 @@
   // Skips |len| bytes in |current_read_buf_|.
   void SkipReadBuffer(int len);
 
+  void StartClosingHandshake();
+  void DoForceCloseConnection();
+  void FailConnection();
   // Handles closed connection.
   void DoClose();
 
-  // Handles error report.
-  void DoError(int error);
+  // Handles socket error report.
+  void DoSocketError(int error);
 
   State ready_state_;
-  Mode mode_;
   scoped_ptr<Request> request_;
+  scoped_ptr<WebSocketHandshake> handshake_;
   WebSocketDelegate* delegate_;
   MessageLoop* origin_loop_;
 
-  // Handshake messages that server sent.
-  std::string ws_origin_;
-  std::string ws_location_;
-  std::string ws_protocol_;
-
   scoped_refptr<SocketStream> socket_stream_;
   int max_pending_send_allowed_;
 
@@ -226,6 +212,17 @@
   // Front IOBuffer is being sent via |current_write_buf_|.
   PendingDataQueue pending_write_bufs_;
 
+  // True when the 0xFF frame with length 0x00 is received.
+  bool server_closing_handshake_;
+  // True when trying to send 0xFF and 0x00 bytes.
+  bool client_closing_handshake_;
+  // True when send 0xFF and 0x00 bytes.
+  bool closing_handshake_started_;
+  // Task to close the connection after closing handshake has started and
+  // |closing_handshake_timeout_|.
+  CancelableTask* force_close_task_;
+  int64 closing_handshake_timeout_;
+
   DISALLOW_COPY_AND_ASSIGN(WebSocket);
 };
 
diff --git a/net/websockets/websocket_frame_handler.cc b/net/websockets/websocket_frame_handler.cc
new file mode 100644
index 0000000..5ac395e
--- /dev/null
+++ b/net/websockets/websocket_frame_handler.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2010 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 <algorithm>
+#include <limits>
+
+#include "net/websockets/websocket_frame_handler.h"
+
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+WebSocketFrameHandler::WebSocketFrameHandler()
+    : current_buffer_size_(0),
+      original_current_buffer_size_(0) {
+}
+
+WebSocketFrameHandler::~WebSocketFrameHandler() {
+}
+
+void WebSocketFrameHandler::AppendData(const char* data, int length) {
+  scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(length);
+  memcpy(buffer->data(), data, length);
+  pending_buffers_.push_back(buffer);
+}
+
+int WebSocketFrameHandler::UpdateCurrentBuffer(bool buffered) {
+  if (current_buffer_)
+    return 0;
+  DCHECK(!current_buffer_size_);
+  DCHECK(!original_current_buffer_size_);
+
+  if (pending_buffers_.empty())
+    return 0;
+  scoped_refptr<IOBufferWithSize> buffer = pending_buffers_.front();
+
+  int buffer_size = 0;
+  if (buffered) {
+    std::vector<FrameInfo> frame_info;
+    buffer_size =
+        ParseWebSocketFrame(buffer->data(), buffer->size(), &frame_info);
+    if (buffer_size <= 0)
+      return buffer_size;
+
+    original_current_buffer_size_ = buffer_size;
+
+    // TODO(ukai): filter(e.g. compress or decompress) frame messages.
+  } else {
+    original_current_buffer_size_ = buffer->size();
+    buffer_size = buffer->size();
+  }
+
+  current_buffer_ = buffer;
+  current_buffer_size_ = buffer_size;
+  return buffer_size;
+}
+
+void WebSocketFrameHandler::ReleaseCurrentBuffer() {
+  DCHECK(!pending_buffers_.empty());
+  scoped_refptr<IOBufferWithSize> front_buffer = pending_buffers_.front();
+  pending_buffers_.pop_front();
+  int remaining_size = front_buffer->size() - original_current_buffer_size_;
+  if (remaining_size > 0) {
+    scoped_refptr<IOBufferWithSize> next_buffer = NULL;
+    int buffer_size = remaining_size;
+    if (!pending_buffers_.empty()) {
+      next_buffer = pending_buffers_.front();
+      buffer_size += next_buffer->size();
+      pending_buffers_.pop_front();
+    }
+    // TODO(ukai): don't copy data.
+    scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(buffer_size);
+    memcpy(buffer->data(), front_buffer->data() + original_current_buffer_size_,
+           remaining_size);
+    if (next_buffer)
+      memcpy(buffer->data() + remaining_size,
+             next_buffer->data(), next_buffer->size());
+    pending_buffers_.push_front(buffer);
+  }
+  current_buffer_ = NULL;
+  current_buffer_size_ = 0;
+  original_current_buffer_size_ = 0;
+}
+
+/* static */
+int WebSocketFrameHandler::ParseWebSocketFrame(
+    const char* buffer, int size, std::vector<FrameInfo>* frame_info) {
+  const char* end = buffer + size;
+  const char* p = buffer;
+  int buffer_size = 0;
+  while (p < end) {
+    FrameInfo frame;
+    frame.frame_start = p;
+    frame.message_length = -1;
+    unsigned char frame_byte = static_cast<unsigned char>(*p++);
+    if ((frame_byte & 0x80) == 0x80) {
+      int length = 0;
+      while (p < end) {
+        // Note: might overflow later if numeric_limits<int>::max() is not
+        // n*128-1.
+        if (length > std::numeric_limits<int>::max() / 128) {
+          // frame length overflow.
+          return ERR_INSUFFICIENT_RESOURCES;
+        }
+        unsigned char c = static_cast<unsigned char>(*p);
+        length = length * 128 + (c & 0x7f);
+        ++p;
+        if ((c & 0x80) != 0x80)
+          break;
+      }
+      if (end - p >= length) {
+        frame.message_start = p;
+        frame.message_length = length;
+        p += length;
+      } else {
+        break;
+      }
+    } else {
+      frame.message_start = p;
+      while (p < end && *p != '\xff')
+        ++p;
+      if (p < end && *p == '\xff') {
+        frame.message_length = p - frame.message_start;
+        ++p;
+      } else {
+        break;
+      }
+    }
+    if (frame.message_length >= 0 && p <= end) {
+      frame.frame_length = p - frame.frame_start;
+      buffer_size += frame.frame_length;
+      frame_info->push_back(frame);
+    }
+  }
+  return buffer_size;
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_frame_handler.h b/net/websockets/websocket_frame_handler.h
new file mode 100644
index 0000000..d6c38da
--- /dev/null
+++ b/net/websockets/websocket_frame_handler.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2010 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_WEBSOCKETS_WEBSOCKET_FRAME_HANDLER_H_
+#define NET_WEBSOCKETS_WEBSOCKET_FRAME_HANDLER_H_
+
+#include <deque>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+
+namespace net {
+
+class IOBuffer;
+class IOBufferWithSize;
+
+// Handles WebSocket frame messages.
+class WebSocketFrameHandler {
+ public:
+  struct FrameInfo {
+    const char* frame_start;
+    int frame_length;
+    const char* message_start;
+    int message_length;
+  };
+
+  WebSocketFrameHandler();
+  ~WebSocketFrameHandler();
+
+  // Appends WebSocket raw data on connection.
+  // For sending, this is data from WebKit.
+  // For receiving, this is data from network.
+  void AppendData(const char* data, int len);
+
+  // Updates current IOBuffer.
+  // If |buffered| is true, it tries to find WebSocket frames.
+  // Otherwise, it just picks the first buffer in |pending_buffers_|.
+  // Returns available size of data, 0 if no more data or current buffer was
+  // not released, and negative if some error occurred.
+  int UpdateCurrentBuffer(bool buffered);
+
+  // Gets current IOBuffer.
+  // For sending, this is data to network.
+  // For receiving, this is data to WebKit.
+  // Returns NULL just after ReleaseCurrentBuffer() was called.
+  IOBuffer* GetCurrentBuffer() { return current_buffer_.get(); }
+  int GetCurrentBufferSize() const { return current_buffer_size_; }
+
+  // Returns original buffer size of current IOBuffer.
+  // This might differ from GetCurrentBufferSize() if frame message is
+  // compressed or decompressed.
+  int GetOriginalBufferSize() const { return original_current_buffer_size_; }
+
+  // Releases current IOBuffer.
+  void ReleaseCurrentBuffer();
+
+  // Parses WebSocket frame in [|buffer|, |buffer|+|size|), fills frame
+  // information in |frame_info|, and returns number of bytes for
+  // complete WebSocket frames.
+  static int ParseWebSocketFrame(const char* buffer, int size,
+                                 std::vector<FrameInfo>* frame_info);
+
+ private:
+  typedef std::deque< scoped_refptr<IOBufferWithSize> > PendingDataQueue;
+
+  scoped_refptr<IOBuffer> current_buffer_;
+  int current_buffer_size_;
+
+  int original_current_buffer_size_;
+
+  // Deque of IOBuffers in pending.
+  PendingDataQueue pending_buffers_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketFrameHandler);
+};
+
+}  // namespace net
+
+#endif  // NET_WEBSOCKETS_WEBSOCKET_FRAME_HANDLER_H_
diff --git a/net/websockets/websocket_frame_handler_unittest.cc b/net/websockets/websocket_frame_handler_unittest.cc
new file mode 100644
index 0000000..52d70de
--- /dev/null
+++ b/net/websockets/websocket_frame_handler_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2010 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/scoped_ptr.h"
+#include "net/base/io_buffer.h"
+#include "net/websockets/websocket_frame_handler.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace net {
+
+TEST(WebSocketFrameHandlerTest, Basic) {
+  const char kInputData[] = "\0hello, world\xff";
+  const int kInputDataLen = sizeof(kInputData) - 1;  // no terminating NUL.
+
+  scoped_ptr<WebSocketFrameHandler> handler(new WebSocketFrameHandler);
+
+  // No data.
+  EXPECT_EQ(0, handler->UpdateCurrentBuffer(true));
+  EXPECT_TRUE(handler->GetCurrentBuffer() == NULL);
+  EXPECT_EQ(0, handler->GetCurrentBufferSize());
+
+  // WebKit sends data (WebSocketJob::SendData),
+  // or data is received from network (WebSocketJob::OnReceivedData)
+  handler->AppendData(kInputData, kInputDataLen);
+  EXPECT_TRUE(handler->GetCurrentBuffer() == NULL);
+  EXPECT_GT(handler->UpdateCurrentBuffer(true), 0);
+  // Get data to send to the socket (send),
+  // or to send to WebKit (receive).
+  IOBuffer* buf = handler->GetCurrentBuffer();
+  ASSERT_TRUE(buf != NULL);
+  EXPECT_TRUE(memcmp(buf->data(), kInputData, kInputDataLen) == 0);
+  EXPECT_EQ(kInputDataLen, handler->GetCurrentBufferSize());
+  EXPECT_EQ(kInputDataLen, handler->GetOriginalBufferSize());
+  // Data was sent. (WebSocketJob::OnSentData)
+  buf = NULL;
+  handler->ReleaseCurrentBuffer();
+  EXPECT_TRUE(handler->GetCurrentBuffer() == NULL);
+  EXPECT_EQ(0, handler->GetCurrentBufferSize());
+  EXPECT_EQ(0, handler->UpdateCurrentBuffer(true));
+}
+
+TEST(WebSocketFrameHandlerTest, ParseFrame) {
+  std::vector<WebSocketFrameHandler::FrameInfo> frames;
+  const char kInputData[] = "\0hello, world\xff\xff\0";
+  const int kInputDataLen = sizeof(kInputData) - 1;
+  const int kHelloWorldFrameLen = 14;
+
+  EXPECT_EQ(kInputDataLen,
+            WebSocketFrameHandler::ParseWebSocketFrame(
+                kInputData, kInputDataLen, &frames));
+  EXPECT_EQ(2UL, frames.size());
+
+  EXPECT_EQ(kInputData, frames[0].frame_start);
+  EXPECT_EQ(kHelloWorldFrameLen, frames[0].frame_length);
+  EXPECT_EQ(kInputData + 1, frames[0].message_start);
+  EXPECT_EQ(kHelloWorldFrameLen - 2, frames[0].message_length);
+
+  EXPECT_EQ(kInputData + kHelloWorldFrameLen, frames[1].frame_start);
+  EXPECT_EQ(2, frames[1].frame_length);
+  EXPECT_EQ(0, frames[1].message_length);
+}
+
+TEST(WebSocketFrameHandlerTest, ParseFrameLength) {
+  std::vector<WebSocketFrameHandler::FrameInfo> frames;
+  const char kHelloWorldFrame[] = "\0hello, world\xff";
+  const int kHelloWorldFrameLen = sizeof(kHelloWorldFrame) - 1;
+  const char kLengthFrame[3 + 129] = "\x80\x81\x01\x01\0should be skipped\xff";
+  const int kLengthFrameLen = sizeof(kLengthFrame);
+  const int kInputDataLen = kHelloWorldFrameLen +
+      kLengthFrameLen +
+      kHelloWorldFrameLen;
+  char inputData[kInputDataLen];
+  memcpy(inputData, kHelloWorldFrame, kHelloWorldFrameLen);
+  memcpy(inputData + kHelloWorldFrameLen, kLengthFrame, kLengthFrameLen);
+  memcpy(inputData + kHelloWorldFrameLen + kLengthFrameLen,
+         kHelloWorldFrame, kHelloWorldFrameLen);
+
+  EXPECT_EQ(kInputDataLen,
+            WebSocketFrameHandler::ParseWebSocketFrame(
+                inputData, kInputDataLen, &frames));
+  ASSERT_EQ(3UL, frames.size());
+
+  EXPECT_EQ(inputData, frames[0].frame_start);
+  EXPECT_EQ(kHelloWorldFrameLen, frames[0].frame_length);
+  EXPECT_EQ(inputData + 1, frames[0].message_start);
+  EXPECT_EQ(kHelloWorldFrameLen - 2, frames[0].message_length);
+
+  EXPECT_EQ(inputData + kHelloWorldFrameLen, frames[1].frame_start);
+  EXPECT_EQ(kLengthFrameLen, frames[1].frame_length);
+  EXPECT_EQ(inputData + kHelloWorldFrameLen + 3, frames[1].message_start);
+  EXPECT_EQ(kLengthFrameLen - 3, frames[1].message_length);
+
+  EXPECT_EQ(inputData + kHelloWorldFrameLen + kLengthFrameLen,
+            frames[2].frame_start);
+  EXPECT_EQ(kHelloWorldFrameLen, frames[2].frame_length);
+  EXPECT_EQ(inputData + kHelloWorldFrameLen + kLengthFrameLen + 1,
+            frames[2].message_start);
+  EXPECT_EQ(kHelloWorldFrameLen - 2, frames[2].message_length);
+}
+
+TEST(WebSocketFrameHandlerTest, ParsePartialFrame) {
+  std::vector<WebSocketFrameHandler::FrameInfo> frames;
+  const char kInputData[] = "\0hello, world\xff"
+      "\x80\x81\x01"  // skip 1*128+1 bytes.
+      "\x01\xff"
+      "\0should be skipped\xff";
+  const int kInputDataLen = sizeof(kInputData) - 1;
+  const int kHelloWorldFrameLen = 14;
+
+  EXPECT_EQ(kHelloWorldFrameLen,
+            WebSocketFrameHandler::ParseWebSocketFrame(
+                kInputData, kInputDataLen, &frames));
+  ASSERT_EQ(1UL, frames.size());
+
+  EXPECT_EQ(kInputData, frames[0].frame_start);
+  EXPECT_EQ(kHelloWorldFrameLen, frames[0].frame_length);
+  EXPECT_EQ(kInputData + 1, frames[0].message_start);
+  EXPECT_EQ(kHelloWorldFrameLen - 2, frames[0].message_length);
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_handshake.cc b/net/websockets/websocket_handshake.cc
new file mode 100644
index 0000000..5adfa67
--- /dev/null
+++ b/net/websockets/websocket_handshake.cc
@@ -0,0 +1,303 @@
+// Copyright (c) 2010 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/websockets/websocket_handshake.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/md5.h"
+#include "base/rand_util.h"
+#include "base/ref_counted.h"
+#include "base/string_util.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+
+namespace net {
+
+const int WebSocketHandshake::kWebSocketPort = 80;
+const int WebSocketHandshake::kSecureWebSocketPort = 443;
+
+WebSocketHandshake::WebSocketHandshake(
+    const GURL& url,
+    const std::string& origin,
+    const std::string& location,
+    const std::string& protocol)
+    : url_(url),
+      origin_(origin),
+      location_(location),
+      protocol_(protocol),
+      mode_(MODE_INCOMPLETE) {
+}
+
+WebSocketHandshake::~WebSocketHandshake() {
+}
+
+bool WebSocketHandshake::is_secure() const {
+  return url_.SchemeIs("wss");
+}
+
+std::string WebSocketHandshake::CreateClientHandshakeMessage() {
+  if (!parameter_.get()) {
+    parameter_.reset(new Parameter);
+    parameter_->GenerateKeys();
+  }
+  std::string msg;
+
+  // WebSocket protocol 4.1 Opening handshake.
+
+  msg = "GET ";
+  msg += GetResourceName();
+  msg += " HTTP/1.1\r\n";
+
+  std::vector<std::string> fields;
+
+  fields.push_back("Upgrade: WebSocket");
+  fields.push_back("Connection: Upgrade");
+
+  fields.push_back("Host: " + GetHostFieldValue());
+
+  fields.push_back("Origin: " + GetOriginFieldValue());
+
+  if (!protocol_.empty())
+    fields.push_back("Sec-WebSocket-Protocol: " + protocol_);
+
+  // TODO(ukai): Add cookie if necessary.
+
+  fields.push_back("Sec-WebSocket-Key1: " + parameter_->GetSecWebSocketKey1());
+  fields.push_back("Sec-WebSocket-Key2: " + parameter_->GetSecWebSocketKey2());
+
+  std::random_shuffle(fields.begin(), fields.end());
+
+  for (size_t i = 0; i < fields.size(); i++) {
+    msg += fields[i] + "\r\n";
+  }
+  msg += "\r\n";
+
+  msg.append(parameter_->GetKey3());
+  return msg;
+}
+
+int WebSocketHandshake::ReadServerHandshake(const char* data, size_t len) {
+  mode_ = MODE_INCOMPLETE;
+  int eoh = HttpUtil::LocateEndOfHeaders(data, len);
+  if (eoh < 0)
+    return -1;
+
+  scoped_refptr<HttpResponseHeaders> headers(
+      new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(data, eoh)));
+
+  if (headers->response_code() != 101) {
+    mode_ = MODE_FAILED;
+    DLOG(INFO) << "Bad response code: " << headers->response_code();
+    return eoh;
+  }
+  mode_ = MODE_NORMAL;
+  if (!ProcessHeaders(*headers) || !CheckResponseHeaders()) {
+    DLOG(INFO) << "Process Headers failed: "
+               << std::string(data, eoh);
+    mode_ = MODE_FAILED;
+    return eoh;
+  }
+  if (len < static_cast<size_t>(eoh + Parameter::kExpectedResponseSize)) {
+    mode_ = MODE_INCOMPLETE;
+    return -1;
+  }
+  uint8 expected[Parameter::kExpectedResponseSize];
+  parameter_->GetExpectedResponse(expected);
+  if (memcmp(&data[eoh], expected, Parameter::kExpectedResponseSize)) {
+    mode_ = MODE_FAILED;
+    return eoh + Parameter::kExpectedResponseSize;
+  }
+  mode_ = MODE_CONNECTED;
+  return eoh + Parameter::kExpectedResponseSize;
+}
+
+std::string WebSocketHandshake::GetResourceName() const {
+  std::string resource_name = url_.path();
+  if (url_.has_query()) {
+    resource_name += "?";
+    resource_name += url_.query();
+  }
+  return resource_name;
+}
+
+std::string WebSocketHandshake::GetHostFieldValue() const {
+  // url_.host() is expected to be encoded in punnycode here.
+  std::string host = StringToLowerASCII(url_.host());
+  if (url_.has_port()) {
+    bool secure = is_secure();
+    int port = url_.EffectiveIntPort();
+    if ((!secure &&
+         port != kWebSocketPort && port != url_parse::PORT_UNSPECIFIED) ||
+        (secure &&
+         port != kSecureWebSocketPort && port != url_parse::PORT_UNSPECIFIED)) {
+      host += ":";
+      host += IntToString(port);
+    }
+  }
+  return host;
+}
+
+std::string WebSocketHandshake::GetOriginFieldValue() const {
+  // It's OK to lowercase the origin as the Origin header does not contain
+  // the path or query portions, as per
+  // http://tools.ietf.org/html/draft-abarth-origin-00.
+  //
+  // TODO(satorux): Should we trim the port portion here if it's 80 for
+  // http:// or 443 for https:// ? Or can we assume it's done by the
+  // client of the library?
+  return StringToLowerASCII(origin_);
+}
+
+/* static */
+bool WebSocketHandshake::GetSingleHeader(const HttpResponseHeaders& headers,
+                                         const std::string& name,
+                                         std::string* value) {
+  std::string first_value;
+  void* iter = NULL;
+  if (!headers.EnumerateHeader(&iter, name, &first_value))
+    return false;
+
+  // Checks no more |name| found in |headers|.
+  // Second call of EnumerateHeader() must return false.
+  std::string second_value;
+  if (headers.EnumerateHeader(&iter, name, &second_value))
+    return false;
+  *value = first_value;
+  return true;
+}
+
+bool WebSocketHandshake::ProcessHeaders(const HttpResponseHeaders& headers) {
+  std::string value;
+  if (!GetSingleHeader(headers, "upgrade", &value) ||
+      value != "WebSocket")
+    return false;
+
+  if (!GetSingleHeader(headers, "connection", &value) ||
+      !LowerCaseEqualsASCII(value, "upgrade"))
+    return false;
+
+  if (!GetSingleHeader(headers, "sec-websocket-origin", &ws_origin_))
+    return false;
+
+  if (!GetSingleHeader(headers, "sec-websocket-location", &ws_location_))
+    return false;
+
+  // If |protocol_| is not specified by client, we don't care if there's
+  // protocol field or not as specified in the spec.
+  if (!protocol_.empty()
+      && !GetSingleHeader(headers, "sec-websocket-protocol", &ws_protocol_))
+    return false;
+  return true;
+}
+
+bool WebSocketHandshake::CheckResponseHeaders() const {
+  DCHECK(mode_ == MODE_NORMAL);
+  if (!LowerCaseEqualsASCII(origin_, ws_origin_.c_str()))
+    return false;
+  if (location_ != ws_location_)
+    return false;
+  if (!protocol_.empty() && protocol_ != ws_protocol_)
+    return false;
+  return true;
+}
+
+namespace {
+
+// unsigned int version of base::RandInt().
+// we can't use base::RandInt(), because max would be negative if it is
+// represented as int, so DCHECK(min <= max) fails.
+uint32 RandUint32(uint32 min, uint32 max) {
+  DCHECK(min <= max);
+
+  uint64 range = static_cast<int64>(max) - min + 1;
+  uint64 number = base::RandUint64();
+  // TODO(ukai): fix to be uniform.
+  // the distribution of the result of modulo will be biased.
+  uint32 result = min + static_cast<uint32>(number % range);
+  DCHECK(result >= min && result <= max);
+  return result;
+}
+
+}
+
+uint32 (*WebSocketHandshake::Parameter::rand_)(uint32 min, uint32 max) =
+    RandUint32;
+uint8 randomCharacterInSecWebSocketKey[0x2F - 0x20 + 0x7E - 0x39];
+
+WebSocketHandshake::Parameter::Parameter()
+    : number_1_(0), number_2_(0) {
+  if (randomCharacterInSecWebSocketKey[0] == '\0') {
+    int i = 0;
+    for (int ch = 0x21; ch <= 0x2F; ch++, i++)
+      randomCharacterInSecWebSocketKey[i] = ch;
+    for (int ch = 0x3A; ch <= 0x7E; ch++, i++)
+      randomCharacterInSecWebSocketKey[i] = ch;
+  }
+}
+
+WebSocketHandshake::Parameter::~Parameter() {}
+
+void WebSocketHandshake::Parameter::GenerateKeys() {
+  GenerateSecWebSocketKey(&number_1_, &key_1_);
+  GenerateSecWebSocketKey(&number_2_, &key_2_);
+  GenerateKey3();
+}
+
+static void SetChallengeNumber(uint8* buf, uint32 number) {
+  uint8* p = buf + 3;
+  for (int i = 0; i < 4; i++) {
+    *p = (uint8)(number & 0xFF);
+    --p;
+    number >>= 8;
+  }
+}
+
+void WebSocketHandshake::Parameter::GetExpectedResponse(uint8 *expected) const {
+  uint8 challenge[kExpectedResponseSize];
+  SetChallengeNumber(&challenge[0], number_1_);
+  SetChallengeNumber(&challenge[4], number_2_);
+  memcpy(&challenge[8], key_3_.data(), kKey3Size);
+  MD5Digest digest;
+  MD5Sum(challenge, kExpectedResponseSize, &digest);
+  memcpy(expected, digest.a, kExpectedResponseSize);
+}
+
+/* static */
+void WebSocketHandshake::Parameter::SetRandomNumberGenerator(
+    uint32 (*rand)(uint32 min, uint32 max)) {
+  rand_ = rand;
+}
+
+void WebSocketHandshake::Parameter::GenerateSecWebSocketKey(
+    uint32* number, std::string* key) {
+  uint32 space = rand_(1, 12);
+  uint32 max = 4294967295U / space;
+  *number = rand_(0, max);
+  uint32 product = *number * space;
+
+  std::string s = StringPrintf("%u", product);
+  int n = rand_(1, 12);
+  for (int i = 0; i < n; i++) {
+    int pos = rand_(0, s.length());
+    int chpos = rand_(0, sizeof(randomCharacterInSecWebSocketKey) - 1);
+    s = s.substr(0, pos).append(1, randomCharacterInSecWebSocketKey[chpos]) +
+        s.substr(pos);
+  }
+  for (uint32 i = 0; i < space; i++) {
+    int pos = rand_(1, s.length() - 1);
+    s = s.substr(0, pos) + " " + s.substr(pos);
+  }
+  *key = s;
+}
+
+void WebSocketHandshake::Parameter::GenerateKey3() {
+  key_3_.clear();
+  for (int i = 0; i < 8; i++) {
+    key_3_.append(1, rand_(0, 255));
+  }
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_handshake.h b/net/websockets/websocket_handshake.h
new file mode 100644
index 0000000..3f64b8b
--- /dev/null
+++ b/net/websockets/websocket_handshake.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2010 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_WEBSOCKETS_WEBSOCKET_HANDSHAKE_H_
+#define NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
+
+namespace net {
+
+class HttpResponseHeaders;
+
+class WebSocketHandshake {
+ public:
+  static const int kWebSocketPort;
+  static const int kSecureWebSocketPort;
+
+  enum Mode {
+    MODE_INCOMPLETE, MODE_NORMAL, MODE_FAILED, MODE_CONNECTED
+  };
+  WebSocketHandshake(const GURL& url,
+                     const std::string& origin,
+                     const std::string& location,
+                     const std::string& protocol);
+  virtual ~WebSocketHandshake();
+
+  bool is_secure() const;
+  // Creates the client handshake message from |this|.
+  virtual std::string CreateClientHandshakeMessage();
+
+  // Reads server handshake message in |len| of |data|, updates |mode_| and
+  // returns number of bytes of the server handshake message.
+  // Once connection is established, |mode_| will be MODE_CONNECTED.
+  // If connection establishment failed, |mode_| will be MODE_FAILED.
+  // Returns negative if the server handshake message is incomplete.
+  virtual int ReadServerHandshake(const char* data, size_t len);
+  Mode mode() const { return mode_; }
+
+ protected:
+  std::string GetResourceName() const;
+  std::string GetHostFieldValue() const;
+  std::string GetOriginFieldValue() const;
+
+  // Gets the value of the specified header.
+  // It assures only one header of |name| in |headers|.
+  // Returns true iff single header of |name| is found in |headers|
+  // and |value| is filled with the value.
+  // Returns false otherwise.
+  static bool GetSingleHeader(const HttpResponseHeaders& headers,
+                              const std::string& name,
+                              std::string* value);
+
+  GURL url_;
+  // Handshake messages that the client is going to send out.
+  std::string origin_;
+  std::string location_;
+  std::string protocol_;
+
+  Mode mode_;
+
+  // Handshake messages that server sent.
+  std::string ws_origin_;
+  std::string ws_location_;
+  std::string ws_protocol_;
+
+ private:
+  friend class WebSocketHandshakeTest;
+
+  class Parameter {
+   public:
+    static const int kKey3Size = 8;
+    static const int kExpectedResponseSize = 16;
+    Parameter();
+    ~Parameter();
+
+    void GenerateKeys();
+    const std::string& GetSecWebSocketKey1() const { return key_1_; }
+    const std::string& GetSecWebSocketKey2() const { return key_2_; }
+    const std::string& GetKey3() const { return key_3_; }
+
+    void GetExpectedResponse(uint8* expected) const;
+
+   private:
+    friend class WebSocketHandshakeTest;
+
+    // Set random number generator. |rand| should return a random number
+    // between min and max (inclusive).
+    static void SetRandomNumberGenerator(
+        uint32 (*rand)(uint32 min, uint32 max));
+    void GenerateSecWebSocketKey(uint32* number, std::string* key);
+    void GenerateKey3();
+
+    uint32 number_1_;
+    uint32 number_2_;
+    std::string key_1_;
+    std::string key_2_;
+    std::string key_3_;
+
+    static uint32 (*rand_)(uint32 min, uint32 max);
+  };
+
+  virtual bool ProcessHeaders(const HttpResponseHeaders& headers);
+  virtual bool CheckResponseHeaders() const;
+
+  scoped_ptr<Parameter> parameter_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketHandshake);
+};
+
+}  // namespace net
+
+#endif  // NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_H_
diff --git a/net/websockets/websocket_handshake_draft75.cc b/net/websockets/websocket_handshake_draft75.cc
new file mode 100644
index 0000000..78805fb
--- /dev/null
+++ b/net/websockets/websocket_handshake_draft75.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2010 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/websockets/websocket_handshake_draft75.h"
+
+#include "base/ref_counted.h"
+#include "base/string_util.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+
+namespace net {
+
+const char WebSocketHandshakeDraft75::kServerHandshakeHeader[] =
+    "HTTP/1.1 101 Web Socket Protocol Handshake\r\n";
+const size_t WebSocketHandshakeDraft75::kServerHandshakeHeaderLength =
+    sizeof(kServerHandshakeHeader) - 1;
+
+const char WebSocketHandshakeDraft75::kUpgradeHeader[] =
+    "Upgrade: WebSocket\r\n";
+const size_t WebSocketHandshakeDraft75::kUpgradeHeaderLength =
+    sizeof(kUpgradeHeader) - 1;
+
+const char WebSocketHandshakeDraft75::kConnectionHeader[] =
+    "Connection: Upgrade\r\n";
+const size_t WebSocketHandshakeDraft75::kConnectionHeaderLength =
+    sizeof(kConnectionHeader) - 1;
+
+WebSocketHandshakeDraft75::WebSocketHandshakeDraft75(
+    const GURL& url,
+    const std::string& origin,
+    const std::string& location,
+    const std::string& protocol)
+    : WebSocketHandshake(url, origin, location, protocol) {
+}
+
+WebSocketHandshakeDraft75::~WebSocketHandshakeDraft75() {
+}
+
+std::string WebSocketHandshakeDraft75::CreateClientHandshakeMessage() {
+  std::string msg;
+  msg = "GET ";
+  msg += GetResourceName();
+  msg += " HTTP/1.1\r\n";
+  msg += kUpgradeHeader;
+  msg += kConnectionHeader;
+  msg += "Host: ";
+  msg += GetHostFieldValue();
+  msg += "\r\n";
+  msg += "Origin: ";
+  msg += GetOriginFieldValue();
+  msg += "\r\n";
+  if (!protocol_.empty()) {
+    msg += "WebSocket-Protocol: ";
+    msg += protocol_;
+    msg += "\r\n";
+  }
+  // TODO(ukai): Add cookie if necessary.
+  msg += "\r\n";
+  return msg;
+}
+
+int WebSocketHandshakeDraft75::ReadServerHandshake(
+    const char* data, size_t len) {
+  mode_ = MODE_INCOMPLETE;
+  if (len < kServerHandshakeHeaderLength) {
+    return -1;
+  }
+  if (!memcmp(data, kServerHandshakeHeader, kServerHandshakeHeaderLength)) {
+    mode_ = MODE_NORMAL;
+  } else {
+    int eoh = HttpUtil::LocateEndOfHeaders(data, len);
+    if (eoh < 0)
+      return -1;
+    return eoh;
+  }
+  const char* p = data + kServerHandshakeHeaderLength;
+  const char* end = data + len;
+
+  if (mode_ == MODE_NORMAL) {
+    size_t header_size = end - p;
+    if (header_size < kUpgradeHeaderLength)
+      return -1;
+    if (memcmp(p, kUpgradeHeader, kUpgradeHeaderLength)) {
+      mode_ = MODE_FAILED;
+      DLOG(INFO) << "Bad Upgrade Header "
+                 << std::string(p, kUpgradeHeaderLength);
+      return p - data;
+    }
+    p += kUpgradeHeaderLength;
+    header_size = end - p;
+    if (header_size < kConnectionHeaderLength)
+      return -1;
+    if (memcmp(p, kConnectionHeader, kConnectionHeaderLength)) {
+      mode_ = MODE_FAILED;
+      DLOG(INFO) << "Bad Connection Header "
+                 << std::string(p, kConnectionHeaderLength);
+      return p - data;
+    }
+    p += kConnectionHeaderLength;
+  }
+
+  int eoh = HttpUtil::LocateEndOfHeaders(data, len);
+  if (eoh == -1)
+    return eoh;
+
+  scoped_refptr<HttpResponseHeaders> headers(
+      new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(data, eoh)));
+  if (!ProcessHeaders(*headers)) {
+    DLOG(INFO) << "Process Headers failed: "
+               << std::string(data, eoh);
+    mode_ = MODE_FAILED;
+  }
+  switch (mode_) {
+    case MODE_NORMAL:
+      if (CheckResponseHeaders()) {
+        mode_ = MODE_CONNECTED;
+      } else {
+        mode_ = MODE_FAILED;
+      }
+      break;
+    default:
+      mode_ = MODE_FAILED;
+      break;
+  }
+  return eoh;
+}
+
+bool WebSocketHandshakeDraft75::ProcessHeaders(
+    const HttpResponseHeaders& headers) {
+  if (!GetSingleHeader(headers, "websocket-origin", &ws_origin_))
+    return false;
+
+  if (!GetSingleHeader(headers, "websocket-location", &ws_location_))
+    return false;
+
+  // If |protocol_| is not specified by client, we don't care if there's
+  // protocol field or not as specified in the spec.
+  if (!protocol_.empty()
+      && !GetSingleHeader(headers, "websocket-protocol", &ws_protocol_))
+    return false;
+  return true;
+}
+
+bool WebSocketHandshakeDraft75::CheckResponseHeaders() const {
+  DCHECK(mode_ == MODE_NORMAL);
+  if (!LowerCaseEqualsASCII(origin_, ws_origin_.c_str()))
+    return false;
+  if (location_ != ws_location_)
+    return false;
+  if (!protocol_.empty() && protocol_ != ws_protocol_)
+    return false;
+  return true;
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_handshake_draft75.h b/net/websockets/websocket_handshake_draft75.h
new file mode 100644
index 0000000..6cc0506
--- /dev/null
+++ b/net/websockets/websocket_handshake_draft75.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2010 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_WEBSOCKETS_WEBSOCKET_HANDSHAKE_DRAFT75_H_
+#define NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_DRAFT75_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "googleurl/src/gurl.h"
+#include "net/websockets/websocket_handshake.h"
+
+namespace net {
+
+class HttpResponseHeaders;
+
+class WebSocketHandshakeDraft75 : public WebSocketHandshake {
+ public:
+  static const int kWebSocketPort;
+  static const int kSecureWebSocketPort;
+  static const char kServerHandshakeHeader[];
+  static const size_t kServerHandshakeHeaderLength;
+  static const char kUpgradeHeader[];
+  static const size_t kUpgradeHeaderLength;
+  static const char kConnectionHeader[];
+  static const size_t kConnectionHeaderLength;
+
+  WebSocketHandshakeDraft75(const GURL& url,
+                            const std::string& origin,
+                            const std::string& location,
+                            const std::string& protocol);
+  virtual ~WebSocketHandshakeDraft75();
+
+  // Creates the client handshake message from |this|.
+  virtual std::string CreateClientHandshakeMessage();
+
+  // Reads server handshake message in |len| of |data|, updates |mode_| and
+  // returns number of bytes of the server handshake message.
+  // Once connection is established, |mode_| will be MODE_CONNECTED.
+  // If connection establishment failed, |mode_| will be MODE_FAILED.
+  // Returns negative if the server handshake message is incomplete.
+  virtual int ReadServerHandshake(const char* data, size_t len);
+
+ private:
+  // Processes server handshake message, parsed as |headers|, and updates
+  // |ws_origin_|, |ws_location_| and |ws_protocol_|.
+  // Returns true if it's ok.
+  // Returns false otherwise (e.g. duplicate WebSocket-Origin: header, etc.)
+  virtual bool ProcessHeaders(const HttpResponseHeaders& headers);
+
+  // Checks |ws_origin_|, |ws_location_| and |ws_protocol_| are valid
+  // against |origin_|, |location_| and |protocol_|.
+  // Returns true if it's ok.
+  // Returns false otherwise (e.g. origin mismatch, etc.)
+  virtual bool CheckResponseHeaders() const;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeDraft75);
+};
+
+}  // namespace net
+
+#endif  // NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_DRAFT75_H_
diff --git a/net/websockets/websocket_handshake_draft75_unittest.cc b/net/websockets/websocket_handshake_draft75_unittest.cc
new file mode 100644
index 0000000..aff75ad
--- /dev/null
+++ b/net/websockets/websocket_handshake_draft75_unittest.cc
@@ -0,0 +1,217 @@
+// Copyright (c) 2010 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 <vector>
+
+#include "base/scoped_ptr.h"
+#include "net/websockets/websocket_handshake_draft75.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/platform_test.h"
+
+namespace net {
+
+TEST(WebSocketHandshakeDraft75Test, Connect) {
+  const std::string kExpectedClientHandshakeMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Host: example.com\r\n"
+      "Origin: http://example.com\r\n"
+      "WebSocket-Protocol: sample\r\n"
+      "\r\n";
+
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("ws://example.com/demo"),
+                                    "http://example.com",
+                                    "ws://example.com/demo",
+                                    "sample"));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+  EXPECT_EQ(kExpectedClientHandshakeMessage,
+            handshake->CreateClientHandshakeMessage());
+
+  const char kResponse[] = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "WebSocket-Origin: http://example.com\r\n"
+      "WebSocket-Location: ws://example.com/demo\r\n"
+      "WebSocket-Protocol: sample\r\n"
+      "\r\n";
+
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+  // too short
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(kResponse, 16));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+  // only status line
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(
+      kResponse,
+      WebSocketHandshakeDraft75::kServerHandshakeHeaderLength));
+  EXPECT_EQ(WebSocketHandshake::MODE_NORMAL, handshake->mode());
+  // by upgrade header
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(
+      kResponse,
+      WebSocketHandshakeDraft75::kServerHandshakeHeaderLength +
+      WebSocketHandshakeDraft75::kUpgradeHeaderLength));
+  EXPECT_EQ(WebSocketHandshake::MODE_NORMAL, handshake->mode());
+  // by connection header
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(
+      kResponse,
+      WebSocketHandshakeDraft75::kServerHandshakeHeaderLength +
+      WebSocketHandshakeDraft75::kUpgradeHeaderLength +
+      WebSocketHandshakeDraft75::kConnectionHeaderLength));
+  EXPECT_EQ(WebSocketHandshake::MODE_NORMAL, handshake->mode());
+
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(
+      kResponse, sizeof(kResponse) - 2));
+  EXPECT_EQ(WebSocketHandshake::MODE_NORMAL, handshake->mode());
+
+  int handshake_length = strlen(kResponse);
+  EXPECT_EQ(handshake_length, handshake->ReadServerHandshake(
+      kResponse, sizeof(kResponse) - 1));  // -1 for terminating \0
+  EXPECT_EQ(WebSocketHandshake::MODE_CONNECTED, handshake->mode());
+}
+
+TEST(WebSocketHandshakeDraft75Test, ServerSentData) {
+  const std::string kExpectedClientHandshakeMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Host: example.com\r\n"
+      "Origin: http://example.com\r\n"
+      "WebSocket-Protocol: sample\r\n"
+      "\r\n";
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("ws://example.com/demo"),
+                                    "http://example.com",
+                                    "ws://example.com/demo",
+                                    "sample"));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+  EXPECT_EQ(kExpectedClientHandshakeMessage,
+            handshake->CreateClientHandshakeMessage());
+
+  const char kResponse[] ="HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "WebSocket-Origin: http://example.com\r\n"
+      "WebSocket-Location: ws://example.com/demo\r\n"
+      "WebSocket-Protocol: sample\r\n"
+      "\r\n"
+      "\0Hello\xff";
+
+  int handshake_length = strlen(kResponse);
+  EXPECT_EQ(handshake_length, handshake->ReadServerHandshake(
+      kResponse, sizeof(kResponse) - 1));  // -1 for terminating \0
+  EXPECT_EQ(WebSocketHandshake::MODE_CONNECTED, handshake->mode());
+}
+
+TEST(WebSocketHandshakeDraft75Test, CreateClientHandshakeMessage_Simple) {
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("ws://example.com/demo"),
+                                    "http://example.com",
+                                    "ws://example.com/demo",
+                                    "sample"));
+  EXPECT_EQ("GET /demo HTTP/1.1\r\n"
+            "Upgrade: WebSocket\r\n"
+            "Connection: Upgrade\r\n"
+            "Host: example.com\r\n"
+            "Origin: http://example.com\r\n"
+            "WebSocket-Protocol: sample\r\n"
+            "\r\n",
+            handshake->CreateClientHandshakeMessage());
+}
+
+TEST(WebSocketHandshakeDraft75Test, CreateClientHandshakeMessage_PathAndQuery) {
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("ws://example.com/Test?q=xxx&p=%20"),
+                                    "http://example.com",
+                                    "ws://example.com/demo",
+                                    "sample"));
+  // Path and query should be preserved as-is.
+  EXPECT_THAT(handshake->CreateClientHandshakeMessage(),
+              testing::HasSubstr("GET /Test?q=xxx&p=%20 HTTP/1.1\r\n"));
+}
+
+TEST(WebSocketHandshakeDraft75Test, CreateClientHandshakeMessage_Host) {
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("ws://Example.Com/demo"),
+                                    "http://Example.Com",
+                                    "ws://Example.Com/demo",
+                                    "sample"));
+  // Host should be lowercased
+  EXPECT_THAT(handshake->CreateClientHandshakeMessage(),
+              testing::HasSubstr("Host: example.com\r\n"));
+  EXPECT_THAT(handshake->CreateClientHandshakeMessage(),
+              testing::HasSubstr("Origin: http://example.com\r\n"));
+}
+
+TEST(WebSocketHandshakeDraft75Test, CreateClientHandshakeMessage_TrimPort80) {
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("ws://example.com:80/demo"),
+                                    "http://example.com",
+                                    "ws://example.com/demo",
+                                    "sample"));
+  // :80 should be trimmed as it's the default port for ws://.
+  EXPECT_THAT(handshake->CreateClientHandshakeMessage(),
+              testing::HasSubstr("Host: example.com\r\n"));
+}
+
+TEST(WebSocketHandshakeDraft75Test, CreateClientHandshakeMessage_TrimPort443) {
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("wss://example.com:443/demo"),
+                                    "http://example.com",
+                                    "wss://example.com/demo",
+                                    "sample"));
+  // :443 should be trimmed as it's the default port for wss://.
+  EXPECT_THAT(handshake->CreateClientHandshakeMessage(),
+              testing::HasSubstr("Host: example.com\r\n"));
+}
+
+TEST(WebSocketHandshakeDraft75Test,
+     CreateClientHandshakeMessage_NonDefaultPortForWs) {
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("ws://example.com:8080/demo"),
+                                    "http://example.com",
+                                    "wss://example.com/demo",
+                                    "sample"));
+  // :8080 should be preserved as it's not the default port for ws://.
+  EXPECT_THAT(handshake->CreateClientHandshakeMessage(),
+              testing::HasSubstr("Host: example.com:8080\r\n"));
+}
+
+TEST(WebSocketHandshakeDraft75Test,
+     CreateClientHandshakeMessage_NonDefaultPortForWss) {
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("wss://example.com:4443/demo"),
+                                    "http://example.com",
+                                    "wss://example.com/demo",
+                                    "sample"));
+  // :4443 should be preserved as it's not the default port for wss://.
+  EXPECT_THAT(handshake->CreateClientHandshakeMessage(),
+              testing::HasSubstr("Host: example.com:4443\r\n"));
+}
+
+TEST(WebSocketHandshakeDraft75Test, CreateClientHandshakeMessage_WsBut443) {
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("ws://example.com:443/demo"),
+                                    "http://example.com",
+                                    "ws://example.com/demo",
+                                    "sample"));
+  // :443 should be preserved as it's not the default port for ws://.
+  EXPECT_THAT(handshake->CreateClientHandshakeMessage(),
+              testing::HasSubstr("Host: example.com:443\r\n"));
+}
+
+TEST(WebSocketHandshakeDraft75Test, CreateClientHandshakeMessage_WssBut80) {
+  scoped_ptr<WebSocketHandshakeDraft75> handshake(
+      new WebSocketHandshakeDraft75(GURL("wss://example.com:80/demo"),
+                                    "http://example.com",
+                                    "wss://example.com/demo",
+                                    "sample"));
+  // :80 should be preserved as it's not the default port for wss://.
+  EXPECT_THAT(handshake->CreateClientHandshakeMessage(),
+              testing::HasSubstr("Host: example.com:80\r\n"));
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_handshake_handler.cc b/net/websockets/websocket_handshake_handler.cc
new file mode 100644
index 0000000..5278151
--- /dev/null
+++ b/net/websockets/websocket_handshake_handler.cc
@@ -0,0 +1,424 @@
+// Copyright (c) 2010 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/websockets/websocket_handshake_handler.h"
+
+#include "base/md5.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+#include "net/http/http_util.h"
+
+namespace {
+
+const size_t kRequestKey3Size = 8U;
+const size_t kResponseKeySize = 16U;
+
+void ParseHandshakeHeader(
+    const char* handshake_message, int len,
+    std::string* status_line,
+    std::string* headers) {
+  size_t i = base::StringPiece(handshake_message, len).find_first_of("\r\n");
+  if (i == base::StringPiece::npos) {
+    *status_line = std::string(handshake_message, len);
+    *headers = "";
+    return;
+  }
+  // |status_line| includes \r\n.
+  *status_line = std::string(handshake_message, i + 2);
+
+  int header_len = len - (i + 2) - 2;
+  if (header_len > 0) {
+    // |handshake_message| includes tailing \r\n\r\n.
+    // |headers| doesn't include 2nd \r\n.
+    *headers = std::string(handshake_message + i + 2, header_len);
+  } else {
+    *headers = "";
+  }
+}
+
+void FetchHeaders(const std::string& headers,
+                  const char* const headers_to_get[],
+                  size_t headers_to_get_len,
+                  std::vector<std::string>* values) {
+  net::HttpUtil::HeadersIterator iter(headers.begin(), headers.end(), "\r\n");
+  while (iter.GetNext()) {
+    for (size_t i = 0; i < headers_to_get_len; i++) {
+      if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
+                               headers_to_get[i])) {
+        values->push_back(iter.values());
+      }
+    }
+  }
+}
+
+bool GetHeaderName(std::string::const_iterator line_begin,
+                   std::string::const_iterator line_end,
+                   std::string::const_iterator* name_begin,
+                   std::string::const_iterator* name_end) {
+  std::string::const_iterator colon = std::find(line_begin, line_end, ':');
+  if (colon == line_end) {
+    return false;
+  }
+  *name_begin = line_begin;
+  *name_end = colon;
+  if (*name_begin == *name_end || net::HttpUtil::IsLWS(**name_begin))
+    return false;
+  net::HttpUtil::TrimLWS(name_begin, name_end);
+  return true;
+}
+
+// Similar to HttpUtil::StripHeaders, but it preserves malformed headers, that
+// is, lines that are not formatted as "<name>: <value>\r\n".
+std::string FilterHeaders(
+    const std::string& headers,
+    const char* const headers_to_remove[],
+    size_t headers_to_remove_len) {
+  std::string filtered_headers;
+
+  StringTokenizer lines(headers.begin(), headers.end(), "\r\n");
+  while (lines.GetNext()) {
+    std::string::const_iterator line_begin = lines.token_begin();
+    std::string::const_iterator line_end = lines.token_end();
+    std::string::const_iterator name_begin;
+    std::string::const_iterator name_end;
+    bool should_remove = false;
+    if (GetHeaderName(line_begin, line_end, &name_begin, &name_end)) {
+      for (size_t i = 0; i < headers_to_remove_len; ++i) {
+        if (LowerCaseEqualsASCII(name_begin, name_end, headers_to_remove[i])) {
+          should_remove = true;
+          break;
+        }
+      }
+    }
+    if (!should_remove) {
+      filtered_headers.append(line_begin, line_end);
+      filtered_headers.append("\r\n");
+    }
+  }
+  return filtered_headers;
+}
+
+// Gets a key number from |key| and appends the number to |challenge|.
+// The key number (/part_N/) is extracted as step 4.-8. in
+// 5.2. Sending the server's opening handshake of
+// http://www.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-00.txt
+void GetKeyNumber(const std::string& key, std::string* challenge) {
+  uint32 key_number = 0;
+  uint32 spaces = 0;
+  for (size_t i = 0; i < key.size(); ++i) {
+    if (isdigit(key[i])) {
+      // key_number should not overflow. (it comes from
+      // WebCore/websockets/WebSocketHandshake.cpp).
+      key_number = key_number * 10 + key[i] - '0';
+    } else if (key[i] == ' ') {
+      ++spaces;
+    }
+  }
+  // spaces should not be zero in valid handshake request.
+  if (spaces == 0)
+    return;
+  key_number /= spaces;
+
+  char part[4];
+  for (int i = 0; i < 4; i++) {
+    part[3 - i] = key_number & 0xFF;
+    key_number >>= 8;
+  }
+  challenge->append(part, 4);
+}
+
+}  // anonymous namespace
+
+namespace net {
+
+WebSocketHandshakeRequestHandler::WebSocketHandshakeRequestHandler()
+    : original_length_(0),
+      raw_length_(0) {}
+
+bool WebSocketHandshakeRequestHandler::ParseRequest(
+    const char* data, int length) {
+  DCHECK_GT(length, 0);
+  std::string input(data, length);
+  int input_header_length =
+      HttpUtil::LocateEndOfHeaders(input.data(), input.size(), 0);
+  if (input_header_length <= 0 ||
+      input_header_length + kRequestKey3Size > input.size())
+    return false;
+
+  ParseHandshakeHeader(input.data(),
+                       input_header_length,
+                       &status_line_,
+                       &headers_);
+
+  // draft-hixie-thewebsocketprotocol-76 or later will send /key3/
+  // after handshake request header.
+  // Assumes WebKit doesn't send any data after handshake request message
+  // until handshake is finished.
+  // Thus, |key3_| is part of handshake message, and not in part
+  // of WebSocket frame stream.
+  DCHECK_EQ(kRequestKey3Size,
+            input.size() -
+            input_header_length);
+  key3_ = std::string(input.data() + input_header_length,
+                      input.size() - input_header_length);
+  original_length_ = input.size();
+  return true;
+}
+
+size_t WebSocketHandshakeRequestHandler::original_length() const {
+  return original_length_;
+}
+
+void WebSocketHandshakeRequestHandler::AppendHeaderIfMissing(
+    const std::string& name, const std::string& value) {
+  DCHECK(headers_.size() > 0);
+  HttpUtil::AppendHeaderIfMissing(name.c_str(), value, &headers_);
+}
+
+void WebSocketHandshakeRequestHandler::RemoveHeaders(
+    const char* const headers_to_remove[],
+    size_t headers_to_remove_len) {
+  DCHECK(headers_.size() > 0);
+  headers_ = FilterHeaders(
+      headers_, headers_to_remove, headers_to_remove_len);
+}
+
+HttpRequestInfo WebSocketHandshakeRequestHandler::GetRequestInfo(
+    const GURL& url, std::string* challenge) {
+  HttpRequestInfo request_info;
+  request_info.url = url;
+  base::StringPiece method = status_line_.data();
+  size_t method_end = base::StringPiece(
+      status_line_.data(), status_line_.size()).find_first_of(" ");
+  if (method_end != base::StringPiece::npos)
+    request_info.method = std::string(status_line_.data(), method_end);
+
+  request_info.extra_headers.Clear();
+  request_info.extra_headers.AddHeadersFromString(headers_);
+
+  request_info.extra_headers.RemoveHeader("Upgrade");
+  request_info.extra_headers.RemoveHeader("Connection");
+
+  challenge->clear();
+  std::string key;
+  request_info.extra_headers.GetHeader("Sec-WebSocket-Key1", &key);
+  request_info.extra_headers.RemoveHeader("Sec-WebSocket-Key1");
+  GetKeyNumber(key, challenge);
+
+  request_info.extra_headers.GetHeader("Sec-WebSocket-Key2", &key);
+  request_info.extra_headers.RemoveHeader("Sec-WebSocket-Key2");
+  GetKeyNumber(key, challenge);
+
+  challenge->append(key3_);
+
+  return request_info;
+}
+
+bool WebSocketHandshakeRequestHandler::GetRequestHeaderBlock(
+    const GURL& url, spdy::SpdyHeaderBlock* headers, std::string* challenge) {
+  // We don't set "method" and "version".  These are fixed value in WebSocket
+  // protocol.
+  (*headers)["url"] = url.spec();
+
+  std::string key1;
+  std::string key2;
+  HttpUtil::HeadersIterator iter(headers_.begin(), headers_.end(), "\r\n");
+  while (iter.GetNext()) {
+    if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
+                             "connection")) {
+      // Ignore "Connection" header.
+      continue;
+    } else if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
+                                    "upgrade")) {
+      // Ignore "Upgrade" header.
+      continue;
+    } else if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
+                                    "sec-websocket-key1")) {
+      // Use only for generating challenge.
+      key1 = iter.values();
+      continue;
+    } else if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
+                                    "sec-websocket-key2")) {
+      // Use only for generating challenge.
+      key2 = iter.values();
+      continue;
+    }
+    // Others should be sent out to |headers|.
+    std::string name = StringToLowerASCII(iter.name());
+    spdy::SpdyHeaderBlock::iterator found = headers->find(name);
+    if (found == headers->end()) {
+      (*headers)[name] = iter.values();
+    } else {
+      // For now, websocket doesn't use multiple headers, but follows to http.
+      found->second.append(1, '\0');  // +=() doesn't append 0's
+      found->second.append(iter.values());
+    }
+  }
+
+  challenge->clear();
+  GetKeyNumber(key1, challenge);
+  GetKeyNumber(key2, challenge);
+  challenge->append(key3_);
+
+  return true;
+}
+
+std::string WebSocketHandshakeRequestHandler::GetRawRequest() {
+  DCHECK(status_line_.size() > 0);
+  DCHECK(headers_.size() > 0);
+  DCHECK_EQ(kRequestKey3Size, key3_.size());
+  std::string raw_request = status_line_ + headers_ + "\r\n" + key3_;
+  raw_length_ = raw_request.size();
+  return raw_request;
+}
+
+size_t WebSocketHandshakeRequestHandler::raw_length() const {
+  DCHECK_GT(raw_length_, 0);
+  return raw_length_;
+}
+
+WebSocketHandshakeResponseHandler::WebSocketHandshakeResponseHandler()
+    : original_header_length_(0) {
+}
+
+size_t WebSocketHandshakeResponseHandler::ParseRawResponse(
+    const char* data, int length) {
+  DCHECK_GT(length, 0);
+  if (HasResponse()) {
+    DCHECK(status_line_.size() > 0);
+    DCHECK(headers_.size() > 0);
+    DCHECK_EQ(kResponseKeySize, key_.size());
+    return 0;
+  }
+
+  size_t old_original_length = original_.size();
+
+  original_.append(data, length);
+  // TODO(ukai): fail fast when response gives wrong status code.
+  original_header_length_ = HttpUtil::LocateEndOfHeaders(
+      original_.data(), original_.size(), 0);
+  if (!HasResponse())
+    return length;
+
+  ParseHandshakeHeader(original_.data(),
+                       original_header_length_,
+                       &status_line_,
+                       &headers_);
+  int header_size = status_line_.size() + headers_.size();
+  DCHECK_GE(original_header_length_, header_size);
+  header_separator_ = std::string(original_.data() + header_size,
+                                  original_header_length_ - header_size);
+  key_ = std::string(original_.data() + original_header_length_,
+                     kResponseKeySize);
+
+  return original_header_length_ + kResponseKeySize - old_original_length;
+}
+
+bool WebSocketHandshakeResponseHandler::HasResponse() const {
+  return original_header_length_ > 0 &&
+      original_header_length_ + kResponseKeySize <= original_.size();
+}
+
+bool WebSocketHandshakeResponseHandler::ParseResponseInfo(
+    const HttpResponseInfo& response_info,
+    const std::string& challenge) {
+  if (!response_info.headers.get())
+    return false;
+
+  std::string response_message;
+  response_message = response_info.headers->GetStatusLine();
+  response_message += "\r\n";
+  response_message += "Upgrade: WebSocket\r\n";
+  response_message += "Connection: Upgrade\r\n";
+  void* iter = NULL;
+  std::string name;
+  std::string value;
+  while (response_info.headers->EnumerateHeaderLines(&iter, &name, &value)) {
+    response_message += name + ": " + value + "\r\n";
+  }
+  response_message += "\r\n";
+
+  MD5Digest digest;
+  MD5Sum(challenge.data(), challenge.size(), &digest);
+
+  const char* digest_data = reinterpret_cast<char*>(digest.a);
+  response_message.append(digest_data, sizeof(digest.a));
+
+  return ParseRawResponse(response_message.data(),
+                          response_message.size()) == response_message.size();
+}
+
+bool WebSocketHandshakeResponseHandler::ParseResponseHeaderBlock(
+    const spdy::SpdyHeaderBlock& headers,
+    const std::string& challenge) {
+  std::string response_message;
+  response_message = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n";
+  response_message += "Upgrade: WebSocket\r\n";
+  response_message += "Connection: Upgrade\r\n";
+  for (spdy::SpdyHeaderBlock::const_iterator iter = headers.begin();
+       iter != headers.end();
+       ++iter) {
+    // For each value, if the server sends a NUL-separated list of values,
+    // we separate that back out into individual headers for each value
+    // in the list.
+    const std::string& value = iter->second;
+    size_t start = 0;
+    size_t end = 0;
+    do {
+      end = value.find('\0', start);
+      std::string tval;
+      if (end != std::string::npos)
+        tval = value.substr(start, (end - start));
+      else
+        tval = value.substr(start);
+      response_message += iter->first + ": " + tval + "\r\n";
+      start = end + 1;
+    } while (end != std::string::npos);
+  }
+  response_message += "\r\n";
+
+  MD5Digest digest;
+  MD5Sum(challenge.data(), challenge.size(), &digest);
+
+  const char* digest_data = reinterpret_cast<char*>(digest.a);
+  response_message.append(digest_data, sizeof(digest.a));
+
+  return ParseRawResponse(response_message.data(),
+                          response_message.size()) == response_message.size();
+}
+
+void WebSocketHandshakeResponseHandler::GetHeaders(
+    const char* const headers_to_get[],
+    size_t headers_to_get_len,
+    std::vector<std::string>* values) {
+  DCHECK(HasResponse());
+  DCHECK(status_line_.size() > 0);
+  DCHECK(headers_.size() > 0);
+  DCHECK_EQ(kResponseKeySize, key_.size());
+
+  FetchHeaders(headers_, headers_to_get, headers_to_get_len, values);
+}
+
+void WebSocketHandshakeResponseHandler::RemoveHeaders(
+    const char* const headers_to_remove[],
+    size_t headers_to_remove_len) {
+  DCHECK(HasResponse());
+  DCHECK(status_line_.size() > 0);
+  DCHECK(headers_.size() > 0);
+  DCHECK_EQ(kResponseKeySize, key_.size());
+
+  headers_ = FilterHeaders(headers_, headers_to_remove, headers_to_remove_len);
+}
+
+std::string WebSocketHandshakeResponseHandler::GetResponse() {
+  DCHECK(HasResponse());
+  DCHECK(status_line_.size() > 0);
+  // headers_ might be empty for wrong response from server.
+  DCHECK_EQ(kResponseKeySize, key_.size());
+
+  return status_line_ + headers_ + header_separator_ + key_;
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_handshake_handler.h b/net/websockets/websocket_handshake_handler.h
new file mode 100644
index 0000000..cf5700b
--- /dev/null
+++ b/net/websockets/websocket_handshake_handler.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2010 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.
+//
+// WebSocketHandshake*Handler handles WebSocket handshake request message
+// from WebKit renderer process, and WebSocket handshake response message
+// from WebSocket server.
+// It modifies messages for the following reason:
+// - We don't trust WebKit renderer process, so we'll not expose HttpOnly
+//   cookies to the renderer process, so handles HttpOnly cookies in
+//   browser process.
+
+#ifndef NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_HANDLER_H_
+#define NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_HANDLER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
+#include "net/spdy/spdy_framer.h"
+
+namespace net {
+
+class WebSocketHandshakeRequestHandler {
+ public:
+  WebSocketHandshakeRequestHandler();
+  ~WebSocketHandshakeRequestHandler() {}
+
+  // Parses WebSocket handshake request from renderer process.
+  // It assumes a WebSocket handshake request message is given at once, and
+  // no other data is added to the request message.
+  bool ParseRequest(const char* data, int length);
+
+  size_t original_length() const;
+
+  // Appends the header value pair for |name| and |value|, if |name| doesn't
+  // exist.
+  void AppendHeaderIfMissing(const std::string& name,
+                             const std::string& value);
+  // Removes the headers that matches (case insensitive).
+  void RemoveHeaders(const char* const headers_to_remove[],
+                     size_t headers_to_remove_len);
+
+  // Gets request info to open WebSocket connection.
+  // Also, fill challange data in |challenge|.
+  HttpRequestInfo GetRequestInfo(const GURL& url, std::string* challenge);
+  // Gets request as SpdyHeaderBlock.
+  // Also, fill challenge data in |challenge|.
+  bool GetRequestHeaderBlock(const GURL& url,
+                             spdy::SpdyHeaderBlock* headers,
+                             std::string* challenge);
+  // Gets WebSocket handshake raw request message to open WebSocket
+  // connection.
+  std::string GetRawRequest();
+  // Calling raw_length is valid only after GetRawRquest() call.
+  size_t raw_length() const;
+
+ private:
+  std::string status_line_;
+  std::string headers_;
+  std::string key3_;
+  int original_length_;
+  int raw_length_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeRequestHandler);
+};
+
+class WebSocketHandshakeResponseHandler {
+ public:
+  WebSocketHandshakeResponseHandler();
+  ~WebSocketHandshakeResponseHandler() {}
+
+  // Parses WebSocket handshake response from WebSocket server.
+  // Returns number of bytes in |data| used for WebSocket handshake response
+  // message, including response key.  If it already got whole WebSocket
+  // handshake response message, returns zero.  In other words,
+  // [data + returned value, data + length) will be WebSocket frame data
+  // after handshake response message.
+  // TODO(ukai): fail fast when response gives wrong status code.
+  size_t ParseRawResponse(const char* data, int length);
+  // Returns true if it already parses full handshake response message.
+  bool HasResponse() const;
+  // Parses WebSocket handshake response info given as HttpResponseInfo.
+  bool ParseResponseInfo(const HttpResponseInfo& response_info,
+                         const std::string& challenge);
+  // Parses WebSocket handshake response as SpdyHeaderBlock.
+  bool ParseResponseHeaderBlock(const spdy::SpdyHeaderBlock& headers,
+                                const std::string& challenge);
+
+  // Gets the headers value.
+  void GetHeaders(const char* const headers_to_get[],
+                  size_t headers_to_get_len,
+                  std::vector<std::string>* values);
+  // Removes the headers that matches (case insensitive).
+  void RemoveHeaders(const char* const headers_to_remove[],
+                     size_t headers_to_remove_len);
+
+  // Gets WebSocket handshake response message sent to renderer process.
+  std::string GetResponse();
+
+ private:
+  std::string original_;
+  int original_header_length_;
+  std::string status_line_;
+  std::string headers_;
+  std::string header_separator_;
+  std::string key_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketHandshakeResponseHandler);
+};
+
+}  // namespace net
+
+#endif  // NET_WEBSOCKETS_WEBSOCKET_HANDSHAKE_HANDLER_H_
diff --git a/net/websockets/websocket_handshake_handler_unittest.cc b/net/websockets/websocket_handshake_handler_unittest.cc
new file mode 100644
index 0000000..65e0712
--- /dev/null
+++ b/net/websockets/websocket_handshake_handler_unittest.cc
@@ -0,0 +1,404 @@
+// Copyright (c) 2010 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 <vector>
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+#include "googleurl/src/gurl.h"
+#include "net/http/http_util.h"
+#include "net/websockets/websocket_handshake_handler.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace {
+
+const char* const kCookieHeaders[] = {
+  "cookie", "cookie2"
+};
+
+const char* const kSetCookieHeaders[] = {
+  "set-cookie", "set-cookie2"
+};
+
+}
+
+namespace net {
+
+TEST(WebSocketHandshakeRequestHandlerTest, SimpleRequest) {
+  WebSocketHandshakeRequestHandler handler;
+
+  static const char* kHandshakeRequestMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  EXPECT_TRUE(handler.ParseRequest(kHandshakeRequestMessage,
+                                   strlen(kHandshakeRequestMessage)));
+
+  handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders));
+
+  EXPECT_EQ(kHandshakeRequestMessage, handler.GetRawRequest());
+}
+
+TEST(WebSocketHandshakeRequestHandlerTest, ReplaceRequestCookies) {
+  WebSocketHandshakeRequestHandler handler;
+
+  static const char* kHandshakeRequestMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "Cookie: WK-websocket-test=1\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  EXPECT_TRUE(handler.ParseRequest(kHandshakeRequestMessage,
+                                   strlen(kHandshakeRequestMessage)));
+
+  handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders));
+
+  handler.AppendHeaderIfMissing("Cookie",
+                                "WK-websocket-test=1; "
+                                "WK-websocket-test-httponly=1");
+
+  static const char* kHandshakeRequestExpectedMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "Cookie: WK-websocket-test=1; WK-websocket-test-httponly=1\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  EXPECT_EQ(kHandshakeRequestExpectedMessage, handler.GetRawRequest());
+}
+
+TEST(WebSocketHandshakeResponseHandlerTest, SimpleResponse) {
+  WebSocketHandshakeResponseHandler handler;
+
+  static const char* kHandshakeResponseMessage =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  EXPECT_EQ(strlen(kHandshakeResponseMessage),
+            handler.ParseRawResponse(kHandshakeResponseMessage,
+                                     strlen(kHandshakeResponseMessage)));
+  EXPECT_TRUE(handler.HasResponse());
+
+  handler.RemoveHeaders(kCookieHeaders, arraysize(kCookieHeaders));
+
+  EXPECT_EQ(kHandshakeResponseMessage, handler.GetResponse());
+}
+
+TEST(WebSocketHandshakeResponseHandlerTest, ReplaceResponseCookies) {
+  WebSocketHandshakeResponseHandler handler;
+
+  static const char* kHandshakeResponseMessage =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Set-Cookie: WK-websocket-test-1\r\n"
+      "Set-Cookie: WK-websocket-test-httponly=1; HttpOnly\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  EXPECT_EQ(strlen(kHandshakeResponseMessage),
+            handler.ParseRawResponse(kHandshakeResponseMessage,
+                                     strlen(kHandshakeResponseMessage)));
+  EXPECT_TRUE(handler.HasResponse());
+  std::vector<std::string> cookies;
+  handler.GetHeaders(kSetCookieHeaders, arraysize(kSetCookieHeaders), &cookies);
+  ASSERT_EQ(2U, cookies.size());
+  EXPECT_EQ("WK-websocket-test-1", cookies[0]);
+  EXPECT_EQ("WK-websocket-test-httponly=1; HttpOnly", cookies[1]);
+  handler.RemoveHeaders(kSetCookieHeaders, arraysize(kSetCookieHeaders));
+
+  static const char* kHandshakeResponseExpectedMessage =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  EXPECT_EQ(kHandshakeResponseExpectedMessage, handler.GetResponse());
+}
+
+TEST(WebSocketHandshakeResponseHandlerTest, BadResponse) {
+  WebSocketHandshakeResponseHandler handler;
+
+  static const char* kBadMessage = "\n\n\r\net-Location: w";
+  EXPECT_EQ(strlen(kBadMessage),
+            handler.ParseRawResponse(kBadMessage, strlen(kBadMessage)));
+  EXPECT_TRUE(handler.HasResponse());
+  EXPECT_EQ(kBadMessage, handler.GetResponse());
+}
+
+TEST(WebSocketHandshakeResponseHandlerTest, BadResponse2) {
+  WebSocketHandshakeResponseHandler handler;
+
+  static const char* kBadMessage = "\n\r\n\r\net-Location: w";
+  EXPECT_EQ(strlen(kBadMessage),
+            handler.ParseRawResponse(kBadMessage, strlen(kBadMessage)));
+  EXPECT_TRUE(handler.HasResponse());
+  EXPECT_EQ(kBadMessage, handler.GetResponse());
+}
+
+TEST(WebSocketHandshakeHandlerTest, HttpRequestResponse) {
+  WebSocketHandshakeRequestHandler request_handler;
+
+  static const char* kHandshakeRequestMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage,
+                                           strlen(kHandshakeRequestMessage)));
+
+  GURL url("ws://example.com/demo");
+  std::string challenge;
+  const HttpRequestInfo& request_info =
+      request_handler.GetRequestInfo(url, &challenge);
+
+  EXPECT_EQ(url, request_info.url);
+  EXPECT_EQ("GET", request_info.method);
+  EXPECT_FALSE(request_info.extra_headers.HasHeader("Upgrade"));
+  EXPECT_FALSE(request_info.extra_headers.HasHeader("Connection"));
+  EXPECT_FALSE(request_info.extra_headers.HasHeader("Sec-WebSocket-Key1"));
+  EXPECT_FALSE(request_info.extra_headers.HasHeader("Sec-WebSocket-Key2"));
+  std::string value;
+  EXPECT_TRUE(request_info.extra_headers.GetHeader("Host", &value));
+  EXPECT_EQ("example.com", value);
+  EXPECT_TRUE(request_info.extra_headers.GetHeader("Origin", &value));
+  EXPECT_EQ("http://example.com", value);
+  EXPECT_TRUE(request_info.extra_headers.GetHeader("Sec-WebSocket-Protocol",
+                                                   &value));
+  EXPECT_EQ("sample", value);
+
+  const char expected_challenge[] = "\x31\x6e\x41\x13\x0f\x7e\xd6\x3c^n:ds[4U";
+
+  EXPECT_EQ(expected_challenge, challenge);
+
+  static const char* kHandshakeResponseHeader =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n";
+
+  std::string raw_headers =
+      HttpUtil::AssembleRawHeaders(kHandshakeResponseHeader,
+                                   strlen(kHandshakeResponseHeader));
+  HttpResponseInfo response_info;
+  response_info.headers = new HttpResponseHeaders(raw_headers);
+
+  EXPECT_TRUE(StartsWithASCII(response_info.headers->GetStatusLine(),
+                              "HTTP/1.1 101 ", false));
+  EXPECT_FALSE(response_info.headers->HasHeader("Upgrade"));
+  EXPECT_FALSE(response_info.headers->HasHeader("Connection"));
+  EXPECT_TRUE(response_info.headers->HasHeaderValue("Sec-WebSocket-Origin",
+                                                    "http://example.com"));
+  EXPECT_TRUE(response_info.headers->HasHeaderValue("Sec-WebSocket-Location",
+                                                    "ws://example.com/demo"));
+  EXPECT_TRUE(response_info.headers->HasHeaderValue("Sec-WebSocket-Protocol",
+                                                    "sample"));
+
+  WebSocketHandshakeResponseHandler response_handler;
+  EXPECT_TRUE(response_handler.ParseResponseInfo(response_info, challenge));
+  EXPECT_TRUE(response_handler.HasResponse());
+
+  static const char* kHandshakeResponseExpectedMessage =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse());
+}
+
+TEST(WebSocketHandshakeHandlerTest, SpdyRequestResponse) {
+  WebSocketHandshakeRequestHandler request_handler;
+
+  static const char* kHandshakeRequestMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "X-bogus-header: X\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "X-bogus-header: Y\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage,
+                                           strlen(kHandshakeRequestMessage)));
+
+  GURL url("ws://example.com/demo");
+  std::string challenge;
+  spdy::SpdyHeaderBlock headers;
+  ASSERT_TRUE(request_handler.GetRequestHeaderBlock(url, &headers, &challenge));
+
+  EXPECT_EQ(url.spec(), headers["url"]);
+  EXPECT_TRUE(headers.find("upgrade") == headers.end());
+  EXPECT_TRUE(headers.find("Upgrade") == headers.end());
+  EXPECT_TRUE(headers.find("connection") == headers.end());
+  EXPECT_TRUE(headers.find("Connection") == headers.end());
+  EXPECT_TRUE(headers.find("Sec-WebSocket-Key1") == headers.end());
+  EXPECT_TRUE(headers.find("sec-websocket-key1") == headers.end());
+  EXPECT_TRUE(headers.find("Sec-WebSocket-Key2") == headers.end());
+  EXPECT_TRUE(headers.find("sec-websocket-key2") == headers.end());
+  EXPECT_EQ("example.com", headers["host"]);
+  EXPECT_EQ("http://example.com", headers["origin"]);
+  EXPECT_EQ("sample", headers["sec-websocket-protocol"]);
+  const char bogus_header[] = "X\0Y";
+  std::string bogus_header_str(bogus_header, sizeof(bogus_header) - 1);
+  EXPECT_EQ(bogus_header_str, headers["x-bogus-header"]);
+
+  const char expected_challenge[] = "\x31\x6e\x41\x13\x0f\x7e\xd6\x3c^n:ds[4U";
+
+  EXPECT_EQ(expected_challenge, challenge);
+
+  headers.clear();
+
+  headers["sec-websocket-origin"] = "http://example.com";
+  headers["sec-websocket-location"] = "ws://example.com/demo";
+  headers["sec-websocket-protocol"] = "sample";
+
+  WebSocketHandshakeResponseHandler response_handler;
+  EXPECT_TRUE(response_handler.ParseResponseHeaderBlock(headers, challenge));
+  EXPECT_TRUE(response_handler.HasResponse());
+
+  // Note that order of sec-websocket-* is sensitive with hash_map order.
+  static const char* kHandshakeResponseExpectedMessage =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "sec-websocket-location: ws://example.com/demo\r\n"
+      "sec-websocket-origin: http://example.com\r\n"
+      "sec-websocket-protocol: sample\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse());
+}
+
+
+TEST(WebSocketHandshakeHandlerTest, SpdyRequestResponseWithCookies) {
+  WebSocketHandshakeRequestHandler request_handler;
+
+  // Note that websocket won't use multiple headers in request now.
+  static const char* kHandshakeRequestMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "Cookie: WK-websocket-test=1; WK-websocket-test-httponly=1\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  EXPECT_TRUE(request_handler.ParseRequest(kHandshakeRequestMessage,
+                                           strlen(kHandshakeRequestMessage)));
+
+  GURL url("ws://example.com/demo");
+  std::string challenge;
+  spdy::SpdyHeaderBlock headers;
+  ASSERT_TRUE(request_handler.GetRequestHeaderBlock(url, &headers, &challenge));
+
+  EXPECT_EQ(url.spec(), headers["url"]);
+  EXPECT_TRUE(headers.find("upgrade") == headers.end());
+  EXPECT_TRUE(headers.find("Upgrade") == headers.end());
+  EXPECT_TRUE(headers.find("connection") == headers.end());
+  EXPECT_TRUE(headers.find("Connection") == headers.end());
+  EXPECT_TRUE(headers.find("Sec-WebSocket-Key1") == headers.end());
+  EXPECT_TRUE(headers.find("sec-websocket-key1") == headers.end());
+  EXPECT_TRUE(headers.find("Sec-WebSocket-Key2") == headers.end());
+  EXPECT_TRUE(headers.find("sec-websocket-key2") == headers.end());
+  EXPECT_EQ("example.com", headers["host"]);
+  EXPECT_EQ("http://example.com", headers["origin"]);
+  EXPECT_EQ("sample", headers["sec-websocket-protocol"]);
+  EXPECT_EQ("WK-websocket-test=1; WK-websocket-test-httponly=1",
+            headers["cookie"]);
+
+  const char expected_challenge[] = "\x31\x6e\x41\x13\x0f\x7e\xd6\x3c^n:ds[4U";
+
+  EXPECT_EQ(expected_challenge, challenge);
+
+  headers.clear();
+
+  headers["sec-websocket-origin"] = "http://example.com";
+  headers["sec-websocket-location"] = "ws://example.com/demo";
+  headers["sec-websocket-protocol"] = "sample";
+  std::string cookie = "WK-websocket-test=1";
+  cookie.append(1, '\0');
+  cookie += "WK-websocket-test-httponly=1; HttpOnly";
+  headers["set-cookie"] = cookie;
+
+  WebSocketHandshakeResponseHandler response_handler;
+  EXPECT_TRUE(response_handler.ParseResponseHeaderBlock(headers, challenge));
+  EXPECT_TRUE(response_handler.HasResponse());
+
+  // Note that order of sec-websocket-* is sensitive with hash_map order.
+  static const char* kHandshakeResponseExpectedMessage =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "sec-websocket-location: ws://example.com/demo\r\n"
+      "sec-websocket-origin: http://example.com\r\n"
+      "sec-websocket-protocol: sample\r\n"
+      "set-cookie: WK-websocket-test=1\r\n"
+      "set-cookie: WK-websocket-test-httponly=1; HttpOnly\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  EXPECT_EQ(kHandshakeResponseExpectedMessage, response_handler.GetResponse());
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_handshake_unittest.cc b/net/websockets/websocket_handshake_unittest.cc
new file mode 100644
index 0000000..f688554
--- /dev/null
+++ b/net/websockets/websocket_handshake_unittest.cc
@@ -0,0 +1,328 @@
+// Copyright (c) 2010 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 <vector>
+
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "net/websockets/websocket_handshake.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/platform_test.h"
+
+namespace net {
+
+class WebSocketHandshakeTest : public testing::Test {
+ public:
+  static void SetUpParameter(WebSocketHandshake* handshake,
+                             uint32 number_1, uint32 number_2,
+                             const std::string& key_1, const std::string& key_2,
+                             const std::string& key_3) {
+    WebSocketHandshake::Parameter* parameter =
+        new WebSocketHandshake::Parameter;
+    parameter->number_1_ = number_1;
+    parameter->number_2_ = number_2;
+    parameter->key_1_ = key_1;
+    parameter->key_2_ = key_2;
+    parameter->key_3_ = key_3;
+    handshake->parameter_.reset(parameter);
+  }
+
+  static void ExpectHeaderEquals(const std::string& expected,
+                          const std::string& actual) {
+    std::vector<std::string> expected_lines;
+    Tokenize(expected, "\r\n", &expected_lines);
+    std::vector<std::string> actual_lines;
+    Tokenize(actual, "\r\n", &actual_lines);
+    // Request lines.
+    EXPECT_EQ(expected_lines[0], actual_lines[0]);
+
+    std::vector<std::string> expected_headers;
+    for (size_t i = 1; i < expected_lines.size(); i++) {
+      // Finish at first CRLF CRLF.  Note that /key_3/ might include CRLF.
+      if (expected_lines[i] == "")
+        break;
+      expected_headers.push_back(expected_lines[i]);
+    }
+    sort(expected_headers.begin(), expected_headers.end());
+
+    std::vector<std::string> actual_headers;
+    for (size_t i = 1; i < actual_lines.size(); i++) {
+      // Finish at first CRLF CRLF.  Note that /key_3/ might include CRLF.
+      if (actual_lines[i] == "")
+        break;
+      actual_headers.push_back(actual_lines[i]);
+    }
+    sort(actual_headers.begin(), actual_headers.end());
+
+    EXPECT_EQ(expected_headers.size(), actual_headers.size())
+        << "expected:" << expected
+        << "\nactual:" << actual;
+    for (size_t i = 0; i < expected_headers.size(); i++) {
+      EXPECT_EQ(expected_headers[i], actual_headers[i]);
+    }
+  }
+
+  static void ExpectHandshakeMessageEquals(const std::string& expected,
+                                           const std::string& actual) {
+    // Headers.
+    ExpectHeaderEquals(expected, actual);
+    // Compare tailing \r\n\r\n<key3> (4 + 8 bytes).
+    ASSERT_GT(expected.size(), 12U);
+    const char* expected_key3 = expected.data() + expected.size() - 12;
+    EXPECT_GT(actual.size(), 12U);
+    if (actual.size() <= 12U)
+      return;
+    const char* actual_key3 = actual.data() + actual.size() - 12;
+    EXPECT_TRUE(memcmp(expected_key3, actual_key3, 12) == 0)
+        << "expected_key3:" << DumpKey(expected_key3, 12)
+        << ", actual_key3:" << DumpKey(actual_key3, 12);
+  }
+
+  static std::string DumpKey(const char* buf, int len) {
+    std::string s;
+    for (int i = 0; i < len; i++) {
+      if (isprint(buf[i]))
+        s += StringPrintf("%c", buf[i]);
+      else
+        s += StringPrintf("\\x%02x", buf[i]);
+    }
+    return s;
+  }
+
+  static std::string GetResourceName(WebSocketHandshake* handshake) {
+    return handshake->GetResourceName();
+  }
+  static std::string GetHostFieldValue(WebSocketHandshake* handshake) {
+    return handshake->GetHostFieldValue();
+  }
+  static std::string GetOriginFieldValue(WebSocketHandshake* handshake) {
+    return handshake->GetOriginFieldValue();
+  }
+};
+
+
+TEST_F(WebSocketHandshakeTest, Connect) {
+  const std::string kExpectedClientHandshakeMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Host: example.com\r\n"
+      "Origin: http://example.com\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Sec-WebSocket-Key1: 388P O503D&ul7 {K%gX( %7  15\r\n"
+      "Sec-WebSocket-Key2: 1 N ?|k UT0or 3o  4 I97N 5-S3O 31\r\n"
+      "\r\n"
+      "\x47\x30\x22\x2D\x5A\x3F\x47\x58";
+
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("ws://example.com/demo"),
+                             "http://example.com",
+                             "ws://example.com/demo",
+                             "sample"));
+  SetUpParameter(handshake.get(), 777007543U, 114997259U,
+                 "388P O503D&ul7 {K%gX( %7  15",
+                 "1 N ?|k UT0or 3o  4 I97N 5-S3O 31",
+                 std::string("\x47\x30\x22\x2D\x5A\x3F\x47\x58", 8));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+  ExpectHandshakeMessageEquals(
+      kExpectedClientHandshakeMessage,
+      handshake->CreateClientHandshakeMessage());
+
+  const char kResponse[] = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "\r\n"
+      "\x30\x73\x74\x33\x52\x6C\x26\x71\x2D\x32\x5A\x55\x5E\x77\x65\x75";
+  std::vector<std::string> response_lines;
+  SplitStringDontTrim(kResponse, '\n', &response_lines);
+
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+  // too short
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(kResponse, 16));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+
+  // only status line
+  std::string response = response_lines[0];
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(
+      response.data(), response.size()));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+  // by upgrade header
+  response += response_lines[1];
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(
+      response.data(), response.size()));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+  // by connection header
+  response += response_lines[2];
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(
+      response.data(), response.size()));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+
+  response += response_lines[3];  // Sec-WebSocket-Origin
+  response += response_lines[4];  // Sec-WebSocket-Location
+  response += response_lines[5];  // Sec-WebSocket-Protocol
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(
+      response.data(), response.size()));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+
+  response += response_lines[6];  // \r\n
+  EXPECT_EQ(-1, handshake->ReadServerHandshake(
+      response.data(), response.size()));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+
+  int handshake_length = sizeof(kResponse) - 1;  // -1 for terminating \0
+  EXPECT_EQ(handshake_length, handshake->ReadServerHandshake(
+      kResponse, handshake_length));  // -1 for terminating \0
+  EXPECT_EQ(WebSocketHandshake::MODE_CONNECTED, handshake->mode());
+}
+
+TEST_F(WebSocketHandshakeTest, ServerSentData) {
+  const std::string kExpectedClientHandshakeMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Host: example.com\r\n"
+      "Origin: http://example.com\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Sec-WebSocket-Key1: 388P O503D&ul7 {K%gX( %7  15\r\n"
+      "Sec-WebSocket-Key2: 1 N ?|k UT0or 3o  4 I97N 5-S3O 31\r\n"
+      "\r\n"
+      "\x47\x30\x22\x2D\x5A\x3F\x47\x58";
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("ws://example.com/demo"),
+                             "http://example.com",
+                             "ws://example.com/demo",
+                             "sample"));
+  SetUpParameter(handshake.get(), 777007543U, 114997259U,
+                 "388P O503D&ul7 {K%gX( %7  15",
+                 "1 N ?|k UT0or 3o  4 I97N 5-S3O 31",
+                 std::string("\x47\x30\x22\x2D\x5A\x3F\x47\x58", 8));
+  EXPECT_EQ(WebSocketHandshake::MODE_INCOMPLETE, handshake->mode());
+  ExpectHandshakeMessageEquals(
+      kExpectedClientHandshakeMessage,
+      handshake->CreateClientHandshakeMessage());
+
+  const char kResponse[] = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "\r\n"
+      "\x30\x73\x74\x33\x52\x6C\x26\x71\x2D\x32\x5A\x55\x5E\x77\x65\x75"
+      "\0Hello\xff";
+
+  int handshake_length = strlen(kResponse);  // key3 doesn't contain \0.
+  EXPECT_EQ(handshake_length, handshake->ReadServerHandshake(
+      kResponse, sizeof(kResponse) - 1));  // -1 for terminating \0
+  EXPECT_EQ(WebSocketHandshake::MODE_CONNECTED, handshake->mode());
+}
+
+TEST_F(WebSocketHandshakeTest, is_secure_false) {
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("ws://example.com/demo"),
+                             "http://example.com",
+                             "ws://example.com/demo",
+                             "sample"));
+  EXPECT_FALSE(handshake->is_secure());
+}
+
+TEST_F(WebSocketHandshakeTest, is_secure_true) {
+  // wss:// is secure.
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("wss://example.com/demo"),
+                             "http://example.com",
+                             "wss://example.com/demo",
+                             "sample"));
+  EXPECT_TRUE(handshake->is_secure());
+}
+
+TEST_F(WebSocketHandshakeTest, CreateClientHandshakeMessage_ResourceName) {
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("ws://example.com/Test?q=xxx&p=%20"),
+                             "http://example.com",
+                             "ws://example.com/demo",
+                             "sample"));
+  // Path and query should be preserved as-is.
+  EXPECT_EQ("/Test?q=xxx&p=%20", GetResourceName(handshake.get()));
+}
+
+TEST_F(WebSocketHandshakeTest, CreateClientHandshakeMessage_Host) {
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("ws://Example.Com/demo"),
+                             "http://Example.Com",
+                             "ws://Example.Com/demo",
+                             "sample"));
+  // Host should be lowercased
+  EXPECT_EQ("example.com", GetHostFieldValue(handshake.get()));
+  EXPECT_EQ("http://example.com", GetOriginFieldValue(handshake.get()));
+}
+
+TEST_F(WebSocketHandshakeTest, CreateClientHandshakeMessage_TrimPort80) {
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("ws://example.com:80/demo"),
+                             "http://example.com",
+                             "ws://example.com/demo",
+                             "sample"));
+  // :80 should be trimmed as it's the default port for ws://.
+  EXPECT_EQ("example.com", GetHostFieldValue(handshake.get()));
+}
+
+TEST_F(WebSocketHandshakeTest, CreateClientHandshakeMessage_TrimPort443) {
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("wss://example.com:443/demo"),
+                             "http://example.com",
+                             "wss://example.com/demo",
+                             "sample"));
+  // :443 should be trimmed as it's the default port for wss://.
+  EXPECT_EQ("example.com", GetHostFieldValue(handshake.get()));
+}
+
+TEST_F(WebSocketHandshakeTest,
+       CreateClientHandshakeMessage_NonDefaultPortForWs) {
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("ws://example.com:8080/demo"),
+                             "http://example.com",
+                             "wss://example.com/demo",
+                             "sample"));
+  // :8080 should be preserved as it's not the default port for ws://.
+  EXPECT_EQ("example.com:8080", GetHostFieldValue(handshake.get()));
+}
+
+TEST_F(WebSocketHandshakeTest,
+     CreateClientHandshakeMessage_NonDefaultPortForWss) {
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("wss://example.com:4443/demo"),
+                             "http://example.com",
+                             "wss://example.com/demo",
+                             "sample"));
+  // :4443 should be preserved as it's not the default port for wss://.
+  EXPECT_EQ("example.com:4443", GetHostFieldValue(handshake.get()));
+}
+
+TEST_F(WebSocketHandshakeTest, CreateClientHandshakeMessage_WsBut443) {
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("ws://example.com:443/demo"),
+                             "http://example.com",
+                             "ws://example.com/demo",
+                             "sample"));
+  // :443 should be preserved as it's not the default port for ws://.
+  EXPECT_EQ("example.com:443", GetHostFieldValue(handshake.get()));
+}
+
+TEST_F(WebSocketHandshakeTest, CreateClientHandshakeMessage_WssBut80) {
+  scoped_ptr<WebSocketHandshake> handshake(
+      new WebSocketHandshake(GURL("wss://example.com:80/demo"),
+                             "http://example.com",
+                             "wss://example.com/demo",
+                             "sample"));
+  // :80 should be preserved as it's not the default port for wss://.
+  EXPECT_EQ("example.com:80", GetHostFieldValue(handshake.get()));
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_job.cc b/net/websockets/websocket_job.cc
new file mode 100644
index 0000000..17dc54b
--- /dev/null
+++ b/net/websockets/websocket_job.cc
@@ -0,0 +1,498 @@
+// Copyright (c) 2010 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/websockets/websocket_job.h"
+
+#include <algorithm>
+
+#include "base/string_tokenizer.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/base/cookie_policy.h"
+#include "net/base/cookie_store.h"
+#include "net/base/io_buffer.h"
+#include "net/http/http_util.h"
+#include "net/url_request/url_request_context.h"
+#include "net/websockets/websocket_frame_handler.h"
+#include "net/websockets/websocket_handshake_handler.h"
+#include "net/websockets/websocket_throttle.h"
+
+namespace {
+
+// lower-case header names.
+const char* const kCookieHeaders[] = {
+  "cookie", "cookie2"
+};
+const char* const kSetCookieHeaders[] = {
+  "set-cookie", "set-cookie2"
+};
+
+net::SocketStreamJob* WebSocketJobFactory(
+    const GURL& url, net::SocketStream::Delegate* delegate) {
+  net::WebSocketJob* job = new net::WebSocketJob(delegate);
+  job->InitSocketStream(new net::SocketStream(url, job));
+  return job;
+}
+
+class WebSocketJobInitSingleton {
+ private:
+  friend struct DefaultSingletonTraits<WebSocketJobInitSingleton>;
+  WebSocketJobInitSingleton() {
+    net::SocketStreamJob::RegisterProtocolFactory("ws", WebSocketJobFactory);
+    net::SocketStreamJob::RegisterProtocolFactory("wss", WebSocketJobFactory);
+  }
+};
+
+}  // anonymous namespace
+
+namespace net {
+
+// static
+void WebSocketJob::EnsureInit() {
+  Singleton<WebSocketJobInitSingleton>::get();
+}
+
+WebSocketJob::WebSocketJob(SocketStream::Delegate* delegate)
+    : delegate_(delegate),
+      state_(INITIALIZED),
+      waiting_(false),
+      callback_(NULL),
+      handshake_request_(new WebSocketHandshakeRequestHandler),
+      handshake_response_(new WebSocketHandshakeResponseHandler),
+      handshake_request_sent_(0),
+      response_cookies_save_index_(0),
+      ALLOW_THIS_IN_INITIALIZER_LIST(can_get_cookies_callback_(
+          this, &WebSocketJob::OnCanGetCookiesCompleted)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(can_set_cookie_callback_(
+          this, &WebSocketJob::OnCanSetCookieCompleted)),
+      send_frame_handler_(new WebSocketFrameHandler),
+      receive_frame_handler_(new WebSocketFrameHandler) {
+}
+
+WebSocketJob::~WebSocketJob() {
+  DCHECK_EQ(CLOSED, state_);
+  DCHECK(!delegate_);
+  DCHECK(!socket_.get());
+}
+
+void WebSocketJob::Connect() {
+  DCHECK(socket_.get());
+  DCHECK_EQ(state_, INITIALIZED);
+  state_ = CONNECTING;
+  socket_->Connect();
+}
+
+bool WebSocketJob::SendData(const char* data, int len) {
+  switch (state_) {
+    case INITIALIZED:
+      return false;
+
+    case CONNECTING:
+      return SendHandshakeRequest(data, len);
+
+    case OPEN:
+      {
+        send_frame_handler_->AppendData(data, len);
+        // If current buffer is sending now, this data will be sent in
+        // SendPending() after current data was sent.
+        // Do not buffer sending data for now.  Since
+        // WebCore::SocketStreamHandle controls traffic to keep number of
+        // pending bytes less than max_pending_send_allowed, so when sending
+        // larger message than max_pending_send_allowed should not be buffered.
+        // If we don't call OnSentData, WebCore::SocketStreamHandle would stop
+        // sending more data when pending data reaches max_pending_send_allowed.
+        // TODO(ukai): Fix this to support compression for larger message.
+        int err = 0;
+        if (!send_frame_handler_->GetCurrentBuffer() &&
+            (err = send_frame_handler_->UpdateCurrentBuffer(false)) > 0) {
+          DCHECK(!current_buffer_);
+          current_buffer_ = new DrainableIOBuffer(
+              send_frame_handler_->GetCurrentBuffer(),
+              send_frame_handler_->GetCurrentBufferSize());
+          return socket_->SendData(
+              current_buffer_->data(), current_buffer_->BytesRemaining());
+        }
+        return err >= 0;
+      }
+
+    case CLOSING:
+    case CLOSED:
+      return false;
+  }
+  return false;
+}
+
+void WebSocketJob::Close() {
+  state_ = CLOSING;
+  if (current_buffer_) {
+    // Will close in SendPending.
+    return;
+  }
+  state_ = CLOSED;
+  socket_->Close();
+}
+
+void WebSocketJob::RestartWithAuth(
+    const std::wstring& username,
+    const std::wstring& password) {
+  state_ = CONNECTING;
+  socket_->RestartWithAuth(username, password);
+}
+
+void WebSocketJob::DetachDelegate() {
+  state_ = CLOSED;
+  Singleton<WebSocketThrottle>::get()->RemoveFromQueue(this);
+  Singleton<WebSocketThrottle>::get()->WakeupSocketIfNecessary();
+
+  scoped_refptr<WebSocketJob> protect(this);
+
+  delegate_ = NULL;
+  if (socket_)
+    socket_->DetachDelegate();
+  socket_ = NULL;
+  if (callback_) {
+    waiting_ = false;
+    callback_ = NULL;
+    Release();  // Balanced with OnStartOpenConnection().
+  }
+}
+
+int WebSocketJob::OnStartOpenConnection(
+    SocketStream* socket, CompletionCallback* callback) {
+  DCHECK(!callback_);
+  state_ = CONNECTING;
+  addresses_.Copy(socket->address_list().head(), true);
+  Singleton<WebSocketThrottle>::get()->PutInQueue(this);
+  if (!waiting_)
+    return OK;
+  callback_ = callback;
+  AddRef();  // Balanced when callback_ becomes NULL.
+  return ERR_IO_PENDING;
+}
+
+void WebSocketJob::OnConnected(
+    SocketStream* socket, int max_pending_send_allowed) {
+  if (state_ == CLOSED)
+    return;
+  DCHECK_EQ(CONNECTING, state_);
+  if (delegate_)
+    delegate_->OnConnected(socket, max_pending_send_allowed);
+}
+
+void WebSocketJob::OnSentData(SocketStream* socket, int amount_sent) {
+  DCHECK_NE(INITIALIZED, state_);
+  if (state_ == CLOSED)
+    return;
+  if (state_ == CONNECTING) {
+    OnSentHandshakeRequest(socket, amount_sent);
+    return;
+  }
+  if (delegate_) {
+    DCHECK(state_ == OPEN || state_ == CLOSING);
+    DCHECK_GT(amount_sent, 0);
+    DCHECK(current_buffer_);
+    current_buffer_->DidConsume(amount_sent);
+    if (current_buffer_->BytesRemaining() > 0)
+      return;
+
+    // We need to report amount_sent of original buffer size, instead of
+    // amount sent to |socket|.
+    amount_sent = send_frame_handler_->GetOriginalBufferSize();
+    DCHECK_GT(amount_sent, 0);
+    current_buffer_ = NULL;
+    send_frame_handler_->ReleaseCurrentBuffer();
+    delegate_->OnSentData(socket, amount_sent);
+    MessageLoopForIO::current()->PostTask(
+        FROM_HERE, NewRunnableMethod(this, &WebSocketJob::SendPending));
+  }
+}
+
+void WebSocketJob::OnReceivedData(
+    SocketStream* socket, const char* data, int len) {
+  DCHECK_NE(INITIALIZED, state_);
+  if (state_ == CLOSED)
+    return;
+  if (state_ == CONNECTING) {
+    OnReceivedHandshakeResponse(socket, data, len);
+    return;
+  }
+  DCHECK(state_ == OPEN || state_ == CLOSING);
+  std::string received_data;
+  receive_frame_handler_->AppendData(data, len);
+  // Don't buffer receiving data for now.
+  // TODO(ukai): fix performance of WebSocketFrameHandler.
+  while (receive_frame_handler_->UpdateCurrentBuffer(false) > 0) {
+    received_data +=
+        std::string(receive_frame_handler_->GetCurrentBuffer()->data(),
+                    receive_frame_handler_->GetCurrentBufferSize());
+    receive_frame_handler_->ReleaseCurrentBuffer();
+  }
+  if (delegate_ && received_data.size() > 0)
+      delegate_->OnReceivedData(
+          socket, received_data.data(), received_data.size());
+}
+
+void WebSocketJob::OnClose(SocketStream* socket) {
+  state_ = CLOSED;
+  Singleton<WebSocketThrottle>::get()->RemoveFromQueue(this);
+  Singleton<WebSocketThrottle>::get()->WakeupSocketIfNecessary();
+
+  scoped_refptr<WebSocketJob> protect(this);
+
+  SocketStream::Delegate* delegate = delegate_;
+  delegate_ = NULL;
+  socket_ = NULL;
+  if (callback_) {
+    waiting_ = false;
+    callback_ = NULL;
+    Release();  // Balanced with OnStartOpenConnection().
+  }
+  if (delegate)
+    delegate->OnClose(socket);
+}
+
+void WebSocketJob::OnAuthRequired(
+    SocketStream* socket, AuthChallengeInfo* auth_info) {
+  if (delegate_)
+    delegate_->OnAuthRequired(socket, auth_info);
+}
+
+void WebSocketJob::OnError(const SocketStream* socket, int error) {
+  if (delegate_)
+    delegate_->OnError(socket, error);
+}
+
+bool WebSocketJob::SendHandshakeRequest(const char* data, int len) {
+  DCHECK_EQ(state_, CONNECTING);
+  if (!handshake_request_->ParseRequest(data, len))
+    return false;
+
+  // handshake message is completed.
+  AddCookieHeaderAndSend();
+  // Just buffered in |handshake_request_|.
+  return true;
+}
+
+void WebSocketJob::AddCookieHeaderAndSend() {
+  AddRef();  // Balanced in OnCanGetCookiesCompleted
+
+  int policy = OK;
+  if (socket_->context()->cookie_policy()) {
+    GURL url_for_cookies = GetURLForCookies();
+    policy = socket_->context()->cookie_policy()->CanGetCookies(
+        url_for_cookies,
+        url_for_cookies,
+        &can_get_cookies_callback_);
+    if (policy == ERR_IO_PENDING)
+      return;  // Wait for completion callback
+  }
+  OnCanGetCookiesCompleted(policy);
+}
+
+void WebSocketJob::OnCanGetCookiesCompleted(int policy) {
+  if (socket_ && delegate_ && state_ == CONNECTING) {
+    handshake_request_->RemoveHeaders(
+        kCookieHeaders, arraysize(kCookieHeaders));
+    if (policy == OK) {
+      // Add cookies, including HttpOnly cookies.
+      if (socket_->context()->cookie_store()) {
+        CookieOptions cookie_options;
+        cookie_options.set_include_httponly();
+        std::string cookie =
+            socket_->context()->cookie_store()->GetCookiesWithOptions(
+                GetURLForCookies(), cookie_options);
+        if (!cookie.empty())
+          handshake_request_->AppendHeaderIfMissing("Cookie", cookie);
+      }
+    }
+
+    const std::string& handshake_request = handshake_request_->GetRawRequest();
+    handshake_request_sent_ = 0;
+    socket_->SendData(handshake_request.data(),
+                      handshake_request.size());
+  }
+  Release();  // Balance AddRef taken in AddCookieHeaderAndSend
+}
+
+void WebSocketJob::OnSentHandshakeRequest(
+    SocketStream* socket, int amount_sent) {
+  DCHECK_EQ(state_, CONNECTING);
+  handshake_request_sent_ += amount_sent;
+  DCHECK_LE(handshake_request_sent_, handshake_request_->raw_length());
+  if (handshake_request_sent_ >= handshake_request_->raw_length()) {
+    // handshake request has been sent.
+    // notify original size of handshake request to delegate.
+    if (delegate_)
+      delegate_->OnSentData(
+          socket,
+          handshake_request_->original_length());
+    handshake_request_.reset();
+  }
+}
+
+void WebSocketJob::OnReceivedHandshakeResponse(
+    SocketStream* socket, const char* data, int len) {
+  DCHECK_EQ(state_, CONNECTING);
+  if (handshake_response_->HasResponse()) {
+    // If we already has handshake response, received data should be frame
+    // data, not handshake message.
+    receive_frame_handler_->AppendData(data, len);
+    return;
+  }
+
+  size_t response_length = handshake_response_->ParseRawResponse(data, len);
+  if (!handshake_response_->HasResponse()) {
+    // not yet. we need more data.
+    return;
+  }
+  // handshake message is completed.
+  if (len - response_length > 0) {
+    // If we received extra data, it should be frame data.
+    receive_frame_handler_->AppendData(data + response_length,
+                                       len - response_length);
+  }
+  SaveCookiesAndNotifyHeaderComplete();
+}
+
+void WebSocketJob::SaveCookiesAndNotifyHeaderComplete() {
+  // handshake message is completed.
+  DCHECK(handshake_response_->HasResponse());
+
+  response_cookies_.clear();
+  response_cookies_save_index_ = 0;
+
+  handshake_response_->GetHeaders(
+      kSetCookieHeaders, arraysize(kSetCookieHeaders), &response_cookies_);
+
+  // Now, loop over the response cookies, and attempt to persist each.
+  SaveNextCookie();
+}
+
+void WebSocketJob::SaveNextCookie() {
+  if (response_cookies_save_index_ == response_cookies_.size()) {
+    response_cookies_.clear();
+    response_cookies_save_index_ = 0;
+
+    // Remove cookie headers, with malformed headers preserved.
+    // Actual handshake should be done in WebKit.
+    handshake_response_->RemoveHeaders(
+        kSetCookieHeaders, arraysize(kSetCookieHeaders));
+    std::string received_data = handshake_response_->GetResponse();
+    // Don't buffer receiving data for now.
+    // TODO(ukai): fix performance of WebSocketFrameHandler.
+    while (receive_frame_handler_->UpdateCurrentBuffer(false) > 0) {
+      received_data +=
+          std::string(receive_frame_handler_->GetCurrentBuffer()->data(),
+                      receive_frame_handler_->GetCurrentBufferSize());
+      receive_frame_handler_->ReleaseCurrentBuffer();
+    }
+
+    state_ = OPEN;
+    if (delegate_)
+      delegate_->OnReceivedData(
+          socket_, received_data.data(), received_data.size());
+
+    handshake_response_.reset();
+
+    Singleton<WebSocketThrottle>::get()->RemoveFromQueue(this);
+    Singleton<WebSocketThrottle>::get()->WakeupSocketIfNecessary();
+    return;
+  }
+
+  AddRef();  // Balanced in OnCanSetCookieCompleted
+
+  int policy = OK;
+  if (socket_->context()->cookie_policy()) {
+    GURL url_for_cookies = GetURLForCookies();
+    policy = socket_->context()->cookie_policy()->CanSetCookie(
+        url_for_cookies,
+        url_for_cookies,
+        response_cookies_[response_cookies_save_index_],
+        &can_set_cookie_callback_);
+    if (policy == ERR_IO_PENDING)
+      return;  // Wait for completion callback
+  }
+
+  OnCanSetCookieCompleted(policy);
+}
+
+void WebSocketJob::OnCanSetCookieCompleted(int policy) {
+  if (socket_ && delegate_ && state_ == CONNECTING) {
+    if ((policy == OK || policy == OK_FOR_SESSION_ONLY) &&
+        socket_->context()->cookie_store()) {
+      CookieOptions options;
+      options.set_include_httponly();
+      if (policy == OK_FOR_SESSION_ONLY)
+        options.set_force_session();
+      GURL url_for_cookies = GetURLForCookies();
+      socket_->context()->cookie_store()->SetCookieWithOptions(
+          url_for_cookies, response_cookies_[response_cookies_save_index_],
+          options);
+    }
+    response_cookies_save_index_++;
+    SaveNextCookie();
+  }
+  Release();  // Balance AddRef taken in SaveNextCookie
+}
+
+GURL WebSocketJob::GetURLForCookies() const {
+  GURL url = socket_->url();
+  std::string scheme = socket_->is_secure() ? "https" : "http";
+  url_canon::Replacements<char> replacements;
+  replacements.SetScheme(scheme.c_str(),
+                         url_parse::Component(0, scheme.length()));
+  return url.ReplaceComponents(replacements);
+}
+
+const AddressList& WebSocketJob::address_list() const {
+  return addresses_;
+}
+
+void WebSocketJob::SetWaiting() {
+  waiting_ = true;
+}
+
+bool WebSocketJob::IsWaiting() const {
+  return waiting_;
+}
+
+void WebSocketJob::Wakeup() {
+  if (!waiting_)
+    return;
+  waiting_ = false;
+  DCHECK(callback_);
+  MessageLoopForIO::current()->PostTask(
+      FROM_HERE,
+      NewRunnableMethod(this,
+                        &WebSocketJob::DoCallback));
+}
+
+void WebSocketJob::DoCallback() {
+  // |callback_| may be NULL if OnClose() or DetachDelegate() was called.
+  if (callback_) {
+    net::CompletionCallback* callback = callback_;
+    callback_ = NULL;
+    callback->Run(net::OK);
+    Release();  // Balanced with OnStartOpenConnection().
+  }
+}
+
+void WebSocketJob::SendPending() {
+  if (current_buffer_)
+    return;
+  // Current buffer is done.  Try next buffer if any.
+  // Don't buffer sending data. See comment on case OPEN in SendData().
+  if (send_frame_handler_->UpdateCurrentBuffer(false) <= 0) {
+    // No more data to send.
+    if (state_ == CLOSING)
+      socket_->Close();
+    return;
+  }
+  current_buffer_ = new DrainableIOBuffer(
+      send_frame_handler_->GetCurrentBuffer(),
+      send_frame_handler_->GetCurrentBufferSize());
+  socket_->SendData(current_buffer_->data(), current_buffer_->BytesRemaining());
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_job.h b/net/websockets/websocket_job.h
new file mode 100644
index 0000000..833726b
--- /dev/null
+++ b/net/websockets/websocket_job.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2010 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_WEBSOCKETS_WEBSOCKET_JOB_H_
+#define NET_WEBSOCKETS_WEBSOCKET_JOB_H_
+
+#include <string>
+#include <vector>
+
+#include "net/base/address_list.h"
+#include "net/base/completion_callback.h"
+#include "net/socket_stream/socket_stream_job.h"
+
+class GURL;
+
+namespace net {
+
+class DrainableIOBuffer;
+class WebSocketFrameHandler;
+class WebSocketHandshakeRequestHandler;
+class WebSocketHandshakeResponseHandler;
+
+// WebSocket protocol specific job on SocketStream.
+// It captures WebSocket handshake message and handles cookie operations.
+// Chrome security policy doesn't allow renderer process (except dev tools)
+// see HttpOnly cookies, so it injects cookie header in handshake request and
+// strips set-cookie headers in handshake response.
+// TODO(ukai): refactor websocket.cc to use this.
+class WebSocketJob : public SocketStreamJob, public SocketStream::Delegate {
+ public:
+  // This is state of WebSocket, not SocketStream.
+  enum State {
+    INITIALIZED = -1,
+    CONNECTING = 0,
+    OPEN = 1,
+    CLOSING = 2,
+    CLOSED = 3,
+  };
+  static void EnsureInit();
+
+  explicit WebSocketJob(SocketStream::Delegate* delegate);
+
+  State state() const { return state_; }
+  virtual void Connect();
+  virtual bool SendData(const char* data, int len);
+  virtual void Close();
+  virtual void RestartWithAuth(
+      const std::wstring& username,
+      const std::wstring& password);
+  virtual void DetachDelegate();
+
+  // SocketStream::Delegate methods.
+  virtual int OnStartOpenConnection(
+      SocketStream* socket, CompletionCallback* callback);
+  virtual void OnConnected(
+      SocketStream* socket, int max_pending_send_allowed);
+  virtual void OnSentData(
+      SocketStream* socket, int amount_sent);
+  virtual void OnReceivedData(
+      SocketStream* socket, const char* data, int len);
+  virtual void OnClose(SocketStream* socket);
+  virtual void OnAuthRequired(
+      SocketStream* socket, AuthChallengeInfo* auth_info);
+  virtual void OnError(
+      const SocketStream* socket, int error);
+
+ private:
+  friend class WebSocketThrottle;
+  friend class WebSocketJobTest;
+  virtual ~WebSocketJob();
+
+  bool SendHandshakeRequest(const char* data, int len);
+  void AddCookieHeaderAndSend();
+  void OnCanGetCookiesCompleted(int policy);
+
+  void OnSentHandshakeRequest(SocketStream* socket, int amount_sent);
+  void OnReceivedHandshakeResponse(
+      SocketStream* socket, const char* data, int len);
+  void SaveCookiesAndNotifyHeaderComplete();
+  void SaveNextCookie();
+  void OnCanSetCookieCompleted(int policy);
+
+  GURL GetURLForCookies() const;
+
+  const AddressList& address_list() const;
+  void SetWaiting();
+  bool IsWaiting() const;
+  void Wakeup();
+  void DoCallback();
+
+  void SendPending();
+
+  SocketStream::Delegate* delegate_;
+  State state_;
+  bool waiting_;
+  AddressList addresses_;
+  CompletionCallback* callback_;  // for throttling.
+
+  scoped_ptr<WebSocketHandshakeRequestHandler> handshake_request_;
+  scoped_ptr<WebSocketHandshakeResponseHandler> handshake_response_;
+
+  size_t handshake_request_sent_;
+
+  std::vector<std::string> response_cookies_;
+  size_t response_cookies_save_index_;
+
+  CompletionCallbackImpl<WebSocketJob> can_get_cookies_callback_;
+  CompletionCallbackImpl<WebSocketJob> can_set_cookie_callback_;
+
+  scoped_ptr<WebSocketFrameHandler> send_frame_handler_;
+  scoped_refptr<DrainableIOBuffer> current_buffer_;
+  scoped_ptr<WebSocketFrameHandler> receive_frame_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebSocketJob);
+};
+
+}  // namespace
+
+#endif  // NET_WEBSOCKETS_WEBSOCKET_JOB_H_
diff --git a/net/websockets/websocket_job_unittest.cc b/net/websockets/websocket_job_unittest.cc
new file mode 100644
index 0000000..0ec760c
--- /dev/null
+++ b/net/websockets/websocket_job_unittest.cc
@@ -0,0 +1,522 @@
+// Copyright (c) 2010 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 <vector>
+
+#include "base/ref_counted.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/cookie_policy.h"
+#include "net/base/cookie_store.h"
+#include "net/base/net_errors.h"
+#include "net/base/sys_addrinfo.h"
+#include "net/socket_stream/socket_stream.h"
+#include "net/url_request/url_request_context.h"
+#include "net/websockets/websocket_job.h"
+#include "net/websockets/websocket_throttle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/platform_test.h"
+
+namespace net {
+
+class MockSocketStream : public SocketStream {
+ public:
+  MockSocketStream(const GURL& url, SocketStream::Delegate* delegate)
+      : SocketStream(url, delegate) {}
+  virtual ~MockSocketStream() {}
+
+  virtual void Connect() {}
+  virtual bool SendData(const char* data, int len) {
+    sent_data_ += std::string(data, len);
+    return true;
+  }
+
+  virtual void Close() {}
+  virtual void RestartWithAuth(
+      const std::wstring& username, std::wstring& password) {}
+  virtual void DetachDelegate() {
+    delegate_ = NULL;
+  }
+
+  const std::string& sent_data() const {
+    return sent_data_;
+  }
+
+ private:
+  std::string sent_data_;
+};
+
+class MockSocketStreamDelegate : public SocketStream::Delegate {
+ public:
+  MockSocketStreamDelegate()
+      : amount_sent_(0) {}
+  virtual ~MockSocketStreamDelegate() {}
+
+  virtual void OnConnected(SocketStream* socket, int max_pending_send_allowed) {
+  }
+  virtual void OnSentData(SocketStream* socket, int amount_sent) {
+    amount_sent_ += amount_sent;
+  }
+  virtual void OnReceivedData(SocketStream* socket,
+                              const char* data, int len) {
+    received_data_ += std::string(data, len);
+  }
+  virtual void OnClose(SocketStream* socket) {
+  }
+
+  size_t amount_sent() const { return amount_sent_; }
+  const std::string& received_data() const { return received_data_; }
+
+ private:
+  int amount_sent_;
+  std::string received_data_;
+};
+
+class MockCookieStore : public CookieStore {
+ public:
+  struct Entry {
+    GURL url;
+    std::string cookie_line;
+    CookieOptions options;
+  };
+  MockCookieStore() {}
+
+  virtual bool SetCookieWithOptions(const GURL& url,
+                                    const std::string& cookie_line,
+                                    const CookieOptions& options) {
+    Entry entry;
+    entry.url = url;
+    entry.cookie_line = cookie_line;
+    entry.options = options;
+    entries_.push_back(entry);
+    return true;
+  }
+  virtual std::string GetCookiesWithOptions(const GURL& url,
+                                            const CookieOptions& options) {
+    std::string result;
+    for (size_t i = 0; i < entries_.size(); i++) {
+      Entry &entry = entries_[i];
+      if (url == entry.url) {
+        if (!result.empty()) {
+          result += "; ";
+        }
+        result += entry.cookie_line;
+      }
+    }
+    return result;
+  }
+  virtual void DeleteCookie(const GURL& url,
+                            const std::string& cookie_name) {}
+  virtual CookieMonster* GetCookieMonster() { return NULL; }
+
+  const std::vector<Entry>& entries() const { return entries_; }
+
+ private:
+  friend class base::RefCountedThreadSafe<MockCookieStore>;
+  virtual ~MockCookieStore() {}
+
+  std::vector<Entry> entries_;
+};
+
+class MockCookiePolicy : public CookiePolicy,
+                         public base::RefCountedThreadSafe<MockCookiePolicy> {
+ public:
+  MockCookiePolicy() : allow_all_cookies_(true), callback_(NULL) {}
+
+  void set_allow_all_cookies(bool allow_all_cookies) {
+    allow_all_cookies_ = allow_all_cookies;
+  }
+
+  virtual int CanGetCookies(const GURL& url,
+                            const GURL& first_party_for_cookies,
+                            CompletionCallback* callback) {
+    DCHECK(!callback_);
+    callback_ = callback;
+    MessageLoop::current()->PostTask(
+        FROM_HERE, NewRunnableMethod(this, &MockCookiePolicy::OnCanGetCookies));
+    return ERR_IO_PENDING;
+  }
+
+  virtual int CanSetCookie(const GURL& url,
+                           const GURL& first_party_for_cookies,
+                           const std::string& cookie_line,
+                           CompletionCallback* callback) {
+    DCHECK(!callback_);
+    callback_ = callback;
+    MessageLoop::current()->PostTask(
+        FROM_HERE, NewRunnableMethod(this, &MockCookiePolicy::OnCanSetCookie));
+    return ERR_IO_PENDING;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<MockCookiePolicy>;
+  virtual ~MockCookiePolicy() {}
+
+  void OnCanGetCookies() {
+    CompletionCallback* callback = callback_;
+    callback_ = NULL;
+    if (allow_all_cookies_)
+      callback->Run(OK);
+    else
+      callback->Run(ERR_ACCESS_DENIED);
+  }
+  void OnCanSetCookie() {
+    CompletionCallback* callback = callback_;
+    callback_ = NULL;
+    if (allow_all_cookies_)
+      callback->Run(OK);
+    else
+      callback->Run(ERR_ACCESS_DENIED);
+  }
+
+  bool allow_all_cookies_;
+  CompletionCallback* callback_;
+};
+
+class MockURLRequestContext : public URLRequestContext {
+ public:
+  MockURLRequestContext(CookieStore* cookie_store,
+                        CookiePolicy* cookie_policy) {
+    cookie_store_ = cookie_store;
+    cookie_policy_ = cookie_policy;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<MockURLRequestContext>;
+  virtual ~MockURLRequestContext() {}
+};
+
+class WebSocketJobTest : public PlatformTest {
+ public:
+  virtual void SetUp() {
+    cookie_store_ = new MockCookieStore;
+    cookie_policy_ = new MockCookiePolicy;
+    context_ = new MockURLRequestContext(
+        cookie_store_.get(), cookie_policy_.get());
+  }
+  virtual void TearDown() {
+    cookie_store_ = NULL;
+    cookie_policy_ = NULL;
+    context_ = NULL;
+    websocket_ = NULL;
+    socket_ = NULL;
+  }
+ protected:
+  void InitWebSocketJob(const GURL& url, MockSocketStreamDelegate* delegate) {
+    websocket_ = new WebSocketJob(delegate);
+    socket_ = new MockSocketStream(url, websocket_.get());
+    websocket_->InitSocketStream(socket_.get());
+    websocket_->set_context(context_.get());
+    websocket_->state_ = WebSocketJob::CONNECTING;
+    struct addrinfo addr;
+    memset(&addr, 0, sizeof(struct addrinfo));
+    addr.ai_family = AF_INET;
+    addr.ai_addrlen = sizeof(struct sockaddr_in);
+    struct sockaddr_in sa_in;
+    memset(&sa_in, 0, sizeof(struct sockaddr_in));
+    memcpy(&sa_in.sin_addr, "\x7f\0\0\1", 4);
+    addr.ai_addr = reinterpret_cast<sockaddr*>(&sa_in);
+    addr.ai_next = NULL;
+    websocket_->addresses_.Copy(&addr, true);
+    Singleton<WebSocketThrottle>::get()->PutInQueue(websocket_);
+  }
+  WebSocketJob::State GetWebSocketJobState() {
+    return websocket_->state_;
+  }
+  void CloseWebSocketJob() {
+    if (websocket_->socket_) {
+      websocket_->socket_->DetachDelegate();
+      Singleton<WebSocketThrottle>::get()->RemoveFromQueue(websocket_);
+    }
+    websocket_->state_ = WebSocketJob::CLOSED;
+    websocket_->delegate_ = NULL;
+    websocket_->socket_ = NULL;
+  }
+
+  scoped_refptr<MockCookieStore> cookie_store_;
+  scoped_refptr<MockCookiePolicy> cookie_policy_;
+  scoped_refptr<MockURLRequestContext> context_;
+  scoped_refptr<WebSocketJob> websocket_;
+  scoped_refptr<MockSocketStream> socket_;
+};
+
+TEST_F(WebSocketJobTest, SimpleHandshake) {
+  GURL url("ws://example.com/demo");
+  MockSocketStreamDelegate delegate;
+  InitWebSocketJob(url, &delegate);
+
+  static const char* kHandshakeRequestMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  bool sent = websocket_->SendData(kHandshakeRequestMessage,
+                                   strlen(kHandshakeRequestMessage));
+  EXPECT_EQ(true, sent);
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(kHandshakeRequestMessage, socket_->sent_data());
+  EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
+  websocket_->OnSentData(socket_.get(), strlen(kHandshakeRequestMessage));
+  EXPECT_EQ(strlen(kHandshakeRequestMessage), delegate.amount_sent());
+
+  const char kHandshakeResponseMessage[] =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  websocket_->OnReceivedData(socket_.get(),
+                             kHandshakeResponseMessage,
+                             strlen(kHandshakeResponseMessage));
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(kHandshakeResponseMessage, delegate.received_data());
+  EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
+  CloseWebSocketJob();
+}
+
+TEST_F(WebSocketJobTest, SlowHandshake) {
+  GURL url("ws://example.com/demo");
+  MockSocketStreamDelegate delegate;
+  InitWebSocketJob(url, &delegate);
+
+  static const char* kHandshakeRequestMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  bool sent = websocket_->SendData(kHandshakeRequestMessage,
+                                   strlen(kHandshakeRequestMessage));
+  EXPECT_EQ(true, sent);
+  // We assume request is sent in one data chunk (from WebKit)
+  // We don't support streaming request.
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(kHandshakeRequestMessage, socket_->sent_data());
+  EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
+  websocket_->OnSentData(socket_.get(), strlen(kHandshakeRequestMessage));
+  EXPECT_EQ(strlen(kHandshakeRequestMessage), delegate.amount_sent());
+
+  const char kHandshakeResponseMessage[] =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  std::vector<std::string> lines;
+  SplitString(kHandshakeResponseMessage, '\n', &lines);
+  for (size_t i = 0; i < lines.size() - 2; i++) {
+    std::string line = lines[i] + "\r\n";
+    SCOPED_TRACE("Line: " + line);
+    websocket_->OnReceivedData(socket_,
+                               line.c_str(),
+                               line.size());
+    MessageLoop::current()->RunAllPending();
+    EXPECT_TRUE(delegate.received_data().empty());
+    EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
+  }
+  websocket_->OnReceivedData(socket_.get(), "\r\n", 2);
+  MessageLoop::current()->RunAllPending();
+  EXPECT_TRUE(delegate.received_data().empty());
+  EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
+  websocket_->OnReceivedData(socket_.get(), "8jKS'y:G*Co,Wxa-", 16);
+  EXPECT_EQ(kHandshakeResponseMessage, delegate.received_data());
+  EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
+  CloseWebSocketJob();
+}
+
+TEST_F(WebSocketJobTest, HandshakeWithCookie) {
+  GURL url("ws://example.com/demo");
+  GURL cookieUrl("http://example.com/demo");
+  CookieOptions cookie_options;
+  cookie_store_->SetCookieWithOptions(
+      cookieUrl, "CR-test=1", cookie_options);
+  cookie_options.set_include_httponly();
+  cookie_store_->SetCookieWithOptions(
+      cookieUrl, "CR-test-httponly=1", cookie_options);
+
+  MockSocketStreamDelegate delegate;
+  InitWebSocketJob(url, &delegate);
+
+  static const char* kHandshakeRequestMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "Cookie: WK-test=1\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  static const char* kHandshakeRequestExpected =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "Cookie: CR-test=1; CR-test-httponly=1\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  bool sent = websocket_->SendData(kHandshakeRequestMessage,
+                                   strlen(kHandshakeRequestMessage));
+  EXPECT_EQ(true, sent);
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(kHandshakeRequestExpected, socket_->sent_data());
+  EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
+  websocket_->OnSentData(socket_, strlen(kHandshakeRequestExpected));
+  EXPECT_EQ(strlen(kHandshakeRequestMessage), delegate.amount_sent());
+
+  const char kHandshakeResponseMessage[] =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Set-Cookie: CR-set-test=1\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  static const char* kHandshakeResponseExpected =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  websocket_->OnReceivedData(socket_.get(),
+                             kHandshakeResponseMessage,
+                             strlen(kHandshakeResponseMessage));
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(kHandshakeResponseExpected, delegate.received_data());
+  EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
+
+  EXPECT_EQ(3U, cookie_store_->entries().size());
+  EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url);
+  EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line);
+  EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url);
+  EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line);
+  EXPECT_EQ(cookieUrl, cookie_store_->entries()[2].url);
+  EXPECT_EQ("CR-set-test=1", cookie_store_->entries()[2].cookie_line);
+
+  CloseWebSocketJob();
+}
+
+TEST_F(WebSocketJobTest, HandshakeWithCookieButNotAllowed) {
+  GURL url("ws://example.com/demo");
+  GURL cookieUrl("http://example.com/demo");
+  CookieOptions cookie_options;
+  cookie_store_->SetCookieWithOptions(
+      cookieUrl, "CR-test=1", cookie_options);
+  cookie_options.set_include_httponly();
+  cookie_store_->SetCookieWithOptions(
+      cookieUrl, "CR-test-httponly=1", cookie_options);
+  cookie_policy_->set_allow_all_cookies(false);
+
+  MockSocketStreamDelegate delegate;
+  InitWebSocketJob(url, &delegate);
+
+  static const char* kHandshakeRequestMessage =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "Cookie: WK-test=1\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  static const char* kHandshakeRequestExpected =
+      "GET /demo HTTP/1.1\r\n"
+      "Host: example.com\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
+      "Origin: http://example.com\r\n"
+      "\r\n"
+      "^n:ds[4U";
+
+  bool sent = websocket_->SendData(kHandshakeRequestMessage,
+                                   strlen(kHandshakeRequestMessage));
+  EXPECT_EQ(true, sent);
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(kHandshakeRequestExpected, socket_->sent_data());
+  EXPECT_EQ(WebSocketJob::CONNECTING, GetWebSocketJobState());
+  websocket_->OnSentData(socket_, strlen(kHandshakeRequestExpected));
+  EXPECT_EQ(strlen(kHandshakeRequestMessage), delegate.amount_sent());
+
+  const char kHandshakeResponseMessage[] =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "Set-Cookie: CR-set-test=1\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  static const char* kHandshakeResponseExpected =
+      "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
+      "Upgrade: WebSocket\r\n"
+      "Connection: Upgrade\r\n"
+      "Sec-WebSocket-Origin: http://example.com\r\n"
+      "Sec-WebSocket-Location: ws://example.com/demo\r\n"
+      "Sec-WebSocket-Protocol: sample\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+
+  websocket_->OnReceivedData(socket_.get(),
+                             kHandshakeResponseMessage,
+                             strlen(kHandshakeResponseMessage));
+  MessageLoop::current()->RunAllPending();
+  EXPECT_EQ(kHandshakeResponseExpected, delegate.received_data());
+  EXPECT_EQ(WebSocketJob::OPEN, GetWebSocketJobState());
+
+  EXPECT_EQ(2U, cookie_store_->entries().size());
+  EXPECT_EQ(cookieUrl, cookie_store_->entries()[0].url);
+  EXPECT_EQ("CR-test=1", cookie_store_->entries()[0].cookie_line);
+  EXPECT_EQ(cookieUrl, cookie_store_->entries()[1].url);
+  EXPECT_EQ("CR-test-httponly=1", cookie_store_->entries()[1].cookie_line);
+
+  CloseWebSocketJob();
+}
+
+}  // namespace net
diff --git a/net/websockets/websocket_throttle.cc b/net/websockets/websocket_throttle.cc
index 8d0d1fb..9e33cad 100644
--- a/net/websockets/websocket_throttle.cc
+++ b/net/websockets/websocket_throttle.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/hash_tables.h"
 #include "base/message_loop.h"
 #include "base/ref_counted.h"
 #include "base/singleton.h"
@@ -13,6 +14,7 @@
 #include "net/base/io_buffer.h"
 #include "net/base/sys_addrinfo.h"
 #include "net/socket_stream/socket_stream.h"
+#include "net/websockets/websocket_job.h"
 
 namespace net {
 
@@ -41,119 +43,7 @@
   }
 }
 
-// State for WebSocket protocol on each SocketStream.
-// This is owned in SocketStream as UserData keyed by WebSocketState::kKeyName.
-// This is alive between connection starts and handshake is finished.
-// In this class, it doesn't check actual handshake finishes, but only checks
-// end of header is found in read data.
-class WebSocketThrottle::WebSocketState : public SocketStream::UserData {
- public:
-  explicit WebSocketState(const AddressList& addrs)
-      : address_list_(addrs),
-        callback_(NULL),
-        waiting_(false),
-        handshake_finished_(false),
-        buffer_(NULL) {
-  }
-  ~WebSocketState() {}
-
-  int OnStartOpenConnection(CompletionCallback* callback) {
-    DCHECK(!callback_);
-    if (!waiting_)
-      return OK;
-    callback_ = callback;
-    return ERR_IO_PENDING;
-  }
-
-  int OnRead(const char* data, int len, CompletionCallback* callback) {
-    DCHECK(!waiting_);
-    DCHECK(!callback_);
-    DCHECK(!handshake_finished_);
-    static const int kBufferSize = 8129;
-
-    if (!buffer_) {
-      // Fast path.
-      int eoh = HttpUtil::LocateEndOfHeaders(data, len, 0);
-      if (eoh > 0) {
-        handshake_finished_ = true;
-        return OK;
-      }
-      buffer_ = new GrowableIOBuffer();
-      buffer_->SetCapacity(kBufferSize);
-    } else if (buffer_->RemainingCapacity() < len) {
-      buffer_->SetCapacity(buffer_->capacity() + kBufferSize);
-    }
-    memcpy(buffer_->data(), data, len);
-    buffer_->set_offset(buffer_->offset() + len);
-
-    int eoh = HttpUtil::LocateEndOfHeaders(buffer_->StartOfBuffer(),
-                                           buffer_->offset(), 0);
-    handshake_finished_ = (eoh > 0);
-    return OK;
-  }
-
-  const AddressList& address_list() const { return address_list_; }
-  void SetWaiting() { waiting_ = true; }
-  bool IsWaiting() const { return waiting_; }
-  bool HandshakeFinished() const { return handshake_finished_; }
-  void Wakeup() {
-    waiting_ = false;
-    // We wrap |callback_| to keep this alive while this is released.
-    scoped_refptr<CompletionCallbackRunner> runner =
-        new CompletionCallbackRunner(callback_);
-    callback_ = NULL;
-    MessageLoopForIO::current()->PostTask(
-        FROM_HERE,
-        NewRunnableMethod(runner.get(),
-                          &CompletionCallbackRunner::Run));
-  }
-
-  static const char* kKeyName;
-
- private:
-  class CompletionCallbackRunner
-      : public base::RefCountedThreadSafe<CompletionCallbackRunner> {
-   public:
-    explicit CompletionCallbackRunner(CompletionCallback* callback)
-        : callback_(callback) {
-      DCHECK(callback_);
-    }
-    void Run() {
-      callback_->Run(OK);
-    }
-   private:
-    friend class base::RefCountedThreadSafe<CompletionCallbackRunner>;
-
-    virtual ~CompletionCallbackRunner() {}
-
-    CompletionCallback* callback_;
-
-    DISALLOW_COPY_AND_ASSIGN(CompletionCallbackRunner);
-  };
-
-  const AddressList& address_list_;
-
-  CompletionCallback* callback_;
-  // True if waiting another websocket connection is established.
-  // False if the websocket is performing handshaking.
-  bool waiting_;
-
-  // True if the websocket handshake is completed.
-  // If true, it will be removed from queue and deleted from the SocketStream
-  // UserData soon.
-  bool handshake_finished_;
-
-  // Buffer for read data to check handshake response message.
-  scoped_refptr<GrowableIOBuffer> buffer_;
-
-  DISALLOW_COPY_AND_ASSIGN(WebSocketState);
-};
-
-const char* WebSocketThrottle::WebSocketState::kKeyName = "WebSocketState";
-
 WebSocketThrottle::WebSocketThrottle() {
-  SocketStreamThrottle::RegisterSocketStreamThrottle("ws", this);
-  SocketStreamThrottle::RegisterSocketStreamThrottle("wss", this);
 }
 
 WebSocketThrottle::~WebSocketThrottle() {
@@ -161,105 +51,87 @@
   DCHECK(addr_map_.empty());
 }
 
-int WebSocketThrottle::OnStartOpenConnection(
-    SocketStream* socket, CompletionCallback* callback) {
-  WebSocketState* state = new WebSocketState(socket->address_list());
-  PutInQueue(socket, state);
-  return state->OnStartOpenConnection(callback);
-}
-
-int WebSocketThrottle::OnRead(SocketStream* socket,
-                              const char* data, int len,
-                              CompletionCallback* callback) {
-  WebSocketState* state = static_cast<WebSocketState*>(
-      socket->GetUserData(WebSocketState::kKeyName));
-  // If no state, handshake was already completed. Do nothing.
-  if (!state)
-    return OK;
-
-  int result = state->OnRead(data, len, callback);
-  if (state->HandshakeFinished()) {
-    RemoveFromQueue(socket, state);
-    WakeupSocketIfNecessary();
-  }
-  return result;
-}
-
-int WebSocketThrottle::OnWrite(SocketStream* socket,
-                               const char* data, int len,
-                               CompletionCallback* callback) {
-  // Do nothing.
-  return OK;
-}
-
-void WebSocketThrottle::OnClose(SocketStream* socket) {
-  WebSocketState* state = static_cast<WebSocketState*>(
-      socket->GetUserData(WebSocketState::kKeyName));
-  if (!state)
-    return;
-  RemoveFromQueue(socket, state);
-  WakeupSocketIfNecessary();
-}
-
-void WebSocketThrottle::PutInQueue(SocketStream* socket,
-                                   WebSocketState* state) {
-  socket->SetUserData(WebSocketState::kKeyName, state);
-  queue_.push_back(state);
-  const AddressList& address_list = socket->address_list();
+void WebSocketThrottle::PutInQueue(WebSocketJob* job) {
+  queue_.push_back(job);
+  const AddressList& address_list = job->address_list();
+  base::hash_set<std::string> address_set;
   for (const struct addrinfo* addrinfo = address_list.head();
        addrinfo != NULL;
        addrinfo = addrinfo->ai_next) {
     std::string addrkey = AddrinfoToHashkey(addrinfo);
+
+    // If |addrkey| is already processed, don't do it again.
+    if (address_set.find(addrkey) != address_set.end())
+      continue;
+    address_set.insert(addrkey);
+
     ConnectingAddressMap::iterator iter = addr_map_.find(addrkey);
     if (iter == addr_map_.end()) {
       ConnectingQueue* queue = new ConnectingQueue();
-      queue->push_back(state);
+      queue->push_back(job);
       addr_map_[addrkey] = queue;
     } else {
-      iter->second->push_back(state);
-      state->SetWaiting();
+      iter->second->push_back(job);
+      job->SetWaiting();
+      DLOG(INFO) << "Waiting on " << addrkey;
     }
   }
 }
 
-void WebSocketThrottle::RemoveFromQueue(SocketStream* socket,
-                                        WebSocketState* state) {
-  const AddressList& address_list = socket->address_list();
+void WebSocketThrottle::RemoveFromQueue(WebSocketJob* job) {
+  bool in_queue = false;
+  for (ConnectingQueue::iterator iter = queue_.begin();
+       iter != queue_.end();
+       ++iter) {
+    if (*iter == job) {
+      queue_.erase(iter);
+      in_queue = true;
+      break;
+    }
+  }
+  if (!in_queue)
+    return;
+  const AddressList& address_list = job->address_list();
+  base::hash_set<std::string> address_set;
   for (const struct addrinfo* addrinfo = address_list.head();
        addrinfo != NULL;
        addrinfo = addrinfo->ai_next) {
     std::string addrkey = AddrinfoToHashkey(addrinfo);
+    // If |addrkey| is already processed, don't do it again.
+    if (address_set.find(addrkey) != address_set.end())
+      continue;
+    address_set.insert(addrkey);
+
     ConnectingAddressMap::iterator iter = addr_map_.find(addrkey);
     DCHECK(iter != addr_map_.end());
+
     ConnectingQueue* queue = iter->second;
-    DCHECK(state == queue->front());
-    queue->pop_front();
+    // Job may not be front of queue when job is closed early while waiting.
+    for (ConnectingQueue::iterator iter = queue->begin();
+         iter != queue->end();
+         ++iter) {
+      if (*iter == job) {
+        queue->erase(iter);
+        break;
+      }
+    }
     if (queue->empty()) {
       delete queue;
       addr_map_.erase(iter);
     }
   }
-  for (ConnectingQueue::iterator iter = queue_.begin();
-       iter != queue_.end();
-       ++iter) {
-    if (*iter == state) {
-      queue_.erase(iter);
-      break;
-    }
-  }
-  socket->SetUserData(WebSocketState::kKeyName, NULL);
 }
 
 void WebSocketThrottle::WakeupSocketIfNecessary() {
   for (ConnectingQueue::iterator iter = queue_.begin();
        iter != queue_.end();
        ++iter) {
-    WebSocketState* state = *iter;
-    if (!state->IsWaiting())
+    WebSocketJob* job = *iter;
+    if (!job->IsWaiting())
       continue;
 
     bool should_wakeup = true;
-    const AddressList& address_list = state->address_list();
+    const AddressList& address_list = job->address_list();
     for (const struct addrinfo* addrinfo = address_list.head();
          addrinfo != NULL;
          addrinfo = addrinfo->ai_next) {
@@ -267,19 +139,14 @@
       ConnectingAddressMap::iterator iter = addr_map_.find(addrkey);
       DCHECK(iter != addr_map_.end());
       ConnectingQueue* queue = iter->second;
-      if (state != queue->front()) {
+      if (job != queue->front()) {
         should_wakeup = false;
         break;
       }
     }
     if (should_wakeup)
-      state->Wakeup();
+      job->Wakeup();
   }
 }
 
-/* static */
-void WebSocketThrottle::Init() {
-  Singleton<WebSocketThrottle>::get();
-}
-
 }  // namespace net
diff --git a/net/websockets/websocket_throttle.h b/net/websockets/websocket_throttle.h
index 279aea2..d05b246 100644
--- a/net/websockets/websocket_throttle.h
+++ b/net/websockets/websocket_throttle.h
@@ -5,12 +5,17 @@
 #ifndef NET_WEBSOCKETS_WEBSOCKET_THROTTLE_H_
 #define NET_WEBSOCKETS_WEBSOCKET_THROTTLE_H_
 
+#include <deque>
+#include <string>
+
 #include "base/hash_tables.h"
 #include "base/singleton.h"
-#include "net/socket_stream/socket_stream_throttle.h"
 
 namespace net {
 
+class SocketStream;
+class WebSocketJob;
+
 // SocketStreamThrottle for WebSocket protocol.
 // Implements the client-side requirements in the spec.
 // http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
@@ -19,35 +24,16 @@
 //        remote host (IP address) identified by /host/, even if known by
 //        another name, wait until that connection has been established or
 //        for that connection to have failed.
-class WebSocketThrottle : public SocketStreamThrottle {
+class WebSocketThrottle {
  public:
-  virtual int OnStartOpenConnection(SocketStream* socket,
-                                    CompletionCallback* callback);
-  virtual int OnRead(SocketStream* socket, const char* data, int len,
-                     CompletionCallback* callback);
-  virtual int OnWrite(SocketStream* socket, const char* data, int len,
-                      CompletionCallback* callback);
-  virtual void OnClose(SocketStream* socket);
+  // Puts |job| in |queue_| and queues for the destination addresses
+  // of |job|.
+  // If other job is using the same destination address, set |job| waiting.
+  void PutInQueue(WebSocketJob* job);
 
-  static void Init();
-
- private:
-  class WebSocketState;
-  typedef std::deque<WebSocketState*> ConnectingQueue;
-  typedef base::hash_map<std::string, ConnectingQueue*> ConnectingAddressMap;
-
-  WebSocketThrottle();
-  virtual ~WebSocketThrottle();
-  friend struct DefaultSingletonTraits<WebSocketThrottle>;
-
-  // Puts |socket| in |queue_| and queues for the destination addresses
-  // of |socket|.  Also sets |state| as UserData of |socket|.
-  // If other socket is using the same destination address, set |state| waiting.
-  void PutInQueue(SocketStream* socket, WebSocketState* state);
-
-  // Removes |socket| from |queue_| and queues for the destination addresses
-  // of |socket|.  Also releases |state| from UserData of |socket|.
-  void RemoveFromQueue(SocketStream* socket, WebSocketState* state);
+  // Removes |job| from |queue_| and queues for the destination addresses
+  // of |job|.
+  void RemoveFromQueue(WebSocketJob* job);
 
   // Checks sockets waiting in |queue_| and check the socket is the front of
   // every queue for the destination addresses of |socket|.
@@ -55,6 +41,14 @@
   // the socket.
   void WakeupSocketIfNecessary();
 
+ private:
+  typedef std::deque<WebSocketJob*> ConnectingQueue;
+  typedef base::hash_map<std::string, ConnectingQueue*> ConnectingAddressMap;
+
+  WebSocketThrottle();
+  virtual ~WebSocketThrottle();
+  friend struct DefaultSingletonTraits<WebSocketThrottle>;
+
   // Key: string of host's address.  Value: queue of sockets for the address.
   ConnectingAddressMap addr_map_;
 
diff --git a/net/websockets/websocket_throttle_unittest.cc b/net/websockets/websocket_throttle_unittest.cc
index 55276e9..6d8c619 100644
--- a/net/websockets/websocket_throttle_unittest.cc
+++ b/net/websockets/websocket_throttle_unittest.cc
@@ -10,6 +10,7 @@
 #include "net/base/sys_addrinfo.h"
 #include "net/base/test_completion_callback.h"
 #include "net/socket_stream/socket_stream.h"
+#include "net/websockets/websocket_job.h"
 #include "net/websockets/websocket_throttle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
@@ -60,100 +61,248 @@
     }
   }
 
-  static void SetAddressList(SocketStream* socket, struct addrinfo* head) {
+  static void MockSocketStreamConnect(
+      SocketStream* socket, struct addrinfo* head) {
     socket->CopyAddrInfo(head);
+    // In SocketStream::Connect(), it adds reference to socket, which is
+    // balanced with SocketStream::Finish() that is finally called from
+    // SocketStream::Close() or SocketStream::DetachDelegate(), when
+    // next_state_ is not STATE_NONE.
+    // If next_state_ is STATE_NONE, SocketStream::Close() or
+    // SocketStream::DetachDelegate() won't call SocketStream::Finish(),
+    // so Release() won't be called.  Thus, we don't need socket->AddRef()
+    // here.
+    DCHECK_EQ(socket->next_state_, SocketStream::STATE_NONE);
   }
 };
 
 TEST_F(WebSocketThrottleTest, Throttle) {
-  WebSocketThrottle::Init();
   DummySocketStreamDelegate delegate;
 
-  WebSocketThrottle* throttle = Singleton<WebSocketThrottle>::get();
-
-  EXPECT_EQ(throttle,
-            SocketStreamThrottle::GetSocketStreamThrottleForScheme("ws"));
-  EXPECT_EQ(throttle,
-            SocketStreamThrottle::GetSocketStreamThrottleForScheme("wss"));
-
   // For host1: 1.2.3.4, 1.2.3.5, 1.2.3.6
   struct addrinfo* addr = AddAddr(1, 2, 3, 4, NULL);
   addr = AddAddr(1, 2, 3, 5, addr);
   addr = AddAddr(1, 2, 3, 6, addr);
+  scoped_refptr<WebSocketJob> w1 = new WebSocketJob(&delegate);
   scoped_refptr<SocketStream> s1 =
-      new SocketStream(GURL("ws://host1/"), &delegate);
-  WebSocketThrottleTest::SetAddressList(s1, addr);
+      new SocketStream(GURL("ws://host1/"), w1.get());
+  w1->InitSocketStream(s1.get());
+  WebSocketThrottleTest::MockSocketStreamConnect(s1, addr);
   DeleteAddrInfo(addr);
 
+  DLOG(INFO) << "socket1";
   TestCompletionCallback callback_s1;
-  EXPECT_EQ(OK, throttle->OnStartOpenConnection(s1, &callback_s1));
+  // Trying to open connection to host1 will start without wait.
+  EXPECT_EQ(OK, w1->OnStartOpenConnection(s1, &callback_s1));
+
+  // Now connecting to host1, so waiting queue looks like
+  // Address | head -> tail
+  // 1.2.3.4 | w1
+  // 1.2.3.5 | w1
+  // 1.2.3.6 | w1
 
   // For host2: 1.2.3.4
   addr = AddAddr(1, 2, 3, 4, NULL);
+  scoped_refptr<WebSocketJob> w2 = new WebSocketJob(&delegate);
   scoped_refptr<SocketStream> s2 =
-      new SocketStream(GURL("ws://host2/"), &delegate);
-  WebSocketThrottleTest::SetAddressList(s2, addr);
+      new SocketStream(GURL("ws://host2/"), w2.get());
+  w2->InitSocketStream(s2.get());
+  WebSocketThrottleTest::MockSocketStreamConnect(s2, addr);
   DeleteAddrInfo(addr);
 
+  DLOG(INFO) << "socket2";
   TestCompletionCallback callback_s2;
-  EXPECT_EQ(ERR_IO_PENDING, throttle->OnStartOpenConnection(s2, &callback_s2));
+  // Trying to open connection to host2 will wait for w1.
+  EXPECT_EQ(ERR_IO_PENDING, w2->OnStartOpenConnection(s2, &callback_s2));
+  // Now waiting queue looks like
+  // Address | head -> tail
+  // 1.2.3.4 | w1 w2
+  // 1.2.3.5 | w1
+  // 1.2.3.6 | w1
 
   // For host3: 1.2.3.5
   addr = AddAddr(1, 2, 3, 5, NULL);
+  scoped_refptr<WebSocketJob> w3 = new WebSocketJob(&delegate);
   scoped_refptr<SocketStream> s3 =
-      new SocketStream(GURL("ws://host3/"), &delegate);
-  WebSocketThrottleTest::SetAddressList(s3, addr);
+      new SocketStream(GURL("ws://host3/"), w3.get());
+  w3->InitSocketStream(s3.get());
+  WebSocketThrottleTest::MockSocketStreamConnect(s3, addr);
   DeleteAddrInfo(addr);
 
+  DLOG(INFO) << "socket3";
   TestCompletionCallback callback_s3;
-  EXPECT_EQ(ERR_IO_PENDING, throttle->OnStartOpenConnection(s3, &callback_s3));
+  // Trying to open connection to host3 will wait for w1.
+  EXPECT_EQ(ERR_IO_PENDING, w3->OnStartOpenConnection(s3, &callback_s3));
+  // Address | head -> tail
+  // 1.2.3.4 | w1 w2
+  // 1.2.3.5 | w1    w3
+  // 1.2.3.6 | w1
 
   // For host4: 1.2.3.4, 1.2.3.6
   addr = AddAddr(1, 2, 3, 4, NULL);
   addr = AddAddr(1, 2, 3, 6, addr);
+  scoped_refptr<WebSocketJob> w4 = new WebSocketJob(&delegate);
   scoped_refptr<SocketStream> s4 =
-      new SocketStream(GURL("ws://host4/"), &delegate);
-  WebSocketThrottleTest::SetAddressList(s4, addr);
+      new SocketStream(GURL("ws://host4/"), w4.get());
+  w4->InitSocketStream(s4.get());
+  WebSocketThrottleTest::MockSocketStreamConnect(s4, addr);
   DeleteAddrInfo(addr);
 
+  DLOG(INFO) << "socket4";
   TestCompletionCallback callback_s4;
-  EXPECT_EQ(ERR_IO_PENDING, throttle->OnStartOpenConnection(s4, &callback_s4));
+  // Trying to open connection to host4 will wait for w1, w2.
+  EXPECT_EQ(ERR_IO_PENDING, w4->OnStartOpenConnection(s4, &callback_s4));
+  // Address | head -> tail
+  // 1.2.3.4 | w1 w2    w4
+  // 1.2.3.5 | w1    w3
+  // 1.2.3.6 | w1       w4
 
-  static const char kHeader[] = "HTTP/1.1 101 Web Socket Protocol\r\n";
-  EXPECT_EQ(OK,
-            throttle->OnRead(s1.get(), kHeader, sizeof(kHeader) - 1, NULL));
+  // For host5: 1.2.3.6
+  addr = AddAddr(1, 2, 3, 6, NULL);
+  scoped_refptr<WebSocketJob> w5 = new WebSocketJob(&delegate);
+  scoped_refptr<SocketStream> s5 =
+      new SocketStream(GURL("ws://host5/"), w5.get());
+  w5->InitSocketStream(s5.get());
+  WebSocketThrottleTest::MockSocketStreamConnect(s5, addr);
+  DeleteAddrInfo(addr);
+
+  DLOG(INFO) << "socket5";
+  TestCompletionCallback callback_s5;
+  // Trying to open connection to host5 will wait for w1, w4
+  EXPECT_EQ(ERR_IO_PENDING, w5->OnStartOpenConnection(s5, &callback_s5));
+  // Address | head -> tail
+  // 1.2.3.4 | w1 w2    w4
+  // 1.2.3.5 | w1    w3
+  // 1.2.3.6 | w1       w4 w5
+
+  // For host6: 1.2.3.6
+  addr = AddAddr(1, 2, 3, 6, NULL);
+  scoped_refptr<WebSocketJob> w6 = new WebSocketJob(&delegate);
+  scoped_refptr<SocketStream> s6 =
+      new SocketStream(GURL("ws://host6/"), w6.get());
+  w6->InitSocketStream(s6.get());
+  WebSocketThrottleTest::MockSocketStreamConnect(s6, addr);
+  DeleteAddrInfo(addr);
+
+  DLOG(INFO) << "socket6";
+  TestCompletionCallback callback_s6;
+  // Trying to open connection to host6 will wait for w1, w4, w5
+  EXPECT_EQ(ERR_IO_PENDING, w6->OnStartOpenConnection(s6, &callback_s6));
+  // Address | head -> tail
+  // 1.2.3.4 | w1 w2    w4
+  // 1.2.3.5 | w1    w3
+  // 1.2.3.6 | w1       w4 w5 w6
+
+  // Receive partial response on w1, still connecting.
+  DLOG(INFO) << "socket1 1";
+  static const char kHeader[] = "HTTP/1.1 101 WebSocket Protocol\r\n";
+  w1->OnReceivedData(s1.get(), kHeader, sizeof(kHeader) - 1);
   EXPECT_FALSE(callback_s2.have_result());
   EXPECT_FALSE(callback_s3.have_result());
   EXPECT_FALSE(callback_s4.have_result());
+  EXPECT_FALSE(callback_s5.have_result());
+  EXPECT_FALSE(callback_s6.have_result());
 
+  // Receive rest of handshake response on w1.
+  DLOG(INFO) << "socket1 2";
   static const char kHeader2[] =
       "Upgrade: WebSocket\r\n"
       "Connection: Upgrade\r\n"
-      "WebSocket-Origin: http://www.google.com\r\n"
-      "WebSocket-Location: ws://websocket.chromium.org\r\n"
-      "\r\n";
-  EXPECT_EQ(OK,
-            throttle->OnRead(s1.get(), kHeader2, sizeof(kHeader2) - 1, NULL));
+      "Sec-WebSocket-Origin: http://www.google.com\r\n"
+      "Sec-WebSocket-Location: ws://websocket.chromium.org\r\n"
+      "\r\n"
+      "8jKS'y:G*Co,Wxa-";
+  w1->OnReceivedData(s1.get(), kHeader2, sizeof(kHeader2) - 1);
   MessageLoopForIO::current()->RunAllPending();
+  // Now, w1 is open.
+  EXPECT_EQ(WebSocketJob::OPEN, w1->state());
+  // So, w2 and w3 can start connecting. w4 needs to wait w2 (1.2.3.4)
   EXPECT_TRUE(callback_s2.have_result());
   EXPECT_TRUE(callback_s3.have_result());
   EXPECT_FALSE(callback_s4.have_result());
+  // Address | head -> tail
+  // 1.2.3.4 |    w2    w4
+  // 1.2.3.5 |       w3
+  // 1.2.3.6 |          w4 w5 w6
 
-  throttle->OnClose(s1.get());
+  // Closing s1 doesn't change waiting queue.
+  DLOG(INFO) << "socket1 close";
+  w1->OnClose(s1.get());
   MessageLoopForIO::current()->RunAllPending();
   EXPECT_FALSE(callback_s4.have_result());
   s1->DetachDelegate();
+  // Address | head -> tail
+  // 1.2.3.4 |    w2    w4
+  // 1.2.3.5 |       w3
+  // 1.2.3.6 |          w4 w5 w6
 
-  throttle->OnClose(s2.get());
+  // w5 can close while waiting in queue.
+  DLOG(INFO) << "socket5 close";
+  // w5 close() closes SocketStream that change state to STATE_CLOSE, calls
+  // DoLoop(), so OnClose() callback will be called.
+  w5->OnClose(s5.get());
+  MessageLoopForIO::current()->RunAllPending();
+  EXPECT_FALSE(callback_s4.have_result());
+  // Address | head -> tail
+  // 1.2.3.4 |    w2    w4
+  // 1.2.3.5 |       w3
+  // 1.2.3.6 |          w4 w6
+  s5->DetachDelegate();
+
+  // w6 close abnormally (e.g. renderer finishes) while waiting in queue.
+  DLOG(INFO) << "socket6 close abnormally";
+  w6->DetachDelegate();
+  MessageLoopForIO::current()->RunAllPending();
+  EXPECT_FALSE(callback_s4.have_result());
+  // Address | head -> tail
+  // 1.2.3.4 |    w2    w4
+  // 1.2.3.5 |       w3
+  // 1.2.3.6 |          w4
+
+  // Closing s2 kicks w4 to start connecting.
+  DLOG(INFO) << "socket2 close";
+  w2->OnClose(s2.get());
   MessageLoopForIO::current()->RunAllPending();
   EXPECT_TRUE(callback_s4.have_result());
+  // Address | head -> tail
+  // 1.2.3.4 |          w4
+  // 1.2.3.5 |       w3
+  // 1.2.3.6 |          w4
   s2->DetachDelegate();
 
-  throttle->OnClose(s3.get());
+  DLOG(INFO) << "socket3 close";
+  w3->OnClose(s3.get());
   MessageLoopForIO::current()->RunAllPending();
   s3->DetachDelegate();
-  throttle->OnClose(s4.get());
+  w4->OnClose(s4.get());
   s4->DetachDelegate();
+  DLOG(INFO) << "Done";
+  MessageLoopForIO::current()->RunAllPending();
+}
+
+TEST_F(WebSocketThrottleTest, NoThrottleForDuplicateAddress) {
+  DummySocketStreamDelegate delegate;
+
+  // For localhost: 127.0.0.1, 127.0.0.1
+  struct addrinfo* addr = AddAddr(127, 0, 0, 1, NULL);
+  addr = AddAddr(127, 0, 0, 1, addr);
+  scoped_refptr<WebSocketJob> w1 = new WebSocketJob(&delegate);
+  scoped_refptr<SocketStream> s1 =
+      new SocketStream(GURL("ws://localhost/"), w1.get());
+  w1->InitSocketStream(s1.get());
+  WebSocketThrottleTest::MockSocketStreamConnect(s1, addr);
+  DeleteAddrInfo(addr);
+
+  DLOG(INFO) << "socket1";
+  TestCompletionCallback callback_s1;
+  // Trying to open connection to localhost will start without wait.
+  EXPECT_EQ(OK, w1->OnStartOpenConnection(s1, &callback_s1));
+
+  DLOG(INFO) << "socket1 close";
+  w1->OnClose(s1.get());
+  s1->DetachDelegate();
+  DLOG(INFO) << "Done";
+  MessageLoopForIO::current()->RunAllPending();
 }
 
 }
diff --git a/net/websockets/websocket_unittest.cc b/net/websockets/websocket_unittest.cc
index e3c5725..4b65ae9 100644
--- a/net/websockets/websocket_unittest.cc
+++ b/net/websockets/websocket_unittest.cc
@@ -5,7 +5,7 @@
 #include <string>
 #include <vector>
 
-#include "base/task.h"
+#include "base/callback.h"
 #include "net/base/completion_callback.h"
 #include "net/base/io_buffer.h"
 #include "net/base/mock_host_resolver.h"
@@ -19,16 +19,18 @@
 
 struct WebSocketEvent {
   enum EventType {
-    EVENT_OPEN, EVENT_MESSAGE, EVENT_CLOSE,
+    EVENT_OPEN, EVENT_MESSAGE, EVENT_ERROR, EVENT_CLOSE,
   };
 
   WebSocketEvent(EventType type, net::WebSocket* websocket,
-                 const std::string& websocket_msg)
-      : event_type(type), socket(websocket), msg(websocket_msg) {}
+                 const std::string& websocket_msg, bool websocket_flag)
+      : event_type(type), socket(websocket), msg(websocket_msg),
+        flag(websocket_flag) {}
 
   EventType event_type;
   net::WebSocket* socket;
   std::string msg;
+  bool flag;
 };
 
 class WebSocketEventRecorder : public net::WebSocketDelegate {
@@ -36,11 +38,13 @@
   explicit WebSocketEventRecorder(net::CompletionCallback* callback)
       : onopen_(NULL),
         onmessage_(NULL),
+        onerror_(NULL),
         onclose_(NULL),
         callback_(callback) {}
   virtual ~WebSocketEventRecorder() {
     delete onopen_;
     delete onmessage_;
+    delete onerror_;
     delete onclose_;
   }
 
@@ -56,20 +60,29 @@
 
   virtual void OnOpen(net::WebSocket* socket) {
     events_.push_back(
-        WebSocketEvent(WebSocketEvent::EVENT_OPEN, socket, std::string()));
+        WebSocketEvent(WebSocketEvent::EVENT_OPEN, socket,
+                       std::string(), false));
     if (onopen_)
       onopen_->Run(&events_.back());
   }
 
   virtual void OnMessage(net::WebSocket* socket, const std::string& msg) {
     events_.push_back(
-        WebSocketEvent(WebSocketEvent::EVENT_MESSAGE, socket, msg));
+        WebSocketEvent(WebSocketEvent::EVENT_MESSAGE, socket, msg, false));
     if (onmessage_)
       onmessage_->Run(&events_.back());
   }
-  virtual void OnClose(net::WebSocket* socket) {
+  virtual void OnError(net::WebSocket* socket) {
     events_.push_back(
-        WebSocketEvent(WebSocketEvent::EVENT_CLOSE, socket, std::string()));
+        WebSocketEvent(WebSocketEvent::EVENT_ERROR, socket,
+                       std::string(), false));
+    if (onerror_)
+      onerror_->Run(&events_.back());
+  }
+  virtual void OnClose(net::WebSocket* socket, bool was_clean) {
+    events_.push_back(
+        WebSocketEvent(WebSocketEvent::EVENT_CLOSE, socket,
+                       std::string(), was_clean));
     if (onclose_)
       onclose_->Run(&events_.back());
     if (callback_)
@@ -88,6 +101,7 @@
   std::vector<WebSocketEvent> events_;
   Callback1<WebSocketEvent*>::Type* onopen_;
   Callback1<WebSocketEvent*>::Type* onmessage_;
+  Callback1<WebSocketEvent*>::Type* onerror_;
   Callback1<WebSocketEvent*>::Type* onclose_;
   net::CompletionCallback* callback_;
 
@@ -148,7 +162,8 @@
               "WebSocket-Protocol: sample\r\n"
               "\r\n"),
   };
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   mock_socket_factory.AddSocketDataProvider(&data);
 
   WebSocket::Request* request(
@@ -156,6 +171,7 @@
                              "sample",
                              "http://example.com",
                              "ws://example.com/demo",
+                             WebSocket::DRAFT75,
                              new TestURLRequestContext()));
   request->SetHostResolver(new MockHostResolver());
   request->SetClientSocketFactory(&mock_socket_factory);
@@ -208,7 +224,8 @@
               "WebSocket-Protocol: sample\r\n"
               "\r\n"),
   };
-  StaticSocketDataProvider data(data_reads, data_writes);
+  StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+                                data_writes, arraysize(data_writes));
   mock_socket_factory.AddSocketDataProvider(&data);
 
   WebSocket::Request* request(
@@ -216,6 +233,7 @@
                              "sample",
                              "http://example.com",
                              "ws://example.com/demo",
+                             WebSocket::DRAFT75,
                              new TestURLRequestContext()));
   request->SetHostResolver(new MockHostResolver());
   request->SetClientSocketFactory(&mock_socket_factory);
@@ -250,6 +268,7 @@
                              "sample",
                              "http://example.com",
                              "ws://example.com/demo",
+                             WebSocket::DRAFT75,
                              new TestURLRequestContext()));
   TestCompletionCallback callback;
   scoped_ptr<WebSocketEventRecorder> delegate(
@@ -274,7 +293,9 @@
                        kExpectedRemainingFrame, kExpectedRemainingLength);
   // No onmessage event expected.
   const std::vector<WebSocketEvent>& events = delegate->GetSeenEvents();
-  EXPECT_EQ(0U, events.size());
+  EXPECT_EQ(1U, events.size());
+
+  EXPECT_EQ(WebSocketEvent::EVENT_ERROR, events[0].event_type);
 
   websocket->DetachDelegate();
 }
@@ -285,6 +306,7 @@
                              "sample",
                              "http://example.com",
                              "ws://example.com/demo",
+                             WebSocket::DRAFT75,
                              new TestURLRequestContext()));
   TestCompletionCallback callback;
   scoped_ptr<WebSocketEventRecorder> delegate(
@@ -327,129 +349,4 @@
   websocket->DetachDelegate();
 }
 
-TEST(WebSocketRequestTest, is_secure_false) {
-  WebSocket::Request request(GURL("ws://example.com/demo"),
-                             "sample",
-                             "http://example.com",
-                             "ws://example.com/demo",
-                             NULL);
-  EXPECT_FALSE(request.is_secure());
-}
-
-TEST(WebSocketRequestTest, is_secure_true) {
-  // wss:// is secure.
-  WebSocket::Request request(GURL("wss://example.com/demo"),
-                             "sample",
-                             "http://example.com",
-                             "wss://example.com/demo",
-                             NULL);
-  EXPECT_TRUE(request.is_secure());
-}
-
-TEST(WebSocketRequestTest, CreateClientHandshakeMessage_Simple) {
-  WebSocket::Request request(GURL("ws://example.com/demo"),
-                             "sample",
-                             "http://example.com",
-                             "ws://example.com/demo",
-                             NULL);
-  EXPECT_EQ("GET /demo HTTP/1.1\r\n"
-            "Upgrade: WebSocket\r\n"
-            "Connection: Upgrade\r\n"
-            "Host: example.com\r\n"
-            "Origin: http://example.com\r\n"
-            "WebSocket-Protocol: sample\r\n"
-            "\r\n",
-            request.CreateClientHandshakeMessage());
-}
-
-TEST(WebSocketRequestTest, CreateClientHandshakeMessage_PathAndQuery) {
-  WebSocket::Request request(GURL("ws://example.com/Test?q=xxx&p=%20"),
-                             "sample",
-                             "http://example.com",
-                             "ws://example.com/demo",
-                             NULL);
-  // Path and query should be preserved as-is.
-  EXPECT_THAT(request.CreateClientHandshakeMessage(),
-              testing::HasSubstr("GET /Test?q=xxx&p=%20 HTTP/1.1\r\n"));
-}
-
-TEST(WebSocketRequestTest, CreateClientHandshakeMessage_Host) {
-  WebSocket::Request request(GURL("ws://Example.Com/demo"),
-                             "sample",
-                             "http://Example.Com",
-                             "ws://Example.Com/demo",
-                             NULL);
-  // Host should be lowercased
-  EXPECT_THAT(request.CreateClientHandshakeMessage(),
-              testing::HasSubstr("Host: example.com\r\n"));
-  EXPECT_THAT(request.CreateClientHandshakeMessage(),
-              testing::HasSubstr("Origin: http://example.com\r\n"));
-}
-
-TEST(WebSocketRequestTest, CreateClientHandshakeMessage_TrimPort80) {
-  WebSocket::Request request(GURL("ws://example.com:80/demo"),
-                             "sample",
-                             "http://example.com",
-                             "ws://example.com/demo",
-                             NULL);
-  // :80 should be trimmed as it's the default port for ws://.
-  EXPECT_THAT(request.CreateClientHandshakeMessage(),
-              testing::HasSubstr("Host: example.com\r\n"));
-}
-
-TEST(WebSocketRequestTest, CreateClientHandshakeMessage_TrimPort443) {
-  WebSocket::Request request(GURL("wss://example.com:443/demo"),
-                             "sample",
-                             "http://example.com",
-                             "wss://example.com/demo",
-                             NULL);
-  // :443 should be trimmed as it's the default port for wss://.
-  EXPECT_THAT(request.CreateClientHandshakeMessage(),
-              testing::HasSubstr("Host: example.com\r\n"));
-}
-
-TEST(WebSocketRequestTest, CreateClientHandshakeMessage_NonDefaultPortForWs) {
-  WebSocket::Request request(GURL("ws://example.com:8080/demo"),
-                             "sample",
-                             "http://example.com",
-                             "wss://example.com/demo",
-                             NULL);
-  // :8080 should be preserved as it's not the default port for ws://.
-  EXPECT_THAT(request.CreateClientHandshakeMessage(),
-              testing::HasSubstr("Host: example.com:8080\r\n"));
-}
-
-TEST(WebSocketRequestTest, CreateClientHandshakeMessage_NonDefaultPortForWss) {
-  WebSocket::Request request(GURL("wss://example.com:4443/demo"),
-                             "sample",
-                             "http://example.com",
-                             "wss://example.com/demo",
-                             NULL);
-  // :4443 should be preserved as it's not the default port for wss://.
-  EXPECT_THAT(request.CreateClientHandshakeMessage(),
-              testing::HasSubstr("Host: example.com:4443\r\n"));
-}
-
-TEST(WebSocketRequestTest, CreateClientHandshakeMessage_WsBut443) {
-  WebSocket::Request request(GURL("ws://example.com:443/demo"),
-                             "sample",
-                             "http://example.com",
-                             "ws://example.com/demo",
-                             NULL);
-  // :443 should be preserved as it's not the default port for ws://.
-  EXPECT_THAT(request.CreateClientHandshakeMessage(),
-              testing::HasSubstr("Host: example.com:443\r\n"));
-}
-
-TEST(WebSocketRequestTest, CreateClientHandshakeMessage_WssBut80) {
-  WebSocket::Request request(GURL("wss://example.com:80/demo"),
-                             "sample",
-                             "http://example.com",
-                             "wss://example.com/demo",
-                             NULL);
-  // :80 should be preserved as it's not the default port for wss://.
-  EXPECT_THAT(request.CreateClientHandshakeMessage(),
-              testing::HasSubstr("Host: example.com:80\r\n"));
-}
-
 }  // namespace net