Ignore non-active fullscreen windows for shelf state.
[chromium-blink-merge.git] / content / browser / renderer_host / p2p / socket_host_udp.cc
blob417cb2b45617d2a9807d656d1d0c34a1d1d7a4dd
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 "content/browser/renderer_host/p2p/socket_host_udp.h"
7 #include "base/bind.h"
8 #include "base/debug/trace_event.h"
9 #include "base/stl_util.h"
10 #include "content/browser/renderer_host/p2p/socket_host_throttler.h"
11 #include "content/common/p2p_messages.h"
12 #include "ipc/ipc_sender.h"
13 #include "net/base/io_buffer.h"
14 #include "net/base/net_errors.h"
15 #include "net/base/net_util.h"
17 namespace {
19 // UDP packets cannot be bigger than 64k.
20 const int kReadBufferSize = 65536;
21 // Socket receive buffer size.
22 const int kRecvSocketBufferSize = 65536; // 64K
24 // Defines set of transient errors. These errors are ignored when we get them
25 // from sendto() or recvfrom() calls.
27 // net::ERR_OUT_OF_MEMORY
29 // This is caused by ENOBUFS which means the buffer of the network interface
30 // is full.
32 // net::ERR_CONNECTION_RESET
34 // This is caused by WSAENETRESET or WSAECONNRESET which means the
35 // last send resulted in an "ICMP Port Unreachable" message.
36 bool IsTransientError(int error) {
37 return error == net::ERR_ADDRESS_UNREACHABLE ||
38 error == net::ERR_ADDRESS_INVALID ||
39 error == net::ERR_ACCESS_DENIED ||
40 error == net::ERR_CONNECTION_RESET ||
41 error == net::ERR_OUT_OF_MEMORY;
44 } // namespace
46 namespace content {
48 P2PSocketHostUdp::PendingPacket::PendingPacket(
49 const net::IPEndPoint& to,
50 const std::vector<char>& content,
51 net::DiffServCodePoint dscp_,
52 uint64 id)
53 : to(to),
54 data(new net::IOBuffer(content.size())),
55 size(content.size()),
56 dscp(dscp_),
57 id(id) {
58 memcpy(data->data(), &content[0], size);
61 P2PSocketHostUdp::PendingPacket::~PendingPacket() {
64 P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender,
65 int id,
66 P2PMessageThrottler* throttler)
67 : P2PSocketHost(message_sender, id),
68 socket_(new net::UDPServerSocket(NULL, net::NetLog::Source())),
69 send_pending_(false),
70 last_dscp_(net::DSCP_CS0),
71 throttler_(throttler) {
74 P2PSocketHostUdp::~P2PSocketHostUdp() {
75 if (state_ == STATE_OPEN) {
76 DCHECK(socket_.get());
77 socket_.reset();
81 bool P2PSocketHostUdp::Init(const net::IPEndPoint& local_address,
82 const net::IPEndPoint& remote_address) {
83 DCHECK_EQ(state_, STATE_UNINITIALIZED);
85 int result = socket_->Listen(local_address);
86 if (result < 0) {
87 LOG(ERROR) << "bind() failed: " << result;
88 OnError();
89 return false;
92 // Setting recv socket buffer size.
93 if (!socket_->SetReceiveBufferSize(kRecvSocketBufferSize)) {
94 LOG(WARNING) << "Failed to set socket receive buffer size to "
95 << kRecvSocketBufferSize;
98 net::IPEndPoint address;
99 result = socket_->GetLocalAddress(&address);
100 if (result < 0) {
101 LOG(ERROR) << "P2PSocketHostUdp::Init(): unable to get local address: "
102 << result;
103 OnError();
104 return false;
106 VLOG(1) << "Local address: " << address.ToString();
108 state_ = STATE_OPEN;
110 message_sender_->Send(new P2PMsg_OnSocketCreated(id_, address));
112 recv_buffer_ = new net::IOBuffer(kReadBufferSize);
113 DoRead();
115 return true;
118 void P2PSocketHostUdp::OnError() {
119 socket_.reset();
120 send_queue_.clear();
122 if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN)
123 message_sender_->Send(new P2PMsg_OnError(id_));
125 state_ = STATE_ERROR;
128 void P2PSocketHostUdp::DoRead() {
129 int result;
130 do {
131 result = socket_->RecvFrom(
132 recv_buffer_.get(),
133 kReadBufferSize,
134 &recv_address_,
135 base::Bind(&P2PSocketHostUdp::OnRecv, base::Unretained(this)));
136 if (result == net::ERR_IO_PENDING)
137 return;
138 HandleReadResult(result);
139 } while (state_ == STATE_OPEN);
142 void P2PSocketHostUdp::OnRecv(int result) {
143 HandleReadResult(result);
144 if (state_ == STATE_OPEN) {
145 DoRead();
149 void P2PSocketHostUdp::HandleReadResult(int result) {
150 DCHECK_EQ(STATE_OPEN, state_);
152 if (result > 0) {
153 std::vector<char> data(recv_buffer_->data(), recv_buffer_->data() + result);
155 if (!ContainsKey(connected_peers_, recv_address_)) {
156 P2PSocketHost::StunMessageType type;
157 bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
158 if (stun && IsRequestOrResponse(type)) {
159 connected_peers_.insert(recv_address_);
160 } else if (!stun || type == STUN_DATA_INDICATION) {
161 LOG(ERROR) << "Received unexpected data packet from "
162 << recv_address_.ToString()
163 << " before STUN binding is finished.";
164 return;
168 message_sender_->Send(new P2PMsg_OnDataReceived(id_, recv_address_, data));
169 } else if (result < 0 && !IsTransientError(result)) {
170 LOG(ERROR) << "Error when reading from UDP socket: " << result;
171 OnError();
175 void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
176 const std::vector<char>& data,
177 net::DiffServCodePoint dscp,
178 uint64 packet_id) {
179 if (!socket_) {
180 // The Send message may be sent after the an OnError message was
181 // sent by hasn't been processed the renderer.
182 return;
185 if (!ContainsKey(connected_peers_, to)) {
186 P2PSocketHost::StunMessageType type = P2PSocketHost::StunMessageType();
187 bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
188 if (!stun || type == STUN_DATA_INDICATION) {
189 LOG(ERROR) << "Page tried to send a data packet to " << to.ToString()
190 << " before STUN binding is finished.";
191 OnError();
192 return;
195 if (throttler_->DropNextPacket(data.size())) {
196 VLOG(0) << "STUN message is dropped due to high volume.";
197 // Do not reset socket.
198 return;
202 if (send_pending_) {
203 send_queue_.push_back(PendingPacket(to, data, dscp, packet_id));
204 } else {
205 PendingPacket packet(to, data, dscp, packet_id);
206 DoSend(packet);
210 void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
211 TRACE_EVENT_ASYNC_STEP_INTO1("p2p", "Send", packet.id, "UdpAsyncSendTo",
212 "size", packet.size);
213 // Don't try to set DSCP in following conditions,
214 // 1. If the outgoing packet is set to DSCP_NO_CHANGE
215 // 2. If no change in DSCP value from last packet
216 // 3. If there is any error in setting DSCP on socket.
217 if (packet.dscp != net::DSCP_NO_CHANGE &&
218 last_dscp_ != packet.dscp && last_dscp_ != net::DSCP_NO_CHANGE) {
219 int result = socket_->SetDiffServCodePoint(packet.dscp);
220 if (result == net::OK) {
221 last_dscp_ = packet.dscp;
222 } else if (!IsTransientError(result) && last_dscp_ != net::DSCP_CS0) {
223 // We receieved a non-transient error, and it seems we have
224 // not changed the DSCP in the past, disable DSCP as it unlikely
225 // to work in the future.
226 last_dscp_ = net::DSCP_NO_CHANGE;
229 int result = socket_->SendTo(
230 packet.data.get(),
231 packet.size,
232 packet.to,
233 base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this), packet.id));
235 // sendto() may return an error, e.g. if we've received an ICMP Destination
236 // Unreachable message. When this happens try sending the same packet again,
237 // and just drop it if it fails again.
238 if (IsTransientError(result)) {
239 result = socket_->SendTo(
240 packet.data.get(),
241 packet.size,
242 packet.to,
243 base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this),
244 packet.id));
247 if (result == net::ERR_IO_PENDING) {
248 send_pending_ = true;
249 } else {
250 HandleSendResult(packet.id, result);
254 void P2PSocketHostUdp::OnSend(uint64 packet_id, int result) {
255 DCHECK(send_pending_);
256 DCHECK_NE(result, net::ERR_IO_PENDING);
258 send_pending_ = false;
260 HandleSendResult(packet_id, result);
262 // Send next packets if we have them waiting in the buffer.
263 while (state_ == STATE_OPEN && !send_queue_.empty() && !send_pending_) {
264 DoSend(send_queue_.front());
265 send_queue_.pop_front();
269 void P2PSocketHostUdp::HandleSendResult(uint64 packet_id, int result) {
270 TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id,
271 "result", result);
272 if (result > 0) {
273 message_sender_->Send(new P2PMsg_OnSendComplete(id_));
274 } else if (IsTransientError(result)) {
275 VLOG(0) << "sendto() has failed twice returning a "
276 " transient error. Dropping the packet.";
277 } else if (result < 0) {
278 LOG(ERROR) << "Error when sending data in UDP socket: " << result;
279 OnError();
283 P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
284 const net::IPEndPoint& remote_address, int id) {
285 NOTREACHED();
286 OnError();
287 return NULL;
290 } // namespace content