blob: cede942eee80efc9a55cdde9d4901b4a850fa9be [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
172 session_->flip_session_pool()->Remove(this);
[email protected]aea80602009-09-18 00:55:08173}
174
175net::Error FlipSession::Connect(const std::string& group_name,
176 const HostResolver::RequestInfo& host,
177 int priority) {
[email protected]ff57bb82009-11-12 06:52:14178 DCHECK(priority >= FLIP_PRIORITY_HIGHEST && priority < FLIP_PRIORITY_LOWEST);
[email protected]aea80602009-09-18 00:55:08179
180 // If the connect process is started, let the caller continue.
181 if (connection_started_)
182 return net::OK;
183
184 connection_started_ = true;
185
186 static StatsCounter flip_sessions("flip.sessions");
187 flip_sessions.Increment();
188
189 int rv = connection_.Init(group_name, host, priority, &connect_callback_,
190 session_->tcp_socket_pool(), NULL);
191
192 // If the connect is pending, we still return ok. The APIs enqueue
193 // work until after the connect completes asynchronously later.
194 if (rv == net::ERR_IO_PENDING)
195 return net::OK;
196 return static_cast<net::Error>(rv);
197}
198
[email protected]a677f2b2009-11-22 00:43:00199scoped_refptr<FlipStream> FlipSession::GetOrCreateStream(
200 const HttpRequestInfo& request,
201 const UploadDataStream* upload_data) {
202 const GURL& url = request.url;
203 const std::string& path = url.PathForRequest();
[email protected]aea80602009-09-18 00:55:08204
[email protected]a677f2b2009-11-22 00:43:00205 scoped_refptr<FlipStream> stream;
[email protected]aea80602009-09-18 00:55:08206
207 // Check if we have a push stream for this path.
[email protected]a677f2b2009-11-22 00:43:00208 if (request.method == "GET") {
[email protected]aea80602009-09-18 00:55:08209 stream = GetPushStream(path);
[email protected]a677f2b2009-11-22 00:43:00210 if (stream)
211 return stream;
[email protected]aea80602009-09-18 00:55:08212 }
213
[email protected]18c53ae2009-10-01 18:18:52214 // Check if we have a pending push stream for this url.
[email protected]2931238b2009-11-19 01:19:51215 PendingStreamMap::iterator it;
[email protected]a677f2b2009-11-22 00:43:00216 it = pending_streams_.find(path);
[email protected]18c53ae2009-10-01 18:18:52217 if (it != pending_streams_.end()) {
[email protected]a677f2b2009-11-22 00:43:00218 DCHECK(!it->second);
219 // Server will assign a stream id when the push stream arrives. Use 0 for
220 // now.
221 FlipStream* stream = new FlipStream(this, 0, true);
222 stream->set_path(path);
223 it->second = stream;
224 return it->second;
[email protected]18c53ae2009-10-01 18:18:52225 }
226
[email protected]a677f2b2009-11-22 00:43:00227 const flip::FlipStreamId stream_id = GetNewStreamId();
[email protected]aea80602009-09-18 00:55:08228
[email protected]a677f2b2009-11-22 00:43:00229 // If we still don't have a stream, activate one now.
230 stream = new FlipStream(this, stream_id, false);
231 stream->set_priority(request.priority);
232 stream->set_path(path);
233 ActivateStream(stream);
234
235 LOG(INFO) << "FlipStream: Creating stream " << stream_id << " for " << url;
[email protected]aea80602009-09-18 00:55:08236
237 // TODO(mbelshe): Optimize memory allocations
[email protected]a677f2b2009-11-22 00:43:00238 int priority = request.priority;
[email protected]aea80602009-09-18 00:55:08239
240 // Hack for the priorities
241 // TODO(mbelshe): These need to be plumbed through the Http Network Stack.
242 if (path.find(".css") != path.npos) {
243 priority = 1;
244 } else if (path.find(".html") != path.npos) {
245 priority = 0;
246 } else if (path.find(".js") != path.npos) {
247 priority = 1;
248 } else {
249 priority = 3;
250 }
251
252 DCHECK(priority >= FLIP_PRIORITY_HIGHEST &&
253 priority <= FLIP_PRIORITY_LOWEST);
254
255 // Convert from HttpRequestHeaders to Flip Headers.
256 flip::FlipHeaderBlock headers;
[email protected]a677f2b2009-11-22 00:43:00257 CreateFlipHeadersFromHttpRequest(request, &headers);
[email protected]aea80602009-09-18 00:55:08258
[email protected]72552f02009-10-28 15:25:01259 flip::FlipControlFlags flags = flip::CONTROL_FLAG_NONE;
[email protected]a677f2b2009-11-22 00:43:00260 if (!request.upload_data || !upload_data->size())
[email protected]72552f02009-10-28 15:25:01261 flags = flip::CONTROL_FLAG_FIN;
262
[email protected]aea80602009-09-18 00:55:08263 // Create a SYN_STREAM packet and add to the output queue.
[email protected]22eb9682009-11-06 21:51:58264 scoped_ptr<flip::FlipSynStreamControlFrame> syn_frame(
265 flip_framer_.CreateSynStream(stream_id, priority, flags, false,
266 &headers));
267 int length = flip::FlipFrame::size() + syn_frame->length();
268 IOBufferWithSize* buffer = new IOBufferWithSize(length);
269 memcpy(buffer->data(), syn_frame->data(), length);
[email protected]eebc0b42009-11-04 00:17:13270 queue_.push(FlipIOBuffer(buffer, priority, stream));
[email protected]aea80602009-09-18 00:55:08271
272 static StatsCounter flip_requests("flip.requests");
273 flip_requests.Increment();
274
[email protected]a677f2b2009-11-22 00:43:00275 LOG(INFO) << "FETCHING: " << request.url.spec();
[email protected]aea80602009-09-18 00:55:08276
[email protected]967dd5412009-11-21 23:33:30277 LOG(INFO) << "FLIP SYN_STREAM HEADERS ----------------------------------";
278 DumpFlipHeaders(headers);
[email protected]aea80602009-09-18 00:55:08279
[email protected]aea80602009-09-18 00:55:08280 // Schedule to write to the socket after we've made it back
281 // to the message loop so that we can aggregate multiple
282 // requests.
283 // TODO(mbelshe): Should we do the "first" request immediately?
284 // maybe we should only 'do later' for subsequent
285 // requests.
286 WriteSocketLater();
287
[email protected]a677f2b2009-11-22 00:43:00288 return stream;
[email protected]aea80602009-09-18 00:55:08289}
290
[email protected]ff57bb82009-11-12 06:52:14291int FlipSession::WriteStreamData(flip::FlipStreamId stream_id,
292 net::IOBuffer* data, int len) {
[email protected]967dd5412009-11-21 23:33:30293 LOG(INFO) << "Writing Stream Data for stream " << stream_id << " (" << len
294 << " bytes)";
[email protected]ff57bb82009-11-12 06:52:14295 const int kMss = 1430; // This is somewhat arbitrary and not really fixed,
296 // but it will always work reasonably with ethernet.
297 // Chop the world into 2-packet chunks. This is somewhat arbitrary, but
298 // is reasonably small and ensures that we elicit ACKs quickly from TCP
299 // (because TCP tries to only ACK every other packet).
300 const int kMaxFlipFrameChunkSize = (2 * kMss) - flip::FlipFrame::size();
301
302 // Find our stream
303 DCHECK(IsStreamActive(stream_id));
[email protected]a677f2b2009-11-22 00:43:00304 scoped_refptr<FlipStream> stream = active_streams_[stream_id];
305 CHECK(stream->stream_id() == stream_id);
[email protected]ff57bb82009-11-12 06:52:14306 if (!stream)
307 return ERR_INVALID_FLIP_STREAM;
308
[email protected]2931238b2009-11-19 01:19:51309 // TODO(mbelshe): Setting of the FIN is assuming that the caller will pass
310 // all data to write in a single chunk. Is this always true?
311
[email protected]ff57bb82009-11-12 06:52:14312 // Set the flags on the upload.
313 flip::FlipDataFlags flags = flip::DATA_FLAG_FIN;
314 if (len > kMaxFlipFrameChunkSize) {
315 len = kMaxFlipFrameChunkSize;
316 flags = flip::DATA_FLAG_NONE;
317 }
318
319 // TODO(mbelshe): reduce memory copies here.
320 scoped_ptr<flip::FlipDataFrame> frame(
321 flip_framer_.CreateDataFrame(stream_id, data->data(), len, flags));
322 int length = flip::FlipFrame::size() + frame->length();
323 IOBufferWithSize* buffer = new IOBufferWithSize(length);
324 memcpy(buffer->data(), frame->data(), length);
[email protected]a677f2b2009-11-22 00:43:00325 queue_.push(FlipIOBuffer(buffer, stream->priority(), stream));
[email protected]2931238b2009-11-19 01:19:51326
327 // Whenever we queue onto the socket we need to ensure that we will write to
328 // it later.
329 WriteSocketLater();
330
[email protected]ff57bb82009-11-12 06:52:14331 return ERR_IO_PENDING;
332}
333
334bool FlipSession::CancelStream(flip::FlipStreamId stream_id) {
335 LOG(INFO) << "Cancelling stream " << stream_id;
336 if (!IsStreamActive(stream_id))
[email protected]aea80602009-09-18 00:55:08337 return false;
[email protected]93300672009-10-24 13:22:51338
339 // TODO(mbelshe): Write a method for tearing down a stream
340 // that cleans it out of the active list, the pending list,
341 // etc.
[email protected]a677f2b2009-11-22 00:43:00342 scoped_refptr<FlipStream> stream = active_streams_[stream_id];
[email protected]ff57bb82009-11-12 06:52:14343 DeactivateStream(stream_id);
[email protected]aea80602009-09-18 00:55:08344 return true;
345}
346
[email protected]ff57bb82009-11-12 06:52:14347bool FlipSession::IsStreamActive(flip::FlipStreamId stream_id) const {
[email protected]a677f2b2009-11-22 00:43:00348 return ContainsKey(active_streams_, stream_id);
[email protected]aea80602009-09-18 00:55:08349}
350
351LoadState FlipSession::GetLoadState() const {
352 // TODO(mbelshe): needs more work
353 return LOAD_STATE_CONNECTING;
354}
355
[email protected]18c53ae2009-10-01 18:18:52356void FlipSession::OnTCPConnect(int result) {
[email protected]aea80602009-09-18 00:55:08357 LOG(INFO) << "Flip socket connected (result=" << result << ")";
358
359 if (result != net::OK) {
360 net::Error err = static_cast<net::Error>(result);
361 CloseAllStreams(err);
362 this->Release();
363 return;
364 }
365
366 // Adjust socket buffer sizes.
367 // FLIP uses one socket, and we want a really big buffer.
368 // This greatly helps on links with packet loss - we can even
369 // outperform Vista's dynamic window sizing algorithm.
370 // TODO(mbelshe): more study.
371 const int kSocketBufferSize = 512 * 1024;
372 connection_.socket()->SetReceiveBufferSize(kSocketBufferSize);
373 connection_.socket()->SetSendBufferSize(kSocketBufferSize);
374
[email protected]affe8fe2009-10-14 20:06:05375 if (use_ssl_) {
[email protected]18c53ae2009-10-01 18:18:52376 // Add a SSL socket on top of our existing transport socket.
377 ClientSocket* socket = connection_.release_socket();
378 // TODO(mbelshe): Fix the hostname. This is BROKEN without having
379 // a real hostname.
380 socket = session_->socket_factory()->CreateSSLClientSocket(
381 socket, "" /* request_->url.HostNoBrackets() */ , ssl_config_);
382 connection_.set_socket(socket);
[email protected]5a05c47a2009-11-02 23:25:19383 // TODO(willchan): Plumb LoadLog into FLIP code.
384 int status = connection_.socket()->Connect(&ssl_connect_callback_, NULL);
[email protected]18c53ae2009-10-01 18:18:52385 CHECK(status == net::ERR_IO_PENDING);
386 } else {
387 connection_ready_ = true;
388
389 // Make sure we get any pending data sent.
390 WriteSocketLater();
391 // Start reading
392 ReadSocket();
393 }
394}
395
396void FlipSession::OnSSLConnect(int result) {
397 // TODO(mbelshe): We need to replicate the functionality of
398 // HttpNetworkTransaction::DoSSLConnectComplete here, where it calls
399 // HandleCertificateError() and such.
400 if (IsCertificateError(result))
401 result = OK; // TODO(mbelshe): pretend we're happy anyway.
402
[email protected]93300672009-10-24 13:22:51403 if (result == OK) {
404 connection_ready_ = true;
[email protected]18c53ae2009-10-01 18:18:52405
[email protected]93300672009-10-24 13:22:51406 // After we've connected, send any data to the server, and then issue
407 // our read.
408 WriteSocketLater();
409 ReadSocket();
410 } else {
411 NOTREACHED();
412 // TODO(mbelshe): handle the error case: could not connect
413 }
[email protected]aea80602009-09-18 00:55:08414}
415
416void FlipSession::OnReadComplete(int bytes_read) {
417 // Parse a frame. For now this code requires that the frame fit into our
418 // buffer (32KB).
419 // TODO(mbelshe): support arbitrarily large frames!
420
421 LOG(INFO) << "Flip socket read: " << bytes_read << " bytes";
422
423 read_pending_ = false;
424
425 if (bytes_read <= 0) {
426 // Session is tearing down.
427 net::Error err = static_cast<net::Error>(bytes_read);
428 CloseAllStreams(err);
429 this->Release();
430 return;
431 }
432
433 char *data = read_buffer_->data();
434 while (bytes_read &&
435 flip_framer_.error_code() == flip::FlipFramer::FLIP_NO_ERROR) {
436 uint32 bytes_processed = flip_framer_.ProcessInput(data, bytes_read);
437 bytes_read -= bytes_processed;
438 data += bytes_processed;
439 if (flip_framer_.state() == flip::FlipFramer::FLIP_DONE)
440 flip_framer_.Reset();
441 }
442 // NOTE(mbelshe): Could cause circular callbacks. (when ReadSocket
443 // completes synchronously, calling OnReadComplete, etc). Should
444 // probably return to the message loop.
445 ReadSocket();
446}
447
448void FlipSession::OnWriteComplete(int result) {
449 DCHECK(write_pending_);
450 write_pending_ = false;
451
452 LOG(INFO) << "Flip write complete (result=" << result << ")";
453
[email protected]93300672009-10-24 13:22:51454 if (result >= 0) {
[email protected]ff57bb82009-11-12 06:52:14455 // TODO(mbelshe) Verify that we wrote ALL the bytes of the frame.
[email protected]eebc0b42009-11-04 00:17:13456 // The code current is broken in the case of a partial write.
457 DCHECK_EQ(static_cast<size_t>(result), in_flight_write_.size());
458
[email protected]ff57bb82009-11-12 06:52:14459 // We only notify the stream when we've fully written the pending flip
460 // frame.
[email protected]a677f2b2009-11-22 00:43:00461 scoped_refptr<FlipStream> stream = in_flight_write_.stream();
462 DCHECK(stream.get());
[email protected]ff57bb82009-11-12 06:52:14463
[email protected]a677f2b2009-11-22 00:43:00464 if (!stream->cancelled()) {
465 // Report the number of bytes written to the caller, but exclude the
466 // frame size overhead.
467 if (result > 0) {
468 DCHECK(result > static_cast<int>(flip::FlipFrame::size()));
469 result -= static_cast<int>(flip::FlipFrame::size());
470 }
471 stream->OnWriteComplete(result);
[email protected]ff57bb82009-11-12 06:52:14472 }
[email protected]ff57bb82009-11-12 06:52:14473
[email protected]93300672009-10-24 13:22:51474 // Cleanup the write which just completed.
475 in_flight_write_.release();
[email protected]aea80602009-09-18 00:55:08476
[email protected]93300672009-10-24 13:22:51477 // Write more data. We're already in a continuation, so we can
478 // go ahead and write it immediately (without going back to the
479 // message loop).
480 WriteSocketLater();
481 } else {
482 // TODO(mbelshe): Deal with result < 0 error case.
483 NOTIMPLEMENTED();
484 }
[email protected]aea80602009-09-18 00:55:08485}
486
487void FlipSession::ReadSocket() {
488 if (read_pending_)
489 return;
490
[email protected]230cadd2009-09-22 16:33:59491 int bytes_read = connection_.socket()->Read(read_buffer_.get(),
492 kReadBufferSize,
[email protected]aea80602009-09-18 00:55:08493 &read_callback_);
494 switch (bytes_read) {
495 case 0:
496 // Socket is closed!
497 // TODO(mbelshe): Need to abort any active streams here.
498 DCHECK(!active_streams_.size());
499 return;
500 case net::ERR_IO_PENDING:
501 // Waiting for data. Nothing to do now.
502 read_pending_ = true;
503 return;
504 default:
505 // Data was read, process it.
506 // TODO(mbelshe): check that we can't get a recursive stack?
507 OnReadComplete(bytes_read);
508 break;
509 }
510}
511
512void FlipSession::WriteSocketLater() {
513 if (delayed_write_pending_)
514 return;
515
516 delayed_write_pending_ = true;
517 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]18c53ae2009-10-01 18:18:52518 this, &FlipSession::WriteSocket));
[email protected]aea80602009-09-18 00:55:08519}
520
521void FlipSession::WriteSocket() {
522 // This function should only be called via WriteSocketLater.
523 DCHECK(delayed_write_pending_);
524 delayed_write_pending_ = false;
525
526 // If the socket isn't connected yet, just wait; we'll get called
527 // again when the socket connection completes.
[email protected]18c53ae2009-10-01 18:18:52528 if (!connection_ready_)
[email protected]aea80602009-09-18 00:55:08529 return;
530
531 if (write_pending_) // Another write is in progress still.
532 return;
533
[email protected]eebc0b42009-11-04 00:17:13534 // Loop sending frames until we've sent everything or until the write
535 // returns error (or ERR_IO_PENDING).
[email protected]aea80602009-09-18 00:55:08536 while (queue_.size()) {
[email protected]eebc0b42009-11-04 00:17:13537 // Grab the next FlipFrame to send.
538 FlipIOBuffer next_buffer = queue_.top();
539 queue_.pop();
[email protected]aea80602009-09-18 00:55:08540
[email protected]eebc0b42009-11-04 00:17:13541 // We've deferred compression until just before we write it to the socket,
[email protected]2931238b2009-11-19 01:19:51542 // which is now. At this time, we don't compress our data frames.
[email protected]22eb9682009-11-06 21:51:58543 flip::FlipFrame uncompressed_frame(next_buffer.buffer()->data(), false);
[email protected]2931238b2009-11-19 01:19:51544 size_t size;
545 if (uncompressed_frame.is_control_frame()) {
546 scoped_ptr<flip::FlipFrame> compressed_frame(
547 flip_framer_.CompressFrame(&uncompressed_frame));
548 size = compressed_frame->length() + flip::FlipFrame::size();
[email protected]aea80602009-09-18 00:55:08549
[email protected]2931238b2009-11-19 01:19:51550 DCHECK(size > 0);
[email protected]eebc0b42009-11-04 00:17:13551
[email protected]2931238b2009-11-19 01:19:51552 // TODO(mbelshe): We have too much copying of data here.
553 IOBufferWithSize* buffer = new IOBufferWithSize(size);
554 memcpy(buffer->data(), compressed_frame->data(), size);
[email protected]eebc0b42009-11-04 00:17:13555
[email protected]2931238b2009-11-19 01:19:51556 // Attempt to send the frame.
557 in_flight_write_ = FlipIOBuffer(buffer, 0, next_buffer.stream());
558 } else {
559 size = uncompressed_frame.length() + flip::FlipFrame::size();
560 in_flight_write_ = next_buffer;
561 }
562
[email protected]93300672009-10-24 13:22:51563 write_pending_ = true;
[email protected]aea80602009-09-18 00:55:08564 int rv = connection_.socket()->Write(in_flight_write_.buffer(),
[email protected]eebc0b42009-11-04 00:17:13565 size, &write_callback_);
[email protected]93300672009-10-24 13:22:51566 if (rv == net::ERR_IO_PENDING)
[email protected]aea80602009-09-18 00:55:08567 break;
[email protected]eebc0b42009-11-04 00:17:13568
569 // We sent the frame successfully.
[email protected]93300672009-10-24 13:22:51570 OnWriteComplete(rv);
[email protected]eebc0b42009-11-04 00:17:13571
572 // TODO(mbelshe): Test this error case. Maybe we should mark the socket
573 // as in an error state.
574 if (rv < 0)
575 break;
[email protected]aea80602009-09-18 00:55:08576 }
577}
578
579void FlipSession::CloseAllStreams(net::Error code) {
580 LOG(INFO) << "Closing all FLIP Streams";
581
582 static StatsCounter abandoned_streams("flip.abandoned_streams");
583 static StatsCounter abandoned_push_streams("flip.abandoned_push_streams");
584
585 if (active_streams_.size()) {
586 abandoned_streams.Add(active_streams_.size());
587
588 // Create a copy of the list, since aborting streams can invalidate
589 // our list.
[email protected]cd314c822009-10-29 03:13:06590 FlipStream** list = new FlipStream*[active_streams_.size()];
[email protected]aea80602009-09-18 00:55:08591 ActiveStreamMap::const_iterator it;
592 int index = 0;
593 for (it = active_streams_.begin(); it != active_streams_.end(); ++it)
594 list[index++] = it->second;
595
596 // Issue the aborts.
597 for (--index; index >= 0; index--) {
[email protected]7f78b592009-11-06 17:46:56598 LOG(ERROR) << "ABANDONED (stream_id=" << list[index]->stream_id()
599 << "): " << list[index]->path();
[email protected]a677f2b2009-11-22 00:43:00600 list[index]->OnClose(ERR_ABORTED);
[email protected]aea80602009-09-18 00:55:08601 }
602
603 // Clear out anything pending.
604 active_streams_.clear();
605
[email protected]289bd7f2009-10-29 17:32:40606 delete[] list;
[email protected]aea80602009-09-18 00:55:08607 }
608
609 if (pushed_streams_.size()) {
610 abandoned_push_streams.Add(pushed_streams_.size());
611 pushed_streams_.clear();
612 }
613}
614
615int FlipSession::GetNewStreamId() {
616 int id = stream_hi_water_mark_;
617 stream_hi_water_mark_ += 2;
618 if (stream_hi_water_mark_ > 0x7fff)
619 stream_hi_water_mark_ = 1;
620 return id;
621}
622
[email protected]a677f2b2009-11-22 00:43:00623void FlipSession::ActivateStream(FlipStream* stream) {
624 const flip::FlipStreamId id = stream->stream_id();
[email protected]aea80602009-09-18 00:55:08625 DCHECK(!IsStreamActive(id));
626
[email protected]aea80602009-09-18 00:55:08627 active_streams_[id] = stream;
[email protected]aea80602009-09-18 00:55:08628}
629
[email protected]ba051e312009-10-07 22:12:33630void FlipSession::DeactivateStream(flip::FlipStreamId id) {
[email protected]aea80602009-09-18 00:55:08631 DCHECK(IsStreamActive(id));
632
633 // Verify it is not on the pushed_streams_ list.
634 ActiveStreamList::iterator it;
635 for (it = pushed_streams_.begin(); it != pushed_streams_.end(); ++it) {
[email protected]a677f2b2009-11-22 00:43:00636 scoped_refptr<FlipStream> curr = *it;
637 if (id == curr->stream_id()) {
[email protected]aea80602009-09-18 00:55:08638 pushed_streams_.erase(it);
639 break;
640 }
641 }
642
643 active_streams_.erase(id);
644}
645
[email protected]a677f2b2009-11-22 00:43:00646scoped_refptr<FlipStream> FlipSession::GetPushStream(const std::string& path) {
[email protected]aea80602009-09-18 00:55:08647 static StatsCounter used_push_streams("flip.claimed_push_streams");
648
649 LOG(INFO) << "Looking for push stream: " << path;
650
[email protected]a677f2b2009-11-22 00:43:00651 scoped_refptr<FlipStream> stream;
652
[email protected]aea80602009-09-18 00:55:08653 // We just walk a linear list here.
654 ActiveStreamList::iterator it;
655 for (it = pushed_streams_.begin(); it != pushed_streams_.end(); ++it) {
[email protected]a677f2b2009-11-22 00:43:00656 stream = *it;
657 if (path == stream->path()) {
658 CHECK(stream->pushed());
[email protected]aea80602009-09-18 00:55:08659 pushed_streams_.erase(it);
660 used_push_streams.Increment();
661 LOG(INFO) << "Push Stream Claim for: " << path;
[email protected]a677f2b2009-11-22 00:43:00662 break;
[email protected]aea80602009-09-18 00:55:08663 }
664 }
[email protected]a677f2b2009-11-22 00:43:00665
666 return stream;
[email protected]aea80602009-09-18 00:55:08667}
668
669void FlipSession::OnError(flip::FlipFramer* framer) {
[email protected]2931238b2009-11-19 01:19:51670 LOG(ERROR) << "FlipSession error: " << framer->error_code();
[email protected]aea80602009-09-18 00:55:08671 CloseAllStreams(net::ERR_UNEXPECTED);
[email protected]18c53ae2009-10-01 18:18:52672 Release();
[email protected]aea80602009-09-18 00:55:08673}
674
675void FlipSession::OnStreamFrameData(flip::FlipStreamId stream_id,
676 const char* data,
[email protected]aeac1e42009-10-10 00:26:01677 size_t len) {
[email protected]aea80602009-09-18 00:55:08678 LOG(INFO) << "Flip data for stream " << stream_id << ", " << len << " bytes";
679 bool valid_stream = IsStreamActive(stream_id);
680 if (!valid_stream) {
[email protected]affe8fe2009-10-14 20:06:05681 LOG(WARNING) << "Received data frame for invalid stream " << stream_id;
[email protected]aea80602009-09-18 00:55:08682 return;
683 }
[email protected]93300672009-10-24 13:22:51684
[email protected]a677f2b2009-11-22 00:43:00685 scoped_refptr<FlipStream> stream = active_streams_[stream_id];
686 bool success = stream->OnDataReceived(data, len);
687 // |len| == 0 implies a closed stream.
688 if (!success || !len)
689 DeactivateStream(stream_id);
[email protected]aea80602009-09-18 00:55:08690}
691
692void FlipSession::OnSyn(const flip::FlipSynStreamControlFrame* frame,
693 const flip::FlipHeaderBlock* headers) {
694 flip::FlipStreamId stream_id = frame->stream_id();
695
[email protected]93300672009-10-24 13:22:51696 // Server-initiated streams should have even sequence numbers.
[email protected]aea80602009-09-18 00:55:08697 if ((stream_id & 0x1) != 0) {
[email protected]18c53ae2009-10-01 18:18:52698 LOG(ERROR) << "Received invalid OnSyn stream id " << stream_id;
[email protected]aea80602009-09-18 00:55:08699 return;
700 }
701
702 if (IsStreamActive(stream_id)) {
[email protected]18c53ae2009-10-01 18:18:52703 LOG(ERROR) << "Received OnSyn for active stream " << stream_id;
[email protected]aea80602009-09-18 00:55:08704 return;
705 }
706
[email protected]a677f2b2009-11-22 00:43:00707 LOG(INFO) << "FlipSession: SynReply received for stream: " << stream_id;
708
709 DCHECK(ContainsKey(*headers, "version"));
710 DCHECK(ContainsKey(*headers, "status"));
[email protected]967dd5412009-11-21 23:33:30711 LOG(INFO) << "FLIP SYN_REPLY RESPONSE HEADERS -----------------------";
712 DumpFlipHeaders(*headers);
713
[email protected]18c53ae2009-10-01 18:18:52714 // TODO(mbelshe): DCHECK that this is a GET method?
715
[email protected]a677f2b2009-11-22 00:43:00716 const std::string& path = ContainsKey(*headers, "path") ?
717 headers->find("path")->second : "";
718
[email protected]aea80602009-09-18 00:55:08719 // Verify that the response had a URL for us.
[email protected]a677f2b2009-11-22 00:43:00720 DCHECK(!path.empty());
721 if (path.empty()) {
[email protected]aea80602009-09-18 00:55:08722 LOG(WARNING) << "Pushed stream did not contain a path.";
[email protected]aea80602009-09-18 00:55:08723 return;
724 }
[email protected]18c53ae2009-10-01 18:18:52725
[email protected]a677f2b2009-11-22 00:43:00726 scoped_refptr<FlipStream> stream;
727
[email protected]18c53ae2009-10-01 18:18:52728 // Check if we already have a delegate awaiting this stream.
[email protected]2931238b2009-11-19 01:19:51729 PendingStreamMap::iterator it;
[email protected]a677f2b2009-11-22 00:43:00730 it = pending_streams_.find(path);
[email protected]18c53ae2009-10-01 18:18:52731 if (it != pending_streams_.end()) {
[email protected]a677f2b2009-11-22 00:43:00732 stream = it->second;
[email protected]18c53ae2009-10-01 18:18:52733 pending_streams_.erase(it);
[email protected]a677f2b2009-11-22 00:43:00734 if (stream)
[email protected]18c53ae2009-10-01 18:18:52735 pushed_streams_.push_back(stream);
736 } else {
737 pushed_streams_.push_back(stream);
738 }
[email protected]aea80602009-09-18 00:55:08739
[email protected]a677f2b2009-11-22 00:43:00740 if (stream) {
741 CHECK(stream->pushed());
742 CHECK(stream->stream_id() == 0);
743 stream->set_stream_id(stream_id);
744 } else {
745 stream = new FlipStream(this, stream_id, true);
746 }
747
748 // Activate a stream and parse the headers.
749 ActivateStream(stream);
750
751 stream->set_path(path);
752
753 // TODO(mbelshe): For now we convert from our nice hash map back
754 // to a string of headers; this is because the HttpResponseInfo
755 // is a bit rigid for its http (non-flip) design.
756 const HttpResponseInfo response = FlipHeadersToHttpResponse(*headers);
757 stream->OnResponseReceived(response);
758
[email protected]aea80602009-09-18 00:55:08759 LOG(INFO) << "Got pushed stream for " << stream->path();
760
761 static StatsCounter push_requests("flip.pushed_streams");
762 push_requests.Increment();
763}
764
765void FlipSession::OnSynReply(const flip::FlipSynReplyControlFrame* frame,
766 const flip::FlipHeaderBlock* headers) {
767 DCHECK(headers);
768 flip::FlipStreamId stream_id = frame->stream_id();
769 bool valid_stream = IsStreamActive(stream_id);
770 if (!valid_stream) {
[email protected]affe8fe2009-10-14 20:06:05771 LOG(WARNING) << "Received SYN_REPLY for invalid stream " << stream_id;
[email protected]aea80602009-09-18 00:55:08772 return;
773 }
774
[email protected]18c53ae2009-10-01 18:18:52775 // We record content declared as being pushed so that we don't
776 // request a duplicate stream which is already scheduled to be
777 // sent to us.
778 flip::FlipHeaderBlock::const_iterator it;
779 it = headers->find("X-Associated-Content");
780 if (it != headers->end()) {
781 const std::string& content = it->second;
782 std::string::size_type start = 0;
783 std::string::size_type end = 0;
784 do {
785 end = content.find("||", start);
[email protected]ba051e312009-10-07 22:12:33786 if (end == std::string::npos)
[email protected]18c53ae2009-10-01 18:18:52787 end = content.length();
788 std::string url = content.substr(start, end - start);
789 std::string::size_type pos = url.find("??");
[email protected]ba051e312009-10-07 22:12:33790 if (pos == std::string::npos)
[email protected]18c53ae2009-10-01 18:18:52791 break;
[email protected]ba051e312009-10-07 22:12:33792 url = url.substr(pos + 2);
[email protected]18c53ae2009-10-01 18:18:52793 GURL gurl(url);
[email protected]84b562f2009-11-20 18:25:30794 std::string path = gurl.PathForRequest();
795 if (path.length())
796 pending_streams_[path] = NULL;
797 else
798 LOG(INFO) << "Invalid X-Associated-Content path: " << url;
[email protected]18c53ae2009-10-01 18:18:52799 start = end + 2;
[email protected]84b562f2009-11-20 18:25:30800 } while (start < content.length());
[email protected]18c53ae2009-10-01 18:18:52801 }
802
[email protected]a677f2b2009-11-22 00:43:00803 scoped_refptr<FlipStream> stream = active_streams_[stream_id];
804 CHECK(stream->stream_id() == stream_id);
805 const HttpResponseInfo response = FlipHeadersToHttpResponse(*headers);
806 stream->OnResponseReceived(response);
[email protected]aea80602009-09-18 00:55:08807}
808
809void FlipSession::OnControl(const flip::FlipControlFrame* frame) {
810 flip::FlipHeaderBlock headers;
[email protected]aea80602009-09-18 00:55:08811 uint32 type = frame->type();
812 if (type == flip::SYN_STREAM || type == flip::SYN_REPLY) {
[email protected]22eb9682009-11-06 21:51:58813 if (!flip_framer_.ParseHeaderBlock(frame, &headers)) {
[email protected]aea80602009-09-18 00:55:08814 LOG(WARNING) << "Could not parse Flip Control Frame Header";
[email protected]84b562f2009-11-20 18:25:30815 // TODO(mbelshe): Error the session?
[email protected]aea80602009-09-18 00:55:08816 return;
817 }
818 }
819
820 switch (type) {
821 case flip::SYN_STREAM:
822 LOG(INFO) << "Flip SynStream for stream " << frame->stream_id();
823 OnSyn(reinterpret_cast<const flip::FlipSynStreamControlFrame*>(frame),
824 &headers);
825 break;
826 case flip::SYN_REPLY:
827 LOG(INFO) << "Flip SynReply for stream " << frame->stream_id();
828 OnSynReply(
829 reinterpret_cast<const flip::FlipSynReplyControlFrame*>(frame),
830 &headers);
831 break;
832 case flip::FIN_STREAM:
833 LOG(INFO) << "Flip Fin for stream " << frame->stream_id();
834 OnFin(reinterpret_cast<const flip::FlipFinStreamControlFrame*>(frame));
835 break;
836 default:
837 DCHECK(false); // Error!
838 }
839}
840
841void FlipSession::OnFin(const flip::FlipFinStreamControlFrame* frame) {
842 flip::FlipStreamId stream_id = frame->stream_id();
843 bool valid_stream = IsStreamActive(stream_id);
844 if (!valid_stream) {
845 LOG(WARNING) << "Received FIN for invalid stream" << stream_id;
846 return;
847 }
[email protected]a677f2b2009-11-22 00:43:00848 scoped_refptr<FlipStream> stream = active_streams_[stream_id];
849 CHECK(stream->stream_id() == stream_id);
[email protected]93300672009-10-24 13:22:51850 if (frame->status() == 0) {
[email protected]a677f2b2009-11-22 00:43:00851 stream->OnDataReceived(NULL, 0);
[email protected]aea80602009-09-18 00:55:08852 } else {
[email protected]93300672009-10-24 13:22:51853 LOG(ERROR) << "Flip stream closed: " << frame->status();
854 // TODO(mbelshe): Map from Flip-protocol errors to something sensical.
855 // For now, it doesn't matter much - it is a protocol error.
[email protected]a677f2b2009-11-22 00:43:00856 stream->OnClose(ERR_FAILED);
[email protected]aea80602009-09-18 00:55:08857 }
858
[email protected]a677f2b2009-11-22 00:43:00859 DeactivateStream(stream_id);
[email protected]aea80602009-09-18 00:55:08860}
861
[email protected]aea80602009-09-18 00:55:08862} // namespace net