blob: 2b03425a403a414b23f7c403de95bc911516e9e9 [file] [log] [blame]
[email protected]c8b16742010-02-05 20:49:531// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]aea80602009-09-18 00:55:082// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]dab9c7d2010-02-06 21:44:325#include "net/spdy/spdy_session.h"
[email protected]aea80602009-09-18 00:55:086
7#include "base/basictypes.h"
8#include "base/logging.h"
9#include "base/message_loop.h"
10#include "base/rand_util.h"
11#include "base/stats_counters.h"
[email protected]a677f2b2009-11-22 00:43:0012#include "base/stl_util-inl.h"
[email protected]aea80602009-09-18 00:55:0813#include "base/string_util.h"
[email protected]3f662f12010-03-25 19:56:1214#include "base/time.h"
[email protected]5e2e6c77d12009-12-24 21:57:1615#include "net/base/connection_type_histograms.h"
[email protected]aea80602009-09-18 00:55:0816#include "net/base/load_flags.h"
[email protected]9e743cd2010-03-16 07:03:5317#include "net/base/net_log.h"
[email protected]33751562009-10-29 02:17:1118#include "net/base/net_util.h"
[email protected]aea80602009-09-18 00:55:0819#include "net/http/http_network_session.h"
20#include "net/http/http_request_info.h"
21#include "net/http/http_response_headers.h"
22#include "net/http/http_response_info.h"
[email protected]1f14a912009-12-21 20:32:4423#include "net/socket/client_socket.h"
[email protected]18c53ae2009-10-01 18:18:5224#include "net/socket/client_socket_factory.h"
25#include "net/socket/ssl_client_socket.h"
[email protected]dab9c7d2010-02-06 21:44:3226#include "net/spdy/spdy_frame_builder.h"
27#include "net/spdy/spdy_protocol.h"
[email protected]74188f22010-04-09 20:18:5028#include "net/spdy/spdy_settings_storage.h"
[email protected]dab9c7d2010-02-06 21:44:3229#include "net/spdy/spdy_stream.h"
[email protected]aea80602009-09-18 00:55:0830#include "net/tools/dump_cache/url_to_filename_encoder.h"
31
[email protected]967dd5412009-11-21 23:33:3032namespace {
33
34// Diagnostics function to dump the headers of a request.
35// TODO(mbelshe): Remove this function.
[email protected]955fc2e72010-02-08 20:37:3036void DumpSpdyHeaders(const spdy::SpdyHeaderBlock& headers) {
[email protected]967dd5412009-11-21 23:33:3037 // Because this function gets called on every request,
38 // take extra care to optimize it away if logging is turned off.
39 if (logging::LOG_INFO < logging::GetMinLogLevel())
40 return;
41
[email protected]955fc2e72010-02-08 20:37:3042 spdy::SpdyHeaderBlock::const_iterator it = headers.begin();
[email protected]967dd5412009-11-21 23:33:3043 while (it != headers.end()) {
44 std::string val = (*it).second;
45 std::string::size_type pos = 0;
46 while ((pos = val.find('\0', pos)) != val.npos)
47 val[pos] = '\n';
48 LOG(INFO) << (*it).first << "==" << val;
49 ++it;
50 }
51}
52
53} // namespace
54
[email protected]aea80602009-09-18 00:55:0855namespace net {
56
[email protected]a677f2b2009-11-22 00:43:0057namespace {
58
[email protected]9fc38b32010-01-11 21:34:4659#ifdef WIN32
60// We use an artificially small buffer size on windows because the async IO
61// system will artifiially delay IO completions when we use large buffers.
62const int kReadBufferSize = 2 * 1024;
63#else
64const int kReadBufferSize = 8 * 1024;
65#endif
[email protected]a677f2b2009-11-22 00:43:0066
[email protected]955fc2e72010-02-08 20:37:3067// Convert a SpdyHeaderBlock into an HttpResponseInfo.
68// |headers| input parameter with the SpdyHeaderBlock.
[email protected]dd11b932009-11-30 19:39:4869// |info| output parameter for the HttpResponseInfo.
70// Returns true if successfully converted. False if there was a failure
[email protected]955fc2e72010-02-08 20:37:3071// or if the SpdyHeaderBlock was invalid.
72bool SpdyHeadersToHttpResponse(const spdy::SpdyHeaderBlock& headers,
[email protected]dd11b932009-11-30 19:39:4873 HttpResponseInfo* response) {
74 std::string version;
75 std::string status;
76
77 // The "status" and "version" headers are required.
[email protected]955fc2e72010-02-08 20:37:3078 spdy::SpdyHeaderBlock::const_iterator it;
[email protected]dd11b932009-11-30 19:39:4879 it = headers.find("status");
80 if (it == headers.end()) {
[email protected]955fc2e72010-02-08 20:37:3081 LOG(ERROR) << "SpdyHeaderBlock without status header.";
[email protected]dd11b932009-11-30 19:39:4882 return false;
83 }
84 status = it->second;
85
86 // Grab the version. If not provided by the server,
87 it = headers.find("version");
88 if (it == headers.end()) {
[email protected]955fc2e72010-02-08 20:37:3089 LOG(ERROR) << "SpdyHeaderBlock without version header.";
[email protected]dd11b932009-11-30 19:39:4890 return false;
91 }
92 version = it->second;
93
[email protected]3f662f12010-03-25 19:56:1294 response->response_time = base::Time::Now();
95
[email protected]dd11b932009-11-30 19:39:4896 std::string raw_headers(version);
97 raw_headers.push_back(' ');
98 raw_headers.append(status);
99 raw_headers.push_back('\0');
[email protected]a677f2b2009-11-22 00:43:00100 for (it = headers.begin(); it != headers.end(); ++it) {
101 // For each value, if the server sends a NUL-separated
102 // list of values, we separate that back out into
103 // individual headers for each value in the list.
104 // e.g.
105 // Set-Cookie "foo\0bar"
106 // becomes
107 // Set-Cookie: foo\0
108 // Set-Cookie: bar\0
109 std::string value = it->second;
110 size_t start = 0;
111 size_t end = 0;
112 do {
113 end = value.find('\0', start);
114 std::string tval;
115 if (end != value.npos)
116 tval = value.substr(start, (end - start));
117 else
118 tval = value.substr(start);
119 raw_headers.append(it->first);
120 raw_headers.push_back(':');
121 raw_headers.append(tval);
122 raw_headers.push_back('\0');
123 start = end + 1;
124 } while (end != value.npos);
125 }
126
[email protected]dd11b932009-11-30 19:39:48127 response->headers = new HttpResponseHeaders(raw_headers);
[email protected]9fc38b32010-01-11 21:34:46128 response->was_fetched_via_spdy = true;
[email protected]dd11b932009-11-30 19:39:48129 return true;
[email protected]a677f2b2009-11-22 00:43:00130}
131
[email protected]955fc2e72010-02-08 20:37:30132// Create a SpdyHeaderBlock for a Spdy SYN_STREAM Frame from
[email protected]a677f2b2009-11-22 00:43:00133// a HttpRequestInfo block.
[email protected]955fc2e72010-02-08 20:37:30134void CreateSpdyHeadersFromHttpRequest(
135 const HttpRequestInfo& info, spdy::SpdyHeaderBlock* headers) {
[email protected]8c76ae22010-04-20 22:15:43136 // TODO(willchan): It's not really necessary to convert from
137 // HttpRequestHeaders to spdy::SpdyHeaderBlock.
138
[email protected]a677f2b2009-11-22 00:43:00139 static const char kHttpProtocolVersion[] = "HTTP/1.1";
140
[email protected]8c76ae22010-04-20 22:15:43141 HttpRequestHeaders::Iterator it(info.extra_headers);
142
[email protected]a677f2b2009-11-22 00:43:00143 while (it.GetNext()) {
144 std::string name = StringToLowerASCII(it.name());
145 if (headers->find(name) == headers->end()) {
[email protected]8c76ae22010-04-20 22:15:43146 (*headers)[name] = it.value();
[email protected]a677f2b2009-11-22 00:43:00147 } else {
148 std::string new_value = (*headers)[name];
[email protected]3f662f12010-03-25 19:56:12149 new_value.append(1, '\0'); // +=() doesn't append 0's
[email protected]8c76ae22010-04-20 22:15:43150 new_value += it.value();
[email protected]a677f2b2009-11-22 00:43:00151 (*headers)[name] = new_value;
152 }
153 }
154
[email protected]6371bf42009-12-04 05:13:12155 // TODO(mbelshe): Add Proxy headers here. (See http_network_transaction.cc)
156 // TODO(mbelshe): Add authentication headers here.
157
[email protected]a677f2b2009-11-22 00:43:00158 (*headers)["method"] = info.method;
159 (*headers)["url"] = info.url.spec();
160 (*headers)["version"] = kHttpProtocolVersion;
[email protected]a677f2b2009-11-22 00:43:00161 if (!info.referrer.is_empty())
162 (*headers)["referer"] = info.referrer.spec();
163
164 // Honor load flags that impact proxy caches.
165 if (info.load_flags & LOAD_BYPASS_CACHE) {
166 (*headers)["pragma"] = "no-cache";
167 (*headers)["cache-control"] = "no-cache";
168 } else if (info.load_flags & LOAD_VALIDATE_CACHE) {
169 (*headers)["cache-control"] = "max-age=0";
170 }
171}
172
[email protected]1f14a912009-12-21 20:32:44173void AdjustSocketBufferSizes(ClientSocket* socket) {
174 // Adjust socket buffer sizes.
[email protected]955fc2e72010-02-08 20:37:30175 // SPDY uses one socket, and we want a really big buffer.
[email protected]1f14a912009-12-21 20:32:44176 // This greatly helps on links with packet loss - we can even
177 // outperform Vista's dynamic window sizing algorithm.
178 // TODO(mbelshe): more study.
179 const int kSocketBufferSize = 512 * 1024;
180 socket->SetReceiveBufferSize(kSocketBufferSize);
181 socket->SetSendBufferSize(kSocketBufferSize);
182}
183
[email protected]a677f2b2009-11-22 00:43:00184} // namespace
185
[email protected]aea80602009-09-18 00:55:08186// static
[email protected]955fc2e72010-02-08 20:37:30187bool SpdySession::use_ssl_ = true;
[email protected]aea80602009-09-18 00:55:08188
[email protected]367ead42010-02-26 00:15:21189SpdySession::SpdySession(const HostPortPair& host_port_pair,
190 HttpNetworkSession* session)
[email protected]aea80602009-09-18 00:55:08191 : ALLOW_THIS_IN_INITIALIZER_LIST(
[email protected]955fc2e72010-02-08 20:37:30192 connect_callback_(this, &SpdySession::OnTCPConnect)),
[email protected]18c53ae2009-10-01 18:18:52193 ALLOW_THIS_IN_INITIALIZER_LIST(
[email protected]955fc2e72010-02-08 20:37:30194 ssl_connect_callback_(this, &SpdySession::OnSSLConnect)),
[email protected]aea80602009-09-18 00:55:08195 ALLOW_THIS_IN_INITIALIZER_LIST(
[email protected]955fc2e72010-02-08 20:37:30196 read_callback_(this, &SpdySession::OnReadComplete)),
[email protected]aea80602009-09-18 00:55:08197 ALLOW_THIS_IN_INITIALIZER_LIST(
[email protected]955fc2e72010-02-08 20:37:30198 write_callback_(this, &SpdySession::OnWriteComplete)),
[email protected]367ead42010-02-26 00:15:21199 host_port_pair_(host_port_pair),
[email protected]aea80602009-09-18 00:55:08200 session_(session),
[email protected]1f14a912009-12-21 20:32:44201 connection_(new ClientSocketHandle),
[email protected]230cadd2009-09-22 16:33:59202 read_buffer_(new IOBuffer(kReadBufferSize)),
[email protected]aea80602009-09-18 00:55:08203 read_pending_(false),
[email protected]ba051e312009-10-07 22:12:33204 stream_hi_water_mark_(1), // Always start at 1 for the first stream id.
[email protected]dd11b932009-11-30 19:39:48205 write_pending_(false),
[email protected]60253bd2009-12-01 01:16:39206 delayed_write_pending_(false),
[email protected]dcc6bbb2009-12-09 19:09:01207 is_secure_(false),
[email protected]60253bd2009-12-01 01:16:39208 error_(OK),
[email protected]9b010802009-12-27 22:55:30209 state_(IDLE),
210 streams_initiated_count_(0),
211 streams_pushed_count_(0),
212 streams_pushed_and_claimed_count_(0),
[email protected]4b4762a2010-04-23 16:04:14213 streams_abandoned_count_(0),
214 in_session_pool_(true) {
[email protected]ba051e312009-10-07 22:12:33215 // TODO(mbelshe): consider randomization of the stream_hi_water_mark.
[email protected]aea80602009-09-18 00:55:08216
[email protected]955fc2e72010-02-08 20:37:30217 spdy_framer_.set_visitor(this);
[email protected]18c53ae2009-10-01 18:18:52218
219 session_->ssl_config_service()->GetSSLConfig(&ssl_config_);
[email protected]74188f22010-04-09 20:18:50220
221 SendSettings();
[email protected]aea80602009-09-18 00:55:08222}
223
[email protected]955fc2e72010-02-08 20:37:30224SpdySession::~SpdySession() {
[email protected]93300672009-10-24 13:22:51225 // Cleanup all the streams.
226 CloseAllStreams(net::ERR_ABORTED);
227
[email protected]1f14a912009-12-21 20:32:44228 if (connection_->is_initialized()) {
[email protected]955fc2e72010-02-08 20:37:30229 // With Spdy we can't recycle sockets.
[email protected]1f14a912009-12-21 20:32:44230 connection_->socket()->Disconnect();
[email protected]aea80602009-09-18 00:55:08231 }
[email protected]d1eda932009-11-04 01:03:10232
[email protected]9b010802009-12-27 22:55:30233 // Record per-session histograms here.
234 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPerSession",
235 streams_initiated_count_,
236 0, 300, 50);
237 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedPerSession",
238 streams_pushed_count_,
239 0, 300, 50);
240 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsPushedAndClaimedPerSession",
241 streams_pushed_and_claimed_count_,
242 0, 300, 50);
243 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyStreamsAbandonedPerSession",
244 streams_abandoned_count_,
245 0, 300, 50);
[email protected]aea80602009-09-18 00:55:08246}
247
[email protected]5fe524e2010-02-20 00:43:22248void SpdySession::InitializeWithSSLSocket(ClientSocketHandle* connection) {
[email protected]955fc2e72010-02-08 20:37:30249 static StatsCounter spdy_sessions("spdy.sessions");
250 spdy_sessions.Increment();
[email protected]1f14a912009-12-21 20:32:44251
252 AdjustSocketBufferSizes(connection->socket());
253
254 state_ = CONNECTED;
255 connection_.reset(connection);
[email protected]5fe524e2010-02-20 00:43:22256 is_secure_ = true; // |connection| contains an SSLClientSocket.
[email protected]1f14a912009-12-21 20:32:44257
258 // This is a newly initialized session that no client should have a handle to
259 // yet, so there's no need to start writing data as in OnTCPConnect(), but we
260 // should start reading data.
261 ReadSocket();
262}
263
[email protected]955fc2e72010-02-08 20:37:30264net::Error SpdySession::Connect(const std::string& group_name,
[email protected]7fc5b09a2010-02-27 00:07:38265 const TCPSocketParams& destination,
[email protected]eec34d7d2009-12-07 22:09:23266 RequestPriority priority,
[email protected]9e743cd2010-03-16 07:03:53267 const BoundNetLog& net_log) {
[email protected]955fc2e72010-02-08 20:37:30268 DCHECK(priority >= SPDY_PRIORITY_HIGHEST && priority <= SPDY_PRIORITY_LOWEST);
[email protected]aea80602009-09-18 00:55:08269
270 // If the connect process is started, let the caller continue.
[email protected]60253bd2009-12-01 01:16:39271 if (state_ > IDLE)
[email protected]aea80602009-09-18 00:55:08272 return net::OK;
273
[email protected]60253bd2009-12-01 01:16:39274 state_ = CONNECTING;
[email protected]aea80602009-09-18 00:55:08275
[email protected]955fc2e72010-02-08 20:37:30276 static StatsCounter spdy_sessions("spdy.sessions");
277 spdy_sessions.Increment();
[email protected]aea80602009-09-18 00:55:08278
[email protected]7fc5b09a2010-02-27 00:07:38279 int rv = connection_->Init(group_name, destination, priority,
280 &connect_callback_, session_->tcp_socket_pool(),
[email protected]9e743cd2010-03-16 07:03:53281 net_log);
[email protected]843c5072009-12-04 22:09:47282 DCHECK(rv <= 0);
[email protected]aea80602009-09-18 00:55:08283
284 // If the connect is pending, we still return ok. The APIs enqueue
285 // work until after the connect completes asynchronously later.
286 if (rv == net::ERR_IO_PENDING)
287 return net::OK;
[email protected]651b77c2010-03-10 19:29:42288 OnTCPConnect(rv);
[email protected]aea80602009-09-18 00:55:08289 return static_cast<net::Error>(rv);
290}
291
[email protected]955fc2e72010-02-08 20:37:30292scoped_refptr<SpdyStream> SpdySession::GetOrCreateStream(
[email protected]a677f2b2009-11-22 00:43:00293 const HttpRequestInfo& request,
[email protected]dac358042009-12-18 02:07:48294 const UploadDataStream* upload_data,
[email protected]9e743cd2010-03-16 07:03:53295 const BoundNetLog& log) {
[email protected]a677f2b2009-11-22 00:43:00296 const GURL& url = request.url;
297 const std::string& path = url.PathForRequest();
[email protected]aea80602009-09-18 00:55:08298
[email protected]955fc2e72010-02-08 20:37:30299 scoped_refptr<SpdyStream> stream;
[email protected]aea80602009-09-18 00:55:08300
301 // Check if we have a push stream for this path.
[email protected]a677f2b2009-11-22 00:43:00302 if (request.method == "GET") {
[email protected]aea80602009-09-18 00:55:08303 stream = GetPushStream(path);
[email protected]9b010802009-12-27 22:55:30304 if (stream) {
305 DCHECK(streams_pushed_and_claimed_count_ < streams_pushed_count_);
[email protected]3f662f12010-03-25 19:56:12306 // Update the request time
307 stream->SetRequestTime(base::Time::Now());
308 // Change the request info, updating the response's request time too
309 stream->SetRequestInfo(request);
310 const HttpResponseInfo* response = stream->GetResponseInfo();
311 if (response && response->headers->HasHeader("vary")) {
312 // TODO(ahendrickson) -- What is the right thing to do if the server
313 // pushes data with a vary field?
314 void* iter = NULL;
315 std::string value;
316 response->headers->EnumerateHeader(&iter, "vary", &value);
317 LOG(ERROR) << "SpdyStream: "
318 << "Received pushed stream ID " << stream->stream_id()
319 << "with vary field value '" << value << "'";
320 }
[email protected]9b010802009-12-27 22:55:30321 streams_pushed_and_claimed_count_++;
[email protected]a677f2b2009-11-22 00:43:00322 return stream;
[email protected]9b010802009-12-27 22:55:30323 }
[email protected]aea80602009-09-18 00:55:08324 }
325
[email protected]18c53ae2009-10-01 18:18:52326 // Check if we have a pending push stream for this url.
[email protected]2931238b2009-11-19 01:19:51327 PendingStreamMap::iterator it;
[email protected]a677f2b2009-11-22 00:43:00328 it = pending_streams_.find(path);
[email protected]18c53ae2009-10-01 18:18:52329 if (it != pending_streams_.end()) {
[email protected]3f662f12010-03-25 19:56:12330 // Server has advertised a stream, but not yet sent it.
[email protected]a677f2b2009-11-22 00:43:00331 DCHECK(!it->second);
332 // Server will assign a stream id when the push stream arrives. Use 0 for
333 // now.
[email protected]9e743cd2010-03-16 07:03:53334 log.AddEvent(NetLog::TYPE_SPDY_STREAM_ADOPTED_PUSH_STREAM);
[email protected]955fc2e72010-02-08 20:37:30335 SpdyStream* stream = new SpdyStream(this, 0, true, log);
[email protected]3f662f12010-03-25 19:56:12336 stream->SetRequestInfo(request);
[email protected]a677f2b2009-11-22 00:43:00337 stream->set_path(path);
338 it->second = stream;
339 return it->second;
[email protected]18c53ae2009-10-01 18:18:52340 }
341
[email protected]955fc2e72010-02-08 20:37:30342 const spdy::SpdyStreamId stream_id = GetNewStreamId();
[email protected]aea80602009-09-18 00:55:08343
[email protected]a677f2b2009-11-22 00:43:00344 // If we still don't have a stream, activate one now.
[email protected]955fc2e72010-02-08 20:37:30345 stream = new SpdyStream(this, stream_id, false, log);
[email protected]3f662f12010-03-25 19:56:12346 stream->SetRequestInfo(request);
[email protected]a677f2b2009-11-22 00:43:00347 stream->set_priority(request.priority);
348 stream->set_path(path);
349 ActivateStream(stream);
350
[email protected]9b010802009-12-27 22:55:30351 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.SpdyPriorityCount",
352 static_cast<int>(request.priority), 0, 10, 11);
353
[email protected]955fc2e72010-02-08 20:37:30354 LOG(INFO) << "SpdyStream: Creating stream " << stream_id << " for " << url;
[email protected]aea80602009-09-18 00:55:08355
356 // TODO(mbelshe): Optimize memory allocations
[email protected]955fc2e72010-02-08 20:37:30357 DCHECK(request.priority >= SPDY_PRIORITY_HIGHEST &&
358 request.priority <= SPDY_PRIORITY_LOWEST);
[email protected]aea80602009-09-18 00:55:08359
[email protected]955fc2e72010-02-08 20:37:30360 // Convert from HttpRequestHeaders to Spdy Headers.
361 spdy::SpdyHeaderBlock headers;
362 CreateSpdyHeadersFromHttpRequest(request, &headers);
[email protected]aea80602009-09-18 00:55:08363
[email protected]955fc2e72010-02-08 20:37:30364 spdy::SpdyControlFlags flags = spdy::CONTROL_FLAG_NONE;
[email protected]a677f2b2009-11-22 00:43:00365 if (!request.upload_data || !upload_data->size())
[email protected]955fc2e72010-02-08 20:37:30366 flags = spdy::CONTROL_FLAG_FIN;
[email protected]72552f02009-10-28 15:25:01367
[email protected]aea80602009-09-18 00:55:08368 // Create a SYN_STREAM packet and add to the output queue.
[email protected]955fc2e72010-02-08 20:37:30369 scoped_ptr<spdy::SpdySynStreamControlFrame> syn_frame(
[email protected]0558bdf2010-02-10 19:24:29370 spdy_framer_.CreateSynStream(stream_id, 0, request.priority, flags, false,
[email protected]22eb9682009-11-06 21:51:58371 &headers));
[email protected]74188f22010-04-09 20:18:50372 QueueFrame(syn_frame.get(), request.priority, stream);
[email protected]aea80602009-09-18 00:55:08373
[email protected]955fc2e72010-02-08 20:37:30374 static StatsCounter spdy_requests("spdy.requests");
375 spdy_requests.Increment();
[email protected]aea80602009-09-18 00:55:08376
[email protected]a677f2b2009-11-22 00:43:00377 LOG(INFO) << "FETCHING: " << request.url.spec();
[email protected]9b010802009-12-27 22:55:30378 streams_initiated_count_++;
[email protected]aea80602009-09-18 00:55:08379
[email protected]955fc2e72010-02-08 20:37:30380 LOG(INFO) << "SPDY SYN_STREAM HEADERS ----------------------------------";
381 DumpSpdyHeaders(headers);
[email protected]aea80602009-09-18 00:55:08382
[email protected]a677f2b2009-11-22 00:43:00383 return stream;
[email protected]aea80602009-09-18 00:55:08384}
385
[email protected]955fc2e72010-02-08 20:37:30386int SpdySession::WriteStreamData(spdy::SpdyStreamId stream_id,
[email protected]ff57bb82009-11-12 06:52:14387 net::IOBuffer* data, int len) {
[email protected]967dd5412009-11-21 23:33:30388 LOG(INFO) << "Writing Stream Data for stream " << stream_id << " (" << len
389 << " bytes)";
[email protected]ff57bb82009-11-12 06:52:14390 const int kMss = 1430; // This is somewhat arbitrary and not really fixed,
391 // but it will always work reasonably with ethernet.
392 // Chop the world into 2-packet chunks. This is somewhat arbitrary, but
393 // is reasonably small and ensures that we elicit ACKs quickly from TCP
394 // (because TCP tries to only ACK every other packet).
[email protected]955fc2e72010-02-08 20:37:30395 const int kMaxSpdyFrameChunkSize = (2 * kMss) - spdy::SpdyFrame::size();
[email protected]ff57bb82009-11-12 06:52:14396
397 // Find our stream
398 DCHECK(IsStreamActive(stream_id));
[email protected]955fc2e72010-02-08 20:37:30399 scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
[email protected]b1f031dd2010-03-02 23:19:33400 CHECK_EQ(stream->stream_id(), stream_id);
[email protected]ff57bb82009-11-12 06:52:14401 if (!stream)
[email protected]955fc2e72010-02-08 20:37:30402 return ERR_INVALID_SPDY_STREAM;
[email protected]ff57bb82009-11-12 06:52:14403
[email protected]2931238b2009-11-19 01:19:51404 // TODO(mbelshe): Setting of the FIN is assuming that the caller will pass
405 // all data to write in a single chunk. Is this always true?
406
[email protected]ff57bb82009-11-12 06:52:14407 // Set the flags on the upload.
[email protected]955fc2e72010-02-08 20:37:30408 spdy::SpdyDataFlags flags = spdy::DATA_FLAG_FIN;
409 if (len > kMaxSpdyFrameChunkSize) {
410 len = kMaxSpdyFrameChunkSize;
411 flags = spdy::DATA_FLAG_NONE;
[email protected]ff57bb82009-11-12 06:52:14412 }
413
414 // TODO(mbelshe): reduce memory copies here.
[email protected]955fc2e72010-02-08 20:37:30415 scoped_ptr<spdy::SpdyDataFrame> frame(
416 spdy_framer_.CreateDataFrame(stream_id, data->data(), len, flags));
[email protected]74188f22010-04-09 20:18:50417 QueueFrame(frame.get(), stream->priority(), stream);
[email protected]ff57bb82009-11-12 06:52:14418 return ERR_IO_PENDING;
419}
420
[email protected]955fc2e72010-02-08 20:37:30421bool SpdySession::CancelStream(spdy::SpdyStreamId stream_id) {
[email protected]ff57bb82009-11-12 06:52:14422 LOG(INFO) << "Cancelling stream " << stream_id;
423 if (!IsStreamActive(stream_id))
[email protected]aea80602009-09-18 00:55:08424 return false;
[email protected]93300672009-10-24 13:22:51425
[email protected]0558bdf2010-02-10 19:24:29426 // TODO(mbelshe): We should send a RST_STREAM control frame here
[email protected]92683b52009-12-21 21:01:12427 // so that the server can cancel a large send.
428
[email protected]93300672009-10-24 13:22:51429 // TODO(mbelshe): Write a method for tearing down a stream
430 // that cleans it out of the active list, the pending list,
431 // etc.
[email protected]955fc2e72010-02-08 20:37:30432 scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
[email protected]ff57bb82009-11-12 06:52:14433 DeactivateStream(stream_id);
[email protected]aea80602009-09-18 00:55:08434 return true;
435}
436
[email protected]955fc2e72010-02-08 20:37:30437bool SpdySession::IsStreamActive(spdy::SpdyStreamId stream_id) const {
[email protected]a677f2b2009-11-22 00:43:00438 return ContainsKey(active_streams_, stream_id);
[email protected]aea80602009-09-18 00:55:08439}
440
[email protected]955fc2e72010-02-08 20:37:30441LoadState SpdySession::GetLoadState() const {
[email protected]9be4f02c2010-01-07 18:30:09442 // NOTE: The application only queries the LoadState via the
[email protected]955fc2e72010-02-08 20:37:30443 // SpdyNetworkTransaction, and details are only needed when
[email protected]9be4f02c2010-01-07 18:30:09444 // we're in the process of connecting.
445
446 // If we're connecting, defer to the connection to give us the actual
447 // LoadState.
448 if (state_ == CONNECTING)
449 return connection_->GetLoadState();
450
451 // Just report that we're idle since the session could be doing
452 // many things concurrently.
453 return LOAD_STATE_IDLE;
[email protected]aea80602009-09-18 00:55:08454}
455
[email protected]955fc2e72010-02-08 20:37:30456void SpdySession::OnTCPConnect(int result) {
457 LOG(INFO) << "Spdy socket connected (result=" << result << ")";
[email protected]aea80602009-09-18 00:55:08458
[email protected]5e2e6c77d12009-12-24 21:57:16459 // We shouldn't be coming through this path if we didn't just open a fresh
460 // socket (or have an error trying to do so).
461 DCHECK(!connection_->socket() || !connection_->is_reused());
462
[email protected]aea80602009-09-18 00:55:08463 if (result != net::OK) {
[email protected]353f6162009-12-10 18:04:12464 DCHECK_LT(result, 0);
465 CloseSessionOnError(static_cast<net::Error>(result));
[email protected]aea80602009-09-18 00:55:08466 return;
[email protected]616925a2010-03-02 19:02:38467 } else {
468 UpdateConnectionTypeHistograms(CONNECTION_SPDY);
[email protected]aea80602009-09-18 00:55:08469 }
470
[email protected]1f14a912009-12-21 20:32:44471 AdjustSocketBufferSizes(connection_->socket());
[email protected]aea80602009-09-18 00:55:08472
[email protected]affe8fe2009-10-14 20:06:05473 if (use_ssl_) {
[email protected]18c53ae2009-10-01 18:18:52474 // Add a SSL socket on top of our existing transport socket.
[email protected]1f14a912009-12-21 20:32:44475 ClientSocket* socket = connection_->release_socket();
[email protected]18c53ae2009-10-01 18:18:52476 // TODO(mbelshe): Fix the hostname. This is BROKEN without having
477 // a real hostname.
478 socket = session_->socket_factory()->CreateSSLClientSocket(
479 socket, "" /* request_->url.HostNoBrackets() */ , ssl_config_);
[email protected]1f14a912009-12-21 20:32:44480 connection_->set_socket(socket);
[email protected]dcc6bbb2009-12-09 19:09:01481 is_secure_ = true;
[email protected]9e743cd2010-03-16 07:03:53482 // TODO(willchan): Plumb NetLog into SPDY code.
[email protected]a2006ece2010-04-23 16:44:02483 int status = connection_->socket()->Connect(&ssl_connect_callback_);
[email protected]843c5072009-12-04 22:09:47484 if (status != ERR_IO_PENDING)
485 OnSSLConnect(status);
[email protected]18c53ae2009-10-01 18:18:52486 } else {
[email protected]60253bd2009-12-01 01:16:39487 DCHECK_EQ(state_, CONNECTING);
488 state_ = CONNECTED;
[email protected]18c53ae2009-10-01 18:18:52489
490 // Make sure we get any pending data sent.
491 WriteSocketLater();
492 // Start reading
493 ReadSocket();
494 }
495}
496
[email protected]955fc2e72010-02-08 20:37:30497void SpdySession::OnSSLConnect(int result) {
[email protected]18c53ae2009-10-01 18:18:52498 // TODO(mbelshe): We need to replicate the functionality of
499 // HttpNetworkTransaction::DoSSLConnectComplete here, where it calls
500 // HandleCertificateError() and such.
501 if (IsCertificateError(result))
502 result = OK; // TODO(mbelshe): pretend we're happy anyway.
503
[email protected]93300672009-10-24 13:22:51504 if (result == OK) {
[email protected]60253bd2009-12-01 01:16:39505 DCHECK_EQ(state_, CONNECTING);
506 state_ = CONNECTED;
[email protected]18c53ae2009-10-01 18:18:52507
[email protected]93300672009-10-24 13:22:51508 // After we've connected, send any data to the server, and then issue
509 // our read.
510 WriteSocketLater();
511 ReadSocket();
512 } else {
[email protected]353f6162009-12-10 18:04:12513 DCHECK_LT(result, 0); // It should be an error, not a byte count.
514 CloseSessionOnError(static_cast<net::Error>(result));
[email protected]93300672009-10-24 13:22:51515 }
[email protected]aea80602009-09-18 00:55:08516}
517
[email protected]955fc2e72010-02-08 20:37:30518void SpdySession::OnReadComplete(int bytes_read) {
[email protected]aea80602009-09-18 00:55:08519 // Parse a frame. For now this code requires that the frame fit into our
520 // buffer (32KB).
521 // TODO(mbelshe): support arbitrarily large frames!
522
[email protected]955fc2e72010-02-08 20:37:30523 LOG(INFO) << "Spdy socket read: " << bytes_read << " bytes";
[email protected]aea80602009-09-18 00:55:08524
525 read_pending_ = false;
526
527 if (bytes_read <= 0) {
528 // Session is tearing down.
[email protected]353f6162009-12-10 18:04:12529 net::Error error = static_cast<net::Error>(bytes_read);
[email protected]bce9f682010-03-30 16:36:02530 if (bytes_read == 0) {
531 LOG(INFO) << "Spdy socket closed by server[" <<
532 host_port_pair().ToString() << "].";
[email protected]353f6162009-12-10 18:04:12533 error = ERR_CONNECTION_CLOSED;
[email protected]bce9f682010-03-30 16:36:02534 }
[email protected]353f6162009-12-10 18:04:12535 CloseSessionOnError(error);
[email protected]aea80602009-09-18 00:55:08536 return;
537 }
538
[email protected]955fc2e72010-02-08 20:37:30539 // The SpdyFramer will use callbacks onto |this| as it parses frames.
[email protected]94d78132010-01-22 00:53:00540 // When errors occur, those callbacks can lead to teardown of all references
541 // to |this|, so maintain a reference to self during this call for safe
542 // cleanup.
[email protected]955fc2e72010-02-08 20:37:30543 scoped_refptr<SpdySession> self(this);
[email protected]94d78132010-01-22 00:53:00544
[email protected]aea80602009-09-18 00:55:08545 char *data = read_buffer_->data();
546 while (bytes_read &&
[email protected]955fc2e72010-02-08 20:37:30547 spdy_framer_.error_code() == spdy::SpdyFramer::SPDY_NO_ERROR) {
548 uint32 bytes_processed = spdy_framer_.ProcessInput(data, bytes_read);
[email protected]aea80602009-09-18 00:55:08549 bytes_read -= bytes_processed;
550 data += bytes_processed;
[email protected]955fc2e72010-02-08 20:37:30551 if (spdy_framer_.state() == spdy::SpdyFramer::SPDY_DONE)
552 spdy_framer_.Reset();
[email protected]aea80602009-09-18 00:55:08553 }
[email protected]57f123222010-01-19 22:00:28554
[email protected]94d78132010-01-22 00:53:00555 if (state_ != CLOSED)
556 ReadSocket();
[email protected]aea80602009-09-18 00:55:08557}
558
[email protected]955fc2e72010-02-08 20:37:30559void SpdySession::OnWriteComplete(int result) {
[email protected]aea80602009-09-18 00:55:08560 DCHECK(write_pending_);
[email protected]bf2491a92009-11-29 16:39:48561 DCHECK(in_flight_write_.size());
[email protected]8549f5e2010-04-16 22:39:09562 DCHECK_NE(result, 0); // This shouldn't happen for write.
[email protected]bf2491a92009-11-29 16:39:48563
[email protected]aea80602009-09-18 00:55:08564 write_pending_ = false;
565
[email protected]74188f22010-04-09 20:18:50566 scoped_refptr<SpdyStream> stream = in_flight_write_.stream();
567
568 LOG(INFO) << "Spdy write complete (result=" << result << ")"
[email protected]8549f5e2010-04-16 22:39:09569 << (stream ? std::string(" for stream ") +
570 IntToString(stream->stream_id()) : "");
[email protected]aea80602009-09-18 00:55:08571
[email protected]93300672009-10-24 13:22:51572 if (result >= 0) {
[email protected]bf2491a92009-11-29 16:39:48573 // It should not be possible to have written more bytes than our
574 // in_flight_write_.
575 DCHECK_LE(result, in_flight_write_.buffer()->BytesRemaining());
[email protected]eebc0b42009-11-04 00:17:13576
[email protected]bf2491a92009-11-29 16:39:48577 in_flight_write_.buffer()->DidConsume(result);
[email protected]ff57bb82009-11-12 06:52:14578
[email protected]bf2491a92009-11-29 16:39:48579 // We only notify the stream when we've fully written the pending frame.
580 if (!in_flight_write_.buffer()->BytesRemaining()) {
[email protected]955fc2e72010-02-08 20:37:30581 scoped_refptr<SpdyStream> stream = in_flight_write_.stream();
[email protected]74188f22010-04-09 20:18:50582 if (stream) {
583 // Report the number of bytes written to the caller, but exclude the
584 // frame size overhead. NOTE: if this frame was compressed the
585 // reported bytes written is the compressed size, not the original
586 // size.
587 if (result > 0) {
588 result = in_flight_write_.buffer()->size();
589 DCHECK_GT(result, static_cast<int>(spdy::SpdyFrame::size()));
590 result -= static_cast<int>(spdy::SpdyFrame::size());
591 }
[email protected]bf2491a92009-11-29 16:39:48592
[email protected]74188f22010-04-09 20:18:50593 // It is possible that the stream was cancelled while we were writing
594 // to the socket.
595 if (!stream->cancelled())
596 stream->OnWriteComplete(result);
[email protected]bf2491a92009-11-29 16:39:48597 }
[email protected]92683b52009-12-21 21:01:12598
[email protected]bf2491a92009-11-29 16:39:48599 // Cleanup the write which just completed.
600 in_flight_write_.release();
[email protected]ff57bb82009-11-12 06:52:14601 }
[email protected]aea80602009-09-18 00:55:08602
[email protected]93300672009-10-24 13:22:51603 // Write more data. We're already in a continuation, so we can
604 // go ahead and write it immediately (without going back to the
605 // message loop).
606 WriteSocketLater();
607 } else {
[email protected]6572f122009-11-30 18:23:13608 in_flight_write_.release();
[email protected]dd11b932009-11-30 19:39:48609
[email protected]bf2491a92009-11-29 16:39:48610 // The stream is now errored. Close it down.
[email protected]353f6162009-12-10 18:04:12611 CloseSessionOnError(static_cast<net::Error>(result));
[email protected]93300672009-10-24 13:22:51612 }
[email protected]aea80602009-09-18 00:55:08613}
614
[email protected]955fc2e72010-02-08 20:37:30615void SpdySession::ReadSocket() {
[email protected]aea80602009-09-18 00:55:08616 if (read_pending_)
617 return;
618
[email protected]94d78132010-01-22 00:53:00619 if (state_ == CLOSED) {
620 NOTREACHED();
621 return;
622 }
623
624 CHECK(connection_.get());
625 CHECK(connection_->socket());
[email protected]1f14a912009-12-21 20:32:44626 int bytes_read = connection_->socket()->Read(read_buffer_.get(),
[email protected]94d78132010-01-22 00:53:00627 kReadBufferSize,
628 &read_callback_);
[email protected]aea80602009-09-18 00:55:08629 switch (bytes_read) {
630 case 0:
631 // Socket is closed!
632 // TODO(mbelshe): Need to abort any active streams here.
633 DCHECK(!active_streams_.size());
634 return;
635 case net::ERR_IO_PENDING:
636 // Waiting for data. Nothing to do now.
637 read_pending_ = true;
638 return;
639 default:
640 // Data was read, process it.
[email protected]57f123222010-01-19 22:00:28641 // Schedule the work through the message loop to avoid recursive
642 // callbacks.
643 read_pending_ = true;
644 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]955fc2e72010-02-08 20:37:30645 this, &SpdySession::OnReadComplete, bytes_read));
[email protected]aea80602009-09-18 00:55:08646 break;
647 }
648}
649
[email protected]955fc2e72010-02-08 20:37:30650void SpdySession::WriteSocketLater() {
[email protected]aea80602009-09-18 00:55:08651 if (delayed_write_pending_)
652 return;
653
[email protected]74188f22010-04-09 20:18:50654 if (state_ < CONNECTED)
655 return;
656
[email protected]aea80602009-09-18 00:55:08657 delayed_write_pending_ = true;
658 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]955fc2e72010-02-08 20:37:30659 this, &SpdySession::WriteSocket));
[email protected]aea80602009-09-18 00:55:08660}
661
[email protected]955fc2e72010-02-08 20:37:30662void SpdySession::WriteSocket() {
[email protected]aea80602009-09-18 00:55:08663 // This function should only be called via WriteSocketLater.
664 DCHECK(delayed_write_pending_);
665 delayed_write_pending_ = false;
666
667 // If the socket isn't connected yet, just wait; we'll get called
[email protected]6371bf42009-12-04 05:13:12668 // again when the socket connection completes. If the socket is
669 // closed, just return.
670 if (state_ < CONNECTED || state_ == CLOSED)
[email protected]aea80602009-09-18 00:55:08671 return;
672
673 if (write_pending_) // Another write is in progress still.
674 return;
675
[email protected]eebc0b42009-11-04 00:17:13676 // Loop sending frames until we've sent everything or until the write
677 // returns error (or ERR_IO_PENDING).
[email protected]8549f5e2010-04-16 22:39:09678 while (in_flight_write_.buffer() || !queue_.empty()) {
[email protected]bf2491a92009-11-29 16:39:48679 if (!in_flight_write_.buffer()) {
[email protected]955fc2e72010-02-08 20:37:30680 // Grab the next SpdyFrame to send.
681 SpdyIOBuffer next_buffer = queue_.top();
[email protected]bf2491a92009-11-29 16:39:48682 queue_.pop();
[email protected]aea80602009-09-18 00:55:08683
[email protected]bf2491a92009-11-29 16:39:48684 // We've deferred compression until just before we write it to the socket,
685 // which is now. At this time, we don't compress our data frames.
[email protected]955fc2e72010-02-08 20:37:30686 spdy::SpdyFrame uncompressed_frame(next_buffer.buffer()->data(), false);
[email protected]bf2491a92009-11-29 16:39:48687 size_t size;
[email protected]68b74502010-04-16 21:04:55688 if (spdy_framer_.IsCompressible(&uncompressed_frame)) {
[email protected]955fc2e72010-02-08 20:37:30689 scoped_ptr<spdy::SpdyFrame> compressed_frame(
690 spdy_framer_.CompressFrame(&uncompressed_frame));
[email protected]68b74502010-04-16 21:04:55691 if (!compressed_frame.get()) {
692 LOG(ERROR) << "SPDY Compression failure";
693 CloseSessionOnError(net::ERR_SPDY_PROTOCOL_ERROR);
694 return;
695 }
696
[email protected]955fc2e72010-02-08 20:37:30697 size = compressed_frame->length() + spdy::SpdyFrame::size();
[email protected]aea80602009-09-18 00:55:08698
[email protected]8549f5e2010-04-16 22:39:09699 DCHECK_GT(size, 0u);
[email protected]eebc0b42009-11-04 00:17:13700
[email protected]bf2491a92009-11-29 16:39:48701 // TODO(mbelshe): We have too much copying of data here.
702 IOBufferWithSize* buffer = new IOBufferWithSize(size);
703 memcpy(buffer->data(), compressed_frame->data(), size);
[email protected]eebc0b42009-11-04 00:17:13704
[email protected]bf2491a92009-11-29 16:39:48705 // Attempt to send the frame.
[email protected]955fc2e72010-02-08 20:37:30706 in_flight_write_ = SpdyIOBuffer(buffer, size, 0, next_buffer.stream());
[email protected]bf2491a92009-11-29 16:39:48707 } else {
[email protected]955fc2e72010-02-08 20:37:30708 size = uncompressed_frame.length() + spdy::SpdyFrame::size();
[email protected]bf2491a92009-11-29 16:39:48709 in_flight_write_ = next_buffer;
710 }
[email protected]2931238b2009-11-19 01:19:51711 } else {
[email protected]bf2491a92009-11-29 16:39:48712 DCHECK(in_flight_write_.buffer()->BytesRemaining());
[email protected]2931238b2009-11-19 01:19:51713 }
714
[email protected]93300672009-10-24 13:22:51715 write_pending_ = true;
[email protected]1f14a912009-12-21 20:32:44716 int rv = connection_->socket()->Write(in_flight_write_.buffer(),
[email protected]bf2491a92009-11-29 16:39:48717 in_flight_write_.buffer()->BytesRemaining(), &write_callback_);
[email protected]93300672009-10-24 13:22:51718 if (rv == net::ERR_IO_PENDING)
[email protected]aea80602009-09-18 00:55:08719 break;
[email protected]eebc0b42009-11-04 00:17:13720
721 // We sent the frame successfully.
[email protected]93300672009-10-24 13:22:51722 OnWriteComplete(rv);
[email protected]eebc0b42009-11-04 00:17:13723
724 // TODO(mbelshe): Test this error case. Maybe we should mark the socket
725 // as in an error state.
726 if (rv < 0)
727 break;
[email protected]aea80602009-09-18 00:55:08728 }
729}
730
[email protected]955fc2e72010-02-08 20:37:30731void SpdySession::CloseAllStreams(net::Error code) {
[email protected]bce9f682010-03-30 16:36:02732 LOG(INFO) << "Closing all SPDY Streams for " << host_port_pair().ToString();
[email protected]aea80602009-09-18 00:55:08733
[email protected]955fc2e72010-02-08 20:37:30734 static StatsCounter abandoned_streams("spdy.abandoned_streams");
735 static StatsCounter abandoned_push_streams("spdy.abandoned_push_streams");
[email protected]aea80602009-09-18 00:55:08736
737 if (active_streams_.size()) {
738 abandoned_streams.Add(active_streams_.size());
739
740 // Create a copy of the list, since aborting streams can invalidate
741 // our list.
[email protected]955fc2e72010-02-08 20:37:30742 SpdyStream** list = new SpdyStream*[active_streams_.size()];
[email protected]aea80602009-09-18 00:55:08743 ActiveStreamMap::const_iterator it;
744 int index = 0;
745 for (it = active_streams_.begin(); it != active_streams_.end(); ++it)
746 list[index++] = it->second;
747
748 // Issue the aborts.
749 for (--index; index >= 0; index--) {
[email protected]7f78b592009-11-06 17:46:56750 LOG(ERROR) << "ABANDONED (stream_id=" << list[index]->stream_id()
751 << "): " << list[index]->path();
[email protected]bf2491a92009-11-29 16:39:48752 list[index]->OnClose(code);
[email protected]aea80602009-09-18 00:55:08753 }
754
755 // Clear out anything pending.
756 active_streams_.clear();
757
[email protected]289bd7f2009-10-29 17:32:40758 delete[] list;
[email protected]aea80602009-09-18 00:55:08759 }
760
761 if (pushed_streams_.size()) {
[email protected]9b010802009-12-27 22:55:30762 streams_abandoned_count_ += pushed_streams_.size();
[email protected]aea80602009-09-18 00:55:08763 abandoned_push_streams.Add(pushed_streams_.size());
764 pushed_streams_.clear();
765 }
[email protected]ab8949a2010-03-29 21:16:54766
767 // We also need to drain the queue.
768 while (queue_.size())
769 queue_.pop();
[email protected]aea80602009-09-18 00:55:08770}
771
[email protected]955fc2e72010-02-08 20:37:30772int SpdySession::GetNewStreamId() {
[email protected]aea80602009-09-18 00:55:08773 int id = stream_hi_water_mark_;
774 stream_hi_water_mark_ += 2;
775 if (stream_hi_water_mark_ > 0x7fff)
776 stream_hi_water_mark_ = 1;
777 return id;
778}
779
[email protected]74188f22010-04-09 20:18:50780void SpdySession::QueueFrame(spdy::SpdyFrame* frame,
781 spdy::SpdyPriority priority,
782 SpdyStream* stream) {
783 int length = spdy::SpdyFrame::size() + frame->length();
784 IOBuffer* buffer = new IOBuffer(length);
785 memcpy(buffer->data(), frame->data(), length);
786 queue_.push(SpdyIOBuffer(buffer, length, priority, stream));
787
788 WriteSocketLater();
789}
790
[email protected]955fc2e72010-02-08 20:37:30791void SpdySession::CloseSessionOnError(net::Error err) {
[email protected]69d717bd2010-04-21 18:43:21792 // Closing all streams can have a side-effect of dropping the last reference
793 // to |this|. Hold a reference through this function.
794 scoped_refptr<SpdySession> self(this);
795
[email protected]353f6162009-12-10 18:04:12796 DCHECK_LT(err, OK);
[email protected]bce9f682010-03-30 16:36:02797 LOG(INFO) << "spdy::CloseSessionOnError(" << err << ") for " <<
798 host_port_pair().ToString();
[email protected]60253bd2009-12-01 01:16:39799
800 // Don't close twice. This can occur because we can have both
801 // a read and a write outstanding, and each can complete with
802 // an error.
803 if (state_ != CLOSED) {
804 state_ = CLOSED;
805 error_ = err;
806 CloseAllStreams(err);
[email protected]4b4762a2010-04-23 16:04:14807 RemoveFromPool();
[email protected]60253bd2009-12-01 01:16:39808 }
809}
810
[email protected]955fc2e72010-02-08 20:37:30811void SpdySession::ActivateStream(SpdyStream* stream) {
812 const spdy::SpdyStreamId id = stream->stream_id();
[email protected]aea80602009-09-18 00:55:08813 DCHECK(!IsStreamActive(id));
814
[email protected]aea80602009-09-18 00:55:08815 active_streams_[id] = stream;
[email protected]aea80602009-09-18 00:55:08816}
817
[email protected]955fc2e72010-02-08 20:37:30818void SpdySession::DeactivateStream(spdy::SpdyStreamId id) {
[email protected]aea80602009-09-18 00:55:08819 DCHECK(IsStreamActive(id));
820
821 // Verify it is not on the pushed_streams_ list.
822 ActiveStreamList::iterator it;
823 for (it = pushed_streams_.begin(); it != pushed_streams_.end(); ++it) {
[email protected]955fc2e72010-02-08 20:37:30824 scoped_refptr<SpdyStream> curr = *it;
[email protected]a677f2b2009-11-22 00:43:00825 if (id == curr->stream_id()) {
[email protected]aea80602009-09-18 00:55:08826 pushed_streams_.erase(it);
827 break;
828 }
829 }
830
831 active_streams_.erase(id);
832}
833
[email protected]4b4762a2010-04-23 16:04:14834void SpdySession::RemoveFromPool() {
835 if (in_session_pool_) {
836 session_->spdy_session_pool()->Remove(this);
837 in_session_pool_ = false;
838 }
839}
840
[email protected]955fc2e72010-02-08 20:37:30841scoped_refptr<SpdyStream> SpdySession::GetPushStream(const std::string& path) {
842 static StatsCounter used_push_streams("spdy.claimed_push_streams");
[email protected]aea80602009-09-18 00:55:08843
844 LOG(INFO) << "Looking for push stream: " << path;
845
[email protected]955fc2e72010-02-08 20:37:30846 scoped_refptr<SpdyStream> stream;
[email protected]a677f2b2009-11-22 00:43:00847
[email protected]aea80602009-09-18 00:55:08848 // We just walk a linear list here.
849 ActiveStreamList::iterator it;
850 for (it = pushed_streams_.begin(); it != pushed_streams_.end(); ++it) {
[email protected]a677f2b2009-11-22 00:43:00851 stream = *it;
852 if (path == stream->path()) {
853 CHECK(stream->pushed());
[email protected]aea80602009-09-18 00:55:08854 pushed_streams_.erase(it);
855 used_push_streams.Increment();
856 LOG(INFO) << "Push Stream Claim for: " << path;
[email protected]a677f2b2009-11-22 00:43:00857 break;
[email protected]aea80602009-09-18 00:55:08858 }
859 }
[email protected]a677f2b2009-11-22 00:43:00860
861 return stream;
[email protected]aea80602009-09-18 00:55:08862}
863
[email protected]955fc2e72010-02-08 20:37:30864void SpdySession::GetSSLInfo(SSLInfo* ssl_info) {
[email protected]dcc6bbb2009-12-09 19:09:01865 if (is_secure_) {
866 SSLClientSocket* ssl_socket =
[email protected]1f14a912009-12-21 20:32:44867 reinterpret_cast<SSLClientSocket*>(connection_->socket());
[email protected]dcc6bbb2009-12-09 19:09:01868 ssl_socket->GetSSLInfo(ssl_info);
869 }
870}
871
[email protected]955fc2e72010-02-08 20:37:30872void SpdySession::OnError(spdy::SpdyFramer* framer) {
873 LOG(ERROR) << "SpdySession error: " << framer->error_code();
874 CloseSessionOnError(net::ERR_SPDY_PROTOCOL_ERROR);
[email protected]aea80602009-09-18 00:55:08875}
876
[email protected]955fc2e72010-02-08 20:37:30877void SpdySession::OnStreamFrameData(spdy::SpdyStreamId stream_id,
[email protected]aea80602009-09-18 00:55:08878 const char* data,
[email protected]aeac1e42009-10-10 00:26:01879 size_t len) {
[email protected]955fc2e72010-02-08 20:37:30880 LOG(INFO) << "Spdy data for stream " << stream_id << ", " << len << " bytes";
[email protected]aea80602009-09-18 00:55:08881 bool valid_stream = IsStreamActive(stream_id);
882 if (!valid_stream) {
[email protected]92683b52009-12-21 21:01:12883 // NOTE: it may just be that the stream was cancelled.
[email protected]affe8fe2009-10-14 20:06:05884 LOG(WARNING) << "Received data frame for invalid stream " << stream_id;
[email protected]aea80602009-09-18 00:55:08885 return;
886 }
[email protected]93300672009-10-24 13:22:51887
[email protected]955fc2e72010-02-08 20:37:30888 scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
[email protected]a677f2b2009-11-22 00:43:00889 bool success = stream->OnDataReceived(data, len);
890 // |len| == 0 implies a closed stream.
891 if (!success || !len)
892 DeactivateStream(stream_id);
[email protected]aea80602009-09-18 00:55:08893}
894
[email protected]3f662f12010-03-25 19:56:12895bool SpdySession::Respond(const spdy::SpdyHeaderBlock& headers,
896 const scoped_refptr<SpdyStream> stream) {
897 // TODO(mbelshe): For now we convert from our nice hash map back
898 // to a string of headers; this is because the HttpResponseInfo
899 // is a bit rigid for its http (non-spdy) design.
900 HttpResponseInfo response;
901 // TODO(ahendrickson): This is recorded after the entire SYN_STREAM control
902 // frame has been received and processed. Move to framer?
903 response.response_time = base::Time::Now();
904 if (SpdyHeadersToHttpResponse(headers, &response)) {
905 GetSSLInfo(&response.ssl_info);
906 response.request_time = stream->GetRequestTime();
907 response.vary_data.Init(*stream->GetRequestInfo(), *response.headers);
908 stream->OnResponseReceived(response);
909 } else {
910 const spdy::SpdyStreamId stream_id = stream->stream_id();
911 stream->OnClose(ERR_INVALID_RESPONSE);
912 DeactivateStream(stream_id);
913 return false;
914 }
915 return true;
916}
917
[email protected]651b77c2010-03-10 19:29:42918void SpdySession::OnSyn(const spdy::SpdySynStreamControlFrame& frame,
919 const spdy::SpdyHeaderBlock& headers) {
920 spdy::SpdyStreamId stream_id = frame.stream_id();
[email protected]aea80602009-09-18 00:55:08921
[email protected]9ccdad22010-03-04 19:19:21922 LOG(INFO) << "Spdy SynStream for stream " << stream_id;
923
[email protected]93300672009-10-24 13:22:51924 // Server-initiated streams should have even sequence numbers.
[email protected]aea80602009-09-18 00:55:08925 if ((stream_id & 0x1) != 0) {
[email protected]18c53ae2009-10-01 18:18:52926 LOG(ERROR) << "Received invalid OnSyn stream id " << stream_id;
[email protected]aea80602009-09-18 00:55:08927 return;
928 }
929
930 if (IsStreamActive(stream_id)) {
[email protected]18c53ae2009-10-01 18:18:52931 LOG(ERROR) << "Received OnSyn for active stream " << stream_id;
[email protected]aea80602009-09-18 00:55:08932 return;
933 }
934
[email protected]9b010802009-12-27 22:55:30935 streams_pushed_count_++;
[email protected]a677f2b2009-11-22 00:43:00936
[email protected]955fc2e72010-02-08 20:37:30937 LOG(INFO) << "SpdySession: Syn received for stream: " << stream_id;
[email protected]9b010802009-12-27 22:55:30938
[email protected]955fc2e72010-02-08 20:37:30939 LOG(INFO) << "SPDY SYN RESPONSE HEADERS -----------------------";
[email protected]651b77c2010-03-10 19:29:42940 DumpSpdyHeaders(headers);
[email protected]967dd5412009-11-21 23:33:30941
[email protected]18c53ae2009-10-01 18:18:52942 // TODO(mbelshe): DCHECK that this is a GET method?
943
[email protected]651b77c2010-03-10 19:29:42944 const std::string& path = ContainsKey(headers, "path") ?
945 headers.find("path")->second : "";
[email protected]a677f2b2009-11-22 00:43:00946
[email protected]aea80602009-09-18 00:55:08947 // Verify that the response had a URL for us.
[email protected]a677f2b2009-11-22 00:43:00948 DCHECK(!path.empty());
949 if (path.empty()) {
[email protected]aea80602009-09-18 00:55:08950 LOG(WARNING) << "Pushed stream did not contain a path.";
[email protected]aea80602009-09-18 00:55:08951 return;
952 }
[email protected]18c53ae2009-10-01 18:18:52953
[email protected]955fc2e72010-02-08 20:37:30954 scoped_refptr<SpdyStream> stream;
[email protected]a677f2b2009-11-22 00:43:00955
[email protected]18c53ae2009-10-01 18:18:52956 // Check if we already have a delegate awaiting this stream.
[email protected]2931238b2009-11-19 01:19:51957 PendingStreamMap::iterator it;
[email protected]a677f2b2009-11-22 00:43:00958 it = pending_streams_.find(path);
[email protected]18c53ae2009-10-01 18:18:52959 if (it != pending_streams_.end()) {
[email protected]a677f2b2009-11-22 00:43:00960 stream = it->second;
[email protected]18c53ae2009-10-01 18:18:52961 pending_streams_.erase(it);
[email protected]18c53ae2009-10-01 18:18:52962 }
[email protected]aea80602009-09-18 00:55:08963
[email protected]a677f2b2009-11-22 00:43:00964 if (stream) {
965 CHECK(stream->pushed());
[email protected]b1f031dd2010-03-02 23:19:33966 CHECK_EQ(0u, stream->stream_id());
[email protected]a677f2b2009-11-22 00:43:00967 stream->set_stream_id(stream_id);
968 } else {
[email protected]9e743cd2010-03-16 07:03:53969 // TODO(mbelshe): can we figure out how to use a NetLog here?
[email protected]955fc2e72010-02-08 20:37:30970 stream = new SpdyStream(this, stream_id, true, NULL);
[email protected]c8b16742010-02-05 20:49:53971
972 // A new HttpResponseInfo object needs to be generated so the call to
973 // OnResponseReceived below has something to fill in.
[email protected]955fc2e72010-02-08 20:37:30974 // When a SpdyNetworkTransaction is created for this resource, the
[email protected]c8b16742010-02-05 20:49:53975 // response_info is copied over and this version is destroyed.
976 //
977 // TODO(cbentzel): Minimize allocations and copies of HttpResponseInfo
[email protected]955fc2e72010-02-08 20:37:30978 // object. Should it just be part of SpdyStream?
[email protected]c8b16742010-02-05 20:49:53979 HttpResponseInfo* response_info = new HttpResponseInfo();
980 stream->set_response_info_pointer(response_info);
[email protected]a677f2b2009-11-22 00:43:00981 }
982
[email protected]c8b16742010-02-05 20:49:53983 pushed_streams_.push_back(stream);
984
[email protected]a677f2b2009-11-22 00:43:00985 // Activate a stream and parse the headers.
986 ActivateStream(stream);
987
988 stream->set_path(path);
989
[email protected]3f662f12010-03-25 19:56:12990 if (!Respond(headers, stream))
[email protected]dd11b932009-11-30 19:39:48991 return;
[email protected]a677f2b2009-11-22 00:43:00992
[email protected]aea80602009-09-18 00:55:08993 LOG(INFO) << "Got pushed stream for " << stream->path();
994
[email protected]955fc2e72010-02-08 20:37:30995 static StatsCounter push_requests("spdy.pushed_streams");
[email protected]aea80602009-09-18 00:55:08996 push_requests.Increment();
997}
998
[email protected]651b77c2010-03-10 19:29:42999void SpdySession::OnSynReply(const spdy::SpdySynReplyControlFrame& frame,
1000 const spdy::SpdyHeaderBlock& headers) {
1001 spdy::SpdyStreamId stream_id = frame.stream_id();
[email protected]9ccdad22010-03-04 19:19:211002 LOG(INFO) << "Spdy SynReply for stream " << stream_id;
1003
[email protected]aea80602009-09-18 00:55:081004 bool valid_stream = IsStreamActive(stream_id);
1005 if (!valid_stream) {
[email protected]92683b52009-12-21 21:01:121006 // NOTE: it may just be that the stream was cancelled.
[email protected]affe8fe2009-10-14 20:06:051007 LOG(WARNING) << "Received SYN_REPLY for invalid stream " << stream_id;
[email protected]aea80602009-09-18 00:55:081008 return;
1009 }
1010
[email protected]955fc2e72010-02-08 20:37:301011 LOG(INFO) << "SPDY SYN_REPLY RESPONSE HEADERS for stream: " << stream_id;
[email protected]651b77c2010-03-10 19:29:421012 DumpSpdyHeaders(headers);
[email protected]be91bc62009-12-04 23:45:031013
[email protected]18c53ae2009-10-01 18:18:521014 // We record content declared as being pushed so that we don't
1015 // request a duplicate stream which is already scheduled to be
1016 // sent to us.
[email protected]955fc2e72010-02-08 20:37:301017 spdy::SpdyHeaderBlock::const_iterator it;
[email protected]651b77c2010-03-10 19:29:421018 it = headers.find("X-Associated-Content");
1019 if (it != headers.end()) {
[email protected]18c53ae2009-10-01 18:18:521020 const std::string& content = it->second;
1021 std::string::size_type start = 0;
1022 std::string::size_type end = 0;
1023 do {
1024 end = content.find("||", start);
[email protected]ba051e312009-10-07 22:12:331025 if (end == std::string::npos)
[email protected]18c53ae2009-10-01 18:18:521026 end = content.length();
1027 std::string url = content.substr(start, end - start);
1028 std::string::size_type pos = url.find("??");
[email protected]ba051e312009-10-07 22:12:331029 if (pos == std::string::npos)
[email protected]18c53ae2009-10-01 18:18:521030 break;
[email protected]ba051e312009-10-07 22:12:331031 url = url.substr(pos + 2);
[email protected]18c53ae2009-10-01 18:18:521032 GURL gurl(url);
[email protected]84b562f2009-11-20 18:25:301033 std::string path = gurl.PathForRequest();
1034 if (path.length())
1035 pending_streams_[path] = NULL;
1036 else
1037 LOG(INFO) << "Invalid X-Associated-Content path: " << url;
[email protected]18c53ae2009-10-01 18:18:521038 start = end + 2;
[email protected]84b562f2009-11-20 18:25:301039 } while (start < content.length());
[email protected]18c53ae2009-10-01 18:18:521040 }
1041
[email protected]955fc2e72010-02-08 20:37:301042 scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
[email protected]b1f031dd2010-03-02 23:19:331043 CHECK_EQ(stream->stream_id(), stream_id);
[email protected]92683b52009-12-21 21:01:121044 CHECK(!stream->cancelled());
[email protected]3f662f12010-03-25 19:56:121045
1046 Respond(headers, stream);
[email protected]aea80602009-09-18 00:55:081047}
1048
[email protected]955fc2e72010-02-08 20:37:301049void SpdySession::OnControl(const spdy::SpdyControlFrame* frame) {
1050 spdy::SpdyHeaderBlock headers;
[email protected]aea80602009-09-18 00:55:081051 uint32 type = frame->type();
[email protected]955fc2e72010-02-08 20:37:301052 if (type == spdy::SYN_STREAM || type == spdy::SYN_REPLY) {
1053 if (!spdy_framer_.ParseHeaderBlock(frame, &headers)) {
1054 LOG(WARNING) << "Could not parse Spdy Control Frame Header";
[email protected]84b562f2009-11-20 18:25:301055 // TODO(mbelshe): Error the session?
[email protected]aea80602009-09-18 00:55:081056 return;
1057 }
1058 }
1059
1060 switch (type) {
[email protected]74188f22010-04-09 20:18:501061 case spdy::GOAWAY:
1062 OnGoAway(*reinterpret_cast<const spdy::SpdyGoAwayControlFrame*>(frame));
1063 break;
1064 case spdy::SETTINGS:
1065 OnSettings(
1066 *reinterpret_cast<const spdy::SpdySettingsControlFrame*>(frame));
1067 break;
1068 case spdy::RST_STREAM:
1069 OnFin(*reinterpret_cast<const spdy::SpdyRstStreamControlFrame*>(frame));
1070 break;
[email protected]955fc2e72010-02-08 20:37:301071 case spdy::SYN_STREAM:
[email protected]651b77c2010-03-10 19:29:421072 OnSyn(*reinterpret_cast<const spdy::SpdySynStreamControlFrame*>(frame),
1073 headers);
[email protected]aea80602009-09-18 00:55:081074 break;
[email protected]955fc2e72010-02-08 20:37:301075 case spdy::SYN_REPLY:
[email protected]aea80602009-09-18 00:55:081076 OnSynReply(
[email protected]651b77c2010-03-10 19:29:421077 *reinterpret_cast<const spdy::SpdySynReplyControlFrame*>(frame),
1078 headers);
[email protected]aea80602009-09-18 00:55:081079 break;
[email protected]aea80602009-09-18 00:55:081080 default:
1081 DCHECK(false); // Error!
1082 }
1083}
1084
[email protected]651b77c2010-03-10 19:29:421085void SpdySession::OnFin(const spdy::SpdyRstStreamControlFrame& frame) {
1086 spdy::SpdyStreamId stream_id = frame.stream_id();
[email protected]9ccdad22010-03-04 19:19:211087 LOG(INFO) << "Spdy Fin for stream " << stream_id;
1088
[email protected]aea80602009-09-18 00:55:081089 bool valid_stream = IsStreamActive(stream_id);
1090 if (!valid_stream) {
[email protected]92683b52009-12-21 21:01:121091 // NOTE: it may just be that the stream was cancelled.
[email protected]aea80602009-09-18 00:55:081092 LOG(WARNING) << "Received FIN for invalid stream" << stream_id;
1093 return;
1094 }
[email protected]955fc2e72010-02-08 20:37:301095 scoped_refptr<SpdyStream> stream = active_streams_[stream_id];
[email protected]b1f031dd2010-03-02 23:19:331096 CHECK_EQ(stream->stream_id(), stream_id);
[email protected]92683b52009-12-21 21:01:121097 CHECK(!stream->cancelled());
[email protected]651b77c2010-03-10 19:29:421098 if (frame.status() == 0) {
[email protected]a677f2b2009-11-22 00:43:001099 stream->OnDataReceived(NULL, 0);
[email protected]aea80602009-09-18 00:55:081100 } else {
[email protected]651b77c2010-03-10 19:29:421101 LOG(ERROR) << "Spdy stream closed: " << frame.status();
[email protected]955fc2e72010-02-08 20:37:301102 // TODO(mbelshe): Map from Spdy-protocol errors to something sensical.
[email protected]93300672009-10-24 13:22:511103 // For now, it doesn't matter much - it is a protocol error.
[email protected]a677f2b2009-11-22 00:43:001104 stream->OnClose(ERR_FAILED);
[email protected]aea80602009-09-18 00:55:081105 }
1106
[email protected]a677f2b2009-11-22 00:43:001107 DeactivateStream(stream_id);
[email protected]aea80602009-09-18 00:55:081108}
1109
[email protected]651b77c2010-03-10 19:29:421110void SpdySession::OnGoAway(const spdy::SpdyGoAwayControlFrame& frame) {
[email protected]bce9f682010-03-30 16:36:021111 LOG(INFO) << "Spdy GOAWAY for session[" << this << "] for " <<
1112 host_port_pair().ToString();
[email protected]4b4762a2010-04-23 16:04:141113 RemoveFromPool();
[email protected]651b77c2010-03-10 19:29:421114
1115 // TODO(willchan): Cancel any streams that are past the GoAway frame's
1116 // |last_accepted_stream_id|.
1117
1118 // Don't bother killing any streams that are still reading. They'll either
1119 // complete successfully or get an ERR_CONNECTION_CLOSED when the socket is
1120 // closed.
1121}
1122
[email protected]74188f22010-04-09 20:18:501123void SpdySession::OnSettings(const spdy::SpdySettingsControlFrame& frame) {
1124 spdy::SpdySettings settings;
1125 if (spdy_framer_.ParseSettings(&frame, &settings)) {
1126 SpdySettingsStorage* settings_storage = session_->mutable_spdy_settings();
1127 settings_storage->Set(host_port_pair_, settings);
1128 }
1129}
1130
1131void SpdySession::SendSettings() {
1132 const SpdySettingsStorage& settings_storage = session_->spdy_settings();
1133 const spdy::SpdySettings& settings = settings_storage.Get(host_port_pair_);
1134 if (settings.empty())
1135 return;
1136
1137 // Create the SETTINGS frame and send it.
1138 scoped_ptr<spdy::SpdySettingsControlFrame> settings_frame(
1139 spdy_framer_.CreateSettings(settings));
1140 QueueFrame(settings_frame.get(), 0, NULL);
1141}
1142
[email protected]aea80602009-09-18 00:55:081143} // namespace net