Home | History | Annotate | Download | only in quic
      1 // Copyright (c) 2012 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/tools/quic/quic_dispatcher.h"
      6 
      7 #include <errno.h>
      8 
      9 #include "base/logging.h"
     10 #include "base/stl_util.h"
     11 #include "net/quic/quic_blocked_writer_interface.h"
     12 #include "net/quic/quic_utils.h"
     13 #include "net/tools/quic/quic_default_packet_writer.h"
     14 #include "net/tools/quic/quic_epoll_connection_helper.h"
     15 #include "net/tools/quic/quic_socket_utils.h"
     16 
     17 namespace net {
     18 namespace tools {
     19 
     20 using std::make_pair;
     21 
     22 class DeleteSessionsAlarm : public EpollAlarm {
     23  public:
     24   explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher)
     25       : dispatcher_(dispatcher) {
     26   }
     27 
     28   virtual int64 OnAlarm() OVERRIDE {
     29     EpollAlarm::OnAlarm();
     30     dispatcher_->DeleteSessions();
     31     return 0;
     32   }
     33 
     34  private:
     35   QuicDispatcher* dispatcher_;
     36 };
     37 
     38 QuicDispatcher::QuicDispatcher(const QuicConfig& config,
     39                                const QuicCryptoServerConfig& crypto_config,
     40                                const QuicVersionVector& supported_versions,
     41                                int fd,
     42                                EpollServer* epoll_server)
     43     : config_(config),
     44       crypto_config_(crypto_config),
     45       time_wait_list_manager_(
     46           new QuicTimeWaitListManager(this, epoll_server, supported_versions)),
     47       delete_sessions_alarm_(new DeleteSessionsAlarm(this)),
     48       epoll_server_(epoll_server),
     49       fd_(fd),
     50       write_blocked_(false),
     51       helper_(new QuicEpollConnectionHelper(epoll_server_)),
     52       writer_(new QuicDefaultPacketWriter(fd)),
     53       supported_versions_(supported_versions) {
     54 }
     55 
     56 QuicDispatcher::~QuicDispatcher() {
     57   STLDeleteValues(&session_map_);
     58   STLDeleteElements(&closed_session_list_);
     59 }
     60 
     61 void QuicDispatcher::set_fd(int fd) {
     62   fd_ = fd;
     63   writer_.reset(new QuicDefaultPacketWriter(fd));
     64 }
     65 
     66 WriteResult QuicDispatcher::WritePacket(const char* buffer, size_t buf_len,
     67                                         const IPAddressNumber& self_address,
     68                                         const IPEndPoint& peer_address,
     69                                         QuicBlockedWriterInterface* writer) {
     70   if (write_blocked_) {
     71     write_blocked_list_.insert(make_pair(writer, true));
     72     return WriteResult(WRITE_STATUS_BLOCKED, EAGAIN);
     73   }
     74 
     75   WriteResult result =
     76       writer_->WritePacket(buffer, buf_len, self_address, peer_address, writer);
     77   if (result.status == WRITE_STATUS_BLOCKED) {
     78     write_blocked_list_.insert(make_pair(writer, true));
     79     write_blocked_ = true;
     80   }
     81   return result;
     82 }
     83 
     84 bool QuicDispatcher::IsWriteBlockedDataBuffered() const {
     85   return writer_->IsWriteBlockedDataBuffered();
     86 }
     87 
     88 void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address,
     89                                    const IPEndPoint& client_address,
     90                                    QuicGuid guid,
     91                                    bool has_version_flag,
     92                                    const QuicEncryptedPacket& packet) {
     93   QuicSession* session = NULL;
     94 
     95   SessionMap::iterator it = session_map_.find(guid);
     96   if (it == session_map_.end()) {
     97     if (time_wait_list_manager_->IsGuidInTimeWait(guid)) {
     98       time_wait_list_manager_->ProcessPacket(server_address,
     99                                              client_address,
    100                                              guid,
    101                                              packet);
    102       return;
    103     }
    104 
    105     // Ensure the packet has a version negotiation bit set before creating a new
    106     // session for it.  All initial packets for a new connection are required to
    107     // have the flag set.  Otherwise it may be a stray packet.
    108     if (has_version_flag) {
    109       session = CreateQuicSession(guid, server_address, client_address);
    110     }
    111 
    112     if (session == NULL) {
    113       DLOG(INFO) << "Failed to create session for " << guid;
    114       // Add this guid fo the time-wait state, to safely reject future packets.
    115       // We don't know the version here, so assume latest.
    116       // TODO(ianswett): Produce a no-version version negotiation packet.
    117       time_wait_list_manager_->AddGuidToTimeWait(guid,
    118                                                  supported_versions_.front(),
    119                                                  NULL);
    120       time_wait_list_manager_->ProcessPacket(server_address,
    121                                              client_address,
    122                                              guid,
    123                                              packet);
    124       return;
    125     }
    126     DLOG(INFO) << "Created new session for " << guid;
    127     session_map_.insert(make_pair(guid, session));
    128   } else {
    129     session = it->second;
    130   }
    131 
    132   session->connection()->ProcessUdpPacket(
    133       server_address, client_address, packet);
    134 }
    135 
    136 void QuicDispatcher::CleanUpSession(SessionMap::iterator it) {
    137   QuicConnection* connection = it->second->connection();
    138   QuicEncryptedPacket* connection_close_packet =
    139           connection->ReleaseConnectionClosePacket();
    140   write_blocked_list_.erase(connection);
    141   time_wait_list_manager_->AddGuidToTimeWait(it->first,
    142                                              connection->version(),
    143                                              connection_close_packet);
    144   session_map_.erase(it);
    145 }
    146 
    147 void QuicDispatcher::DeleteSessions() {
    148   STLDeleteElements(&closed_session_list_);
    149 }
    150 
    151 void QuicDispatcher::UseWriter(QuicPacketWriter* writer) {
    152   writer_.reset(writer);
    153 }
    154 
    155 bool QuicDispatcher::OnCanWrite() {
    156   // We got an EPOLLOUT: the socket should not be blocked.
    157   write_blocked_ = false;
    158 
    159   // Give each writer one attempt to write.
    160   int num_writers = write_blocked_list_.size();
    161   for (int i = 0; i < num_writers; ++i) {
    162     if (write_blocked_list_.empty()) {
    163       break;
    164     }
    165     QuicBlockedWriterInterface* writer = write_blocked_list_.begin()->first;
    166     write_blocked_list_.erase(write_blocked_list_.begin());
    167     bool can_write_more = writer->OnCanWrite();
    168     if (write_blocked_) {
    169       // We were unable to write.  Wait for the next EPOLLOUT.
    170       // In this case, the session would have been added to the blocked list
    171       // up in WritePacket.
    172       return false;
    173     }
    174     // The socket is not blocked but the writer has ceded work.  Add it to the
    175     // end of the list.
    176     if (can_write_more) {
    177       write_blocked_list_.insert(make_pair(writer, true));
    178     }
    179   }
    180 
    181   // We're not write blocked.  Return true if there's more work to do.
    182   return !write_blocked_list_.empty();
    183 }
    184 
    185 void QuicDispatcher::Shutdown() {
    186   while (!session_map_.empty()) {
    187     QuicSession* session = session_map_.begin()->second;
    188     session->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
    189     // Validate that the session removes itself from the session map on close.
    190     DCHECK(session_map_.empty() || session_map_.begin()->second != session);
    191   }
    192   DeleteSessions();
    193 }
    194 
    195 void QuicDispatcher::OnConnectionClosed(QuicGuid guid, QuicErrorCode error) {
    196   SessionMap::iterator it = session_map_.find(guid);
    197   if (it == session_map_.end()) {
    198     LOG(DFATAL) << "GUID " << guid << " does not exist in the session map.  "
    199                 << "Error: " << QuicUtils::ErrorToString(error);
    200     return;
    201   }
    202 
    203   DLOG_IF(INFO, error != QUIC_NO_ERROR) << "Closing connection (" << guid
    204                                         << ") due to error: "
    205                                         << QuicUtils::ErrorToString(error);
    206 
    207   if (closed_session_list_.empty()) {
    208     epoll_server_->RegisterAlarmApproximateDelta(
    209         0, delete_sessions_alarm_.get());
    210   }
    211   closed_session_list_.push_back(it->second);
    212   CleanUpSession(it);
    213 }
    214 
    215 QuicSession* QuicDispatcher::CreateQuicSession(
    216     QuicGuid guid,
    217     const IPEndPoint& server_address,
    218     const IPEndPoint& client_address) {
    219   QuicServerSession* session = new QuicServerSession(
    220       config_, new QuicConnection(guid, client_address, helper_.get(), this,
    221                                   true, supported_versions_), this);
    222   session->InitializeSession(crypto_config_);
    223   return session;
    224 }
    225 
    226 }  // namespace tools
    227 }  // namespace net
    228