Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / content / browser / renderer_host / p2p / socket_host_udp.cc
blob77c817e552c9b0b617a7a609dde095f41cd0d399
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 "content/public/browser/content_browser_client.h"
13 #include "content/public/common/content_client.h"
14 #include "ipc/ipc_sender.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/net_errors.h"
17 #include "net/base/net_util.h"
18 #include "third_party/libjingle/source/talk/base/asyncpacketsocket.h"
20 namespace {
22 // UDP packets cannot be bigger than 64k.
23 const int kReadBufferSize = 65536;
24 // Socket receive buffer size.
25 const int kRecvSocketBufferSize = 65536; // 64K
27 // Defines set of transient errors. These errors are ignored when we get them
28 // from sendto() or recvfrom() calls.
30 // net::ERR_OUT_OF_MEMORY
32 // This is caused by ENOBUFS which means the buffer of the network interface
33 // is full.
35 // net::ERR_CONNECTION_RESET
37 // This is caused by WSAENETRESET or WSAECONNRESET which means the
38 // last send resulted in an "ICMP Port Unreachable" message.
39 bool IsTransientError(int error) {
40 return error == net::ERR_ADDRESS_UNREACHABLE ||
41 error == net::ERR_ADDRESS_INVALID ||
42 error == net::ERR_ACCESS_DENIED ||
43 error == net::ERR_CONNECTION_RESET ||
44 error == net::ERR_OUT_OF_MEMORY ||
45 error == net::ERR_INTERNET_DISCONNECTED;
48 } // namespace
50 namespace content {
52 P2PSocketHostUdp::PendingPacket::PendingPacket(
53 const net::IPEndPoint& to,
54 const std::vector<char>& content,
55 const talk_base::PacketOptions& options,
56 uint64 id)
57 : to(to),
58 data(new net::IOBuffer(content.size())),
59 size(content.size()),
60 packet_options(options),
61 id(id) {
62 memcpy(data->data(), &content[0], size);
65 P2PSocketHostUdp::PendingPacket::~PendingPacket() {
68 P2PSocketHostUdp::P2PSocketHostUdp(IPC::Sender* message_sender,
69 int id,
70 P2PMessageThrottler* throttler)
71 : P2PSocketHost(message_sender, id),
72 socket_(new net::UDPServerSocket(
73 GetContentClient()->browser()->GetNetLog(),
74 net::NetLog::Source())),
75 send_pending_(false),
76 last_dscp_(net::DSCP_CS0),
77 throttler_(throttler) {
80 P2PSocketHostUdp::~P2PSocketHostUdp() {
81 if (state_ == STATE_OPEN) {
82 DCHECK(socket_.get());
83 socket_.reset();
87 bool P2PSocketHostUdp::Init(const net::IPEndPoint& local_address,
88 const P2PHostAndIPEndPoint& remote_address) {
89 DCHECK_EQ(state_, STATE_UNINITIALIZED);
91 int result = socket_->Listen(local_address);
92 if (result < 0) {
93 LOG(ERROR) << "bind() failed: " << result;
94 OnError();
95 return false;
98 // Setting recv socket buffer size.
99 if (socket_->SetReceiveBufferSize(kRecvSocketBufferSize) != net::OK) {
100 LOG(WARNING) << "Failed to set socket receive buffer size to "
101 << kRecvSocketBufferSize;
104 net::IPEndPoint address;
105 result = socket_->GetLocalAddress(&address);
106 if (result < 0) {
107 LOG(ERROR) << "P2PSocketHostUdp::Init(): unable to get local address: "
108 << result;
109 OnError();
110 return false;
112 VLOG(1) << "Local address: " << address.ToString();
114 state_ = STATE_OPEN;
116 message_sender_->Send(new P2PMsg_OnSocketCreated(id_, address));
118 recv_buffer_ = new net::IOBuffer(kReadBufferSize);
119 DoRead();
121 return true;
124 void P2PSocketHostUdp::OnError() {
125 socket_.reset();
126 send_queue_.clear();
128 if (state_ == STATE_UNINITIALIZED || state_ == STATE_OPEN)
129 message_sender_->Send(new P2PMsg_OnError(id_));
131 state_ = STATE_ERROR;
134 void P2PSocketHostUdp::DoRead() {
135 int result;
136 do {
137 result = socket_->RecvFrom(
138 recv_buffer_.get(),
139 kReadBufferSize,
140 &recv_address_,
141 base::Bind(&P2PSocketHostUdp::OnRecv, base::Unretained(this)));
142 if (result == net::ERR_IO_PENDING)
143 return;
144 HandleReadResult(result);
145 } while (state_ == STATE_OPEN);
148 void P2PSocketHostUdp::OnRecv(int result) {
149 HandleReadResult(result);
150 if (state_ == STATE_OPEN) {
151 DoRead();
155 void P2PSocketHostUdp::HandleReadResult(int result) {
156 DCHECK_EQ(STATE_OPEN, state_);
158 if (result > 0) {
159 std::vector<char> data(recv_buffer_->data(), recv_buffer_->data() + result);
161 if (!ContainsKey(connected_peers_, recv_address_)) {
162 P2PSocketHost::StunMessageType type;
163 bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
164 if ((stun && IsRequestOrResponse(type))) {
165 connected_peers_.insert(recv_address_);
166 } else if (!stun || type == STUN_DATA_INDICATION) {
167 LOG(ERROR) << "Received unexpected data packet from "
168 << recv_address_.ToString()
169 << " before STUN binding is finished.";
170 return;
174 message_sender_->Send(new P2PMsg_OnDataReceived(
175 id_, recv_address_, data, base::TimeTicks::Now()));
176 } else if (result < 0 && !IsTransientError(result)) {
177 LOG(ERROR) << "Error when reading from UDP socket: " << result;
178 OnError();
182 void P2PSocketHostUdp::Send(const net::IPEndPoint& to,
183 const std::vector<char>& data,
184 const talk_base::PacketOptions& options,
185 uint64 packet_id) {
186 if (!socket_) {
187 // The Send message may be sent after the an OnError message was
188 // sent by hasn't been processed the renderer.
189 return;
192 if (!ContainsKey(connected_peers_, to)) {
193 P2PSocketHost::StunMessageType type = P2PSocketHost::StunMessageType();
194 bool stun = GetStunPacketType(&*data.begin(), data.size(), &type);
195 if (!stun || type == STUN_DATA_INDICATION) {
196 LOG(ERROR) << "Page tried to send a data packet to " << to.ToString()
197 << " before STUN binding is finished.";
198 OnError();
199 return;
202 if (throttler_->DropNextPacket(data.size())) {
203 VLOG(0) << "STUN message is dropped due to high volume.";
204 // Do not reset socket.
205 return;
209 if (send_pending_) {
210 send_queue_.push_back(PendingPacket(to, data, options, packet_id));
211 } else {
212 // TODO(mallinath: Remove unnecessary memcpy in this case.
213 PendingPacket packet(to, data, options, packet_id);
214 DoSend(packet);
218 void P2PSocketHostUdp::DoSend(const PendingPacket& packet) {
219 TRACE_EVENT_ASYNC_STEP_INTO1("p2p", "Send", packet.id, "UdpAsyncSendTo",
220 "size", packet.size);
221 // Don't try to set DSCP in following conditions,
222 // 1. If the outgoing packet is set to DSCP_NO_CHANGE
223 // 2. If no change in DSCP value from last packet
224 // 3. If there is any error in setting DSCP on socket.
225 net::DiffServCodePoint dscp =
226 static_cast<net::DiffServCodePoint>(packet.packet_options.dscp);
227 if (dscp != net::DSCP_NO_CHANGE && last_dscp_ != dscp &&
228 last_dscp_ != net::DSCP_NO_CHANGE) {
229 int result = socket_->SetDiffServCodePoint(dscp);
230 if (result == net::OK) {
231 last_dscp_ = dscp;
232 } else if (!IsTransientError(result) && last_dscp_ != net::DSCP_CS0) {
233 // We receieved a non-transient error, and it seems we have
234 // not changed the DSCP in the past, disable DSCP as it unlikely
235 // to work in the future.
236 last_dscp_ = net::DSCP_NO_CHANGE;
239 packet_processing_helpers::ApplyPacketOptions(
240 packet.data->data(), packet.size, packet.packet_options, 0);
241 int result = socket_->SendTo(
242 packet.data.get(),
243 packet.size,
244 packet.to,
245 base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this), packet.id));
247 // sendto() may return an error, e.g. if we've received an ICMP Destination
248 // Unreachable message. When this happens try sending the same packet again,
249 // and just drop it if it fails again.
250 if (IsTransientError(result)) {
251 result = socket_->SendTo(
252 packet.data.get(),
253 packet.size,
254 packet.to,
255 base::Bind(&P2PSocketHostUdp::OnSend, base::Unretained(this),
256 packet.id));
259 if (result == net::ERR_IO_PENDING) {
260 send_pending_ = true;
261 } else {
262 HandleSendResult(packet.id, result);
266 void P2PSocketHostUdp::OnSend(uint64 packet_id, int result) {
267 DCHECK(send_pending_);
268 DCHECK_NE(result, net::ERR_IO_PENDING);
270 send_pending_ = false;
272 HandleSendResult(packet_id, result);
274 // Send next packets if we have them waiting in the buffer.
275 while (state_ == STATE_OPEN && !send_queue_.empty() && !send_pending_) {
276 DoSend(send_queue_.front());
277 send_queue_.pop_front();
281 void P2PSocketHostUdp::HandleSendResult(uint64 packet_id, int result) {
282 TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id,
283 "result", result);
284 if (result < 0) {
285 if (!IsTransientError(result)) {
286 LOG(ERROR) << "Error when sending data in UDP socket: " << result;
287 OnError();
288 return;
290 VLOG(0) << "sendto() has failed twice returning a "
291 " transient error. Dropping the packet.";
293 message_sender_->Send(new P2PMsg_OnSendComplete(id_));
296 P2PSocketHost* P2PSocketHostUdp::AcceptIncomingTcpConnection(
297 const net::IPEndPoint& remote_address, int id) {
298 NOTREACHED();
299 OnError();
300 return NULL;
303 bool P2PSocketHostUdp::SetOption(P2PSocketOption option, int value) {
304 DCHECK_EQ(STATE_OPEN, state_);
305 switch (option) {
306 case P2P_SOCKET_OPT_RCVBUF:
307 return socket_->SetReceiveBufferSize(value) == net::OK;
308 case P2P_SOCKET_OPT_SNDBUF:
309 return socket_->SetSendBufferSize(value) == net::OK;
310 case P2P_SOCKET_OPT_DSCP:
311 return (net::OK == socket_->SetDiffServCodePoint(
312 static_cast<net::DiffServCodePoint>(value))) ? true : false;
313 default:
314 NOTREACHED();
315 return false;
319 } // namespace content