blob: aa19f7aeb0c8f2b9dba885ddfa677497f2047b53 [file] [log] [blame]
[email protected]aea80602009-09-18 00:55:081// Copyright (c) 2009 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/flip/flip_session.h"
6
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"
14#include "net/base/load_flags.h"
[email protected]33751562009-10-29 02:17:1115#include "net/base/net_util.h"
[email protected]aea80602009-09-18 00:55:0816#include "net/flip/flip_frame_builder.h"
17#include "net/flip/flip_protocol.h"
[email protected]cd314c822009-10-29 03:13:0618#include "net/flip/flip_stream.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]18c53ae2009-10-01 18:18:5223#include "net/socket/client_socket_factory.h"
24#include "net/socket/ssl_client_socket.h"
[email protected]aea80602009-09-18 00:55:0825#include "net/tools/dump_cache/url_to_filename_encoder.h"
26
[email protected]967dd5412009-11-21 23:33:3027namespace {
28
29// Diagnostics function to dump the headers of a request.
30// TODO(mbelshe): Remove this function.
31void DumpFlipHeaders(const flip::FlipHeaderBlock& headers) {
32 // Because this function gets called on every request,
33 // take extra care to optimize it away if logging is turned off.
34 if (logging::LOG_INFO < logging::GetMinLogLevel())
35 return;
36
37 flip::FlipHeaderBlock::const_iterator it = headers.begin();
38 while (it != headers.end()) {
39 std::string val = (*it).second;
40 std::string::size_type pos = 0;
41 while ((pos = val.find('\0', pos)) != val.npos)
42 val[pos] = '\n';
43 LOG(INFO) << (*it).first << "==" << val;
44 ++it;
45 }
46}
47
48} // namespace
49
[email protected]aea80602009-09-18 00:55:0850namespace net {
51
[email protected]a677f2b2009-11-22 00:43:0052namespace {
53
54const int kReadBufferSize = 4 * 1024;
55
56HttpResponseInfo FlipHeadersToHttpResponse(
57 const flip::FlipHeaderBlock& headers) {
58 std::string raw_headers(headers.find("version")->second);
59 raw_headers.push_back(' ');
60 raw_headers.append(headers.find("status")->second);
61 raw_headers.push_back('\0');
62 flip::FlipHeaderBlock::const_iterator it;
63 for (it = headers.begin(); it != headers.end(); ++it) {
64 // For each value, if the server sends a NUL-separated
65 // list of values, we separate that back out into
66 // individual headers for each value in the list.
67 // e.g.
68 // Set-Cookie "foo\0bar"
69 // becomes
70 // Set-Cookie: foo\0
71 // Set-Cookie: bar\0
72 std::string value = it->second;
73 size_t start = 0;
74 size_t end = 0;
75 do {
76 end = value.find('\0', start);
77 std::string tval;
78 if (end != value.npos)
79 tval = value.substr(start, (end - start));
80 else
81 tval = value.substr(start);
82 raw_headers.append(it->first);
83 raw_headers.push_back(':');
84 raw_headers.append(tval);
85 raw_headers.push_back('\0');
86 start = end + 1;
87 } while (end != value.npos);
88 }
89
90 HttpResponseInfo response;
91 response.headers = new HttpResponseHeaders(raw_headers);
92 return response;
93}
94
95// Create a FlipHeaderBlock for a Flip SYN_STREAM Frame from
96// a HttpRequestInfo block.
97void CreateFlipHeadersFromHttpRequest(
98 const HttpRequestInfo& info, flip::FlipHeaderBlock* headers) {
99 static const char kHttpProtocolVersion[] = "HTTP/1.1";
100
101 HttpUtil::HeadersIterator it(info.extra_headers.begin(),
102 info.extra_headers.end(),
103 "\r\n");
104 while (it.GetNext()) {
105 std::string name = StringToLowerASCII(it.name());
106 if (headers->find(name) == headers->end()) {
107 (*headers)[name] = it.values();
108 } else {
109 std::string new_value = (*headers)[name];
110 new_value += "\0";
111 new_value += it.values();
112 (*headers)[name] = new_value;
113 }
114 }
115
116 (*headers)["method"] = info.method;
117 (*headers)["url"] = info.url.spec();
118 (*headers)["version"] = kHttpProtocolVersion;
119 if (info.user_agent.length())
120 (*headers)["user-agent"] = info.user_agent;
121 if (!info.referrer.is_empty())
122 (*headers)["referer"] = info.referrer.spec();
123
124 // Honor load flags that impact proxy caches.
125 if (info.load_flags & LOAD_BYPASS_CACHE) {
126 (*headers)["pragma"] = "no-cache";
127 (*headers)["cache-control"] = "no-cache";
128 } else if (info.load_flags & LOAD_VALIDATE_CACHE) {
129 (*headers)["cache-control"] = "max-age=0";
130 }
131}
132
133} // namespace
134
[email protected]aea80602009-09-18 00:55:08135// static
[email protected]affe8fe2009-10-14 20:06:05136bool FlipSession::use_ssl_ = true;
[email protected]aea80602009-09-18 00:55:08137
[email protected]c8b49442009-11-12 21:20:40138FlipSession::FlipSession(const std::string& host, HttpNetworkSession* session)
[email protected]aea80602009-09-18 00:55:08139 : ALLOW_THIS_IN_INITIALIZER_LIST(
[email protected]18c53ae2009-10-01 18:18:52140 connect_callback_(this, &FlipSession::OnTCPConnect)),
141 ALLOW_THIS_IN_INITIALIZER_LIST(
142 ssl_connect_callback_(this, &FlipSession::OnSSLConnect)),
[email protected]aea80602009-09-18 00:55:08143 ALLOW_THIS_IN_INITIALIZER_LIST(
144 read_callback_(this, &FlipSession::OnReadComplete)),
145 ALLOW_THIS_IN_INITIALIZER_LIST(
146 write_callback_(this, &FlipSession::OnWriteComplete)),
147 domain_(host),
148 session_(session),
149 connection_started_(false),
[email protected]18c53ae2009-10-01 18:18:52150 connection_ready_(false),
[email protected]230cadd2009-09-22 16:33:59151 read_buffer_(new IOBuffer(kReadBufferSize)),
[email protected]aea80602009-09-18 00:55:08152 read_pending_(false),
[email protected]ba051e312009-10-07 22:12:33153 stream_hi_water_mark_(1), // Always start at 1 for the first stream id.
154 delayed_write_pending_(false),
155 write_pending_(false) {
156 // TODO(mbelshe): consider randomization of the stream_hi_water_mark.
[email protected]aea80602009-09-18 00:55:08157
158 flip_framer_.set_visitor(this);
[email protected]18c53ae2009-10-01 18:18:52159
160 session_->ssl_config_service()->GetSSLConfig(&ssl_config_);
[email protected]aea80602009-09-18 00:55:08161}
162
163FlipSession::~FlipSession() {
[email protected]93300672009-10-24 13:22:51164 // Cleanup all the streams.
165 CloseAllStreams(net::ERR_ABORTED);
166
[email protected]aea80602009-09-18 00:55:08167 if (connection_.is_initialized()) {
168 // With Flip we can't recycle sockets.
169 connection_.socket()->Disconnect();
170 }
[email protected]d1eda932009-11-04 01:03:10171
[email protected]9f7c4fd2009-11-24 18:50:15172 // TODO(willchan): Don't hardcode port 80 here.
173 DCHECK(!session_->flip_session_pool()->HasSession(
174 HostResolver::RequestInfo(domain_, 80)));
[email protected]aea80602009-09-18 00:55:08175}
176
177net::Error FlipSession::Connect(const std::string& group_name,
178 const HostResolver::RequestInfo& host,
179 int priority) {
[email protected]ff57bb82009-11-12 06:52:14180 DCHECK(priority >= FLIP_PRIORITY_HIGHEST && priority < FLIP_PRIORITY_LOWEST);
[email protected]aea80602009-09-18 00:55:08181
182 // If the connect process is started, let the caller continue.
183 if (connection_started_)
184 return net::OK;
185
186 connection_started_ = true;
187
188 static StatsCounter flip_sessions("flip.sessions");
189 flip_sessions.Increment();
190
191 int rv = connection_.Init(group_name, host, priority, &connect_callback_,
192 session_->tcp_socket_pool(), NULL);
193
194 // If the connect is pending, we still return ok. The APIs enqueue
195 // work until after the connect completes asynchronously later.
196 if (rv == net::ERR_IO_PENDING)
197 return net::OK;
198 return static_cast<net::Error>(rv);
199}
200
[email protected]a677f2b2009-11-22 00:43:00201scoped_refptr<FlipStream> FlipSession::GetOrCreateStream(
202 const HttpRequestInfo& request,
203 const UploadDataStream* upload_data) {
204 const GURL& url = request.url;
205 const std::string& path = url.PathForRequest();
[email protected]aea80602009-09-18 00:55:08206
[email protected]a677f2b2009-11-22 00:43:00207 scoped_refptr<FlipStream> stream;
[email protected]aea80602009-09-18 00:55:08208
209 // Check if we have a push stream for this path.
[email protected]a677f2b2009-11-22 00:43:00210 if (request.method == "GET") {
[email protected]aea80602009-09-18 00:55:08211 stream = GetPushStream(path);
[email protected]a677f2b2009-11-22 00:43:00212 if (stream)
213 return stream;
[email protected]aea80602009-09-18 00:55:08214 }
215
[email protected]18c53ae2009-10-01 18:18:52216 // Check if we have a pending push stream for this url.
[email protected]2931238b2009-11-19 01:19:51217 PendingStreamMap::iterator it;
[email protected]a677f2b2009-11-22 00:43:00218 it = pending_streams_.find(path);
[email protected]18c53ae2009-10-01 18:18:52219 if (it != pending_streams_.end()) {
[email protected]a677f2b2009-11-22 00:43:00220 DCHECK(!it->second);
221 // Server will assign a stream id when the push stream arrives. Use 0 for
222 // now.
223 FlipStream* stream = new FlipStream(this, 0, true);
224 stream->set_path(path);
225 it->second = stream;
226 return it->second;
[email protected]18c53ae2009-10-01 18:18:52227 }
228
[email protected]a677f2b2009-11-22 00:43:00229 const flip::FlipStreamId stream_id = GetNewStreamId();
[email protected]aea80602009-09-18 00:55:08230
[email protected]a677f2b2009-11-22 00:43:00231 // If we still don't have a stream, activate one now.
232 stream = new FlipStream(this, stream_id, false);
233 stream->set_priority(request.priority);
234 stream->set_path(path);
235 ActivateStream(stream);
236
237 LOG(INFO) << "FlipStream: Creating stream " << stream_id << " for " << url;
[email protected]aea80602009-09-18 00:55:08238
239 // TODO(mbelshe): Optimize memory allocations
[email protected]a677f2b2009-11-22 00:43:00240 int priority = request.priority;
[email protected]aea80602009-09-18 00:55:08241
242 // Hack for the priorities
243 // TODO(mbelshe): These need to be plumbed through the Http Network Stack.
244 if (path.find(".css") != path.npos) {
245 priority = 1;
246 } else if (path.find(".html") != path.npos) {
247 priority = 0;
248 } else if (path.find(".js") != path.npos) {
249 priority = 1;
250 } else {
251 priority = 3;
252 }
253
254 DCHECK(priority >= FLIP_PRIORITY_HIGHEST &&
255 priority <= FLIP_PRIORITY_LOWEST);
256
257 // Convert from HttpRequestHeaders to Flip Headers.
258 flip::FlipHeaderBlock headers;
[email protected]a677f2b2009-11-22 00:43:00259 CreateFlipHeadersFromHttpRequest(request, &headers);
[email protected]aea80602009-09-18 00:55:08260
[email protected]72552f02009-10-28 15:25:01261 flip::FlipControlFlags flags = flip::CONTROL_FLAG_NONE;
[email protected]a677f2b2009-11-22 00:43:00262 if (!request.upload_data || !upload_data->size())
[email protected]72552f02009-10-28 15:25:01263 flags = flip::CONTROL_FLAG_FIN;
264
[email protected]aea80602009-09-18 00:55:08265 // Create a SYN_STREAM packet and add to the output queue.
[email protected]22eb9682009-11-06 21:51:58266 scoped_ptr<flip::FlipSynStreamControlFrame> syn_frame(
267 flip_framer_.CreateSynStream(stream_id, priority, flags, false,
268 &headers));
269 int length = flip::FlipFrame::size() + syn_frame->length();
[email protected]bf2491a92009-11-29 16:39:48270 IOBuffer* buffer = new IOBuffer(length);
[email protected]22eb9682009-11-06 21:51:58271 memcpy(buffer->data(), syn_frame->data(), length);
[email protected]bf2491a92009-11-29 16:39:48272 queue_.push(FlipIOBuffer(buffer, length, priority, stream));
[email protected]aea80602009-09-18 00:55:08273
274 static StatsCounter flip_requests("flip.requests");
275 flip_requests.Increment();
276
[email protected]a677f2b2009-11-22 00:43:00277 LOG(INFO) << "FETCHING: " << request.url.spec();
[email protected]aea80602009-09-18 00:55:08278
[email protected]967dd5412009-11-21 23:33:30279 LOG(INFO) << "FLIP SYN_STREAM HEADERS ----------------------------------";
280 DumpFlipHeaders(headers);
[email protected]aea80602009-09-18 00:55:08281
[email protected]aea80602009-09-18 00:55:08282 // Schedule to write to the socket after we've made it back
283 // to the message loop so that we can aggregate multiple
284 // requests.
285 // TODO(mbelshe): Should we do the "first" request immediately?
286 // maybe we should only 'do later' for subsequent
287 // requests.
288 WriteSocketLater();
289
[email protected]a677f2b2009-11-22 00:43:00290 return stream;
[email protected]aea80602009-09-18 00:55:08291}
292
[email protected]ff57bb82009-11-12 06:52:14293int FlipSession::WriteStreamData(flip::FlipStreamId stream_id,
294 net::IOBuffer* data, int len) {
[email protected]967dd5412009-11-21 23:33:30295 LOG(INFO) << "Writing Stream Data for stream " << stream_id << " (" << len
296 << " bytes)";
[email protected]ff57bb82009-11-12 06:52:14297 const int kMss = 1430; // This is somewhat arbitrary and not really fixed,
298 // but it will always work reasonably with ethernet.
299 // Chop the world into 2-packet chunks. This is somewhat arbitrary, but
300 // is reasonably small and ensures that we elicit ACKs quickly from TCP
301 // (because TCP tries to only ACK every other packet).
302 const int kMaxFlipFrameChunkSize = (2 * kMss) - flip::FlipFrame::size();
303
304 // Find our stream
305 DCHECK(IsStreamActive(stream_id));
[email protected]a677f2b2009-11-22 00:43:00306 scoped_refptr<FlipStream> stream = active_streams_[stream_id];
307 CHECK(stream->stream_id() == stream_id);
[email protected]ff57bb82009-11-12 06:52:14308 if (!stream)
309 return ERR_INVALID_FLIP_STREAM;
310
[email protected]2931238b2009-11-19 01:19:51311 // TODO(mbelshe): Setting of the FIN is assuming that the caller will pass
312 // all data to write in a single chunk. Is this always true?
313
[email protected]ff57bb82009-11-12 06:52:14314 // Set the flags on the upload.
315 flip::FlipDataFlags flags = flip::DATA_FLAG_FIN;
316 if (len > kMaxFlipFrameChunkSize) {
317 len = kMaxFlipFrameChunkSize;
318 flags = flip::DATA_FLAG_NONE;
319 }
320
321 // TODO(mbelshe): reduce memory copies here.
322 scoped_ptr<flip::FlipDataFrame> frame(
323 flip_framer_.CreateDataFrame(stream_id, data->data(), len, flags));
324 int length = flip::FlipFrame::size() + frame->length();
325 IOBufferWithSize* buffer = new IOBufferWithSize(length);
326 memcpy(buffer->data(), frame->data(), length);
[email protected]bf2491a92009-11-29 16:39:48327 queue_.push(FlipIOBuffer(buffer, length, stream->priority(), stream));
[email protected]2931238b2009-11-19 01:19:51328
329 // Whenever we queue onto the socket we need to ensure that we will write to
330 // it later.
331 WriteSocketLater();
332
[email protected]ff57bb82009-11-12 06:52:14333 return ERR_IO_PENDING;
334}
335
336bool FlipSession::CancelStream(flip::FlipStreamId stream_id) {
337 LOG(INFO) << "Cancelling stream " << stream_id;
338 if (!IsStreamActive(stream_id))
[email protected]aea80602009-09-18 00:55:08339 return false;
[email protected]93300672009-10-24 13:22:51340
341 // TODO(mbelshe): Write a method for tearing down a stream
342 // that cleans it out of the active list, the pending list,
343 // etc.
[email protected]a677f2b2009-11-22 00:43:00344 scoped_refptr<FlipStream> stream = active_streams_[stream_id];
[email protected]ff57bb82009-11-12 06:52:14345 DeactivateStream(stream_id);
[email protected]aea80602009-09-18 00:55:08346 return true;
347}
348
[email protected]ff57bb82009-11-12 06:52:14349bool FlipSession::IsStreamActive(flip::FlipStreamId stream_id) const {
[email protected]a677f2b2009-11-22 00:43:00350 return ContainsKey(active_streams_, stream_id);
[email protected]aea80602009-09-18 00:55:08351}
352
353LoadState FlipSession::GetLoadState() const {
354 // TODO(mbelshe): needs more work
355 return LOAD_STATE_CONNECTING;
356}
357
[email protected]18c53ae2009-10-01 18:18:52358void FlipSession::OnTCPConnect(int result) {
[email protected]aea80602009-09-18 00:55:08359 LOG(INFO) << "Flip socket connected (result=" << result << ")";
360
361 if (result != net::OK) {
362 net::Error err = static_cast<net::Error>(result);
363 CloseAllStreams(err);
[email protected]9f7c4fd2009-11-24 18:50:15364 session_->flip_session_pool()->Remove(this);
[email protected]aea80602009-09-18 00:55:08365 return;
366 }
367
368 // Adjust socket buffer sizes.
369 // FLIP uses one socket, and we want a really big buffer.
370 // This greatly helps on links with packet loss - we can even
371 // outperform Vista's dynamic window sizing algorithm.
372 // TODO(mbelshe): more study.
373 const int kSocketBufferSize = 512 * 1024;
374 connection_.socket()->SetReceiveBufferSize(kSocketBufferSize);
375 connection_.socket()->SetSendBufferSize(kSocketBufferSize);
376
[email protected]affe8fe2009-10-14 20:06:05377 if (use_ssl_) {
[email protected]18c53ae2009-10-01 18:18:52378 // Add a SSL socket on top of our existing transport socket.
379 ClientSocket* socket = connection_.release_socket();
380 // TODO(mbelshe): Fix the hostname. This is BROKEN without having
381 // a real hostname.
382 socket = session_->socket_factory()->CreateSSLClientSocket(
383 socket, "" /* request_->url.HostNoBrackets() */ , ssl_config_);
384 connection_.set_socket(socket);
[email protected]5a05c47a2009-11-02 23:25:19385 // TODO(willchan): Plumb LoadLog into FLIP code.
386 int status = connection_.socket()->Connect(&ssl_connect_callback_, NULL);
[email protected]18c53ae2009-10-01 18:18:52387 CHECK(status == net::ERR_IO_PENDING);
388 } else {
389 connection_ready_ = true;
390
391 // Make sure we get any pending data sent.
392 WriteSocketLater();
393 // Start reading
394 ReadSocket();
395 }
396}
397
398void FlipSession::OnSSLConnect(int result) {
399 // TODO(mbelshe): We need to replicate the functionality of
400 // HttpNetworkTransaction::DoSSLConnectComplete here, where it calls
401 // HandleCertificateError() and such.
402 if (IsCertificateError(result))
403 result = OK; // TODO(mbelshe): pretend we're happy anyway.
404
[email protected]93300672009-10-24 13:22:51405 if (result == OK) {
406 connection_ready_ = true;
[email protected]18c53ae2009-10-01 18:18:52407
[email protected]93300672009-10-24 13:22:51408 // After we've connected, send any data to the server, and then issue
409 // our read.
410 WriteSocketLater();
411 ReadSocket();
412 } else {
413 NOTREACHED();
414 // TODO(mbelshe): handle the error case: could not connect
415 }
[email protected]aea80602009-09-18 00:55:08416}
417
418void FlipSession::OnReadComplete(int bytes_read) {
419 // Parse a frame. For now this code requires that the frame fit into our
420 // buffer (32KB).
421 // TODO(mbelshe): support arbitrarily large frames!
422
423 LOG(INFO) << "Flip socket read: " << bytes_read << " bytes";
424
425 read_pending_ = false;
426
427 if (bytes_read <= 0) {
428 // Session is tearing down.
429 net::Error err = static_cast<net::Error>(bytes_read);
430 CloseAllStreams(err);
[email protected]9f7c4fd2009-11-24 18:50:15431 session_->flip_session_pool()->Remove(this);
[email protected]aea80602009-09-18 00:55:08432 return;
433 }
434
435 char *data = read_buffer_->data();
436 while (bytes_read &&
437 flip_framer_.error_code() == flip::FlipFramer::FLIP_NO_ERROR) {
438 uint32 bytes_processed = flip_framer_.ProcessInput(data, bytes_read);
439 bytes_read -= bytes_processed;
440 data += bytes_processed;
441 if (flip_framer_.state() == flip::FlipFramer::FLIP_DONE)
442 flip_framer_.Reset();
443 }
444 // NOTE(mbelshe): Could cause circular callbacks. (when ReadSocket
445 // completes synchronously, calling OnReadComplete, etc). Should
446 // probably return to the message loop.
447 ReadSocket();
448}
449
450void FlipSession::OnWriteComplete(int result) {
451 DCHECK(write_pending_);
[email protected]bf2491a92009-11-29 16:39:48452 DCHECK(in_flight_write_.size());
453 DCHECK(result != 0); // This shouldn't happen for write.
454
[email protected]aea80602009-09-18 00:55:08455 write_pending_ = false;
456
457 LOG(INFO) << "Flip write complete (result=" << result << ")";
458
[email protected]93300672009-10-24 13:22:51459 if (result >= 0) {
[email protected]bf2491a92009-11-29 16:39:48460 // It should not be possible to have written more bytes than our
461 // in_flight_write_.
462 DCHECK_LE(result, in_flight_write_.buffer()->BytesRemaining());
[email protected]eebc0b42009-11-04 00:17:13463
[email protected]bf2491a92009-11-29 16:39:48464 in_flight_write_.buffer()->DidConsume(result);
[email protected]ff57bb82009-11-12 06:52:14465
[email protected]bf2491a92009-11-29 16:39:48466 // We only notify the stream when we've fully written the pending frame.
467 if (!in_flight_write_.buffer()->BytesRemaining()) {
468 scoped_refptr<FlipStream> stream = in_flight_write_.stream();
469 DCHECK(stream.get());
470
471 // Report the number of bytes written to the caller, but exclude the
472 // frame size overhead. NOTE: if this frame was compressed the reported
473 // bytes written is the compressed size, not the original size.
474 if (result > 0) {
475 result = in_flight_write_.buffer()->size();
476 DCHECK_GT(result, static_cast<int>(flip::FlipFrame::size()));
477 result -= static_cast<int>(flip::FlipFrame::size());
478 }
479 stream->OnWriteComplete(result);
480
481 // Cleanup the write which just completed.
482 in_flight_write_.release();
[email protected]ff57bb82009-11-12 06:52:14483 }
[email protected]aea80602009-09-18 00:55:08484
[email protected]93300672009-10-24 13:22:51485 // Write more data. We're already in a continuation, so we can
486 // go ahead and write it immediately (without going back to the
487 // message loop).
488 WriteSocketLater();
489 } else {
[email protected]6572f122009-11-30 18:23:13490 in_flight_write_.release();
[email protected]bf2491a92009-11-29 16:39:48491 // The stream is now errored. Close it down.
492 CloseAllStreams(static_cast<net::Error>(result));
493 // TODO(mbelshe): we need to cleanup the session here as well.
494 // Right now, but a read and a write can fail, and each will
495 // remove the session from the pool, which trips asserts. We
496 // need to cleanup the close logic so that we only call it
497 // once.
498 //session_->flip_session_pool()->Remove(this);
[email protected]93300672009-10-24 13:22:51499 }
[email protected]aea80602009-09-18 00:55:08500}
501
502void FlipSession::ReadSocket() {
503 if (read_pending_)
504 return;
505
[email protected]230cadd2009-09-22 16:33:59506 int bytes_read = connection_.socket()->Read(read_buffer_.get(),
507 kReadBufferSize,
[email protected]aea80602009-09-18 00:55:08508 &read_callback_);
509 switch (bytes_read) {
510 case 0:
511 // Socket is closed!
512 // TODO(mbelshe): Need to abort any active streams here.
513 DCHECK(!active_streams_.size());
514 return;
515 case net::ERR_IO_PENDING:
516 // Waiting for data. Nothing to do now.
517 read_pending_ = true;
518 return;
519 default:
520 // Data was read, process it.
521 // TODO(mbelshe): check that we can't get a recursive stack?
522 OnReadComplete(bytes_read);
523 break;
524 }
525}
526
527void FlipSession::WriteSocketLater() {
528 if (delayed_write_pending_)
529 return;
530
531 delayed_write_pending_ = true;
532 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]18c53ae2009-10-01 18:18:52533 this, &FlipSession::WriteSocket));
[email protected]aea80602009-09-18 00:55:08534}
535
536void FlipSession::WriteSocket() {
537 // This function should only be called via WriteSocketLater.
538 DCHECK(delayed_write_pending_);
539 delayed_write_pending_ = false;
540
541 // If the socket isn't connected yet, just wait; we'll get called
542 // again when the socket connection completes.
[email protected]18c53ae2009-10-01 18:18:52543 if (!connection_ready_)
[email protected]aea80602009-09-18 00:55:08544 return;
545
546 if (write_pending_) // Another write is in progress still.
547 return;
548
[email protected]eebc0b42009-11-04 00:17:13549 // Loop sending frames until we've sent everything or until the write
550 // returns error (or ERR_IO_PENDING).
[email protected]bf2491a92009-11-29 16:39:48551 while (in_flight_write_.buffer() || queue_.size()) {
552 if (!in_flight_write_.buffer()) {
553 // Grab the next FlipFrame to send.
554 FlipIOBuffer next_buffer = queue_.top();
555 queue_.pop();
[email protected]aea80602009-09-18 00:55:08556
[email protected]bf2491a92009-11-29 16:39:48557 // We've deferred compression until just before we write it to the socket,
558 // which is now. At this time, we don't compress our data frames.
559 flip::FlipFrame uncompressed_frame(next_buffer.buffer()->data(), false);
560 size_t size;
561 if (uncompressed_frame.is_control_frame()) {
562 scoped_ptr<flip::FlipFrame> compressed_frame(
563 flip_framer_.CompressFrame(&uncompressed_frame));
564 size = compressed_frame->length() + flip::FlipFrame::size();
[email protected]aea80602009-09-18 00:55:08565
[email protected]bf2491a92009-11-29 16:39:48566 DCHECK(size > 0);
[email protected]eebc0b42009-11-04 00:17:13567
[email protected]bf2491a92009-11-29 16:39:48568 // TODO(mbelshe): We have too much copying of data here.
569 IOBufferWithSize* buffer = new IOBufferWithSize(size);
570 memcpy(buffer->data(), compressed_frame->data(), size);
[email protected]eebc0b42009-11-04 00:17:13571
[email protected]bf2491a92009-11-29 16:39:48572 // Attempt to send the frame.
573 in_flight_write_ = FlipIOBuffer(buffer, size, 0, next_buffer.stream());
574 } else {
575 size = uncompressed_frame.length() + flip::FlipFrame::size();
576 in_flight_write_ = next_buffer;
577 }
[email protected]2931238b2009-11-19 01:19:51578 } else {
[email protected]bf2491a92009-11-29 16:39:48579 DCHECK(in_flight_write_.buffer()->BytesRemaining());
[email protected]2931238b2009-11-19 01:19:51580 }
581
[email protected]93300672009-10-24 13:22:51582 write_pending_ = true;
[email protected]aea80602009-09-18 00:55:08583 int rv = connection_.socket()->Write(in_flight_write_.buffer(),
[email protected]bf2491a92009-11-29 16:39:48584 in_flight_write_.buffer()->BytesRemaining(), &write_callback_);
[email protected]93300672009-10-24 13:22:51585 if (rv == net::ERR_IO_PENDING)
[email protected]aea80602009-09-18 00:55:08586 break;
[email protected]eebc0b42009-11-04 00:17:13587
588 // We sent the frame successfully.
[email protected]93300672009-10-24 13:22:51589 OnWriteComplete(rv);
[email protected]eebc0b42009-11-04 00:17:13590
591 // TODO(mbelshe): Test this error case. Maybe we should mark the socket
592 // as in an error state.
593 if (rv < 0)
594 break;
[email protected]aea80602009-09-18 00:55:08595 }
596}
597
598void FlipSession::CloseAllStreams(net::Error code) {
599 LOG(INFO) << "Closing all FLIP Streams";
600
601 static StatsCounter abandoned_streams("flip.abandoned_streams");
602 static StatsCounter abandoned_push_streams("flip.abandoned_push_streams");
603
604 if (active_streams_.size()) {
605 abandoned_streams.Add(active_streams_.size());
606
607 // Create a copy of the list, since aborting streams can invalidate
608 // our list.
[email protected]cd314c822009-10-29 03:13:06609 FlipStream** list = new FlipStream*[active_streams_.size()];
[email protected]aea80602009-09-18 00:55:08610 ActiveStreamMap::const_iterator it;
611 int index = 0;
612 for (it = active_streams_.begin(); it != active_streams_.end(); ++it)
613 list[index++] = it->second;
614
615 // Issue the aborts.
616 for (--index; index >= 0; index--) {
[email protected]7f78b592009-11-06 17:46:56617 LOG(ERROR) << "ABANDONED (stream_id=" << list[index]->stream_id()
618 << "): " << list[index]->path();
[email protected]bf2491a92009-11-29 16:39:48619 list[index]->OnClose(code);
[email protected]aea80602009-09-18 00:55:08620 }
621
622 // Clear out anything pending.
623 active_streams_.clear();
624
[email protected]289bd7f2009-10-29 17:32:40625 delete[] list;
[email protected]aea80602009-09-18 00:55:08626 }
627
628 if (pushed_streams_.size()) {
629 abandoned_push_streams.Add(pushed_streams_.size());
630 pushed_streams_.clear();
631 }
632}
633
634int FlipSession::GetNewStreamId() {
635 int id = stream_hi_water_mark_;
636 stream_hi_water_mark_ += 2;
637 if (stream_hi_water_mark_ > 0x7fff)
638 stream_hi_water_mark_ = 1;
639 return id;
640}
641
[email protected]a677f2b2009-11-22 00:43:00642void FlipSession::ActivateStream(FlipStream* stream) {
643 const flip::FlipStreamId id = stream->stream_id();
[email protected]aea80602009-09-18 00:55:08644 DCHECK(!IsStreamActive(id));
645
[email protected]aea80602009-09-18 00:55:08646 active_streams_[id] = stream;
[email protected]aea80602009-09-18 00:55:08647}
648
[email protected]ba051e312009-10-07 22:12:33649void FlipSession::DeactivateStream(flip::FlipStreamId id) {
[email protected]aea80602009-09-18 00:55:08650 DCHECK(IsStreamActive(id));
651
652 // Verify it is not on the pushed_streams_ list.
653 ActiveStreamList::iterator it;
654 for (it = pushed_streams_.begin(); it != pushed_streams_.end(); ++it) {
[email protected]a677f2b2009-11-22 00:43:00655 scoped_refptr<FlipStream> curr = *it;
656 if (id == curr->stream_id()) {
[email protected]aea80602009-09-18 00:55:08657 pushed_streams_.erase(it);
658 break;
659 }
660 }
661
662 active_streams_.erase(id);
663}
664
[email protected]a677f2b2009-11-22 00:43:00665scoped_refptr<FlipStream> FlipSession::GetPushStream(const std::string& path) {
[email protected]aea80602009-09-18 00:55:08666 static StatsCounter used_push_streams("flip.claimed_push_streams");
667
668 LOG(INFO) << "Looking for push stream: " << path;
669
[email protected]a677f2b2009-11-22 00:43:00670 scoped_refptr<FlipStream> stream;
671
[email protected]aea80602009-09-18 00:55:08672 // We just walk a linear list here.
673 ActiveStreamList::iterator it;
674 for (it = pushed_streams_.begin(); it != pushed_streams_.end(); ++it) {
[email protected]a677f2b2009-11-22 00:43:00675 stream = *it;
676 if (path == stream->path()) {
677 CHECK(stream->pushed());
[email protected]aea80602009-09-18 00:55:08678 pushed_streams_.erase(it);
679 used_push_streams.Increment();
680 LOG(INFO) << "Push Stream Claim for: " << path;
[email protected]a677f2b2009-11-22 00:43:00681 break;
[email protected]aea80602009-09-18 00:55:08682 }
683 }
[email protected]a677f2b2009-11-22 00:43:00684
685 return stream;
[email protected]aea80602009-09-18 00:55:08686}
687
688void FlipSession::OnError(flip::FlipFramer* framer) {
[email protected]2931238b2009-11-19 01:19:51689 LOG(ERROR) << "FlipSession error: " << framer->error_code();
[email protected]aea80602009-09-18 00:55:08690 CloseAllStreams(net::ERR_UNEXPECTED);
[email protected]18c53ae2009-10-01 18:18:52691 Release();
[email protected]aea80602009-09-18 00:55:08692}
693
694void FlipSession::OnStreamFrameData(flip::FlipStreamId stream_id,
695 const char* data,
[email protected]aeac1e42009-10-10 00:26:01696 size_t len) {
[email protected]aea80602009-09-18 00:55:08697 LOG(INFO) << "Flip data for stream " << stream_id << ", " << len << " bytes";
698 bool valid_stream = IsStreamActive(stream_id);
699 if (!valid_stream) {
[email protected]affe8fe2009-10-14 20:06:05700 LOG(WARNING) << "Received data frame for invalid stream " << stream_id;
[email protected]aea80602009-09-18 00:55:08701 return;
702 }
[email protected]93300672009-10-24 13:22:51703
[email protected]a677f2b2009-11-22 00:43:00704 scoped_refptr<FlipStream> stream = active_streams_[stream_id];
705 bool success = stream->OnDataReceived(data, len);
706 // |len| == 0 implies a closed stream.
707 if (!success || !len)
708 DeactivateStream(stream_id);
[email protected]aea80602009-09-18 00:55:08709}
710
711void FlipSession::OnSyn(const flip::FlipSynStreamControlFrame* frame,
712 const flip::FlipHeaderBlock* headers) {
713 flip::FlipStreamId stream_id = frame->stream_id();
714
[email protected]93300672009-10-24 13:22:51715 // Server-initiated streams should have even sequence numbers.
[email protected]aea80602009-09-18 00:55:08716 if ((stream_id & 0x1) != 0) {
[email protected]18c53ae2009-10-01 18:18:52717 LOG(ERROR) << "Received invalid OnSyn stream id " << stream_id;
[email protected]aea80602009-09-18 00:55:08718 return;
719 }
720
721 if (IsStreamActive(stream_id)) {
[email protected]18c53ae2009-10-01 18:18:52722 LOG(ERROR) << "Received OnSyn for active stream " << stream_id;
[email protected]aea80602009-09-18 00:55:08723 return;
724 }
725
[email protected]a677f2b2009-11-22 00:43:00726 LOG(INFO) << "FlipSession: SynReply received for stream: " << stream_id;
727
728 DCHECK(ContainsKey(*headers, "version"));
729 DCHECK(ContainsKey(*headers, "status"));
[email protected]967dd5412009-11-21 23:33:30730 LOG(INFO) << "FLIP SYN_REPLY RESPONSE HEADERS -----------------------";
731 DumpFlipHeaders(*headers);
732
[email protected]18c53ae2009-10-01 18:18:52733 // TODO(mbelshe): DCHECK that this is a GET method?
734
[email protected]a677f2b2009-11-22 00:43:00735 const std::string& path = ContainsKey(*headers, "path") ?
736 headers->find("path")->second : "";
737
[email protected]aea80602009-09-18 00:55:08738 // Verify that the response had a URL for us.
[email protected]a677f2b2009-11-22 00:43:00739 DCHECK(!path.empty());
740 if (path.empty()) {
[email protected]aea80602009-09-18 00:55:08741 LOG(WARNING) << "Pushed stream did not contain a path.";
[email protected]aea80602009-09-18 00:55:08742 return;
743 }
[email protected]18c53ae2009-10-01 18:18:52744
[email protected]a677f2b2009-11-22 00:43:00745 scoped_refptr<FlipStream> stream;
746
[email protected]18c53ae2009-10-01 18:18:52747 // Check if we already have a delegate awaiting this stream.
[email protected]2931238b2009-11-19 01:19:51748 PendingStreamMap::iterator it;
[email protected]a677f2b2009-11-22 00:43:00749 it = pending_streams_.find(path);
[email protected]18c53ae2009-10-01 18:18:52750 if (it != pending_streams_.end()) {
[email protected]a677f2b2009-11-22 00:43:00751 stream = it->second;
[email protected]18c53ae2009-10-01 18:18:52752 pending_streams_.erase(it);
[email protected]a677f2b2009-11-22 00:43:00753 if (stream)
[email protected]18c53ae2009-10-01 18:18:52754 pushed_streams_.push_back(stream);
755 } else {
756 pushed_streams_.push_back(stream);
757 }
[email protected]aea80602009-09-18 00:55:08758
[email protected]a677f2b2009-11-22 00:43:00759 if (stream) {
760 CHECK(stream->pushed());
761 CHECK(stream->stream_id() == 0);
762 stream->set_stream_id(stream_id);
763 } else {
764 stream = new FlipStream(this, stream_id, true);
765 }
766
767 // Activate a stream and parse the headers.
768 ActivateStream(stream);
769
770 stream->set_path(path);
771
772 // TODO(mbelshe): For now we convert from our nice hash map back
773 // to a string of headers; this is because the HttpResponseInfo
774 // is a bit rigid for its http (non-flip) design.
775 const HttpResponseInfo response = FlipHeadersToHttpResponse(*headers);
776 stream->OnResponseReceived(response);
777
[email protected]aea80602009-09-18 00:55:08778 LOG(INFO) << "Got pushed stream for " << stream->path();
779
780 static StatsCounter push_requests("flip.pushed_streams");
781 push_requests.Increment();
782}
783
784void FlipSession::OnSynReply(const flip::FlipSynReplyControlFrame* frame,
785 const flip::FlipHeaderBlock* headers) {
786 DCHECK(headers);
787 flip::FlipStreamId stream_id = frame->stream_id();
788 bool valid_stream = IsStreamActive(stream_id);
789 if (!valid_stream) {
[email protected]affe8fe2009-10-14 20:06:05790 LOG(WARNING) << "Received SYN_REPLY for invalid stream " << stream_id;
[email protected]aea80602009-09-18 00:55:08791 return;
792 }
793
[email protected]18c53ae2009-10-01 18:18:52794 // We record content declared as being pushed so that we don't
795 // request a duplicate stream which is already scheduled to be
796 // sent to us.
797 flip::FlipHeaderBlock::const_iterator it;
798 it = headers->find("X-Associated-Content");
799 if (it != headers->end()) {
800 const std::string& content = it->second;
801 std::string::size_type start = 0;
802 std::string::size_type end = 0;
803 do {
804 end = content.find("||", start);
[email protected]ba051e312009-10-07 22:12:33805 if (end == std::string::npos)
[email protected]18c53ae2009-10-01 18:18:52806 end = content.length();
807 std::string url = content.substr(start, end - start);
808 std::string::size_type pos = url.find("??");
[email protected]ba051e312009-10-07 22:12:33809 if (pos == std::string::npos)
[email protected]18c53ae2009-10-01 18:18:52810 break;
[email protected]ba051e312009-10-07 22:12:33811 url = url.substr(pos + 2);
[email protected]18c53ae2009-10-01 18:18:52812 GURL gurl(url);
[email protected]84b562f2009-11-20 18:25:30813 std::string path = gurl.PathForRequest();
814 if (path.length())
815 pending_streams_[path] = NULL;
816 else
817 LOG(INFO) << "Invalid X-Associated-Content path: " << url;
[email protected]18c53ae2009-10-01 18:18:52818 start = end + 2;
[email protected]84b562f2009-11-20 18:25:30819 } while (start < content.length());
[email protected]18c53ae2009-10-01 18:18:52820 }
821
[email protected]a677f2b2009-11-22 00:43:00822 scoped_refptr<FlipStream> stream = active_streams_[stream_id];
823 CHECK(stream->stream_id() == stream_id);
824 const HttpResponseInfo response = FlipHeadersToHttpResponse(*headers);
825 stream->OnResponseReceived(response);
[email protected]aea80602009-09-18 00:55:08826}
827
828void FlipSession::OnControl(const flip::FlipControlFrame* frame) {
829 flip::FlipHeaderBlock headers;
[email protected]aea80602009-09-18 00:55:08830 uint32 type = frame->type();
831 if (type == flip::SYN_STREAM || type == flip::SYN_REPLY) {
[email protected]22eb9682009-11-06 21:51:58832 if (!flip_framer_.ParseHeaderBlock(frame, &headers)) {
[email protected]aea80602009-09-18 00:55:08833 LOG(WARNING) << "Could not parse Flip Control Frame Header";
[email protected]84b562f2009-11-20 18:25:30834 // TODO(mbelshe): Error the session?
[email protected]aea80602009-09-18 00:55:08835 return;
836 }
837 }
838
839 switch (type) {
840 case flip::SYN_STREAM:
841 LOG(INFO) << "Flip SynStream for stream " << frame->stream_id();
842 OnSyn(reinterpret_cast<const flip::FlipSynStreamControlFrame*>(frame),
843 &headers);
844 break;
845 case flip::SYN_REPLY:
846 LOG(INFO) << "Flip SynReply for stream " << frame->stream_id();
847 OnSynReply(
848 reinterpret_cast<const flip::FlipSynReplyControlFrame*>(frame),
849 &headers);
850 break;
851 case flip::FIN_STREAM:
852 LOG(INFO) << "Flip Fin for stream " << frame->stream_id();
853 OnFin(reinterpret_cast<const flip::FlipFinStreamControlFrame*>(frame));
854 break;
855 default:
856 DCHECK(false); // Error!
857 }
858}
859
860void FlipSession::OnFin(const flip::FlipFinStreamControlFrame* frame) {
861 flip::FlipStreamId stream_id = frame->stream_id();
862 bool valid_stream = IsStreamActive(stream_id);
863 if (!valid_stream) {
864 LOG(WARNING) << "Received FIN for invalid stream" << stream_id;
865 return;
866 }
[email protected]a677f2b2009-11-22 00:43:00867 scoped_refptr<FlipStream> stream = active_streams_[stream_id];
868 CHECK(stream->stream_id() == stream_id);
[email protected]93300672009-10-24 13:22:51869 if (frame->status() == 0) {
[email protected]a677f2b2009-11-22 00:43:00870 stream->OnDataReceived(NULL, 0);
[email protected]aea80602009-09-18 00:55:08871 } else {
[email protected]93300672009-10-24 13:22:51872 LOG(ERROR) << "Flip stream closed: " << frame->status();
873 // TODO(mbelshe): Map from Flip-protocol errors to something sensical.
874 // For now, it doesn't matter much - it is a protocol error.
[email protected]a677f2b2009-11-22 00:43:00875 stream->OnClose(ERR_FAILED);
[email protected]aea80602009-09-18 00:55:08876 }
877
[email protected]a677f2b2009-11-22 00:43:00878 DeactivateStream(stream_id);
[email protected]aea80602009-09-18 00:55:08879}
880
[email protected]aea80602009-09-18 00:55:08881} // namespace net