diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-04 16:26:27 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-04 16:26:27 +0000 |
commit | 25762d8fd7f1e4436fc5bc35927f24bac640f2a9 (patch) | |
tree | 8d30f1b4fa9315e9a8a995dd41c485dfc6a36c36 | |
parent | f0ebb6d3b38f85df2df955b69790b54053e172f9 (diff) | |
download | chromium_src-25762d8fd7f1e4436fc5bc35927f24bac640f2a9.zip chromium_src-25762d8fd7f1e4436fc5bc35927f24bac640f2a9.tar.gz chromium_src-25762d8fd7f1e4436fc5bc35927f24bac640f2a9.tar.bz2 |
Implement IpcPacketSocketFactory
BUG=None
TEST=None
Review URL: http://codereview.chromium.org/6611025
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76921 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/chrome_renderer.gypi | 3 | ||||
-rw-r--r-- | chrome/renderer/p2p/ipc_socket_factory.cc | 322 | ||||
-rw-r--r-- | chrome/renderer/p2p/ipc_socket_factory.h | 43 |
3 files changed, 368 insertions, 0 deletions
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 02ec697..0b78b88 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -23,6 +23,7 @@ '../third_party/hunspell/hunspell.gyp:hunspell', '../third_party/icu/icu.gyp:icui18n', '../third_party/icu/icu.gyp:icuuc', + '../third_party/libjingle/libjingle.gyp:libjingle', '../third_party/npapi/npapi.gyp:npapi', '../third_party/WebKit/Source/WebKit/chromium/WebKit.gyp:webkit', '../webkit/support/webkit_support.gyp:glue', @@ -130,6 +131,8 @@ 'renderer/notification_provider.cc', 'renderer/notification_provider.h', 'renderer/paint_aggregator.cc', + 'renderer/p2p/ipc_socket_factory.cc', + 'renderer/p2p/ipc_socket_factory.h', 'renderer/p2p/socket_client.cc', 'renderer/p2p/socket_client.h', 'renderer/p2p/socket_dispatcher.cc', diff --git a/chrome/renderer/p2p/ipc_socket_factory.cc b/chrome/renderer/p2p/ipc_socket_factory.cc new file mode 100644 index 0000000..d9284f8 --- /dev/null +++ b/chrome/renderer/p2p/ipc_socket_factory.cc @@ -0,0 +1,322 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/p2p/ipc_socket_factory.h" + +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" +#include "chrome/renderer/p2p/socket_client.h" +#include "chrome/renderer/p2p/socket_dispatcher.h" +#include "third_party/libjingle/source/talk/base/asyncpacketsocket.h" + +namespace { + +const size_t kIPv4AddressSize = 4; + +// Chromium and libjingle represent socket addresses differently. The +// following two functions are used to convert addresses from one +// representation to another. +bool ChromeToLibjingleSocketAddress(const P2PSocketAddress& address_chrome, + talk_base::SocketAddress* address_lj) { + if (address_chrome.address.size() != kIPv4AddressSize) { + LOG(ERROR) << "Only IPv4 addresses are supported."; + return false; + } + uint32 ip_as_int = ntohl(*reinterpret_cast<const uint32*>( + &address_chrome.address[0])); + *address_lj = talk_base::SocketAddress(ip_as_int, address_chrome.port); + return true; +} + +bool LibjingleToChromeSocketAddress(const talk_base::SocketAddress& address_lj, + P2PSocketAddress* address_chrome) { + uint32 ip = htonl(address_lj.ip()); + address_chrome->address.resize(kIPv4AddressSize); + memcpy(&address_chrome->address[0], &ip, kIPv4AddressSize); + address_chrome->port = address_lj.port(); + return true; +} + +// IpcPacketSocket implements talk_base::AsyncPacketSocket interface +// using P2PSocketClient that works over IPC-channel. It must be used +// on the thread it was created. +class IpcPacketSocket : public talk_base::AsyncPacketSocket, + public P2PSocketClient::Delegate { + public: + IpcPacketSocket(); + virtual ~IpcPacketSocket(); + + bool Init(P2PSocketType type, P2PSocketClient* client, + const talk_base::SocketAddress& address); + + // talk_base::AsyncPacketSocket interface. + virtual talk_base::SocketAddress GetLocalAddress(bool* allocated) const; + virtual talk_base::SocketAddress GetRemoteAddress() const; + virtual int Send(const void *pv, size_t cb); + virtual int SendTo(const void *pv, size_t cb, + const talk_base::SocketAddress& addr); + virtual int Close(); + virtual talk_base::Socket::ConnState GetState() const; + virtual int GetOption(talk_base::Socket::Option opt, int* value); + virtual int SetOption(talk_base::Socket::Option opt, int value); + virtual int GetError() const; + virtual void SetError(int error); + + // P2PSocketClient::Delegate + virtual void OnOpen(const P2PSocketAddress& address); + virtual void OnError(); + virtual void OnDataReceived(const P2PSocketAddress& address, + const std::vector<char>& data); + + private: + enum State { + STATE_UNINITIALIZED, + STATE_OPENING, + STATE_OPEN, + STATE_CLOSED, + STATE_ERROR, + }; + + // Message loop on which this socket was created and being used. + MessageLoop* message_loop_; + + // Corresponding P2P socket client. + scoped_refptr<P2PSocketClient> client_; + + // Local address is allocated by the browser process, and the + // renderer side doesn't know the address until it receives OnOpen() + // event from the browser. + talk_base::SocketAddress local_address_; + bool address_initialized_; + + // Remote address for client TCP connections. + talk_base::SocketAddress remote_address_; + + // Current state of the object. + State state_; + + // Current error code. Valid when state_ == STATE_ERROR. + int error_; + + DISALLOW_COPY_AND_ASSIGN(IpcPacketSocket); +}; + +IpcPacketSocket::IpcPacketSocket() + : message_loop_(MessageLoop::current()), + address_initialized_(false), + state_(STATE_UNINITIALIZED), error_(0) { +} + +IpcPacketSocket::~IpcPacketSocket() { + if (state_ == STATE_OPENING || state_ == STATE_OPEN || + state_ == STATE_ERROR) { + Close(); + } +} + +bool IpcPacketSocket::Init(P2PSocketType type, P2PSocketClient* client, + const talk_base::SocketAddress& address) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + DCHECK_EQ(state_, STATE_UNINITIALIZED); + + client_ = client; + remote_address_ = address; + state_ = STATE_OPENING; + + P2PSocketAddress address_chrome; + if (!LibjingleToChromeSocketAddress(address, &address_chrome)) { + return false; + } + + client_->Init(type, address_chrome, this, + base::MessageLoopProxy::CreateForCurrentThread()); + + return true; +} + +// talk_base::AsyncPacketSocket interface. +talk_base::SocketAddress IpcPacketSocket::GetLocalAddress( + bool* allocated) const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + *allocated = address_initialized_; + return local_address_; +} + +talk_base::SocketAddress IpcPacketSocket::GetRemoteAddress() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + return remote_address_; +} + +int IpcPacketSocket::Send(const void *data, size_t data_size) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return SendTo(data, data_size, remote_address_); +} + +int IpcPacketSocket::SendTo(const void *data, size_t data_size, + const talk_base::SocketAddress& address) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + switch (state_) { + case STATE_UNINITIALIZED: + NOTREACHED(); + return EWOULDBLOCK; + case STATE_OPENING: + return EWOULDBLOCK; + case STATE_CLOSED: + return ENOTCONN; + case STATE_ERROR: + return error_; + case STATE_OPEN: + // Continue sending the packet. + break; + } + + const char* data_char = reinterpret_cast<const char*>(data); + std::vector<char> data_vector(data_char, data_char + data_size); + + P2PSocketAddress address_chrome; + if (!LibjingleToChromeSocketAddress(address, &address_chrome)) { + // Just drop the packet if we failed to convert the address. + return 0; + } + + client_->Send(address_chrome, data_vector); + + // Fake successful send. The caller ignores result anyway. + return data_size; +} + +int IpcPacketSocket::Close() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + client_->Close(); + state_ = STATE_CLOSED; + + return 0; +} + +talk_base::Socket::ConnState IpcPacketSocket::GetState() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + switch (state_) { + case STATE_UNINITIALIZED: + NOTREACHED(); + return talk_base::Socket::CS_CONNECTING; + + case STATE_OPENING: + return talk_base::Socket::CS_CONNECTING; + + case STATE_OPEN: + return talk_base::Socket::CS_CONNECTED; + + case STATE_CLOSED: + case STATE_ERROR: + return talk_base::Socket::CS_CLOSED; + } + + NOTREACHED(); + return talk_base::Socket::CS_CLOSED; +} + +int IpcPacketSocket::GetOption(talk_base::Socket::Option opt, int* value) { + // We don't support socket options for IPC sockets. + return -1; +} + +int IpcPacketSocket::SetOption(talk_base::Socket::Option opt, int value) { + // We don't support socket options for IPC sockets. + // + // TODO(sergeyu): Make sure we set proper socket options on the + // browser side. + return -1; +} + +int IpcPacketSocket::GetError() const { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return error_; +} + +void IpcPacketSocket::SetError(int error) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + error_ = error; +} + +void IpcPacketSocket::OnOpen(const P2PSocketAddress& address) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + if (!ChromeToLibjingleSocketAddress(address, &local_address_)) { + // Always expect correct IPv4 address to be allocated. + NOTREACHED(); + } + SignalAddressReady(this, local_address_); + address_initialized_ = true; + state_ = STATE_OPEN; +} + +void IpcPacketSocket::OnError() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + state_ = STATE_ERROR; + error_ = ECONNABORTED; +} + +void IpcPacketSocket::OnDataReceived(const P2PSocketAddress& address, + const std::vector<char>& data) { + DCHECK_EQ(MessageLoop::current(), message_loop_); + + talk_base::SocketAddress address_lj; + if (!ChromeToLibjingleSocketAddress(address, &address_lj)) { + // We should always be able to convert address here because we + // don't expect IPv6 address on IPv4 connections. + NOTREACHED(); + return; + } + + SignalReadPacket(this, &data[0], data.size(), address_lj); +} + +} // namespace + +IpcPacketSocketFactory::IpcPacketSocketFactory( + P2PSocketDispatcher* socket_dispatcher) + : socket_dispatcher_(socket_dispatcher) { +} + +IpcPacketSocketFactory::~IpcPacketSocketFactory() { +} + +talk_base::AsyncPacketSocket* IpcPacketSocketFactory::CreateUdpSocket( + const talk_base::SocketAddress& local_address, int min_port, int max_port) { + talk_base::SocketAddress crome_address; + P2PSocketClient* socket_client = new P2PSocketClient(socket_dispatcher_); + scoped_ptr<IpcPacketSocket> socket(new IpcPacketSocket()); + // TODO(sergeyu): Respect local_address and port limits here (need + // to pass them over IPC channel to the browser). + if (!socket->Init(P2P_SOCKET_UDP, socket_client, + talk_base::SocketAddress())) { + return NULL; + } + + // Socket increments reference count if Init() was successful. + return socket.release(); +} + +talk_base::AsyncPacketSocket* IpcPacketSocketFactory::CreateServerTcpSocket( + const talk_base::SocketAddress& local_address, int min_port, int max_port, + bool listen, bool ssl) { + // TODO(sergeyu): Implement this; + NOTIMPLEMENTED(); + return NULL; +} + +talk_base::AsyncPacketSocket* IpcPacketSocketFactory::CreateClientTcpSocket( + const talk_base::SocketAddress& local_address, + const talk_base::SocketAddress& remote_address, + const talk_base::ProxyInfo& proxy_info, + const std::string& user_agent, bool ssl) { + // TODO(sergeyu): Implement this; + NOTIMPLEMENTED(); + return NULL; +} diff --git a/chrome/renderer/p2p/ipc_socket_factory.h b/chrome/renderer/p2p/ipc_socket_factory.h new file mode 100644 index 0000000..7c603dd --- /dev/null +++ b/chrome/renderer/p2p/ipc_socket_factory.h @@ -0,0 +1,43 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_RENDERER_P2P_IPC_SOCKET_FACTORY_H_ +#define CHROME_RENDERER_P2P_IPC_SOCKET_FACTORY_H_ + +#include "base/basictypes.h" +#include "third_party/libjingle/source/talk/base/packetsocketfactory.h" + +class P2PSocketDispatcher; + +// IpcPacketSocketFactory implements talk_base::PacketSocketFactory +// interface for libjingle using IPC-based P2P sockets. The class must +// be used on a thread that is a libjingle thread (implements +// talk_base::Thread) and also has associated base::MessageLoop. Each +// socket created by the factory must be used on the thread it was +// created on. +class IpcPacketSocketFactory : public talk_base::PacketSocketFactory { + public: + explicit IpcPacketSocketFactory(P2PSocketDispatcher* socket_dispatcher); + virtual ~IpcPacketSocketFactory(); + + virtual talk_base::AsyncPacketSocket* CreateUdpSocket( + const talk_base::SocketAddress& local_address, + int min_port, int max_port); + virtual talk_base::AsyncPacketSocket* CreateServerTcpSocket( + const talk_base::SocketAddress& local_address, int min_port, int max_port, + bool listen, bool ssl); + virtual talk_base::AsyncPacketSocket* CreateClientTcpSocket( + const talk_base::SocketAddress& local_address, + const talk_base::SocketAddress& remote_address, + const talk_base::ProxyInfo& proxy_info, + const std::string& user_agent, + bool ssl); + + private: + P2PSocketDispatcher* socket_dispatcher_; + + DISALLOW_COPY_AND_ASSIGN(IpcPacketSocketFactory); +}; + +#endif // CHROME_RENDERER_P2P_IPC_SOCKET_FACTORY_H_ |