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