1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/http/http_network_layer.h"
6
7#include "base/logging.h"
8#include "base/string_number_conversions.h"
9#include "base/string_split.h"
10#include "base/string_util.h"
11#include "net/http/http_network_session.h"
12#include "net/http/http_network_transaction.h"
13#include "net/spdy/spdy_framer.h"
14#include "net/spdy/spdy_session.h"
15#include "net/spdy/spdy_session_pool.h"
16
17namespace net {
18
19//-----------------------------------------------------------------------------
20HttpNetworkLayer::HttpNetworkLayer(HttpNetworkSession* session)
21    : session_(session),
22      suspended_(false) {
23  DCHECK(session_.get());
24}
25
26HttpNetworkLayer::~HttpNetworkLayer() {
27}
28
29//-----------------------------------------------------------------------------
30
31// static
32HttpTransactionFactory* HttpNetworkLayer::CreateFactory(
33    HttpNetworkSession* session) {
34  DCHECK(session);
35
36  return new HttpNetworkLayer(session);
37}
38
39// static
40void HttpNetworkLayer::EnableSpdy(const std::string& mode) {
41  static const char kOff[] = "off";
42  static const char kSSL[] = "ssl";
43  static const char kDisableSSL[] = "no-ssl";
44  static const char kDisablePing[] = "no-ping";
45  static const char kExclude[] = "exclude";  // Hosts to exclude
46  static const char kDisableCompression[] = "no-compress";
47  static const char kDisableAltProtocols[] = "no-alt-protocols";
48  static const char kEnableVersionOne[] = "v1";
49  static const char kForceAltProtocols[] = "force-alt-protocols";
50  static const char kSingleDomain[] = "single-domain";
51
52  // If flow-control is enabled, received WINDOW_UPDATE and SETTINGS
53  // messages are processed and outstanding window size is actually obeyed
54  // when sending data frames, and WINDOW_UPDATE messages are generated
55  // when data is consumed.
56  static const char kEnableFlowControl[] = "flow-control";
57
58  // We want an A/B experiment between SPDY enabled and SPDY disabled,
59  // but only for pages where SPDY *could have been* negotiated.  To do
60  // this, we use NPN, but prevent it from negotiating SPDY.  If the
61  // server negotiates HTTP, rather than SPDY, today that will only happen
62  // on servers that installed NPN (and could have done SPDY).  But this is
63  // a bit of a hack, as this correlation between NPN and SPDY is not
64  // really guaranteed.
65  static const char kEnableNPN[] = "npn";
66  static const char kEnableNpnHttpOnly[] = "npn-http";
67
68  // Except for the first element, the order is irrelevant.  First element
69  // specifies the fallback in case nothing matches
70  // (SSLClientSocket::kNextProtoNoOverlap).  Otherwise, the SSL library
71  // will choose the first overlapping protocol in the server's list, since
72  // it presumedly has a better understanding of which protocol we should
73  // use, therefore the rest of the ordering here is not important.
74  static const char kNpnProtosFull[] = "\x08http/1.1\x06spdy/2";
75  // This is a temporary hack to pretend we support version 1.
76  static const char kNpnProtosFullV1[] = "\x08http/1.1\x06spdy/1";
77  // No spdy specified.
78  static const char kNpnProtosHttpOnly[] = "\x08http/1.1\x07http1.1";
79
80  std::vector<std::string> spdy_options;
81  base::SplitString(mode, ',', &spdy_options);
82
83  bool use_alt_protocols = true;
84
85  for (std::vector<std::string>::iterator it = spdy_options.begin();
86       it != spdy_options.end(); ++it) {
87    const std::string& element = *it;
88    std::vector<std::string> name_value;
89    base::SplitString(element, '=', &name_value);
90    const std::string& option = name_value[0];
91    const std::string value = name_value.size() > 1 ? name_value[1] : "";
92
93    if (option == kOff) {
94      HttpStreamFactory::set_spdy_enabled(false);
95    } else if (option == kDisableSSL) {
96      SpdySession::SetSSLMode(false);  // Disable SSL
97      HttpStreamFactory::set_force_spdy_over_ssl(false);
98      HttpStreamFactory::set_force_spdy_always(true);
99    } else if (option == kSSL) {
100      HttpStreamFactory::set_force_spdy_over_ssl(true);
101      HttpStreamFactory::set_force_spdy_always(true);
102    } else if (option == kDisablePing) {
103      SpdySession::set_enable_ping_based_connection_checking(false);
104    } else if (option == kExclude) {
105      HttpStreamFactory::add_forced_spdy_exclusion(value);
106    } else if (option == kDisableCompression) {
107      spdy::SpdyFramer::set_enable_compression_default(false);
108    } else if (option == kEnableNPN) {
109      HttpStreamFactory::set_use_alternate_protocols(use_alt_protocols);
110      HttpStreamFactory::set_next_protos(kNpnProtosFull);
111    } else if (option == kEnableNpnHttpOnly) {
112      // Avoid alternate protocol in this case. Otherwise, browser will try SSL
113      // and then fallback to http. This introduces extra load.
114      HttpStreamFactory::set_use_alternate_protocols(false);
115      HttpStreamFactory::set_next_protos(kNpnProtosHttpOnly);
116    } else if (option == kEnableVersionOne) {
117      spdy::SpdyFramer::set_protocol_version(1);
118      HttpStreamFactory::set_next_protos(kNpnProtosFullV1);
119    } else if (option == kDisableAltProtocols) {
120      use_alt_protocols = false;
121      HttpStreamFactory::set_use_alternate_protocols(false);
122    } else if (option == kEnableFlowControl) {
123      SpdySession::set_flow_control(true);
124    } else if (option == kForceAltProtocols) {
125      HttpAlternateProtocols::PortProtocolPair pair;
126      pair.port = 443;
127      pair.protocol = HttpAlternateProtocols::NPN_SPDY_2;
128      HttpAlternateProtocols::ForceAlternateProtocol(pair);
129    } else if (option == kSingleDomain) {
130      SpdySessionPool::ForceSingleDomain();
131      LOG(ERROR) << "FORCING SINGLE DOMAIN";
132    } else if (option.empty() && it == spdy_options.begin()) {
133      continue;
134    } else {
135      LOG(DFATAL) << "Unrecognized spdy option: " << option;
136    }
137  }
138}
139
140//-----------------------------------------------------------------------------
141
142int HttpNetworkLayer::CreateTransaction(scoped_ptr<HttpTransaction>* trans) {
143  if (suspended_)
144    return ERR_NETWORK_IO_SUSPENDED;
145
146  trans->reset(new HttpNetworkTransaction(GetSession()));
147  return OK;
148}
149
150HttpCache* HttpNetworkLayer::GetCache() {
151  return NULL;
152}
153
154HttpNetworkSession* HttpNetworkLayer::GetSession() {
155  return session_;
156}
157
158void HttpNetworkLayer::Suspend(bool suspend) {
159  suspended_ = suspend;
160
161  if (suspend && session_)
162    session_->CloseIdleConnections();
163}
164
165}  // namespace net
166