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.
5 #include "net/tools/quic/quic_dispatcher.h"
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"
21 class DeleteSessionsAlarm
: public EpollAlarm
{
23 explicit DeleteSessionsAlarm(QuicDispatcher
* dispatcher
)
24 : dispatcher_(dispatcher
) {
27 virtual int64
OnAlarm() OVERRIDE
{
28 EpollAlarm::OnAlarm();
29 dispatcher_
->DeleteSessions();
34 QuicDispatcher
* dispatcher_
;
37 QuicDispatcher::QuicDispatcher(const QuicConfig
& config
,
38 const QuicCryptoServerConfig
& crypto_config
,
40 EpollServer
* epoll_server
)
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
),
48 write_blocked_(false) {
51 QuicDispatcher::~QuicDispatcher() {
52 STLDeleteValues(&session_map_
);
53 STLDeleteElements(&closed_session_list_
);
56 int QuicDispatcher::WritePacket(const char* buffer
, size_t buf_len
,
57 const IPAddressNumber
& self_address
,
58 const IPEndPoint
& peer_address
,
59 QuicBlockedWriterInterface
* writer
,
62 write_blocked_list_
.AddBlockedObject(writer
);
67 int rc
= QuicSocketUtils::WritePacket(fd_
, buffer
, buf_len
,
68 self_address
, peer_address
,
70 if (rc
== -1 && (*error
== EWOULDBLOCK
|| *error
== EAGAIN
)) {
71 write_blocked_list_
.AddBlockedObject(writer
);
72 write_blocked_
= true;
77 void QuicDispatcher::ProcessPacket(const IPEndPoint
& server_address
,
78 const IPEndPoint
& client_address
,
80 const QuicEncryptedPacket
& packet
) {
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
,
92 session
= CreateQuicSession(guid
, client_address
, fd_
, epoll_server_
);
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
,
105 DLOG(INFO
) << "Created new session for " << guid
;
106 session_map_
.insert(make_pair(guid
, session
));
108 session
= it
->second
;
111 session
->connection()->ProcessUdpPacket(
112 server_address
, client_address
, packet
);
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
);
123 void QuicDispatcher::DeleteSessions() {
124 STLDeleteElements(&closed_session_list_
);
127 bool QuicDispatcher::OnCanWrite() {
128 // We got an EPOLLOUT: the socket should not be blocked.
129 write_blocked_
= false;
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()) {
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.
146 // The socket is not blocked but the writer has ceded work. Add it to the
148 if (can_write_more
) {
149 write_blocked_list_
.AddBlockedObject(writer
);
153 // We're not write blocked. Return true if there's more work to do.
154 return !write_blocked_list_
.IsEmpty();
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
);
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
);
175 DLOG_IF(INFO
, error
!= QUIC_NO_ERROR
) << "Closing connection due to error: "
176 << QuicUtils::ErrorToString(error
);
178 if (closed_session_list_
.empty()) {
179 epoll_server_
->RegisterAlarmApproximateDelta(
180 0, delete_sessions_alarm_
.get());
182 closed_session_list_
.push_back(it
->second
);
186 QuicSession
* QuicDispatcher::CreateQuicSession(
188 const IPEndPoint
& client_address
,
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_
);