Extract SIGPIPE ignoring code to a common place.
[chromium-blink-merge.git] / content / renderer / p2p / ipc_socket_factory.cc
blob230d99b8518bf1c9f3bf31fc2fdf5dd23ce07058
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/renderer/p2p/ipc_socket_factory.h"
7 #include "base/compiler_specific.h"
8 #include "base/message_loop.h"
9 #include "base/message_loop_proxy.h"
10 #include "content/renderer/p2p/socket_client.h"
11 #include "content/renderer/p2p/socket_dispatcher.h"
12 #include "jingle/glue/utils.h"
13 #include "third_party/libjingle/source/talk/base/asyncpacketsocket.h"
15 namespace content {
17 namespace {
19 // IpcPacketSocket implements talk_base::AsyncPacketSocket interface
20 // using P2PSocketClient that works over IPC-channel. It must be used
21 // on the thread it was created.
22 class IpcPacketSocket : public talk_base::AsyncPacketSocket,
23 public P2PSocketClient::Delegate {
24 public:
25 IpcPacketSocket();
26 virtual ~IpcPacketSocket();
28 // Always takes ownership of client even if initialization fails.
29 bool Init(P2PSocketType type, P2PSocketClient* client,
30 const talk_base::SocketAddress& local_address,
31 const talk_base::SocketAddress& remote_address);
33 // talk_base::AsyncPacketSocket interface.
34 virtual talk_base::SocketAddress GetLocalAddress() const;
35 virtual talk_base::SocketAddress GetRemoteAddress() const;
36 virtual int Send(const void *pv, size_t cb);
37 virtual int SendTo(const void *pv, size_t cb,
38 const talk_base::SocketAddress& addr);
39 virtual int Close();
40 virtual State GetState() const;
41 virtual int GetOption(talk_base::Socket::Option opt, int* value);
42 virtual int SetOption(talk_base::Socket::Option opt, int value);
43 virtual int GetError() const;
44 virtual void SetError(int error);
46 // P2PSocketClient::Delegate implementation.
47 virtual void OnOpen(const net::IPEndPoint& address) OVERRIDE;
48 virtual void OnIncomingTcpConnection(const net::IPEndPoint& address,
49 P2PSocketClient* client) OVERRIDE;
50 virtual void OnError();
51 virtual void OnDataReceived(const net::IPEndPoint& address,
52 const std::vector<char>& data) OVERRIDE;
54 private:
55 enum InternalState {
56 IS_UNINITIALIZED,
57 IS_OPENING,
58 IS_OPEN,
59 IS_CLOSED,
60 IS_ERROR,
63 void InitAcceptedTcp(P2PSocketClient* client,
64 const talk_base::SocketAddress& local_address,
65 const talk_base::SocketAddress& remote_address);
67 P2PSocketType type_;
69 // Message loop on which this socket was created and being used.
70 MessageLoop* message_loop_;
72 // Corresponding P2P socket client.
73 scoped_refptr<P2PSocketClient> client_;
75 // Local address is allocated by the browser process, and the
76 // renderer side doesn't know the address until it receives OnOpen()
77 // event from the browser.
78 talk_base::SocketAddress local_address_;
80 // Remote address for client TCP connections.
81 talk_base::SocketAddress remote_address_;
83 // Current state of the object.
84 InternalState state_;
86 // Current error code. Valid when state_ == IS_ERROR.
87 int error_;
89 DISALLOW_COPY_AND_ASSIGN(IpcPacketSocket);
92 IpcPacketSocket::IpcPacketSocket()
93 : type_(P2P_SOCKET_UDP),
94 message_loop_(MessageLoop::current()),
95 state_(IS_UNINITIALIZED),
96 error_(0) {
99 IpcPacketSocket::~IpcPacketSocket() {
100 if (state_ == IS_OPENING || state_ == IS_OPEN ||
101 state_ == IS_ERROR) {
102 Close();
106 bool IpcPacketSocket::Init(P2PSocketType type, P2PSocketClient* client,
107 const talk_base::SocketAddress& local_address,
108 const talk_base::SocketAddress& remote_address) {
109 DCHECK_EQ(MessageLoop::current(), message_loop_);
110 DCHECK_EQ(state_, IS_UNINITIALIZED);
112 type_ = type;
113 client_ = client;
114 local_address_ = local_address;
115 remote_address_ = remote_address;
116 state_ = IS_OPENING;
118 net::IPEndPoint local_endpoint;
119 if (!jingle_glue::SocketAddressToIPEndPoint(local_address, &local_endpoint)) {
120 return false;
123 net::IPEndPoint remote_endpoint;
124 if (!jingle_glue::SocketAddressToIPEndPoint(
125 remote_address, &remote_endpoint)) {
126 return false;
129 client_->Init(type, local_endpoint, remote_endpoint, this);
131 return true;
134 void IpcPacketSocket::InitAcceptedTcp(
135 P2PSocketClient* client,
136 const talk_base::SocketAddress& local_address,
137 const talk_base::SocketAddress& remote_address) {
138 DCHECK_EQ(MessageLoop::current(), message_loop_);
139 DCHECK_EQ(state_, IS_UNINITIALIZED);
141 client_ = client;
142 local_address_ = local_address;
143 remote_address_ = remote_address;
144 state_ = IS_OPEN;
145 client_->set_delegate(this);
148 // talk_base::AsyncPacketSocket interface.
149 talk_base::SocketAddress IpcPacketSocket::GetLocalAddress() const {
150 DCHECK_EQ(MessageLoop::current(), message_loop_);
151 return local_address_;
154 talk_base::SocketAddress IpcPacketSocket::GetRemoteAddress() const {
155 DCHECK_EQ(MessageLoop::current(), message_loop_);
156 return remote_address_;
159 int IpcPacketSocket::Send(const void *data, size_t data_size) {
160 DCHECK_EQ(MessageLoop::current(), message_loop_);
161 return SendTo(data, data_size, remote_address_);
164 int IpcPacketSocket::SendTo(const void *data, size_t data_size,
165 const talk_base::SocketAddress& address) {
166 DCHECK_EQ(MessageLoop::current(), message_loop_);
168 switch (state_) {
169 case IS_UNINITIALIZED:
170 NOTREACHED();
171 return EWOULDBLOCK;
172 case IS_OPENING:
173 return EWOULDBLOCK;
174 case IS_CLOSED:
175 return ENOTCONN;
176 case IS_ERROR:
177 return error_;
178 case IS_OPEN:
179 // Continue sending the packet.
180 break;
183 const char* data_char = reinterpret_cast<const char*>(data);
184 std::vector<char> data_vector(data_char, data_char + data_size);
186 net::IPEndPoint address_chrome;
187 if (!jingle_glue::SocketAddressToIPEndPoint(address, &address_chrome)) {
188 // Just drop the packet if we failed to convert the address.
189 return 0;
192 client_->Send(address_chrome, data_vector);
194 // Fake successful send. The caller ignores result anyway.
195 return data_size;
198 int IpcPacketSocket::Close() {
199 DCHECK_EQ(MessageLoop::current(), message_loop_);
201 client_->Close();
202 state_ = IS_CLOSED;
204 return 0;
207 talk_base::AsyncPacketSocket::State IpcPacketSocket::GetState() const {
208 DCHECK_EQ(MessageLoop::current(), message_loop_);
210 switch (state_) {
211 case IS_UNINITIALIZED:
212 NOTREACHED();
213 return STATE_CLOSED;
215 case IS_OPENING:
216 return STATE_BINDING;
218 case IS_OPEN:
219 if (type_ == P2P_SOCKET_TCP_CLIENT) {
220 return STATE_CONNECTED;
221 } else {
222 return STATE_BOUND;
225 case IS_CLOSED:
226 case IS_ERROR:
227 return STATE_CLOSED;
230 NOTREACHED();
231 return STATE_CLOSED;
234 int IpcPacketSocket::GetOption(talk_base::Socket::Option opt, int* value) {
235 // We don't support socket options for IPC sockets.
236 return -1;
239 int IpcPacketSocket::SetOption(talk_base::Socket::Option opt, int value) {
240 // We don't support socket options for IPC sockets.
242 // TODO(sergeyu): Make sure we set proper socket options on the
243 // browser side.
244 return -1;
247 int IpcPacketSocket::GetError() const {
248 DCHECK_EQ(MessageLoop::current(), message_loop_);
249 return error_;
252 void IpcPacketSocket::SetError(int error) {
253 DCHECK_EQ(MessageLoop::current(), message_loop_);
254 error_ = error;
257 void IpcPacketSocket::OnOpen(const net::IPEndPoint& address) {
258 DCHECK_EQ(MessageLoop::current(), message_loop_);
260 if (!jingle_glue::IPEndPointToSocketAddress(address, &local_address_)) {
261 // Always expect correct IPv4 address to be allocated.
262 NOTREACHED();
263 OnError();
264 return;
267 state_ = IS_OPEN;
269 SignalAddressReady(this, local_address_);
270 if (type_ == P2P_SOCKET_TCP_CLIENT)
271 SignalConnect(this);
274 void IpcPacketSocket::OnIncomingTcpConnection(
275 const net::IPEndPoint& address,
276 P2PSocketClient* client) {
277 DCHECK_EQ(MessageLoop::current(), message_loop_);
279 scoped_ptr<IpcPacketSocket> socket(new IpcPacketSocket());
281 talk_base::SocketAddress remote_address;
282 if (!jingle_glue::IPEndPointToSocketAddress(address, &remote_address)) {
283 // Always expect correct IPv4 address to be allocated.
284 NOTREACHED();
286 socket->InitAcceptedTcp(client, local_address_, remote_address);
287 SignalNewConnection(this, socket.release());
290 void IpcPacketSocket::OnError() {
291 DCHECK_EQ(MessageLoop::current(), message_loop_);
292 state_ = IS_ERROR;
293 error_ = ECONNABORTED;
296 void IpcPacketSocket::OnDataReceived(const net::IPEndPoint& address,
297 const std::vector<char>& data) {
298 DCHECK_EQ(MessageLoop::current(), message_loop_);
300 talk_base::SocketAddress address_lj;
301 if (!jingle_glue::IPEndPointToSocketAddress(address, &address_lj)) {
302 // We should always be able to convert address here because we
303 // don't expect IPv6 address on IPv4 connections.
304 NOTREACHED();
305 return;
308 SignalReadPacket(this, &data[0], data.size(), address_lj);
311 } // namespace
313 IpcPacketSocketFactory::IpcPacketSocketFactory(
314 P2PSocketDispatcher* socket_dispatcher)
315 : socket_dispatcher_(socket_dispatcher) {
318 IpcPacketSocketFactory::~IpcPacketSocketFactory() {
321 talk_base::AsyncPacketSocket* IpcPacketSocketFactory::CreateUdpSocket(
322 const talk_base::SocketAddress& local_address, int min_port, int max_port) {
323 talk_base::SocketAddress crome_address;
324 P2PSocketClient* socket_client = new P2PSocketClient(socket_dispatcher_);
325 scoped_ptr<IpcPacketSocket> socket(new IpcPacketSocket());
326 // TODO(sergeyu): Respect local_address and port limits here (need
327 // to pass them over IPC channel to the browser).
328 if (!socket->Init(P2P_SOCKET_UDP, socket_client,
329 local_address, talk_base::SocketAddress())) {
330 return NULL;
332 return socket.release();
335 talk_base::AsyncPacketSocket* IpcPacketSocketFactory::CreateServerTcpSocket(
336 const talk_base::SocketAddress& local_address, int min_port, int max_port,
337 bool ssl) {
338 // TODO(sergeyu): Implement SSL support.
339 if (ssl)
340 return NULL;
342 talk_base::SocketAddress crome_address;
343 P2PSocketClient* socket_client = new P2PSocketClient(socket_dispatcher_);
344 scoped_ptr<IpcPacketSocket> socket(new IpcPacketSocket());
345 if (!socket->Init(P2P_SOCKET_TCP_SERVER, socket_client, local_address,
346 talk_base::SocketAddress())) {
347 return NULL;
349 return socket.release();
352 talk_base::AsyncPacketSocket* IpcPacketSocketFactory::CreateClientTcpSocket(
353 const talk_base::SocketAddress& local_address,
354 const talk_base::SocketAddress& remote_address,
355 const talk_base::ProxyInfo& proxy_info,
356 const std::string& user_agent, bool ssl) {
357 // TODO(sergeyu): Implement SSL support.
358 if (ssl)
359 return NULL;
361 talk_base::SocketAddress crome_address;
362 P2PSocketClient* socket_client = new P2PSocketClient(socket_dispatcher_);
363 scoped_ptr<IpcPacketSocket> socket(new IpcPacketSocket());
364 if (!socket->Init(P2P_SOCKET_TCP_CLIENT, socket_client, local_address,
365 remote_address))
366 return NULL;
367 return socket.release();
370 } // namespace content