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